Tk Source Code

Documentation
Login
/*
 * ttkMacOSXTheme.c --
 *
 *	Tk theme engine for Mac OSX, using the Appearance Manager API.
 *
 * Copyright (c) 2004 Joe English
 * Copyright (c) 2005 Neil Madden
 * Copyright (c) 2006-2009 Daniel A. Steffen <[email protected]>
 * Copyright 2008-2009, Apple Inc.
 * Copyright 2009 Kevin Walzer/WordTech Communications LLC.
 * Copyright 2019 Marc Culler
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * See also:
 *
 * <URL: http://developer.apple.com/documentation/Carbon/Reference/
 *	Appearance_Manager/appearance_manager/APIIndex.html >
 *
 * Notes:
 *	"Active" means different things in Mac and Tk terminology --
 *	On Aqua, widgets are "Active" if they belong to the foreground window,
 *	"Inactive" if they are in a background window.
 *	Tk uses the term "active" to mean that the mouse cursor
 *	is over a widget; aka "hover", "prelight", or "hot-tracked".
 *	Aqua doesn't use this kind of feedback.
 *
 *	The QuickDraw/Carbon coordinate system is relative to the
 *	top-level window, not to the Tk_Window.  BoxToRect()
 *	accounts for this.
 */

#include "tkMacOSXPrivate.h"
#include "ttk/ttkTheme.h"
#include <math.h>
/*
 * Macros for handling drawing contexts.
 */
#define BEGIN_DRAWING(d) { \
    TkMacOSXDrawingContext dc; \
    if (!TkMacOSXSetupDrawingContext((d), NULL, 1, &dc)) {return;}
#define END_DRAWING \
    TkMacOSXRestoreDrawingContext(&dc); }

#define HIOrientation kHIThemeOrientationNormal
#define NoThemeMetric 0xFFFFFFFF

#ifdef __LP64__
#define RangeToFactor(maximum) (((double) (INT_MAX >> 1)) / (maximum))
#else
#define RangeToFactor(maximum) (((double) (LONG_MAX >> 1)) / (maximum))
#endif /* __LP64__ */

#define TTK_STATE_FIRST_TAB	TTK_STATE_USER1
#define TTK_STATE_LAST_TAB 	TTK_STATE_USER2

/*----------------------------------------------------------------------
 * +++ Utilities.
 */

/*
 * BoxToRect --
 *    Convert a Ttk_Box in Tk coordinates relative to the given Drawable
 *    to a native Rect relative to the containing port.
 */
static inline CGRect BoxToRect(Drawable d, Ttk_Box b)
{
    MacDrawable *md = (MacDrawable*)d;
    CGRect rect;

    rect.origin.y	= b.y + md->yOff;
    rect.origin.x	= b.x + md->xOff;
    rect.size.height	= b.height;
    rect.size.width	= b.width;

    return rect;
}

/*
 * Table mapping Tk states to Appearance manager ThemeStates
 */

static Ttk_StateTable ThemeStateTable[] = {
    {kThemeStateUnavailable, TTK_STATE_DISABLED, 0},
    {kThemeStatePressed, TTK_STATE_PRESSED, 0},
    {kThemeStateInactive, TTK_STATE_BACKGROUND, 0},
    {kThemeStateActive, 0, 0}
/* Others: Not sure what these are supposed to mean.
   Up/Down have something to do with "little arrow" increment controls...
   Dunno what a "Rollover" is.
   NEM: Rollover is TTK_STATE_ACTIVE... but we don't handle that yet, by the
   looks of things
    {kThemeStateRollover, 0, 0},
    {kThemeStateUnavailableInactive, 0, 0}
    {kThemeStatePressedUp, 0, 0},
    {kThemeStatePressedDown, 0, 0}
*/
};

#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
/*----------------------------------------------------------------------
 * +++ Support for contrasting background colors when GroupBoxes
 * or Tabbed panes are nested inside each other.
 */

/*
 * For systems older than 10.14, [NSColor windowBackGroundColor] generates
 * garbage when called from this function.  In 10.14 it works correctly,
 * and must be used in order to have a background color which responds
 * to Dark Mode.  So we use this hard-wired RGBA color on the older systems
 * which don't support Dark Mode anyway.
 */

static CGFloat windowBackground[4] = {235.0/255, 235.0/255, 235.0/255, 1.0};

static int GetBoxColor(
    CGContextRef context,
    Tk_Window tkwin,
    int depth,
    CGFloat *fill)
{
    TkWindow *winPtr = (TkWindow *)tkwin;
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    if ([NSApp macMinorVersion] > 13) {
        NSColor *windowColor = [[NSColor windowBackgroundColor]
				   colorUsingColorSpace: deviceRGB];
        [windowColor getComponents: fill];
    } else {
	for (int i = 0; i < 4; i++) {
	    fill[i] = windowBackground[i];
	}
    }
    int isDark  = (fill[0] + fill[1] + fill[2] < 1.5);

    /*
     * Compute the nesting depth of the widget.
     */

    for (TkWindow *topPtr = winPtr->parentPtr; topPtr != NULL;
	 topPtr = topPtr->parentPtr) {
	if (topPtr->privatePtr &&
	    (topPtr->privatePtr->flags & TTK_HAS_DARKER_BG)) {
	    depth++;
	}
	if (depth > 7) {
	    break;
	}
    }

    /*
     * Increase the contrast by one step for each level of nesting.
     */

    if (isDark) {
	for (int i=0; i<3; i++) {
	    fill[i] += (1.0 + depth*7.0)/255.0;
	}
    } else {
	for (int i=0; i<3; i++) {
	    fill[i] -= (depth*8.0)/255.0;
	}
    }
    return depth;
}

/*
 * DrawGroupBox --
 *
 *    This is a standalone drawing procedure which draws the contrasting
 *    rounded rectangular box for LabelFrames and Notebook panes.
 */

static void DrawGroupBox(
    CGRect bounds,
    CGContextRef context,
    Tk_Window tkwin)
{
    CGPathRef path;
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *borderColor, *bgColor;
    static CGFloat border[4] = {1.0, 1.0, 1.0, 0.25};
    CGFloat fill[4];
    GetBoxColor(context, tkwin, 1, fill);
    bgColor = [NSColor colorWithColorSpace: deviceRGB components: fill
				     count: 4];
    CGContextSetFillColorSpace(context, deviceRGB.CGColorSpace);
    CGContextSetFillColorWithColor(context, bgColor.CGColor);
    path = CGPathCreateWithRoundedRect(bounds, 4, 4, NULL);
    CGContextClipToRect(context, bounds);
    CGContextBeginPath(context);
    CGContextAddPath(context, path);
    CGContextFillPath(context);
    borderColor = [NSColor colorWithColorSpace: deviceRGB components: border
					 count: 4];
    CGContextSetFillColorWithColor(context, borderColor.CGColor);
    [borderColor getComponents: fill];
    CGContextSetRGBFillColor(context, fill[0], fill[1], fill[2], fill[3]);

    CGContextBeginPath(context);
    CGContextAddPath(context, path);
    CGContextReplacePathWithStrokedPath(context);
    CGContextFillPath(context);
    CFRelease(path);
}

/*
 * NormalizeButtonBounds --
 *
 * Apple's Human Interface Guidelines only allow three specific heights for buttons:
 * Regular, small and mini. We always use the regular size.  However, Ttk may
 * provide an arbitrary bounding rectangle.  We always draw the button centered
 * vertically on the rectangle, and having the same width as the rectangle.
 * This function returns the actual bounding rectangle that will be used in
 * drawing the button.
 */

static CGRect NormalizeButtonBounds(
    SInt32 heightMetric,
    CGRect bounds)
{
    SInt32 height;
    ChkErr(GetThemeMetric, heightMetric, &height);
    bounds.origin.y += (bounds.size.height - height)/2;
    bounds.size.height = height;
    return bounds;
}

/* SolidFillButtonFace --
 *
 *     Fill a rounded rectangle with a specified solid color.
 */

static void SolidFillButtonFace(
   CGContextRef context,
   CGRect bounds,
   CGFloat radius,
   NSColor *color)
{
    CGPathRef path;
    CGContextSetFillColorWithColor(context, color.CGColor);
    path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
    CGContextBeginPath(context);
    CGContextAddPath(context, path);
    CGContextFillPath(context);
    CFRelease(path);
}

#endif /* MAC_OS_X_VERSION_MIN_REQUIRED > 1080 */

