diff --git a/ChangeLog b/ChangeLog
index 08bde2142..99e50313c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2006-09-22 Richard Frith-Macdonald
This interface is HIGHLY unstable + and incomplete at present. +
++ This is a class used for 'theming', which is mostly a matter of + encapsulating common drawing behaviors so that GUI appearance can + be easily modified, but also includes mechanisms for altering + some GUI behavior (such mas orientation and position of menus). +
++ Methods in this class standardize drawing of buttons, borders + and other common GUI elements, so that all other classes within + the GUI will provide a consistent appearance by using these + methods. +
+
+ The default implementation uses the standard configurable colors
+ defined in NSColor, such as controlLightHighlightColor
,
+ controlShadowColor
and controlDarkShadowColor
.
+ Themes are expected to override the default system color list with their
+ own versions, and this class cooperates with [NSColor] and [NSColorList]
+ to establish the correct system color list when a theme is activated.
+
+ The class provides a mechanism for automatic loading of theme bundles + consisting of resources used to define how drawing is done, plus an + optional binary subclass of this class (to replace/extend the drawing + methods this class provides). +
++ In future this class should provide mechanisms to draw controls by + tiling of images, and provide control over GUI behavior by controlling + the values returned by NSInterfaceStyleForKey() so that controls + use the appropriate behavior. +
+*/ @interface GSDrawFunctions : NSObject +{ +@private + NSBundle *_bundle; + NSMutableArray *_images; + NSMutableDictionary *_tiles; +} -+ (id) theme; -+ (void) setTheme: (id)theme; +/** + * Set the currently active theme to be the instance specified.This method is called automatically when the receiver is made into + * the currently active theme by the +setTheme: method. Subclasses may + * override it to perform startup operations, but should call the super + * class implementation before doing their own thing. + *
+ *The base implementation handles setup and caching of certain image
+ * information and then sends a GSThemeDidActivateNotification to allow
+ * other parts of the GUI library to update themselves from the new theme.
+ * If the theme sets an alternative system color list, the notification
+ * userInfo dictionary will contain that list keyed on Colors.
+ *
This method is called automatically when the receiver is stopped from + * being the currently active theme by the use of the +setTheme: method + * to make another theme active. Subclasses may override it to perform + * shutdown operations, but should call the super class implementation + * after their own. + *
+ *The base implementation handles some cleanup and then sends a + * GSThemeDidDeactivateNotification to allow other parts of the GUI library + * to update themselves. + *
+ */ +- (void) deactivate; + +/**A window has a frame. This is the frame of the entire
* window on the screen, including all decorations and borders. The origin
* of the frame represents its bottom left corner and the frame is expressed
- * in screen coordinates (see [NSScreen]).
+ * in screen coordinates (see [NSScreen]).
*
When a window is created, it has a private [NSView] instance * which fills the entire window frame and whose coordinate system is the @@ -122,7 +122,7 @@ APPKIT_EXPORT NSSize NSTokenSize; * draw window decorations if the backend library is not handling the * window decorations. *
- *A window always contains a content view which is the highest
+ * A window always contains a content view which is the highest
* level view available for public (application) use. This view fills the
* area of the window inside any decoration/border.
- This is a simple class used for encapsulating common drawing behaviors.
- These methods standardize drawing of buttons, borders and other common
- GUI elements. The drawing functions are encapsulated in a class to
- allow overriding of the methods so that these elements can be drawn
- in different ways (e.g. with themes).
-
- The default implementation uses the standard configurable colors defined in
- NSColor, such as
* This is the only part of the window that application programmers are
diff --git a/Source/DocMakefile b/Source/DocMakefile
index e99603f82..249ab28ae 100644
--- a/Source/DocMakefile
+++ b/Source/DocMakefile
@@ -19,7 +19,8 @@
#
# You should have received a copy of the GNU Library General Public
# License along with this library; if not, write to the Free
-# Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA
+# Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02111 USA
#
MAKEFILE_NAME = DocMakefile
@@ -147,6 +148,7 @@ NSWorkspace.h
AUTOGSDOC_HEADERS_GUIADD = \
GSDisplayServer.h \
+GSDrawFunctions.h \
GSFusedSilica.h \
GSTable.h \
GSHbox.h \
diff --git a/Source/GSDrawFunctions.m b/Source/GSDrawFunctions.m
index c558514b0..706bc3489 100644
--- a/Source/GSDrawFunctions.m
+++ b/Source/GSDrawFunctions.m
@@ -21,112 +21,441 @@
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
- Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA.
+ Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02111 USA.
*/
+#include "Foundation/NSBundle.h"
+#include "Foundation/NSDictionary.h"
+#include "Foundation/NSFileManager.h"
+#include "Foundation/NSNotification.h"
+#include "Foundation/NSNull.h"
+#include "Foundation/NSPathUtilities.h"
+#include "Foundation/NSUserDefaults.h"
#include "GNUstepGUI/GSDrawFunctions.h"
#include "AppKit/NSColor.h"
+#include "AppKit/NSColorList.h"
#include "AppKit/NSGraphics.h"
+#include "AppKit/NSImage.h"
#include "AppKit/NSView.h"
+#include "AppKit/NSBezierPath.h"
#include "AppKit/PSOperators.h"
+NSString *GSThemeDidActivateNotification
+ = @"GSThemeDidActivateNotification";
+NSString *GSThemeDidDeactivateNotification
+ = @"GSThemeDidDeactivateNotification";
+
+/** These are the nine types of tile used to draw a rectangular object.
+ */
+typedef enum {
+ TileTL = 0, /** Top left corner */
+ TileTM = 1, /** Top middle section */
+ TileTR = 2, /** Top right corner */
+ TileCL = 3, /** Centerj left corner */
+ TileCM = 4, /** Centerj middle section */
+ TileCR = 5, /** Centerj right corner */
+ TileBL = 6, /** Bottom left corner */
+ TileBM = 7, /** Bottom middle section */
+ TileBR = 8 /** Bottom right corner */
+} GSDrawFunctionsTileOffset;
+
+/** This is a trivial class to hold the nine tiles needed to draw a rectangle
+ */
+@interface GSDrawTiles : NSObject
+{
+@public
+ NSImage *images[9]; /** The tile images */
+ NSRect rects[9]; /** The rectangles to use when drawing */
+}
+- (id) initWithImage: (NSImage*)image;
+@end
+
+@implementation GSDrawTiles
+- (void) dealloc
+{
+ unsigned i;
+
+ for (i = 0; i < 9; i++)
+ {
+ RELEASE(images[i]);
+ }
+ [super dealloc];
+}
/**
- controlLightHighlightColor
,
- controlShadowColor
and controlDarkShadowColor
.
-
+ * If aName is nil or an empty string, this reverts to the default theme.
+ * If the named theme is already active, this has no effect.
+ * Returns YES on success, NO if the theme could not be loaded.
+ */
++ (BOOL) loadThemeNamed: (NSString*)aName;
+@end
+
+
@implementation GSDrawFunctions
-static id theTheme = nil;
+static GSDrawFunctions *defaultTheme = nil;
+static GSDrawFunctions *theTheme = nil;
+static NSString *theThemeName = nil;
+static NSMutableDictionary *themes = nil;
+static NSNull *null = nil;
-
-+ (id) theme
++ (void) defaultsDidChange: (NSNotification*)n
{
- if (theTheme == nil)
+ NSUserDefaults *defs;
+ NSString *name;
+
+ defs = [NSUserDefaults standardUserDefaults];
+ name = [defs stringForKey: @"GSTheme"];
+ if (name != theThemeName && [name isEqual: theThemeName] == NO)
{
- theTheme = [self new];
+ [self loadThemeNamed: name];
}
+}
+
++ (void) initialize
+{
+ if (themes == nil)
+ {
+ themes = [NSMutableDictionary new];
+ [self theme]; // Initialise/create the default theme
+ [[NSNotificationCenter defaultCenter]
+ addObserver: self
+ selector: @selector(defaultsDidChange:)
+ name: NSUserDefaultsDidChangeNotification
+ object: nil];
+ }
+ if (null == nil)
+ {
+ null = RETAIN([NSNull null]);
+ }
+ if (defaultTheme == nil)
+ {
+ NSBundle *aBundle = [NSBundle bundleForClass: self];
+
+ defaultTheme = [[self alloc] initWithBundle: aBundle];
+ ASSIGN(theTheme, defaultTheme);
+ }
+}
+
++ (BOOL) loadThemeNamed: (NSString*)aName
+{
+ NSBundle *bundle;
+ Class cls;
+ GSDrawFunctions *instance;
+ NSString *theme;
+
+ if ([aName length] == 0)
+ {
+ [self setTheme: nil];
+ [self theme];
+ return YES;
+ }
+
+ /* Ensure that the theme name does not contain path components
+ * and has the 'theme' extension.
+ */
+ aName = [aName lastPathComponent];
+ if ([[aName pathExtension] isEqualToString: @"theme"] == YES)
+ {
+ theme = aName;
+ }
+ else
+ {
+ theme = [aName stringByAppendingPathExtension: @"theme"];
+ }
+
+ bundle = [themes objectForKey: theme];
+ if (bundle == nil)
+ {
+ NSString *path;
+ NSEnumerator *enumerator;
+ NSFileManager *mgr = [NSFileManager defaultManager];
+
+ enumerator = [NSSearchPathForDirectoriesInDomains
+ (NSAllLibrariesDirectory, NSAllDomainsMask, YES) objectEnumerator];
+ while ((path = [enumerator nextObject]) != nil)
+ {
+ BOOL isDir;
+
+ path = [path stringByAppendingPathComponent: @"Themes"];
+ path = [path stringByAppendingPathComponent: theme];
+ if ([mgr fileExistsAtPath: path isDirectory: &isDir])
+ {
+ break;
+ }
+ }
+
+ if (path == nil)
+ {
+ NSLog (@"No theme named '%@' found", aName);
+ return NO;
+ }
+ else
+ {
+ bundle = [NSBundle bundleWithPath: path];
+ [themes setObject: bundle forKey: theme];
+ [bundle load]; // Ensure code is loaded.
+ }
+ }
+
+ cls = [bundle principalClass];
+ if (cls == 0)
+ {
+ cls = self;
+ }
+ instance = [[cls alloc] initWithBundle: bundle];
+ [self setTheme: instance];
+ RELEASE(instance);
+ return YES;
+}
+
++ (void) setTheme: (GSDrawFunctions*)theme
+{
+ if (theme == nil)
+ {
+ theme = defaultTheme;
+ }
+ if (theme != theTheme)
+ {
+ [theTheme deactivate];
+ ASSIGN (theTheme, theme);
+ [theTheme activate];
+ }
+}
+
++ (GSDrawFunctions*) theme
+{
return theTheme;
}
-+ (void) setTheme: (id) aTheme
+
+- (void) activate
{
- ASSIGN (theTheme, aTheme);
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+ NSArray *imagePaths;
+ NSEnumerator *enumerator;
+ NSString *imagePath;
+ NSString *colorsPath;
+
+ colorsPath = [_bundle pathForResource: @"ThemeColors" ofType: @"clr"];
+ if (colorsPath != nil)
+ {
+ NSColorList *list = nil;
+
+ list = [[NSColorList alloc] initWithName: @"System"
+ fromFile: colorsPath];
+ if (list != nil)
+ {
+ [userInfo setObject: list forKey: @"Colors"];
+ RELEASE(list);
+ }
+ }
+
+ /*
+ * We step through all the bundle image resources and load them in
+ * to memory, setting their names so that they are visible to
+ * [NSImage+imageNamed:] and storing them in our local array.
+ */
+ imagePaths = [_bundle pathsForResourcesOfType: nil
+ inDirectory: @"ThemeImages"];
+ enumerator = [imagePaths objectEnumerator];
+ while ((imagePath = [enumerator nextObject]) != nil)
+ {
+ NSImage *image;
+
+ image = [[NSImage alloc] initWithContentsOfFile: imagePath];
+ if (image != nil)
+ {
+ NSString *imageName;
+
+ imageName = [imagePath lastPathComponent];
+ imageName = [imageName stringByDeletingPathExtension];
+ [_images addObject: image];
+ [image setName: imageName];
+ RELEASE(image);
+ }
+ }
+
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName: GSThemeDidActivateNotification
+ object: self
+ userInfo: userInfo];
}
-/** Tell current theme to draw a button border */
-+ (NSRect) drawButton: (NSRect)border : (NSRect)clip
+- (NSBundle*) bundle
{
- return [[self theme] drawButton: border : clip];
+ return _bundle;
}
-/** Tell current theme to draw a "dark" button border (used in tableviews) */
-+ (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip
+- (void) deactivate
{
- return [[self theme] drawDarkButton: border : clip];
+ NSEnumerator *enumerator;
+ NSImage *image;
+
+ /*
+ * Remove all cached bundle images from both NSImage's name dictionary
+ * and our cache array.
+ */
+ enumerator = [_images objectEnumerator];
+ while ((image = [enumerator nextObject]) != nil)
+ {
+ [image setName: nil];
+ }
+ [_images removeAllObjects];
+
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName: GSThemeDidDeactivateNotification
+ object: self
+ userInfo: nil];
+
}
-/** Tell current theme to draw a dark bezel border */
-+ (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip
+- (void) dealloc
{
- return [[self theme] drawDarkBezel: border : clip];
+ RELEASE(_bundle);
+ RELEASE(_images);
+ RELEASE(_tiles);
+ [super dealloc];
}
-/** Tell current theme to draw a light bezel border */
-+ (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip
+- (id) initWithBundle: (NSBundle*)bundle
{
- return [[self theme] drawLightBezel: border : clip];
+ ASSIGN(_bundle, bundle);
+ _images = [NSMutableArray new];
+ _tiles = [NSMutableDictionary new];
+ return self;
}
-/** Tell current theme to draw a white bezel border */
-+ (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip
+- (GSDrawTiles*) tilesNamed: (NSString*)aName
{
- return [[self theme] drawWhiteBezel: border : clip];
+ GSDrawTiles *tiles = [_tiles objectForKey: aName];
+
+ if (tiles == nil)
+ {
+ NSImage *image = [NSImage imageNamed: aName];
+
+ if (image != nil)
+ {
+ tiles = [[GSDrawTiles alloc] initWithImage: image];
+ }
+ else
+ {
+ tiles = RETAIN(null);
+ }
+ [_tiles setObject: tiles forKey: aName];
+ RELEASE(_tiles);
+ }
+ if (tiles == (id)null)
+ {
+ tiles = nil;
+ }
+ return tiles;
}
-/** Tell current theme to draw a grey bezel border */
-+ (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip
+@end
+
+
+@implementation GSDrawFunctions (Drawing)
+
+- (NSRect) drawButton: (NSRect) frame
+ in: (NSButtonCell*) cell
+ view: (NSView*) view
+ style: (int) style
+ state: (int) state
{
- return [[self theme] drawGrayBezel: border : clip];
+ /* computes the interior frame rect */
+
+ NSRect interiorFrame = [cell drawingRectForBounds: frame];
+
+ /* Draw the button background */
+
+ if (state == 0) /* default state, unpressed */
+ {
+ [[NSColor controlBackgroundColor] set];
+ NSRectFill(frame);
+ [GSDrawFunctions drawButton: frame : NSZeroRect];
+ }
+ else if (state == 1) /* highlighted state */
+ {
+ [[NSColor selectedControlColor] set];
+ NSRectFill(frame);
+ [GSDrawFunctions drawGrayBezel: frame : NSZeroRect];
+ }
+ else if (state == 2) /* pushed state */
+ {
+ [[NSColor selectedControlColor] set];
+ NSRectFill(frame);
+ [GSDrawFunctions drawGrayBezel: frame : NSZeroRect];
+ interiorFrame
+ = NSOffsetRect(interiorFrame, 1.0, [view isFlipped] ? 1.0 : -1.0);
+ }
+
+ /* returns the interior frame rect */
+
+ return interiorFrame;
}
-/** Tell current theme to draw a groove border */
-+ (NSRect) drawGroove: (NSRect)border : (NSRect)clip
+- (void) drawFocusFrame: (NSRect) frame view: (NSView*) view
{
- return [[self theme] drawGroove: border : clip];
+ NSDottedFrameRect(frame);
}
-/** Tell current theme to draw a frame photo border. Used in NSImageView. */
-+ (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip
+- (void) drawWindowBackground: (NSRect) frame view: (NSView*) view
{
- return [[self theme] drawFramePhoto: border : clip];
+ NSColor *c;
+
+ c = [[view window] backgroundColor];
+ [c set];
+ NSRectFill (frame);
}
-/** Tell current theme to draw a gradient border. */
-+ (NSRect) drawGradientBorder: (NSGradientType)gradientType
- inRect: (NSRect)border
- withClip: (NSRect)clip
-{
- return [[self theme] drawGradientBorder: gradientType
- inRect: border
- withClip: clip];
-}
+@end
+
-- (NSRect) drawButton: (NSRect)border : (NSRect)clip
+@implementation GSDrawFunctions (MidLevelDrawing)
+
+- (NSRect) drawButton: (NSRect)border withClip: (NSRect)clip
{
NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
NSMinXEdge, NSMaxYEdge,
@@ -138,8 +467,7 @@ static id theTheme = nil;
NSColor *black = [NSColor controlDarkShadowColor];
NSColor *dark = [NSColor controlShadowColor];
NSColor *white = [NSColor controlLightHighlightColor];
- NSColor *colors[] = {black, black, white, white,
- dark, dark};
+ NSColor *colors[] = {black, black, white, white, dark, dark};
if ([[NSView focusView] isFlipped] == YES)
{
@@ -151,30 +479,7 @@ static id theTheme = nil;
}
}
-/** Draw a "dark" button border (used in tableviews) */
-- (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip
-{
- NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
- NSMinXEdge, NSMaxYEdge};
- NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge,
- NSMinXEdge, NSMinYEdge};
- // These names are role names not the actual colours
- NSColor *black = [NSColor controlDarkShadowColor];
- NSColor *white = [NSColor controlHighlightColor];
- NSColor *colors[] = {black, black, white, white};
-
- if ([[NSView focusView] isFlipped] == YES)
- {
- return NSDrawColorTiledRects(border, clip, dn_sides, colors, 4);
- }
- else
- {
- return NSDrawColorTiledRects(border, clip, up_sides, colors, 4);
- }
-}
-
-/** Draw a dark bezel border */
-- (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip
+- (NSRect) drawDarkBezel: (NSRect)border withClip: (NSRect)clip
{
NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
NSMinXEdge, NSMaxYEdge, NSMaxXEdge, NSMinYEdge};
@@ -185,8 +490,7 @@ static id theTheme = nil;
NSColor *dark = [NSColor controlShadowColor];
NSColor *light = [NSColor controlColor];
NSColor *white = [NSColor controlLightHighlightColor];
- NSColor *colors[] = {white, white, dark, dark,
- black, black, light, light};
+ NSColor *colors[] = {white, white, dark, dark, black, black, light, light};
NSRect rect;
if ([[NSView focusView] isFlipped] == YES)
@@ -208,113 +512,28 @@ static id theTheme = nil;
return rect;
}
-/** Draw a light bezel border */
-- (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip
+- (NSRect) drawDarkButton: (NSRect)border withClip: (NSRect)clip
{
- NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
- NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge};
- NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
- NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge};
- // These names are role names not the actual colours
- NSColor *dark = [NSColor controlShadowColor];
- NSColor *light = [NSColor controlColor];
- NSColor *white = [NSColor controlLightHighlightColor];
- NSColor *colors[] = {white, white, dark, dark,
- light, light, dark, dark};
-
- if ([[NSView focusView] isFlipped] == YES)
- {
- return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
- }
- else
- {
- return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
- }
-}
-
-/** Draw a white bezel border */
-- (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip
-{
- NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge,
- NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge};
- NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge,
- NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge};
- // These names are role names not the actual colours
- NSColor *dark = [NSColor controlShadowColor];
- NSColor *light = [NSColor controlColor];
- NSColor *white = [NSColor controlLightHighlightColor];
- NSColor *colors[] = {dark, white, white, dark,
- dark, light, light, dark};
-
- if ([[NSView focusView] isFlipped] == YES)
- {
- return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
- }
- else
- {
- return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
- }
-}
-
-/** Draw a grey bezel border */
-- (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip
-{
- NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
- NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge};
- NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
- NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge};
+ NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
+ NSMinXEdge, NSMaxYEdge};
+ NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge,
+ NSMinXEdge, NSMinYEdge};
// These names are role names not the actual colours
NSColor *black = [NSColor controlDarkShadowColor];
- NSColor *dark = [NSColor controlShadowColor];
- NSColor *light = [NSColor controlColor];
- NSColor *white = [NSColor controlLightHighlightColor];
- NSColor *colors[] = {white, white, dark, dark,
- light, light, black, black};
- NSRect rect;
+ NSColor *white = [NSColor controlHighlightColor];
+ NSColor *colors[] = {black, black, white, white};
if ([[NSView focusView] isFlipped] == YES)
{
- rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
- [dark set];
- PSrectfill(NSMinX(border) + 1., NSMaxY(border) - 2., 1., 1.);
- PSrectfill(NSMaxX(border) - 2., NSMinY(border) + 1., 1., 1.);
+ return NSDrawColorTiledRects(border, clip, dn_sides, colors, 4);
}
else
{
- rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
- [dark set];
- PSrectfill(NSMinX(border) + 1., NSMinY(border) + 1., 1., 1.);
- PSrectfill(NSMaxX(border) - 2., NSMaxY(border) - 2., 1., 1.);
- }
- return rect;
-}
-
-/** Draw a groove border */
-- (NSRect) drawGroove: (NSRect)border : (NSRect)clip
-{
- // go clockwise from the top twice -- makes the groove come out right
- NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge,
- NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge};
- NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge,
- NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge};
- // These names are role names not the actual colours
- NSColor *dark = [NSColor controlShadowColor];
- NSColor *white = [NSColor controlLightHighlightColor];
- NSColor *colors[] = {dark, white, white, dark,
- white, dark, dark, white};
-
- if ([[NSView focusView] isFlipped] == YES)
- {
- return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
- }
- else
- {
- return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
+ return NSDrawColorTiledRects(border, clip, up_sides, colors, 4);
}
}
-/** Draw a frame photo border. Used in NSImageView. */
-- (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip
+- (NSRect) drawFramePhoto: (NSRect)border withClip: (NSRect)clip
{
NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge,
NSMinXEdge, NSMaxYEdge,
@@ -338,7 +557,6 @@ static id theTheme = nil;
}
}
-/** Draw a gradient border. */
- (NSRect) drawGradientBorder: (NSGradientType)gradientType
inRect: (NSRect)border
withClip: (NSRect)clip
@@ -351,14 +569,10 @@ static id theTheme = nil;
NSColor *dark = [NSColor controlShadowColor];
NSColor *light = [NSColor controlColor];
NSColor **colors;
- NSColor *concaveWeak[] = {dark, dark,
- light, light};
- NSColor *concaveStrong[] = {black, black,
- light, light};
- NSColor *convexWeak[] = {light, light,
- dark, dark};
- NSColor *convexStrong[] = {light, light,
- black, black};
+ NSColor *concaveWeak[] = {dark, dark, light, light};
+ NSColor *concaveStrong[] = {black, black, light, light};
+ NSColor *convexWeak[] = {light, light, dark, dark};
+ NSColor *convexStrong[] = {light, light, black, black};
NSRect rect;
switch (gradientType)
@@ -392,4 +606,500 @@ static id theTheme = nil;
return rect;
}
+- (NSRect) drawGrayBezel: (NSRect)border withClip: (NSRect)clip
+{
+ NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
+ NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge};
+ NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
+ NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge};
+ // These names are role names not the actual colours
+ NSColor *black = [NSColor controlDarkShadowColor];
+ NSColor *dark = [NSColor controlShadowColor];
+ NSColor *light = [NSColor controlColor];
+ NSColor *white = [NSColor controlLightHighlightColor];
+ NSColor *colors[] = {white, white, dark, dark,
+ light, light, black, black};
+ NSRect rect;
+
+ if ([[NSView focusView] isFlipped] == YES)
+ {
+ rect = NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
+ [dark set];
+ PSrectfill(NSMinX(border) + 1., NSMaxY(border) - 2., 1., 1.);
+ PSrectfill(NSMaxX(border) - 2., NSMinY(border) + 1., 1., 1.);
+ }
+ else
+ {
+ rect = NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
+ [dark set];
+ PSrectfill(NSMinX(border) + 1., NSMinY(border) + 1., 1., 1.);
+ PSrectfill(NSMaxX(border) - 2., NSMaxY(border) - 2., 1., 1.);
+ }
+ return rect;
+}
+
+- (NSRect) drawGroove: (NSRect)border withClip: (NSRect)clip
+{
+ // go clockwise from the top twice -- makes the groove come out right
+ NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge,
+ NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge};
+ NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge,
+ NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge};
+ // These names are role names not the actual colours
+ NSColor *dark = [NSColor controlShadowColor];
+ NSColor *white = [NSColor controlLightHighlightColor];
+ NSColor *colors[] = {dark, white, white, dark,
+ white, dark, dark, white};
+
+ if ([[NSView focusView] isFlipped] == YES)
+ {
+ return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
+ }
+ else
+ {
+ return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
+ }
+}
+
+- (NSRect) drawLightBezel: (NSRect)border withClip: (NSRect)clip
+{
+ NSRectEdge up_sides[] = {NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge,
+ NSMaxXEdge, NSMinYEdge, NSMinXEdge, NSMaxYEdge};
+ NSRectEdge dn_sides[] = {NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge,
+ NSMaxXEdge, NSMaxYEdge, NSMinXEdge, NSMinYEdge};
+ // These names are role names not the actual colours
+ NSColor *dark = [NSColor controlShadowColor];
+ NSColor *light = [NSColor controlColor];
+ NSColor *white = [NSColor controlLightHighlightColor];
+ NSColor *colors[] = {white, white, dark, dark,
+ light, light, dark, dark};
+
+ if ([[NSView focusView] isFlipped] == YES)
+ {
+ return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
+ }
+ else
+ {
+ return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
+ }
+}
+
+- (NSRect) drawWhiteBezel: (NSRect)border withClip: (NSRect)clip
+{
+ NSRectEdge up_sides[] = {NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge,
+ NSMaxYEdge, NSMaxXEdge, NSMinYEdge, NSMinXEdge};
+ NSRectEdge dn_sides[] = {NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge,
+ NSMinYEdge, NSMaxXEdge, NSMaxYEdge, NSMinXEdge};
+ // These names are role names not the actual colours
+ NSColor *dark = [NSColor controlShadowColor];
+ NSColor *light = [NSColor controlColor];
+ NSColor *white = [NSColor controlLightHighlightColor];
+ NSColor *colors[] = {dark, white, white, dark,
+ dark, light, light, dark};
+
+ if ([[NSView focusView] isFlipped] == YES)
+ {
+ return NSDrawColorTiledRects(border, clip, dn_sides, colors, 8);
+ }
+ else
+ {
+ return NSDrawColorTiledRects(border, clip, up_sides, colors, 8);
+ }
+}
+
@end
+
+
+
+@implementation GSDrawFunctions (LowLevelDrawing)
+
+- (void) fillHorizontalRect: (NSRect)rect
+ withImage: (NSImage*)image
+ fromRect: (NSRect)source
+ flipped: (BOOL)flipped
+{
+ NSGraphicsContext *ctxt = GSCurrentContext();
+ NSBezierPath *path;
+ unsigned repetitions;
+ unsigned count;
+ float y;
+
+ DPSgsave (ctxt);
+ path = [NSBezierPath bezierPathWithRect: rect];
+ [path addClip];
+ repetitions = (rect.size.width / source.size.width) + 1;
+ y = rect.origin.y;
+
+ if (flipped) y = rect.origin.y + rect.size.height;
+
+ for (count = 0; count < repetitions; count++)
+ {
+ NSPoint p = NSMakePoint (rect.origin.x + count * source.size.width, y);
+
+ [image compositeToPoint: p
+ fromRect: source
+ operation: NSCompositeSourceOver];
+ }
+ DPSgrestore (ctxt);
+}
+
+- (void) fillRect: (NSRect)rect
+withRepeatedImage: (NSImage*)image
+ fromRect: (NSRect)source
+ center: (BOOL)center
+{
+ NSGraphicsContext *ctxt = GSCurrentContext ();
+ NSBezierPath *path;
+ NSSize size;
+ unsigned xrepetitions;
+ unsigned yrepetitions;
+ unsigned x;
+ unsigned y;
+
+ DPSgsave (ctxt);
+ path = [NSBezierPath bezierPathWithRect: rect];
+ [path addClip];
+ size = [image size];
+ xrepetitions = (rect.size.width / size.width) + 1;
+ yrepetitions = (rect.size.height / size.height) + 1;
+
+ for (x = 0; x < xrepetitions; x++)
+ {
+ for (y = 0; y < yrepetitions; y++)
+ {
+ NSPoint p;
+
+ p = NSMakePoint (rect.origin.x + x * size.width,
+ rect.origin.y + y * size.height);
+ [image compositeToPoint: p
+ fromRect: source
+ operation: NSCompositeSourceOver];
+ }
+ }
+ DPSgrestore (ctxt);
+}
+
+- (void) fillRect: (NSRect)rect
+ withTiles: (GSDrawTiles*)tiles
+ background: (NSColor*)color
+ fillStyle: (GSDrawFunctionsFillStyle)style
+{
+ NSGraphicsContext *ctxt = GSCurrentContext();
+ NSSize tls = tiles->rects[TileTL].size;
+ NSSize tms = tiles->rects[TileTM].size;
+ NSSize trs = tiles->rects[TileTR].size;
+ NSSize cls = tiles->rects[TileCL].size;
+ NSSize crs = tiles->rects[TileCR].size;
+ NSSize bls = tiles->rects[TileBL].size;
+ NSSize bms = tiles->rects[TileBM].size;
+ NSSize brs = tiles->rects[TileBR].size;
+ NSRect inFill;
+ BOOL flipped = [[ctxt focusView] isFlipped];
+
+ if (color == nil)
+ {
+ [[NSColor redColor] set];
+ }
+ else
+ {
+ [color set];
+ }
+ NSRectFill(rect);
+
+ if (flipped)
+ {
+ [self fillHorizontalRect:
+ NSMakeRect (rect.origin.x + bls.width,
+ rect.origin.y + rect.size.height - bms.height,
+ rect.size.width - bls.width - brs.width,
+ bms.height)
+ withImage: tiles->images[TileBM]
+ fromRect: tiles->rects[TileBM]
+ flipped: YES];
+ [self fillHorizontalRect:
+ NSMakeRect (rect.origin.x + tls.width,
+ rect.origin.y,
+ rect.size.width - tls.width - trs.width,
+ tms.height)
+ withImage: tiles->images[TileTM]
+ fromRect: tiles->rects[TileTM]
+ flipped: YES];
+ [self fillVerticalRect:
+ NSMakeRect (rect.origin.x,
+ rect.origin.y + bls.height,
+ cls.width,
+ rect.size.height - bls.height - tls.height)
+ withImage: tiles->images[TileCL]
+ fromRect: tiles->rects[TileCL]
+ flipped: NO];
+ [self fillVerticalRect:
+ NSMakeRect (rect.origin.x + rect.size.width - crs.width,
+ rect.origin.y + brs.height,
+ crs.width,
+ rect.size.height - brs.height - trs.height)
+ withImage: tiles->images[TileCR]
+ fromRect: tiles->rects[TileCR]
+ flipped: NO];
+
+ [tiles->images[TileTL] compositeToPoint:
+ NSMakePoint (rect.origin.x,
+ rect.origin.y)
+ fromRect: tiles->rects[TileTL]
+ operation: NSCompositeSourceOver];
+ [tiles->images[TileTR] compositeToPoint:
+ NSMakePoint (rect.origin.x + rect.size.width - tls.width,
+ rect.origin.y)
+ fromRect: tiles->rects[TileTR]
+ operation: NSCompositeSourceOver];
+ [tiles->images[TileBL] compositeToPoint:
+ NSMakePoint (rect.origin.x,
+ rect.origin.y + rect.size.height - tls.height)
+ fromRect: tiles->rects[TileBL]
+ operation: NSCompositeSourceOver];
+ [tiles->images[TileBR] compositeToPoint:
+ NSMakePoint (rect.origin.x + rect.size.width - brs.width,
+ rect.origin.y + rect.size.height - tls.height)
+ fromRect: tiles->rects[TileBR]
+ operation: NSCompositeSourceOver];
+
+ inFill = NSMakeRect (rect.origin.x +cls.width,
+ rect.origin.y + bms.height,
+ rect.size.width - cls.width - crs.width,
+ rect.size.height - bms.height - tms.height);
+ if (style == FillStyleCenter)
+ {
+ [self fillRect: inFill
+ withRepeatedImage: tiles->images[TileCM]
+ fromRect: tiles->rects[TileCM]
+ center: NO];
+ }
+ else if (style == FillStyleRepeat)
+ {
+ [self fillRect: inFill
+ withRepeatedImage: tiles->images[TileCM]
+ fromRect: tiles->rects[TileCM]
+ center: NO];
+ }
+ else if (style == FillStyleScale)
+ {
+ [tiles->images[TileCM] setScalesWhenResized: YES];
+ [tiles->images[TileCM] setSize: inFill.size];
+ [tiles->images[TileCM] compositeToPoint: inFill.origin
+ fromRect: tiles->rects[TileCM]
+ operation: NSCompositeSourceOver];
+ }
+ }
+ else
+ {
+ [self fillHorizontalRect:
+ NSMakeRect(
+ rect.origin.x + tls.width,
+ rect.origin.y + rect.size.height - tms.height,
+ rect.size.width - bls.width - brs.width,
+ tms.height)
+ withImage: tiles->images[TileTM]
+ fromRect: tiles->rects[TileTM]
+ flipped: NO];
+ [self fillHorizontalRect:
+ NSMakeRect(
+ rect.origin.x + bls.width,
+ rect.origin.y,
+ rect.size.width - bls.width - brs.width,
+ bms.height)
+ withImage: tiles->images[TileBM]
+ fromRect: tiles->rects[TileBM]
+ flipped: NO];
+ [self fillVerticalRect:
+ NSMakeRect(
+ rect.origin.x,
+ rect.origin.y + bls.height,
+ cls.width,
+ rect.size.height - tls.height - bls.height)
+ withImage: tiles->images[TileCL]
+ fromRect: tiles->rects[TileCL]
+ flipped: NO];
+ [self fillVerticalRect:
+ NSMakeRect(
+ rect.origin.x + rect.size.width - crs.width,
+ rect.origin.y + brs.height,
+ crs.width,
+ rect.size.height - trs.height - brs.height)
+ withImage: tiles->images[TileCR]
+ fromRect: tiles->rects[TileCR]
+ flipped: NO];
+
+ [tiles->images[TileTL] compositeToPoint:
+ NSMakePoint (
+ rect.origin.x,
+ rect.origin.y + rect.size.height - tls.height)
+ fromRect: tiles->rects[TileTL]
+ operation: NSCompositeSourceOver];
+ [tiles->images[TileTR] compositeToPoint:
+ NSMakePoint(
+ rect.origin.x + rect.size.width - trs.width,
+ rect.origin.y + rect.size.height - trs.height)
+ fromRect: tiles->rects[TileTR]
+ operation: NSCompositeSourceOver];
+ [tiles->images[TileBL] compositeToPoint:
+ NSMakePoint(
+ rect.origin.x,
+ rect.origin.y)
+ fromRect: tiles->rects[TileBL]
+ operation: NSCompositeSourceOver];
+ [tiles->images[TileBR] compositeToPoint:
+ NSMakePoint(
+ rect.origin.x + rect.size.width - brs.width,
+ rect.origin.y)
+ fromRect: tiles->rects[TileBR]
+ operation: NSCompositeSourceOver];
+
+ inFill = NSMakeRect (rect.origin.x +cls.width,
+ rect.origin.y + bms.height,
+ rect.size.width - cls.width - crs.width,
+ rect.size.height - bms.height - tms.height);
+
+ if (style == FillStyleCenter)
+ {
+ [self fillRect: inFill
+ withRepeatedImage: tiles->images[TileCM]
+ fromRect: tiles->rects[TileCM]
+ center: NO];
+ }
+ else if (style == FillStyleRepeat)
+ {
+ [self fillRect: inFill
+ withRepeatedImage: tiles->images[TileCM]
+ fromRect: tiles->rects[TileCM]
+ center: YES];
+ }
+ else if (style == FillStyleScale)
+ {
+ [tiles->images[TileCM] setScalesWhenResized: YES];
+ [tiles->images[TileCM] setSize: inFill.size];
+ [tiles->images[TileCM] compositeToPoint: inFill.origin
+ fromRect: tiles->rects[TileCM]
+ operation: NSCompositeSourceOver];
+ }
+ }
+}
+
+- (void) fillVerticalRect: (NSRect)rect
+ withImage: (NSImage*)image
+ fromRect: (NSRect)source
+ flipped: (BOOL)flipped
+{
+ NSGraphicsContext *ctxt = GSCurrentContext();
+ NSBezierPath *path;
+ unsigned repetitions;
+ unsigned count;
+ NSPoint p;
+
+ DPSgsave (ctxt);
+ path = [NSBezierPath bezierPathWithRect: rect];
+ [path addClip];
+ repetitions = (rect.size.height / source.size.height) + 1;
+
+ if (flipped)
+ {
+ for (count = 0; count < repetitions; count++)
+ {
+ p = NSMakePoint (rect.origin.x,
+ rect.origin.y + rect.size.height - count * source.size.height);
+ [image compositeToPoint: p
+ fromRect: source
+ operation: NSCompositeSourceOver];
+ }
+ }
+ else
+ {
+ for (count = 0; count < repetitions; count++)
+ {
+ p = NSMakePoint (rect.origin.x,
+ rect.origin.y + count * source.size.height);
+ [image compositeToPoint: p
+ fromRect: source
+ operation: NSCompositeSourceOver];
+ }
+ }
+ DPSgrestore (ctxt);
+}
+
+@end
+
+
+
+@implementation GSDrawFunctions (deprecated)
++ (NSRect) drawButton: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawButton: border : clip];
+}
++ (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawDarkButton: border : clip];
+}
++ (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawDarkBezel: border : clip];
+}
++ (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawLightBezel: border : clip];
+}
++ (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawWhiteBezel: border : clip];
+}
++ (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawGrayBezel: border : clip];
+}
++ (NSRect) drawGroove: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawGroove: border : clip];
+}
++ (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip
+{
+ return [[self theme] drawFramePhoto: border : clip];
+}
++ (NSRect) drawGradientBorder: (NSGradientType)gradientType
+ inRect: (NSRect)border
+ withClip: (NSRect)clip
+{
+ return [[self theme] drawGradientBorder: gradientType
+ inRect: border
+ withClip: clip];
+}
+- (NSRect) drawButton: (NSRect)border : (NSRect)clip
+{
+ return [self drawButton: border withClip: clip];
+}
+- (NSRect) drawDarkButton: (NSRect)border : (NSRect)clip
+{
+ return [self drawDarkButton: border withClip: clip];
+}
+- (NSRect) drawDarkBezel: (NSRect)border : (NSRect)clip
+{
+ return [self drawDarkBezel: border withClip: clip];
+}
+- (NSRect) drawLightBezel: (NSRect)border : (NSRect)clip
+{
+ return [self drawLightBezel: border withClip: clip];
+}
+- (NSRect) drawWhiteBezel: (NSRect)border : (NSRect)clip
+{
+ return [self drawWhiteBezel: border withClip: clip];
+}
+- (NSRect) drawGrayBezel: (NSRect)border : (NSRect)clip
+{
+ return [self drawGrayBezel: border withClip: clip];
+}
+- (NSRect) drawGroove: (NSRect)border : (NSRect)clip
+{
+ return [self drawGroove: border withClip: clip];
+}
+- (NSRect) drawFramePhoto: (NSRect)border : (NSRect)clip
+{
+ return [self drawFramePhoto: border withClip: clip];
+}
+@end
+
diff --git a/Source/GSTitleView.m b/Source/GSTitleView.m
index 304dade63..d6f0b4260 100644
--- a/Source/GSTitleView.m
+++ b/Source/GSTitleView.m
@@ -195,6 +195,7 @@
- (void) drawRect: (NSRect)rect
{
+ GSDrawFunctions *theme = [GSDrawFunctions theme];
NSRect workRect = [self bounds];
NSSize titleSize;
NSRectEdge top_left[] = {NSMinXEdge, NSMaxYEdge};
@@ -213,7 +214,7 @@
// Rectangle 2
// Draw the title box's button.
- [GSDrawFunctions drawButton: workRect :workRect];
+ [theme drawButton: workRect withClip: workRect];
// Overdraw white top and left lines with light gray lines for window title
workRect.origin.y += 1;
diff --git a/Source/GSWindowDecorationView.m b/Source/GSWindowDecorationView.m
index 391ed4de1..9096ac3fa 100644
--- a/Source/GSWindowDecorationView.m
+++ b/Source/GSWindowDecorationView.m
@@ -30,6 +30,7 @@
#include "AppKit/NSColor.h"
#include "AppKit/NSWindow.h"
#include "GNUstepGUI/GSDisplayServer.h"
+#include "GNUstepGUI/GSDrawFunctions.h"
struct NSWindow_struct
@@ -215,12 +216,9 @@ struct NSWindow_struct
- (void) drawRect: (NSRect)rect
{
- NSColor *c = [_window backgroundColor];
-
if (NSIntersectsRect(rect, contentRect))
{
- [c set];
- NSRectFill(contentRect);
+ [[GSDrawFunctions theme] drawWindowBackground: contentRect view: self];
}
}
diff --git a/Source/NSButtonCell.m b/Source/NSButtonCell.m
index 41232b6cb..e172394f5 100644
--- a/Source/NSButtonCell.m
+++ b/Source/NSButtonCell.m
@@ -836,6 +836,9 @@ typedef struct _GSButtonCellFlags
- (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
{
+ unsigned mask;
+ int buttonState = 0;
+
// Save last view drawn to
if (_control_view != controlView)
_control_view = controlView;
@@ -848,22 +851,59 @@ typedef struct _GSButtonCellFlags
if (NSIsEmptyRect(cellFrame))
return;
- // draw the border if needed
- if ((_cell.is_bordered) &&
- (!_shows_border_only_while_mouse_inside || _mouse_inside))
+ // set the mask
+ if (_cell.is_highlighted)
{
- // FIXME Should check the bezel and gradient style
- if (_cell.is_highlighted && (_highlightsByMask & NSPushInCellMask))
+ mask = _highlightsByMask;
+ if (_cell.state)
{
- [GSDrawFunctions drawGrayBezel: cellFrame : NSZeroRect];
- }
- else
+ mask &= ~_showAltStateMask;
+ }
+ }
+ else if (_cell.state)
+ mask = _showAltStateMask;
+ else
+ mask = NSNoCellMask;
+
+ /* Draw the cell's background color.
+ We draw when there is a border or when highlightsByMask
+ is NSChangeBackgroundCellMask or NSChangeGrayCellMask,
+ as required by our nextstep-like look and feel. */
+ if (_cell.is_bordered
+ || (_highlightsByMask & NSChangeBackgroundCellMask)
+ || (_highlightsByMask & NSChangeGrayCellMask))
+ {
+ /* Determine the background color. */
+ if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask))
{
- [GSDrawFunctions drawButton: cellFrame : NSZeroRect];
+ buttonState = 1; /* highlighted state */
}
}
+ /* Pushed in buttons contents are displaced to the bottom right 1px. */
+ if (_cell.is_bordered && (mask & NSPushInCellMask))
+ {
+ buttonState = 2; // pushed button
+ }
+
+ // draw the border if needed
+ if ((_cell.is_bordered)
+ && (!_shows_border_only_while_mouse_inside || _mouse_inside))
+ {
+ cellFrame = [[GSDrawFunctions theme]
+ drawButton: cellFrame in: self view: controlView
+ style: _bezel_style
+ state: buttonState];
+ }
+
[self drawInteriorWithFrame: cellFrame inView: controlView];
+
+ // Draw first responder
+ if (_cell.shows_first_responder
+ && [[controlView window] firstResponder] == controlView)
+ {
+ [[GSDrawFunctions theme] drawFocusFrame: cellFrame view: controlView];
+ }
}
- (void) drawGradientWithFrame: (NSRect)cellFrame inView: (NSView *)controlView
@@ -884,47 +924,47 @@ typedef struct _GSButtonCellFlags
switch (_gradient_type)
{
- case NSGradientNone:
- return;
- break;
+ case NSGradientNone:
+ return;
+ break;
- case NSGradientConcaveWeak:
- [gray getHue: &h saturation: &s brightness: &v alpha: &a];
- start_white = [lightGray brightnessComponent];
- end_white = [gray brightnessComponent];
- break;
-
- case NSGradientConvexWeak:
- [darkGray getHue: &h saturation: &s brightness: &v alpha: &a];
- start_white = [gray brightnessComponent];
- end_white = [lightGray brightnessComponent];
- break;
-
- case NSGradientConcaveStrong:
- [lightGray getHue: &h saturation: &s brightness: &v alpha: &a];
- start_white = [lightGray brightnessComponent];
- end_white = [darkGray brightnessComponent];
- break;
-
- case NSGradientConvexStrong:
- [darkGray getHue: &h saturation: &s brightness: &v alpha: &a];
- start_white = [darkGray brightnessComponent];
- end_white = [lightGray brightnessComponent];
- break;
+ case NSGradientConcaveWeak:
+ [gray getHue: &h saturation: &s brightness: &v alpha: &a];
+ start_white = [lightGray brightnessComponent];
+ end_white = [gray brightnessComponent];
+ break;
+
+ case NSGradientConvexWeak:
+ [darkGray getHue: &h saturation: &s brightness: &v alpha: &a];
+ start_white = [gray brightnessComponent];
+ end_white = [lightGray brightnessComponent];
+ break;
+
+ case NSGradientConcaveStrong:
+ [lightGray getHue: &h saturation: &s brightness: &v alpha: &a];
+ start_white = [lightGray brightnessComponent];
+ end_white = [darkGray brightnessComponent];
+ break;
+
+ case NSGradientConvexStrong:
+ [darkGray getHue: &h saturation: &s brightness: &v alpha: &a];
+ start_white = [darkGray brightnessComponent];
+ end_white = [lightGray brightnessComponent];
+ break;
- default:
- break;
+ default:
+ break;
}
white = start_white;
- white_step = fabs(start_white - end_white)/
- (cellFrame.size.width + cellFrame.size.height);
+ white_step = fabs(start_white - end_white)
+ / (cellFrame.size.width + cellFrame.size.height);
// Start from top left
- p1 = NSMakePoint(cellFrame.origin.x,
- cellFrame.size.height + cellFrame.origin.y);
+ p1 = NSMakePoint(cellFrame.origin.x,
+ cellFrame.size.height + cellFrame.origin.y);
p2 = NSMakePoint(cellFrame.origin.x,
- cellFrame.size.height + cellFrame.origin.y);
+ cellFrame.size.height + cellFrame.origin.y);
// Move by Y
while (p1.y > cellFrame.origin.y)
@@ -974,7 +1014,6 @@ typedef struct _GSButtonCellFlags
NSRect titleRect;
NSSize imageSize = {0, 0};
NSSize titleSize = {0, 0};
- NSColor *backgroundColor = nil;
BOOL flippedView = [controlView isFlipped];
NSCellImagePosition ipos = _cell.image_position;
@@ -998,38 +1037,6 @@ typedef struct _GSButtonCellFlags
else
mask = NSNoCellMask;
- /* Pushed in buttons contents are displaced to the bottom right 1px. */
- if (_cell.is_bordered && (mask & NSPushInCellMask))
- {
- cellFrame = NSOffsetRect(cellFrame, 1., flippedView ? 1. : -1.);
- }
-
- /* Draw the cell's background color.
- We draw when there is a border or when highlightsByMask
- is NSChangeBackgroundCellMask or NSChangeGrayCellMask,
- as required by our nextstep-like look and feel. */
- if (_cell.is_bordered
- || (_highlightsByMask & NSChangeBackgroundCellMask)
- || (_highlightsByMask & NSChangeGrayCellMask))
- {
- /* Determine the background color. */
- if (mask & (NSChangeGrayCellMask | NSChangeBackgroundCellMask))
- {
- backgroundColor = [NSColor selectedControlColor];
- }
- else if (_cell.is_bordered)
- {
- backgroundColor = [NSColor controlBackgroundColor];
- }
-
- if (backgroundColor != nil)
- {
- [backgroundColor set];
- NSRectFill (cellFrame);
- }
-
- }
-
/*
* Determine the image and the title that will be
* displayed. If the NSContentsCellMask is set the
@@ -1195,7 +1202,8 @@ typedef struct _GSButtonCellFlags
imageRect.origin.x = cellFrame.origin.x;
imageRect.origin.y = cellFrame.origin.y;
imageRect.size.width = cellFrame.size.width;
- imageRect.size.height = titleRect.origin.y - GSCellTextImageYDist - imageRect.origin.y;
+ imageRect.size.height
+ = titleRect.origin.y - GSCellTextImageYDist - imageRect.origin.y;
if (_cell.is_bordered || _cell.is_bezeled)
{
@@ -1229,7 +1237,9 @@ typedef struct _GSButtonCellFlags
// Draw image
if (imageToDisplay != nil)
{
- [self _drawImage: imageToDisplay inFrame: imageRect isFlipped: flippedView];
+ [self _drawImage: imageToDisplay
+ inFrame: imageRect
+ isFlipped: flippedView];
}
// Draw title
@@ -1237,13 +1247,6 @@ typedef struct _GSButtonCellFlags
{
[self _drawAttributedText: titleToDisplay inFrame: titleRect];
}
-
- // Draw first responder
- if (_cell.shows_first_responder
- && [[controlView window] firstResponder] == controlView)
- {
- NSDottedFrameRect(cellFrame);
- }
}
- (NSSize) cellSize
diff --git a/Source/NSColor.m b/Source/NSColor.m
index a2c2e4e52..470c55d28 100644
--- a/Source/NSColor.m
+++ b/Source/NSColor.m
@@ -45,9 +45,19 @@
#include "AppKit/NSImage.h"
#include "AppKit/NSGraphics.h"
#include "AppKit/PSOperators.h"
+#include "GNUstepGUI/GSDrawFunctions.h"
static Class NSColorClass;
+/* This interface must be provided in NSColorList to let us manage
+ * system colors.
+ */
+@interface NSColorList (GNUstepPrivate)
++ (void) _setDefaultSystemColorList: (NSColorList*)aList;
++ (void) _setThemeSystemColorList: (NSColorList*)aList;
+@end
+
+
@interface GSNamedColor : NSColor
{
NSString *_catalog_name;
@@ -153,12 +163,14 @@ static Class NSColorClass;
+ (NSColor*) colorFromString: (NSString*)string;
+ (void) defaultsDidChange: (NSNotification*)notification;
++ (void) themeDidActivate: (NSNotification*)notification;
@end
// Class variables
static BOOL gnustep_gui_ignores_alpha = YES;
static NSColorList *systemColors = nil;
+static NSColorList *defaultSystemColors = nil;
static NSMutableDictionary *colorStrings = nil;
static NSMutableDictionary *systemDict = nil;
@@ -224,9 +236,11 @@ void initSystemColors(void)
nil];
systemColors = [NSColorList colorListNamed: @"System"];
+ defaultSystemColors = [[NSColorList alloc] initWithName: @"System"];
+ [NSColorList _setDefaultSystemColorList: defaultSystemColors];
if (systemColors == nil)
{
- systemColors = [[NSColorList alloc] initWithName: @"System"];
+ ASSIGN(systemColors, defaultSystemColors);
}
{
@@ -240,24 +254,24 @@ void initSystemColors(void)
while ((key = (NSString *)[enumerator nextObject]))
{
- NSString *aColorString;
NSColor *color;
- if ([systemColors colorWithKey: key])
- continue;
+ if ((color = [systemColors colorWithKey: key]) == nil)
+ {
+ NSString *aColorString;
- aColorString = [colorStrings objectForKey: key];
- color = [NSColorClass colorFromString: aColorString];
+ aColorString = [colorStrings objectForKey: key];
+ color = [NSColorClass colorFromString: aColorString];
- NSCAssert1(color, @"couldn't get default system color %@", key);
-
- [systemColors setColor: color forKey: key];
-
- changed = YES;
+ NSCAssert1(color, @"couldn't get default system color %@", key);
+ [systemColors setColor: color forKey: key];
+ changed = YES;
+ }
+ if (defaultSystemColors != systemColors)
+ {
+ [defaultSystemColors setColor: color forKey: key];
+ }
}
-
- if (changed)
- [systemColors writeToFile: nil];
}
systemDict = [NSMutableDictionary new];
@@ -309,6 +323,12 @@ systemColorWithName(NSString *name)
selector: @selector(defaultsDidChange:)
name: NSUserDefaultsDidChangeNotification
object: nil];
+ // watch for themes which may provide new system color lists
+ [[NSNotificationCenter defaultCenter]
+ addObserver: self
+ selector: @selector(themeDidActivate:)
+ name: GSThemeDidActivateNotification
+ object: nil];
}
}
@@ -1636,6 +1656,35 @@ systemColorWithName(NSString *name)
}
}
+/*
+ * Handle activation of a new theme ... look for a 'System' color list
+ * in the theme bundle and use it instead of the default system color
+ * list if it is present.
+ */
++ (void) themeDidActivate: (NSNotification*)notification
+{
+ NSDictionary *userInfo = [notification userInfo];
+ NSColorList *list = [userInfo objectForKey: @"Colors"];
+
+ if (list == nil)
+ {
+ list = defaultSystemColors;
+ }
+ NSAssert([[list name] isEqual: @"System"], NSInvalidArgumentException);
+ [NSColorList _setThemeSystemColorList: list];
+
+ /* We always update the system dictionary and send a notification, since
+ * the theme may have gicen us a pre-existing color list, but have changed
+ * one or more of the colors in it.
+ */
+ list = [NSColorList colorListNamed: @"System"];
+ ASSIGN(systemColors, list);
+ [systemDict removeAllObjects];
+NSLog(@"Theme activation with control background %@", [self controlBackgroundColor]);
+ [[NSNotificationCenter defaultCenter]
+ postNotificationName: NSSystemColorsDidChangeNotification object: nil];
+}
+
@end
diff --git a/Source/NSColorList.m b/Source/NSColorList.m
index 4cdfddaf3..2d78b6dbb 100644
--- a/Source/NSColorList.m
+++ b/Source/NSColorList.m
@@ -29,6 +29,7 @@
#include "config.h"
#include
+ * If called with a nil argument, this will check to see if the
+ * lists have already been loaded, and only load if they haven't been.
+ */
++ (void) _loadAvailableColorLists: (NSNotification*)aNotification;
+
+/* Set the default system color list ... to be used if no system color
+ * list has been loaded from file. This should always be the last of
+ * the array of available color lists even though it has noit been
+ * written to file.
+ */
++ (void) _setDefaultSystemColorList: (NSColorList*)aList;
+
+/* Set the theme system color list ... if this is not nil, it is placed
+ * at the start of the array of available lists and is used as the system
+ * color list.
+ */
++ (void) _setThemeSystemColorList: (NSColorList*)aList;
+
+@end
@implementation NSColorList
//
// Class methods
//
-+ (void)initialize
++ (void) initialize
{
if (self == [NSColorList class])
{
[self setVersion: 2];
+ _colorListLock = [NSRecursiveLock new];
}
}
-/*
- * Private Method which loads the color lists.
- * Invoke if _gnustep_available_color_lists == nil
- * before any operation with that object or its lock.
- *
- * The aim is to defer reading the available color lists
- * till we really need to, so that only programs which really use
- * this feature get the overhead.
- */
-+ (void) _loadAvailableColorLists
-{
- NSString *dir;
- NSString *file;
- NSEnumerator *e;
- NSFileManager *fm = [NSFileManager defaultManager];
- NSDirectoryEnumerator *de;
- NSColorList *newList;
-
- // Create the global array of color lists
- _gnustep_available_color_lists = [[NSMutableArray alloc] init];
-
- /*
- * Load color lists found in standard paths into the array
- * FIXME: Check exactly where in the directory tree we should scan.
- */
- e = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
- NSAllDomainsMask, YES) objectEnumerator];
-
- while ((dir = (NSString *)[e nextObject]))
- {
- BOOL flag;
-
- dir = [dir stringByAppendingPathComponent: @"Colors"];
- if (![fm fileExistsAtPath: dir isDirectory: &flag] || !flag)
- {
- // Only process existing directories
- continue;
- }
-
- de = [fm enumeratorAtPath: dir];
- while ((file = [de nextObject]))
- {
- if ([[file pathExtension] isEqualToString: @"clr"])
- {
- NSString *name = [file stringByDeletingPathExtension];
- newList = [[NSColorList alloc] initWithName: name
- fromFile: [dir stringByAppendingPathComponent: file]];
- [_gnustep_available_color_lists addObject: newList];
- RELEASE(newList);
- }
- }
- }
-
- // And create its access lock
- _gnustep_color_list_lock = [[NSLock alloc] init];
-}
-
/*
* Getting All Color Lists
*/
@@ -126,15 +96,11 @@ static NSLock *_gnustep_color_list_lock = nil;
{
NSArray *a;
- if (_gnustep_available_color_lists == nil)
- [NSColorList _loadAvailableColorLists];
-
// Serialize access to color list
- [_gnustep_color_list_lock lock];
-
- a = [NSArray arrayWithArray: _gnustep_available_color_lists];
-
- [_gnustep_color_list_lock unlock];
+ [_colorListLock lock];
+ [NSColorList _loadAvailableColorLists: nil];
+ a = [NSArray arrayWithArray: _availableColorLists];
+ [_colorListLock unlock];
return a;
}
@@ -146,31 +112,25 @@ static NSLock *_gnustep_color_list_lock = nil;
{
NSColorList *r;
NSEnumerator *e;
- BOOL found = NO;
-
- if (_gnustep_available_color_lists == nil)
- [NSColorList _loadAvailableColorLists];
// Serialize access to color list
- [_gnustep_color_list_lock lock];
+ [_colorListLock lock];
- e = [_gnustep_available_color_lists objectEnumerator];
+ [NSColorList _loadAvailableColorLists: nil];
+ e = [_availableColorLists objectEnumerator];
- while ((r = (NSColorList *)[e nextObject]))
+ while ((r = (NSColorList *)[e nextObject]) != nil)
{
if ([[r name] isEqualToString: name])
{
- found = YES;
+ RETAIN(r);
break;
}
}
- [_gnustep_color_list_lock unlock];
+ [_colorListLock unlock];
- if (found)
- return r;
- else
- return nil;
+ return AUTORELEASE(r);
}
@@ -371,6 +331,8 @@ static NSLock *_gnustep_color_list_lock = nil;
key: (NSString *)key
atIndex: (unsigned)location
{
+ NSNotification *n;
+
if (_is_editable == NO)
[NSException raise: NSColorListNotEditableException
format: @"Color list cannot be edited\n"];
@@ -379,13 +341,20 @@ static NSLock *_gnustep_color_list_lock = nil;
[_orderedColorKeys removeObject: key];
[_orderedColorKeys insertObject: key atIndex: location];
- [[NSNotificationCenter defaultCenter]
- postNotificationName: NSColorListChangedNotification
- object: self];
+ n = [NSNotification notificationWithName: NSColorListChangedNotification
+ object: self
+ userInfo: nil];
+ [[NSNotificationQueue defaultQueue]
+ enqueueNotification: n
+ postingStyle: NSPostASAP
+ coalesceMask: NSNotificationCoalescingOnSender
+ forModes: nil];
}
- (void) removeColorWithKey: (NSString *)key
{
+ NSNotification *n;
+
if (_is_editable == NO)
[NSException raise: NSColorListNotEditableException
format: @"Color list cannot be edited\n"];
@@ -393,14 +362,21 @@ static NSLock *_gnustep_color_list_lock = nil;
[_colorDictionary removeObjectForKey: key];
[_orderedColorKeys removeObject: key];
- [[NSNotificationCenter defaultCenter]
- postNotificationName: NSColorListChangedNotification
- object: self];
+ n = [NSNotification notificationWithName: NSColorListChangedNotification
+ object: self
+ userInfo: nil];
+ [[NSNotificationQueue defaultQueue]
+ enqueueNotification: n
+ postingStyle: NSPostASAP
+ coalesceMask: NSNotificationCoalescingOnSender
+ forModes: nil];
}
- (void) setColor: (NSColor *)aColor
forKey: (NSString *)key
{
+ NSNotification *n;
+
if (_is_editable == NO)
[NSException raise: NSColorListNotEditableException
format: @"Color list cannot be edited\n"];
@@ -410,9 +386,14 @@ static NSLock *_gnustep_color_list_lock = nil;
if ([_orderedColorKeys containsObject: key] == NO)
[_orderedColorKeys addObject: key];
- [[NSNotificationCenter defaultCenter]
- postNotificationName: NSColorListChangedNotification
- object: self];
+ n = [NSNotification notificationWithName: NSColorListChangedNotification
+ object: self
+ userInfo: nil];
+ [[NSNotificationQueue defaultQueue]
+ enqueueNotification: n
+ postingStyle: NSPostASAP
+ coalesceMask: NSNotificationCoalescingOnSender
+ forModes: nil];
}
/*
@@ -438,8 +419,7 @@ static NSLock *_gnustep_color_list_lock = nil;
* We need to initialize before saving, to avoid the new file being
* counted as a different list thus making us appear twice
*/
- if (_gnustep_available_color_lists == nil)
- [NSColorList _loadAvailableColorLists];
+ [NSColorList _loadAvailableColorLists: nil];
if (path == nil || ([fm fileExistsAtPath: path isDirectory: &isDir] == NO))
{
@@ -513,10 +493,10 @@ static NSLock *_gnustep_color_list_lock = nil;
if (success && path_is_standard)
{
- [_gnustep_color_list_lock lock];
- if ([_gnustep_available_color_lists containsObject: self] == NO)
- [_gnustep_available_color_lists addObject: self];
- [_gnustep_color_list_lock unlock];
+ [_colorListLock lock];
+ if ([_availableColorLists containsObject: self] == NO)
+ [_availableColorLists addObject: self];
+ [_colorListLock unlock];
return YES;
}
@@ -532,12 +512,9 @@ static NSLock *_gnustep_color_list_lock = nil;
handler: nil];
// Remove the color list from the global list of colors
- if (_gnustep_available_color_lists == nil)
- [NSColorList _loadAvailableColorLists];
-
- [_gnustep_color_list_lock lock];
- [_gnustep_available_color_lists removeObject: self];
- [_gnustep_color_list_lock unlock];
+ [_colorListLock lock];
+ [_availableColorLists removeObject: self];
+ [_colorListLock unlock];
// Reset file name
_fullFileName = nil;
@@ -562,3 +539,117 @@ static NSLock *_gnustep_color_list_lock = nil;
@end
+@implementation NSColorList (GNUstepPrivate)
+
++ (void) _loadAvailableColorLists: (NSNotification*)aNotification
+{
+ [_colorListLock lock];
+ /* FIXME ... we should ensure that we get housekeeping notifications */
+ if (_availableColorLists != nil && aNotification == nil)
+ {
+ // Nothing to do ... already loaded
+ [_colorListLock unlock];
+ }
+ else
+ {
+ NSString *dir;
+ NSString *file;
+ NSEnumerator *e;
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSDirectoryEnumerator *de;
+ NSColorList *newList;
+
+ if (_availableColorLists == nil)
+ {
+ // Create the global array of color lists
+ _availableColorLists = [[NSMutableArray alloc] init];
+ }
+ else
+ {
+ [_availableColorLists removeAllObjects];
+ }
+
+ /*
+ * Keep any pre-loaded system color list.
+ */
+ if (themeColorList != nil)
+ {
+ [_availableColorLists addObject: themeColorList];
+ }
+
+ /*
+ * Load color lists found in standard paths into the array
+ * FIXME: Check exactly where in the directory tree we should scan.
+ */
+ e = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
+ NSAllDomainsMask, YES) objectEnumerator];
+
+ while ((dir = (NSString *)[e nextObject]))
+ {
+ BOOL flag;
+
+ dir = [dir stringByAppendingPathComponent: @"Colors"];
+ if (![fm fileExistsAtPath: dir isDirectory: &flag] || !flag)
+ {
+ // Only process existing directories
+ continue;
+ }
+
+ de = [fm enumeratorAtPath: dir];
+ while ((file = [de nextObject]))
+ {
+ if ([[file pathExtension] isEqualToString: @"clr"])
+ {
+ NSString *name;
+
+ name = [file stringByDeletingPathExtension];
+ newList = [[NSColorList alloc] initWithName: name
+ fromFile: [dir stringByAppendingPathComponent: file]];
+ [_availableColorLists addObject: newList];
+ RELEASE(newList);
+ }
+ }
+ }
+
+ if (defaultSystemColorList != nil)
+ {
+ [_availableColorLists addObject: defaultSystemColorList];
+ }
+ [_colorListLock unlock];
+ }
+}
+
++ (void) _setDefaultSystemColorList: (NSColorList*)aList
+{
+ [_colorListLock lock];
+ if (defaultSystemColorList != aList)
+ {
+ if (defaultSystemColorList != nil
+ && [_availableColorLists lastObject] == defaultSystemColorList)
+ {
+ [_availableColorLists removeLastObject];
+ }
+ ASSIGN(defaultSystemColorList, aList);
+ [_availableColorLists addObject: aList];
+ }
+ [_colorListLock unlock];
+}
+
++ (void) _setThemeSystemColorList: (NSColorList*)aList
+{
+ [_colorListLock lock];
+ if (themeColorList != aList)
+ {
+ if (themeColorList != nil && [_availableColorLists count] > 0
+ && [_availableColorLists objectAtIndex: 0] == themeColorList)
+ {
+ [_availableColorLists removeObjectAtIndex: 0];
+ }
+ ASSIGN(themeColorList, aList);
+ [_availableColorLists insertObject: aList atIndex: 0];
+ }
+ [_colorListLock unlock];
+}
+
+@end
+
diff --git a/Source/NSTabView.m b/Source/NSTabView.m
index 7a9988941..fd9d7147a 100644
--- a/Source/NSTabView.m
+++ b/Source/NSTabView.m
@@ -365,6 +365,7 @@
- (void) drawRect: (NSRect)rect
{
NSGraphicsContext *ctxt = GSCurrentContext();
+ GSDrawFunctions *theme = [GSDrawFunctions theme];
int howMany = [_items count];
int i;
NSRect previousRect = NSMakeRect(0, 0, 0, 0);
@@ -378,18 +379,18 @@
default:
case NSTopTabsBezelBorder:
aRect.size.height -= 16;
- [GSDrawFunctions drawButton: aRect : NSZeroRect];
+ [theme drawButton: aRect withClip: NSZeroRect];
break;
case NSBottomTabsBezelBorder:
aRect.size.height -= 16;
aRect.origin.y += 16;
- [GSDrawFunctions drawButton: aRect : rect];
+ [theme drawButton: aRect withClip: rect];
aRect.origin.y -= 16;
break;
case NSNoTabsBezelBorder:
- [GSDrawFunctions drawButton: aRect : rect];
+ [theme drawButton: aRect withClip: rect];
break;
case NSNoTabsLineBorder: