From effd2e71de06fdc483dd5c110dd90833772e7972 Mon Sep 17 00:00:00 2001 From: qmathe Date: Wed, 16 Nov 2011 10:32:15 +0000 Subject: [PATCH] Improved the menu theming to support some common menu look variations. Fixed bug #34792 too. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@34174 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 22 ++++ Headers/Additions/GNUstepGUI/GSTheme.h | 110 ++++++++++++++++- Source/GSTheme.m | 1 + Source/GSThemeDrawing.m | 161 +++++++++++++++++++------ Source/NSMenuItemCell.m | 27 +++-- 5 files changed, 268 insertions(+), 53 deletions(-) diff --git a/ChangeLog b/ChangeLog index f68cffaba..76a7d70de 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2011-11-16 Quentin Mathe + + Improved the menu theming to support some common menu look variations. + Fixed bug #34792 too. + * Headers/Additions/GNUstepGUI/GSTheme.h + * Source/GSTheme.m + * Source/GSThemeDrawing.m + (-menuBackgroundColor, -menuItemBackgroundColor, -menuBorderColor + -menuBorderColorForEdge:isHorizontal:, + -drawsBorderForMenuItemCell:state:isHorizontal:, + -drawTitleForMenuItemCell:withFrame:inView:state:isHorizontal:, + -menuSeparatorInset): Added. + (-menuBarBackgroundColor, -menuBarBorderColor): Added but not yet used. + (-menuSeparatorColor): Renamed -menuSeparatorItemColor. + * Source/NSMenuItemCell.m + (-backgroundColor:): Changed to get the background color from + -menuItemBackgroundColor. + (-themeControlState): Added. + (-drawTitleWithFrame:inView:): Moved drawing code to GSTheme. + Made separator menu item themable, by moving the drawing code from + NSMenuItemCell to a new method in GSThemeDrawing. + 2011-11-13 Fred Kiefer * Source/NSImage.m (-bestRepresentationForDevice:): Fix last diff --git a/Headers/Additions/GNUstepGUI/GSTheme.h b/Headers/Additions/GNUstepGUI/GSTheme.h index e0df32878..a73eac7e6 100644 --- a/Headers/Additions/GNUstepGUI/GSTheme.h +++ b/Headers/Additions/GNUstepGUI/GSTheme.h @@ -877,40 +877,138 @@ APPKIT_EXPORT NSString *GSThemeWillDeactivateNotification; roundedLeft: (BOOL)roundedLeft roundedRight: (BOOL)roundedRight; +/** + *

Returns the color used to draw a menu view background.

+ * + *

By default, looks up the color named menuBackgroundColor, + * otherwise returns the window background color.

+ * + *

The returned color is used by + * -drawBackgroundForMenuView:withFrame:dirtyRect:horizontal:

+ * + *

Can be overriden in subclasses to return a custom color.

+ */ +- (NSColor *) menuBackgroundColor; +/** + *

Returns the color used to draw a menu item background.

+ * + * The menu item background is drawn atop the menu background. + * + *

By default, looks up the color named menuItemBackgroundColor, + * otherwise returns the control background color.
+ * When selected or highlighted, the background color is provided by + * [NSColor+selectedMenuItemColor].

+ * + *

The returned value used by + * -drawBorderAndBackgroundForMenuItemCell:withFrame:inView:state:isHorizontal: + * and [NSMenuItemCell-backgroundColor].

+ * + *

Can be overriden in subclasses to return a custom color.

+ */ +- (NSColor *) menuItemBackgroundColor; +- (NSColor *) menuBarBackgroundColor; +- (NSColor *) menuBarBorderColor; +/** + *

Returns the color used to draw a menu view border.

+ * + *

By default, looks up the color named menuBorderColor, + * otherwise returns the dark gray color.

+ * + *

The returned color is used by + * -drawBackgroundForMenuView:withFrame:dirtyRect:horizontal:

+ * + *

Can be overriden in subclasses to return a custom color.

+ */ +- (NSColor *) menuBorderColor; +/** + *

Returns a color to draw each edge in a menu view border.

+ * + *

By default, returns -menuBorderColor for the upper and left edges of a + * vertical menu, or for the bottom edge of a horizontal one.

+ * + *

The returned edge color is used by + * -drawBackgroundForMenuView:withFrame:dirtyRect:horizontal:

+ * + *

Can be overriden in subclasses to return a custom color per edge.

+ */ +- (NSColor *) menuBorderColorForEdge: (NSRectEdge)edge + isHorizontal: (BOOL)horizontal; - (void) drawBackgroundForMenuView: (NSMenuView*)menuView withFrame: (NSRect)bounds dirtyRect: (NSRect)dirtyRect horizontal: (BOOL)horizontal; - +/** + *

Returns whether the menu item border should be drawn or not.

+ * + *

By default, returns [NSMenuItemCell-isBordered] value. The value is NO + * when the menu is horizontal, YES when vertical.

+ * + *

The returned value used by + * -drawBorderAndBackgroundForMenuItemCell:withFrame:inView:state:isHorizontal:

+ * + *

Can be overriden in subclasses.

+ */ +- (BOOL) drawsBorderForMenuItemCell: (NSMenuItemCell *)cell + state: (GSThemeControlState)state + isHorizontal: (BOOL)horizontal; // menu item cell drawing method - (void) drawBorderAndBackgroundForMenuItemCell: (NSMenuItemCell *)cell withFrame: (NSRect)cellFrame inView: (NSView *)controlView state: (GSThemeControlState)state isHorizontal: (BOOL)isHorizontal; +/** + *