#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
/*----------------------------------------------------------------------
 * +++ Drawing procedures for widgets in Apple's "Dark Mode" (10.14 and up).
 *
 *    The HIToolbox does not support Dark Mode, and apparently never will,
 *    so to make widgets look "native" we have to provide analogues of the
 *    HITheme drawing functions to be used in DarkAqua.  We continue to use
 *    HITheme in Aqua, since it understands earlier versions of the OS.
 */

/*
 *  Colors and gradients used in Dark Mode.
 */

static CGFloat darkButtonFace[4] = {112.0/255, 113.0/255, 115.0/255, 1.0};
static CGFloat darkDisabledButtonFace[4] = {86.0/255, 87.0/255, 89.0/255, 1.0};
static CGFloat darkInactiveSelectedTab[4] = {159.0/255, 160.0/255, 161.0/255, 1.0};
static CGFloat darkTabSeparator[4] = {0.0, 0.0, 0.0, 0.25};
static CGFloat darkTrack[4] = {1.0, 1.0, 1.0, 0.25};
static CGFloat darkTopGradient[8] = {1.0, 1.0, 1.0, 0.3,
				     1.0, 1.0, 1.0, 0.0};
static CGFloat darkBackgroundGradient[8] = {0.0, 0.0, 0.0, 0.1,
					    0.0, 0.0, 0.0, 0.25};
static CGFloat darkInactiveGradient[8] = {89.0/255, 90.0/255, 93.0/255, 1.0,
				       119.0/255, 120.0/255, 122.0/255, 1.0};
static CGFloat darkSelectedGradient[8] = {23.0/255, 111.0/255, 232.0/255, 1.0,
					  20.0/255, 94.0/255,  206.0/255, 1.0};

/* FillButtonBackground --
 *
 *    Fills a rounded rectangle with a transparent black gradient.
 */

static void FillButtonBackground(
    CGContextRef context,
    CGRect bounds,
    CGFloat radius)
{
    CGPathRef path;
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    CGGradientRef backgroundGradient = CGGradientCreateWithColorComponents(
	deviceRGB.CGColorSpace, darkBackgroundGradient, NULL, 2);
    CGPoint backgroundEnd = {bounds.origin.x,
			     bounds.origin.y + bounds.size.height};
    CGContextBeginPath(context);
    path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
    CGContextAddPath(context, path);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, backgroundGradient,
				bounds.origin, backgroundEnd, 0);
    CFRelease(path);
    CFRelease(backgroundGradient);
}

/* HighlightButtonBorder --
 *
 * Accent the top border of a rounded rectangle with a transparent
 * white gradient.
 */

static void HighlightButtonBorder(
    CGContextRef context,
    CGRect bounds)
{
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    CGPoint topEnd = {bounds.origin.x, bounds.origin.y + 3};
    CGGradientRef topGradient = CGGradientCreateWithColorComponents(
    		      deviceRGB.CGColorSpace, darkTopGradient, NULL, 2);
    CGContextSaveGState(context);
    CGContextBeginPath(context);
    CGContextAddArc(context, bounds.origin.x + 4, bounds.origin.y + 4,
    		    4, PI, 3*PI/2, 0);
    CGContextAddArc(context, bounds.origin.x + bounds.size.width - 4,
    		    bounds.origin.y + 4, 4, 3*PI/2, 0, 0);
    CGContextReplacePathWithStrokedPath(context);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, topGradient, bounds.origin, topEnd, 0.0);
    CGContextRestoreGState(context);
    CFRelease(topGradient);
}

/* GradientFillButtonFace --
 *
 *     Fill a rounded rectangle with a specified gradient.
 */

static void GradientFillButtonFace(
   CGContextRef context,
   CGRect bounds,
   CGFloat radius,
   CGFloat* colors,
   int numColors)
{
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    CGPathRef path;
    CGPoint end = {bounds.origin.x,
		   bounds.origin.y + bounds.size.height};
    CGGradientRef gradient = CGGradientCreateWithColorComponents(
	 deviceRGB.CGColorSpace, colors, NULL, numColors);
    path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
    CGContextBeginPath(context);
    CGContextAddPath(context, path);
    CGContextClip(context);
    CGContextDrawLinearGradient(context, gradient, bounds.origin, end, 0);
    CFRelease(path);
    CFRelease(gradient);
}

/*
 * DrawDarkButton --
 *
 *    This is a standalone drawing procedure which draws PushButtons and
 *    PopupButtons in the Dark Mode style.
 */

static void DrawDarkButton(
    CGRect bounds,
    ThemeButtonKind kind,
    Ttk_State state,
    CGContextRef context)
{
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *faceColor;

    /*
     * To match the appearance of Apple's buttons we need to increase the
     * height by 1 pixel.
     */

    bounds.size.height += 1;

    CGContextClipToRect(context, bounds);
    FillButtonBackground(context, bounds, 5);

    /*
     * Fill the button face with the appropriate color.
     */

    bounds = CGRectInset(bounds, 1, 1);
    if (kind == kThemePushButton && (state & TTK_STATE_PRESSED)) {
	GradientFillButtonFace(context, bounds, 4,
				   darkSelectedGradient, 2);
    } else {
	if (state & TTK_STATE_DISABLED) {
	    faceColor = [NSColor colorWithColorSpace: deviceRGB
					  components: darkDisabledButtonFace
					       count: 4];
	} else {
	    faceColor = [NSColor colorWithColorSpace: deviceRGB
					  components: darkButtonFace
					       count: 4];
	}
	SolidFillButtonFace(context, bounds, 4, faceColor);
    }
	
    /*
     * If this is a popup, draw the arrow button.
     */

    if (kind == kThemePopupButton) {
	CGFloat x, y;
	CGRect arrowBounds = bounds;
	arrowBounds.size.width = 16;
	arrowBounds.origin.x += bounds.size.width - 16;
	CGContextSaveGState(context);

	/*
	 * If the toplevel is front, paint the button blue.
	 */

	if (!(state & TTK_STATE_BACKGROUND)) {
	    GradientFillButtonFace(context, arrowBounds, 4,
				   darkSelectedGradient, 2);
	}

	/*
	 * Stroke the arrows.
	 */

	CGContextSetRGBStrokeColor(context, 1.0, 1.0, 1.0, 1.0);
	CGContextSetLineWidth(context, 1.5);
	x = arrowBounds.origin.x + 5;
	y = arrowBounds.origin.y + trunc(arrowBounds.size.height/2);
	CGContextBeginPath(context);
	CGPoint bottomArrow[3] = {{x, y+2}, {x+3.5, y+5.5}, {x+7, y+2}};
	CGContextAddLines(context, bottomArrow, 3);
	CGPoint topArrow[3] = {{x, y-2}, {x+3.5, y-5.5}, {x+7, y-2}};
	CGContextAddLines(context, topArrow, 3);
	CGContextStrokePath(context);
	CGContextRestoreGState(context);
    }

    HighlightButtonBorder(context, bounds);
}

/*
 * DrawDarkCheckBox --
 *
 *    This is a standalone drawing procedure which draws Checkboxes
 *    in the Dark Mode style.
 */

static void DrawDarkCheckBox(
    CGRect bounds,
    Ttk_State state,
    CGContextRef context)
{
    CGRect checkbounds = {{0, bounds.size.height/2 - 8},{16, 16}};
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *stroke;
    CGFloat x, y;
    bounds = CGRectOffset(checkbounds, bounds.origin.x, bounds.origin.y);
    x = bounds.origin.x;
    y = bounds.origin.y;

    CGContextClipToRect(context, bounds);
    FillButtonBackground(context, bounds, 4);
    bounds = CGRectInset(bounds, 1, 1);
    if (!(state & TTK_STATE_BACKGROUND) &&
	!(state & TTK_STATE_DISABLED) &&
	((state  & TTK_STATE_SELECTED) || (state & TTK_STATE_ALTERNATE))) {
	GradientFillButtonFace(context, bounds, 3, darkSelectedGradient, 2);
    } else {
	GradientFillButtonFace(context, bounds, 3, darkInactiveGradient, 2);
    }
    HighlightButtonBorder(context, bounds);
    if ((state  & TTK_STATE_SELECTED) || (state & TTK_STATE_ALTERNATE)) {
	CGContextSetStrokeColorSpace(context, deviceRGB.CGColorSpace);
	if (state & TTK_STATE_DISABLED) {
	    stroke = [NSColor disabledControlTextColor];
	} else {
	    stroke = [NSColor controlTextColor];
	}
	CGContextSetStrokeColorWithColor(context, stroke.CGColor);
    }
    if (state & TTK_STATE_SELECTED) {
	CGContextSetLineWidth(context, 1.5);
	CGContextBeginPath(context);
	CGPoint check[3] = {{x+4, y+8}, {x+7, y+11}, {x+11, y+4}};
	CGContextAddLines(context, check, 3);
	CGContextStrokePath(context);
    } else if (state & TTK_STATE_ALTERNATE) {
	CGContextSetLineWidth(context, 2.0);
	CGContextBeginPath(context);
	CGPoint bar[2] = {{x+4, y+8}, {x+12, y+8}};
	CGContextAddLines(context, bar, 2);
	CGContextStrokePath(context);
    }
}

