Using full width of NSPopupButton for text, no arr

2019-08-19 02:17发布

问题:

I like to display an NSPopupButton that has no arrows. I like to use that space where the arrows would be for the label text. That doesn't seem to work by default.

Here's the text (which is "01234567890") with the arrows enabled:

And here with no arrows:

As you can see, the space the arrows would occupy is not used by the text.

How can I use the full content width for the text?

I've inspected the control with F-Script but found that the control's subviews array appears to be empty - I had expected to find two subviews, and if that were the case, I'd remove the one for the arrows and make the one for the text wider.

回答1:

One way to accomplish this is to create an NSPopupButtonCell subclass and draw the text yourself in its drawTitle:withFrame:inView: method.

Here's the essence of it:

@interface MyCell : NSPopUpButtonCell
    @property NSDictionary<NSString*,id> *attrs;
@end

@implementation MyCell

-(NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView
{
    CGSize cs = controlView.bounds.size;
    CGPoint or = frame.origin;
    CGSize fs = frame.size;
    const int th = fs.height;
    const int inset = th * 2 / 3;
    const int indent = self.image ? th+6 : 0; // assuming the img is on the left of the text, we'll indent the text
    frame = CGRectMake (inset+indent, or.y, cs.width-2*inset-indent, fs.height);
    return [super drawTitle:title withFrame:frame inView:controlView];
}

@end

Note: This code does not cover all possible cases of adding an icon to the menu items - it only handles the most common case of having a fitting icon on the left, and that's still a bit wonky because of using indenting values I found by trial-and-error. Adjust them yourself as needed.

See also the comment below by @NSGod about setting up the cell's class in your nib or storyboard.

Update: Optimized the code to need only a subclass of the NSPopUpButtonCell but not of NSPopUpButton, thanks to @NSGod.

Update 2: Instead of implementing drawTitleWithFrame:inView: I now overwrite drawTitle:withFrame:inView: because that gives me the correctly centered frame for drawing and makes the whole code shorter.