NSCell interiorBackgroundStyle wrong?

  • Perhaps only an Apple employee like Corbin can really answer this...

    Consider an NSCell where:
    • backgroundStyle is always set to NSBackgroundLight
    • highlightColorWithFrame:inView: returns nil so that the cell itself draws no highlight
    • drawsBackground is NO

    In this case, shouldn't the interiorBackgroundStyle be NSBackgroundLight when the cell is both highlighted and not highlighted?

    Trivial testing shows that it is actually NSBackgroundDark when the cell is highlighted and Light when not. In the case of NSTextField, this results in the wrong text color being used when the cell is highlighted (it uses white instead of black).

    --
    Seth Willits
  • On Fri, May 31, 2013, at 11:47 AM, Seth Willits wrote:
    >
    > Perhaps only an Apple employee like Corbin can really answer this...
    >
    >
    > Consider an NSCell where:
    > • backgroundStyle is always set to NSBackgroundLight
    > • highlightColorWithFrame:inView: returns nil so that the cell itself draws no highlight
    > • drawsBackground is NO
    >
    > In this case, shouldn't the interiorBackgroundStyle be NSBackgroundLight
    > when the cell is both highlighted and not highlighted?

    If you override -highlightColorWithFrame:inView:, you also need to
    override -interiorBackgroundStyle. NSCell doesn't look at the return
    value of -highlightColorWithFrame:inView: to determine whether it has a
    light or dark background.

    Furthermore, a lot of the NSCell methods regarding highlighting have
    been completely bypassed, without any mention of this fact (Apple folk,
    see rdar://problem/12011090, which deals with NSTextCell in particular
    completely avoiding any methods with "hilight" in their name).

    --Kyle Sluder
  • On May 31, 2013, at 1:25 PM, Kyle Sluder wrote:

    > If you override -highlightColorWithFrame:inView:, you also need to
    > override -interiorBackgroundStyle. NSCell doesn't look at the return
    > value of -highlightColorWithFrame:inView: to determine whether it has a
    > light or dark background.

    After having been looking at this and more for a further two hours, in retrospect the issue is minor and somewhat logical. If you override one, you simply are expected to override the other, but the undocumented relationship just caught me off guard. I'll file a report on that.

    And as I've now discovered in my further exploration, -interiorBackgroundStyle is used for double duty. Not only is used to describe the *cell's* background, it's also used to describe the *field editor's* background during editing. THAT is weird and undocumented.

    In my case, since the cell does not draw its own background, the interiorBackgroundStyle should simply be whatever the backgroundStyle is, right? If the background is dark, then the cell's interior background is effectively dark, and the cell should draw in light colors. However, literally returning the self.backgroundStyle from -interiorBackgroundStyle is incorrect because when you start editing the cell, the field editor will be using Light color text since editing calls the private method _textAttributes which uses the opposite of interiorBackgroundStyle to determine the text color. (iBS is dark so it uses white in the field editor.)

    So the correct solution involves overriding the editing-related methods and setting an internal flag to know when editing is happening, and return NSBackgroundLight when editing, otherwise return self.backgroundStyle. Ugh.

    I think _textAttributes should be made public, especially given that -attributedStringValue is never even called.

    --
    Seth Willits
  • On Fri, May 31, 2013, at 01:42 PM, Seth Willits wrote:
    > In my case, since the cell does not draw its own background, the
    > interiorBackgroundStyle should simply be whatever the backgroundStyle is,
    > right?

    backgroundStyle refers to the style of the _container_ relative to the
    _control_. That's why it has a setter.

    interiorBackgroundStyle refers to the style of _some of the cell's
    drawing_ related to _some of the cell's other drawing_. That's why it
    doesn't have a setter, because all the state that affects it is
    contained within the cell.

    Take the buttons on the Safari bookmarks bar, for example. The toolbar
    probably sets their background style to NSBackgroundStyleLight. This
    makes sense, because the _container_ knows what kind of background it is
    drawing behind the controls. The button cells themselves probably return
    NSBackgroundStyleRaised for their -interiorBackgroundStyle, because that
    corresponds to the relationship between the background the cell draws
    (none) and the label it draws (etched in).

    > So the correct solution involves overriding the editing-related methods
    > and setting an internal flag to know when editing is happening, and
    > return NSBackgroundLight when editing, otherwise return
    > self.backgroundStyle. Ugh.

    Can you instead just override -setUpFieldEditorAttributes: to call super
    and then trump its decisions?

    >
    > I think _textAttributes should be made public, especially given that
    > -attributedStringValue is never even called.

    Yes, the relationship between attributed string values, text cells, and
    the field editor is quite murky, and we could cut through it all if we
    had direct access to the cell's text system.

    --Kyle Sluder
  • On May 31, 2013, at 2:11 PM, Kyle Sluder wrote:

    > On Fri, May 31, 2013, at 01:42 PM, Seth Willits wrote:
    >> In my case, since the cell does not draw its own background, the
    >> interiorBackgroundStyle should simply be whatever the backgroundStyle is,
    >> right?
    >
    > backgroundStyle refers to the style of the _container_ relative to the
    > _control_. That's why it has a setter.
    >
    > interiorBackgroundStyle refers to the style of _some of the cell's
    > drawing_ related to _some of the cell's other drawing_. That's why it
    > doesn't have a setter, because all the state that affects it is
    > contained within the cell.

    I'm very well aware of all of that. I'm not sure what gave the impression I don't.

    My point is that if the cell itself has no background, then the cell's "interior background" has to reflect whatever the container's background is. Hence interiorBackgroundStyle would return the same value as backgroundStyle which is set from wherever.

    (The problem is that interiorBackgroundStyle is doing dual duty so you have to handle the field editor as well…)

    >> So the correct solution involves overriding the editing-related methods
    >> and setting an internal flag to know when editing is happening, and
    >> return NSBackgroundLight when editing, otherwise return
    >> self.backgroundStyle. Ugh.
    >
    > Can you instead just override -setUpFieldEditorAttributes: to call super
    > and then trump its decisions?

    I could, but I consider that a bigger can of ugly. What if something else calls interiorBackgroundStyle and makes the same mistake? Considering NSCell/NSTextFieldCell itself tweaks the interiorBackgroundStyle rather than handling it in -setupFieldEditorAttributes:, I don't think it's a far fetched idea.

    Overall, it's actually simple. It just took me hours to figure out how all the pieces fit together:

    - (NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
    {
    return nil;
    }

    - (NSBackgroundStyle)interiorBackgroundStyle;
    {
    if (mIsEditing) {
      return NSBackgroundStyleLight;
    }

    return self.backgroundStyle;
    }

    - (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent;
    {
    mIsEditing = YES;
    [super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent];
    }

    - (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength;
    {
    mIsEditing = YES;
    [super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
    }

    - (void)endEditing:(NSText *)textObj;
    {
    mIsEditing = NO;
    [super endEditing:textObj];
    }

    With the above, the cell can be dumped into a standard or customized table view and its backgroundStyle set to either Light or Dark and it'll adapt correctly, unlike NSTextFieldCell right out of the box.

    (Related: I haven't come across reasoning for why a cell draws the highlight color within its drawInterior…: method. Where does that ever get used? In a table view, for instance, the table view draws the selection highlight anyway.)

    -- Seth
previous month may 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