/*
 * DrawDarkRadioButton --
 *
 *    This is a standalone drawing procedure which draws RadioButtons
 *    in the Dark Mode style.
 */

static void DrawDarkRadioButton(
    CGRect bounds,
    Ttk_State state,
    CGContextRef context)
{
    CGRect checkbounds = {{0, bounds.size.height/2 - 9},{18, 18}};
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *fill;
    CGFloat x, y;
    bounds = CGRectOffset(checkbounds, bounds.origin.x, bounds.origin.y);
    x = bounds.origin.x;
    y = bounds.origin.y;

    CGContextClipToRect(context, bounds);
    FillButtonBackground(context, bounds, 9);
    bounds = CGRectInset(bounds, 1, 1);
    if (!(state & TTK_STATE_BACKGROUND) &&
	!(state & TTK_STATE_DISABLED) &&
	((state  & TTK_STATE_SELECTED) || (state & TTK_STATE_ALTERNATE))) {
	GradientFillButtonFace(context, bounds, 8, darkSelectedGradient, 2);
    } else {
	GradientFillButtonFace(context, bounds, 8, darkInactiveGradient, 2);
    }
    HighlightButtonBorder(context, bounds);
    if ((state  & TTK_STATE_SELECTED) || (state & TTK_STATE_ALTERNATE)) {
	CGContextSetStrokeColorSpace(context, deviceRGB.CGColorSpace);
	if (state & TTK_STATE_DISABLED) {
	    fill = [NSColor disabledControlTextColor];
	} else {
	    fill = [NSColor controlTextColor];
	}
	CGContextSetFillColorWithColor(context, fill.CGColor);
    }
    if (state & TTK_STATE_SELECTED) {
	CGContextBeginPath(context);
	CGRect dot = {{x + 6, y + 6}, {6, 6}};
	CGContextAddEllipseInRect(context, dot);
	CGContextFillPath(context);
    } else if (state & TTK_STATE_ALTERNATE) {
	CGRect bar = {{x + 5, y + 8}, {8, 2}};
	CGContextFillRect(context, bar);
    }
}

/*
 * DrawDarkTab --
 *
 *    This is a standalone drawing procedure which draws Tabbed Pane
 *    Tabs in the Dark Mode style.
 */

static void DrawDarkTab(
    CGRect bounds,
    Ttk_State state,
    CGContextRef context)
{
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *faceColor, *stroke;
    CGRect originalBounds= bounds;

    CGContextSetLineWidth(context, 1.0);
    CGContextClipToRect(context, bounds);

    /*
     * Extend the bounds to one or both sides so the rounded part will be
     * clipped off.
     */

    if (!(state & TTK_STATE_FIRST_TAB)) {
	bounds.origin.x -= 10;
	bounds.size.width += 10;
    }

    if (!(state & TTK_STATE_LAST_TAB)) {
	bounds.size.width += 10;
    }

    /*
     * Fill the tab face with the appropriate color or gradient.  Use a
     * solid color if the tab is not selected, otherwise use a blue or
     * gray gradient.
     */

    bounds = CGRectInset(bounds, 1, 1);
    if (!(state & TTK_STATE_SELECTED)) {
	if (state & TTK_STATE_DISABLED) {
	    faceColor = [NSColor colorWithColorSpace: deviceRGB
				     components: darkDisabledButtonFace
					  count: 4];
	} else {
	    faceColor = [NSColor colorWithColorSpace: deviceRGB
				     components: darkButtonFace
					  count: 4];
	}
	SolidFillButtonFace(context, bounds, 4, faceColor);

	/*
	 * Draw a separator line on the left side of the tab if it
	 * not first.
	 */

	if (!(state & TTK_STATE_FIRST_TAB)) {
	    CGContextSaveGState(context);
	    CGContextSetShouldAntialias(context, false);
	    stroke = [NSColor colorWithColorSpace: deviceRGB
				       components: darkTabSeparator
					    count: 4];
	    CGContextSetStrokeColorWithColor(context, stroke.CGColor);
	    CGContextBeginPath(context);
	    CGContextMoveToPoint(context, originalBounds.origin.x,
	        originalBounds.origin.y + 1);
	    CGContextAddLineToPoint(context, originalBounds.origin.x,
	        originalBounds.origin.y + originalBounds.size.height - 1);
	    CGContextStrokePath(context);
	    CGContextRestoreGState(context);
	}
    } else {

	/*
	 * This is the selected tab; paint it blue.  If it is first, cover up
	 * the separator line drawn by the second one.  (The selected tab is
	 * always drawn last.)
	 */

	if ((state & TTK_STATE_FIRST_TAB) && !(state & TTK_STATE_LAST_TAB)) {
	    bounds.size.width += 1;
	}
	if (!(state & TTK_STATE_BACKGROUND)) {
	    GradientFillButtonFace(context, bounds, 4, darkSelectedGradient, 2);
	} else {
	    faceColor = [NSColor colorWithColorSpace: deviceRGB
				     components: darkInactiveSelectedTab
					  count: 4];
	    SolidFillButtonFace(context, bounds, 4, faceColor);
	}
	HighlightButtonBorder(context, bounds);
    }
}

/*
 * DrawDarkSeparator --
 *
 *    This is a standalone drawing procedure which draws a separator widget
 *    in Dark Mode.
 */

static void DrawDarkSeparator(
    CGRect bounds,
    CGContextRef context,
    Tk_Window tkwin)
{
    static CGFloat fill[4] = {1.0, 1.0, 1.0, 0.3};
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *fillColor = [NSColor colorWithColorSpace: deviceRGB
					   components: fill
						count:4];
    CGContextSetFillColorWithColor(context, fillColor.CGColor);
    CGContextFillRect(context, bounds);
}

#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >101300 */

/*----------------------------------------------------------------------
 * +++ Button element: Used for elements drawn with DrawThemeButton.
 */


typedef struct {
    ThemeButtonKind kind;
    ThemeMetric heightMetric;
} ThemeButtonParams;

static ThemeButtonParams
    PushButtonParams = { kThemePushButton, kThemeMetricPushButtonHeight },
    CheckBoxParams = { kThemeCheckBox, kThemeMetricCheckBoxHeight },
    RadioButtonParams = { kThemeRadioButton, kThemeMetricRadioButtonHeight },
    BevelButtonParams = { kThemeBevelButton, NoThemeMetric },
    PopupButtonParams = { kThemePopupButton, kThemeMetricPopupButtonHeight },
    DisclosureParams = { kThemeDisclosureButton, kThemeMetricDisclosureTriangleHeight },
    ListHeaderParams = { kThemeListHeaderButton, kThemeMetricListHeaderHeight };

static Ttk_StateTable ButtonValueTable[] = {
    { kThemeButtonMixed, TTK_STATE_ALTERNATE, 0 },
    { kThemeButtonOn, TTK_STATE_SELECTED, 0 },
    { kThemeButtonOff, 0, 0 }
/* Others: kThemeDisclosureRight, kThemeDisclosureDown, kThemeDisclosureLeft */
};

static Ttk_StateTable ButtonAdornmentTable[] = {
    { kThemeAdornmentDefault | kThemeAdornmentFocus,
	TTK_STATE_ALTERNATE | TTK_STATE_FOCUS, 0 },
    { kThemeAdornmentDefault, TTK_STATE_ALTERNATE, 0 },
    { kThemeAdornmentFocus, TTK_STATE_FOCUS, 0 },
    { kThemeAdornmentNone, 0, 0 }};

/*
 * computeButtonDrawInfo --
 *    Fill in an appearance manager HIThemeButtonDrawInfo record.
 */

