Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Text boxes don't have context menus and don't allow copy/paste through keyboard shortcuts #7

Open
PaulBol opened this issue Dec 19, 2018 · 8 comments

Comments

@PaulBol
Copy link

PaulBol commented Dec 19, 2018

Unlike most native controls text boxes do not show a context menu on right click. There is one exception: When right-clicking on a TextBoxBase_NSTextView while it does not have focus the macOS context menu is shown. It does not work when right-clicking after the control was activated.
image

Also Apple+C/V/X shortcuts don't work.

I'd be happy to trying adding the functionality. However, I'm wondering if overriding the mouse button and key down events and adding the required calls here is would be the right approach.

@filipnavara Can you share any hints?

@filipnavara
Copy link
Collaborator

We probably don't have an implementation strategy for this yet.

In our application we have a custom implementation of the application menu which triggers the standard selectors for the Copy/Cut/Paste commands and thus makes the shortcuts work. Obviously the drawback is that it doesn't work out of the box for normal WinForms applications. It would probably be possible to change the default menu code to include this as well, but it may not be the perfect solution. Another thing I contemplated about was properly routing the Windows WM_CLEAR/WM_COPY/WM_CUT/WM_PASTE messages and implementing them on the native controls, but that probably won't solve anything for this particular case either.

I currently don't have any thoughts for the context menu. My coworker wrote the code, so he'd probably know better (@aconcagua21?). Are you interested in the standard system context menu, or also a way to use a custom one?

@PaulBol
Copy link
Author

PaulBol commented Dec 19, 2018

Thanks for your feedback. Do you have any idea why for the NSTextView the context menu shows when it does not have focus? I'm trying to understand if it might be possible to have the same functionality when focused.

Could you share a pointer how to add a custom application menu or add the default menu?

@filipnavara
Copy link
Collaborator

Do you have any idea why for the NSTextView the context menu shows when it does not have focus?

Honestly I am quite puzzled by that. I didn't notice it until you pointed it out.

Could you share a pointer how to add a custom application menu or add the default menu?

We used to translate the window menu in XplatUICocoa and provide some reasonable defaults there if no window menu was present, but it seems the code has bit-rotten and we eventually removed it. Even if it was resurrected there's still issue with the fact that it only worked on the old-school Menu/MenuItem objects and not on the newer ToolStripMenuItem code that most applications use nowadays.

Unfortunately I cannot share the code from our application. We have the menu implemented using ToolStripMenuItems (shared across our Windows and Mac code base). The rough translation code to Cocoa NSMenu is similar to the implementation in ToNSMenu method in ToolStripDropDown.Mac.cs. Then we add special handling for the Mac-specific menus like "Window". Lastly we assign the resulting menu to NSApplication.SharedApplication.Menu.

For the special menu items like Copy/Paste we register a code similar to this to the Click handler of ToolStripMenuItem:

var selector = new Selector("copy:"));
var responder = NSApplication.SharedApplication.KeyWindow?.FirstResponder;

if (responder != null) {
    if (!IsSwfResponder(responder))
        if (SwfContainerDoCommandBySelector(responder, selector))
            return; // swallowed or executed by swf container
    if (responder.TryToPerformwith(selector, this))
        return;
}

and the helper methods:

		// The following selectors must match method selectors in MonoView and WindowsEventResponder
		internal static readonly Selector swfControlHandleSel = new Selector("swfControlHandle");
		internal static readonly Selector embeddedControlDoCommandBySelectorSel = new Selector("embeddedControl:doCommandBySelector:");

		internal static bool IsSwfResponder(NSResponder responder)
		{
			return responder.RespondsToSelector(swfControlHandleSel);
		}

		internal static NSResponder NextSwfResponder(NSResponder responder)
		{
			while (responder != null && !IsSwfResponder(responder))
				responder = responder.NextResponder;

			return responder;
		}

		internal static bool SwfContainerDoCommandBySelector(NSResponder target, Selector command)
		{
			return SwfContainerDoCommandBySelector(NextSwfResponder(target), target, command);
		}

		internal static bool SwfContainerDoCommandBySelector(NSResponder container, NSResponder target, Selector command)
		{
			if (container?.RespondsToSelector(embeddedControlDoCommandBySelectorSel) ?? false)
				return LibObjc.bool_objc_msgSend_IntPtr_IntPtr(container.Handle, embeddedControlDoCommandBySelectorSel.Handle, target.Handle, command.Handle);

			return false;
		}

The approach is far from universal and I would be happy if the code is cleaned up since we now depend on the internal implementation in XplatUICocoa. I didn't think about how to clean it up though, so ideas are more than welcome.

@aconcagua21
Copy link
Contributor

So far, I don't know why context menus don't work.
However, copy/paste/select all is supported. Just add appropriate menu items, like this:

	var menuBar = new NSMenu("");

	var appMenuItem = new NSMenuItem("");
	var appMenu = new NSMenu("FormsTest");
	var quitItem = new NSMenuItem("Quit") { KeyEquivalent = "q" };
	quitItem.Activated += (sender, e) => { Terminate(); };
	appMenu.AddItem(quitItem);
	menuBar.AddItem(appMenuItem);
	menuBar.SetSubmenu(appMenu, appMenuItem);

	var editMenuItem = new NSMenuItem();
	var editMenu = new NSMenu("Edit");
	var selectAllItem = new NSMenuItem("Select All") { Action = new ObjCRuntime.Selector("selectAll:"), KeyEquivalent = "a" };
	editMenu.AddItem(selectAllItem);
	var copyItem = new NSMenuItem("Copy") { Action = new ObjCRuntime.Selector("copy:"), KeyEquivalent="c" };
	editMenu.AddItem(copyItem);
	var pasteItem = new NSMenuItem("Paste") { Action = new ObjCRuntime.Selector("paste:") , KeyEquivalent="v" };
	editMenu.AddItem(pasteItem);
	menuBar.AddItem(editMenuItem);
	menuBar.SetSubmenu(editMenu, editMenuItem);

	NSApplication.SharedApplication.Menu = menuBar;

@filipnavara
Copy link
Collaborator

Now I remembered how I wanted to use the WM_COPY/WM_PASTE/WM_CUT messages. The idea was to implement the standard "copy:"/"paste:"/"cut:" selectors on MonoView and translate them into the window messages. Then the selectors should work on both native controls and the WinForms ones.

@aconcagua21
Copy link
Contributor

Yes, that's how it is done (not in MonoView, but in WindowsEventResponder).

@filipnavara
Copy link
Collaborator

The context menu problem should be resolved by 7e01729.

@PaulBol
Copy link
Author

PaulBol commented Apr 16, 2019

Thanks for the update. I will try it but it may take a while.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants