diff --git a/ChangeLog b/ChangeLog index d3b00f135..44d709b0b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2011-08-15 Eric Wasylishen + + * Headers/Additions/GNUstepGUI/GSTheme.h: New GSThemeMargins struct. + Rename buttonBorderForCell:style:state: to buttonMarginsForCell:style:state: + and return the top/bottom/left/right margins instead of just two values. + (This is a theme API break.) + * Source/GSThemeDrawing.m: Implement buttonMarginsForCell:style:state:. + Also reduce the top and left margin of the default button by 1 pt, to + better reflect the button's appearance. + * Source/GSGuiPrivate.h: Add a GSRoundTowardsNegativeInfinity function + * Source/NSButtonCell.m (-drawImage:withFrame:inView:): Round the image + position down and to the right, as this seems to look the best. + * Source/NSButtonCell.m (-cellSize): Call new buttonMarginsForCell: method + * Source/NSButtonCell.m (-drawingRectForBounds:): Call new + buttonMarginsForCell: method + 2011-08-15 Eric Wasylishen * Source/NSImage.m (GSResolutionOfImageRep): Specify behaviour when the diff --git a/Headers/Additions/GNUstepGUI/GSTheme.h b/Headers/Additions/GNUstepGUI/GSTheme.h index 94e5399e5..e16663167 100644 --- a/Headers/Additions/GNUstepGUI/GSTheme.h +++ b/Headers/Additions/GNUstepGUI/GSTheme.h @@ -287,7 +287,17 @@ APPKIT_EXPORT NSString *GSProgressIndicatorBarDeterminate; */ APPKIT_EXPORT NSString *GSColorWell; - +/** + * Structure to describe the size of top/bottom/left/right margins inside + * a button + */ +typedef struct GSThemeMargins +{ + CGFloat left; + CGFloat right; + CGFloat top; + CGFloat bottom; +} GSThemeMargins; /** * This defines how the values in a tile array should be used when @@ -732,9 +742,9 @@ APPKIT_EXPORT NSString *GSThemeWillDeactivateNotification; /** * Amount by which the button is inset by the border. */ -- (NSSize) buttonBorderForCell: (NSCell*)cell - style: (int)style - state: (GSThemeControlState)state; +- (GSThemeMargins) buttonMarginsForCell: (NSCell*)cell + style: (int)style + state: (GSThemeControlState)state; /** * Draws the indicator (normally a dotted rectangle) to show that diff --git a/Source/GSGuiPrivate.h b/Source/GSGuiPrivate.h index 281577091..e40a84e56 100644 --- a/Source/GSGuiPrivate.h +++ b/Source/GSGuiPrivate.h @@ -88,5 +88,20 @@ static inline CGFloat GSRoundTowardsInfinity(CGFloat x) return floor(x + 0.5); } +/** + * Rounds to the nearest integer, and in the case of ties, round to the + * smaller integer. + * + * For example: + * GSRoundTowardsNegativeInfinity(0.8) == 1.0 + * GSRoundTowardsNegativeInfinity(0.5) == 0.0 + * GSRoundTowardsNegativeInfinity(0.1) == 0.0 + * GSRoundTowardsNegativeInfinity(-2.5) == -3.0 + */ +static inline CGFloat GSRoundTowardsNegativeInfinity(CGFloat x) +{ + return ceil(x - 0.5); +} + #endif /* _GNUstep_H_GSGuiPrivate */ diff --git a/Source/GSThemeDrawing.m b/Source/GSThemeDrawing.m index 202fd045b..682105685 100644 --- a/Source/GSThemeDrawing.m +++ b/Source/GSThemeDrawing.m @@ -189,12 +189,13 @@ } } -- (NSSize) buttonBorderForCell: (NSCell*)cell - style: (int)style - state: (GSThemeControlState)state +- (GSThemeMargins) buttonMarginsForCell: (NSCell*)cell + style: (int)style + state: (GSThemeControlState)state { GSDrawTiles *tiles = nil; NSString *name = [self nameForElement: cell]; + GSThemeMargins margins; if (name == nil) { @@ -214,36 +215,48 @@ case NSRoundRectBezelStyle: case NSTexturedRoundedBezelStyle: case NSRoundedBezelStyle: - return NSMakeSize(5, 5); + margins.left = 5; margins.top = 5; margins.right = 5; margins.bottom = 5; + return margins; case NSTexturedSquareBezelStyle: - return NSMakeSize(3, 3); + margins.left = 3; margins.top = 3; margins.right = 3; margins.bottom = 3; + return margins; case NSSmallSquareBezelStyle: case NSRegularSquareBezelStyle: case NSShadowlessSquareBezelStyle: - return NSMakeSize(2, 2); + margins.left = 2; margins.top = 2; margins.right = 2; margins.bottom = 2; + return margins; case NSThickSquareBezelStyle: - return NSMakeSize(3, 3); + margins.left = 3; margins.top = 3; margins.right = 3; margins.bottom = 3; + return margins; case NSThickerSquareBezelStyle: - return NSMakeSize(4, 4); + margins.left = 4; margins.top = 4; margins.right = 4; margins.bottom = 4; + return margins; case NSCircularBezelStyle: - return NSMakeSize(5, 5); + margins.left = 5; margins.top = 5; margins.right = 5; margins.bottom = 5; + return margins; case NSHelpButtonBezelStyle: - return NSMakeSize(2, 2); + margins.left = 2; margins.top = 2; margins.right = 2; margins.bottom = 2; + return margins; case NSDisclosureBezelStyle: case NSRoundedDisclosureBezelStyle: case NSRecessedBezelStyle: // FIXME - return NSMakeSize(0, 0); + margins.left = 0; margins.top = 0; margins.right = 0; margins.bottom = 0; + return margins; default: - return NSMakeSize(3, 3); + margins.left = 2; margins.top = 2; margins.right = 3; margins.bottom = 3; + return margins; } } else { - // FIXME: We assume the button's top and right padding are the same as - // its bottom and left. - return NSMakeSize(tiles->contentRect.origin.x, - tiles->contentRect.origin.y); + // FIXME: Move this code to a method in GSDrawTiles? + // FIXME: Not correct, need to get the content area of the draw tiles + margins.left = tiles->rects[TileCL].size.width; + margins.top = tiles->rects[TileTM].size.height; + margins.right = tiles->rects[TileCR].size.width; + margins.bottom = tiles->rects[TileBM].size.height; + return margins; } } diff --git a/Source/NSButtonCell.m b/Source/NSButtonCell.m index cfb3e42a7..b4c9db006 100644 --- a/Source/NSButtonCell.m +++ b/Source/NSButtonCell.m @@ -966,37 +966,51 @@ typedef struct _GSButtonCellFlags // Draw image if (imageToDisplay != nil) { - NSSize size; - NSPoint position; - - size = [imageToDisplay size]; - position.x = MAX(NSMidX(cellFrame) - (size.width / 2.), 0.); - position.y = MAX(NSMidY(cellFrame) - (size.height / 2.), 0.); - + const NSSize size = [imageToDisplay size]; + + /* Calculate an offset from the cellFrame origin */ + + NSPoint offset = NSMakePoint((NSWidth(cellFrame) - size.width) / 2.0, + (NSHeight(cellFrame) - size.height) / 2.0); + + /* Pixel-align the offset */ + + if (controlView) + { + NSPoint inBase = [controlView convertPointToBase: offset]; + + // By convention we will round down and to the right. + // With the standard button design this looks good + // because the bottom and right edges of the button look 'heavier' + // so if the image's center must be offset from the button's geometric + // center, it looks beter if it's closer to the 'heavier' part + + inBase.x = GSRoundTowardsInfinity(inBase.x); + inBase.y = [controlView isFlipped] ? + GSRoundTowardsInfinity(inBase.y) : + GSRoundTowardsNegativeInfinity(inBase.y); + + offset = [controlView convertPointFromBase: inBase]; + } + /* * Images are always drawn with their bottom-left corner at the origin * so we must adjust the position to take account of a flipped view. */ if ([controlView isFlipped]) - { - position.y += size.height; - } - - /* Pixel-align the drawing point */ - if (controlView) { - position = [controlView convertPointToBase: position]; - } - position = NSMakePoint(GSRoundTowardsInfinity(position.x), GSRoundTowardsInfinity(position.y)); - if (controlView) - { - position = [controlView convertPointFromBase: position]; + offset.y += size.height; } - [[GSTheme theme] drawImage: imageToDisplay - inButtonCell: self - withFrame: cellFrame - position: position]; + { + const NSPoint position = NSMakePoint(cellFrame.origin.x + offset.x, + cellFrame.origin.y + offset.y); + + [[GSTheme theme] drawImage: imageToDisplay + inButtonCell: self + withFrame: cellFrame + position: position]; + } } } @@ -1300,7 +1314,7 @@ typedef struct _GSButtonCellFlags - (NSSize) cellSize { NSSize s; - NSSize borderSize; + GSThemeMargins border; unsigned mask; NSImage *imageToDisplay; NSAttributedString *titleToDisplay; @@ -1400,13 +1414,18 @@ typedef struct _GSButtonCellFlags buttonState = GSThemeSelectedState; } - borderSize = [[GSTheme theme] buttonBorderForCell: self - style: _bezel_style - state: buttonState]; + border = [[GSTheme theme] buttonMarginsForCell: self + style: _bezel_style + state: buttonState]; } else - borderSize = NSZeroSize; - + { + border.left = 0; + border.top = 0; + border.right = 0; + border.bottom = 0; + } + /* Add an additional 6 pixels horizontally so that the text is not * too near the boundaries of the button. Without them, autosized * buttons look too tiny and crammed. This might be made @@ -1419,12 +1438,13 @@ typedef struct _GSButtonCellFlags if ((_cell.is_bordered && (_cell.image_position != NSImageOnly)) || _cell.is_bezeled) { - borderSize.width += 6; + border.left += 6; + border.right += 6; } // Add border size - s.width += 2 * borderSize.width; - s.height += 2 * borderSize.height; + s.width += border.left + border.right; + s.height += border.top + border.bottom; return s; } @@ -1433,7 +1453,7 @@ typedef struct _GSButtonCellFlags { if (_cell.is_bordered) { - NSSize borderSize; + GSThemeMargins border; unsigned mask; GSThemeControlState buttonState = GSThemeNormalState; NSRect interiorFrame; @@ -1464,10 +1484,16 @@ typedef struct _GSButtonCellFlags buttonState = GSThemeSelectedState; } - borderSize = [[GSTheme theme] buttonBorderForCell: self - style: _bezel_style - state: buttonState]; - interiorFrame = NSInsetRect(theRect, borderSize.width, borderSize.height); + border = [[GSTheme theme] buttonMarginsForCell: self + style: _bezel_style + state: buttonState]; + + interiorFrame = theRect; + interiorFrame.origin.x += border.left; + interiorFrame.size.width -= border.left + border.right; + interiorFrame.origin.y += ([_control_view isFlipped] ? + border.top : border.bottom); + interiorFrame.size.height -= border.bottom + border.top; /* Pushed in buttons contents are displaced to the bottom right 1px. */ if (mask & NSPushInCellMask)