OK, I tried the suggestion to do the update in windowDidBecomeKey and this
works nicely with the following (perhaps a little ugly) code:
- (void) specialiseCloseMenu:(BOOL)state {
NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
NSMenu *fileMenu = [[mainMenu itemAtIndex:1] submenu];
NSMenuItem *closeMenuItem = [fileMenu itemWithTag:1090];
NSMenuItem *closeTabMenuItem = [fileMenu itemWithTag:1110];
if (state) {
[closeMenuItem setKeyEquivalent:@"W"];
[closeTabMenuItem setKeyEquivalent:@"w"];
} else {
[closeMenuItem setKeyEquivalent:@"w"];
[closeTabMenuItem setKeyEquivalent:@""];
}
}
- (void) windowDidBecomeKey:(NSNotification *)notification {
[self specialiseCloseMenu:YES];
}
- (void) windowDidResignKey:(NSNotification *)notification {
[self specialiseCloseMenu:NO];
}
Thanks all for your help and thoughts.
Cheers,
Martin
On 20 Oct 2013, at 01:21 pm, Martin Hewitson <[email protected]> wrote:
>
> On 20 Oct 2013, at 01:15 am, Kyle Sluder <[email protected]> wrote:
>
>> Rather than rely on intercepting responder chain-based validation, wouldn't
>> it be much easier and more reliable to make some object the delegate of all
>> of your NSMenus and implement -menuNeedsUpdate:?
>>
>
> But wouldn’t this object then need to know details about the different
> windows which are presented in the app in order to decide which shortcut key
> to set? I have the feeling that the tabbed window (or its delegate) is the
> place to make changes to the default menu configuration. I think I will try
> updating the menu in the windowDidBecomeKey/windowDidResignKey calls of the
> window’s delegate. The thing I don’t like about this is that the delegate (my
> document subclass) now needs to have a link to the menu items which are
> created in the main menu nib. I could try ’scanning' for the relevant menu
> items, maybe searching by tag. Let’s see how far I get with that.
>
> Thanks!
>
> Martin
>
>
>> --Kyle Sluder
>>
>>> On Oct 19, 2013, at 1:28 PM, Andy Lee <[email protected]> wrote:
>>>
>>> Uli and I both remembered the app delegate is checked *after* the
>>> window-related objects in the responder chain, which is what you discovered.
>>>
>>> I *thought* you could work around this by changing the actions of the Close
>>> menu items to methods that only the app delegate implements. But you
>>> really don't want to do that, because it'll break any parts of the built-in
>>> menu validation that assume performClose: is the action for closing windows.
>>>
>>> So I propose yet another solution, which may well be, again, flawed. It
>>> does not go through validateMenuItem:. The following may seem like a lot
>>> of explaining, but it is only a few one- or two-line methods.
>>>
>>> * Have the app delegate listen for NSWindowDidBecomeMainNotification.
>>> * Handle the notification by setting the menu item shortcuts appropriately
>>> for the main window.
>>>
>>> There are different approaches you could take to set the shortcuts
>>> appropriately for the window. One way would be to add two category methods
>>> on NSWindow, something like
>>>
>>> + (void)updateShortcutForCloseMenuItem:(NSMenuItem *)menuItem
>>> {
>>> [menuItem setKeyEquivalent:@"w"];
>>> }
>>>
>>> + (void)updateShortcutForCloseTabMenuItem:(NSMenuItem *)menuItem
>>> {
>>> [menuItem setKeyEquivalent:@""];
>>> }
>>>
>>> For your window that has tabs, use an NSWindow subclass that overrides
>>> these methods:
>>>
>>> + (void)updateShortcutForCloseMenuItem:(NSMenuItem *)menuItem
>>> {
>>> [menuItem setKeyEquivalent:@"W"];
>>> }
>>>
>>> + (void)updateShortcutForCloseTabMenuItem:(NSMenuItem *)menuItem
>>> {
>>> [menuItem setKeyEquivalent:@"w"];
>>> }
>>>
>>> Your app delegate could have outlets to the two menu items in question.
>>> Then your notification-handling method can be:
>>>
>>> - (void)handleWindowDidBecomeMainNotification:(NSNotification *)notif
>>> {
>>> [[[NSApp mainWindow] class] updateShortcutForCloseMenuItem:_closeMenuItem];
>>> [[[NSApp mainWindow] class]
>>> updateShortcutForCloseTabMenuItem:_closeTabMenuItem];
>>> }
>>>
>>> I think the only case this does not handle is when the very last window is
>>> closed. You may or may not care what the shortcuts are, since the menu
>>> items will be dimmed. If you do care, you can:
>>>
>>> * Have the app delegate listen for NSWindowDidResignMainNotification.
>>> * Handle the notification by first checking whether [NSApp mainWindow] is
>>> nil. If so (and *only* if so, for the reason Uli pointed out), set the
>>> shortcuts to whatever you want them to be in that case. For example, you
>>> could do this (which is why I made those "update" methods class methods):
>>>
>>> - (void)handleWindowDidResignMainNotification:(NSNotification *)notif
>>> {
>>> if ([NSApp mainWindow] == nil)
>>> {
>>> [NSWindow updateShortcutForCloseMenuItem:_closeMenuItem];
>>> [NSWindow updateShortcutForCloseTabMenuItem:_closeTabMenuItem];
>>> }
>>> }
>>>
>>> --Andy
>>>
>>>
>>>> On Oct 19, 2013, at 2:32 PM, Martin Hewitson <[email protected]>
>>>> wrote:
>>>>
>>>> I guess I didn’t understand correctly since my app delegate does not get
>>>> asked to validate the Close menu item. So far the only thing that get’s
>>>> asked to validate this is the tabbed window object. Even the window’s
>>>> delegate is not asked.
>>>>
>>>> The documentation states:
>>>>
>>>> For document-based applications, the default responder chain for the main
>>>> window consists of the following responders and delegates:
>>>> • The main window’s first responder and the successive responder objects
>>>> up the view hierarchy
>>>> • The main window itself
>>>> • The window's NSWindowController object (which inherits from NSResponder)
>>>> • The main window’s delegate.
>>>> • The NSDocument object (if different from the main window’s delegate)
>>>> • The application object, NSApp
>>>> • The application object's delegate
>>>> • The application's document controller (an NSDocumentController object,
>>>> which does not inherit from NSResponder)
>>>>
>>>> My NSPersistentDocument subclass is the main window’s delegate.
>>>>
>>>> Clearly my thinking is flawed, but where? If I want the Close menu item to
>>>> vary depending on the window that is key, and I have to do this with
>>>> validateMenuItem:, then the responder chain above seems to suggest that I
>>>> need to have a validateMenuItem: in each and every window in the app. I
>>>> hope that’s not the case….
>>>>
>>>> Martin
>>>>
>>>>
>>>>> On 19 Oct 2013, at 07:28 pm, Martin Hewitson <[email protected]>
>>>>> wrote:
>>>>>
>>>>> OK, so the idea is,
>>>>>
>>>>> + validateMenuItem in app delegate gets a first shot at setting the
>>>>> keyboard shortcuts
>>>>> + I override validateMenuItem in my tabbed window and reset the keyboard
>>>>> shortcuts
>>>>> + Other windows stick with the settings arranged by the app delegate
>>>>>
>>>>> Did I understand correctly?
>>>>>
>>>>> Thanks to all who replied.
>>>>>
>>>>> Cheers,
>>>>>
>>>>> Martin
>>>>>
>>>>>
>>>>>> On 19, Oct, 2013, at 02:46 pm, Uli Kusterer
>>>>>> <[email protected]> wrote:
>>>>>>
>>>>>>
>>>>>>> On 19 Oct 2013, at 14:27, Andy Lee <[email protected]> wrote:
>>>>>>>> On Oct 19, 2013, at 6:58 AM, Martin Hewitson
>>>>>>>> <[email protected]> wrote:
>>>>>>>> Main Window with tabs:
>>>>>>>> close (cmd-shift-w)
>>>>>>>> close tab (cmd-w)
>>>>>>>>
>>>>>>>> All other windows:
>>>>>>>> close (cmd-w)
>>>>>>>> close tab (inactive, no keyboard shortcut)
>>>>>>>>
>>>>>>>> This is pretty much the way things work in Xcode.
>>>>>>>>
>>>>>>>> So, my question is, is there a smart way to do this, or do I need to
>>>>>>>> implement -validateMenuItem: on every window in the app and set the
>>>>>>>> keyboard shortcuts there?
>>>>>>>
>>>>>>> Untested idea: implement windowDidBecomeKey: and windowDidResignKey: in
>>>>>>> the delegate of the window that has tabs and do the switching of
>>>>>>> shortcuts there.
>>>>>>
>>>>>> Would rather recommend against this. I don’t think there’s any guarantee
>>>>>> given what gets called first, validateMenuItem: or windowDidResignKey:.
>>>>>> You might be obliterating something already set by the incoming window.
>>>>>>
>>>>>>> If you want to be extra careful you could have two ivars that remember
>>>>>>> what the shortcuts were before you changed them to cmd-shift-w and
>>>>>>> cmd-w. Then in windowDidResignKey: plug those shortcuts in rather than
>>>>>>> hard-code cmd-w and @“”.
>>>>>>
>>>>>> Also, while I’m not aware of any localization that doesn’t use Cmd-W for
>>>>>> close, it’s in general a good idea to keep your shortcuts localizable
>>>>>> (e.g. on a German keyboard, there’s no way to type Cmd-~, because it has
>>>>>> no ~-key, so window rotation is done using Cmd-< there).
>>>>>>
>>>>>> I’d recommend adding a validateMenuItem: handler in the application
>>>>>> delegate, as long as you’re aware that this won’t be called for modal
>>>>>> panels or windows that have sheets on them, but since those generally
>>>>>> don’t enable the “Close” menu item, you should be fine. At least then
>>>>>> you’re modifying the items.
>>>>>>
>>>>>> Also, I’m not sure whether menu shortcut disambiguation lets you do
>>>>>> that, but have you tried hiding and showing menu items with the same
>>>>>> names but different shortcuts instead of actually changing the items’
>>>>>> shortcut? I.e. create your menu as
>>>>>>
>>>>>> Close Cmd-Shift-W -> -performCloseForTabbedWindow:
>>>>>> Close Cmd-W -> -performClose:
>>>>>> Close Tab Cmd-W -> -performCloseTab:
>>>>>> Close Tab -> -performCloseTabDummy:
>>>>>>
>>>>>> And then hide/show the “tab” items? That way, localization would be
>>>>>> easier, and you’re not hard-coding any shortcuts. Though that doesn’t
>>>>>> solve the issue of properly restoring the items after you’ve hidden them.
>>>>>>
>>>>>> Cheers,
>>>>>> -- Uli Kusterer
>>>>>> “The Witnesses of TeachText are everywhere...”
>>>>>> http://zathras.de
>>>>>
>
_______________________________________________
Cocoa-dev mailing list ([email protected])
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com
This email sent to [email protected]