static inline HIThemeButtonDrawInfo computeButtonDrawInfo(
    ThemeButtonParams *params,
    Ttk_State state,
    Tk_Window tkwin)
{
    /*
     *  See ButtonElementDraw for the explanation of why we always draw
     *  PushButtons in the active state.
     */

    SInt32 HIThemeState;
    switch (params->kind) {
    case kThemePushButton:
	HIThemeState = kThemeStateActive;
	break;
    default:
	HIThemeState = Ttk_StateTableLookup(ThemeStateTable, state);
	break;
    }
	
    const HIThemeButtonDrawInfo info = {
	.version = 0,
	.state = HIThemeState,
	.kind = params ? params->kind : 0,
	.value = Ttk_StateTableLookup(ButtonValueTable, state),
	.adornment = Ttk_StateTableLookup(ButtonAdornmentTable, state),
    };
    return info;
}

static void ButtonElementSizeNoPadding(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    ThemeButtonParams *params = clientData;

    if (params->heightMetric != NoThemeMetric) {
	SInt32 height;

	ChkErr(GetThemeMetric, params->heightMetric, &height);
	*heightPtr = height;
    }
}

static void ButtonElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    ThemeButtonParams *params = clientData;
    const HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, 0, tkwin);
    static const CGRect scratchBounds = {{0, 0}, {100, 100}};
    CGRect contentBounds;

    ButtonElementSizeNoPadding( clientData, elementRecord, tkwin,
    	widthPtr, heightPtr, paddingPtr);

    /*
     * To compute internal padding, query the appearance manager
     * for the content bounds of a dummy rectangle, then use
     * the difference as the padding.
     */

    ChkErr(HIThemeGetButtonContentBounds,
	&scratchBounds, &info, &contentBounds);

    paddingPtr->left = CGRectGetMinX(contentBounds);
    paddingPtr->right = CGRectGetMaxX(scratchBounds) - CGRectGetMaxX(contentBounds);
}

static void ButtonElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    BEGIN_DRAWING(d)
    ThemeButtonParams *params = clientData;
    CGRect bounds = BoxToRect(d, b);
    HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, state, tkwin);
    bounds = NormalizeButtonBounds(params->heightMetric, bounds);

    if (TkMacOSXInDarkMode(tkwin)) {
#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
	switch (info.kind) {
	case kThemePushButton:
	case kThemePopupButton:
	    DrawDarkButton(bounds, info.kind, state, dc.context);
	    break;
	case kThemeCheckBox:
	    DrawDarkCheckBox(bounds, state, dc.context);
	    break;
	case kThemeRadioButton:
	    DrawDarkRadioButton(bounds, state, dc.context);
	    break;
	default:
	    ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
	}
#endif
    } else {
	/*
	 *  Apple's PushButton and PopupButton do not change their (white) fill
	 *  color when the window is inactive although, except in 10.7 (Lion),
	 *  the color of the arrow button on a PopupButton does change.  For
	 *  some reason HITheme fills inactive buttons with a transparent color
	 *  that allows the window background to show through, leading to
	 *  inconsistent behavior.  We work around this by filling behind an
	 *  inactive PopupButton with a white color before asking HIToolbox to
	 *  draw it.  PopupButton.  For PushButtons, we simply draw them in the
	 *  active state.
	 */
	if (info.kind == kThemePopupButton && (state & TTK_STATE_BACKGROUND)) {
	    CGRect innerBounds = CGRectInset(bounds, 1, 1);
	    SolidFillButtonFace(dc.context, innerBounds, 4, [NSColor whiteColor]);
	}
	ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
    }
    END_DRAWING
}

static Ttk_ElementSpec ButtonElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    ButtonElementSize,
    ButtonElementDraw
};

/*----------------------------------------------------------------------
 * +++ Notebook elements.
 */


/* Tab position logic, c.f. ttkNotebook.c TabState() */

static Ttk_StateTable TabStyleTable[] = {
    { kThemeTabFrontInactive, TTK_STATE_SELECTED | TTK_STATE_BACKGROUND},
    { kThemeTabNonFrontInactive, TTK_STATE_BACKGROUND},
    { kThemeTabFrontUnavailable, TTK_STATE_DISABLED | TTK_STATE_SELECTED},
    { kThemeTabNonFrontUnavailable, TTK_STATE_DISABLED},
    { kThemeTabFront, TTK_STATE_SELECTED},
    { kThemeTabNonFrontPressed, TTK_STATE_PRESSED},
    { kThemeTabNonFront, 0}
};

static Ttk_StateTable TabAdornmentTable[] = {
    { kHIThemeTabAdornmentNone, TTK_STATE_FIRST_TAB | TTK_STATE_LAST_TAB},
    { kHIThemeTabAdornmentTrailingSeparator, TTK_STATE_FIRST_TAB},
    { kHIThemeTabAdornmentNone, TTK_STATE_LAST_TAB},
    { kHIThemeTabAdornmentTrailingSeparator, 0 },
};

static Ttk_StateTable TabPositionTable[] = {
    { kHIThemeTabPositionOnly, TTK_STATE_FIRST_TAB | TTK_STATE_LAST_TAB},
    { kHIThemeTabPositionFirst, TTK_STATE_FIRST_TAB},
    { kHIThemeTabPositionLast, TTK_STATE_LAST_TAB},
    { kHIThemeTabPositionMiddle, 0 },
};

/*
 * Apple XHIG Tab View Specifications:
 *
 * Control sizes: Tab views are available in regular, small, and mini sizes.
 * The tab height is fixed for each size, but you control the size of the pane
 * area. The tab heights for each size are listed below:
 *  - Regular size: 20 pixels.
 *  - Small: 17 pixels.
 *  - Mini: 15 pixels.
 *
 * Label spacing and fonts: The tab labels should be in a font that’s
 * proportional to the size of the tab view control. In addition, the label
 * should be placed so that there are equal margins of space before and after
 * it. The guidelines below provide the specifications you should use for tab
 * labels:
 *  - Regular size: System font. Center in tab, leaving 12 pixels on each side.
 *  - Small: Small system font. Center in tab, leaving 10 pixels on each side.
 *  - Mini: Mini system font. Center in tab, leaving 8 pixels on each side.
 *
 * Control spacing: Whether you decide to inset a tab view in a window or
 * extend its edges to the window sides and bottom, you should place the top
 * edge of the tab view 12 or 14 pixels below the bottom edge of the title bar
 * (or toolbar, if there is one). If you choose to inset a tab view in a
 * window, you should leave a margin of 20 pixels between the sides and bottom
 * of the tab view and the sides and bottom of the window (although 16 pixels
 * is also an acceptable margin-width). If you need to provide controls below
 * the tab view, leave enough space below the tab view so the controls are 20
 * pixels above the bottom edge of the window and 12 pixels between the tab
 * view and the controls.
 * If you choose to extend the tab view sides and bottom so that they meet the
 * window sides and bottom, you should leave a margin of at least 20 pixels
 * between the content in the tab view and the tab-view edges.
 *
 * <URL: http://developer.apple.com/documentation/userexperience/Conceptual/
 *       AppleHIGuidelines/XHIGControls/XHIGControls.html#//apple_ref/doc/uid/
 *       TP30000359-TPXREF116>
 */

static void TabElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    GetThemeMetric(kThemeMetricLargeTabHeight, (SInt32 *)heightPtr);
    *paddingPtr = Ttk_MakePadding(0, 0, 0, 2);

}

static void TabElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    CGRect bounds = BoxToRect(d, b);
    HIThemeTabDrawInfo info = {
	.version = 1,
	.style = Ttk_StateTableLookup(TabStyleTable, state),
	.direction = kThemeTabNorth,
	.size = kHIThemeTabSizeNormal,
	.adornment = Ttk_StateTableLookup(TabAdornmentTable, state),
	.kind = kHIThemeTabKindNormal,
	.position = Ttk_StateTableLookup(TabPositionTable, state),
    };
    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
    if (TkMacOSXInDarkMode(tkwin)) {
	DrawDarkTab(bounds, state, dc.context);
    } else {
	ChkErr(HIThemeDrawTab, &bounds, &info, dc.context, HIOrientation, NULL);
    }
#else
    ChkErr(HIThemeDrawTab, &bounds, &info, dc.context, HIOrientation, NULL);
#endif
    END_DRAWING
}

static Ttk_ElementSpec TabElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    TabElementSize,
    TabElementDraw
};

/*
 * Notebook panes:
 */

static void PaneElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    *paddingPtr = Ttk_MakePadding(9, 5, 9, 9);
}

