Pop Up Menu in NSTableView

  • I have a table which has a list of items on which multiple actions can be performed.  Initially, I thought to use check box cells (there are only two possible actions that can be performed on each item right now - so the list of columns is manageable)  I thought a little further about the problem and it became apparent to me that, whilst there are only two possible actions right now, there might be twenty in the future - and, given the limited amount of space available to the table, twenty columns might become unmanageable.

    My idea, and whether it's good or not remains to be seen, is to use an NSPopUpButtonCell in the NSTableView instead.  When an action is selected from the NSPopUpButtonCell the check mark against its name would be toggled (multiple selections would be permitted, so multiple items in the menu might have a check mark next to their names).  In this manner, the table would only have two columns (item and menu) and the whole thing would be far more manageable.

    SO:
    Question 1. Is this possible, and am I even using the correct tool to do the job?
    Question 2. Are there any examples of how to achieve what I am trying to do?

    At the moment, my code looks like this (nothing is happening yet - I'm just trying to see if I can get the toggle to work - and I can't):
    - (IBAction)cellPreferenceChanged:(id)sender
    {
        [[sender selectedItem] setState:NSOnState];
    }

    cellPreferenceChanged is bound to the NSPopUpButtonCell in IB.  Oddly, despite this binding, this code results in [NSTableView selectedItem]: unrecognized selector sent to instance.  Why is this?  Surely, since it is the NSPopUpButtonCell that is bound it should be the NSPopUpButtonCell instance for the selected row that is sent?

    I am utterly perplexed, and clearly grasping the wrong end of the stick.  Any help would be most gratefully received.
  • On Mar 24, 2013, at 13:47 , Pascal Harris <45rpmlists...> wrote:

    > Question 1. Is this possible, and am I even using the correct tool to do the job?

    It's certainly possible to use popup buttons in a table column.

    It's questionable whether this is the right tool for this job. Popup buttons aren't great at showing multiple selections -- except when their menu is actually popped up, of course. It's irritatingly harder (though not exceedingly hard) to force the button to display a title that isn't the same as the (single) selected item.

    And what title to display? That's a significant problem, especially if "there might be twenty [actions] in the future".

    > At the moment, my code looks like this (nothing is happening yet - I'm just trying to see if I can get the toggle to work - and I can't):
    > - (IBAction)cellPreferenceChanged:(id)sender
    > {
    > [[sender selectedItem] setState:NSOnState];
    > }
    >
    > cellPreferenceChanged is bound to the NSPopUpButtonCell in IB.

    What do you mean by "bound"? Are you using a Cocoa binding, or do you just mean you've connected the "selector" connection from the cell to some target?

    > Oddly, despite this binding, this code results in [NSTableView selectedItem]: unrecognized selector sent to instance.  Why is this?  Surely, since it is the NSPopUpButtonCell that is bound it should be the NSPopUpButtonCell instance for the selected row that is sent?

    I dunno, but table views do things to cells that you might not always expect. If you set a breakpoint in your 'cellPreferenceChanged' method, what does the backtrace look like?
  • With regard to the title to display, I was thinking of 'Activated' if actions are selected (and by clicking on the pop up one can see exactly what actions are selected), and 'Deactivated' if no selections have been made.  That wording might need a little work, but that's the gist of it.

    As to your second question, I've connected the "selector" connection from the cell to 'cellPreferenceChanged'.  In my header I have:
        IBOutlet NSPopUpButtonCell* preferenceCell;
        - (IBAction)cellPreferenceChanged:(id)sender;
    and both are hooked up in IB.

    As to the third, how do I find out?

    Thanks for taking the time to look at this!

    On 24 Mar 2013, at 21:21, Quincey Morris <quinceymorris...> wrote:

    > On Mar 24, 2013, at 13:47 , Pascal Harris <45rpmlists...> wrote:
    >
    >> Question 1. Is this possible, and am I even using the correct tool to do the job?
    >
    > It's certainly possible to use popup buttons in a table column.
    >
    > It's questionable whether this is the right tool for this job. Popup buttons aren't great at showing multiple selections -- except when their menu is actually popped up, of course. It's irritatingly harder (though not exceedingly hard) to force the button to display a title that isn't the same as the (single) selected item.
    >
    > And what title to display? That's a significant problem, especially if "there might be twenty [actions] in the future".
    >
    >> At the moment, my code looks like this (nothing is happening yet - I'm just trying to see if I can get the toggle to work - and I can't):
    >> - (IBAction)cellPreferenceChanged:(id)sender
    >> {
    >> [[sender selectedItem] setState:NSOnState];
    >> }
    >>
    >> cellPreferenceChanged is bound to the NSPopUpButtonCell in IB.
    >
    > What do you mean by "bound"? Are you using a Cocoa binding, or do you just mean you've connected the "selector" connection from the cell to some target?
    >
    >> Oddly, despite this binding, this code results in [NSTableView selectedItem]: unrecognized selector sent to instance.  Why is this?  Surely, since it is the NSPopUpButtonCell that is bound it should be the NSPopUpButtonCell instance for the selected row that is sent?
    >
    > I dunno, but table views do things to cells that you might not always expect. If you set a breakpoint in your 'cellPreferenceChanged' method, what does the backtrace look like?
    >
  • On Mar 24, 2013, at 14:50 , Pax <45rpmlists...> wrote:

    You may be more successful if you use a view-based table rather than a cell-based table. One of the reasons we have view-based tables is to be able to avoid dealing with cells directly, in situations like this.

    However, if you've never used a view-based table before, the learning curve is fairly steep (though short).

    > As to your second question, I've connected the "selector" connection from the cell to 'cellPreferenceChanged'.  In my header I have:
    > IBOutlet NSPopUpButtonCell* preferenceCell;
    > - (IBAction)cellPreferenceChanged:(id)sender;
    > and both are hooked up in IB.

    Ugh, it doesn't look right to set up an outlet to a cell like this. Table views deal with cells at two points:

    1. When obtaining a cell for a given column. It uses the delegate method 'tableView:dataCellForTableColumn:row:' to get the cell, which may or may not be the one that's in the nib file. It does a certain amount of configuration of the cell after this method returns.

    2. When preparing the cell for a drawing specific row. It uses the delegate method 'tableView:willDisplayCell:forTableColumn:row:', and does more configuration before this method is called.

    So, rather than keeping an outlet to a cell that might not be the one used in any given case, you should use one of the above delegate methods to find out which cell is actually being used.

    That's if you need to know the cell in your code, which is doubtful.

    > As to the third, how do I find out?

    In Xcode, set a breakpoint in the action method. When you get to the breakpoint, go to the debugger console and type the "bt" command. That will give a backtrace you can copy and paste into an email.
  • Ah.  I shall have to look that up.  Do you know of any good examples for view based tables?

    As to the backtrace:
    * thread #1: tid = 0x2403, 0x00000001004cb55e DefaultPlugin`-[DefaultPlug cellPreferenceChanged:](self=0x000000010182a5b0, _cmd=0x0000000101826ea0, sender=0x000000010182cc00) + 30 at DefaultPlugin.m:233, stop reason = breakpoint 1.1
        frame #0: 0x00000001004cb55e DefaultPlugin`-[DefaultPlug cellPreferenceChanged:](self=0x000000010182a5b0, _cmd=0x0000000101826ea0, sender=0x000000010182cc00) + 30 at DefaultPlugin.m:233
        frame #1: 0x00007fff81d45989 AppKit`-[NSApplication sendAction:to:from:] + 342
        frame #2: 0x00007fff81d457e7 AppKit`-[NSControl sendAction:to:] + 85
        frame #3: 0x00007fff81d4571b AppKit`-[NSCell _sendActionFrom:] + 138
        frame #4: 0x00007fff81d45989 AppKit`-[NSApplication sendAction:to:from:] + 342
        frame #5: 0x00007fff81e7b37c AppKit`-[NSMenuItem _corePerformAction] + 406
        frame #6: 0x00007fff81e7b06a AppKit`-[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 133
        frame #7: 0x00007fff81b6828f AppKit`-[NSMenu _internalPerformActionForItemAtIndex:] + 36
        frame #8: 0x00007fff81b68117 AppKit`-[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:] + 135
        frame #9: 0x00007fff81e74175 AppKit`NSSLMMenuEventHandler + 342
        frame #10: 0x00007fff8ae81d1a HIToolbox`DispatchEventToHandlers(EventTargetRec*, OpaqueEventRef*, HandlerCallRec*) + 1206
        frame #11: 0x00007fff8ae811e9 HIToolbox`SendEventToEventTargetInternal(OpaqueEventRef*, OpaqueEventTargetRef*, HandlerCallRec*) + 410
        frame #12: 0x00007fff8ae96fc9 HIToolbox`SendEventToEventTarget + 40
        frame #13: 0x00007fff8aecdca9 HIToolbox`SendHICommandEvent(unsigned int, HICommand const*, unsigned int, unsigned int, unsigned char, void const*, OpaqueEventTargetRef*, OpaqueEventTargetRef*, OpaqueEventRef**) + 443
        frame #14: 0x00007fff8ae72a21 HIToolbox`SendMenuCommandWithContextAndModifiers + 59
        frame #15: 0x00007fff8ae729d3 HIToolbox`SendMenuItemSelectedEvent + 254
        frame #16: 0x00007fff8ae7285f HIToolbox`FinishMenuSelection(SelectionData*, MenuResult*, MenuResult*) + 94
        frame #17: 0x00007fff8afe8e79 HIToolbox`PopUpMenuSelectCore(MenuData*, Point, double, Point, unsigned short, unsigned int, Rect const*, unsigned short, unsigned int, Rect const*, Rect const*, __CFString const*, OpaqueMenuRef**, unsigned short*) + 1673
        frame #18: 0x00007fff8afe8794 HIToolbox`_HandlePopUpMenuSelection7 + 629
        frame #19: 0x00007fff81ef754b AppKit`_NSSLMPopUpCarbonMenu3 + 3916
        frame #20: 0x00007fff82258caa AppKit`_NSPopUpCarbonMenu3 + 39
        frame #21: 0x00007fff81ef650c AppKit`-[NSCarbonMenuImpl popUpMenu:atLocation:width:forView:withSelectedItem:withFont:withFlags:withOptions:] + 346
        frame #22: 0x00007fff820ba295 AppKit`-[NSPopUpButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 540
        frame #23: 0x00007fff8214aeb0 AppKit`-[NSTableView _tryCellBasedMouseDown:atRow:column:withView:] + 1650
        frame #24: 0x00007fff8214c32b AppKit`-[NSTableView mouseDown:] + 4540
        frame #25: 0x00007fff81d3a53e AppKit`-[NSWindow sendEvent:] + 6853
        frame #26: 0x00007fff81d36674 AppKit`-[NSApplication sendEvent:] + 5761
        frame #27: 0x00007fff81c4c24a AppKit`-[NSApplication run] + 636
        frame #28: 0x00007fff81bf0c06 AppKit`NSApplicationMain + 869
        frame #29: 0x00000001000035b2 TestApp`main(argc=3, argv=0x00007fff5fbff858) + 34 at main.m:13
        frame #30: 0x0000000100001c94 TestApp`start + 52

    On 24 Mar 2013, at 22:06, Quincey Morris <quinceymorris...> wrote:

    > On Mar 24, 2013, at 14:50 , Pax <45rpmlists...> wrote:
    >
    > You may be more successful if you use a view-based table rather than a cell-based table. One of the reasons we have view-based tables is to be able to avoid dealing with cells directly, in situations like this.
    >
    > However, if you've never used a view-based table before, the learning curve is fairly steep (though short).
    >
    >> As to your second question, I've connected the "selector" connection from the cell to 'cellPreferenceChanged'.  In my header I have:
    >> IBOutlet NSPopUpButtonCell* preferenceCell;
    >> - (IBAction)cellPreferenceChanged:(id)sender;
    >> and both are hooked up in IB.
    >
    > Ugh, it doesn't look right to set up an outlet to a cell like this. Table views deal with cells at two points:
    >
    > 1. When obtaining a cell for a given column. It uses the delegate method 'tableView:dataCellForTableColumn:row:' to get the cell, which may or may not be the one that's in the nib file. It does a certain amount of configuration of the cell after this method returns.
    >
    > 2. When preparing the cell for a drawing specific row. It uses the delegate method 'tableView:willDisplayCell:forTableColumn:row:', and does more configuration before this method is called.
    >
    > So, rather than keeping an outlet to a cell that might not be the one used in any given case, you should use one of the above delegate methods to find out which cell is actually being used.
    >
    > That's if you need to know the cell in your code, which is doubtful.
    >
    >> As to the third, how do I find out?
    >
    > In Xcode, set a breakpoint in the action method. When you get to the breakpoint, go to the debugger console and type the "bt" command. That will give a backtrace you can copy and paste into an email.
    >
    >
  • On Mar 24, 2013, at 15:16 , Pax <45rpmlists...> wrote:

    > Ah.  I shall have to look that up.  Do you know of any good examples for view based tables?

    Not really. You can work through the Apple documentation:

    https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Tab
    leView/Introduction/Introduction.html


    Much of the information is there, but it's woefully inadequate, particularly in regard to view identifiers, custom cell views, and bindings within cell views (as opposed to binding from cell views to the data model).

    > As to the backtrace:

    That looks unsurprising. In particular, after the table view passed the mouse-down to the popup button, it didn't get involved again later.

    Are you sure the action method isn't being called twice, with different senders? With the breakpoint in place, you can do "po sender" in the debugger whenever it breaks, and the result will tell you the actual class of 'sender'. Then click the Continue button to see if you get to the breakpoint again.
  • On Mar 24, 2013, at 5:26 PM, Quincey Morris wrote:

    >> As to the backtrace:
    >
    > That looks unsurprising. In particular, after the table view passed the mouse-down to the popup button, it didn't get involved again later.
    >
    > Are you sure the action method isn't being called twice, with different senders? With the breakpoint in place, you can do "po sender" in the debugger whenever it breaks, and the result will tell you the actual class of 'sender'. Then click the Continue button to see if you get to the breakpoint again.

    Inspecting the sender is the trick--and don't be surprised at the result. I ran into this some time ago. Is the sender is the NSTableView, instead instead of the NSPopupButtonCell? IIRC, the only way to do what you want with a popup is to connect the action to each and every individual menu item rather than the cell as a whole.

    Also, I don't think you will find bindings useful in this case at all. The data source and delegate methods will help most.

    HTH,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
  • First of all, I don't want to dissuade you from considering a view-based table as Quincey suggested, if that works for you.

    As Quincey said, NSPopUpButtonCell can be used in tables.  I've done it many times.

    On 2013 Mar 24, at 13:47, Pascal Harris <45rpmlists...> wrote:

    > cellPreferenceChanged is bound to the NSPopUpButtonCell in IB.

    Echoing Quincey's questioning what you mean by that, you can't use Cocoa bindings on an action.  Cocoa bindings are, in my experience, good to use with tables, but the bindings for a popup button cell are not obvious:

    • NSTableColumn 'selected object' must be bound to 'arranged objects' of array controller.  (This is always true, regardless of what the cell type is.)

    • For dynamic menu contents, NSPopupButtonCell 'content' may be bound to an object and key path which returns an array containing objects which are assignable to the 'selected object'.

    • NSPopupButtonCell 'content values' may be bound to an array giving the strings you want displayed in the table, by appending an additional key onto the key path which 'content' is bound to.  The additional key must be a method which which can be sent to the objects in the array that 'contents' is bound to, and return a string.

    I'd be curious to know if you find the view-based table easier than this.  Maybe I should learn to do it that way.
  • I'm going to try both types of table - but I need to find good documentation, preferably with examples, first.  Right now, however, I've decided that this is useful future functionality, and not core to the software.  So I've gone back to plan a - checkboxes in NSTableView (still need to work that out too, but it should be simpler), and I've put a load of extra code in to support the additional flexibility that I'd like to have in the future (so that I don't have to rewrite my plugins once I get this going just as I'd like).

    I'm sorry to be such a wimp - but right now this is just 'too hard'.

    Once I've worked out how the code will look, I'll drop you a personal email with the code that I used (rather than cluttering up this forum by resurrecting what will, by then, be an old thread).

    On 25 Mar 2013, at 17:05, Jerry Krinock <jerry...> wrote:

    >
    > First of all, I don't want to dissuade you from considering a view-based table as Quincey suggested, if that works for you.
    >
    > As Quincey said, NSPopUpButtonCell can be used in tables.  I've done it many times.
    >
    > On 2013 Mar 24, at 13:47, Pascal Harris <45rpmlists...> wrote:
    >
    >> cellPreferenceChanged is bound to the NSPopUpButtonCell in IB.
    >
    > Echoing Quincey's questioning what you mean by that, you can't use Cocoa bindings on an action.  Cocoa bindings are, in my experience, good to use with tables, but the bindings for a popup button cell are not obvious:
    >
    > • NSTableColumn 'selected object' must be bound to 'arranged objects' of array controller.  (This is always true, regardless of what the cell type is.)
    >
    > • For dynamic menu contents, NSPopupButtonCell 'content' may be bound to an object and key path which returns an array containing objects which are assignable to the 'selected object'.
    >
    > • NSPopupButtonCell 'content values' may be bound to an array giving the strings you want displayed in the table, by appending an additional key onto the key path which 'content' is bound to.  The additional key must be a method which which can be sent to the objects in the array that 'contents' is bound to, and return a string.
    >
    >
    > I'd be curious to know if you find the view-based table easier than this.  Maybe I should learn to do it that way.
  • On Mar 25, 2013, at 11:05 AM, Jerry Krinock wrote:

    > • NSTableColumn 'selected object' must be bound to 'arranged objects' of array controller.  (This is always true, regardless of what the cell type is.)

    I don't understand this statement--I didn't think that NSTableColumn has a "selected object" binding--there doesn't seem to be one in the bindings inspector--and if it did, what would it do? Did you mean "value" binding, or NSPopupButtonCell, instead?

    > • For dynamic menu contents, NSPopupButtonCell 'content' may be bound to an object and key path which returns an array containing objects which are assignable to the 'selected object'.

    I can't make sense of this either--just the last bit about "assignable to the 'selected object'."

    > • NSPopupButtonCell 'content values' may be bound to an array giving the strings you want displayed in the table, by appending an additional key onto the key path which 'content' is bound to.  The additional key must be a method which which can be sent to the objects in the array that 'contents' is bound to, and return a string.

    Are you simply describing here the details of popup bindings, or something specific to getting bindings to work with an NSPopupButtonCell in an NSTableView? I don't recall ever being successful at using bindings in this case, but then maybe I didn't try hard enough or just gave up too soon and developed a belief that it wasn't possible...

    TIA,

    Keary Suska
    Esoteritech, Inc.
    "Demystifying technology for your home or business"
previous month march 2013 next month
MTWTFSS
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Go to today