Bindings to enable a menu item based on an array's element count
My web-browser app makes menu items with paired submenus for each day that has WebHistoryItem instances. It’s done with a custom coordinator object that publishes a KVO-compliant array, each element a menu item with a submenu. I made another coordinator object to handle Today’s history items. I set it’s source-menu attribute to one of the day menu-items, and it publishes two KVO-compliant arrays. The first array holds copies of the source menu’s items up to a limit, the second array holds copies of the remaining menu items. (The limit is also an attribute.) Note that these menu items arrays are read-only from the outside. Right now, the menu item that holds the submenu where the overflow menu items go is always visible. I was thinking of controlling its visibility with Cocoa Bindings. I select the menu item in the Interface Builder part of Xcode, go to the Bindings Inspector, reveal the Hidden attribute settings, and set the binding to my app’s delegate. Under the Model Key Path, I put “self.my2ndCoordinator.overflowArray”. Now, that’s an array and I need a Boolean. I tried “.count” and “.@count” at the end, and neither worked. How do I get that part to work? Even then, I still need to stick in the “ 0” part somewhere (unless Bindings does the zero vs. non-zero to Boolean conversion C does). What about that? Or do I have to go indirect and use something like an NSArrayController? — Daryle Walker Mac, Internet, and Video Game Junkie darylew AT mac DOT com ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Bindings to enable a menu item based on an array's element count
On Sep 7, 2014, at 5:15 AM, Daryle Walker dary...@mac.com wrote: Right now, the menu item that holds the submenu where the overflow menu items go is always visible. I was thinking of controlling its visibility with Cocoa Bindings. I select the menu item in the Interface Builder part of Xcode, go to the Bindings Inspector, reveal the Hidden attribute settings, and set the binding to my app’s delegate. Under the Model Key Path, I put “self.my2ndCoordinator.overflowArray”. Now, that’s an array and I need a Boolean. I tried “.count” and “.@count” at the end, and neither worked. What does neither worked mean? What happened? .@count should work. .count would not. That effectively attempts to construct an array by asking each _element_ of overflowArray for its count property, which is always the way that KVC on arrays works. This will probably fail because the elements don't have a count property and even it succeeds, it would result in an array which is not an appropriate result for the hidden binding. (Also, starting a key path with self. is just redundant.) Even then, I still need to stick in the “ 0” part somewhere (unless Bindings does the zero vs. non-zero to Boolean conversion C does). I don't know that I'd describe it as bindings doing that conversion, but, yes, in a boolean context, 0 means false and non-zero means true. Since you presumably want the menu to be hidden when the count is 0, you want to use the NSNegateBoolean value transformer set on the binding. Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Responder Chain Confusion
Hi all I just spent a bit of time poking around the responder chain and nil targeted actions. I built a view controller and a view hierarchy with controls that should be configurable. When instantiating the view controller the interface allows configuring the action SEL of the controls. Reuse is the goal of course. I know 10.10 changes things greatly for view controllers. But on 10.9 that's not there. Anyway, I need to insert the view controller into the responder chain between its top level view and that view's superview. That wasn't too bad. Implement the missing reference to the vc in a view subclass and give the vc a callback when the view is in place. But what I found while tinkering is that for non-document based apps from a vanilla project template there was no next responder for the window or the NSApplication instance. I had a method in the app delegate that I was trying to reach via nil targeted action. It couldn't get there. So I set the window nextResponder to the app and the app to the app delegate. I also made the app delegate an NSResponder subclass. It feels like overkill. Am I missing something simple? Is there something better to do here? ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Responder Chain Confusion
On Sep 7, 2014, at 10:24 AM, dangerwillrobinsondan...@gmail.com wrote: I just spent a bit of time poking around the responder chain and nil targeted actions. I built a view controller and a view hierarchy with controls that should be configurable. When instantiating the view controller the interface allows configuring the action SEL of the controls. Reuse is the goal of course. I know 10.10 changes things greatly for view controllers. But on 10.9 that's not there. Anyway, I need to insert the view controller into the responder chain between its top level view and that view's superview. That wasn't too bad. Implement the missing reference to the vc in a view subclass and give the vc a callback when the view is in place. But what I found while tinkering is that for non-document based apps from a vanilla project template there was no next responder for the window or the NSApplication instance. I had a method in the app delegate that I was trying to reach via nil targeted action. It couldn't get there. So I set the window nextResponder to the app and the app to the app delegate. I also made the app delegate an NSResponder subclass. It feels like overkill. Am I missing something simple? Is there something better to do here? My inclination is that you broke something in your attempts to insert your VC into the responder chain. The app delegate is always the last responder. Any vanilla template will show that the app delegate will respond to unhandled action message. In a vanilla project of any kind (that has a window), implement the action message in your app delegate, wire up a nil-targeted action in a menu, say, then see whether it is handled. If it is not, I would file a bug, because I cannot reproduce this behavior. Keary Suska Esoteritech, Inc. Demystifying technology for your home or business ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Responder Chain Confusion
On Sep 7, 2014, at 9:24 AM, dangerwillrobinsondan...@gmail.com wrote: Hi all I just spent a bit of time poking around the responder chain and nil targeted actions. I built a view controller and a view hierarchy with controls that should be configurable. When instantiating the view controller the interface allows configuring the action SEL of the controls. Reuse is the goal of course. I know 10.10 changes things greatly for view controllers. But on 10.9 that's not there. Anyway, I need to insert the view controller into the responder chain between its top level view and that view's superview. That wasn't too bad. Implement the missing reference to the vc in a view subclass and give the vc a callback when the view is in place. But what I found while tinkering is that for non-document based apps from a vanilla project template there was no next responder for the window or the NSApplication instance. I had a method in the app delegate that I was trying to reach via nil targeted action. It couldn't get there. So I set the window nextResponder to the app and the app to the app delegate. I also made the app delegate an NSResponder subclass. It feels like overkill. Am I missing something simple? IIRC, the window is not supposed to have a nextResponder. Conceptually, the next responder of a window is its delegate, and the next responder of an application is *its* delegate, but there is no requirement that these objects are instances of subclasses of NSResponder. Therefore the responder chain mechanism manually jumps from the window to its delegate, then to the application, and finally to the app’s delegate. (It also inserts window controllers as well as the shared NSDocumentController instance if one has been created.) If the message never reached your app delegate, I suspect you failed to patch the view controller’s nextResponder in correctly. --Kyle Sluder ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Bindings to enable a menu item based on an array's element count
On Sep 7, 2014, at 7:02 AM, Ken Thomases k...@codeweavers.com wrote: On Sep 7, 2014, at 5:15 AM, Daryle Walker dary...@mac.com wrote: Right now, the menu item that holds the submenu where the overflow menu items go is always visible. I was thinking of controlling its visibility with Cocoa Bindings. I select the menu item in the Interface Builder part of Xcode, go to the Bindings Inspector, reveal the Hidden attribute settings, and set the binding to my app’s delegate. Under the Model Key Path, I put “self.my2ndCoordinator.overflowArray”. Now, that’s an array and I need a Boolean. I tried “.count” and “.@count” at the end, and neither worked. What does neither worked mean? What happened? Putting “my2ndCoordinator.overflowArray” gives me a red exclamation stop-sign in the text field with the tool-tip text: “The Hidden expects to be bound to an object of type NSNumber, but my2ndCoordinator.overflowArray is of type NSArray.” Appending a “.@count” gives a grey exclamation stop-sign with a tool-tip of: “Xcode cannot resolve the entered keypath”. Now when I use “.count” I get no complaints, although I did before writing the first message. (Did leaving out the “self.” make a difference?) I do end up crashing (An uncaught exception was raised” followed by “[__NSArrayM 0x6004a770 addObserver:forKeyPath:options:context:] is not supported. Key path: count”). .@count should work. .count would not. That effectively attempts to construct an array by asking each _element_ of overflowArray for its count property, which is always the way that KVC on arrays works. This will probably fail because the elements don't have a count property and even it succeeds, it would result in an array which is not an appropriate result for the hidden binding. (Also, starting a key path with self. is just redundant.) Even then, I still need to stick in the “ 0” part somewhere (unless Bindings does the zero vs. non-zero to Boolean conversion C does). I don't know that I'd describe it as bindings doing that conversion, but, yes, in a boolean context, 0 means false and non-zero means true. Since you presumably want the menu to be hidden when the count is 0, you want to use the NSNegateBoolean value transformer set on the binding. — Daryle Walker Mac, Internet, and Video Game Junkie darylew AT mac DOT com ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Bindings to enable a menu item based on an array's element count
On Sep 7, 2014, at 2:09 PM, Daryle Walker dary...@mac.com wrote: Putting “my2ndCoordinator.overflowArray” gives me a red exclamation stop-sign in the text field […]. Appending a “.@count” gives a grey exclamation stop-sign with a tool-tip of: “Xcode cannot resolve the entered keypath”. Proceed anyway. Xcode is just reporting that it isn't sure. It's a warning, not an error. Regards, Ken ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Bindings to enable a menu item based on an array's element count
On Sep 7, 2014, at 3:53 PM, Ken Thomases k...@codeweavers.com wrote: On Sep 7, 2014, at 2:09 PM, Daryle Walker dary...@mac.com wrote: Putting “my2ndCoordinator.overflowArray” gives me a red exclamation stop-sign in the text field […]. Appending a “.@count” gives a grey exclamation stop-sign with a tool-tip of: “Xcode cannot resolve the entered keypath”. Proceed anyway. Xcode is just reporting that it isn't sure. It's a warning, not an error. Putting a “.@count” didn’t cause a crash. Adding a value transformer of NSNegateBoolean…no crash. Starting app again (with Xcode in debug mode). App has restored web-history entries from yesterday and a few days ago. The “Earlier Today” menu finally isn’t there. Load home page: home page in an item by itself, no “Earlier Today” item, no with-date menu item for today (should be hidden), yesterday’s menu lost the home page entry. Visit a few more new pages: more items directly show up in the history menu. Finally visit another page that exceeds the direct menu item limit, moving an item to the overflow submenu: “Earlier Today” shows up with the excess item in its submenu. Quit and restart: menus in the same configuration as last time. Quit and restart without loading WebHistory: no menu items; loading more pages until the overflow menu shows up works. Clearing the History and repeating the exercise: works. Yay. Thanks. — Daryle Walker Mac, Internet, and Video Game Junkie darylew AT mac DOT com ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Responder Chain Confusion
On 2014/09/08, at 3:16, Kyle Sluder k...@ksluder.com wrote: On Sep 7, 2014, at 9:24 AM, dangerwillrobinsondan...@gmail.com wrote: Hi all I just spent a bit of time poking around the responder chain and nil targeted actions. I built a view controller and a view hierarchy with controls that should be configurable. When instantiating the view controller the interface allows configuring the action SEL of the controls. Reuse is the goal of course. I know 10.10 changes things greatly for view controllers. But on 10.9 that's not there. Anyway, I need to insert the view controller into the responder chain between its top level view and that view's superview. That wasn't too bad. Implement the missing reference to the vc in a view subclass and give the vc a callback when the view is in place. But what I found while tinkering is that for non-document based apps from a vanilla project template there was no next responder for the window or the NSApplication instance. I had a method in the app delegate that I was trying to reach via nil targeted action. It couldn't get there. So I set the window nextResponder to the app and the app to the app delegate. I also made the app delegate an NSResponder subclass. It feels like overkill. Am I missing something simple? IIRC, the window is not supposed to have a nextResponder. Conceptually, the next responder of a window is its delegate, and the next responder of an application is *its* delegate, but there is no requirement that these objects are instances of subclasses of NSResponder. Therefore the responder chain mechanism manually jumps from the window to its delegate, then to the application, and finally to the app’s delegate. (It also inserts window controllers as well as the shared NSDocumentController instance if one has been created.) If the message never reached your app delegate, I suspect you failed to patch the view controller’s nextResponder in correctly. --Kyle Sluder I figured as much. It seemed like none of that should be necessary normally. But why would it be able to send standard responder messages like the orderOut: action message to nil and reach the window? I must be inserting it wrongly into the responder chain. The docs describe exactly what everyone said here. My app delegate does implement the selector. I even exposed it in the header. It is an action message. So without doing anything special I should be able to send it right up the chain and reach the app delegate. The docs even state ( in the Cocoa Event Handling Guide, the others gloss over this) that a window delegate and app delegate do not need to be NSResponder descendants. I will look at this with an even simpler test app and see what's going on. I think you're right that I borked the responder chain insertion. Without inserting the controller into the responder chain, should the view follow the normal behavior of having its superview has nextResponder? ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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 arch...@mail-archive.com
Re: Responder Chain Confusion
On 2014/09/08, at 8:49, dangerwillrobinsondan...@gmail.com wrote: On 2014/09/08, at 3:16, Kyle Sluder k...@ksluder.com wrote: On Sep 7, 2014, at 9:24 AM, dangerwillrobinsondan...@gmail.com wrote: Hi all I just spent a bit of time poking around the responder chain and nil targeted actions. I built a view controller and a view hierarchy with controls that should be configurable. When instantiating the view controller the interface allows configuring the action SEL of the controls. Reuse is the goal of course. I know 10.10 changes things greatly for view controllers. But on 10.9 that's not there. Anyway, I need to insert the view controller into the responder chain between its top level view and that view's superview. That wasn't too bad. Implement the missing reference to the vc in a view subclass and give the vc a callback when the view is in place. But what I found while tinkering is that for non-document based apps from a vanilla project template there was no next responder for the window or the NSApplication instance. I had a method in the app delegate that I was trying to reach via nil targeted action. It couldn't get there. So I set the window nextResponder to the app and the app to the app delegate. I also made the app delegate an NSResponder subclass. It feels like overkill. Am I missing something simple? IIRC, the window is not supposed to have a nextResponder. Conceptually, the next responder of a window is its delegate, and the next responder of an application is *its* delegate, but there is no requirement that these objects are instances of subclasses of NSResponder. Therefore the responder chain mechanism manually jumps from the window to its delegate, then to the application, and finally to the app’s delegate. (It also inserts window controllers as well as the shared NSDocumentController instance if one has been created.) If the message never reached your app delegate, I suspect you failed to patch the view controller’s nextResponder in correctly. --Kyle Sluder I figured as much. It seemed like none of that should be necessary normally. But why would it be able to send standard responder messages like the orderOut: action message to nil and reach the window? I must be inserting it wrongly into the responder chain. The docs describe exactly what everyone said here. My app delegate does implement the selector. I even exposed it in the header. It is an action message. So without doing anything special I should be able to send it right up the chain and reach the app delegate. The docs even state ( in the Cocoa Event Handling Guide, the others gloss over this) that a window delegate and app delegate do not need to be NSResponder descendants. I will look at this with an even simpler test app and see what's going on. I think you're right that I borked the responder chain insertion. Without inserting the controller into the responder chain, should the view follow the normal behavior of having its superview has nextResponder? Ok, I re-read the docs. I found the solution I needed, without inserting anything into the responder chain. I was doing this, and that was why I felt I needed to insert the vc into the responder chain. I even set the vc’s nextResponder to the window, which should have shortened the trip slightly, but the action message stopped at the window. (the self.negativeResponseAction is a configurable SEL property on the vc, the method call is in the vc’s action method of the control inside the vc’s view hierarchy) BOOL performed = [sender tryToPerform:self.negativeResponseAction with:sender]; For what I want to do, vend a view with some controls whose actions are configurable, the approach that works gracefully is this: BOOL performed = [[NSApplication sharedApplication] sendAction:self.negativeResponseAction to:nil from:sender]; In true Cocoa fashion, if it is taking a lot of effort and many lines of code, we are probably doing something wrong and there is probably a short simple solution, even if it isn’t immediately obvious. The second method is clearly and explicitly documented to do things the way the Event Handling Guide says the responder chain should work for actions. The first method is a lot less less documented and makes no assurances in the NSResponder docs about what it will or will not do beyond sending to the nextResponder. (one would think that should be fine from the docs, but the second method is clearly documented to do the right thing and it works well for nil-targeted action) For whatever reason, tryToPerform:with: does not work to forward message up the chain. Sending sendAction:to:from: to the NSApp works like a charm. ___ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) Please do not post admin requests or moderator comments to
Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
Yesterday, I had a thread (b6bbfa38-4761-4d25-bdfa-d6e5d71c1...@mac.com “Bindings to enable a menu item based on an array's element count”) on this list on how to add a Binding to a menu item’s Hidden flag based on the length of a custom object’s array-based property. I got the code working. There is a menu item for each day that has WebHistoryItem instances. (Each menu item has a submenu with items for each web-history entry.) The custom object is pointed to one of those menu items’ submenu and creates two arrays of menu items, one has copies of the first few menu items of the source menu, the other copies of the remaining trailing menu items. With KVO signaling, the first array’s items are directly dumped in the top-level menu and the second array’s items are dumped into a submenu of the menu item following the direct items. The Binding from the previous thread hid that menu item when the corresponding array was empty. Now whenever the custom object has at least one non-empty array, I want to hide the menu item that contains the source submenu, so I don’t have two copies visible. Right now, I handle hiding and un-hiding manually: - (void)prepareTodayHistoryMenu { NSMenu * const browseMenu = self.earlierToday.menu; NSMenuItem * const beyondEarlierTodayMenuItem = [browseMenu itemAtIndex:(1 + [browseMenu indexOfItem:self.earlierToday])]; self.todayHistoryHandler.sourceMenu = (beyondEarlierTodayMenuItem.isSeparatorItem || ![[NSCalendar autoupdatingCurrentCalendar] isDateInToday:beyondEarlierTodayMenuItem.representedObject]) ? nil : beyondEarlierTodayMenuItem.submenu; [beyondEarlierTodayMenuItem setHidden:!!self.todayHistoryHandler.sourceMenu]; } I grouped the source-menu assignment and the hiding of the original menu item together. - (void)notifyOnNewDay:(NSNotification *)notification { NSMenu * constbrowseMenu = self.earlierToday.menu; NSInteger const beyondEarlierTodayIndex = [browseMenu indexOfItem:self.earlierToday] + 1; [[browseMenu itemAtIndex:beyondEarlierTodayIndex] setHidden:NO]; // If the today menu item shifts, we'll lose track of this and therefore can't restore it. [self prepareTodayHistoryMenu]; } This makes sure to disconnect the menu item’s submenu from being the source menu, since it no longer belongs to Today. I assume that the latest per-day menu item has the source submenu. If there are no per-day items, then the following separator item gets a no-op set-visible action. Note that the affected menu item isn’t tracked, so I have to make it (which may be a no-op on the following separator item) re-visible before the prepare action sets its source menu to NIL. (A menu item and submenu for the new Today get created as-needed in the following method.) - (void)rebuildHistoryMenusDueToChange:(NSDictionary *)change { NSMenu * const browseMenu = self.earlierToday.menu; NSInteger beyondEarlierTodayIndex = [browseMenu indexOfItem:self.earlierToday] + 1; NSIndexSet * const indexesChanged = change[NSKeyValueChangeIndexesKey]; // May be nil, depending on 'changeType'. NSParameterAssert(beyondEarlierTodayIndex != -1 + 1); switch ((NSKeyValueChange)[change[NSKeyValueChangeKindKey] unsignedIntegerValue]) { default: case NSKeyValueChangeSetting: { // Do wholesale replacement; get rid of the current menu items and install the new ones. //... [self prepareTodayHistoryMenu]; // Rebuild today's history menus. break; } case NSKeyValueChangeRemoval: { // Purge the menus of the deleted indexes. NSParameterAssert(indexesChanged); [indexesChanged enumerateIndexesWithOptions:NSEnumerationReverse usingBlock:^(NSUInteger idx, BOOL *stop) { [browseMenu removeItemAtIndex:(beyondEarlierTodayIndex + (NSInteger)idx)]; }]; if ([indexesChanged containsIndex:0u]) { // Only if today was deleted,... [self prepareTodayHistoryMenu]; // ...rebuild today's history menus. } break; } case NSKeyValueChangeInsertion: { // Place the menus of the added indexes. NSParameterAssert(indexesChanged); [[browseMenu itemAtIndex:beyondEarlierTodayIndex] setHidden:NO]; // If index 0 shifts, we'll lose track of this and therefore can't restore it. [indexesChanged enumerateIndexesUsingBlock:^(NSUInteger idx, BOOL *stop) { /*…*/ }]; if ([indexesChanged containsIndex:0u]) { // Gained or still have a today menu item, so just update. [self prepareTodayHistoryMenu]; } else { [[browseMenu itemAtIndex:beyondEarlierTodayIndex] setHidden:YES]; // Undo non-hiding from second line. }