static void PaneElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    TkWindow *winPtr = (TkWindow *)tkwin;
    MacDrawable *macWin = winPtr->privatePtr;
    CGRect bounds = BoxToRect(d, b);
    bounds.origin.y -= kThemeMetricTabFrameOverlap;
    bounds.size.height += kThemeMetricTabFrameOverlap;
    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 10800
    DrawGroupBox(bounds, dc.context, tkwin);
#else
    HIThemeTabPaneDrawInfo info = {
	.version = 1,
	.state = Ttk_StateTableLookup(ThemeStateTable, state),
	.direction = kThemeTabNorth,
	.size = kHIThemeTabSizeNormal,
	.kind = kHIThemeTabKindNormal,
	.adornment = kHIThemeTabPaneAdornmentNormal,
    };
    bounds.origin.y -= kThemeMetricTabFrameOverlap;
    bounds.size.height += kThemeMetricTabFrameOverlap;
    ChkErr(HIThemeDrawTabPane, &bounds, &info, dc.context, HIOrientation);
#endif
    END_DRAWING
    [TkMacOSXDrawableView(macWin) setNeedsDisplay:YES];
    if (macWin != NULL) {
	macWin->flags |= TTK_HAS_DARKER_BG;
    }
}

static Ttk_ElementSpec PaneElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    PaneElementSize,
    PaneElementDraw
};

/*
 * Labelframe borders:
 * Use "primary group box ..."
 * Quoth DrawThemePrimaryGroup reference:
 * "The primary group box frame is drawn inside the specified
 * rectangle and is a maximum of 2 pixels thick."
 *
 * "Maximum of 2 pixels thick" is apparently a lie;
 * looks more like 4 to me with shading.
 */

static void GroupElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    *paddingPtr = Ttk_UniformPadding(4);
}

static void GroupElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    TkWindow *winPtr = (TkWindow *)tkwin;
    CGRect bounds = BoxToRect(d, b);

    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 10800
    DrawGroupBox(bounds, dc.context, tkwin);
#else
    const HIThemeGroupBoxDrawInfo info = {
	.version = 0,
	.state = Ttk_StateTableLookup(ThemeStateTable, state),
	.kind = kHIThemeGroupBoxKindPrimaryOpaque,
    };
    ChkErr(HIThemeDrawGroupBox, &bounds, &info, dc.context, HIOrientation);
#endif
    END_DRAWING
    if (winPtr->privatePtr != NULL) {
	winPtr->privatePtr->flags |= TTK_HAS_DARKER_BG;
    }
}

static Ttk_ElementSpec GroupElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    GroupElementSize,
    GroupElementDraw
};

/*----------------------------------------------------------------------
 * +++ Entry element --
 *    3 pixels padding for focus rectangle
 *    2 pixels padding for EditTextFrame
 */

typedef struct {
    Tcl_Obj	*backgroundObj;
} EntryElement;

static Ttk_ElementOptionSpec EntryElementOptions[] = {
    { "-background", TK_OPTION_BORDER,
	    Tk_Offset(EntryElement,backgroundObj), "white" },
    {0}
};

static void EntryElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    *paddingPtr = Ttk_UniformPadding(5);
}

static void EntryElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    EntryElement *e = elementRecord;
    Tk_3DBorder backgroundPtr = Tk_Get3DBorderFromObj(tkwin,e->backgroundObj);
    Ttk_Box inner = Ttk_PadBox(b, Ttk_UniformPadding(3));
    CGRect bounds = BoxToRect(d, inner);
    const HIThemeFrameDrawInfo info = {
	.version = 0,
	.kind = kHIThemeFrameTextFieldSquare,
	.state = Ttk_StateTableLookup(ThemeStateTable, state),
	.isFocused = state & TTK_STATE_FOCUS,
    };

    /*
     * Erase w/background color:
     */

    XFillRectangle(Tk_Display(tkwin), d,
	    Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
	    inner.x,inner.y, inner.width, inner.height);

    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawFrame, &bounds, &info, dc.context, HIOrientation);
    /*if (state & TTK_STATE_FOCUS) {
	ChkErr(DrawThemeFocusRect, &bounds, 1);
    }*/
    END_DRAWING
}

static Ttk_ElementSpec EntryElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(EntryElement),
    EntryElementOptions,
    EntryElementSize,
    EntryElementDraw
};

/*----------------------------------------------------------------------
 * +++ Combobox:
 *
 * NOTES:
 *    kThemeMetricComboBoxLargeDisclosureWidth -> 17
 *    Padding and margins guesstimated by trial-and-error.
 */

static Ttk_Padding ComboboxPadding = { 2, 3, 17, 1 };
static Ttk_Padding ComboboxMargins = { 3, 4, 4, 3 };

static void ComboboxElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    *widthPtr = 0;
    *heightPtr = 0;
    *paddingPtr = Ttk_AddPadding(ComboboxMargins, ComboboxPadding);
}

static void ComboboxElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    CGRect bounds = BoxToRect(d, Ttk_PadBox(b, ComboboxMargins));
    const HIThemeButtonDrawInfo info = {
	.version = 0,
	.state = Ttk_StateTableLookup(ThemeStateTable, state),
	.kind = kThemeComboBox,
	.value = Ttk_StateTableLookup(ButtonValueTable, state),
	.adornment = Ttk_StateTableLookup(ButtonAdornmentTable, state),
    };

    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
    END_DRAWING
}

static Ttk_ElementSpec ComboboxElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    ComboboxElementSize,
    ComboboxElementDraw
};

/*----------------------------------------------------------------------
 * +++ Spinbuttons.
 *
 *    From Apple HIG, part III, section "Controls", "The Stepper Control":
 *    there should be 2 pixels of space between the stepper control (AKA
 *    IncDecButton, AKA "little arrows") and the text field it modifies.
 */

static Ttk_Padding SpinbuttonMargins = {2,0,2,0};
static void SpinButtonElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    SInt32 s;

    ChkErr(GetThemeMetric, kThemeMetricLittleArrowsWidth, &s);
    *widthPtr = s + Ttk_PaddingWidth(SpinbuttonMargins);
    ChkErr(GetThemeMetric, kThemeMetricLittleArrowsHeight, &s);
    *heightPtr = s + Ttk_PaddingHeight(SpinbuttonMargins);
}

static void SpinButtonElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    CGRect bounds = BoxToRect(d, Ttk_PadBox(b, SpinbuttonMargins));
    /* @@@ can't currently distinguish PressedUp (== Pressed) from PressedDown;
     * ignore this bit for now [see #2219588]
     */
    const HIThemeButtonDrawInfo info = {
	.version = 0,
	.state = Ttk_StateTableLookup(ThemeStateTable, state & ~TTK_STATE_PRESSED),
	.kind = kThemeIncDecButton,
	.value = Ttk_StateTableLookup(ButtonValueTable, state),
	.adornment = kThemeAdornmentNone,
    };

    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
    END_DRAWING
}

static Ttk_ElementSpec SpinButtonElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    SpinButtonElementSize,
    SpinButtonElementDraw
};


/*----------------------------------------------------------------------
 * +++ DrawThemeTrack-based elements --
 *    Progress bars and scales. (See also: <<NOTE-TRACKS>>)
 */

/*
 * Apple does not change the appearance of a slider when the window
 * becomes inactive.  So we shouldn't either.
 */

static Ttk_StateTable ThemeTrackEnableTable[] = {
    { kThemeTrackDisabled, TTK_STATE_DISABLED, 0 },
    { kThemeTrackActive, TTK_STATE_BACKGROUND, 0 },
    { kThemeTrackActive, 0, 0 }
    /* { kThemeTrackNothingToScroll, ?, ? }, */
};

typedef struct {	/* TrackElement client data */
    ThemeTrackKind	kind;
    SInt32		thicknessMetric;
} TrackElementData;

static TrackElementData ScaleData = {
    kThemeSlider, kThemeMetricHSliderHeight
};


typedef struct {
    Tcl_Obj *fromObj;		/* minimum value */
    Tcl_Obj *toObj;		/* maximum value */
    Tcl_Obj *valueObj;		/* current value */
    Tcl_Obj *orientObj;		/* horizontal / vertical */
} TrackElement;

static Ttk_ElementOptionSpec TrackElementOptions[] = {
    { "-from", TK_OPTION_DOUBLE, Tk_Offset(TrackElement,fromObj) },
    { "-to", TK_OPTION_DOUBLE, Tk_Offset(TrackElement,toObj) },
    { "-value", TK_OPTION_DOUBLE, Tk_Offset(TrackElement,valueObj) },
    { "-orient", TK_OPTION_STRING, Tk_Offset(TrackElement,orientObj) },
    {0,0,0}
};

