1204 lines
38 KiB
C
1204 lines
38 KiB
C
/*
|
||
* 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 <das@users.sourceforge.net>
|
||
* Copyright 2008-2009, Apple Inc.
|
||
* Copyright 2009 Kevin Walzer/WordTech Communications LLC.
|
||
*
|
||
* 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"
|
||
|
||
/*
|
||
* Use this version in the core:
|
||
*/
|
||
#define BEGIN_DRAWING(d) { \
|
||
TkMacOSXDrawingContext dc; \
|
||
if (!TkMacOSXSetupDrawingContext((d), NULL, 1, &dc)) {return;}
|
||
#define END_DRAWING \
|
||
TkMacOSXRestoreDrawingContext(&dc); }
|
||
|
||
#define HIOrientation kHIThemeOrientationNormal
|
||
|
||
#ifdef __LP64__
|
||
#define RangeToFactor(maximum) (((double) (INT_MAX >> 1)) / (maximum))
|
||
#else
|
||
#define RangeToFactor(maximum) (((double) (LONG_MAX >> 1)) / (maximum))
|
||
#endif /* __LP64__ */
|
||
|
||
/*----------------------------------------------------------------------
|
||
* +++ 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}
|
||
*/
|
||
};
|
||
|
||
/*----------------------------------------------------------------------
|
||
* +++ 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;
|
||
} 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)
|
||
{
|
||
const HIThemeButtonDrawInfo info = {
|
||
.version = 0,
|
||
.state = Ttk_StateTableLookup(ThemeStateTable, state),
|
||
.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);
|
||
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->top = CGRectGetMinY(contentBounds);
|
||
paddingPtr->right = CGRectGetMaxX(scratchBounds) - CGRectGetMaxX(contentBounds) + 1;
|
||
paddingPtr->bottom = CGRectGetMaxY(scratchBounds) - CGRectGetMaxY(contentBounds);
|
||
|
||
/*
|
||
* Now add a little extra padding to account for drop shadows.
|
||
* @@@ SHOULD: call GetThemeButtonBackgroundBounds() instead.
|
||
*/
|
||
|
||
*paddingPtr = Ttk_AddPadding(*paddingPtr, ButtonMargins);
|
||
*widthPtr += Ttk_PaddingWidth(ButtonMargins);
|
||
*heightPtr += Ttk_PaddingHeight(ButtonMargins);
|
||
}
|
||
|
||
static void ButtonElementDraw(
|
||
void *clientData, void *elementRecord, Tk_Window tkwin,
|
||
Drawable d, Ttk_Box b, Ttk_State state)
|
||
{
|
||
ThemeButtonParams *params = clientData;
|
||
CGRect bounds = BoxToRect(d, Ttk_PadBox(b, ButtonMargins));
|
||
const HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, state);
|
||
|
||
BEGIN_DRAWING(d)
|
||
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() */
|
||
|
||
#define TTK_STATE_NOTEBOOK_FIRST TTK_STATE_USER1
|
||
#define TTK_STATE_NOTEBOOK_LAST TTK_STATE_USER2
|
||
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_NOTEBOOK_FIRST|TTK_STATE_NOTEBOOK_LAST},
|
||
{kHIThemeTabAdornmentTrailingSeparator, TTK_STATE_NOTEBOOK_FIRST},
|
||
{kHIThemeTabAdornmentNone, TTK_STATE_NOTEBOOK_LAST},
|
||
{kHIThemeTabAdornmentTrailingSeparator, 0 },
|
||
};
|
||
|
||
static Ttk_StateTable TabPositionTable[] = {
|
||
{ kHIThemeTabPositionOnly,
|
||
TTK_STATE_NOTEBOOK_FIRST|TTK_STATE_NOTEBOOK_LAST},
|
||
{ kHIThemeTabPositionFirst, TTK_STATE_NOTEBOOK_FIRST},
|
||
{ kHIThemeTabPositionLast, TTK_STATE_NOTEBOOK_LAST},
|
||
{ 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 const int TAB_HEIGHT = 10;
|
||
static const int TAB_OVERLAP = 10;
|
||
|
||
static void TabElementSize(
|
||
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)
|
||
{
|
||
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),
|
||
};
|
||
|
||
bounds.size.height += TAB_OVERLAP;
|
||
BEGIN_DRAWING(d)
|
||
ChkErr(HIThemeDrawTab, &bounds, &info, dc.context, HIOrientation, NULL);
|
||
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)
|
||
{
|
||
CGRect bounds = BoxToRect(d, b);
|
||
HIThemeTabPaneDrawInfo info = {
|
||
.version = 1,
|
||
.state = Ttk_StateTableLookup(ThemeStateTable, state),
|
||
.direction = kThemeTabNorth,
|
||
.size = kHIThemeTabSizeNormal,
|
||
.kind = kHIThemeTabKindNormal,
|
||
.adornment = kHIThemeTabPaneAdornmentNormal,
|
||
};
|
||
|
||
bounds.origin.y -= TAB_OVERLAP;
|
||
bounds.size.height += TAB_OVERLAP;
|
||
BEGIN_DRAWING(d)
|
||
ChkErr(HIThemeDrawTabPane, &bounds, &info, dc.context, HIOrientation);
|
||
END_DRAWING
|
||
}
|
||
|
||
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)
|
||
{
|
||
CGRect bounds = BoxToRect(d, b);
|
||
const HIThemeGroupBoxDrawInfo info = {
|
||
.version = 0,
|
||
.state = Ttk_StateTableLookup(ThemeStateTable, state),
|
||
.kind = kHIThemeGroupBoxKindPrimaryOpaque,
|
||
};
|
||
|
||
BEGIN_DRAWING(d)
|
||
ChkErr(HIThemeDrawGroupBox, &bounds, &info, dc.context, HIOrientation);
|
||
END_DRAWING
|
||
}
|
||
|
||
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, 3, 4, 4 };
|
||
|
||
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>>)
|
||
*/
|
||
|
||
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 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;
|
||
info.trackInfo.slider.thumbDir = kThemeThumbPlain;
|
||
}
|
||
|
||
BEGIN_DRAWING(d)
|
||
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)
|
||
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)
|
||
ChkErr(HIThemeDrawSeparator, &bounds, &info, dc.context, HIOrientation);
|
||
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.
|
||
*
|
||
* 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. If we're drawing into an
|
||
* off-screen graphics port this leads to alignment glitches.
|
||
*/
|
||
|
||
static void FillElementDraw(
|
||
void *clientData, void *elementRecord, Tk_Window tkwin,
|
||
Drawable d, Ttk_Box b, Ttk_State state)
|
||
{
|
||
CGRect bounds = BoxToRect(d, b);
|
||
ThemeBrush brush = (state & TTK_STATE_BACKGROUND)
|
||
? kThemeBrushModelessDialogBackgroundInactive
|
||
: kThemeBrushModelessDialogBackgroundActive;
|
||
|
||
BEGIN_DRAWING(d)
|
||
ChkErr(HIThemeSetFill, brush, NULL, dc.context, HIOrientation);
|
||
//QDSetPatternOrigin(PatternOrigin(tkwin, d));
|
||
CGContextFillRect(dc.context, bounds);
|
||
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:
|
||
*/
|
||
|