summaryrefslogtreecommitdiffstats
path: root/macosx/ttkMacOSXTheme.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/ttkMacOSXTheme.c')
-rw-r--r--macosx/ttkMacOSXTheme.c2776
1 files changed, 1564 insertions, 1212 deletions
diff --git a/macosx/ttkMacOSXTheme.c b/macosx/ttkMacOSXTheme.c
index 5465bec..7a89fa7 100644
--- a/macosx/ttkMacOSXTheme.c
+++ b/macosx/ttkMacOSXTheme.c
@@ -30,134 +30,158 @@
*/
#include "tkMacOSXPrivate.h"
-#include "ttk/ttkTheme.h"
+#include "ttk/ttkThemeInt.h"
+#include "ttkMacOSXTheme.h"
+#include "tkColor.h"
+#include <math.h>
+
+MODULE_SCOPE NSColor *controlAccentColor(void) {
+ static int accentPixel = -1;
+ if (accentPixel == -1) {
+ TkColor *temp = TkpGetColor(NULL, "systemControlAccentColor");
+ accentPixel = temp->color.pixel;
+ ckfree(temp);
+ }
+ return TkMacOSXGetNSColor(NULL, accentPixel);
+}
/*
- * Macros for handling drawing contexts.
+ * Values which depend on the OS version. These are initialized
+ * in Ttk_MacOSXInit.
*/
-#define BEGIN_DRAWING(d) { \
- TkMacOSXDrawingContext dc; \
- if (!TkMacOSXSetupDrawingContext((d), NULL, &dc)) { \
- return; \
- } \
+static Ttk_Padding entryElementPadding;
+static CGFloat Ttk_ContrastDelta;
-#define END_DRAWING \
- TkMacOSXRestoreDrawingContext(&dc);}
+/*----------------------------------------------------------------------
+ * +++ ComputeButtonDrawInfo --
+ *
+ * Fill in an appearance manager HIThemeButtonDrawInfo record
+ * from a Ttk state and the ThemeButtonParams used as the
+ * clientData.
+ */
-#define HIOrientation kHIThemeOrientationNormal
-#define NoThemeMetric 0xFFFFFFFF
+static inline HIThemeButtonDrawInfo ComputeButtonDrawInfo(
+ ThemeButtonParams *params,
+ Ttk_State state,
+ TCL_UNUSED(Tk_Window))
+{
+ /*
+ * See ButtonElementDraw for the explanation of why we always draw
+ * some buttons in the active state.
+ */
-#ifdef __LP64__
-#define RangeToFactor(maximum) (((double) (INT_MAX >> 1)) / (maximum))
-#else
-#define RangeToFactor(maximum) (((double) (LONG_MAX >> 1)) / (maximum))
-#endif /* __LP64__ */
+ SInt32 HIThemeState;
+ int adornment = 0;
-#define TTK_STATE_FIRST_TAB TTK_STATE_USER1
-#define TTK_STATE_LAST_TAB TTK_STATE_USER2
-#define TTK_TREEVIEW_STATE_SORTARROW TTK_STATE_USER1
+ HIThemeState = Ttk_StateTableLookup(ThemeStateTable, state);
-/*
- * Colors and gradients used in Dark Mode.
- */
+ /*
+ * HITheme uses the adornment to decide the direction of the
+ * arrow on a Disclosure Button. Also HITheme draws inactive
+ * (TTK_STATE_BACKGROUND) buttons in a gray color but macOS
+ * no longer does that. So we adjust the HIThemeState.
+ */
-static CGFloat darkButtonFace[4] = {
- 90.0 / 255, 86.0 / 255, 95.0 / 255, 1.0
-};
-static CGFloat darkPressedButtonFace[4] = {
- 114.0 / 255, 110.0 / 255, 118.0 / 255, 1.0
-};
-static CGFloat darkPressedBevelFace[4] = {
- 135.0 / 255, 136.0 / 255, 138.0 / 255, 1.0
-};
-static CGFloat darkSelectedBevelFace[4] = {
- 162.0 / 255, 163.0 / 255, 165.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 darkSelectedTab[4] = {
- 97.0 / 255, 94.0 / 255, 102.0 / 255, 1.0
-};
-static CGFloat darkTab[4] = {
- 44.0 / 255, 41.0 / 255, 50.0 / 255, 1.0
-};
-static CGFloat darkFocusRing[4] = {
- 38.0 / 255, 113.0 / 255, 159.0 / 255, 1.0
-};
-static CGFloat darkFocusRingTop[4] = {
- 50.0 / 255, 124.0 / 255, 171.0 / 255, 1.0
-};
-static CGFloat darkFocusRingBottom[4] = {
- 57.0 / 255, 130.0 / 255, 176.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 darkFrameTop[4] = {1.0, 1.0, 1.0, 0.0625};
-static CGFloat darkFrameBottom[4] = {1.0, 1.0, 1.0, 0.125};
-static CGFloat darkFrameAccent[4] = {0.0, 0.0, 0.0, 0.0625};
-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
-};
-static CGFloat pressedPushButtonGradient[8] = {
- 35.0 / 255, 123.0 / 255, 244.0 / 255, 1.0,
- 30.0 / 255, 114.0 / 255, 235.0 / 255, 1.0
-};
+ switch (params->kind) {
+ case kThemeArrowButton:
+ adornment = kThemeAdornmentDrawIndicatorOnly;
+ if (state & TTK_STATE_SELECTED) {
+ adornment |= kThemeAdornmentArrowUpArrow;
+ }
+ /* Fall through. */
+ case kThemeRadioButton:
+ /*
+ * The gray color is better than the blue color for a
+ * background selected Radio Button.
+ */
+
+ if (state & TTK_STATE_SELECTED) {
+ break;
+ }
+ default:
+ if (state & TTK_STATE_BACKGROUND) {
+ HIThemeState |= kThemeStateActive;
+ }
+ break;
+ }
+
+ const HIThemeButtonDrawInfo info = {
+ .version = 0,
+ .state = HIThemeState,
+ .kind = params ? params->kind : 0,
+ .value = Ttk_StateTableLookup(ButtonValueTable, state),
+ .adornment = Ttk_StateTableLookup(ButtonAdornmentTable, state) | adornment,
+ };
+ return info;
+}
/*
- * When building on systems earlier than 10.8 there is no reasonable way to
- * convert an NSColor to a CGColor. We do run-time checking of the OS version,
- * and never need the CGColor property on older systems, so we can use this
- * CGCOLOR macro, which evaluates to NULL without raising compiler warnings.
- * Similarly, we never draw rounded rectangles on older systems which did not
- * have CGPathCreateWithRoundedRect, so we just redefine it to return NULL.
+ * When we draw simulated Apple widgets we use the Core Graphics framework.
+ * Core Graphics uses CGColorRefs, not NSColors. A CGColorRef must be retained
+ * and released explicitly while an NSColor is autoreleased. In version 10.8
+ * of macOS Apple introduced a CGColor property of an NSColor which is guaranteed
+ * to be a valid CGColorRef for (approximately) the same color and is released
+ * when the NSColor is autoreleased.
+ *
+ * When building on systems earlier than 10.8 there is no painless way to
+ * convert an NSColor to a CGColor. On the other hand, on those systems we use
+ * the HIToolbox to draw all widgets, so we never need to call Core Graphics
+ * drawing routines directly. This means that the functions and macros below
+ * which construct CGColorRefs can be defined to return nil on systems before
+ * 10.8.
+ *
+ * Similarly, those older systems did not have CGPathCreateWithRoundedRect, but
+ * since we never need to draw rounded rectangles on those systems we can just
+ * define it to return nil.
*/
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1080
+static CGColorRef
+CGColorFromRGBA(
+ CGFloat *rgba)
+{
+ NSColorSpace *colorSpace = [NSColorSpace sRGBColorSpace];
+ NSColor *nscolor = [NSColor colorWithColorSpace: colorSpace
+ components: rgba
+ count: 4];
+ return nscolor.CGColor;
+}
+
+static CGColorRef
+CGColorFromGray(
+ GrayColor g)
+{
+ CGFloat rgba[4] = {g.grayscale, g.grayscale, g.grayscale, g.alpha};
+ NSColorSpace *colorSpace = [NSColorSpace sRGBColorSpace];
+ NSColor *nscolor = [NSColor colorWithColorSpace: colorSpace
+ components: rgba
+ count: 4];
+ return nscolor.CGColor;
+}
+
#define CGCOLOR(nscolor) (nscolor).CGColor
+
#else
-#define CGCOLOR(nscolor) (0 ? (CGColorRef) (nscolor) : NULL)
-#define CGPathCreateWithRoundedRect(w, x, y, z) NULL
-#endif
-/*
- * If we try to draw a rounded rectangle with too large of a radius
- * CoreGraphics will raise a fatal exception. This macro returns if
- * the width or height is less than twice the radius. Presumably this
- * only happens when a widget has not yet been configured and has size
- * 1x1.
- */
+#define CGCOLOR(nscolor) NULL
+#define CGColorFromRGBA(rgba) NULL
+#define CGColorFromGray(gray) NULL
+#define CGPathCreateWithRoundedRect(w, x, y, z) NULL
-#define CHECK_RADIUS(radius, bounds) \
- if ((radius) > (bounds).size.width / 2 || (radius) > (bounds).size.height / 2) { \
- return; \
- }
+#endif
/*----------------------------------------------------------------------
* +++ Utilities.
*/
-/*
+/*----------------------------------------------------------------------
* BoxToRect --
- * Convert a Ttk_Box in Tk coordinates relative to the given Drawable
- * to a native Rect relative to the containing port.
+ *
+ * Convert a Ttk_Box in Tk coordinates relative to the given Drawable to a
+ * native CGRect relative to the containing NSView. (The coordinate system
+ * is the one used by CGContextRef, which has origin at the upper left
+ * corner, and y increasing downward.)
*/
static inline CGRect BoxToRect(
@@ -175,60 +199,88 @@ static inline CGRect BoxToRect(
return rect;
}
-/*
- * Table mapping Tk states to Appearance manager ThemeStates
+/*----------------------------------------------------------------------
+ * LookupGrayPalette
+ *
+ * Retrieves the palette of grayscale colors needed to draw a particular
+ * type of button, in a particular state, in light or dark mode.
+ *
*/
-static Ttk_StateTable ThemeStateTable[] = {
- {kThemeStateActive, TTK_STATE_ALTERNATE | TTK_STATE_BACKGROUND, 0},
- {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}
- */
-};
+static GrayPalette LookupGrayPalette(
+ const ButtonDesign *design,
+ Ttk_State state,
+ int isDark)
+{
+ const PaletteStateTable *entry = design->palettes;
+ while ((state & entry->onBits) != entry->onBits ||
+ (~state & entry->offBits) != entry->offBits)
+ {
+ ++entry;
+ }
+ return isDark ? entry->dark : entry->light;
+}
/*----------------------------------------------------------------------
* NormalizeButtonBounds --
*
- * Apple's Human Interface Guidelines only allow three specific heights
- * for most 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.
+ * This function returns the actual bounding rectangle that will be used
+ * in drawing the button.
+ *
+ * Apple only allows three specific heights for most buttons: regular,
+ * small and mini. We always use the regular size. However, Ttk may
+ * provide a bounding rectangle with arbitrary height. We draw the Mac
+ * button centered vertically in the Ttk rectangle, with the same width as
+ * the rectangle. (But we take care to produce an integer y coordinate,
+ * to avoid unexpected anti-aliasing.)
+ *
+ * In addition, the button types which are not known to HIToolbox need some
+ * adjustments to their bounds.
*
- * The BevelButton is allowed to have arbitrary size, and also has
- * external padding. This is handled separately here.
*/
static CGRect NormalizeButtonBounds(
- SInt32 heightMetric,
- CGRect bounds)
+ ThemeButtonParams *params,
+ CGRect bounds,
+ int isDark)
{
SInt32 height;
- if (heightMetric != (SInt32) NoThemeMetric) {
- ChkErr(GetThemeMetric, heightMetric, &height);
- bounds.origin.y += (bounds.size.height - height) / 2;
+ if (params->heightMetric != NoThemeMetric) {
+ ChkErr(GetThemeMetric, params->heightMetric, &height);
+ height += 2;
+ bounds.origin.y += round(1 + (bounds.size.height - height) / 2);
bounds.size.height = height;
}
+ switch (params->kind) {
+ case TkRoundedRectButton:
+ bounds.size.height -= 1;
+ break;
+ case TkInlineButton:
+ bounds.size.height -= 4;
+ bounds.origin.y += 1;
+ break;
+ case TkRecessedButton:
+ bounds.size.height -= 2;
+ break;
+ case TkSidebarButton:
+ bounds.size.height += 8;
+ break;
+ case kThemeRoundButtonHelp:
+ if (isDark) {
+ bounds.size.height = bounds.size.width = 22;
+ } else {
+ bounds.size.height = bounds.size.width = 22;
+ }
+ break;
+ default:
+ break;
+ }
return bounds;
}
/*----------------------------------------------------------------------
- * +++ Backgrounds
+ * +++ Background Colors
*
* Support for contrasting background colors when GroupBoxes or Tabbed
* panes are nested inside each other. Early versions of macOS used ridged
@@ -243,31 +295,31 @@ static CGRect NormalizeButtonBounds(
* support Dark Mode anyway.
*/
-static const CGFloat WINDOWBACKGROUND[4] = {
- 235.0 / 255, 235.0 / 255, 235.0 / 255, 1.0
-};
-static const CGFloat WHITERGBA[4] = {1.0, 1.0, 1.0, 1.0};
-static const CGFloat BLACKRGBA[4] = {0.0, 0.0, 0.0, 1.0};
+RGBACOLOR windowBackground[4] = RGBA256(235.0, 235.0, 235.0, 1.0);
/*----------------------------------------------------------------------
* GetBackgroundColor --
*
* Fills the array rgba with the color coordinates for a background color.
- * Start with the background color of a window's geometry container, or
- * the standard ttk window background if there is no container. If the
+ * Start with the background color of a window's container, or the
+ * standard ttk window background if there is no container. If the
* contrast parameter is nonzero, modify this color to be darker, for the
* aqua appearance, or lighter for the DarkAqua appearance. This is
- * primarily used by the Fill and Background elements.
+ * primarily used by the Fill and Background elements. The save parameter
+ * is normally YES, so the contrasting color is saved in the private
+ * data of the widget. This behavior can be disabled in special cases,
+ * such as when drawing notebook tabs in macOS 11.
*/
-static void GetBackgroundColor(
+static void GetBackgroundColorRGBA(
TCL_UNUSED(CGContextRef),
Tk_Window tkwin,
int contrast,
+ Bool save,
CGFloat *rgba)
{
- TkWindow *winPtr = (TkWindow *)tkwin;
- TkWindow *containerPtr = (TkWindow *)TkGetContainer(tkwin);
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ TkWindow *containerPtr = (TkWindow *) TkGetContainer(tkwin);
while (containerPtr && containerPtr->privatePtr) {
if (containerPtr->privatePtr->flags & TTK_HAS_CONTRASTING_BG) {
@@ -287,23 +339,24 @@ static void GetBackgroundColor(
[windowColor getComponents: rgba];
} else {
for (int i = 0; i < 4; i++) {
- rgba[i] = WINDOWBACKGROUND[i];
+ rgba[i] = windowBackground[i];
}
}
}
+
if (contrast) {
int isDark = (rgba[0] + rgba[1] + rgba[2] < 1.5);
if (isDark) {
for (int i = 0; i < 3; i++) {
- rgba[i] += 8.0 / 255.0;
+ rgba[i] += Ttk_ContrastDelta*contrast / 255.0;
}
} else {
for (int i = 0; i < 3; i++) {
- rgba[i] -= 8.0 / 255.0;
+ rgba[i] -= Ttk_ContrastDelta*contrast / 255.0;
}
}
- if (winPtr->privatePtr) {
+ if (save && winPtr->privatePtr) {
winPtr->privatePtr->flags |= TTK_HAS_CONTRASTING_BG;
for (int i = 0; i < 4; i++) {
winPtr->privatePtr->fillRGBA[i] = rgba[i];
@@ -312,12 +365,313 @@ static void GetBackgroundColor(
}
}
+static CGColorRef GetBackgroundCGColor(
+ CGContextRef context,
+ Tk_Window tkwin,
+ int contrast,
+ Bool save)
+{
+ CGFloat rgba[4];
+ GetBackgroundColorRGBA(context, tkwin, contrast, save, rgba);
+ return CGColorFromRGBA(rgba);
+}
/*----------------------------------------------------------------------
- * +++ Single Arrow Images --
+ * +++ Buttons
+ */
+
+/*----------------------------------------------------------------------
+ * FillRoundedRectangle --
*
- * Used in ListHeaders and Comboboxes as well as disclosure triangles in
- * macOS 11.
+ * Fill a rounded rectangle with a specified solid color.
+ */
+
+static void FillRoundedRectangle(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat radius,
+ CGColorRef color)
+{
+ CGPathRef path;
+ CHECK_RADIUS(radius, bounds)
+
+ path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
+ if (!path) {
+ return;
+ }
+ CGContextSetFillColorWithColor(context, color);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextFillPath(context);
+ CFRelease(path);
+}
+
+/*----------------------------------------------------------------------
+ * FillBorder --
+ *
+ * Draw a 1-pixel border around a rounded rectangle using a 3-step
+ * gradient of shades of gray.
+ */
+
+static void FillBorder(
+ CGContextRef context,
+ CGRect bounds,
+ GrayPalette palette,
+ CGFloat radius)
+{
+ if (bounds.size.width < 2) {
+ return;
+ }
+ NSColorSpace *sRGB = [NSColorSpace sRGBColorSpace];
+ CGPoint end = CGPointMake(bounds.origin.x, bounds.origin.y + bounds.size.height);
+ CGFloat corner = (radius > 0 ? radius : 2.0) / bounds.size.height;
+ CGFloat locations[4] = {0.0, corner, 1.0 - corner, 1.0};
+ CGPathRef path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
+ CGFloat colors[16];
+ colors[0] = colors[1] = colors[2] = palette.top / 255.0;
+ colors[4] = colors[5] = colors[6] = palette.side / 255.0;
+ colors[8] = colors[9] = colors[10] = palette.side / 255.0;
+ colors[12] = colors[13] = colors[14] = palette.bottom / 255.0;
+ colors[3] = colors[7] = colors[11] = colors[15] = 1.0;
+ CGGradientRef gradient = CGGradientCreateWithColorComponents(
+ sRGB.CGColorSpace, colors, locations, 4);
+ if (!gradient) {
+ return;
+ }
+ CGContextSaveGState(context);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextClip(context);
+ CGContextDrawLinearGradient(context, gradient, bounds.origin, end, 0.0);
+ CGContextRestoreGState(context);
+ CFRelease(path);
+ CFRelease(gradient);
+}
+
+/*----------------------------------------------------------------------
+ * DrawFocusRing --
+ *
+ * Draw a 4-pixel wide rounded focus ring enclosing a rounded
+ * rectangle, using the current system accent color.
+ */
+
+static void DrawFocusRing(
+ CGContextRef context,
+ CGRect bounds,
+ const ButtonDesign *design)
+{
+ CGColorRef highlightColor;
+ CGFloat highlight[4] = {1.0, 1.0, 1.0, 0.2};
+ CGColorRef focusColor;
+
+ focusColor = CGCOLOR([controlAccentColor() colorWithAlphaComponent:0.6]);
+ FillRoundedRectangle(context, bounds, design->radius, focusColor);
+ bounds = CGRectInset(bounds, 3, 3);
+ highlightColor = CGColorFromRGBA(highlight);
+ CGContextSetFillColorWithColor(context, highlightColor);
+ CGContextFillRect(context, bounds);
+}
+
+/*----------------------------------------------------------------------
+ * DrawGrayButton --
+ *
+ * Draw a button in normal gray colors.
+ *
+ * Aqua buttons are normally drawn in a grayscale color. The buttons,
+ * which are shaped as rounded rectangles have a 1-pixel border which is
+ * drawn in a 3-step gradient and a solid gray face.
+ *
+ * Note that this will produce a round button if length = width =
+ * 2*radius.
+ */
+
+static void DrawGrayButton(
+ CGContextRef context,
+ CGRect bounds,
+ const ButtonDesign *design,
+ Ttk_State state,
+ Tk_Window tkwin)
+{
+ int isDark = TkMacOSXInDarkMode(tkwin);
+ GrayPalette palette = LookupGrayPalette(design, state, isDark);
+ GrayColor faceGray = {.grayscale = 0.0, .alpha = 1.0};
+ CGFloat radius = 2 * design->radius <= bounds.size.height ?
+ design->radius : bounds.size.height / 2;
+ if (palette.top <= 255.0) {
+ FillBorder(context, bounds, palette, radius);
+ }
+ if (palette.face <= 255.0) {
+ faceGray.grayscale = palette.face / 255.0;
+ } else {
+
+ /*
+ * Color values > 255 are "transparent" which really means that we
+ * fill with the background color.
+ */
+
+ CGFloat rgba[4], gray;
+ GetBackgroundColorRGBA(context, tkwin, 0, NO, rgba);
+ gray = (rgba[0] + rgba[1] + rgba[2]) / 3.0;
+ faceGray.grayscale = gray;
+ }
+ FillRoundedRectangle(context, CGRectInset(bounds, 1, 1), radius - 1,
+ CGColorFromGray(faceGray));
+}
+
+/*----------------------------------------------------------------------
+ * DrawAccentedButton --
+ *
+ * The accent color is only used when drawing buttons in the active
+ * window. Push Buttons and segmented Arrow Buttons are drawn in color
+ * when in the pressed state. Selected Check Buttons, Radio Buttons and
+ * notebook Tabs are also drawn in color. The color is based on the
+ * user's current choice for the controlAccentColor, but is actually a
+ * linear gradient with a 1-pixel darker line at the top and otherwise
+ * changing from lighter at the top to darker at the bottom. This
+ * function draws a colored rounded rectangular button.
+ */
+
+static void DrawAccentedButton(
+ CGContextRef context,
+ CGRect bounds,
+ const ButtonDesign *design,
+ int state,
+ int isDark)
+{
+ NSColorSpace *sRGB = [NSColorSpace sRGBColorSpace];
+ CGColorRef faceColor = CGCOLOR(controlAccentColor());
+ CGFloat radius = design->radius;
+ CGPathRef path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
+ // This gradient should only be used for PushButtons and Tabs, and it needs
+ // to be lighter at the top.
+ static CGFloat components[12] = {1.0, 1.0, 1.0, 0.05,
+ 1.0, 1.0, 1.0, 0.2,
+ 1.0, 1.0, 1.0, 0.0};
+ CGFloat locations[3] = {0.0, 0.05, 1.0};
+ CGGradientRef gradient = CGGradientCreateWithColorComponents(
+ sRGB.CGColorSpace, components, locations, 3);
+ CGPoint end;
+ if (bounds.size.height > 2*radius) {
+ bounds.size.height -= 1;
+ }
+ end = CGPointMake(bounds.origin.x, bounds.origin.y + bounds.size.height);
+ CGContextSaveGState(context);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextClip(context);
+ FillRoundedRectangle(context, bounds, radius, faceColor);
+ CGContextDrawLinearGradient(context, gradient, bounds.origin, end, 0.0);
+ if (state & TTK_STATE_PRESSED &&
+ state & TTK_STATE_ALTERNATE) {
+ CGColorRef color = isDark ?
+ CGColorFromGray(darkPressedDefaultButton) :
+ CGColorFromGray(pressedDefaultButton);
+ FillRoundedRectangle(context, bounds, radius, color);
+ }
+ CGContextRestoreGState(context);
+ CFRelease(path);
+ CFRelease(gradient);
+}
+
+/*----------------------------------------------------------------------
+ * DrawAccentedSegment --
+ *
+ * Draw the colored ends of widgets like popup buttons and combo buttons.
+ */
+
+static void DrawAccentedSegment(
+ CGContextRef context,
+ CGRect bounds,
+ const ButtonDesign *design,
+ Ttk_State state,
+ Tk_Window tkwin)
+{
+ /*
+ * Clip to the bounds and then draw an accented button which is extended so
+ * that the rounded corners on the left will be clipped off. This assumes
+ * that the bounds include room for the focus ring.
+ */
+ int isDark = TkMacOSXInDarkMode(tkwin);
+ GrayColor sepGray = isDark ? darkComboSeparator : lightComboSeparator;
+ CGColorRef sepColor = CGColorFromGray(sepGray);
+ CGRect clip = bounds;
+ clip.size.height += 10;
+ bounds.origin.x -= 10;
+ bounds.size.width += 10;
+ CGPoint separator[2] = {
+ CGPointMake(clip.origin.x - 1, bounds.origin.y + 5),
+ CGPointMake(clip.origin.x - 1,
+ bounds.origin.y + bounds.size.height - 3)};
+ CGContextSaveGState(context);
+ CGContextSetStrokeColorWithColor(context, sepColor);
+ CGContextSetShouldAntialias(context, false);
+ CGContextSetLineWidth(context, 0.5);
+ CGContextAddLines(context, separator, 2);
+ CGContextStrokePath(context);
+ CGContextSetShouldAntialias(context, true);
+ if (state & TTK_STATE_FOCUS) {
+ CGRect focusClip = clip;
+ clip.size.width += 4;
+ CGContextClipToRect(context, focusClip);
+ bounds = CGRectInset(bounds, 0, 1);
+ DrawFocusRing(context, bounds, design);
+ }
+ bounds = CGRectInset(bounds, 4, 4);
+ if (state & TTK_STATE_BACKGROUND) {
+ bounds.size.height += 2;
+ } else {
+ bounds.size.height += 1;
+ }
+ CGContextClipToRect(context, clip);
+ if ((state & TTK_STATE_BACKGROUND) || (state & TTK_STATE_DISABLED)) {
+ DrawGrayButton(context, bounds, design, state, tkwin);
+ } else {
+ DrawAccentedButton(context, bounds, design, state | TTK_STATE_ALTERNATE,
+ isDark);
+ }
+ CGContextRestoreGState(context);
+}
+
+/*----------------------------------------------------------------------
+ * +++ Entry boxes
+ */
+
+static void DrawEntry(
+ CGContextRef context,
+ CGRect bounds,
+ const ButtonDesign *design,
+ int state,
+ Tk_Window tkwin)
+{
+ int isDark = TkMacOSXInDarkMode(tkwin);
+ GrayPalette palette = LookupGrayPalette(design, state, isDark);
+ CGColorRef backgroundColor;
+ CGFloat bgRGBA[4];
+ if (isDark) {
+ GetBackgroundColorRGBA(context, tkwin, 0, NO, bgRGBA);
+
+ /*
+ * Lighten the entry background to provide contrast.
+ */
+
+ for (int i = 0; i < 3; i++) {
+ bgRGBA[i] += 8.0 / 255.0;
+ }
+ backgroundColor = CGColorFromRGBA(bgRGBA);
+ } else {
+ backgroundColor = CG_WHITE;
+ }
+ if (state & TTK_STATE_FOCUS) {
+ DrawFocusRing(context, bounds, design);
+ } else {
+ FillBorder(context, CGRectInset(bounds,3,3), palette, design->radius);
+ }
+ bounds = CGRectInset(bounds, 4, 4);
+ FillRoundedRectangle(context, bounds, design->radius, backgroundColor);
+}
+
+/*----------------------------------------------------------------------
+ * +++ Chevrons, CheckMarks, etc. --
*/
static void DrawDownArrow(
@@ -325,11 +679,20 @@ static void DrawDownArrow(
CGRect bounds,
CGFloat inset,
CGFloat size,
- const CGFloat *rgba)
+ int state)
{
+ CGColorRef strokeColor;
CGFloat x, y;
- CGContextSetRGBStrokeColor(context, rgba[0], rgba[1], rgba[2], rgba[3]);
+
+ if (state & TTK_STATE_DISABLED) {
+ strokeColor = CGCOLOR([NSColor disabledControlTextColor]);
+ } else if (state & TTK_STATE_IS_ACCENTED) {
+ strokeColor = CG_WHITE;
+ } else {
+ strokeColor = CGCOLOR([NSColor controlTextColor]);
+ }
+ CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextSetLineWidth(context, 1.5);
x = bounds.origin.x + inset;
y = bounds.origin.y + trunc(bounds.size.height / 2) + 1;
@@ -342,16 +705,28 @@ static void DrawDownArrow(
CGContextStrokePath(context);
}
+/*----------------------------------------------------------------------
+ * DrawUpArrow --
+ *
+ * Draws a single upward pointing arrow for ListHeaders and Disclosure Buttons.
+ */
+
static void DrawUpArrow(
CGContextRef context,
CGRect bounds,
CGFloat inset,
CGFloat size,
- const CGFloat *rgba)
+ int state)
{
+ NSColor *strokeColor;
CGFloat x, y;
- CGContextSetRGBStrokeColor(context, rgba[0], rgba[1], rgba[2], rgba[3]);
+ if (state & TTK_STATE_DISABLED) {
+ strokeColor = [NSColor disabledControlTextColor];
+ } else {
+ strokeColor = [NSColor controlTextColor];
+ }
+ CGContextSetStrokeColorWithColor(context, CGCOLOR(strokeColor));
CGContextSetLineWidth(context, 1.5);
x = bounds.origin.x + inset;
y = bounds.origin.y + trunc(bounds.size.height / 2);
@@ -364,28 +739,60 @@ static void DrawUpArrow(
CGContextStrokePath(context);
}
-static void DrawClosedDisclosure(
+/*----------------------------------------------------------------------
+ * DrawUpDownArrows --
+ *
+ * Draws the double arrows used in menu buttons and spin buttons.
+ */
+
+static void DrawUpDownArrows(
CGContextRef context,
CGRect bounds,
CGFloat inset,
CGFloat size,
- CGFloat *rgba)
+ CGFloat gap,
+ int state,
+ ThemeDrawState drawState)
{
CGFloat x, y;
-
- CGContextSetRGBStrokeColor(context, rgba[0], rgba[1], rgba[2], rgba[3]);
+ NSColor *topStrokeColor, *bottomStrokeColor;
+ if (drawState == BOTH_ARROWS && !(state & TTK_STATE_BACKGROUND)) {
+ topStrokeColor = bottomStrokeColor = [NSColor whiteColor];
+ } else if (drawState == kThemeStatePressedDown) {
+ topStrokeColor = [NSColor controlTextColor];
+ bottomStrokeColor = [NSColor whiteColor];
+ } else if (drawState == kThemeStatePressedUp) {
+ topStrokeColor = [NSColor whiteColor];
+ bottomStrokeColor = [NSColor controlTextColor];
+ } else if (state & TTK_STATE_DISABLED) {
+ topStrokeColor = bottomStrokeColor = [NSColor disabledControlTextColor];
+ } else {
+ topStrokeColor = bottomStrokeColor = [NSColor controlTextColor];
+ }
CGContextSetLineWidth(context, 1.5);
x = bounds.origin.x + inset;
y = bounds.origin.y + trunc(bounds.size.height / 2);
CGContextBeginPath(context);
- CGPoint arrow[3] = {
- {x, y - size / 4 - 1}, {x + size / 2, y}, {x, y + size / 4 + 1}
- };
- CGContextAddLines(context, arrow, 3);
+ CGPoint bottomArrow[3] =
+ {{x, y + gap}, {x + size / 2, y + gap + size / 2}, {x + size, y + gap}};
+ CGContextAddLines(context, bottomArrow, 3);
+ CGContextSetStrokeColorWithColor(context, CGCOLOR(bottomStrokeColor));
+ CGContextStrokePath(context);
+ CGContextBeginPath(context);
+ CGPoint topArrow[3] =
+ {{x, y - gap}, {x + size / 2, y - gap - size / 2}, {x + size, y - gap}};
+ CGContextAddLines(context, topArrow, 3);
+ CGContextSetStrokeColorWithColor(context, CGCOLOR(topStrokeColor));
CGContextStrokePath(context);
}
-static void DrawOpenDisclosure(
+/*----------------------------------------------------------------------
+ * DrawClosedDisclosure --
+ *
+ * Draws a disclosure chevron in the Big Sur style, for Treeviews.
+ */
+
+static void DrawClosedDisclosure(
CGContextRef context,
CGRect bounds,
CGFloat inset,
@@ -400,24 +807,24 @@ static void DrawOpenDisclosure(
y = bounds.origin.y + trunc(bounds.size.height / 2);
CGContextBeginPath(context);
CGPoint arrow[3] = {
- {x, y - size / 4}, {x + size / 2, y + size / 2}, {x + size, y - size / 4}
+ {x, y - size / 4 - 1}, {x + size / 2, y}, {x, y + size / 4 + 1}
};
CGContextAddLines(context, arrow, 3);
CGContextStrokePath(context);
}
/*----------------------------------------------------------------------
- * +++ Double Arrow Buttons --
+ * DrawOpenDisclosure --
*
- * Used in MenuButtons and SpinButtons.
+ * Draws an open disclosure chevron in the Big Sur style, for Treeviews.
*/
-static void DrawUpDownArrows(
+static void DrawOpenDisclosure(
CGContextRef context,
CGRect bounds,
CGFloat inset,
CGFloat size,
- const CGFloat *rgba)
+ CGFloat *rgba)
{
CGFloat x, y;
@@ -426,655 +833,687 @@ static void DrawUpDownArrows(
x = bounds.origin.x + inset;
y = bounds.origin.y + trunc(bounds.size.height / 2);
CGContextBeginPath(context);
- CGPoint bottomArrow[3] =
- {{x, y + 2}, {x + size / 2, y + 2 + size / 2}, {x + size, y + 2}};
- CGContextAddLines(context, bottomArrow, 3);
- CGPoint topArrow[3] =
- {{x, y - 2}, {x + size / 2, y - 2 - size / 2}, {x + size, y - 2}};
- CGContextAddLines(context, topArrow, 3);
+ CGPoint arrow[3] = {
+ {x, y - size / 4}, {x + size / 2, y + size / 2}, {x + size, y - size / 4}
+ };
+ CGContextAddLines(context, arrow, 3);
CGContextStrokePath(context);
}
-
/*----------------------------------------------------------------------
- * +++ FillButtonBackground --
+ * IndicatorColor --
*
- * Fills a rounded rectangle with a transparent black gradient.
- * This is a no-op if building on 10.8 or older.
+ * Returns a CGColorRef of the appropriate shade for a check button or
+ * radio button in a given state.
*/
-static void FillButtonBackground(
- CGContextRef context,
- CGRect bounds,
- CGFloat radius)
+static CGColorRef IndicatorColor(
+ int state,
+ int isDark)
{
- CHECK_RADIUS(radius, bounds)
-
- 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);
+ if (state & TTK_STATE_DISABLED) {
+ return isDark ?
+ CGColorFromGray(darkDisabledIndicator) :
+ CGColorFromGray(lightDisabledIndicator);
+ } else if ((state & TTK_STATE_SELECTED || state & TTK_STATE_ALTERNATE) &&
+ !(state & TTK_STATE_BACKGROUND)) {
+ return CG_WHITE;
+ } else {
+ return CGCOLOR([NSColor controlTextColor]);
+ }
}
/*----------------------------------------------------------------------
- * +++ HighlightButtonBorder --
+ * DrawCheckIndicator --
*
- * Accent the top border of a rounded rectangle with a transparent
- * white gradient.
+ * Draws the checkmark or horizontal bar in a check box.
*/
-static void HighlightButtonBorder(
+static void DrawCheckIndicator(
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);
-}
-
-/*----------------------------------------------------------------------
- * DrawGroupBox --
- *
- * This is a standalone drawing procedure which draws the contrasting
- * rounded rectangular box for LabelFrames and Notebook panes used in
- * more recent versions of macOS.
- */
-
-static void DrawGroupBox(
CGRect bounds,
- CGContextRef context,
- Tk_Window tkwin)
+ int state,
+ int isDark)
{
- CHECK_RADIUS(4, bounds)
-
- CGPathRef path;
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *borderColor, *bgColor;
- static CGFloat border[4] = {1.0, 1.0, 1.0, 0.25};
- CGFloat fill[4];
-
- GetBackgroundColor(context, tkwin, 1, fill);
- bgColor = [NSColor colorWithColorSpace: deviceRGB components: fill
- count: 4];
- CGContextSetFillColorSpace(context, deviceRGB.CGColorSpace);
- CGContextSetFillColorWithColor(context, CGCOLOR(bgColor));
- 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, CGCOLOR(borderColor));
- [borderColor getComponents: fill];
- CGContextSetRGBFillColor(context, fill[0], fill[1], fill[2], fill[3]);
+ CGFloat x = bounds.origin.x, y = bounds.origin.y;
+ CGColorRef strokeColor = IndicatorColor(state, isDark);
- CGContextBeginPath(context);
- CGContextAddPath(context, path);
- CGContextReplacePathWithStrokedPath(context);
- CGContextFillPath(context);
- CFRelease(path);
+ CGContextSetStrokeColorWithColor(context, strokeColor);
+ if (state & TTK_STATE_SELECTED) {
+ CGContextSetLineWidth(context, 1.5);
+ CGContextBeginPath(context);
+ CGPoint check[3] = {{x + 3, y + 7}, {x + 6, y + 10}, {x + 10, y + 3}};
+ CGContextAddLines(context, check, 3);
+ CGContextStrokePath(context);
+ } else if (state & TTK_STATE_ALTERNATE) {
+ CGContextSetLineWidth(context, 2.0);
+ CGContextBeginPath(context);
+ CGPoint bar[2] = {{x + 3, y + 7}, {x + 11, y + 7}};
+ CGContextAddLines(context, bar, 2);
+ CGContextStrokePath(context);
+ }
}
/*----------------------------------------------------------------------
- * SolidFillRoundedRectangle --
+ * DrawRadioIndicator --
*
- * Fill a rounded rectangle with a specified solid color.
+ * Draws the dot in the middle of a selected radio button.
*/
-static void SolidFillRoundedRectangle(
+static void DrawRadioIndicator(
CGContextRef context,
CGRect bounds,
- CGFloat radius,
- NSColor *color)
+ int state,
+ int isDark)
{
- CGPathRef path;
+ CGFloat x = bounds.origin.x, y = bounds.origin.y;
+ CGColorRef fillColor = IndicatorColor(state, isDark);
- CHECK_RADIUS(radius, bounds)
- path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
- if (!path) {
- return;
+ CGContextSetFillColorWithColor(context, fillColor);
+ if (state & TTK_STATE_SELECTED) {
+ CGContextBeginPath(context);
+ CGRect dot = {{x + 5, y + 5}, {6, 6}};
+ CGContextAddEllipseInRect(context, dot);
+ CGContextFillPath(context);
+ } else if (state & TTK_STATE_ALTERNATE) {
+ CGRect bar = {{x + 4, y + 7}, {8, 2}};
+ CGContextFillRect(context, bar);
}
- CGContextSetFillColorWithColor(context, CGCOLOR(color));
- CGContextBeginPath(context);
- CGContextAddPath(context, path);
- CGContextFillPath(context);
- CFRelease(path);
}
-/*----------------------------------------------------------------------
- * +++ DrawListHeader --
- *
- * This is a standalone drawing procedure which draws column headers for
- * a Treeview in the Aqua appearance. The HITheme headers have not
- * matched the native ones since OSX 10.8. Note that the header image is
- * ignored, but we draw arrows according to the state.
- */
-
-static void DrawListHeader(
- CGRect bounds,
+static void
+DrawHelpSymbol(
CGContextRef context,
- Tk_Window tkwin,
+ CGRect bounds,
int state)
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *strokeColor, *bgColor;
- static CGFloat borderRGBA[4] = {
- 200.0 / 255, 200.0 / 255, 200.0 / 255, 1.0
- };
- static CGFloat separatorRGBA[4] = {
- 220.0 / 255, 220.0 / 255, 220.0 / 255, 1.0
- };
- static CGFloat activeBgRGBA[4] = {
- 238.0 / 255, 238.0 / 255, 238.0 / 255, 1.0
- };
- static CGFloat inactiveBgRGBA[4] = {
- 246.0 / 255, 246.0 / 255, 246.0 / 255, 1.0
+ NSFont *font = [NSFont controlContentFontOfSize:15];
+ NSColor *foreground = state & TTK_STATE_DISABLED ?
+ [NSColor disabledControlTextColor] : [NSColor controlTextColor];
+ NSDictionary *attrs = @{
+ NSForegroundColorAttributeName : foreground,
+ NSFontAttributeName : font
};
-
- /*
- * Apple changes the background of a list header when the window is not
- * active. But Ttk does not indicate that in the state of a TreeHeader.
- * So we have to query the Apple window manager.
- */
-
- NSWindow *win = TkMacOSXGetNSWindowForDrawable(Tk_WindowId(tkwin));
- CGFloat *bgRGBA = [win isKeyWindow] ? activeBgRGBA : inactiveBgRGBA;
- CGFloat x = bounds.origin.x, y = bounds.origin.y;
- CGFloat w = bounds.size.width, h = bounds.size.height;
- CGPoint top[2] = {{x, y + 1}, {x + w, y + 1}};
- CGPoint bottom[2] = {{x, y + h}, {x + w, y + h}};
- CGPoint separator[2] = {{x + w - 1, y + 3}, {x + w - 1, y + h - 3}};
-
- bgColor = [NSColor colorWithColorSpace: deviceRGB
- components: bgRGBA
- count: 4];
+ NSAttributedString *attributedString = [[NSAttributedString alloc]
+ initWithString:@"?"
+ attributes:attrs];
+ CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString(
+ (CFAttributedStringRef)attributedString);
+ CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(0, 1));
+ CGAffineTransform t = CGAffineTransformMake(
+ 1.0, 0.0, 0.0, -1.0, 0.0, bounds.size.height);
CGContextSaveGState(context);
- CGContextSetShouldAntialias(context, false);
- CGContextSetFillColorSpace(context, deviceRGB.CGColorSpace);
- CGContextSetStrokeColorSpace(context, deviceRGB.CGColorSpace);
- CGContextBeginPath(context);
- CGContextSetFillColorWithColor(context, CGCOLOR(bgColor));
- CGContextAddRect(context, bounds);
- CGContextFillPath(context);
- strokeColor = [NSColor colorWithColorSpace: deviceRGB
- components: separatorRGBA
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(strokeColor));
- CGContextAddLines(context, separator, 2);
- CGContextStrokePath(context);
- strokeColor = [NSColor colorWithColorSpace: deviceRGB
- components: borderRGBA
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(strokeColor));
- CGContextAddLines(context, top, 2);
- CGContextStrokePath(context);
- CGContextAddLines(context, bottom, 2);
- CGContextStrokePath(context);
+ CGContextSetTextMatrix(context, t);
+ CGContextSetTextPosition(context,
+ bounds.origin.x + 6.5,
+ bounds.origin.y + bounds.size.height - 5);
+ CTLineDraw(line, context);
CGContextRestoreGState(context);
-
- if (state & TTK_TREEVIEW_STATE_SORTARROW) {
- CGRect arrowBounds = bounds;
- arrowBounds.origin.x = bounds.origin.x + bounds.size.width - 16;
- arrowBounds.size.width = 16;
- if (state & TTK_STATE_ALTERNATE) {
- DrawUpArrow(context, arrowBounds, 3, 8, BLACKRGBA);
- } else if (state & TTK_STATE_SELECTED) {
- DrawDownArrow(context, arrowBounds, 3, 8, BLACKRGBA);
- }
- }
+ CFRelease(line);
+ CFRelease(typesetter);
+ [attributedString release];
}
+
+
/*----------------------------------------------------------------------
- * +++ 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.
- *
- * Drawing the dark widgets requires NSColors that were introduced in OSX
- * 10.14, so we make some of these functions be no-ops when building on
- * systems older than 10.14.
+ * +++ Progress bars.
*/
/*----------------------------------------------------------------------
- * GradientFillRoundedRectangle --
+ * DrawProgressBar --
*
- * Fill a rounded rectangle with a specified gradient.
+ * Draws a progress bar, with parameters supplied by a HIThemeTrackDrawInfo
+ * struct. Draws a rounded rectangular track overlayed by a colored
+ * rounded rectangular indicator. An indeterminate progress bar is
+ * animated.
*/
-static void GradientFillRoundedRectangle(
+static void DrawProgressBar(
CGContextRef context,
CGRect bounds,
- CGFloat radius,
- CGFloat *colors,
- int numColors)
+ HIThemeTrackDrawInfo info,
+ int state,
+ Tk_Window tkwin)
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- CGPathRef path;
- CHECK_RADIUS(radius, bounds)
+ CGRect colorBounds;
+ CGFloat rgba[4];
+ CGColorRef trackColor, highlightColor, fillColor;
+ NSColor *accent;
+ CGFloat ratio = (CGFloat) info.value / (CGFloat) (info.max - info.min);
- 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;
+ GetBackgroundColorRGBA(context, tkwin, 0, NO, rgba);
/*
- * To match the appearance of Apple's buttons we need to increase the
- * height by 1 pixel.
+ * Compute the bounds for the track and indicator. The track is 6 pixels
+ * wide in the center of the widget bounds.
*/
- bounds.size.height += 1;
-
- CGContextClipToRect(context, bounds);
- FillButtonBackground(context, bounds, 5);
+ if (info.attributes & kThemeTrackHorizontal) {
+ bounds = CGRectInset(bounds, 1, bounds.size.height / 2 - 3);
+ colorBounds = bounds;
+ if (info.kind == kThemeIndeterminateBar) {
+ CGFloat width = 0.25*bounds.size.width;
+ CGFloat travel = 0.75*bounds.size.width;
+ CGFloat center = bounds.origin.x + (width / 2) + ratio*travel;
+ colorBounds.origin.x = center - width / 2;
+ colorBounds.size.width = width;
+ } else {
+ colorBounds.size.width = ratio*bounds.size.width;
+ }
+ if (colorBounds.size.width > 0 && colorBounds.size.width < 6) {
+ colorBounds.size.width = 6;
+ }
+ } else {
+ bounds = CGRectInset(bounds, bounds.size.width / 2 - 3, 1);
+ colorBounds = bounds;
+ if (info.kind == kThemeIndeterminateBar) {
+ CGFloat height = 0.25*bounds.size.height;
+ CGFloat travel = 0.75*bounds.size.height;
+ CGFloat center = bounds.origin.y + (height / 2) + ratio*travel;
+ colorBounds.origin.y = center - height / 2;
+ colorBounds.size.height = height;
+ } else {
+ colorBounds.size.height = ratio*(bounds.size.height);
+ }
+ if (colorBounds.size.height > 0 && colorBounds.size.height < 6) {
+ colorBounds.size.height = 6;
+ }
+ colorBounds.origin.y += bounds.size.height - colorBounds.size.height;
+ }
/*
- * Fill the button face with the appropriate color.
+ * Compute the colors for the track and indicator.
*/
- bounds = CGRectInset(bounds, 1, 1);
- if (kind == kThemePushButton && (state & TTK_STATE_PRESSED)) {
- if ([NSApp macOSVersion] < 120000) {
- GradientFillRoundedRectangle(context, bounds, 4,
- pressedPushButtonGradient, 2);
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkPressedButtonFace
- count: 4];
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ if (TkMacOSXInDarkMode(tkwin)) {
+ for(int i=0; i < 3; i++) {
+ rgba[i] += 30.0 / 255.0;
}
- } else if (kind == kThemePushButton &&
- (state & TTK_STATE_ALTERNATE) &&
- !(state & TTK_STATE_BACKGROUND)) {
- GradientFillRoundedRectangle(context, bounds, 4,
- darkSelectedGradient, 2);
+ trackColor = CGColorFromRGBA(rgba);
+ for(int i=0; i < 3; i++) {
+ rgba[i] -= 5.0 / 255.0;
+ }
+ highlightColor = CGColorFromRGBA(rgba);
+ FillRoundedRectangle(context, bounds, 3, trackColor);
} else {
- if (state & TTK_STATE_DISABLED) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkDisabledButtonFace
- count: 4];
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkButtonFace
- count: 4];
+ for(int i=0; i < 3; i++) {
+ rgba[i] -= 14.0 / 255.0;
+ }
+ trackColor = CGColorFromRGBA(rgba);
+ for(int i=0; i < 3; i++) {
+ rgba[i] += 3.0 / 255.0;
}
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ highlightColor = CGColorFromRGBA(rgba);
+ bounds.size.height -= 1;
+ bounds = CGRectInset(bounds, 0, -1);
+ }
+ if (state & TTK_STATE_BACKGROUND) {
+ accent = [NSColor colorWithRed:0.72 green:0.72 blue:0.72 alpha:0.72];
+ } else {
+ accent = controlAccentColor();
}
/*
- * If this is a popup, draw the arrow button.
+ * Draw the track, with highlighting around the edge.
*/
- if ((kind == kThemePopupButton) | (kind == kThemeComboBox)) {
- CGRect arrowBounds = bounds;
- arrowBounds.size.width = 16;
- arrowBounds.origin.x += bounds.size.width - 16;
+ FillRoundedRectangle(context, bounds, 3, trackColor);
+ bounds = CGRectInset(bounds, 0, 0.5);
+ FillRoundedRectangle(context, bounds, 2.5, highlightColor);
+ bounds = CGRectInset(bounds, 0.5, 0.5);
+ FillRoundedRectangle(context, bounds, 2, trackColor);
+ bounds = CGRectInset(bounds, -0.5, -1);
- /*
- * If the toplevel is front, paint the button blue.
- */
+ /*
+ * Draw the indicator. Make it slightly transparent around the
+ * edge so the highlightng shows through.
+ */
- if (!(state & TTK_STATE_BACKGROUND) &&
- !(state & TTK_STATE_DISABLED)) {
- GradientFillRoundedRectangle(context, arrowBounds, 4,
- darkSelectedGradient, 2);
- }
- if (kind == kThemePopupButton) {
- DrawUpDownArrows(context, arrowBounds, 3, 7, WHITERGBA);
- } else {
- DrawDownArrow(context, arrowBounds, 4, 8, WHITERGBA);
- }
+ if (info.kind == kThemeIndeterminateBar &&
+ (state & TTK_STATE_SELECTED) == 0) {
+ return;
}
- HighlightButtonBorder(context, bounds);
+ fillColor = CGCOLOR([accent colorWithAlphaComponent:0.9]);
+ FillRoundedRectangle(context, colorBounds, 3, fillColor);
+ colorBounds = CGRectInset(colorBounds, 1, 1);
+ fillColor = CGCOLOR([accent colorWithAlphaComponent:1.0]);
+ FillRoundedRectangle(context, colorBounds, 2.5, fillColor);
}
+
+/*----------------------------------------------------------------------
+ * +++ Sliders.
+ */
/*----------------------------------------------------------------------
- * +++ DrawDarkIncDecButton --
+ * DrawSlider --
+ *
+ * Draws a slider track and round thumb for a Ttk scale widget. The accent
+ * color is used on the left or top part of the track, so the fraction of
+ * the track which is colored is equal to (value - from) / (to - from).
*
- * This is a standalone drawing procedure which draws an IncDecButton
- * (as used in a Spinbox) in the Dark Mode style.
*/
-static void DrawDarkIncDecButton(
+static void DrawSlider(
+ CGContextRef context,
CGRect bounds,
- ThemeDrawState drawState,
- Ttk_State state,
- CGContextRef context)
+ HIThemeTrackDrawInfo info,
+ int state,
+ Tk_Window tkwin)
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *faceColor;
-
- bounds = CGRectInset(bounds, 0, -1);
- CGContextClipToRect(context, bounds);
- FillButtonBackground(context, bounds, 6);
+ CGColorRef trackColor;
+ CGRect clipBounds, trackBounds, thumbBounds;
+ CGPoint thumbPoint;
+ CGFloat position;
+ CGColorRef accentColor;
+ Bool fromIsSmaller = info.reserved;
+ double from = info.min, to = fabs((double) info.max), value = info.value;
/*
- * Fill the button face with the appropriate color.
+ * info.min, info.max and info.value are integers. When this is called
+ * we will have arranged that min = 0 and max is a large positive integer.
*/
- bounds = CGRectInset(bounds, 1, 1);
- if (state & TTK_STATE_DISABLED) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkDisabledButtonFace
- count: 4];
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkButtonFace
- count: 4];
- }
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ double fraction = (from < to) ? (value - from) / (to - from) : 0.5;
+ int isDark = TkMacOSXInDarkMode(tkwin);
- /*
- * If pressed, paint the appropriate half blue.
- */
-
- if (state & TTK_STATE_PRESSED) {
- CGRect clip = bounds;
- clip.size.height /= 2;
- CGContextSaveGState(context);
- if (drawState == kThemeStatePressedDown) {
- clip.origin.y += clip.size.height;
+ if (info.attributes & kThemeTrackHorizontal) {
+ trackBounds = CGRectInset(bounds, 0, bounds.size.height / 2 - 3);
+ trackBounds.size.height = 3;
+ position = 8 + fraction * (trackBounds.size.width - 16);
+ clipBounds = trackBounds;
+ if (fromIsSmaller) {
+ clipBounds.size.width = position;
+ } else {
+ clipBounds.origin.x += position;
+ clipBounds.size.width -= position;
}
- CGContextClipToRect(context, clip);
- GradientFillRoundedRectangle(context, bounds, 5,
- darkSelectedGradient, 2);
- CGContextRestoreGState(context);
+ thumbPoint = CGPointMake(trackBounds.origin.x + position,
+ trackBounds.origin.y + 1);
+ } else {
+ trackBounds = CGRectInset(bounds, bounds.size.width / 2 - 3, 0);
+ trackBounds.size.width = 3;
+ position = 8 + fraction * (trackBounds.size.height - 16);
+ clipBounds = trackBounds;
+ if (fromIsSmaller) {
+ clipBounds.size.height = position;
+ } else {
+ clipBounds.origin.y += position;
+ clipBounds.size.height -= position;
+ }
+ thumbPoint = CGPointMake(trackBounds.origin.x + 1,
+ trackBounds.origin.y + position);
+ }
+ trackColor = isDark ? CGColorFromGray(darkTrack):
+ CGColorFromGray(lightTrack);
+ thumbBounds = CGRectMake(thumbPoint.x - 8, thumbPoint.y - 8, 17, 17);
+ CGContextSaveGState(context);
+ FillRoundedRectangle(context, trackBounds, 1.5, trackColor);
+ CGContextClipToRect(context, clipBounds);
+ if (state & (TTK_STATE_BACKGROUND | TTK_STATE_DISABLED)) {
+ accentColor = isDark ? CGColorFromGray(darkInactiveTrack) :
+ CGColorFromGray(lightInactiveTrack);
+ } else {
+ accentColor = CGCOLOR(controlAccentColor());
}
- DrawUpDownArrows(context, bounds, 3, 5, WHITERGBA);
- HighlightButtonBorder(context, bounds);
+ FillRoundedRectangle(context, trackBounds, 1.5, accentColor);
+ CGContextRestoreGState(context);
+ DrawGrayButton(context, thumbBounds, &sliderDesign, state, tkwin);
}
/*----------------------------------------------------------------------
- * +++ DrawDarkBevelButton --
+ * +++ Drawing procedures for native widgets.
+ *
+ * The HIToolbox does not support Dark Mode, and apparently never will.
+ * It also draws some widgets in discontinued older styles even when used
+ * on new OS releases. So to make widgets look "native" we have to provide
+ * analogues of the HIToolbox drawing functions to be used on newer systems.
+ * We continue to use NIToolbox for older versions of the OS.
+ *
+ * Drawing the dark widgets requires NSColors that were introduced in OSX
+ * 10.14, so we make some of these functions be no-ops when building on
+ * systems older than 10.14.
+ */
+
+/*----------------------------------------------------------------------
+ * DrawButton --
*
- * This is a standalone drawing procedure which draws RoundedBevelButtons
- * in the Dark Mode style.
+ * This is a standalone drawing procedure which draws most types of macOS
+ * buttons for newer OS releases. The button style is specified in the
+ * "kind" field of a HIThemeButtonDrawInfo struct, although some of the
+ * identifiers are not recognized by HIToolbox.
*/
-static void DrawDarkBevelButton(
+static void DrawButton(
CGRect bounds,
+ HIThemeButtonDrawInfo info,
Ttk_State state,
- CGContextRef context)
+ CGContextRef context,
+ Tk_Window tkwin)
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *faceColor;
+ ThemeButtonKind kind = info.kind;
+ ThemeDrawState drawState = info.state;
+ CGRect arrowBounds = bounds = CGRectInset(bounds, 1, 1);
+ int hasIndicator, isDark = TkMacOSXInDarkMode(tkwin);
- CGContextClipToRect(context, bounds);
- FillButtonBackground(context, bounds, 5);
+ switch (kind) {
+ case TkRoundedRectButton:
+ DrawGrayButton(context, bounds, &roundedrectDesign, state, tkwin);
+ break;
+ case TkInlineButton:
+ DrawGrayButton(context, bounds, &inlineDesign, state, tkwin);
+ break;
+ case TkRecessedButton:
+ DrawGrayButton(context, bounds, &recessedDesign, state, tkwin);
+ break;
+ case TkSidebarButton:
+ DrawGrayButton(context, bounds, &sidebarDesign, state, tkwin);
+ break;
+ case kThemeRoundedBevelButton:
+ DrawGrayButton(context, bounds, &bevelDesign, state, tkwin);
+ break;
+ case kThemePushButton:
- /*
- * Fill the button face with the appropriate color.
- */
+ /*
+ * The TTK_STATE_ALTERNATE bit means -default active. Apple only
+ * indicates the default state (which means that the key equivalent is
+ * "\n") for Push Buttons.
+ */
- bounds = CGRectInset(bounds, 1, 1);
- if (state & TTK_STATE_PRESSED) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkPressedBevelFace
- count: 4];
- } else if ((state & TTK_STATE_DISABLED) ||
- (state & TTK_STATE_ALTERNATE)) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkDisabledButtonFace
- count: 4];
- } else if (state & TTK_STATE_SELECTED) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkSelectedBevelFace
- count: 4];
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkButtonFace
- count: 4];
+ if ((state & TTK_STATE_PRESSED || state & TTK_STATE_ALTERNATE) &&
+ !(state & TTK_STATE_BACKGROUND)) {
+ DrawAccentedButton(context, bounds, &pushbuttonDesign, state, isDark);
+ } else {
+ DrawGrayButton(context, bounds, &pushbuttonDesign, state, tkwin);
+ }
+ break;
+ case kThemeRoundButtonHelp:
+ DrawGrayButton(context, bounds, &helpDesign, state, tkwin);
+ DrawHelpSymbol(context, bounds, state);
+ break;
+ case kThemePopupButton:
+ drawState = 0;
+ DrawGrayButton(context, bounds, &popupDesign, state, tkwin);
+ arrowBounds.size.width = 17;
+ arrowBounds.origin.x += bounds.size.width - 17;
+ if (!(state & TTK_STATE_BACKGROUND) &&
+ !(state & TTK_STATE_DISABLED)) {
+ CGRect popupBounds = arrowBounds;
+
+ /*
+ * Allow room for nonexistent focus ring.
+ */
+
+ popupBounds.size.width += 4;
+ popupBounds.origin.y -= 4;
+ popupBounds.size.height += 8;
+ DrawAccentedSegment(context, popupBounds, &popupDesign, state, tkwin);
+ drawState = BOTH_ARROWS;
+ }
+ arrowBounds.origin.x += 2;
+ DrawUpDownArrows(context, arrowBounds, 3, 7, 2, state, drawState);
+ break;
+ case kThemeComboBox:
+ if (state & TTK_STATE_DISABLED) {
+ // Need to add the disabled case to entryDesign.
+ DrawEntry(context, bounds, &entryDesign, state, tkwin);
+ } else {
+ DrawEntry(context, bounds, &entryDesign, state, tkwin);
+ }
+ arrowBounds.size.width = 17;
+ if (state & TTK_STATE_BACKGROUND) {
+ arrowBounds.origin.x += bounds.size.width - 20;
+ arrowBounds.size.width += 4;
+ arrowBounds.origin.y -= 1;
+ } else {
+ arrowBounds.origin.y -= 1;
+ arrowBounds.origin.x += bounds.size.width - 20;
+ arrowBounds.size.width += 4;
+ arrowBounds.size.height += 2;
+ }
+ DrawAccentedSegment(context, arrowBounds, &comboDesign, state, tkwin);
+ if (!(state & TTK_STATE_BACKGROUND)) {
+ state |= TTK_STATE_IS_ACCENTED;
+ }
+ DrawDownArrow(context, arrowBounds, 6, 6, state);
+ break;
+ case kThemeCheckBox:
+ bounds = CGRectOffset(CGRectMake(0, bounds.size.height / 2 - 8, 16, 16),
+ bounds.origin.x, bounds.origin.y);
+ bounds = CGRectInset(bounds, 1, 1);
+ hasIndicator = state & TTK_STATE_SELECTED || state & TTK_STATE_ALTERNATE;
+ if (hasIndicator &&
+ !(state & TTK_STATE_BACKGROUND) &&
+ !(state & TTK_STATE_DISABLED)) {
+ DrawAccentedButton(context, bounds, &checkDesign, 0, isDark);
+ } else {
+ DrawGrayButton(context, bounds, &checkDesign, state, tkwin);
+ }
+ if (hasIndicator) {
+ DrawCheckIndicator(context, bounds, state, isDark);
+ }
+ break;
+ case kThemeRadioButton:
+ bounds = CGRectOffset(CGRectMake(0, bounds.size.height / 2 - 9, 18, 18),
+ bounds.origin.x, bounds.origin.y);
+ bounds = CGRectInset(bounds, 1, 1);
+ hasIndicator = state & TTK_STATE_SELECTED || state & TTK_STATE_ALTERNATE;
+ if (hasIndicator &&
+ !(state & TTK_STATE_BACKGROUND) &&
+ !(state & TTK_STATE_DISABLED)) {
+ DrawAccentedButton(context, bounds, &radioDesign, 0, isDark);
+ } else {
+ DrawGrayButton(context, bounds, &radioDesign, state, tkwin);
+ }
+ if (hasIndicator) {
+ DrawRadioIndicator(context, bounds, state, isDark);
+ }
+ break;
+ case kThemeArrowButton:
+ DrawGrayButton(context, bounds, &pushbuttonDesign, state, tkwin);
+ arrowBounds.origin.x = bounds.origin.x + bounds.size.width - 17;
+ arrowBounds.size.width = 16;
+ arrowBounds.origin.y -= 1;
+ if (state & TTK_STATE_SELECTED) {
+ DrawUpArrow(context, arrowBounds, 5, 6, state);
+ } else {
+ DrawDownArrow(context, arrowBounds, 5, 6, state);
+ }
+ break;
+ case kThemeIncDecButton:
+ DrawGrayButton(context, bounds, &incdecDesign, state, tkwin);
+ if (state & TTK_STATE_PRESSED) {
+ CGRect clip;
+ if (drawState == kThemeStatePressedDown) {
+ clip = bounds;
+ clip.size.height /= 2;
+ clip.origin.y += clip.size.height;
+ bounds.size.height += 1;
+ clip.size.height += 1;
+ } else {
+ clip = bounds;
+ clip.size.height /= 2;
+ }
+ CGContextSaveGState(context);
+ CGContextClipToRect(context, clip);
+ DrawAccentedButton(context, bounds, &incdecDesign, 0, isDark);
+ CGContextRestoreGState(context);
+ }
+ {
+ CGFloat inset = (bounds.size.width - 5) / 2;
+ DrawUpDownArrows(context, bounds, inset, 5, 3, state, drawState);
+ }
+ break;
+ default:
+ break;
}
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
- HighlightButtonBorder(context, bounds);
}
-
+
/*----------------------------------------------------------------------
- * +++ DrawDarkCheckBox --
+ * DrawGroupBox --
*
- * This is a standalone drawing procedure which draws Checkboxes in the
- * Dark Mode style.
+ * This is a standalone drawing procedure which draws the contrasting rounded
+ * rectangular box for LabelFrames and Notebook panes used in more recent
+ * versions of macOS. Normally the contrast is set to one, since the nesting
+ * level of the Group Box is higher by 1 compared to its container. But we
+ * allow higher contrast for special cases, notably notebook tabs in macOS 11.
+ * The save parameter is passed to GetBackgroundColor and should probably be
+ * NO in such special cases.
*/
-static void DrawDarkCheckBox(
+static void DrawGroupBox(
CGRect bounds,
- Ttk_State state,
- CGContextRef context)
+ CGContextRef context,
+ Tk_Window tkwin,
+ int contrast,
+ Bool save)
{
- CGRect checkbounds = {{0, bounds.size.height / 2 - 8}, {16, 16}};
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *stroke;
- CGFloat x, y;
+ CHECK_RADIUS(5, bounds)
- bounds = CGRectOffset(checkbounds, bounds.origin.x, bounds.origin.y);
- x = bounds.origin.x;
- y = bounds.origin.y;
+ CGPathRef path;
+ CGColorRef backgroundColor, borderColor;
+ backgroundColor = GetBackgroundCGColor(context, tkwin, contrast, save);
+ borderColor = CGColorFromGray(boxBorder);
+ CGContextSetFillColorWithColor(context, backgroundColor);
+ path = CGPathCreateWithRoundedRect(bounds, 5, 5, NULL);
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))) {
- GradientFillRoundedRectangle(context, bounds, 3,
- darkSelectedGradient, 2);
- } else {
- GradientFillRoundedRectangle(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, CGCOLOR(stroke));
- }
- 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);
- }
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextFillPath(context);
+ CGContextSetFillColorWithColor(context, borderColor);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextReplacePathWithStrokedPath(context);
+ CGContextFillPath(context);
+ CFRelease(path);
}
-
+
/*----------------------------------------------------------------------
- * +++ DrawDarkRadioButton --
+ * DrawListHeader --
*
- * This is a standalone drawing procedure which draws RadioButtons
- * in the Dark Mode style.
+ * This is a standalone drawing procedure which draws column headers for a
+ * Treeview in the Aqua appearance. (The HIToolbox headers have not matched the
+ * native ones since OSX 10.8) Note that the header image is ignored, but we
+ * draw arrows according to the state.
*/
-static void DrawDarkRadioButton(
+static void DrawListHeader(
CGRect bounds,
- Ttk_State state,
- CGContextRef context)
+ CGContextRef context,
+ Tk_Window tkwin,
+ int state)
{
- CGRect checkbounds = {{0, bounds.size.height / 2 - 9}, {18, 18}};
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *fill;
- CGFloat x, y;
+ int isDark = TkMacOSXInDarkMode(tkwin);
+ CGFloat x = bounds.origin.x, y = bounds.origin.y;
+ CGFloat w = bounds.size.width, h = bounds.size.height;
+ CGPoint top[2] = {{x, y + 1}, {x + w, y + 1}};
+ CGPoint bottom[2] = {{x, y + h}, {x + w, y + h}};
+ CGPoint separator[2] = {{x + w - 1, y + 3}, {x + w - 1, y + h - 3}};
+ CGColorRef strokeColor, backgroundColor;
- bounds = CGRectOffset(checkbounds, bounds.origin.x, bounds.origin.y);
- x = bounds.origin.x;
- y = bounds.origin.y;
+ /*
+ * Apple changes the background color of a list header when the window is
+ * not active. But Ttk does not indicate that in the state of a
+ * TreeHeader. So we have to query the Apple window manager.
+ */
- 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))) {
- GradientFillRoundedRectangle(context, bounds, 8,
- darkSelectedGradient, 2);
- } else {
- GradientFillRoundedRectangle(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, CGCOLOR(fill));
+ NSWindow *win = TkMacOSXGetNSWindowForDrawable(Tk_WindowId(tkwin));
+ if (!isDark) {
+ GrayColor bgGray = [win isKeyWindow] ?
+ listheaderActiveBG : listheaderInactiveBG;
+ backgroundColor = CGColorFromGray(bgGray);
}
- if (state & TTK_STATE_SELECTED) {
+
+ CGContextSaveGState(context);
+ CGContextSetShouldAntialias(context, false);
+ if (!isDark) {
CGContextBeginPath(context);
- CGRect dot = {{x + 6, y + 6}, {6, 6}};
- CGContextAddEllipseInRect(context, dot);
+ CGContextSetFillColorWithColor(context, backgroundColor);
+ CGContextAddRect(context, bounds);
CGContextFillPath(context);
- } else if (state & TTK_STATE_ALTERNATE) {
- CGRect bar = {{x + 5, y + 8}, {8, 2}};
- CGContextFillRect(context, bar);
}
-}
+ strokeColor = isDark ?
+ CGColorFromGray(darkListheaderBorder) :
+ CGColorFromGray(listheaderSeparator);
+ CGContextSetStrokeColorWithColor(context, strokeColor);
+ CGContextAddLines(context, separator, 2);
+ CGContextStrokePath(context);
+ strokeColor = isDark ?
+ CGColorFromGray(darkListheaderBorder) :
+ CGColorFromGray(lightListheaderBorder);
+ CGContextSetStrokeColorWithColor(context, strokeColor);
+ CGContextAddLines(context, top, 2);
+ CGContextStrokePath(context);
+ CGContextAddLines(context, bottom, 2);
+ CGContextStrokePath(context);
+ CGContextRestoreGState(context);
+ if (state & TTK_TREEVIEW_STATE_SORTARROW) {
+ CGRect arrowBounds = bounds;
+ arrowBounds.origin.x = bounds.origin.x + bounds.size.width - 16;
+ arrowBounds.size.width = 16;
+ if (state & TTK_STATE_ALTERNATE) {
+ DrawUpArrow(context, arrowBounds, 3, 8, state);
+ } else if (state & TTK_STATE_SELECTED) {
+ DrawDownArrow(context, arrowBounds, 3, 8, state);
+ }
+ }
+}
+
/*----------------------------------------------------------------------
- * +++ DrawDarkTab --
+ * DrawTab --
*
- * This is a standalone drawing procedure which draws Tabbed Pane
- * Tabs in the Dark Mode style.
+ * This is a standalone drawing procedure which draws Tabbed Pane Tabs for the
+ * notebook widget.
*/
-static void DrawDarkTab(
+static void
+DrawTab(
CGRect bounds,
Ttk_State state,
- CGContextRef context)
+ CGContextRef context,
+ Tk_Window tkwin)
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *faceColor, *stroke;
CGRect originalBounds = bounds;
+ CGColorRef strokeColor;
int OSVersion = [NSApp macOSVersion];
- CGContextSetLineWidth(context, 1.0);
- CGContextClipToRect(context, bounds);
-
/*
* Extend the bounds to one or both sides so the rounded part will be
- * clipped off.
+ * clipped off if the right of the left tab, the left of the right tab,
+ * and both sides of the middle tabs.
*/
+ CGContextClipToRect(context, bounds);
if (OSVersion < 110000 || !(state & TTK_STATE_SELECTED)) {
- if (!(state & TTK_STATE_FIRST_TAB)) {
+ if (!(state & TTK_STATE_FIRST)) {
bounds.origin.x -= 10;
bounds.size.width += 10;
}
- if (!(state & TTK_STATE_LAST_TAB)) {
+ if (!(state & TTK_STATE_LAST)) {
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.
+ * color if the tab is not selected, otherwise use the accent color with
+ * highlights
*/
- bounds = CGRectInset(bounds, 1, 1);
if (!(state & TTK_STATE_SELECTED)) {
- if (state & TTK_STATE_DISABLED) {
- if (OSVersion < 110000) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkDisabledButtonFace
- count: 4];
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkTab
- count: 4];
- }
- } else {
- if (OSVersion < 110000) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkButtonFace
- count: 4];
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkTab
- count: 4];
- }
- }
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ DrawGrayButton(context, bounds, &tabDesign, state, tkwin);
/*
* Draw a separator line on the left side of the tab if it
* not first.
*/
- if (!(state & TTK_STATE_FIRST_TAB)) {
+ if (!(state & TTK_STATE_FIRST)) {
CGContextSaveGState(context);
- CGContextSetShouldAntialias(context, false);
- stroke = [NSColor colorWithColorSpace: deviceRGB
- components: darkTabSeparator
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(stroke));
+ strokeColor = CGColorFromGray(darkTabSeparator);
+ CGContextSetStrokeColorWithColor(context, strokeColor);
CGContextBeginPath(context);
CGContextMoveToPoint(context, originalBounds.origin.x,
originalBounds.origin.y + 1);
@@ -1091,295 +1530,113 @@ static void DrawDarkTab(
* (The selected tab is always drawn last.)
*/
- if ((state & TTK_STATE_FIRST_TAB) && !(state & TTK_STATE_LAST_TAB)) {
+ if ((state & TTK_STATE_FIRST) && !(state & TTK_STATE_LAST)) {
bounds.size.width += 1;
}
if (!(state & TTK_STATE_BACKGROUND)) {
- if (OSVersion < 110000) {
- GradientFillRoundedRectangle(context, bounds, 4,
- darkSelectedGradient, 2);
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkSelectedTab
- count: 4];
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
- }
+ DrawAccentedButton(context, bounds, &tabDesign, 0, 0);
} else {
- if (OSVersion < 110000) {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkInactiveSelectedTab
- count: 4];
- } else {
- faceColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkSelectedTab
- count: 4];
- }
- SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ DrawGrayButton(context, bounds, &tabDesign, state, tkwin);
}
- 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,
- TCL_UNUSED(Tk_Window))
-{
- 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, CGCOLOR(fillColor));
- CGContextFillRect(context, bounds);
-}
-
-/*----------------------------------------------------------------------
- * +++ DrawDarkFocusRing --
- *
- * This is a standalone drawing procedure which draws a focus ring around
- * an Entry widget in Dark Mode.
- */
-
-static void DrawDarkFocusRing(
- CGRect bounds,
- CGContextRef context)
-{
- CGRect insetBounds = CGRectInset(bounds, -3, -3);
- CHECK_RADIUS(4, insetBounds)
-
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *strokeColor;
- NSColor *fillColor = [NSColor colorWithColorSpace:deviceRGB
- components:darkFocusRing
- count:4];
- CGFloat x = bounds.origin.x, y = bounds.origin.y;
- CGFloat w = bounds.size.width, h = bounds.size.height;
- CGPoint topPart[4] = {
- {x, y + h}, {x, y + 1}, {x + w - 1, y + 1}, {x + w - 1, y + h}
- };
- CGPoint bottom[2] = {{x, y + h}, {x + w, y + h}};
-
- CGContextSaveGState(context);
- CGContextSetShouldAntialias(context, false);
- CGContextBeginPath(context);
- strokeColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkFocusRingTop
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(strokeColor));
- CGContextAddLines(context, topPart, 4);
- CGContextStrokePath(context);
- strokeColor = [NSColor colorWithColorSpace: deviceRGB
- components: darkFocusRingBottom
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(strokeColor));
- CGContextAddLines(context, bottom, 2);
- CGContextStrokePath(context);
- CGContextSetShouldAntialias(context, true);
- CGContextSetFillColorWithColor(context, CGCOLOR(fillColor));
- CGPathRef path = CGPathCreateWithRoundedRect(insetBounds, 4, 4, NULL);
- CGContextBeginPath(context);
- CGContextAddPath(context, path);
- CGPathRelease(path);
- CGContextAddRect(context, bounds);
- CGContextEOFillPath(context);
- CGContextRestoreGState(context);
-}
-/*----------------------------------------------------------------------
- * +++ DrawDarkFrame --
- *
- * This is a standalone drawing procedure which draws various
- * types of borders in Dark Mode.
- */
-
-static void DrawDarkFrame(
+static void
+DrawTab11(
CGRect bounds,
+ Ttk_State state,
CGContextRef context,
- HIThemeFrameKind kind)
+ Tk_Window tkwin)
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *stroke;
- CGContextSetStrokeColorSpace(context, deviceRGB.CGColorSpace);
- CGFloat x = bounds.origin.x, y = bounds.origin.y;
- CGFloat w = bounds.size.width, h = bounds.size.height;
- CGPoint topPart[4] = {
- {x, y + h - 1}, {x, y + 1}, {x + w, y + 1}, {x + w, y + h - 1}
- };
- CGPoint bottom[2] = {{x, y + h}, {x + w, y + h}};
- CGPoint accent[2] = {{x, y + 1}, {x + w, y + 1}};
+ if (state & TTK_STATE_SELECTED) {
+ DrawGrayButton(context, bounds, &pushbuttonDesign, state, tkwin);
+ } else {
+ CGRect clipRect = bounds;
+ /*
+ * Draw a segment of a Group Box as a background for non-selected tabs.
+ * Clip the Group Box so that the segments fit together to form a long
+ * rounded rectangle behind the entire tab bar.
+ */
- switch (kind) {
- case kHIThemeFrameTextFieldSquare:
+ if (!(state & TTK_STATE_FIRST)) {
+ clipRect.origin.x -= 5;
+ bounds.origin.x -= 5;
+ bounds.size.width += 5;
+ }
+ if (!(state & TTK_STATE_LAST)) {
+ clipRect.size.width += 5;
+ bounds.size.width += 5;
+ }
CGContextSaveGState(context);
- CGContextSetShouldAntialias(context, false);
- CGContextBeginPath(context);
- stroke = [NSColor colorWithColorSpace: deviceRGB
- components: darkFrameTop
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(stroke));
- CGContextAddLines(context, topPart, 4);
- CGContextStrokePath(context);
- stroke = [NSColor colorWithColorSpace: deviceRGB
- components: darkFrameBottom
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(stroke));
- CGContextAddLines(context, bottom, 2);
- CGContextStrokePath(context);
- stroke = [NSColor colorWithColorSpace: deviceRGB
- components: darkFrameAccent
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(stroke));
- CGContextAddLines(context, accent, 2);
- CGContextStrokePath(context);
+ CGContextClipToRect(context, clipRect);
+ DrawGroupBox(bounds, context, tkwin, 3, NO);
CGContextRestoreGState(context);
- break;
- default:
- break;
}
}
/*----------------------------------------------------------------------
- * +++ DrawListHeader --
+ * DrawDarkSeparator --
*
- * This is a standalone drawing procedure which draws column
- * headers for a Treeview in the Dark Mode.
+ * This is a standalone drawing procedure which draws a separator widget
+ * in Dark Mode. HIToolbox is used in light mode.
*/
-static void DrawDarkListHeader(
+static void DrawDarkSeparator(
CGRect bounds,
CGContextRef context,
- TCL_UNUSED(Tk_Window),
- int state)
+ TCL_UNUSED(Tk_Window))
{
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *stroke;
-
- CGContextSetStrokeColorSpace(context, deviceRGB.CGColorSpace);
- CGFloat x = bounds.origin.x, y = bounds.origin.y;
- CGFloat w = bounds.size.width, h = bounds.size.height;
-
- CGPoint top[2] = {{x, y + 1}, {x + w, y + 1}};
- CGPoint bottom[2] = {{x, y + h}, {x + w, y + h}};
- CGPoint separator[2] = {{x + w - 1, y + 3}, {x + w - 1, y + h - 3}};
- CGContextSaveGState(context);
- CGContextSetShouldAntialias(context, false);
- stroke = [NSColor colorWithColorSpace: deviceRGB
- components: darkFrameBottom
- count: 4];
- CGContextSetStrokeColorWithColor(context, CGCOLOR(stroke));
- CGContextBeginPath(context);
- CGContextAddLines(context, top, 2);
- CGContextStrokePath(context);
- CGContextAddLines(context, bottom, 2);
- CGContextStrokePath(context);
- CGContextAddLines(context, separator, 2);
- CGContextStrokePath(context);
- CGContextRestoreGState(context);
-
- if (state & TTK_TREEVIEW_STATE_SORTARROW) {
- CGRect arrowBounds = bounds;
+ CGColorRef sepColor = CGColorFromGray(darkSeparator);
- arrowBounds.origin.x = bounds.origin.x + bounds.size.width - 16;
- arrowBounds.size.width = 16;
- if (state & TTK_STATE_ALTERNATE) {
- DrawUpArrow(context, arrowBounds, 3, 8, WHITERGBA);
- } else if (state & TTK_STATE_SELECTED) {
- DrawDownArrow(context, arrowBounds, 3, 8, WHITERGBA);
- }
- }
+ CGContextSetFillColorWithColor(context, sepColor);
+ CGContextFillRect(context, bounds);
}
/*----------------------------------------------------------------------
- * +++ Button element: Used for elements drawn with DrawThemeButton.
- */
-
-/*
- * When Ttk draws the various types of buttons, a pointer to one of these
- * is passed as the clientData.
- */
-
-typedef struct {
- ThemeButtonKind kind;
- ThemeMetric heightMetric;
-} ThemeButtonParams;
-static ThemeButtonParams
- PushButtonParams = {kThemePushButton, kThemeMetricPushButtonHeight},
- CheckBoxParams = {kThemeCheckBox, kThemeMetricCheckBoxHeight},
- RadioButtonParams = {kThemeRadioButton, kThemeMetricRadioButtonHeight},
- BevelButtonParams = {kThemeRoundedBevelButton, NoThemeMetric},
- PopupButtonParams = {kThemePopupButton, kThemeMetricPopupButtonHeight},
- DisclosureParams = {
- kThemeDisclosureButton, kThemeMetricDisclosureTriangleHeight
-},
- ListHeaderParams =
-{kThemeListHeaderButton, kThemeMetricListHeaderHeight};
-static Ttk_StateTable ButtonValueTable[] = {
- {kThemeButtonOff, TTK_STATE_ALTERNATE | TTK_STATE_BACKGROUND, 0},
- {kThemeButtonMixed, TTK_STATE_ALTERNATE, 0},
- {kThemeButtonOn, TTK_STATE_SELECTED, 0},
- {kThemeButtonOff, 0, 0}
-
- /*
- * Others: kThemeDisclosureRight, kThemeDisclosureDown,
- * kThemeDisclosureLeft
- */
-
-};
-static Ttk_StateTable ButtonAdornmentTable[] = {
- {kThemeAdornmentNone, TTK_STATE_ALTERNATE | TTK_STATE_BACKGROUND, 0},
- {kThemeAdornmentDefault | kThemeAdornmentFocus,
- TTK_STATE_ALTERNATE | TTK_STATE_FOCUS, 0},
- {kThemeAdornmentFocus, TTK_STATE_FOCUS, 0},
- {kThemeAdornmentDefault, TTK_STATE_ALTERNATE, 0},
- {kThemeAdornmentNone, 0, 0}
-};
-
-/*----------------------------------------------------------------------
- * +++ computeButtonDrawInfo --
+ * +++ DrawGradientButton --
*
- * Fill in an appearance manager HIThemeButtonDrawInfo record.
+ * This is a standalone drawing procedure which draws a
+ * a Gradient Button.
*/
-static inline HIThemeButtonDrawInfo computeButtonDrawInfo(
- ThemeButtonParams *params,
- Ttk_State state,
- TCL_UNUSED(Tk_Window))
+static void DrawGradientBorder(
+ CGRect bounds,
+ CGContextRef context,
+ Tk_Window tkwin,
+ Ttk_State state)
{
+ CGColorRef faceColor, borderColor;
+ GrayColor faceGray, borderGray;
+ CGRect inside = CGRectInset(bounds, 1, 1);
- /*
- * See ButtonElementDraw for the explanation of why we always draw
- * PushButtons in the active state.
- */
-
- SInt32 HIThemeState;
-
- HIThemeState = Ttk_StateTableLookup(ThemeStateTable, state);
- switch (params->kind) {
- case kThemePushButton:
- HIThemeState &= ~kThemeStateInactive;
- HIThemeState |= kThemeStateActive;
- break;
- default:
- break;
+ if (TkMacOSXInDarkMode(tkwin)) {
+ if (state & TTK_STATE_DISABLED) {
+ faceGray = darkGradientDisabled;
+ borderGray = darkGradientBorderDisabled;
+ } else {
+ faceGray = state & TTK_STATE_PRESSED ?
+ darkGradientPressed : darkGradientNormal;
+ borderGray = darkGradientBorder;
+ }
+ } else {
+ if (state & TTK_STATE_DISABLED) {
+ faceGray = lightGradientDisabled;
+ borderGray = lightGradientBorderDisabled;
+ } else {
+ faceGray = state & TTK_STATE_PRESSED ?
+ lightGradientPressed : lightGradientNormal;
+ borderGray = lightGradientBorder;
+ }
}
-
- const HIThemeButtonDrawInfo info = {
- .version = 0,
- .state = HIThemeState,
- .kind = params ? params->kind : 0,
- .value = Ttk_StateTableLookup(ButtonValueTable, state),
- .adornment = Ttk_StateTableLookup(ButtonAdornmentTable, state),
- };
- return info;
+ faceColor = CGColorFromGray(faceGray);
+ borderColor = CGColorFromGray(borderGray);
+ CGContextSetFillColorWithColor(context, faceColor);
+ CGContextFillRect(context, inside);
+ CGContextSetFillColorWithColor(context, borderColor);
+ CGContextAddRect(context, bounds);
+ CGContextAddRect(context, inside);
+ CGContextEOFillPath(context);
}
/*----------------------------------------------------------------------
@@ -1394,7 +1651,7 @@ static void ButtonElementMinSize(
ThemeButtonParams *params = (ThemeButtonParams *)clientData;
if (params->heightMetric != NoThemeMetric) {
- ChkErr(GetThemeMetric, params->heightMetric, (SInt *) minHeight);
+ ChkErr(GetThemeMetric, params->heightMetric, minHeight);
/*
* The theme height does not include the 1-pixel border around
@@ -1411,7 +1668,13 @@ static void ButtonElementMinSize(
* the style.
*/
- *minWidth = 0;
+ if (params->widthMetric != NoThemeMetric) {
+ ChkErr(GetThemeMetric, params->widthMetric, minWidth);
+ *minWidth += 2;
+ *minHeight += 2;
+ } else {
+ *minWidth = 0;
+ }
}
}
@@ -1424,13 +1687,32 @@ static void ButtonElementSize(
Ttk_Padding *paddingPtr)
{
ThemeButtonParams *params = (ThemeButtonParams *)clientData;
- const HIThemeButtonDrawInfo info =
- computeButtonDrawInfo(params, 0, tkwin);
+ HIThemeButtonDrawInfo info =
+ ComputeButtonDrawInfo(params, 0, tkwin);
static const CGRect scratchBounds = {{0, 0}, {100, 100}};
CGRect contentBounds, backgroundBounds;
int verticalPad;
ButtonElementMinSize(clientData, minWidth, minHeight);
+ switch (info.kind) {
+ case TkSidebarButton:
+ *paddingPtr = Ttk_MakePadding(30, 10, 30, 10);
+ return;
+ case TkGradientButton:
+ *paddingPtr = Ttk_MakePadding(1, 1, 1, 1);
+ /* Fall through. */
+ case kThemeArrowButton:
+ case kThemeRoundButtonHelp:
+ return;
+ /* Buttons which are sized like PushButtons but unknown to HITheme. */
+ case TkRoundedRectButton:
+ case TkRecessedButton:
+ case TkInlineButton:
+ info.kind = kThemePushButton;
+ break;
+ default:
+ break;
+ }
/*
* Given a hypothetical bounding rectangle for a button, HIToolbox will
@@ -1439,7 +1721,7 @@ static void ButtonElementSize(
* enough to contain the image of the button in any state, which might
* include highlight borders, shadows, etc. The content rectangle is not
* centered vertically within the background rectangle, presumably because
- * shadows only appear on the bottom. Nonetheless, when HITools is asked
+ * shadows only appear on the bottom. Nonetheless, when HIToolbox is asked
* to draw a button with a certain bounding rectangle it draws the button
* centered within the rectangle.
*
@@ -1458,6 +1740,10 @@ static void ButtonElementSize(
CGRectGetMaxX(backgroundBounds) - CGRectGetMaxX(contentBounds);
verticalPad = backgroundBounds.size.height - contentBounds.size.height;
paddingPtr->top = paddingPtr->bottom = verticalPad / 2;
+ if (info.kind == kThemePopupButton) {
+ paddingPtr->top += 1;
+ paddingPtr->bottom -= 1;
+ }
}
static void ButtonElementDraw(
@@ -1470,38 +1756,63 @@ static void ButtonElementDraw(
{
ThemeButtonParams *params = (ThemeButtonParams *)clientData;
CGRect bounds = BoxToRect(d, b);
- HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, state, tkwin);
+ HIThemeButtonDrawInfo info = ComputeButtonDrawInfo(params, state, tkwin);
+ int isDark = TkMacOSXInDarkMode(tkwin);
- bounds = NormalizeButtonBounds(params->heightMetric, bounds);
+ switch (info.kind) {
- BEGIN_DRAWING(d)
- if (TkMacOSXInDarkMode(tkwin)) {
- 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;
- case kThemeRoundedBevelButton:
- DrawDarkBevelButton(bounds, state, dc.context);
- break;
- default:
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context,
- HIOrientation, NULL);
- }
- } else if (info.kind == kThemePushButton &&
- (state & TTK_STATE_PRESSED)) {
- bounds.size.height += 2;
- if ([NSApp macOSVersion] > 100800) {
- GradientFillRoundedRectangle(dc.context, bounds, 4,
- pressedPushButtonGradient, 2);
- }
- } else {
+ /*
+ * A Gradient Button should have an image and no text. The size is set to
+ * that of the image. All we need to do is draw a 1-pixel border.
+ */
+
+ case TkGradientButton:
+ BEGIN_DRAWING(d)
+ DrawGradientBorder(bounds, dc.context, tkwin, state);
+ END_DRAWING
+ return;
+ /*
+ * Buttons with no height restrictions are ready to draw.
+ */
+
+ case kThemeArrowButton:
+ case kThemeCheckBox:
+ case kThemeRadioButton:
+ case TkSidebarButton:
+ break;
+
+ /*
+ * Other buttons have a maximum height. We have to deal with that.
+ */
+
+ default:
+ bounds = NormalizeButtonBounds(params, bounds, isDark);
+ break;
+ }
+
+ /* We do our own drawing on new systems.*/
+
+ if ([NSApp macOSVersion] > 100800) {
+ BEGIN_DRAWING(d)
+ DrawButton(bounds, info, state, dc.context, tkwin);
+ END_DRAWING
+ return;
+ }
+
+ /*
+ * If execution reaches here it means we should use HIToolbox to draw the
+ * button. Buttons that HIToolbox doesn't know are rendered as
+ * PushButtons.
+ */
+
+ switch (info.kind) {
+ case TkRoundedRectButton:
+ case TkRecessedButton:
+ info.kind = kThemePushButton;
+ break;
+ default:
+ break;
+ }
/*
* Apple's PushButton and PopupButton do not change their fill color
@@ -1515,11 +1826,11 @@ static void ButtonElementDraw(
* active state.
*/
- if (info.kind == kThemePopupButton &&
- (state & TTK_STATE_BACKGROUND)) {
+ BEGIN_DRAWING(d)
+ if (info.kind == kThemePopupButton &&
+ (state & TTK_STATE_BACKGROUND)) {
CGRect innerBounds = CGRectInset(bounds, 1, 1);
- NSColor *whiteRGBA = [NSColor whiteColor];
- SolidFillRoundedRectangle(dc.context, innerBounds, 4, whiteRGBA);
+ FillRoundedRectangle(dc.context, innerBounds, 4, CG_WHITE);
}
/*
@@ -1528,13 +1839,15 @@ static void ButtonElementDraw(
*/
if (info.kind == kThemeRoundedBevelButton &&
- info.value == kThemeButtonMixed) {
+ info.value == kThemeButtonMixed) {
info.value = kThemeButtonOff;
info.state = kThemeStateInactive;
}
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation,
- NULL);
+ if (info.kind == kThemePushButton) {
+ bounds.origin.y -= 2;
}
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation,
+ NULL);
END_DRAWING
}
@@ -1551,7 +1864,7 @@ static Ttk_ElementSpec ButtonElementSpec = {
*/
/* Tab position logic, c.f. ttkNotebook.c TabState() */
-static Ttk_StateTable TabStyleTable[] = {
+static const Ttk_StateTable TabStyleTable[] = {
{kThemeTabFrontInactive, TTK_STATE_SELECTED | TTK_STATE_BACKGROUND, 0},
{kThemeTabNonFrontInactive, TTK_STATE_BACKGROUND, 0},
{kThemeTabFrontUnavailable, TTK_STATE_DISABLED | TTK_STATE_SELECTED, 0},
@@ -1560,16 +1873,16 @@ static Ttk_StateTable TabStyleTable[] = {
{kThemeTabNonFrontPressed, TTK_STATE_PRESSED, 0},
{kThemeTabNonFront, 0, 0}
};
-static Ttk_StateTable TabAdornmentTable[] = {
- {kHIThemeTabAdornmentNone, TTK_STATE_FIRST_TAB | TTK_STATE_LAST_TAB, 0},
- {kHIThemeTabAdornmentTrailingSeparator, TTK_STATE_FIRST_TAB, 0},
- {kHIThemeTabAdornmentNone, TTK_STATE_LAST_TAB, 0},
+static const Ttk_StateTable TabAdornmentTable[] = {
+ {kHIThemeTabAdornmentNone, TTK_STATE_FIRST | TTK_STATE_LAST, 0},
+ {kHIThemeTabAdornmentTrailingSeparator, TTK_STATE_FIRST, 0},
+ {kHIThemeTabAdornmentNone, TTK_STATE_LAST, 0},
{kHIThemeTabAdornmentTrailingSeparator, 0, 0},
};
-static Ttk_StateTable TabPositionTable[] = {
- {kHIThemeTabPositionOnly, TTK_STATE_FIRST_TAB | TTK_STATE_LAST_TAB, 0},
- {kHIThemeTabPositionFirst, TTK_STATE_FIRST_TAB, 0},
- {kHIThemeTabPositionLast, TTK_STATE_LAST_TAB, 0},
+static const Ttk_StateTable TabPositionTable[] = {
+ {kHIThemeTabPositionOnly, TTK_STATE_FIRST | TTK_STATE_LAST, 0},
+ {kHIThemeTabPositionFirst, TTK_STATE_FIRST, 0},
+ {kHIThemeTabPositionLast, TTK_STATE_LAST, 0},
{kHIThemeTabPositionMiddle, 0, 0},
};
@@ -1618,12 +1931,10 @@ static void TabElementSize(
TCL_UNUSED(void *), /* elementRecord */
TCL_UNUSED(Tk_Window), /* tkwin */
TCL_UNUSED(int *), /* minWidth */
- int *minHeight,
+ TCL_UNUSED(int *), /* minHeight */
Ttk_Padding *paddingPtr)
{
- GetThemeMetric(kThemeMetricLargeTabHeight, (SInt32 *) minHeight);
- *paddingPtr = Ttk_MakePadding(0, 0, 0, 2);
-
+ *paddingPtr = Ttk_MakePadding(0, -2, 0, 1);
}
static void TabElementDraw(
@@ -1635,20 +1946,21 @@ static void TabElementDraw(
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 (TkMacOSXInDarkMode(tkwin)) {
- DrawDarkTab(bounds, state, dc.context);
+ if ([NSApp macOSVersion] >= 110000) {
+ DrawTab11(bounds, state, dc.context, tkwin);
+ } else if ([NSApp macOSVersion] > 100800) {
+ DrawTab(bounds, state, dc.context, tkwin);
} else {
+ 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),
+ };
ChkErr(HIThemeDrawTab, &bounds, &info, dc.context, HIOrientation,
NULL);
}
@@ -1692,7 +2004,7 @@ static void PaneElementDraw(
bounds.size.height += kThemeMetricTabFrameOverlap;
BEGIN_DRAWING(d)
if ([NSApp macOSVersion] > 100800) {
- DrawGroupBox(bounds, dc.context, tkwin);
+ DrawGroupBox(bounds, dc.context, tkwin, 1, YES);
} else {
HIThemeTabPaneDrawInfo info = {
.version = 1,
@@ -1724,8 +2036,6 @@ static Ttk_ElementSpec PaneElementSpec = {
* 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(
@@ -1736,7 +2046,7 @@ static void GroupElementSize(
TCL_UNUSED(int *), /* minHeight */
Ttk_Padding *paddingPtr)
{
- *paddingPtr = Ttk_UniformPadding(4);
+ *paddingPtr = Ttk_MakePadding(0, 0, 0, 0);
}
static void GroupElementDraw(
@@ -1751,7 +2061,7 @@ static void GroupElementDraw(
BEGIN_DRAWING(d)
if ([NSApp macOSVersion] > 100800) {
- DrawGroupBox(bounds, dc.context, tkwin);
+ DrawGroupBox(bounds, dc.context, tkwin, 1, YES);
} else {
const HIThemeGroupBoxDrawInfo info = {
.version = 0,
@@ -1787,9 +2097,9 @@ typedef struct {
static Ttk_ElementOptionSpec EntryElementOptions[] = {
{"-background", TK_OPTION_BORDER,
- Tk_Offset(EntryElement, backgroundObj), ENTRY_DEFAULT_BACKGROUND},
+ offsetof(EntryElement, backgroundObj), ENTRY_DEFAULT_BACKGROUND},
{"-fieldbackground", TK_OPTION_BORDER,
- Tk_Offset(EntryElement, fieldbackgroundObj), ENTRY_DEFAULT_BACKGROUND},
+ offsetof(EntryElement, fieldbackgroundObj), ENTRY_DEFAULT_BACKGROUND},
{NULL, TK_OPTION_BOOLEAN, 0, NULL}
};
@@ -1801,11 +2111,11 @@ static void EntryElementSize(
TCL_UNUSED(int *), /* minHeight */
Ttk_Padding *paddingPtr)
{
- *paddingPtr = Ttk_MakePadding(7, 5, 7, 6);
+ *paddingPtr = entryElementPadding;
}
static void EntryElementDraw(
- TCL_UNUSED(void *),
+ void *clientData,
void *elementRecord,
Tk_Window tkwin,
Drawable d,
@@ -1813,40 +2123,31 @@ static void EntryElementDraw(
Ttk_State state)
{
EntryElement *e = (EntryElement *)elementRecord;
- Ttk_Box inner = Ttk_PadBox(b, Ttk_UniformPadding(3));
- CGRect bounds = BoxToRect(d, inner);
- NSColor *background;
+ ThemeFrameParams *params = (ThemeFrameParams *)clientData;
+ HIThemeFrameKind kind = params ? params->kind :
+ kHIThemeFrameTextFieldSquare;
+ CGRect bounds = BoxToRect(d, b);
+ CGColorRef background;
Tk_3DBorder backgroundPtr = NULL;
static const char *defaultBG = ENTRY_DEFAULT_BACKGROUND;
- if (TkMacOSXInDarkMode(tkwin)) {
+ if ([NSApp macOSVersion] > 100800) {
BEGIN_DRAWING(d)
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- CGFloat fill[4];
- GetBackgroundColor(dc.context, tkwin, 1, fill);
-
- /*
- * Lighten the background to provide contrast.
- */
-
- for (int i = 0; i < 3; i++) {
- fill[i] += 9.0 / 255.0;
+ switch(kind) {
+ case kHIThemeFrameTextFieldRound:
+ DrawEntry(dc.context, bounds, &searchDesign, state, tkwin);
+ break;
+ case kHIThemeFrameTextFieldSquare:
+ DrawEntry(dc.context, bounds, &entryDesign, state, tkwin);
+ break;
+ default:
+ return;
}
- background = [NSColor colorWithColorSpace: deviceRGB
- components: fill
- count: 4];
- CGContextSetFillColorWithColor(dc.context, CGCOLOR(background));
- CGContextFillRect(dc.context, bounds);
- if (state & TTK_STATE_FOCUS) {
- DrawDarkFocusRing(bounds, dc.context);
- } else {
- DrawDarkFrame(bounds, dc.context, kHIThemeFrameTextFieldSquare);
- }
END_DRAWING
} else {
const HIThemeFrameDrawInfo info = {
.version = 0,
- .kind = kHIThemeFrameTextFieldSquare,
+ .kind = params->kind,
.state = Ttk_StateTableLookup(ThemeStateTable, state),
.isFocused = state & TTK_STATE_FOCUS,
};
@@ -1869,13 +2170,13 @@ static void EntryElementDraw(
if (backgroundPtr != NULL) {
XFillRectangle(Tk_Display(tkwin), d,
Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
- inner.x, inner.y, inner.width, inner.height);
+ b.x, b.y, b.width, b.height);
}
BEGIN_DRAWING(d)
if (backgroundPtr == NULL) {
if ([NSApp macOSVersion] > 100800) {
- background = [NSColor textBackgroundColor];
- CGContextSetFillColorWithColor(dc.context, CGCOLOR(background));
+ background = CGCOLOR([NSColor textBackgroundColor]);
+ CGContextSetFillColorWithColor(dc.context, background);
} else {
CGContextSetRGBFillColor(dc.context, 1.0, 1.0, 1.0, 1.0);
}
@@ -1914,24 +2215,20 @@ static Ttk_ElementSpec EntryElementSpec = {
* 1 pixel to account for the fact that the button is not centered.
*/
-static Ttk_Padding ComboboxPadding = {4, 4, 20, 4};
-static Ttk_Padding DarkComboboxPadding = {6, 6, 22, 6};
+// OS dependent ???
+static Ttk_Padding ComboboxPadding = {7, 5, 24, 5};
static void ComboboxElementSize(
TCL_UNUSED(void *), /* clientData */
TCL_UNUSED(void *), /* elementRecord */
- Tk_Window tkwin,
+ TCL_UNUSED(Tk_Window), /* tkwin */
int *minWidth,
int *minHeight,
Ttk_Padding *paddingPtr)
{
*minWidth = 24;
- *minHeight = 23;
- if (TkMacOSXInDarkMode(tkwin)) {
- *paddingPtr = DarkComboboxPadding;
- } else {
- *paddingPtr = ComboboxPadding;
- }
+ *minHeight = 0;
+ *paddingPtr = ComboboxPadding;
}
static void ComboboxElementDraw(
@@ -1952,23 +2249,13 @@ static void ComboboxElementDraw(
};
BEGIN_DRAWING(d)
- if (TkMacOSXInDarkMode(tkwin)) {
- bounds = CGRectInset(bounds, 3, 3);
- if (state & TTK_STATE_FOCUS) {
- DrawDarkFocusRing(bounds, dc.context);
- }
- DrawDarkButton(bounds, info.kind, state, dc.context);
+ if ([NSApp macOSVersion] > 100800) {
+ bounds = CGRectInset(bounds, -1, -1);
+ DrawButton(bounds, info, state, dc.context, tkwin);
} else {
- if ([NSApp macOSVersion] > 100800) {
- if ((state & TTK_STATE_BACKGROUND) &&
- !(state & TTK_STATE_DISABLED)) {
- NSColor *background = [NSColor textBackgroundColor];
- CGRect innerBounds = CGRectInset(bounds, 1, 4);
- bounds.origin.y += 1;
- SolidFillRoundedRectangle(dc.context, innerBounds, 4, background);
- }
- }
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ bounds.origin.y += 1;
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation,
+ NULL);
}
END_DRAWING
}
@@ -2002,7 +2289,22 @@ static Ttk_ElementSpec ComboboxElementSpec = {
* that order. So the up button must be listed first in the layout.
*/
-static Ttk_Padding SpinbuttonMargins = {0, 0, 2, 0};
+static Ttk_Padding SpinbuttonMargins = {2, 0, 0, 0};
+
+static void SpinButtonReBounds(
+ Tk_Window tkwin,
+ CGRect *bounds)
+{
+ if (TkMacOSXInDarkMode(tkwin)) {
+ bounds->origin.x -= 2;
+ bounds->origin.y += 1;
+ bounds->size.height -= 0.5;
+ } else {
+ bounds->origin.x -= 3;
+ bounds->origin.y += 1;
+ bounds->size.width += 1;
+ }
+}
static void SpinButtonElementSize(
TCL_UNUSED(void *), /* clientdata */
@@ -2017,7 +2319,7 @@ static void SpinButtonElementSize(
ChkErr(GetThemeMetric, kThemeMetricLittleArrowsWidth, &s);
*minWidth = s + Ttk_PaddingWidth(SpinbuttonMargins);
ChkErr(GetThemeMetric, kThemeMetricLittleArrowsHeight, &s);
- *minHeight = (s + Ttk_PaddingHeight(SpinbuttonMargins)) / 2;
+ *minHeight = 2 + (s + Ttk_PaddingHeight(SpinbuttonMargins)) / 2;
}
static void SpinButtonUpElementDraw(
@@ -2031,6 +2333,7 @@ static void SpinButtonUpElementDraw(
CGRect bounds = BoxToRect(d, Ttk_PadBox(b, SpinbuttonMargins));
int infoState;
+ SpinButtonReBounds(tkwin, &bounds);
bounds.size.height *= 2;
if (state & TTK_STATE_PRESSED) {
infoState = kThemeStatePressedUp;
@@ -2045,8 +2348,8 @@ static void SpinButtonUpElementDraw(
.adornment = kThemeAdornmentNone,
};
BEGIN_DRAWING(d)
- if (TkMacOSXInDarkMode(tkwin)) {
- DrawDarkIncDecButton(bounds, infoState, state, dc.context);
+ if ([NSApp macOSVersion] > 100800) {
+ DrawButton(bounds, info, state, dc.context, tkwin);
} else {
ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation,
NULL);
@@ -2073,6 +2376,7 @@ static void SpinButtonDownElementDraw(
CGRect bounds = BoxToRect(d, Ttk_PadBox(b, SpinbuttonMargins));
int infoState = 0;
+ SpinButtonReBounds(tkwin, &bounds);
bounds.origin.y -= bounds.size.height;
bounds.size.height += bounds.size.height;
if (state & TTK_STATE_PRESSED) {
@@ -2089,8 +2393,8 @@ static void SpinButtonDownElementDraw(
};
BEGIN_DRAWING(d)
- if (TkMacOSXInDarkMode(tkwin)) {
- DrawDarkIncDecButton(bounds, infoState, state, dc.context);
+ if ([NSApp macOSVersion] > 100800) {
+ DrawButton(bounds, info, state, dc.context, tkwin);
} else {
ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation,
NULL);
@@ -2117,7 +2421,7 @@ static Ttk_ElementSpec SpinButtonDownElementSpec = {
* inactive. So we shouldn't either.
*/
-static Ttk_StateTable ThemeTrackEnableTable[] = {
+static const Ttk_StateTable ThemeTrackEnableTable[] = {
{kThemeTrackDisabled, TTK_STATE_DISABLED, 0},
{kThemeTrackActive, TTK_STATE_BACKGROUND, 0},
{kThemeTrackActive, 0, 0}
@@ -2141,10 +2445,10 @@ typedef struct {
} TrackElement;
static Ttk_ElementOptionSpec TrackElementOptions[] = {
- {"-from", TK_OPTION_DOUBLE, Tk_Offset(TrackElement, fromObj), NULL},
- {"-to", TK_OPTION_DOUBLE, Tk_Offset(TrackElement, toObj), NULL},
- {"-value", TK_OPTION_DOUBLE, Tk_Offset(TrackElement, valueObj), NULL},
- {"-orient", TK_OPTION_STRING, Tk_Offset(TrackElement, orientObj), NULL},
+ {"-from", TK_OPTION_DOUBLE, offsetof(TrackElement, fromObj), NULL},
+ {"-to", TK_OPTION_DOUBLE, offsetof(TrackElement, toObj), NULL},
+ {"-value", TK_OPTION_DOUBLE, offsetof(TrackElement, valueObj), NULL},
+ {"-orient", TK_OPTION_STRING, offsetof(TrackElement, orientObj), NULL},
{NULL, TK_OPTION_BOOLEAN, 0, NULL}
};
static void TrackElementSize(
@@ -2172,29 +2476,24 @@ static void TrackElementDraw(
{
TrackElementData *data = (TrackElementData *)clientData;
TrackElement *elem = (TrackElement *)elementRecord;
- int orientation = TTK_ORIENT_HORIZONTAL;
- double from = 0, to = 100, value = 0, factor;
- CGRect bounds;
+ Ttk_Orient orientation = TTK_ORIENT_HORIZONTAL;
+ double from = 0, to = 100, value = 0, fraction, max;
+ CGRect bounds = BoxToRect(d, b);
- Ttk_GetOrientFromObj(NULL, elem->orientObj, &orientation);
+ TtkGetOrientFromObj(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);
- /*
- * HIThemeTrackDrawInfo uses 2-byte alignment; assigning to a separate
- * bounds variable avoids UBSan (-fsanitize=alignment) complaints.
- */
-
- bounds = BoxToRect(d, b);
+ fraction = (value - from) / (to - from);
+ max = RangeToFactor(fabs(to - from));
HIThemeTrackDrawInfo info = {
.version = 0,
.kind = data->kind,
.bounds = bounds,
- .min = from * factor,
- .max = to * factor,
- .value = value * factor,
+ .min = 0,
+ .max = max,
+ .value = fraction * max,
.attributes = kThemeTrackShowThumb |
(orientation == TTK_ORIENT_HORIZONTAL ?
kThemeTrackHorizontal : 0),
@@ -2212,20 +2511,19 @@ static void TrackElementDraw(
}
}
BEGIN_DRAWING(d)
- if (TkMacOSXInDarkMode(tkwin)) {
- 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);
- }
- SolidFillRoundedRectangle(dc.context, bounds, 2, trackColor);
+ if (([NSApp macOSVersion] > 100800) && !(state & TTK_STATE_ALTERNATE)) {
+
+ /*
+ * We use the reserved field to indicate whether "from" is less than
+ * "to". It should be 0 if passing the info to HIThemeDrawInfo, but
+ * we aren't doing that.
+ */
+
+ info.reserved = (from < to);
+ DrawSlider(dc.context, bounds, info, state, tkwin);
+ } else {
+ ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
}
- ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
END_DRAWING
}
@@ -2283,15 +2581,15 @@ typedef struct {
static Ttk_ElementOptionSpec PbarElementOptions[] = {
{"-orient", TK_OPTION_STRING,
- Tk_Offset(PbarElement, orientObj), "horizontal"},
+ offsetof(PbarElement, orientObj), "horizontal"},
{"-value", TK_OPTION_DOUBLE,
- Tk_Offset(PbarElement, valueObj), "0"},
+ offsetof(PbarElement, valueObj), "0.0"},
{"-maximum", TK_OPTION_DOUBLE,
- Tk_Offset(PbarElement, maximumObj), "100"},
+ offsetof(PbarElement, maximumObj), "100.0"},
{"-phase", TK_OPTION_INT,
- Tk_Offset(PbarElement, phaseObj), "0"},
+ offsetof(PbarElement, phaseObj), "0"},
{"-mode", TK_OPTION_STRING,
- Tk_Offset(PbarElement, modeObj), "determinate"},
+ offsetof(PbarElement, modeObj), "determinate"},
{NULL, TK_OPTION_BOOLEAN, 0, NULL}
};
static void PbarElementSize(
@@ -2309,7 +2607,7 @@ static void PbarElementSize(
}
static void PbarElementDraw(
- TCL_UNUSED(void *),
+ TCL_UNUSED(void *), /* clientData */
void *elementRecord,
Tk_Window tkwin,
Drawable d,
@@ -2317,71 +2615,51 @@ static void PbarElementDraw(
Ttk_State state)
{
PbarElement *pbar = (PbarElement *)elementRecord;
- int orientation = TTK_ORIENT_HORIZONTAL, phase = 0, kind;
-
- /*
- * Using 1000 as the maximum should give better than 1 pixel
- * resolution for most progress bars.
- */
+ Ttk_Orient orientation = TTK_ORIENT_HORIZONTAL;
+ int phase;
+ double value = 0, maximum = 100, factor;
+ CGRect bounds = BoxToRect(d, b);
+ int isIndeterminate = !strcmp("indeterminate",
+ Tcl_GetString(pbar->modeObj));
- int ivalue, imaximum = 1000;
- CGRect bounds;
+ TtkGetOrientFromObj(NULL, pbar->orientObj, &orientation);
+ Tcl_GetDoubleFromObj(NULL, pbar->valueObj, &value);
+ Tcl_GetDoubleFromObj(NULL, pbar->maximumObj, &maximum);
+ Tcl_GetIntFromObj(NULL, pbar->phaseObj, &phase);
- Ttk_GetOrientFromObj(NULL, pbar->orientObj, &orientation);
- kind = !strcmp("indeterminate", Tcl_GetString(pbar->modeObj)) ?
- kThemeIndeterminateBar : kThemeProgressBar;
- if (kind == kThemeIndeterminateBar) {
- Tcl_GetIntFromObj(NULL, pbar->phaseObj, &phase);
+ if (isIndeterminate) {
/*
- * On macOS 11 the fraction of an indeterminate progress bar which is
- * traversed by the oscillating thumb is value / maximum. The phase
- * determines the position of the moving thumb in that range and is
- * apparently expected to vary between 0 and 120. On earlier systems
- * it is unclear how the phase is used in generating the animation.
+ * When an indeterminate progress bar is animated the phase is
+ * (currently) always 0 and the value increases from min to max
+ * and then decreases back to min. We scale the value by 3 to
+ * speed the animation up a bit.
*/
- ivalue = imaximum;
- } else {
- double value, maximum;
- Tcl_GetDoubleFromObj(NULL, pbar->valueObj, &value);
- Tcl_GetDoubleFromObj(NULL, pbar->maximumObj, &maximum);
- ivalue = (value / maximum)*1000;
+ double remainder = fmod(3*value, 2*maximum);
+ value = remainder > maximum ? 2*maximum - remainder : remainder;
}
-
- /*
- * HIThemeTrackDrawInfo uses 2-byte alignment; assigning to a separate
- * bounds variable avoids UBSan (-fsanitize=alignment) complaints.
- */
-
- bounds = BoxToRect(d, b);
+ factor = RangeToFactor(maximum);
HIThemeTrackDrawInfo info = {
.version = 0,
- .kind = kind,
+ .kind = isIndeterminate? kThemeIndeterminateBar : kThemeProgressBar,
.bounds = bounds,
.min = 0,
- .max = imaximum,
- .value = ivalue,
+ .max = maximum * factor,
+ .value = value * factor,
.attributes = kThemeTrackShowThumb |
- (orientation == TTK_ORIENT_HORIZONTAL ? kThemeTrackHorizontal : 0),
+ (orientation == TTK_ORIENT_HORIZONTAL ?
+ kThemeTrackHorizontal : 0),
.enableState = Ttk_StateTableLookup(ThemeTrackEnableTable, state),
- .trackInfo.progress.phase = phase
+ .trackInfo.progress.phase = phase,
};
+
BEGIN_DRAWING(d)
- if (TkMacOSXInDarkMode(tkwin)) {
- 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);
- }
- SolidFillRoundedRectangle(dc.context, bounds, 3, trackColor);
+ if ([NSApp macOSVersion] > 100800) {
+ DrawProgressBar(dc.context, bounds, info, state, tkwin);
+ } else {
+ ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
}
- ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
END_DRAWING
}
@@ -2404,7 +2682,7 @@ typedef struct
static Ttk_ElementOptionSpec ScrollbarElementOptions[] = {
{"-orient", TK_OPTION_STRING,
- Tk_Offset(ScrollbarElement, orientObj), "horizontal"},
+ offsetof(ScrollbarElement, orientObj), "horizontal"},
{NULL, TK_OPTION_BOOLEAN, 0, NULL}
};
static void TroughElementSize(
@@ -2416,10 +2694,10 @@ static void TroughElementSize(
Ttk_Padding *paddingPtr)
{
ScrollbarElement *scrollbar = (ScrollbarElement *)elementRecord;
- int orientation = TTK_ORIENT_HORIZONTAL;
+ Ttk_Orient orientation = TTK_ORIENT_HORIZONTAL;
SInt32 thickness = 15;
- Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+ TtkGetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
ChkErr(GetThemeMetric, kThemeMetricScrollBarWidth, &thickness);
if (orientation == TTK_ORIENT_HORIZONTAL) {
*minHeight = thickness;
@@ -2434,20 +2712,6 @@ static void TroughElementSize(
}
}
-static CGFloat lightTrough[4] = {250.0 / 255, 250.0 / 255, 250.0 / 255, 1.0};
-static CGFloat darkTrough[4] = {45.0 / 255, 46.0 / 255, 49.0 / 255, 1.0};
-static CGFloat lightInactiveThumb[4] = {
- 200.0 / 255, 200.0 / 255, 200.0 / 255, 1.0
-};
-static CGFloat lightActiveThumb[4] = {
- 133.0 / 255, 133.0 / 255, 133.0 / 255, 1.0
-};
-static CGFloat darkInactiveThumb[4] = {
- 116.0 / 255, 117.0 / 255, 118.0 / 255, 1.0
-};
-static CGFloat darkActiveThumb[4] = {
- 158.0 / 255, 158.0 / 255, 159.0 / 255, 1.0
-};
static void TroughElementDraw(
TCL_UNUSED(void *), /* clientData */
void *elementRecord,
@@ -2457,24 +2721,20 @@ static void TroughElementDraw(
TCL_UNUSED(Ttk_State)) /* state */
{
ScrollbarElement *scrollbar = (ScrollbarElement *)elementRecord;
- int orientation = TTK_ORIENT_HORIZONTAL;
+ Ttk_Orient orientation = TTK_ORIENT_HORIZONTAL;
CGRect bounds = BoxToRect(d, b);
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *troughColor;
- CGFloat *rgba = TkMacOSXInDarkMode(tkwin) ? darkTrough : lightTrough;
+ GrayColor bgGray;
- Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+ TtkGetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
if (orientation == TTK_ORIENT_HORIZONTAL) {
bounds = CGRectInset(bounds, 0, 1);
} else {
bounds = CGRectInset(bounds, 1, 0);
}
- troughColor = [NSColor colorWithColorSpace: deviceRGB
- components: rgba
- count: 4];
BEGIN_DRAWING(d)
if ([NSApp macOSVersion] > 100800) {
- CGContextSetFillColorWithColor(dc.context, CGCOLOR(troughColor));
+ bgGray = TkMacOSXInDarkMode(tkwin) ? darkTrough : lightTrough;
+ CGContextSetFillColorWithColor(dc.context, CGColorFromGray(bgGray));
} else {
ChkErr(HIThemeSetFill, kThemeBrushDocumentWindowBackground, NULL,
dc.context, HIOrientation);
@@ -2499,9 +2759,9 @@ static void ThumbElementSize(
TCL_UNUSED(Ttk_Padding *)) /* paddingPtr */
{
ScrollbarElement *scrollbar = (ScrollbarElement *)elementRecord;
- int orientation = TTK_ORIENT_HORIZONTAL;
+ Ttk_Orient orientation = TTK_ORIENT_HORIZONTAL;
- Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+ TtkGetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
if (orientation == TTK_ORIENT_VERTICAL) {
*minHeight = 18;
*minWidth = 8;
@@ -2520,9 +2780,9 @@ static void ThumbElementDraw(
Ttk_State state)
{
ScrollbarElement *scrollbar = (ScrollbarElement *)elementRecord;
- int orientation = TTK_ORIENT_HORIZONTAL;
+ Ttk_Orient orientation = TTK_ORIENT_HORIZONTAL;
- Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+ TtkGetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
/*
* In order to make ttk scrollbars work correctly it is necessary to be
@@ -2531,16 +2791,20 @@ static void ThumbElementDraw(
* determine the thumb geometry from the input values of min, max, value
* and viewSize is undocumented. A seemingly natural algorithm is
* implemented below. This code uses that algorithm for older OS versions,
- * because using HITools also handles drawing the buttons and 3D thumb used
+ * because using HIToolbox also handles drawing the buttons and 3D thumb used
* on those systems. For newer systems the cleanest approach is to just
* draw the thumb directly.
*/
if ([NSApp macOSVersion] > 100800) {
CGRect thumbBounds = BoxToRect(d, b);
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *thumbColor;
- CGFloat *rgba;
+ CGColorRef thumbColor;
+ GrayColor bgGray;
+
+ /*
+ * Apple does not draw the thumb when scrolling is not possible.
+ */
+
if ((orientation == TTK_ORIENT_HORIZONTAL &&
thumbBounds.size.width >= Tk_Width(tkwin) - 8) ||
(orientation == TTK_ORIENT_VERTICAL &&
@@ -2550,15 +2814,13 @@ static void ThumbElementDraw(
int isDark = TkMacOSXInDarkMode(tkwin);
if ((state & TTK_STATE_PRESSED) ||
(state & TTK_STATE_HOVER)) {
- rgba = isDark ? darkActiveThumb : lightActiveThumb;
+ bgGray = isDark ? darkActiveThumb : lightActiveThumb;
} else {
- rgba = isDark ? darkInactiveThumb : lightInactiveThumb;
+ bgGray = isDark ? darkInactiveThumb : lightInactiveThumb;
}
- thumbColor = [NSColor colorWithColorSpace: deviceRGB
- components: rgba
- count: 4];
+ thumbColor = CGColorFromGray(bgGray);
BEGIN_DRAWING(d)
- SolidFillRoundedRectangle(dc.context, thumbBounds, 4, thumbColor);
+ FillRoundedRectangle(dc.context, thumbBounds, 4, thumbColor);
END_DRAWING
} else {
double thumbSize, trackSize, visibleSize, factor, fraction;
@@ -2674,7 +2936,7 @@ static void SeparatorElementDraw(
Tk_Window tkwin,
Drawable d,
Ttk_Box b,
- unsigned int state)
+ Ttk_State state)
{
CGRect bounds = BoxToRect(d, b);
const HIThemeSeparatorDrawInfo info = {
@@ -2737,7 +2999,7 @@ static void SizegripElementDraw(
TCL_UNUSED(Tk_Window), /* tkwin */
Drawable d,
Ttk_Box b,
- unsigned int state)
+ Ttk_State state)
{
CGRect bounds = BoxToRect(d, b);
HIThemeGrowBoxDrawInfo info = {
@@ -2819,15 +3081,10 @@ static void FillElementDraw(
CGRect bounds = BoxToRect(d, b);
if ([NSApp macOSVersion] > 100800) {
- NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
- NSColor *bgColor;
- CGFloat fill[4];
+ CGColorRef bgColor;
BEGIN_DRAWING(d)
- GetBackgroundColor(dc.context, tkwin, 0, fill);
- bgColor = [NSColor colorWithColorSpace: deviceRGB components: fill
- count: 4];
- CGContextSetFillColorSpace(dc.context, deviceRGB.CGColorSpace);
- CGContextSetFillColorWithColor(dc.context, CGCOLOR(bgColor));
+ bgColor = GetBackgroundCGColor(dc.context, tkwin, NO, 0);
+ CGContextSetFillColorWithColor(dc.context, bgColor);
CGContextFillRect(dc.context, bounds);
END_DRAWING
} else {
@@ -2847,7 +3104,7 @@ static void BackgroundElementDraw(
Tk_Window tkwin,
Drawable d,
TCL_UNUSED(Ttk_Box),
- unsigned int state)
+ Ttk_State state)
{
FillElementDraw(clientData, elementRecord, tkwin, d, Ttk_WinBox(tkwin),
state);
@@ -2921,7 +3178,7 @@ typedef struct {
static Ttk_ElementOptionSpec FieldElementOptions[] = {
{"-fieldbackground", TK_OPTION_BORDER,
- Tk_Offset(FieldElement, backgroundObj), "white"},
+ offsetof(FieldElement, backgroundObj), "white"},
{NULL, TK_OPTION_BOOLEAN, 0, NULL}
};
@@ -2958,13 +3215,13 @@ static Ttk_ElementSpec FieldElementSpec = {
* buttons, so we draw them from scratch.
*/
-static Ttk_StateTable TreeHeaderValueTable[] = {
+static const Ttk_StateTable TreeHeaderValueTable[] = {
{kThemeButtonOn, TTK_STATE_ALTERNATE, 0},
{kThemeButtonOn, TTK_STATE_SELECTED, 0},
{kThemeButtonOff, 0, 0}
};
-static Ttk_StateTable TreeHeaderAdornmentTable[] = {
+static const Ttk_StateTable TreeHeaderAdornmentTable[] = {
{kThemeAdornmentHeaderButtonSortUp,
TTK_STATE_ALTERNATE | TTK_TREEVIEW_STATE_SORTARROW, 0},
{kThemeAdornmentDefault,
@@ -2990,7 +3247,7 @@ static void TreeAreaElementSize (
*/
if ([NSApp macOSVersion] > 100800) {
- paddingPtr->top = 4;
+ *paddingPtr = Ttk_MakePadding(0, 4, 0, 0);
}
}
@@ -3044,11 +3301,7 @@ static void TreeHeaderElementDraw(
*/
bounds.origin.y -= 4;
- if (TkMacOSXInDarkMode(tkwin)) {
- DrawDarkListHeader(bounds, dc.context, tkwin, state);
- } else {
- DrawListHeader(bounds, dc.context, tkwin, state);
- }
+ DrawListHeader(bounds, dc.context, tkwin, state);
} else {
ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation,
NULL);
@@ -3068,10 +3321,8 @@ static Ttk_ElementSpec TreeHeaderElementSpec = {
* +++ Disclosure triangles --
*/
-#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},
+static const Ttk_StateTable DisclosureValueTable[] = {
+ {kThemeDisclosureDown, TTK_STATE_OPEN, 0},
{kThemeDisclosureRight, 0, 0},
};
static void DisclosureElementSize(
@@ -3098,7 +3349,7 @@ static void DisclosureElementDraw(
Ttk_Box b,
Ttk_State state)
{
- if (!(state & TTK_TREEVIEW_STATE_LEAF)) {
+ if (!(state & TTK_STATE_LEAF)) {
int triangleState = TkMacOSXInDarkMode(tkwin) ?
kThemeStateInactive : kThemeStateActive;
CGRect bounds = BoxToRect(d, b);
@@ -3117,7 +3368,7 @@ static void DisclosureElementDraw(
NSColor *stroke = [[NSColor textColor]
colorUsingColorSpace: deviceRGB];
[stroke getComponents: rgba];
- if (state & TTK_TREEVIEW_STATE_OPEN) {
+ if (state & TTK_STATE_OPEN) {
DrawOpenDisclosure(dc.context, bounds, 2, 8, rgba);
} else {
DrawClosedDisclosure(dc.context, bounds, 2, 12, rgba);
@@ -3172,6 +3423,51 @@ TTK_LAYOUT("TCombobox",
TTK_GROUP("Combobox.padding", TTK_FILL_BOTH,
TTK_NODE("Combobox.textarea", TTK_FILL_BOTH))))
+/* Image Button - no button */
+TTK_LAYOUT("ImageButton",
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH)))
+
+/* Inline Button */
+TTK_LAYOUT("InlineButton",
+ TTK_GROUP("InlineButton.button", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+
+/* Rounded Rect Button -- transparent face */
+TTK_LAYOUT("RoundedRectButton",
+ TTK_GROUP("RoundedRectButton.button", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+
+/* Gradient Button */
+TTK_LAYOUT("GradientButton",
+ TTK_GROUP("GradientButton.button", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+
+/* Recessed Button - text only radio button */
+
+TTK_LAYOUT("RecessedButton",
+ TTK_GROUP("RecessedButton.button", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+
+/* Sidebar Button - text only radio button for sidebars */
+
+TTK_LAYOUT("SidebarButton",
+ TTK_GROUP("SidebarButton.button", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+
+/* DisclosureButton (not a triangle) -- No label, no border*/
+TTK_LAYOUT("DisclosureButton",
+ TTK_NODE("DisclosureButton.button", TTK_FILL_BOTH))
+
+/* HelpButton -- No label, no border*/
+TTK_LAYOUT("HelpButton",
+ TTK_NODE("HelpButton.button", TTK_FILL_BOTH))
+
/* Notebook tabs -- no focus ring */
TTK_LAYOUT("Tab",
TTK_GROUP("Notebook.tab", TTK_FILL_BOTH,
@@ -3186,6 +3482,17 @@ TTK_LAYOUT("TSpinbox",
TTK_GROUP("Spinbox.field", TTK_FILL_X,
TTK_NODE("Spinbox.textarea", TTK_FILL_X)))
+TTK_LAYOUT("TEntry",
+ TTK_GROUP("Entry.field", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Entry.padding", TTK_FILL_BOTH,
+ TTK_NODE("Entry.textarea", TTK_FILL_BOTH))))
+
+/* Searchbox */
+TTK_LAYOUT("Searchbox",
+ TTK_GROUP("Searchbox.field", TTK_FILL_BOTH|TTK_BORDER,
+ TTK_GROUP("Entry.padding", TTK_FILL_BOTH,
+ TTK_NODE("Entry.textarea", TTK_FILL_BOTH))))
+
/* Progress bars -- track only */
TTK_LAYOUT("TProgressbar",
TTK_NODE("Progressbar.track", TTK_FILL_BOTH))
@@ -3229,6 +3536,33 @@ TTK_END_LAYOUT_TABLE
* +++ Initialization --
*/
+/*----------------------------------------------------------------------
+ * +++ Ttk_MacOSXInit --
+ *
+ * Initialize variables which depend on [NSApp macOSVersion]. Called from
+ * [NSApp applicationDidFinishLaunching].
+ */
+
+MODULE_SCOPE void
+Ttk_MacOSXInit(void)
+{
+ if ([NSApp macOSVersion] < 101400) {
+ entryElementPadding = Ttk_MakePadding(7, 6, 7, 5);
+ } else {
+ entryElementPadding = Ttk_MakePadding(7, 5, 7, 6);
+ }
+ if ([NSApp macOSVersion] < 110000) {
+ Ttk_ContrastDelta = 8.0;
+ } else {
+
+ /*
+ * The subtle contrast became event more subtle in 11.0.
+ */
+
+ Ttk_ContrastDelta = 5.0;
+ }
+}
+
static int AquaTheme_Init(
Tcl_Interp *interp)
{
@@ -3251,14 +3585,28 @@ static int AquaTheme_Init(
Ttk_RegisterElementSpec(themePtr, "Button.button",
&ButtonElementSpec, &PushButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "InlineButton.button",
+ &ButtonElementSpec, &InlineButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "RoundedRectButton.button",
+ &ButtonElementSpec, &RoundedRectButtonParams);
Ttk_RegisterElementSpec(themePtr, "Checkbutton.button",
&ButtonElementSpec, &CheckBoxParams);
Ttk_RegisterElementSpec(themePtr, "Radiobutton.button",
&ButtonElementSpec, &RadioButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "RecessedButton.button",
+ &ButtonElementSpec, &RecessedButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "SidebarButton.button",
+ &ButtonElementSpec, &SidebarButtonParams);
Ttk_RegisterElementSpec(themePtr, "Toolbutton.border",
&ButtonElementSpec, &BevelButtonParams);
Ttk_RegisterElementSpec(themePtr, "Menubutton.button",
&ButtonElementSpec, &PopupButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "DisclosureButton.button",
+ &ButtonElementSpec, &DisclosureButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "HelpButton.button",
+ &ButtonElementSpec, &HelpButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "GradientButton.button",
+ &ButtonElementSpec, &GradientButtonParams);
Ttk_RegisterElementSpec(themePtr, "Spinbox.uparrow",
&SpinButtonUpElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "Spinbox.downarrow",
@@ -3277,8 +3625,12 @@ static int AquaTheme_Init(
Ttk_RegisterElementSpec(themePtr, "Labelframe.border", &GroupElementSpec,
0);
- Ttk_RegisterElementSpec(themePtr, "Entry.field", &EntryElementSpec, 0);
- Ttk_RegisterElementSpec(themePtr, "Spinbox.field", &EntryElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "Entry.field", &EntryElementSpec,
+ &EntryFieldParams);
+ Ttk_RegisterElementSpec(themePtr, "Searchbox.field", &EntryElementSpec,
+ &SearchboxFieldParams);
+ Ttk_RegisterElementSpec(themePtr, "Spinbox.field", &EntryElementSpec,
+ &EntryFieldParams);
Ttk_RegisterElementSpec(themePtr, "separator", &SeparatorElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "hseparator", &SeparatorElementSpec, 0);