static void TrackElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    TrackElementData *data = clientData;
    SInt32 size = 24;	/* reasonable default ... */

    ChkErr(GetThemeMetric, data->thicknessMetric, &size);
    *widthPtr = *heightPtr = size;
}

static void TrackElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    TrackElementData *data = clientData;
    TrackElement *elem = elementRecord;
    int orientation = TTK_ORIENT_HORIZONTAL;
    double from = 0, to = 100, value = 0, factor;

    Ttk_GetOrientFromObj(NULL, elem->orientObj, &orientation);
    Tcl_GetDoubleFromObj(NULL, elem->fromObj, &from);
    Tcl_GetDoubleFromObj(NULL, elem->toObj, &to);
    Tcl_GetDoubleFromObj(NULL, elem->valueObj, &value);
    factor = RangeToFactor(to - from);

    HIThemeTrackDrawInfo info = {
	.version = 0,
	.kind = data->kind,
	.bounds = BoxToRect(d, b),
	.min = from * factor,
	.max = to * factor,
	.value = value * factor,
	.attributes = kThemeTrackShowThumb |
		(orientation == TTK_ORIENT_HORIZONTAL ?
		kThemeTrackHorizontal : 0),
	.enableState = Ttk_StateTableLookup(ThemeTrackEnableTable, state),
	.trackInfo.progress.phase = 0,
    };

    if (info.kind == kThemeSlider) {
	info.trackInfo.slider.pressState = state & TTK_STATE_PRESSED ?
		kThemeThumbPressed : 0;
	if (state & TTK_STATE_ALTERNATE) {
	    info.trackInfo.slider.thumbDir = kThemeThumbDownward;
	} else {
	    info.trackInfo.slider.thumbDir = kThemeThumbPlain;
	}
    }
    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
    if (TkMacOSXInDarkMode(tkwin)) {
	CGRect bounds = BoxToRect(d, b);
	NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
	NSColor *trackColor = [NSColor colorWithColorSpace: deviceRGB
	 					components: darkTrack
	 					     count: 4];
	if (orientation == TTK_ORIENT_HORIZONTAL) {
	    bounds = CGRectInset(bounds, 1, bounds.size.height/2 - 2); 
	} else {
	    bounds = CGRectInset(bounds, bounds.size.width/2 - 3, 2); 
	}
	SolidFillButtonFace(dc.context, bounds, 2, trackColor);
    }
#endif
    ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
    END_DRAWING
}

static Ttk_ElementSpec TrackElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(TrackElement),
    TrackElementOptions,
    TrackElementSize,
    TrackElementDraw
};

/*
 * Slider element -- <<NOTE-TRACKS>>
 * Has geometry only. The Scale widget adjusts the position of this element,
 * and uses it for hit detection. In the Aqua theme, the slider is actually
 * drawn as part of the trough element.
 *
 * Also buggy: The geometry here is a Wild-Assed-Guess; I can't
 * figure out how to get the Appearance Manager to tell me the
 * slider size.
 */
static void SliderElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    *widthPtr = *heightPtr = 24;
}

static Ttk_ElementSpec SliderElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    SliderElementSize,
    TtkNullElementDraw
};

/*----------------------------------------------------------------------
 * +++ Progress bar element (new):
 *
 * @@@ NOTE: According to an older revision of the Aqua reference docs,
 * @@@ the 'phase' field is between 0 and 4. Newer revisions say
 * @@@ that it can be any UInt8 value.
 */

typedef struct {
    Tcl_Obj *orientObj;		/* horizontal / vertical */
    Tcl_Obj *valueObj;		/* current value */
    Tcl_Obj *maximumObj;	/* maximum value */
    Tcl_Obj *phaseObj;		/* animation phase */
    Tcl_Obj *modeObj;		/* progress bar mode */
} PbarElement;

static Ttk_ElementOptionSpec PbarElementOptions[] = {
    { "-orient", TK_OPTION_STRING,
	Tk_Offset(PbarElement,orientObj), "horizontal" },
    { "-value", TK_OPTION_DOUBLE,
	Tk_Offset(PbarElement,valueObj), "0" },
    { "-maximum", TK_OPTION_DOUBLE,
	Tk_Offset(PbarElement,maximumObj), "100" },
    { "-phase", TK_OPTION_INT,
	Tk_Offset(PbarElement,phaseObj), "0" },
    { "-mode", TK_OPTION_STRING,
	Tk_Offset(PbarElement,modeObj), "determinate" },
    {0,0,0,0}
};

static void PbarElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    SInt32 size = 24;	/* @@@ Check HIG for correct default */

    ChkErr(GetThemeMetric, kThemeMetricLargeProgressBarThickness, &size);
    *widthPtr = *heightPtr = size;
}

static void PbarElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    PbarElement *pbar = elementRecord;
    int orientation = TTK_ORIENT_HORIZONTAL, phase = 0;
    double value = 0, maximum = 100, factor;

    Ttk_GetOrientFromObj(NULL, pbar->orientObj, &orientation);
    Tcl_GetDoubleFromObj(NULL, pbar->valueObj, &value);
    Tcl_GetDoubleFromObj(NULL, pbar->maximumObj, &maximum);
    Tcl_GetIntFromObj(NULL, pbar->phaseObj, &phase);
    factor = RangeToFactor(maximum);

    HIThemeTrackDrawInfo info = {
	.version = 0,
	.kind = (!strcmp("indeterminate", Tcl_GetString(pbar->modeObj)) && value) ?
		kThemeIndeterminateBar : kThemeProgressBar,
	.bounds = BoxToRect(d, b),
	.min = 0,
	.max = maximum * factor,
	.value = value * factor,
	.attributes = kThemeTrackShowThumb |
		(orientation == TTK_ORIENT_HORIZONTAL ?
		kThemeTrackHorizontal : 0),
	.enableState = Ttk_StateTableLookup(ThemeTrackEnableTable, state),
	.trackInfo.progress.phase = phase,
    };

    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
    if (TkMacOSXInDarkMode(tkwin)) {
	CGRect bounds = BoxToRect(d, b);
	NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
	NSColor *trackColor = [NSColor colorWithColorSpace: deviceRGB
	 					components: darkTrack
	 					     count: 4];
	if (orientation == TTK_ORIENT_HORIZONTAL) {
	    bounds = CGRectInset(bounds, 1, bounds.size.height/2 - 3); 
	} else {
	    bounds = CGRectInset(bounds, bounds.size.width/2 - 3, 1); 
	}
	SolidFillButtonFace(dc.context, bounds, 3, trackColor);
    }
#endif
    ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
    END_DRAWING
}

static Ttk_ElementSpec PbarElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(PbarElement),
    PbarElementOptions,
    PbarElementSize,
    PbarElementDraw
};

/*----------------------------------------------------------------------
 * +++ Separator element.
 *
 *    DrawThemeSeparator() guesses the orientation of the line from the width
 *    and height of the rectangle, so the same element can can be used for
 *    horizontal, vertical, and general separators.
 */

static void SeparatorElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    *widthPtr = *heightPtr = 1;
}

static void SeparatorElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, unsigned int state)
{
    CGRect bounds = BoxToRect(d, b);
    const HIThemeSeparatorDrawInfo info = {
	.version = 0,
	/* Separator only supports kThemeStateActive, kThemeStateInactive */
	.state = Ttk_StateTableLookup(ThemeStateTable, state & TTK_STATE_BACKGROUND),
    };

    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
    if (TkMacOSXInDarkMode(tkwin)) {
	DrawDarkSeparator(bounds, dc.context, tkwin);
    } else {
	ChkErr(HIThemeDrawSeparator, &bounds, &info, dc.context, HIOrientation);
    }
#else
    ChkErr(HIThemeDrawSeparator, &bounds, &info, dc.context, HIOrientation);
#endif
    END_DRAWING
}

static Ttk_ElementSpec SeparatorElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    SeparatorElementSize,
    SeparatorElementDraw
};

/*----------------------------------------------------------------------
 * +++ Size grip element.
 */

static const ThemeGrowDirection sizegripGrowDirection
	= kThemeGrowRight|kThemeGrowDown;

