On Sep 8, 2014, at 3:10 AM, Daryle Walker <dary...@mac.com> wrote: > On Sep 8, 2014, at 1:12 AM, Daryle Walker <dary...@mac.com> wrote: [SNIP] >>> @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?
I tried some log-debugging: > - (id)transformedValue:(id)value { > NSLog(@"Calling -transformedValue with (%@) and self.targetMenu = %@.", > value, self.targetMenu); > return [NSNumber numberWithBool:(self.targetMenu == value)]; > } > 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; > NSLog(@"Creating menu item (%@) for day (%@).", dayItem, day); > > // 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 withKeyPath:MyKeyPathSourceMenu > options:@{NSValueTransformerBindingOption: > appDelegate.myOverflowMenuController.isSourceMenuTransformer}]; > return dayItem; > } and got: > 2014-09-08 03:54:10.776 MyApp[28618:303] Creating menu item (<NSMenuItem: > 0x6080000aa500 Monday, September 8, 2014, submenu: 0x608000263800 (Monday, > September 8, 2014)>) for day (2014-09-08 00:00:00 -0400). > 2014-09-08 03:54:10.777 MyApp[28618:303] Calling -transformedValue with > ((null)) and self.targetMenu = (null). > 2014-09-08 03:54:10.777 MyApp[28618:303] Creating menu item (<NSMenuItem: > 0x6080000aa6e0 Sunday, September 7, 2014, submenu: 0x608000269480 (Sunday, > September 7, 2014)>) for day (2014-09-07 00:00:00 -0400). > 2014-09-08 03:54:10.778 MyApp[28618:303] Calling -transformedValue with > ((null)) and self.targetMenu = (null). > 2014-09-08 03:54:10.778 MyApp[28618:303] Creating menu item (<NSMenuItem: > 0x6080000aa8c0 Saturday, September 6, 2014, submenu: 0x608000268ac0 > (Saturday, September 6, 2014)>) for day (2014-09-06 00:00:00 -0400). > 2014-09-08 03:54:10.779 MyApp[28618:303] Calling -transformedValue with > ((null)) and self.targetMenu = (null). > 2014-09-08 03:54:10.779 MyApp[28618:303] Creating menu item (<NSMenuItem: > 0x6080000aab00 Thursday, September 4, 2014, submenu: 0x608000268dc0 > (Thursday, September 4, 2014)>) for day (2014-09-04 00:00:00 -0400). > 2014-09-08 03:54:10.779 MyApp[28618:303] Calling -transformedValue with > ((null)) and self.targetMenu = (null). > 2014-09-08 03:54:10.782 MyApp[28618:303] Calling -transformedValue with > (<NSMenu: 0x608000263800> SKIP) and self.targetMenu = <NSMenu: > 0x608000263800> SKIP. > 2014-09-08 03:54:10.796 MyApp[28618:303] Calling -transformedValue with > (<NSMenu: 0x608000263800> SKIP) and self.targetMenu = <NSMenu: > 0x608000263800> SKIP. > 2014-09-08 03:54:10.796 MyApp[28618:303] Calling -transformedValue with > (<NSMenu: 0x608000263800> SKIP) and self.targetMenu = <NSMenu: > 0x608000263800> SKIP. > 2014-09-08 03:54:10.797 MyApp[28618:303] Calling -transformedValue with > (<NSMenu: 0x608000263800> SKIP) and self.targetMenu = <NSMenu: > 0x608000263800> SKIP. The test value and the target menu were always the same. Four runs of double NULL then four more with Today’s sub-menu (Sep. 8). So the transformer always returns YES and every per-day history menu item (and sub-menu) is hidden. How do I get “dayItem.submenu” to be the operand for the transformer? Did I mess up the binding? Or the transformer’s very design? Where did those 4 NULL runs come from? … Oh, for that last question, it’s because the “sourceMenu” property starts as NIL and I don’t change it until I create the per-day menus. It would have to be that way since I set the property to the sub-menu of the newest day (unless it’s before Today). — 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