diff options
Diffstat (limited to 'macosx')
-rw-r--r-- | macosx/ttkMacOSXTheme.c | 996 |
1 files changed, 996 insertions, 0 deletions
diff --git a/macosx/ttkMacOSXTheme.c b/macosx/ttkMacOSXTheme.c new file mode 100644 index 0000000..ca10aa9 --- /dev/null +++ b/macosx/ttkMacOSXTheme.c @@ -0,0 +1,996 @@ +/* + * $Id: ttkMacOSXTheme.c,v 1.1 2006/10/31 01:42:27 hobbs Exp $ + * + * Tk theme engine for Mac OSX, using the Appearance Manager API. + * + * Copyright (c) 2004 Joe English + * Copyright (c) 2005 Neil Madden + * + * 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/ttk 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. However, + * since we're drawing into an off-screen port (Tk "Pixmap), + * we don't need to account for this. + */ + +#include <Carbon/Carbon.h> +#include <tkMacOSXInt.h> +#include "ttk/ttkTheme.h" + +/*---------------------------------------------------------------------- + * +++ Utilities. + */ + +static +Rect BoxToRect(Ttk_Box b) +{ + Rect rect; + rect.top = b.y; + rect.left = b.x; + rect.bottom = b.y + b.height; + rect.right = b.x + b.width; + return rect; +} + +#define BEGIN_DRAWING(d) { \ + CGrafPtr saveWorld; GDHandle saveDevice; \ + GetGWorld(&saveWorld, &saveDevice); \ + SetGWorld(TkMacOSXGetDrawablePort(d), 0) ; +#define END_DRAWING \ + SetGWorld(saveWorld,saveDevice); } + +/* 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} +*/ +}; + +/*---------------------------------------------------------------------- + * +++ Button element: Used for elements drawn with DrawThemeButton. + */ + +/* Extra margins to account for drop shadow. + */ +static Ttk_Padding ButtonMargins = {2,2,2,2}; + +#define NoThemeMetric 0xFFFFFFFF + +typedef struct { + ThemeButtonKind kind; + ThemeMetric heightMetric; +} ThemeButtonParms; + +static ThemeButtonParms + PushButtonParms = { kThemePushButton, NoThemeMetric }, + CheckBoxParms = { kThemeCheckBox, kThemeMetricCheckBoxHeight }, + RadioButtonParms = { kThemeRadioButton, kThemeMetricRadioButtonHeight }, + BevelButtonParms = { kThemeBevelButton, NoThemeMetric }, + PopupButtonParms = { kThemePopupButton, NoThemeMetric }, + ListHeaderParms = { 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, TTK_STATE_ALTERNATE, 0 }, + { kThemeAdornmentFocus, TTK_STATE_FOCUS, 0 }, + { kThemeAdornmentNone, 0, 0 } +}; + +/* + * computeButtonDrawInfo -- + * Fill in an appearance manager ThemeButtonDrawInfo record. + */ +static ThemeButtonDrawInfo computeButtonDrawInfo( + ThemeButtonParms *parms, Ttk_State state) +{ + ThemeButtonDrawInfo info; + info.state = Ttk_StateTableLookup(ThemeStateTable, state); + info.value = Ttk_StateTableLookup(ButtonValueTable, state); + info.adornment = Ttk_StateTableLookup(ButtonAdornmentTable, state); + return info; +} + +static void ButtonElementGeometryNoPadding( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ThemeButtonParms *parms = clientData; + + if (parms->heightMetric != NoThemeMetric) { + SInt32 gratuitouslyOverspecifiedType; + GetThemeMetric(parms->heightMetric, &gratuitouslyOverspecifiedType); + *heightPtr = gratuitouslyOverspecifiedType; + } +} + +static void ButtonElementGeometry( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ThemeButtonParms *parms = clientData; + ThemeButtonDrawInfo drawInfo = computeButtonDrawInfo(parms, 0); + Rect scratchRect, contentsRect; + const int scratchSize = 100; + + ButtonElementGeometryNoPadding( + 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. + */ + scratchRect.top = scratchRect.left = 0; + scratchRect.bottom = scratchRect.right = scratchSize; + + GetThemeButtonContentBounds( + &scratchRect, parms->kind, &drawInfo, &contentsRect); + + paddingPtr->left = contentsRect.left; + paddingPtr->top = contentsRect.top; + paddingPtr->bottom = scratchSize - contentsRect.bottom; + paddingPtr->right = scratchSize - contentsRect.right; + + /* Now add a little extra padding to account for drop shadows. + * @@@ SHOULD: call GetThemeButtonBackgroundBounds() instead. + */ + + *paddingPtr = Ttk_AddPadding(*paddingPtr, ButtonMargins); +} + +static void ButtonElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, Ttk_State state) +{ + Rect bounds = BoxToRect(Ttk_PadBox(b, ButtonMargins)); + ThemeButtonParms *parms = clientData; + ThemeButtonDrawInfo info = computeButtonDrawInfo(parms, state); + + BEGIN_DRAWING(d) + DrawThemeButton(&bounds, parms->kind, &info, + NULL/*prevInfo*/,NULL/*eraseProc*/,NULL/*labelProc*/,0/*userData*/); + END_DRAWING +} + +static Ttk_ElementSpec ButtonElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + ButtonElementGeometry, + ButtonElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Notebook elements. + */ + +static Ttk_StateTable TabStyleTable[] = { + { kThemeTabFrontInactive, TTK_STATE_SELECTED|TTK_STATE_BACKGROUND, 0 }, + { kThemeTabNonFrontInactive, TTK_STATE_BACKGROUND, 0 }, + { kThemeTabFrontUnavailable, TTK_STATE_DISABLED|TTK_STATE_SELECTED, 0 }, + { kThemeTabNonFrontUnavailable, TTK_STATE_DISABLED, 0 }, + { kThemeTabFront, TTK_STATE_SELECTED, 0 }, + { kThemeTabNonFrontPressed, TTK_STATE_PRESSED, 0 }, + { kThemeTabNonFront, 0,0 } +}; + +/* Quoth DrawThemeTab() reference manual: + * "Small tabs have a height of 16 pixels large tabs have a height of + * 21 pixels. (The widths of tabs are variable.) Additionally, the + * distance that the tab overlaps the pane must be included in the tab + * rectangle this overlap distance is always 3 pixels, although the + * 3-pixel overlap is only drawn for the front tab." + */ +static const int TAB_HEIGHT = 21; +static const int TAB_OVERLAP = 3; + +static void TabElementGeometry( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + *heightPtr = TAB_HEIGHT + TAB_OVERLAP - 1; +} + +static void TabElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, Ttk_State state) +{ + Rect bounds = BoxToRect(b); + bounds.bottom += TAB_OVERLAP; + BEGIN_DRAWING(d) + DrawThemeTab( + &bounds, Ttk_StateTableLookup(TabStyleTable, state), kThemeTabNorth, + 0/*labelProc*/,0/*userData*/); + END_DRAWING +} + +static Ttk_ElementSpec TabElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + TabElementGeometry, + TabElementDraw +}; + +/* Notebook panes: + */ +static void PaneElementGeometry( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + /* Padding determined by trial-and-error */ + *paddingPtr = Ttk_MakePadding(2,8,2,2); +} + +static void PaneElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, Ttk_State state) +{ + Rect bounds = BoxToRect(b); + BEGIN_DRAWING(d) + DrawThemeTabPane( + &bounds, Ttk_StateTableLookup(ThemeStateTable, state)); + END_DRAWING +} + +static Ttk_ElementSpec PaneElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + PaneElementGeometry, + 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 GroupElementGeometry( + 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) +{ + Rect bounds = BoxToRect(b); + BEGIN_DRAWING(d) + DrawThemePrimaryGroup( + &bounds, Ttk_StateTableLookup(ThemeStateTable, state)); + END_DRAWING +} + +static Ttk_ElementSpec GroupElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + GroupElementGeometry, + 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 EntryElementGeometry( + 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)); + Rect bounds = BoxToRect(inner); + + BEGIN_DRAWING(d) + + /* 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); + + /* Draw border: + */ + DrawThemeEditTextFrame( + &bounds, Ttk_StateTableLookup(ThemeStateTable, state)); + + /* Draw focus highlight: + */ + if (state & TTK_STATE_FOCUS) + DrawThemeFocusRect(&bounds, 1); + + END_DRAWING +} + +static Ttk_ElementSpec EntryElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(EntryElement), + EntryElementOptions, + EntryElementGeometry, + EntryElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Pop-up arrow (for comboboxes) + * NOTE: This isn't right at all, but I can't find the correct + * function in the Appearance Manager reference. + */ + +static void PopupArrowElementGeometry( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + *widthPtr = 12; /* wild-assed guess */ + *heightPtr = 12; /* wild-assed guess */ +} + +static void PopupArrowElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, Ttk_State state) +{ + Rect bounds = BoxToRect(b); + + ThemeButtonParms *parms = clientData; + ThemeButtonDrawInfo info = computeButtonDrawInfo(parms, state); + + bounds.left -= 6; + bounds.top -= 3; + bounds.right -= 6; + bounds.bottom -= 2; + + BEGIN_DRAWING(d) + DrawThemeButton(&bounds, kThemeArrowButton, &info, + NULL/*prevInfo*/,NULL/*eraseProc*/,NULL/*labelProc*/,0/*userData*/); + + bounds = BoxToRect(Ttk_PadBox(b, ButtonMargins)); + bounds.top += 2; + bounds.bottom += 2; + bounds.left -= 2; + bounds.right -= 2; + + DrawThemePopupArrow(&bounds, + kThemeArrowDown, + kThemeArrow9pt, /* ??? */ + Ttk_StateTableLookup(ThemeStateTable, state), + NULL /*eraseProc*/,0/*eraseData*/); + END_DRAWING +} + +static Ttk_ElementSpec PopupArrowElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + PopupArrowElementGeometry, + PopupArrowElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ DrawThemeTrack-based elements -- + * Progress bars and scales. (See also: <<NOTE-TRACKS>>) + */ + +static Ttk_StateTable ThemeTrackEnableTable[] = { + { kThemeTrackDisabled, TTK_STATE_DISABLED, 0 }, + { kThemeTrackInactive, 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 TrackElementGeometry( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + TrackElementData *data = clientData; + SInt32 size = 24; /* reasonable default ... */ + 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; + double from = 0, to = 100, value = 0; + int orientation = TTK_ORIENT_HORIZONTAL; + ThemeTrackDrawInfo drawInfo; + + Tcl_GetDoubleFromObj(NULL, elem->fromObj, &from); + Tcl_GetDoubleFromObj(NULL, elem->toObj, &to); + Tcl_GetDoubleFromObj(NULL, elem->valueObj, &value); + Ttk_GetOrientFromObj(NULL, elem->orientObj, &orientation); + + /* @@@ BUG: min, max, and value should account for resolution: + * @@@ if finer than 1.0, conversion to int breaks. + */ + drawInfo.kind = data->kind; + drawInfo.bounds = BoxToRect(b); + drawInfo.min = (int)from; /* @@@ */ + drawInfo.max = (int)to; /* @@@ */ + drawInfo.value = (int)value; /* @@@ */ + + drawInfo.attributes = orientation == TTK_ORIENT_HORIZONTAL + ? kThemeTrackHorizontal : 0; + drawInfo.attributes |= kThemeTrackShowThumb; + drawInfo.enableState = Ttk_StateTableLookup(ThemeTrackEnableTable, state); + + switch (data->kind) { + case kThemeProgressBar: + drawInfo.trackInfo.progress.phase = 0; /* 1-4: animation phase */ + break; + case kThemeSlider: + drawInfo.trackInfo.slider.pressState = 0; /* @@@ fill this in */ + drawInfo.trackInfo.slider.thumbDir = kThemeThumbPlain; + /* kThemeThumbUpward, kThemeThumbDownward, kThemeThumbPlain */ + break; + } + + BEGIN_DRAWING(d) + DrawThemeTrack(&drawInfo, + NULL/*rgnGhost*/,NULL/*eraseProc*/,0/*eraseData*/); + END_DRAWING +} + +static Ttk_ElementSpec TrackElementSpec = { + TK_STYLE_VERSION_2, + sizeof(TrackElement), + TrackElementOptions, + TrackElementGeometry, + 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 SliderElementGeometry( + 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), + NullElementOptions, + SliderElementGeometry, + NullElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ 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 PbarElementGeometry( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + SInt32 size = 24; /* @@@ Check HIG for correct default */ + 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; + double value = 0, maximum = 100; + int phase = 0; + ThemeTrackDrawInfo drawInfo; + + Ttk_GetOrientFromObj(NULL, pbar->orientObj, &orientation); + Tcl_GetDoubleFromObj(NULL, pbar->valueObj, &value); + Tcl_GetDoubleFromObj(NULL, pbar->maximumObj, &maximum); + Tcl_GetIntFromObj(NULL, pbar->phaseObj, &phase); + + if (!strcmp("indeterminate", Tcl_GetString(pbar->modeObj)) && value) { + drawInfo.kind = kThemeIndeterminateBar; + } else { + drawInfo.kind = kThemeProgressBar; + } + drawInfo.bounds = BoxToRect(b); + drawInfo.min = 0; + drawInfo.max = (int)maximum; /* @@@ See note above */ + drawInfo.value = (int)value; + drawInfo.attributes = orientation == TTK_ORIENT_HORIZONTAL + ? kThemeTrackHorizontal : 0; + drawInfo.attributes |= kThemeTrackShowThumb; + drawInfo.enableState = Ttk_StateTableLookup(ThemeTrackEnableTable, state); + drawInfo.trackInfo.progress.phase = phase; + + BEGIN_DRAWING(d) + DrawThemeTrack(&drawInfo, + NULL/*rgnGhost*/,NULL/*eraseProc*/,0/*eraseData*/); + END_DRAWING +} + +static Ttk_ElementSpec PbarElementSpec = { + TK_STYLE_VERSION_2, + sizeof(PbarElement), + PbarElementOptions, + PbarElementGeometry, + 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 = 2; +} + +static void SeparatorElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + Rect bounds = BoxToRect(b); + + /* DrawThemeSeparator only supports kThemeStateActive / kThemeStateInactive + */ + state &= TTK_STATE_BACKGROUND; + BEGIN_DRAWING(d) + DrawThemeSeparator(&bounds, Ttk_StateTableLookup(ThemeStateTable, state)); + END_DRAWING +} + +static Ttk_ElementSpec SeparatorElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + 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) +{ + Point origin; + Rect bounds; + + origin.h = origin.v = 0; + GetThemeStandaloneGrowBoxBounds( + origin, sizegripGrowDirection, false, &bounds); + *widthPtr = bounds.right - bounds.left; + *heightPtr = bounds.bottom - bounds.top; +} + +static void SizegripElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + Point origin; + origin.h = b.x; origin.v = b.y; + + /* Grow box only supports kThemeStateActive, kThemeStateInactive */ + state &= TTK_STATE_BACKGROUND; + + BEGIN_DRAWING(d) + DrawThemeStandaloneGrowBox( + origin, sizegripGrowDirection, false, + Ttk_StateTableLookup(ThemeStateTable, state)); + END_DRAWING +} + +static Ttk_ElementSpec SizegripElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + SizegripElementSize, + SizegripElementDraw +}; + + +/*---------------------------------------------------------------------- + * +++ Background element -- an experiment. + * + * This isn't quite right: In Aqua, the correct background for + * a control depends on what kind of container it belongs to, + * and the type of the top-level window. + * + * Also: patterned backgrounds should be aligned with the coordinate + * system of the top-level window. Since we're drawing into an + * off-screen graphics port with its own coordinate system, + * this leads to alignment glitches. + * + * Available kTheme constants: + * kThemeBackgroundTabPane, + * kThemeBackgroundPlacard, + * kThemeBackgroundWindowHeader, + * kThemeBackgroundListViewWindowHeader, + * kThemeBackgroundSecondaryGroupBox, + * + * GetThemeBrush() and SetThemeBackground() offer more choices. + * + */ + +static void BackgroundElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, Ttk_State state) +{ + ThemeBackgroundKind kind = kThemeBackgroundWindowHeader; + Rect bounds; + SInt32 depth = 32; /* ??? */ + Boolean inColor = true; + Point origin; + + /* Avoid kThemeStatePressed, which seems to give bad results + * for ApplyThemeBackground: + */ + state &= ~TTK_STATE_PRESSED; + + TkMacOSXWinBounds((TkWindow *) tkwin, &bounds); + origin.v = -bounds.top; + origin.h = -bounds.left; + + bounds.top = bounds.left = 0; + bounds.right = Tk_Width(tkwin); + bounds.bottom = Tk_Height(tkwin); + + BEGIN_DRAWING(d) + ApplyThemeBackground(kind, &bounds, + Ttk_StateTableLookup(ThemeStateTable, state), + depth, inColor); + QDSetPatternOrigin(origin); + EraseRect(&bounds); + END_DRAWING +} + +static Ttk_ElementSpec BackgroundElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + NullElementGeometry, + 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; + Rect bounds; + SInt32 depth = 32; /* ??? */ + Boolean inColor = true; + + bounds.top = bounds.left = 0; + bounds.right = Tk_Width(tkwin); + bounds.bottom = Tk_Height(tkwin); + + BEGIN_DRAWING(d) + SetThemeBackground(brush, + depth, inColor); + EraseRect(&bounds); + END_DRAWING +} + +static Ttk_ElementSpec ToolbarBackgroundElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + NullElementGeometry, + ToolbarBackgroundElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Treeview header + * Redefine the header to use a kThemeListHeaderButton. + */ + +static Ttk_StateTable TreeHeaderAdornmentTable[] = { + { kThemeAdornmentHeaderButtonSortUp, TTK_STATE_ALTERNATE, 0 }, + { kThemeAdornmentFocus, TTK_STATE_FOCUS, 0 }, + { kThemeAdornmentNone, 0, 0 } +}; + +static void TreeHeaderElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, Ttk_State state) +{ + Rect bounds = BoxToRect(b); + ThemeButtonParms *parms = clientData; + ThemeButtonDrawInfo info; + + info.state = Ttk_StateTableLookup(ThemeStateTable, state); + info.value = Ttk_StateTableLookup(ButtonValueTable, state); + info.adornment = Ttk_StateTableLookup(TreeHeaderAdornmentTable, state); + + BEGIN_DRAWING(d) + DrawThemeButton(&bounds, parms->kind, &info, + NULL/*prevInfo*/,NULL/*eraseProc*/,NULL/*labelProc*/,0/*userData*/); + END_DRAWING +} + +static Ttk_ElementSpec TreeHeaderElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + NullElementOptions, + ButtonElementGeometryNoPadding, + TreeHeaderElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Widget layouts. + */ +TTK_BEGIN_LAYOUT(ToolbarLayout) + TTK_NODE("Toolbar.background", TTK_FILL_BOTH) +TTK_END_LAYOUT + +TTK_BEGIN_LAYOUT(ButtonLayout) + TTK_GROUP("Button.button", TTK_FILL_BOTH, + TTK_GROUP("Button.padding", TTK_FILL_BOTH, + TTK_NODE("Button.label", TTK_FILL_BOTH))) +TTK_END_LAYOUT + +TTK_BEGIN_LAYOUT(RadiobuttonLayout) + TTK_GROUP("Radiobutton.button", TTK_FILL_BOTH, + TTK_GROUP("Radiobutton.padding", TTK_FILL_BOTH, + TTK_NODE("Radiobutton.label", TTK_PACK_LEFT))) +TTK_END_LAYOUT + +TTK_BEGIN_LAYOUT(CheckbuttonLayout) + TTK_GROUP("Checkbutton.button", TTK_FILL_BOTH, + TTK_GROUP("Checkbutton.padding", TTK_FILL_BOTH, + TTK_NODE("Checkbutton.label", TTK_PACK_LEFT))) +TTK_END_LAYOUT + +TTK_BEGIN_LAYOUT(MenubuttonLayout) + TTK_GROUP("Menubutton.button", TTK_FILL_BOTH, + TTK_GROUP("Menubutton.padding", TTK_FILL_BOTH, + TTK_NODE("Menubutton.label", TTK_PACK_LEFT))) +TTK_END_LAYOUT + +/* Notebook tabs -- no focus ring */ +TTK_BEGIN_LAYOUT(TabLayout) + 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))) +TTK_END_LAYOUT + +/* Progress bars -- track only */ +TTK_BEGIN_LAYOUT(ProgressbarLayout) + TTK_NODE("Progressbar.track", TTK_EXPAND|TTK_FILL_BOTH) +TTK_END_LAYOUT + +/* Tree heading -- no border, fixed height */ +TTK_BEGIN_LAYOUT(TreeHeadingLayout) + TTK_NODE("Treeheading.cell", TTK_FILL_X) + TTK_NODE("Treeheading.image", TTK_PACK_RIGHT) + TTK_NODE("Treeheading.text", 0) +TTK_END_LAYOUT + +/*---------------------------------------------------------------------- + * +++ Initialization. + */ + +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,"Toolbar.background", + &ToolbarBackgroundElementSpec, 0); + + Ttk_RegisterElementSpec(themePtr, "Button.button", + &ButtonElementSpec, &PushButtonParms); + Ttk_RegisterElementSpec(themePtr, "Checkbutton.button", + &ButtonElementSpec, &CheckBoxParms); + Ttk_RegisterElementSpec(themePtr, "Radiobutton.button", + &ButtonElementSpec, &RadioButtonParms); + Ttk_RegisterElementSpec(themePtr, "Toolbutton.border", + &ButtonElementSpec, &BevelButtonParms); + Ttk_RegisterElementSpec(themePtr, "Menubutton.button", + &ButtonElementSpec, &PopupButtonParms); + Ttk_RegisterElementSpec(themePtr, "Treeheading.cell", + &TreeHeaderElementSpec, &ListHeaderParms); + + 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, "Combobox.field",&EntryElementSpec,0); + Ttk_RegisterElementSpec(themePtr, "Combobox.downarrow", + &PopupArrowElementSpec, 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_RegisterLayout(themePtr, "Toolbar", ToolbarLayout); + Ttk_RegisterLayout(themePtr, "TButton", ButtonLayout); + Ttk_RegisterLayout(themePtr, "TCheckbutton", CheckbuttonLayout); + Ttk_RegisterLayout(themePtr, "TRadiobutton", RadiobuttonLayout); + Ttk_RegisterLayout(themePtr, "TMenubutton", MenubuttonLayout); + Ttk_RegisterLayout(themePtr, "TProgressbar", ProgressbarLayout); + Ttk_RegisterLayout(themePtr, "TNotebook.Tab", TabLayout); + Ttk_RegisterLayout(themePtr, "Heading", TreeHeadingLayout); + + Tcl_PkgProvide(interp, "ttk::theme::aqua", TTK_VERSION); + return TCL_OK; +} + +int Ttk_MacPlatformInit(Tcl_Interp *interp) +{ + return AquaTheme_Init(interp); +} + |