Draws the menu item title.

+ * + *

Can be overriden to customize the text font, size and position.
+ * You can use [[cell menuItem] title] to get the title.

+ * + *

The title color is mapped to the theme state as described below:

+ * + * GSThemeSelectedState[NSColor+selectedMenuItemTextColor] + * GSThemeDisabledState[NSColor+controlTextColor] or + * [NSColor+disabledControlTextColor] + * + */ +- (void) drawTitleForMenuItemCell: (NSMenuItemCell *)cell + withFrame: (NSRect)cellFrame + inView: (NSView *)controlView + state: (GSThemeControlState)state + isHorizontal: (BOOL)isHorizontal; /** *

Returns the color used to draw a separator line in a menu.

* - *

By default, looks up the color named menuSeparatorItemColor, - * otherwise returns the black color.

+ *

By default, looks up the color named menuSeparatorColor, + * otherwise returns nil.

* *

The returned color is used by * -drawSeparatorItemForMenuItemCell:withFrame:inView:isHorizontal:

* *

Can be overriden in subclasses to return a custom color.

*/ -- (NSColor *) menuSeparatorItemColor; +- (NSColor *) menuSeparatorColor; +/** + *

Returns the left and right inset used to draw a separator line in a menu.

+ * + *

By default, returns 3.0.

+ * + *

The returned color is used by + * -drawSeparatorItemForMenuItemCell:withFrame:inView:isHorizontal:

+ * + *

Can be overriden in subclasses to return a custom value.

+ */ +- (CGFloat) menuSeparatorInset; /** *

Draws a separator between normal menu items in a menu.

* *

Each separator corresponds to a menu item that returns YES to * -isSeparatorItem

* - *

You can provide an image tile named GSMenuSeparatoritem to + *

You can provide an image tile named GSMenuSeparatorItem to * draw the separator.
* Can be overriden in subclasses to customize the drawing.

* - *

See also -menuSeparatorItemColor

+ *

See also -menuSeparatorColor and -menuSeparatorInset

*/ - (void) drawSeparatorItemForMenuItemCell: (NSMenuItemCell *)cell withFrame: (NSRect)cellFrame diff --git a/Source/GSTheme.m b/Source/GSTheme.m index 035efce35..fbf6df640 100644 --- a/Source/GSTheme.m +++ b/Source/GSTheme.m @@ -393,6 +393,7 @@ typedef struct { cls = self; } instance = [[cls alloc] initWithBundle: bundle]; + NSLog(@"Instantiated %@ from %@ in %@", instance, cls, [bundle bundlePath]); return AUTORELEASE(instance); } diff --git a/Source/GSThemeDrawing.m b/Source/GSThemeDrawing.m index 77aadceef..bb0564df2 100644 --- a/Source/GSThemeDrawing.m +++ b/Source/GSThemeDrawing.m @@ -803,6 +803,75 @@ } } +- (NSColor *) menuBackgroundColor +{ + NSColor *color = [self colorNamed: @"menuBackgroundColor" + state: GSThemeNormalState]; + if (color == nil) + { + color = [NSColor windowBackgroundColor]; + } + return color; +} + +- (NSColor *) menuItemBackgroundColor +{ + NSColor *color = [self colorNamed: @"menuItemBackgroundColor" + state: GSThemeNormalState]; + if (color == nil) + { + color = [NSColor controlBackgroundColor]; + } + return color; +} + +- (NSColor *) menuBorderColor +{ + NSColor *color = [self colorNamed: @"menuBorderColor" + state: GSThemeNormalState]; + if (color == nil) + { + color = [NSColor darkGrayColor]; + } + return color; +} + +- (NSColor *) menuBarBackgroundColor +{ + NSColor *color = [self colorNamed: @"menuBarBackgroundColor" + state: GSThemeNormalState]; + if (color == nil) + { + color = [self menuBackgroundColor]; + } + return color; +} + +- (NSColor *) menuBarBorderColor +{ + NSColor *color = [self colorNamed: @"menuBarBorderColor" + state: GSThemeNormalState]; + if (color == nil) + { + color = [self menuBorderColor]; + } + return color; +} + +- (NSColor *) menuBorderColorForEdge: (NSRectEdge)edge isHorizontal: (BOOL)horizontal +{ + if (horizontal && edge == NSMinYEdge) + { + return [self menuBorderColor]; + } + else if (edge == NSMinXEdge || edge == NSMaxYEdge) + { + // Draw the dark gray upper left lines. + return [self menuBorderColor]; + } + return nil; +} + - (void) drawBackgroundForMenuView: (NSMenuView*)menuView withFrame: (NSRect)bounds dirtyRect: (NSRect)dirtyRect @@ -814,25 +883,15 @@ if (tiles == nil) { - NSRectEdge sides[2]; - float grays[] = {NSDarkGray, NSDarkGray}; + NSRectEdge sides[4] = { NSMinXEdge, NSMaxYEdge, NSMaxXEdge, NSMinYEdge }; + NSColor *colors[] = {[self menuBorderColorForEdge: NSMinXEdge isHorizontal: horizontal], + [self menuBorderColorForEdge: NSMaxYEdge isHorizontal: horizontal], + [self menuBorderColorForEdge: NSMaxXEdge isHorizontal: horizontal], + [self menuBorderColorForEdge: NSMinYEdge isHorizontal: horizontal]}; - [[NSColor windowBackgroundColor] set]; - NSRectFill(NSIntersectionRect(bounds, dirtyRect)); - - if (horizontal == YES) - { - sides[0] = NSMinYEdge; - sides[1] = NSMinYEdge; - NSDrawTiledRects(bounds, dirtyRect, sides, grays, 2); - } - else - { - sides[0] = NSMinXEdge; - sides[1] = NSMaxYEdge; - // Draw the dark gray upper left lines. - NSDrawTiledRects(bounds, dirtyRect, sides, grays, 2); - } + [[self menuBackgroundColor] set]; + NSRectFill(NSIntersectionRect(bounds, dirtyRect)); + NSDrawColorTiledRects(bounds, dirtyRect, sides, colors, 4); } else { @@ -842,6 +901,13 @@ } } +- (BOOL) drawsBorderForMenuItemCell: (NSMenuItemCell *)cell + state: (GSThemeControlState)state + isHorizontal: (BOOL)horizontal +{ + return [cell isBordered]; +} + - (void) drawBorderAndBackgroundForMenuItemCell: (NSMenuItemCell *)cell withFrame: (NSRect)cellFrame inView: (NSView *)controlView @@ -854,7 +920,6 @@ if (tiles == nil) { - NSColor *backgroundColor = [cell backgroundColor]; if (isHorizontal) @@ -869,8 +934,12 @@ [backgroundColor set]; NSRectFill(cellFrame); - if (![cell isBordered]) - return; + if (![self drawsBorderForMenuItemCell: cell + state: state + isHorizontal: isHorizontal]) + { + return; + } if (state == GSThemeSelectedState) { @@ -889,17 +958,26 @@ } } -- (NSColor *) menuSeparatorItemColor +- (NSColor *) menuSeparatorColor { - NSColor *color = [self colorNamed: @"menuSeparatorItemColor" + NSColor *color = [self colorNamed: @"menuSeparatorColor" state: GSThemeNormalState]; - if (color == nil) + NSInterfaceStyle style = NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil); + + // TODO: Remove the style check... Windows theming should be in a subclass + // probably + if (color == nil && style == NSWindows95InterfaceStyle) { color = [NSColor blackColor]; } return color; } +- (CGFloat) menuSeparatorInset +{ + return 3.0; +} + - (void) drawSeparatorItemForMenuItemCell: (NSMenuItemCell *)cell withFrame: (NSRect)cellFrame inView: (NSView *)controlView @@ -909,23 +987,20 @@ if (tiles == nil) { - NSInterfaceStyle style = NSInterfaceStyleForKey(@"NSMenuInterfaceStyle", nil); - - if (style == NSMacintoshInterfaceStyle || style == NSWindows95InterfaceStyle) - { - NSBezierPath *path = [NSBezierPath bezierPath]; - NSPoint start = NSMakePoint(3, cellFrame.size.height / 2 + - cellFrame.origin.y); - NSPoint end = NSMakePoint(cellFrame.size.width - 3, - cellFrame.size.height / 2 + cellFrame.origin.y); + NSBezierPath *path = [NSBezierPath bezierPath]; + CGFloat inset = [self menuSeparatorInset]; + NSPoint start = NSMakePoint(inset, cellFrame.size.height / 2 + + cellFrame.origin.y + 0.5); + NSPoint end = NSMakePoint(cellFrame.size.width - inset, + cellFrame.size.height / 2 + cellFrame.origin.y + 0.5); - [[NSColor blackColor] set]; + [[self menuSeparatorColor] set]; - [path moveToPoint: start]; - [path lineToPoint: end]; + [path setLineWidth: 0.0]; + [path moveToPoint: start]; + [path lineToPoint: end]; - [path stroke]; - } + [path stroke]; } else { @@ -935,6 +1010,16 @@ } } +- (void) drawTitleForMenuItemCell: (NSMenuItemCell *)cell + withFrame: (NSRect)cellFrame + inView: (NSView *)controlView + state: (GSThemeControlState)state + isHorizontal: (BOOL)isHorizontal +{ + [cell _drawText: [[cell menuItem] title] + inFrame: [cell titleRectForBounds: cellFrame]]; +} + - (Class) titleViewClassForMenuView: (NSMenuView *)aMenuView { return [GSTitleView class]; diff --git a/Source/NSMenuItemCell.m b/Source/NSMenuItemCell.m index eac650b4a..6d95346d9 100644 --- a/Source/NSMenuItemCell.m +++ b/Source/NSMenuItemCell.m @@ -163,6 +163,7 @@ static NSString *commandKeyString = @"#"; state = GSThemeSelectedState; } + // TODO: Make the color lookup simpler. color = [[GSTheme theme] colorNamed: @"NSMenuItem" state: state]; if (color == nil) { @@ -172,7 +173,7 @@ static NSString *commandKeyString = @"#"; } else { - color = [NSColor controlBackgroundColor]; + color = [[GSTheme theme] menuItemBackgroundColor]; } } @@ -657,11 +658,7 @@ static NSString *commandKeyString = @"#"; } } -// -// Drawing. -// -- (void) drawBorderAndBackgroundWithFrame: (NSRect)cellFrame - inView: (NSView *)controlView +- (GSThemeControlState) themeControlState { unsigned mask; GSThemeControlState state = GSThemeNormalState; @@ -695,10 +692,19 @@ static NSString *commandKeyString = @"#"; state = GSThemeSelectedState; } + return state; +} + +// +// Drawing. +// +- (void) drawBorderAndBackgroundWithFrame: (NSRect)cellFrame + inView: (NSView *)controlView +{ [[GSTheme theme] drawBorderAndBackgroundForMenuItemCell: self withFrame: cellFrame inView: controlView - state: state + state: [self themeControlState] isHorizontal: [_menuView isHorizontal]]; } @@ -792,8 +798,11 @@ static NSString *commandKeyString = @"#"; - (void) drawTitleWithFrame: (NSRect)cellFrame inView: (NSView *)controlView { - [self _drawText: [_menuItem title] - inFrame: [self titleRectForBounds: cellFrame]]; + [[GSTheme theme] drawTitleForMenuItemCell: self + withFrame: cellFrame + inView: controlView + state: [self themeControlState] + isHorizontal: [_menuView isHorizontal]]; } - (void) _drawBorderAndBackgroundWithFrame: (NSRect)cellFrame inView: (NSView*)controlView