• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?


  • Subject: Re: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
  • From: Daryle Walker <email@hidden>
  • Date: Mon, 08 Sep 2014 03:10:02 -0400

On Sep 8, 2014, at 1:12 AM, Daryle Walker <email@hidden> wrote:

> Yesterday, I had a thread (<email@hidden> “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 * const                browseMenu = 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.)
[SNIP big example]
> I was thinking that if I used Bindings for one menu item, could I add Bindings between the new menu items and some attribute of the custom object, so the menu items hide themselves when being mirrored. (At most one will be hidden.) I added a NSValueTransformer to my custom object.
>
>> @interface MyOverflowMenuController : NSObject
>>
>> //! Starts as nil; when set, this instance stores copies of the menu’s
>> //! items and tracks the menu for item insertions, removals, and renames.
>> @property (nonatomic) NSMenu *            sourceMenu;
>> //! Starts as zero; if the menu has more menu items that this value,
>> //! the copies of the menu's latter items are stored in the overflow
>> //! array instead of the direct array.
>> @property (nonatomic, assign) NSUInteger  maxDirectCount;
>>
>> //! Starts as empty; updated to mirror the source menu's menu-items.
>> //! Keeps at most 'maxDirectCount' items. KVO-compliant.
>> @property (nonatomic, readonly) NSArray *    directMenuItems;
>> //! Starts as empty; updated to mirror the source menu's menu-items.
>> //! Keeps the overflow from 'directMenuItems'. KVO-compliant.
>> @property (nonatomic, readonly) NSArray *  overflowMenuItems;
>>
>> //! Transforms a NSMenu to a NSNumber with a BOOL value that's YES
>> //! when the given menu is self.sourceMenu.
>> @property (nonatomic, readonly) NSValueTransformer *  isSourceMenuTransformer;
>>
>> @end
>
>
> That last property is of a custom NSValueTransformer subclass. (Is not using a new .h/.m file pair for the subclass OK?) The subclass holds a pointer to the source menu, and gets updated when the outer class changes that property in the setter. The custom object should out-live the per-day history menu items. So how would connect each menu item’s Hidden attribute to the custom object and the value transformer? Am I using the right kind of transformer? The source-menu attribute should be KVO-compliant since I use the automatic getter and a custom setter (that mutate the two arrays).

I tried:

> static inline
> NSMenuItem *  CreateMenuItemForDay(NSCalendarDate *day, NSDateFormatter *format) {
>     NSString * const   dayTitle = [format stringFromDate:day];
>     NSMenu * const   daySubmenu = [[NSMenu alloc] initWithTitle:dayTitle];
>     NSMenuItem * const  dayItem = [[NSMenuItem alloc] initWithTitle:dayTitle action:NULL keyEquivalent:@""];
>
>     dayItem.representedObject = day;
>     dayItem.submenu = daySubmenu;
>
>     // Attach a binding to let the menu item auto-hide when used as the Today menu item.
>     MyAppDelegate * const  appDelegate = [NSApp delegate];
>
>     [dayItem bind:NSHiddenBinding toObject:appDelegate.myOverflowMenuController.sourceMenu withKeyPath:@"sourceMenu" options:@{NSValueTransformerBindingOption: appDelegate.myOverflowMenuController.isSourceMenuTransformer}];
>     return dayItem;
> }

(Good thing I already #imported my application delegate header to get access to the load-URL-from-menu-item action.) I crashed with:

> 2014-09-08 02:43:31.216 MyApp[28296:303] Controller cannot be nil
> 2014-09-08 02:43:31.279 MyApp[28296:303] (
> 	0   CoreFoundation                      0x00007fff8557625c __exceptionPreprocess + 172
> 	1   libobjc.A.dylib                     0x00007fff8d741e75 objc_exception_throw + 43
> 	2   CoreFoundation                      0x00007fff8557610c +[NSException raise:format:] + 204
> 	3   AppKit                              0x00007fff8cc37499 -[NSBinder addBinding:toController:withKeyPath:valueTransformer:options:] + 337
> 	4   AppKit                              0x00007fff8cc41c38 -[NSEditableBinder addBinding:toController:withKeyPath:valueTransformer:options:] + 51
> 	5   AppKit                              0x00007fff8cc32efb -[NSObject(NSKeyValueBindingCreation) bind:toObject:withKeyPath:options:] + 639
> 	6   Prairie                             0x0000000100006246 CreateMenuItemForDay + 678
> 	7   Prairie                             0x0000000100005a44 -[MyHistoryMenus notifyOnHistoryLoad:] + 708
> 	8   CoreFoundation                      0x00007fff85544e0c __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12

(Trimming out the reset of the stack.) The “sourceMenu” starts off as NIL and gets set by -prepareTodayHistoryMenu, which gets called when the array of WebHistory per-day menu items (and sub-menus) gets updated.

…

In the definition, instead of ‘@“sourceMenu”’, I used a constant. Looking at that in this e-mail, I realized I specified that property twice. I changed the second argument to “appDelegate.myOverflowMenuController” and it doesn’t crash. Now, none of the per-day web-history menus show up, not just the one for Today. (The one page I visited today does show up directly in the History, proving that the just-today web-history menu item mirroring system still works. All of the per-day menu items return YES through the custom value-transformer, or I screwed up the binding.

> @interface MyCustomTargettingTransformer : NSValueTransformer
>
> //! Starts as nil; the menu instance to be compared.
> @property (nonatomic) NSMenu *  targetMenu;
>
> @end
>
> @implementation MyCustomTargettingTransformer
>
> + (Class)transformedValueClass {
>     return [NSNumber class];
> }
>
> + (BOOL)allowsReverseTransformation {
>     return NO;
> }
>
> - (id)transformedValue:(id)value {
>     return [NSNumber numberWithBool:(self.targetMenu == value)];
> }
>
> @end


(This is within the *.m file for MyOverflowMenuController.) In the setter for the “sourceMenu” property of MyOverflowMenuController, I set the “targetMenu” property of the “isSourceMenuTransformer” property of MyOverflowMenuController. When a using a custom setter for a property, are KVO notifications sent?

—
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com

_______________________________________________

Cocoa-dev mailing list (email@hidden)

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:

This email sent to email@hidden


  • Follow-Ups:
    • Re: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
      • From: Daryle Walker <email@hidden>
References: 
 >Add bindings for custom menu items' isHidden to (an attribute of) a custom object? (From: Daryle Walker <email@hidden>)

  • Prev by Date: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
  • Next by Date: Re: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
  • Previous by thread: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
  • Next by thread: Re: Add bindings for custom menu items' isHidden to (an attribute of) a custom object?
  • Index(es):
    • Date
    • Thread