static void SizegripElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    HIThemeGrowBoxDrawInfo info = {
	.version = 0,
	.state = kThemeStateActive,
	.kind = kHIThemeGrowBoxKindNormal,
	.direction = sizegripGrowDirection,
	.size = kHIThemeGrowBoxSizeNormal,
    };
    CGRect bounds = CGRectZero;

    ChkErr(HIThemeGetGrowBoxBounds, &bounds.origin, &info, &bounds);
    *widthPtr = bounds.size.width;
    *heightPtr = bounds.size.height;
}

static void SizegripElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, unsigned int state)
{
    CGRect bounds = BoxToRect(d, b);
    HIThemeGrowBoxDrawInfo info = {
	.version = 0,
	/* Grow box only supports kThemeStateActive, kThemeStateInactive */
	.state = Ttk_StateTableLookup(ThemeStateTable, state & TTK_STATE_BACKGROUND),
	.kind = kHIThemeGrowBoxKindNormal,
	.direction = sizegripGrowDirection,
	.size = kHIThemeGrowBoxSizeNormal,
    };

    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawGrowBox, &bounds.origin, &info, dc.context, HIOrientation);
    END_DRAWING
}

static Ttk_ElementSpec SizegripElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    SizegripElementSize,
    SizegripElementDraw
};

/*----------------------------------------------------------------------
 * +++ Background and fill elements.
 *
 *    Before drawing any ttk widget, its bounding rectangle is filled with a
 *    background color.  This color must match the background color of the
 *    containing widget to avoid looking ugly. The need for care when doing
 *    this is exacerbated by the fact that ttk enforces its "native look" by
 *    not allowing user control of the background or highlight colors of ttk
 *    widgets.
 *
 *    This job is made more complicated in recent versions of macOS by the fact
 *    that the Appkit GroupBox (used for ttk LabelFrames) and TabbedPane (used
 *    for the Notebook widget) both place their content inside a rectangle with
 *    rounded corners that has a color which contrasts with the dialog
 *    background color.  Moreover, although the Apple human interface
 *    guidelines recommend against doing so, there are times when one wants to
 *    nest these widgets, for example having a GroupBox inside of a TabbedPane.
 *    To have the right contrast, each level of nesting requires a different
 *    color.
 *
 *    Previous Tk releases used the HIThemeDrawGroupBox routine to draw
 *    GroupBoxes and TabbedPanes. This meant that the best that could be done
 *    was to set the GroupBox to be of kind kHIThemeGroupBoxKindPrimaryOpaque,
 *    and set its fill color to be the system background color.  If widgets
 *    inside the box were drawn with the system background color the
 *    backgrounds would match.  But this produces a GroupBox with no contrast,
 *    the only visual clue being a faint highlighting around the top of the
 *    GroupBox.  Moreover, the TabbedPane does not have an Opaque version, so
 *    while it is drawn inside a contrasting rounded rectangle, the widgets
 *    inside the pane needed to be enclosed in a frame with the system
 *    background color. This added a visual artifact since the frame's
 *    background color does not match the Pane's background color.  That code
 *    has now been replaced with the standalone drawing procedure
 *    macOSXDrawGroupBox, which draws a rounded rectangle with an appropriate
 *    contrasting background color.
 *
 *    Patterned backgrounds, which are now obsolete, should be aligned with the
 *    coordinate system of the top-level window.  Apparently failing to do this
 *    used to cause graphics anomalies when drawing into an off-screen graphics
 *    port.  The code for handling this is currently commented out.
 */


static void FillElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    CGRect bounds = BoxToRect(d, b);
    BEGIN_DRAWING(d)
#if MAC_OS_X_VERSION_MIN_REQUIRED > 10800
    NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
    NSColor *bgColor;
    CGFloat fill[4];
    GetBoxColor(dc.context, tkwin, 0, fill);
    bgColor = [NSColor colorWithColorSpace: deviceRGB components: fill
				     count: 4];
    CGContextSetFillColorSpace(dc.context, deviceRGB.CGColorSpace);
    CGContextSetFillColorWithColor(dc.context, bgColor.CGColor);
    CGContextFillRect(dc.context, bounds);
#else
    ThemeBrush brush = (state & TTK_STATE_BACKGROUND)
	    ? kThemeBrushModelessDialogBackgroundInactive
	    : kThemeBrushModelessDialogBackgroundActive;
    ChkErr(HIThemeSetFill, brush, NULL, dc.context, HIOrientation);
    //QDSetPatternOrigin(PatternOrigin(tkwin, d));
    CGContextFillRect(dc.context, bounds);
#endif
    END_DRAWING
}

static void BackgroundElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, unsigned int state)
{
    FillElementDraw(clientData, elementRecord, tkwin, d, Ttk_WinBox(tkwin),
    		    state);
}

static Ttk_ElementSpec FillElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    TtkNullElementSize,
    FillElementDraw
};

static Ttk_ElementSpec BackgroundElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    TtkNullElementSize,
    BackgroundElementDraw
};

/*----------------------------------------------------------------------
 * +++ ToolbarBackground element -- toolbar style for frames.
 *
 *    This is very similar to the normal background element, but uses a
 *    different ThemeBrush in order to get the lighter pinstripe effect
 *    used in toolbars. We use SetThemeBackground() rather than
 *    ApplyThemeBackground() in order to get the right style.
 *
 * <URL: http://developer.apple.com/documentation/Carbon/Reference/
 *    Appearance_Manager/appearance_manager/constant_7.html#/
 *    /apple_ref/doc/uid/TP30000243/C005321>
 *
 */

static void ToolbarBackgroundElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    ThemeBrush brush = kThemeBrushToolbarBackground;
    CGRect bounds = BoxToRect(d, Ttk_WinBox(tkwin));

    BEGIN_DRAWING(d)
    ChkErr(HIThemeSetFill, brush, NULL, dc.context, HIOrientation);
    //QDSetPatternOrigin(PatternOrigin(tkwin, d));
    CGContextFillRect(dc.context, bounds);
    END_DRAWING
}

static Ttk_ElementSpec ToolbarBackgroundElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    TtkNullElementSize,
    ToolbarBackgroundElementDraw
};

/*----------------------------------------------------------------------
 * +++ Treeview header
 *    Redefine the header to use a kThemeListHeaderButton.
 */

#define TTK_TREEVIEW_STATE_SORTARROW	TTK_STATE_USER1
static Ttk_StateTable TreeHeaderValueTable[] = {
    { kThemeButtonOn, TTK_STATE_ALTERNATE},
    { kThemeButtonOn, TTK_STATE_SELECTED},
    { kThemeButtonOff, 0}
};
static Ttk_StateTable TreeHeaderAdornmentTable[] = {
    { kThemeAdornmentHeaderButtonSortUp,
	    TTK_STATE_ALTERNATE|TTK_TREEVIEW_STATE_SORTARROW},
    { kThemeAdornmentDefault,
	    TTK_STATE_SELECTED|TTK_TREEVIEW_STATE_SORTARROW},
    { kThemeAdornmentHeaderButtonNoSortArrow, TTK_STATE_ALTERNATE},
    { kThemeAdornmentHeaderButtonNoSortArrow, TTK_STATE_SELECTED},
    { kThemeAdornmentFocus, TTK_STATE_FOCUS},
    { kThemeAdornmentNone, 0}
};

static void TreeHeaderElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    ThemeButtonParams *params = clientData;
    CGRect bounds = BoxToRect(d, b);
    const HIThemeButtonDrawInfo info = {
	.version = 0,
	.state = Ttk_StateTableLookup(ThemeStateTable, state),
	.kind = params->kind,
	.value = Ttk_StateTableLookup(TreeHeaderValueTable, state),
	.adornment = Ttk_StateTableLookup(TreeHeaderAdornmentTable, state),
    };

    BEGIN_DRAWING(d)
    ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
    END_DRAWING
}

static Ttk_ElementSpec TreeHeaderElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    ButtonElementSizeNoPadding,
    TreeHeaderElementDraw
};

/*
 * Disclosure triangle:
 */

#define TTK_TREEVIEW_STATE_OPEN 	TTK_STATE_USER1
#define TTK_TREEVIEW_STATE_LEAF 	TTK_STATE_USER2
static Ttk_StateTable DisclosureValueTable[] = {
    { kThemeDisclosureDown, TTK_TREEVIEW_STATE_OPEN, 0 },
    { kThemeDisclosureRight, 0, 0 },
};

