Show NSSegmentedControl menu when segment clicked, despite having set action

坚强是说给别人听的谎言 提交于 2020-01-21 03:19:28

问题


I have an NSSegmentedControl on my UI with 4 buttons. The control is connected to a method that will call different methods depending on which segment is clicked:

- (IBAction)performActionFromClick:(id)sender {
    NSInteger selectedSegment = [sender selectedSegment];
    NSInteger clickedSegmentTag = [[sender cell] tagForSegment:selectedSegment];

    switch (clickedSegmentTag) {
            case 0: [self showNewEventWindow:nil]; break;
            case 1: [self showNewTaskWindow:nil]; break;
            case 2: [self toggleTaskSplitView:nil]; break;
            case 3: [self showGearMenu]; break;
    }
}

Segment 4 has has a menu attached to it in the awakeFromNib method. I'd like this menu to drop down when the user clicks the segment. At this point, it only will drop if the user clicks & holds down on the menu. From my research online this is because of the connected action.

I'm presently working around it by using some code to get the origin point of the segment control and popping up the context menu using NSMenu's popUpContextMenu:withEvent:forView but this is pretty hacktastic and looks bad compared to the standard behavior of having the menu drop down below the segmented control cell.

Is there a way I can have the menu drop down as it should after a single click rather than doing the hacky context menu thing?


回答1:


I'm not sure of any built-in way to do this (though it really is a glaring hole in the NSSegmentedControl API).

My recommendation is to continue doing what you're doing popping up the context menu. However, instead of just using the segmented control's origin, you could position it directly under the segment (like you want) by doing the following:

NSPoint menuOrigin = [segmentedControl frame].origin;
menuOrigin.x = NSMaxX([segmentedControl frame]) - [segmentedControl widthForSegment:4];
// Use menuOrigin where you _were_ just using [segmentedControl frame].origin

It's not perfect or ideal, but it should get the job done and give the appearance/behavior your users expect.

(as an aside, NSSegmentedControl really needs a -rectForSegment: method)




回答2:


Subclass NSSegmentedCell, override method below, and replace the cell class in IB. (Requires no private APIs).

- (SEL)action
{
    //this allows connected menu to popup instantly (because no action is returned for menu button)
    if ([self tagForSegment:[self selectedSegment]]==0) {
        return nil;
    } else {
        return [super action];
    }
}



回答3:


This is the Swift version of the answer by J Hoover and the mod by Adam Treble. The override was not as intuitive as I thought it would be, so this will hopefully help someone else.

override var action : Selector {
        get {
            if self.menuForSegment(self.selectedSegment) != nil {
                return nil
            }
            return super.action
        }
        set {
            super.action = newValue
        }
    }



回答4:


widthForSegment: returns zero if the segment auto-sizes. If you're not concerned about undocumented APIs, there is a rectForSegment:

  • (NSRect)rectForSegment:(NSInteger)segment inFrame:(NSRect)frame;

But to answer the original question, an easier way to get the menu to pop up immediately is to subclass NSSegmentedCell and return 0 for (again, undocumented)

  • (double)_menuDelayTimeForSegment:(NSInteger)segment;


来源:https://stackoverflow.com/questions/1203698/show-nssegmentedcontrol-menu-when-segment-clicked-despite-having-set-action

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!