static void DisclosureElementSize(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
{
    SInt32 s;

    ChkErr(GetThemeMetric, kThemeMetricDisclosureTriangleWidth, &s);
    *widthPtr = s;
    ChkErr(GetThemeMetric, kThemeMetricDisclosureTriangleHeight, &s);
    *heightPtr = s;
}

static void DisclosureElementDraw(
    void *clientData, void *elementRecord, Tk_Window tkwin,
    Drawable d, Ttk_Box b, Ttk_State state)
{
    if (!(state & TTK_TREEVIEW_STATE_LEAF)) {
	CGRect bounds = BoxToRect(d, b);
	const HIThemeButtonDrawInfo info = {
	    .version = 0,
	    .state = Ttk_StateTableLookup(ThemeStateTable, state),
	    .kind = kThemeDisclosureTriangle,
	    .value = Ttk_StateTableLookup(DisclosureValueTable, state),
	    .adornment = kThemeAdornmentDrawIndicatorOnly,
	};

	BEGIN_DRAWING(d)
	ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
	END_DRAWING
    }
}

static Ttk_ElementSpec DisclosureElementSpec = {
    TK_STYLE_VERSION_2,
    sizeof(NullElement),
    TtkNullElementOptions,
    DisclosureElementSize,
    DisclosureElementDraw
};

/*----------------------------------------------------------------------
 * +++ Widget layouts.
 */

TTK_BEGIN_LAYOUT_TABLE(LayoutTable)

TTK_LAYOUT("Toolbar",
    TTK_NODE("Toolbar.background", TTK_FILL_BOTH))

TTK_LAYOUT("TButton",
    TTK_GROUP("Button.button", TTK_FILL_BOTH,
	TTK_GROUP("Button.padding", TTK_FILL_BOTH,
	    TTK_NODE("Button.label", TTK_FILL_BOTH))))

TTK_LAYOUT("TRadiobutton",
    TTK_GROUP("Radiobutton.button", TTK_FILL_BOTH,
	TTK_GROUP("Radiobutton.padding", TTK_FILL_BOTH,
	    TTK_NODE("Radiobutton.label", TTK_PACK_LEFT))))

TTK_LAYOUT("TCheckbutton",
    TTK_GROUP("Checkbutton.button", TTK_FILL_BOTH,
	TTK_GROUP("Checkbutton.padding", TTK_FILL_BOTH,
	    TTK_NODE("Checkbutton.label", TTK_PACK_LEFT))))

TTK_LAYOUT("TMenubutton",
    TTK_GROUP("Menubutton.button", TTK_FILL_BOTH,
	TTK_GROUP("Menubutton.padding", TTK_FILL_BOTH,
	    TTK_NODE("Menubutton.label", TTK_PACK_LEFT))))

TTK_LAYOUT("TCombobox",
    TTK_GROUP("Combobox.button", TTK_PACK_TOP|TTK_FILL_X,
	TTK_GROUP("Combobox.padding", TTK_FILL_BOTH,
	    TTK_NODE("Combobox.textarea", TTK_FILL_X))))

/* Notebook tabs -- no focus ring */
TTK_LAYOUT("Tab",
    TTK_GROUP("Notebook.tab", TTK_FILL_BOTH,
	TTK_GROUP("Notebook.padding", TTK_EXPAND|TTK_FILL_BOTH,
	    TTK_NODE("Notebook.label", TTK_EXPAND|TTK_FILL_BOTH))))

/* Progress bars -- track only */
TTK_LAYOUT("TSpinbox",
    TTK_NODE("Spinbox.spinbutton", TTK_PACK_RIGHT|TTK_STICK_E)
    TTK_GROUP("Spinbox.field", TTK_EXPAND|TTK_FILL_X,
	TTK_NODE("Spinbox.textarea", TTK_EXPAND|TTK_FILL_X)))

TTK_LAYOUT("TProgressbar",
    TTK_NODE("Progressbar.track", TTK_EXPAND|TTK_FILL_BOTH))

/* Tree heading -- no border, fixed height */
TTK_LAYOUT("Heading",
    TTK_NODE("Treeheading.cell", TTK_FILL_X)
    TTK_NODE("Treeheading.image", TTK_PACK_RIGHT)
    TTK_NODE("Treeheading.text", 0))

/* Tree items -- omit focus ring */
TTK_LAYOUT("Item",
    TTK_GROUP("Treeitem.padding", TTK_FILL_BOTH,
	TTK_NODE("Treeitem.indicator", TTK_PACK_LEFT)
	TTK_NODE("Treeitem.image", TTK_PACK_LEFT)
	TTK_NODE("Treeitem.text", TTK_PACK_LEFT)))

TTK_END_LAYOUT_TABLE

/*----------------------------------------------------------------------
 * +++ Initialization.
 */

static int AquaTheme_Init(Tcl_Interp *interp)
{
    Ttk_Theme themePtr = Ttk_CreateTheme(interp, "aqua", NULL);

    if (!themePtr) {
	return TCL_ERROR;
    }

    /*
     * Elements:
     */
    Ttk_RegisterElementSpec(themePtr, "background", &BackgroundElementSpec, 0);
    Ttk_RegisterElementSpec(themePtr, "fill", &FillElementSpec, 0);
    Ttk_RegisterElementSpec(themePtr, "Toolbar.background",
	&ToolbarBackgroundElementSpec, 0);

    Ttk_RegisterElementSpec(themePtr, "Button.button",
	&ButtonElementSpec, &PushButtonParams);
    Ttk_RegisterElementSpec(themePtr, "Checkbutton.button",
	&ButtonElementSpec, &CheckBoxParams);
    Ttk_RegisterElementSpec(themePtr, "Radiobutton.button",
	&ButtonElementSpec, &RadioButtonParams);
    Ttk_RegisterElementSpec(themePtr, "Toolbutton.border",
     	&ButtonElementSpec, &BevelButtonParams);
    Ttk_RegisterElementSpec(themePtr, "Menubutton.button",
	&ButtonElementSpec, &PopupButtonParams);
    Ttk_RegisterElementSpec(themePtr, "Spinbox.spinbutton",
    	&SpinButtonElementSpec, 0);
    Ttk_RegisterElementSpec(themePtr, "Combobox.button",
	&ComboboxElementSpec, 0);
    Ttk_RegisterElementSpec(themePtr, "Treeitem.indicator",
	&DisclosureElementSpec, &DisclosureParams);
    Ttk_RegisterElementSpec(themePtr, "Treeheading.cell",
	&TreeHeaderElementSpec, &ListHeaderParams);

    Ttk_RegisterElementSpec(themePtr, "Notebook.tab", &TabElementSpec, 0);
    Ttk_RegisterElementSpec(themePtr, "Notebook.client", &PaneElementSpec, 0);

    Ttk_RegisterElementSpec(themePtr, "Labelframe.border", &GroupElementSpec,0);
    Ttk_RegisterElementSpec(themePtr, "Entry.field", &EntryElementSpec,0);
    Ttk_RegisterElementSpec(themePtr, "Spinbox.field", &EntryElementSpec,0);

    Ttk_RegisterElementSpec(themePtr, "separator", &SeparatorElementSpec,0);
    Ttk_RegisterElementSpec(themePtr, "hseparator", &SeparatorElementSpec,0);
    Ttk_RegisterElementSpec(themePtr, "vseparator", &SeparatorElementSpec,0);

    Ttk_RegisterElementSpec(themePtr, "sizegrip", &SizegripElementSpec,0);

    /*
     * <<NOTE-TRACKS>>
     * The Progressbar widget adjusts the size of the pbar element.
     * In the Aqua theme, the appearance manager computes the bar geometry;
     * we do all the drawing in the ".track" element and leave the .pbar out.
     */
    Ttk_RegisterElementSpec(themePtr,"Scale.trough",
	&TrackElementSpec, &ScaleData);
    Ttk_RegisterElementSpec(themePtr,"Scale.slider", &SliderElementSpec,0);
    Ttk_RegisterElementSpec(themePtr,"Progressbar.track", &PbarElementSpec, 0);

    /*
     * Layouts:
     */
    Ttk_RegisterLayouts(themePtr, LayoutTable);

    Tcl_PkgProvide(interp, "ttk::theme::aqua", TTK_VERSION);
    return TCL_OK;
}

MODULE_SCOPE
int Ttk_MacOSXPlatformInit(Tcl_Interp *interp)
{
    return AquaTheme_Init(interp);
}

/*
 * Local Variables:
 * mode: objc
 * c-basic-offset: 4
 * fill-column: 79
 * coding: utf-8
 * End:
 */