summaryrefslogtreecommitdiffstats
path: root/macosx
diff options
context:
space:
mode:
authorculler <culler>2019-04-11 04:39:49 (GMT)
committerculler <culler>2019-04-11 04:39:49 (GMT)
commit71b7db434ab281ba494f9fb0235b6d3c88bf8530 (patch)
treeff7925b73b37f09061575bfa71d5c548b3c9e3af /macosx
parent7c1acea352995895a3152bfa54230a3b6e3091f6 (diff)
parentc40319586abe475d1861ac8925b4fcef35f56c32 (diff)
downloadtk-71b7db434ab281ba494f9fb0235b6d3c88bf8530.zip
tk-71b7db434ab281ba494f9fb0235b6d3c88bf8530.tar.gz
tk-71b7db434ab281ba494f9fb0235b6d3c88bf8530.tar.bz2
Support for Apple's Dark Mode and many improvements to the Aqua theme.
Diffstat (limited to 'macosx')
-rw-r--r--macosx/README48
-rw-r--r--macosx/tkMacOSXButton.c12
-rw-r--r--macosx/tkMacOSXColor.c597
-rw-r--r--macosx/tkMacOSXDefault.h39
-rw-r--r--macosx/tkMacOSXInt.h12
-rw-r--r--macosx/tkMacOSXPort.h38
-rw-r--r--macosx/tkMacOSXWm.c133
-rw-r--r--macosx/ttkMacOSXTheme.c2014
8 files changed, 2398 insertions, 495 deletions
diff --git a/macosx/README b/macosx/README
index adc709c..cca90c5 100644
--- a/macosx/README
+++ b/macosx/README
@@ -189,6 +189,16 @@ tk::unsupported::MacWindowStyle tabbingid window ?newId? which can be used to
get or set the tabbingIdentifier for the NSWindow associated with a Tk Window.
See section 3 for details.
+- A command tk::unsupported::MacWindowStyle appearance window ?newAappearance?
+is available when Tk is built and run on macOS 10.14 (Mojave) or later. In
+that case the Ttk widgets all support the "Dark Mode" appearance which was
+introduced in 10.14. The command accepts the following values for the optional
+newAppearance option: "aqua", "darkaqua", or "auto". If the appearance is set
+to aqua or darkaqua then the window will be displayed with the corresponding
+appearance independent of any preferences settings. If it is set to "auto"
+the appearance will be determined by the preferences. This command can be
+used to opt out of Dark Mode on a per-window basis.
+
- If you want to use Remote Debugging with Xcode, you need to set the
environment variable XCNOSTDIN to 1 in the Executable editor for Wish. That will
cause us to force closing stdin & stdout. Otherwise, given how Xcode launches
@@ -275,8 +285,42 @@ ensure that we maintain consistency, changing the tabbingIdentifier of a window
which is already displayed as a tab will also cause it to become a separate
window.
-
-4. Building Tcl/Tk on macOS
+4. Ttk, Dark Mode and semantic colors
+---------------------------------------
+
+With the release of OSX 10.14 (Mojave), Apple introduced the DarkAqua
+appearance. Part of the implementation of the Dark Mode was to make some of
+the named NSColors have dynamic values. Apple calls these "semantic colors"
+because the name does not specify a specific color, but rather refers to the
+context in which the color should be used. Tk now provides the following
+semantic colors as system colors: systemTextColor, systemTextBackgroundColor,
+systemSelectedTextColor, systemSelectedTextBackgroundColor,
+systemControlTextColor, systemDisabledControlTextColor, and systemLabelColor.
+All of these except systemLabelColor, which was introduced in 10.10, were
+present in OSX 10.0. The change in 10.14 was that the RGB color value of these
+colors became dynamic, meaning that the color value can change when the
+application appearance changes. In particular, when a user selects Dark Mode
+in the system preferences these colors change appearance. For example
+systemTextColor is dark in Aqua and light in DarkAqua.
+
+The default background and foreground colors of most of the Tk widgets
+have been set to semantic colors, which means that the widgets will change
+appearance, and remain usable, when Dark Mode is selected in the system
+preferences. However, to get a close match to the native Dark Mode style it
+is recommended to use Ttk widgets when possible.
+
+Apple's tab view and GroupBox objects delimit their content by
+displaying it within a rounded rectangle with a background color that
+contrasts with the background of the containing object. This means
+that the background color of a Ttk widget depends on how deeply it is
+nested inside of other widgets that use contrasting backgrounds. To
+support this, there are 8 contrasting system colors named
+systemTtkBackground, systemTtkBackground1, ... , systemTtkBackground7.
+The systemTtkBackground color is the standard background for a dialog
+window and the others match the contrasting background colors for
+backgrounds which are nested to the corresponding depth.
+
+5. Building Tcl/Tk on macOS
------------------------------
- At least macOS 10.3 is required to build Tcl and TkX11, and macOS 10.6
diff --git a/macosx/tkMacOSXButton.c b/macosx/tkMacOSXButton.c
index 484dcf2..ad963e1 100644
--- a/macosx/tkMacOSXButton.c
+++ b/macosx/tkMacOSXButton.c
@@ -388,7 +388,7 @@ TkpComputeButtonGeometry(
* expanded by the standard padding.
*/
- tmpRect = CGRectMake(0, 0, width + 2*HI_PADX, height + 2*HI_PADY);
+ tmpRect = CGRectMake(0, 0, width + 2*HI_PADX, height + 2*HI_PADY);
HIThemeGetButtonContentBounds(&tmpRect, &mbPtr->drawinfo, &contBounds);
if (height < contBounds.size.height) {
height = contBounds.size.height;
@@ -770,6 +770,16 @@ TkMacOSXDrawButton(
hiinfo.animation.time.start = hiinfo.animation.time.current;
}
+ /*
+ * To avoid buttons with white text on a white background, we always
+ * set the state to inactive in Dark Mode. It isn't perfect but
+ * it is usable. Using a ttk::button would be a better choice,
+ * however.
+ */
+
+ if (TkMacOSXInDarkMode(butPtr->tkwin)) {
+ hiinfo.state = kThemeStateInactive;
+ }
HIThemeDrawButton(&cntrRect, &hiinfo, dc.context, kHIThemeOrientationNormal,
&contHIRec);
diff --git a/macosx/tkMacOSXColor.c b/macosx/tkMacOSXColor.c
index 459ca47..5e983e3 100644
--- a/macosx/tkMacOSXColor.c
+++ b/macosx/tkMacOSXColor.c
@@ -17,11 +17,32 @@
#include "tkMacOSXPrivate.h"
#include "tkColor.h"
+/*
+ * The colorType specifies how the color value should be interpreted. For the
+ * unique rgbColor entry, the RGB values are generated from the pixel value of
+ * an XColor. The ttkBackground and semantic types are dynamic, meaning
+ * that they change when dark mode is enabled on OSX 10.13 and later.
+ */
+
+enum colorType {
+ clearColor, /* There should be only one of these. */
+ rgbColor, /* There should be only one of these. */
+ appearance, /* There should be only one of these. */
+ HIBrush, /* The value is a HITheme brush color table index. */
+ HIText, /* The value is a HITheme text color table index. */
+ HIBackground, /* The value is a HITheme background color table index. */
+ ttkBackground, /* The value can be used as a parameter.*/
+ semantic, /* The value can be used as a parameter.*/
+};
+
+/*
+
+ */
+
struct SystemColorMapEntry {
const char *name;
- ThemeBrush brush;
- ThemeTextColor textColor;
- ThemeBackgroundKind background;
+ enum colorType type;
+ long value;
}; /* unsigned char pixelCode; */
/*
@@ -32,157 +53,171 @@ struct SystemColorMapEntry {
#define MIN_PIXELCODE 30
static const struct SystemColorMapEntry systemColorMap[] = {
- { "Transparent", 0, 0, 0 }, /* 30: TRANSPARENT_PIXEL */
- { "Highlight", kThemeBrushPrimaryHighlightColor, 0, 0 }, /* 31: HIGHLIGHT_PIXEL */
- { "HighlightSecondary", kThemeBrushSecondaryHighlightColor, 0, 0 }, /* 32: HIGHLIGHT_SECONDARY_PIXEL */
- { "HighlightText", kThemeBrushBlack, 0, 0 }, /* 33: HIGHLIGHT_TEXT_PIXEL */
- { "HighlightAlternate", kThemeBrushAlternatePrimaryHighlightColor, 0, 0 }, /* 34: HIGHLIGHT_ALTERNATE_PIXEL */
- { "ButtonText", 0, kThemeTextColorPushButtonActive, 0 }, /* 35: CONTROL_TEXT_PIXEL */
- { "PrimaryHighlightColor", kThemeBrushPrimaryHighlightColor, 0, 0 }, /* 36 */
- { "ButtonFace", kThemeBrushButtonFaceActive, 0, 0 }, /* 37: CONTROL_BODY_PIXEL */
- { "SecondaryHighlightColor", kThemeBrushSecondaryHighlightColor, 0, 0 }, /* 38 */
- { "ButtonFrame", kThemeBrushButtonFrameActive, 0, 0 }, /* 39: CONTROL_FRAME_PIXEL */
- { "AlternatePrimaryHighlightColor", kThemeBrushAlternatePrimaryHighlightColor, 0, 0 }, /* 40 */
- { "WindowBody", kThemeBrushDocumentWindowBackground, 0, 0 }, /* 41: WINDOW_BODY_PIXEL */
- { "SheetBackground", kThemeBrushSheetBackground, 0, 0 }, /* 42 */
- { "MenuActive", kThemeBrushMenuBackgroundSelected, 0, 0 }, /* 43: MENU_ACTIVE_PIXEL */
- { "Black", kThemeBrushBlack, 0, 0 }, /* 44 */
- { "MenuActiveText", 0, kThemeTextColorMenuItemSelected, 0 }, /* 45: MENU_ACTIVE_TEXT_PIXEL */
- { "White", kThemeBrushWhite, 0, 0 }, /* 46 */
- { "Menu", kThemeBrushMenuBackground, 0, 0 }, /* 47: MENU_BACKGROUND_PIXEL */
- { "DialogBackgroundActive", kThemeBrushDialogBackgroundActive, 0, 0 }, /* 48 */
- { "MenuDisabled", 0, kThemeTextColorMenuItemDisabled, 0 }, /* 49: MENU_DISABLED_PIXEL */
- { "DialogBackgroundInactive", kThemeBrushDialogBackgroundInactive, 0, 0 }, /* 50 */
- { "MenuText", 0, kThemeTextColorMenuItemActive, 0 }, /* 51: MENU_TEXT_PIXEL */
- { "AppearanceColor", 0, 0, 0 }, /* 52: APPEARANCE_PIXEL */
- { "AlertBackgroundActive", kThemeBrushAlertBackgroundActive, 0, 0 }, /* 53 */
- { "AlertBackgroundInactive", kThemeBrushAlertBackgroundInactive, 0, 0 }, /* 54 */
- { "ModelessDialogBackgroundActive", kThemeBrushModelessDialogBackgroundActive, 0, 0 }, /* 55 */
- { "ModelessDialogBackgroundInactive", kThemeBrushModelessDialogBackgroundInactive, 0, 0 }, /* 56 */
- { "UtilityWindowBackgroundActive", kThemeBrushUtilityWindowBackgroundActive, 0, 0 }, /* 57 */
- { "UtilityWindowBackgroundInactive", kThemeBrushUtilityWindowBackgroundInactive, 0, 0 }, /* 58 */
- { "ListViewSortColumnBackground", kThemeBrushListViewSortColumnBackground, 0, 0 }, /* 59 */
- { "ListViewBackground", kThemeBrushListViewBackground, 0, 0 }, /* 60 */
- { "IconLabelBackground", kThemeBrushIconLabelBackground, 0, 0 }, /* 61 */
- { "ListViewSeparator", kThemeBrushListViewSeparator, 0, 0 }, /* 62 */
- { "ChasingArrows", kThemeBrushChasingArrows, 0, 0 }, /* 63 */
- { "DragHilite", kThemeBrushDragHilite, 0, 0 }, /* 64 */
- { "DocumentWindowBackground", kThemeBrushDocumentWindowBackground, 0, 0 }, /* 65 */
- { "FinderWindowBackground", kThemeBrushFinderWindowBackground, 0, 0 }, /* 66 */
- { "ScrollBarDelimiterActive", kThemeBrushScrollBarDelimiterActive, 0, 0 }, /* 67 */
- { "ScrollBarDelimiterInactive", kThemeBrushScrollBarDelimiterInactive, 0, 0 }, /* 68 */
- { "FocusHighlight", kThemeBrushFocusHighlight, 0, 0 }, /* 69 */
- { "PopupArrowActive", kThemeBrushPopupArrowActive, 0, 0 }, /* 70 */
- { "PopupArrowPressed", kThemeBrushPopupArrowPressed, 0, 0 }, /* 71 */
- { "PopupArrowInactive", kThemeBrushPopupArrowInactive, 0, 0 }, /* 72 */
- { "AppleGuideCoachmark", kThemeBrushAppleGuideCoachmark, 0, 0 }, /* 73 */
- { "IconLabelBackgroundSelected", kThemeBrushIconLabelBackgroundSelected, 0, 0 }, /* 74 */
- { "StaticAreaFill", kThemeBrushStaticAreaFill, 0, 0 }, /* 75 */
- { "ActiveAreaFill", kThemeBrushActiveAreaFill, 0, 0 }, /* 76 */
- { "ButtonFrameActive", kThemeBrushButtonFrameActive, 0, 0 }, /* 77 */
- { "ButtonFrameInactive", kThemeBrushButtonFrameInactive, 0, 0 }, /* 78 */
- { "ButtonFaceActive", kThemeBrushButtonFaceActive, 0, 0 }, /* 79 */
- { "ButtonFaceInactive", kThemeBrushButtonFaceInactive, 0, 0 }, /* 80 */
- { "ButtonFacePressed", kThemeBrushButtonFacePressed, 0, 0 }, /* 81 */
- { "ButtonActiveDarkShadow", kThemeBrushButtonActiveDarkShadow, 0, 0 }, /* 82 */
- { "ButtonActiveDarkHighlight", kThemeBrushButtonActiveDarkHighlight, 0, 0 }, /* 83 */
- { "ButtonActiveLightShadow", kThemeBrushButtonActiveLightShadow, 0, 0 }, /* 84 */
- { "ButtonActiveLightHighlight", kThemeBrushButtonActiveLightHighlight, 0, 0 }, /* 85 */
- { "ButtonInactiveDarkShadow", kThemeBrushButtonInactiveDarkShadow, 0, 0 }, /* 86 */
- { "ButtonInactiveDarkHighlight", kThemeBrushButtonInactiveDarkHighlight, 0, 0 }, /* 87 */
- { "ButtonInactiveLightShadow", kThemeBrushButtonInactiveLightShadow, 0, 0 }, /* 88 */
- { "ButtonInactiveLightHighlight", kThemeBrushButtonInactiveLightHighlight, 0, 0 }, /* 89 */
- { "ButtonPressedDarkShadow", kThemeBrushButtonPressedDarkShadow, 0, 0 }, /* 90 */
- { "ButtonPressedDarkHighlight", kThemeBrushButtonPressedDarkHighlight, 0, 0 }, /* 91 */
- { "ButtonPressedLightShadow", kThemeBrushButtonPressedLightShadow, 0, 0 }, /* 92 */
- { "ButtonPressedLightHighlight", kThemeBrushButtonPressedLightHighlight, 0, 0 }, /* 93 */
- { "BevelActiveLight", kThemeBrushBevelActiveLight, 0, 0 }, /* 94 */
- { "BevelActiveDark", kThemeBrushBevelActiveDark, 0, 0 }, /* 95 */
- { "BevelInactiveLight", kThemeBrushBevelInactiveLight, 0, 0 }, /* 96 */
- { "BevelInactiveDark", kThemeBrushBevelInactiveDark, 0, 0 }, /* 97 */
- { "NotificationWindowBackground", kThemeBrushNotificationWindowBackground, 0, 0 }, /* 98 */
- { "MovableModalBackground", kThemeBrushMovableModalBackground, 0, 0 }, /* 99 */
- { "SheetBackgroundOpaque", kThemeBrushSheetBackgroundOpaque, 0, 0 }, /* 100 */
- { "DrawerBackground", kThemeBrushDrawerBackground, 0, 0 }, /* 101 */
- { "ToolbarBackground", kThemeBrushToolbarBackground, 0, 0 }, /* 102 */
- { "SheetBackgroundTransparent", kThemeBrushSheetBackgroundTransparent, 0, 0 }, /* 103 */
- { "MenuBackground", kThemeBrushMenuBackground, 0, 0 }, /* 104 */
- { "Pixel", 0, 0, 0 }, /* 105: PIXEL_MAGIC */
- { "MenuBackgroundSelected", kThemeBrushMenuBackgroundSelected, 0, 0 }, /* 106 */
- { "ListViewOddRowBackground", kThemeBrushListViewOddRowBackground, 0, 0 }, /* 107 */
- { "ListViewEvenRowBackground", kThemeBrushListViewEvenRowBackground, 0, 0 }, /* 108 */
- { "ListViewColumnDivider", kThemeBrushListViewColumnDivider, 0, 0 }, /* 109 */
- { "BlackText", 0, kThemeTextColorBlack, 0 }, /* 110 */
- { "DialogActiveText", 0, kThemeTextColorDialogActive, 0 }, /* 111 */
- { "DialogInactiveText", 0, kThemeTextColorDialogInactive, 0 }, /* 112 */
- { "AlertActiveText", 0, kThemeTextColorAlertActive, 0 }, /* 113 */
- { "AlertInactiveText", 0, kThemeTextColorAlertInactive, 0 }, /* 114 */
- { "ModelessDialogActiveText", 0, kThemeTextColorModelessDialogActive, 0 }, /* 115 */
- { "ModelessDialogInactiveText", 0, kThemeTextColorModelessDialogInactive, 0 }, /* 116 */
- { "WindowHeaderActiveText", 0, kThemeTextColorWindowHeaderActive, 0 }, /* 117 */
- { "WindowHeaderInactiveText", 0, kThemeTextColorWindowHeaderInactive, 0 }, /* 118 */
- { "PlacardActiveText", 0, kThemeTextColorPlacardActive, 0 }, /* 119 */
- { "PlacardInactiveText", 0, kThemeTextColorPlacardInactive, 0 }, /* 120 */
- { "PlacardPressedText", 0, kThemeTextColorPlacardPressed, 0 }, /* 121 */
- { "PushButtonActiveText", 0, kThemeTextColorPushButtonActive, 0 }, /* 122 */
- { "PushButtonInactiveText", 0, kThemeTextColorPushButtonInactive, 0 }, /* 123 */
- { "PushButtonPressedText", 0, kThemeTextColorPushButtonPressed, 0 }, /* 124 */
- { "BevelButtonActiveText", 0, kThemeTextColorBevelButtonActive, 0 }, /* 125 */
- { "BevelButtonInactiveText", 0, kThemeTextColorBevelButtonInactive, 0 }, /* 126 */
- { "BevelButtonPressedText", 0, kThemeTextColorBevelButtonPressed, 0 }, /* 127 */
- { "PopupButtonActiveText", 0, kThemeTextColorPopupButtonActive, 0 }, /* 128 */
- { "PopupButtonInactiveText", 0, kThemeTextColorPopupButtonInactive, 0 }, /* 129 */
- { "PopupButtonPressedText", 0, kThemeTextColorPopupButtonPressed, 0 }, /* 130 */
- { "IconLabelText", 0, kThemeTextColorIconLabel, 0 }, /* 131 */
- { "ListViewText", 0, kThemeTextColorListView, 0 }, /* 132 */
- { "DocumentWindowTitleActiveText", 0, kThemeTextColorDocumentWindowTitleActive, 0 }, /* 133 */
- { "DocumentWindowTitleInactiveText", 0, kThemeTextColorDocumentWindowTitleInactive, 0 }, /* 134 */
- { "MovableModalWindowTitleActiveText", 0, kThemeTextColorMovableModalWindowTitleActive, 0 }, /* 135 */
- { "MovableModalWindowTitleInactiveText",0, kThemeTextColorMovableModalWindowTitleInactive, 0 }, /* 136 */
- { "UtilityWindowTitleActiveText", 0, kThemeTextColorUtilityWindowTitleActive, 0 }, /* 137 */
- { "UtilityWindowTitleInactiveText", 0, kThemeTextColorUtilityWindowTitleInactive, 0 }, /* 138 */
- { "PopupWindowTitleActiveText", 0, kThemeTextColorPopupWindowTitleActive, 0 }, /* 139 */
- { "PopupWindowTitleInactiveText", 0, kThemeTextColorPopupWindowTitleInactive, 0 }, /* 140 */
- { "RootMenuActiveText", 0, kThemeTextColorRootMenuActive, 0 }, /* 141 */
- { "RootMenuSelectedText", 0, kThemeTextColorRootMenuSelected, 0 }, /* 142 */
- { "RootMenuDisabledText", 0, kThemeTextColorRootMenuDisabled, 0 }, /* 143 */
- { "MenuItemActiveText", 0, kThemeTextColorMenuItemActive, 0 }, /* 144 */
- { "MenuItemSelectedText", 0, kThemeTextColorMenuItemSelected, 0 }, /* 145 */
- { "MenuItemDisabledText", 0, kThemeTextColorMenuItemDisabled, 0 }, /* 146 */
- { "PopupLabelActiveText", 0, kThemeTextColorPopupLabelActive, 0 }, /* 147 */
- { "PopupLabelInactiveText", 0, kThemeTextColorPopupLabelInactive, 0 }, /* 148 */
- { "TabFrontActiveText", 0, kThemeTextColorTabFrontActive, 0 }, /* 149 */
- { "TabNonFrontActiveText", 0, kThemeTextColorTabNonFrontActive, 0 }, /* 150 */
- { "TabNonFrontPressedText", 0, kThemeTextColorTabNonFrontPressed, 0 }, /* 151 */
- { "TabFrontInactiveText", 0, kThemeTextColorTabFrontInactive, 0 }, /* 152 */
- { "TabNonFrontInactiveText", 0, kThemeTextColorTabNonFrontInactive, 0 }, /* 153 */
- { "IconLabelSelectedText", 0, kThemeTextColorIconLabelSelected, 0 }, /* 154 */
- { "BevelButtonStickyActiveText", 0, kThemeTextColorBevelButtonStickyActive, 0 }, /* 155 */
- { "BevelButtonStickyInactiveText", 0, kThemeTextColorBevelButtonStickyInactive, 0 }, /* 156 */
- { "NotificationText", 0, kThemeTextColorNotification, 0 }, /* 157 */
- { "SystemDetailText", 0, kThemeTextColorSystemDetail, 0 }, /* 158 */
- { "WhiteText", 0, kThemeTextColorWhite, 0 }, /* 159 */
- { "TabPaneBackground", 0, 0, kThemeBackgroundTabPane }, /* 160 */
- { "PlacardBackground", 0, 0, kThemeBackgroundPlacard }, /* 161 */
- { "WindowHeaderBackground", 0, 0, kThemeBackgroundWindowHeader }, /* 162 */
- { "ListViewWindowHeaderBackground", 0, 0, kThemeBackgroundListViewWindowHeader }, /* 163 */
- { "SecondaryGroupBoxBackground", 0, 0, kThemeBackgroundSecondaryGroupBox }, /* 164 */
- { "MetalBackground", 0, 0, kThemeBackgroundMetal }, /* 165 */
- { NULL, 0, 0, 0 }
+ { "Transparent", clearColor, 0 }, /* 30: TRANSPARENT_PIXEL */
+ { "Highlight", HIBrush, kThemeBrushPrimaryHighlightColor }, /* 31 */
+ { "HighlightSecondary", HIBrush, kThemeBrushSecondaryHighlightColor }, /* 32 */
+ { "HighlightText", HIBrush, kThemeBrushBlack }, /* 33 */
+ { "HighlightAlternate", HIBrush, kThemeBrushAlternatePrimaryHighlightColor }, /* 34 */
+ { "ButtonText", HIText, kThemeTextColorPushButtonActive }, /* 35 */
+ { "PrimaryHighlightColor", HIBrush, kThemeBrushPrimaryHighlightColor }, /* 36 */
+ { "ButtonFace", HIBrush, kThemeBrushButtonFaceActive }, /* 37 */
+ { "SecondaryHighlightColor", HIBrush, kThemeBrushSecondaryHighlightColor }, /* 38 */
+ { "ButtonFrame", HIBrush, kThemeBrushButtonFrameActive }, /* 39 */
+ { "AlternatePrimaryHighlightColor", HIBrush, kThemeBrushAlternatePrimaryHighlightColor }, /* 40 */
+ { "WindowBody", HIBrush, kThemeBrushDocumentWindowBackground }, /* 41 */
+ { "SheetBackground", HIBrush, kThemeBrushSheetBackground }, /* 42 */
+ { "MenuActive", HIBrush, kThemeBrushMenuBackgroundSelected }, /* 43 */
+ { "Black", HIBrush, kThemeBrushBlack }, /* 44 */
+ { "MenuActiveText", HIText, kThemeTextColorMenuItemSelected }, /* 45 */
+ { "White", HIBrush, kThemeBrushWhite }, /* 46 */
+ { "Menu", HIBrush, kThemeBrushMenuBackground }, /* 47 */
+ { "DialogBackgroundActive", HIBrush, kThemeBrushDialogBackgroundActive }, /* 48 */
+ { "MenuDisabled", HIText, kThemeTextColorMenuItemDisabled }, /* 49 */
+ { "DialogBackgroundInactive", HIBrush, kThemeBrushDialogBackgroundInactive }, /* 50 */
+ { "MenuText", HIText, kThemeTextColorMenuItemActive }, /* 51 */
+ { "AppearanceColor", appearance, 0 }, /* 52: APPEARANCE_PIXEL */
+ { "AlertBackgroundActive", HIBrush, kThemeBrushAlertBackgroundActive }, /* 53 */
+ { "AlertBackgroundInactive", HIBrush, kThemeBrushAlertBackgroundInactive }, /* 54 */
+ { "ModelessDialogBackgroundActive", HIBrush, kThemeBrushModelessDialogBackgroundActive }, /* 55 */
+ { "ModelessDialogBackgroundInactive", HIBrush, kThemeBrushModelessDialogBackgroundInactive }, /* 56 */
+ { "UtilityWindowBackgroundActive", HIBrush, kThemeBrushUtilityWindowBackgroundActive }, /* 57 */
+ { "UtilityWindowBackgroundInactive", HIBrush, kThemeBrushUtilityWindowBackgroundInactive }, /* 58 */
+ { "ListViewSortColumnBackground", HIBrush, kThemeBrushListViewSortColumnBackground }, /* 59 */
+ { "ListViewBackground", HIBrush, kThemeBrushListViewBackground }, /* 60 */
+ { "IconLabelBackground", HIBrush, kThemeBrushIconLabelBackground }, /* 61 */
+ { "ListViewSeparator", HIBrush, kThemeBrushListViewSeparator }, /* 62 */
+ { "ChasingArrows", HIBrush, kThemeBrushChasingArrows }, /* 63 */
+ { "DragHilite", HIBrush, kThemeBrushDragHilite }, /* 64 */
+ { "DocumentWindowBackground", HIBrush, kThemeBrushDocumentWindowBackground }, /* 65 */
+ { "FinderWindowBackground", HIBrush, kThemeBrushFinderWindowBackground }, /* 66 */
+ { "ScrollBarDelimiterActive", HIBrush, kThemeBrushScrollBarDelimiterActive }, /* 67 */
+ { "ScrollBarDelimiterInactive", HIBrush, kThemeBrushScrollBarDelimiterInactive }, /* 68 */
+ { "FocusHighlight", HIBrush, kThemeBrushFocusHighlight }, /* 69 */
+ { "PopupArrowActive", HIBrush, kThemeBrushPopupArrowActive }, /* 70 */
+ { "PopupArrowPressed", HIBrush, kThemeBrushPopupArrowPressed }, /* 71 */
+ { "PopupArrowInactive", HIBrush, kThemeBrushPopupArrowInactive }, /* 72 */
+ { "AppleGuideCoachmark", HIBrush, kThemeBrushAppleGuideCoachmark }, /* 73 */
+ { "IconLabelBackgroundSelected", HIBrush, kThemeBrushIconLabelBackgroundSelected }, /* 74 */
+ { "StaticAreaFill", HIBrush, kThemeBrushStaticAreaFill }, /* 75 */
+ { "ActiveAreaFill", HIBrush, kThemeBrushActiveAreaFill }, /* 76 */
+ { "ButtonFrameActive", HIBrush, kThemeBrushButtonFrameActive }, /* 77 */
+ { "ButtonFrameInactive", HIBrush, kThemeBrushButtonFrameInactive }, /* 78 */
+ { "ButtonFaceActive", HIBrush, kThemeBrushButtonFaceActive }, /* 79 */
+ { "ButtonFaceInactive", HIBrush, kThemeBrushButtonFaceInactive }, /* 80 */
+ { "ButtonFacePressed", HIBrush, kThemeBrushButtonFacePressed }, /* 81 */
+ { "ButtonActiveDarkShadow", HIBrush, kThemeBrushButtonActiveDarkShadow }, /* 82 */
+ { "ButtonActiveDarkHighlight", HIBrush, kThemeBrushButtonActiveDarkHighlight }, /* 83 */
+ { "ButtonActiveLightShadow", HIBrush, kThemeBrushButtonActiveLightShadow }, /* 84 */
+ { "ButtonActiveLightHighlight", HIBrush, kThemeBrushButtonActiveLightHighlight }, /* 85 */
+ { "ButtonInactiveDarkShadow", HIBrush, kThemeBrushButtonInactiveDarkShadow }, /* 86 */
+ { "ButtonInactiveDarkHighlight", HIBrush, kThemeBrushButtonInactiveDarkHighlight }, /* 87 */
+ { "ButtonInactiveLightShadow", HIBrush, kThemeBrushButtonInactiveLightShadow }, /* 88 */
+ { "ButtonInactiveLightHighlight", HIBrush, kThemeBrushButtonInactiveLightHighlight }, /* 89 */
+ { "ButtonPressedDarkShadow", HIBrush, kThemeBrushButtonPressedDarkShadow }, /* 90 */
+ { "ButtonPressedDarkHighlight", HIBrush, kThemeBrushButtonPressedDarkHighlight }, /* 91 */
+ { "ButtonPressedLightShadow", HIBrush, kThemeBrushButtonPressedLightShadow }, /* 92 */
+ { "ButtonPressedLightHighlight", HIBrush, kThemeBrushButtonPressedLightHighlight }, /* 93 */
+ { "BevelActiveLight", HIBrush, kThemeBrushBevelActiveLight }, /* 94 */
+ { "BevelActiveDark", HIBrush, kThemeBrushBevelActiveDark }, /* 95 */
+ { "BevelInactiveLight", HIBrush, kThemeBrushBevelInactiveLight }, /* 96 */
+ { "BevelInactiveDark", HIBrush, kThemeBrushBevelInactiveDark }, /* 97 */
+ { "NotificationWindowBackground", HIBrush, kThemeBrushNotificationWindowBackground }, /* 98 */
+ { "MovableModalBackground", HIBrush, kThemeBrushMovableModalBackground }, /* 99 */
+ { "SheetBackgroundOpaque", HIBrush, kThemeBrushSheetBackgroundOpaque }, /* 100 */
+ { "DrawerBackground", HIBrush, kThemeBrushDrawerBackground }, /* 101 */
+ { "ToolbarBackground", HIBrush, kThemeBrushToolbarBackground }, /* 102 */
+ { "SheetBackgroundTransparent", HIBrush, kThemeBrushSheetBackgroundTransparent }, /* 103 */
+ { "MenuBackground", HIBrush, kThemeBrushMenuBackground }, /* 104 */
+ { "Pixel", rgbColor, 0 }, /* 105: PIXEL_MAGIC */
+ { "MenuBackgroundSelected", HIBrush, kThemeBrushMenuBackgroundSelected }, /* 106 */
+ { "ListViewOddRowBackground", HIBrush, kThemeBrushListViewOddRowBackground }, /* 107 */
+ { "ListViewEvenRowBackground", HIBrush, kThemeBrushListViewEvenRowBackground }, /* 108 */
+ { "ListViewColumnDivider", HIBrush, kThemeBrushListViewColumnDivider }, /* 109 */
+ { "BlackText", HIText, kThemeTextColorBlack }, /* 110 */
+ { "DialogActiveText", HIText, kThemeTextColorDialogActive }, /* 111 */
+ { "DialogInactiveText", HIText, kThemeTextColorDialogInactive }, /* 112 */
+ { "AlertActiveText", HIText, kThemeTextColorAlertActive }, /* 113 */
+ { "AlertInactiveText", HIText, kThemeTextColorAlertInactive }, /* 114 */
+ { "ModelessDialogActiveText", HIText, kThemeTextColorModelessDialogActive }, /* 115 */
+ { "ModelessDialogInactiveText", HIText, kThemeTextColorModelessDialogInactive }, /* 116 */
+ { "WindowHeaderActiveText", HIText, kThemeTextColorWindowHeaderActive }, /* 117 */
+ { "WindowHeaderInactiveText", HIText, kThemeTextColorWindowHeaderInactive }, /* 118 */
+ { "PlacardActiveText", HIText, kThemeTextColorPlacardActive }, /* 119 */
+ { "PlacardInactiveText", HIText, kThemeTextColorPlacardInactive }, /* 120 */
+ { "PlacardPressedText", HIText, kThemeTextColorPlacardPressed }, /* 121 */
+ { "PushButtonActiveText", HIText, kThemeTextColorPushButtonActive }, /* 122 */
+ { "PushButtonInactiveText", HIText, kThemeTextColorPushButtonInactive }, /* 123 */
+ { "PushButtonPressedText", HIText, kThemeTextColorPushButtonPressed }, /* 124 */
+ { "BevelButtonActiveText", HIText, kThemeTextColorBevelButtonActive }, /* 125 */
+ { "BevelButtonInactiveText", HIText, kThemeTextColorBevelButtonInactive }, /* 126 */
+ { "BevelButtonPressedText", HIText, kThemeTextColorBevelButtonPressed }, /* 127 */
+ { "PopupButtonActiveText", HIText, kThemeTextColorPopupButtonActive }, /* 128 */
+ { "PopupButtonInactiveText", HIText, kThemeTextColorPopupButtonInactive }, /* 129 */
+ { "PopupButtonPressedText", HIText, kThemeTextColorPopupButtonPressed }, /* 130 */
+ { "IconLabelText", HIText, kThemeTextColorIconLabel }, /* 131 */
+ { "ListViewText", HIText, kThemeTextColorListView }, /* 132 */
+ { "DocumentWindowTitleActiveText", HIText, kThemeTextColorDocumentWindowTitleActive }, /* 133 */
+ { "DocumentWindowTitleInactiveText", HIText, kThemeTextColorDocumentWindowTitleInactive }, /* 134 */
+ { "MovableModalWindowTitleActiveText", HIText, kThemeTextColorMovableModalWindowTitleActive }, /* 135 */
+ { "MovableModalWindowTitleInactiveText",HIText, kThemeTextColorMovableModalWindowTitleInactive }, /* 136 */
+ { "UtilityWindowTitleActiveText", HIText, kThemeTextColorUtilityWindowTitleActive }, /* 137 */
+ { "UtilityWindowTitleInactiveText", HIText, kThemeTextColorUtilityWindowTitleInactive }, /* 138 */
+ { "PopupWindowTitleActiveText", HIText, kThemeTextColorPopupWindowTitleActive }, /* 139 */
+ { "PopupWindowTitleInactiveText", HIText, kThemeTextColorPopupWindowTitleInactive }, /* 140 */
+ { "RootMenuActiveText", HIText, kThemeTextColorRootMenuActive }, /* 141 */
+ { "RootMenuSelectedText", HIText, kThemeTextColorRootMenuSelected }, /* 142 */
+ { "RootMenuDisabledText", HIText, kThemeTextColorRootMenuDisabled }, /* 143 */
+ { "MenuItemActiveText", HIText, kThemeTextColorMenuItemActive }, /* 144 */
+ { "MenuItemSelectedText", HIText, kThemeTextColorMenuItemSelected }, /* 145 */
+ { "MenuItemDisabledText", HIText, kThemeTextColorMenuItemDisabled }, /* 146 */
+ { "PopupLabelActiveText", HIText, kThemeTextColorPopupLabelActive }, /* 147 */
+ { "PopupLabelInactiveText", HIText, kThemeTextColorPopupLabelInactive }, /* 148 */
+ { "TabFrontActiveText", HIText, kThemeTextColorTabFrontActive }, /* 149 */
+ { "TabNonFrontActiveText", HIText, kThemeTextColorTabNonFrontActive }, /* 150 */
+ { "TabNonFrontPressedText", HIText, kThemeTextColorTabNonFrontPressed }, /* 151 */
+ { "TabFrontInactiveText", HIText, kThemeTextColorTabFrontInactive }, /* 152 */
+ { "TabNonFrontInactiveText", HIText, kThemeTextColorTabNonFrontInactive }, /* 153 */
+ { "IconLabelSelectedText", HIText, kThemeTextColorIconLabelSelected }, /* 154 */
+ { "BevelButtonStickyActiveText", HIText, kThemeTextColorBevelButtonStickyActive }, /* 155 */
+ { "BevelButtonStickyInactiveText", HIText, kThemeTextColorBevelButtonStickyInactive }, /* 156 */
+ { "NotificationText", HIText, kThemeTextColorNotification }, /* 157 */
+ { "SystemDetailText", HIText, kThemeTextColorSystemDetail }, /* 158 */
+ { "WhiteText", HIText, kThemeTextColorWhite }, /* 159 */
+ { "TabPaneBackground", HIBackground, kThemeBackgroundTabPane }, /* 160 */
+ { "PlacardBackground", HIBackground, kThemeBackgroundPlacard }, /* 161 */
+ { "WindowHeaderBackground", HIBackground, kThemeBackgroundWindowHeader }, /* 162 */
+ { "ListViewWindowHeaderBackground", HIBackground, kThemeBackgroundListViewWindowHeader }, /* 163 */
+ { "SecondaryGroupBoxBackground", HIBackground, kThemeBackgroundSecondaryGroupBox }, /* 164 */
+ { "MetalBackground", HIBackground, kThemeBackgroundMetal }, /* 165 */
+ { "TtkBackground", ttkBackground, 0 }, /* 166 */
+ { "TtkBackground1", ttkBackground, 1 }, /* 167 */
+ { "TtkBackground2", ttkBackground, 2 }, /* 168 */
+ { "TtkBackground3", ttkBackground, 3 }, /* 169 */
+ { "TtkBackground4", ttkBackground, 4 }, /* 170 */
+ { "TtkBackground5", ttkBackground, 5 }, /* 171 */
+ { "TtkBackground6", ttkBackground, 6 }, /* 172 */
+ { "TtkBackground7", ttkBackground, 7 }, /* 173 */
+ { "TextColor", semantic, 0 }, /* 174 */
+ { "SelectedTextColor", semantic, 1 }, /* 175 */
+ { "LabelColor", semantic, 2 }, /* 176 */
+ { "ControlTextColor", semantic, 3 }, /* 177 */
+ { "DisabledControlTextColor", semantic, 4 }, /* 178 */
+ { "TextBackgroundColor", semantic, 5 }, /* 179 */
+ { "SelectedTextBackgroundColor", semantic, 6 }, /* 180 */
+ { NULL, 0, 0 }
};
-#define MAX_PIXELCODE 165
+#define FIRST_SEMANTIC_COLOR 166
+#define MAX_PIXELCODE 180
/*
*----------------------------------------------------------------------
*
- * GetThemeFromPixelCode --
+ * GetEntryFromPixelCode --
*
- * When given a pixel code corresponding to a theme system color,
- * set one of brush, textColor or background to the corresponding
- * Appearance Mgr theme constant.
+ * Extract a SystemColorMapEntry from the table.
*
* Results:
- * Returns false if not a real pixel, true otherwise.
+ * Returns false if the code is out of bounds.
*
* Side effects:
* None.
@@ -190,36 +225,28 @@ static const struct SystemColorMapEntry systemColorMap[] = {
*----------------------------------------------------------------------
*/
-static int
-GetThemeFromPixelCode(
+static bool
+GetEntryFromPixelCode(
unsigned char code,
- ThemeBrush *brush,
- ThemeTextColor *textColor,
- ThemeBackgroundKind *background)
+ struct SystemColorMapEntry *entry)
{
if (code >= MIN_PIXELCODE && code <= MAX_PIXELCODE) {
- *brush = systemColorMap[code - MIN_PIXELCODE].brush;
- *textColor = systemColorMap[code - MIN_PIXELCODE].textColor;
- *background = systemColorMap[code - MIN_PIXELCODE].background;
+ *entry = systemColorMap[code - MIN_PIXELCODE];
+ return true;
} else {
- *brush = 0;
- *textColor = 0;
- *background = 0;
- }
- if (!*brush && !*textColor && !*background && code != PIXEL_MAGIC &&
- code != TRANSPARENT_PIXEL) {
return false;
- } else {
- return true;
}
}
/*
*----------------------------------------------------------------------
*
- * GetThemeColor --
+ * SetCGColorComponents --
*
- * Get RGB color for a given system color or pixel value.
+ * Set the components of a CGColorRef from an XColor pixel value and a
+ * system color map entry. The pixel value is only used in the case where
+ * the color is of type rgbColor. In that case the normalized XColor RGB
+ * values are copied into the CGColorRef.
*
* Results:
* OSStatus
@@ -231,59 +258,150 @@ GetThemeFromPixelCode(
*/
static OSStatus
-GetThemeColor(
+SetCGColorComponents(
+ struct SystemColorMapEntry entry,
unsigned long pixel,
- ThemeBrush brush,
- ThemeTextColor textColor,
- ThemeBackgroundKind background,
CGColorRef *c)
{
OSStatus err = noErr;
+ NSColor *bgColor, *color;
+ CGFloat rgba[4] = {0, 0, 0, 1};
+ static CGColorSpaceRef deviceRGBSpace = NULL;
+ if (!deviceRGBSpace) {
+ deviceRGBSpace = CGColorSpaceCreateDeviceRGB();
+ }
- if (brush) {
- err = ChkErr(HIThemeBrushCreateCGColor, brush, c);
- /*} else if (textColor) {
- err = ChkErr(GetThemeTextColor, textColor, 32, true, c);*/
- } else {
- CGFloat rgba[4] = {0, 0, 0, 1};
-
- switch ((pixel >> 24) & 0xff) {
- case PIXEL_MAGIC: {
- unsigned short red, green, blue;
- red = (pixel >> 16) & 0xff;
- green = (pixel >> 8) & 0xff;
- blue = (pixel ) & 0xff;
- red |= red << 8;
- green |= green << 8;
- blue |= blue << 8;
- rgba[0] = red / 65535.0;
- rgba[1] = green / 65535.0;
- rgba[2] = blue / 65535.0;
+ switch (entry.type) {
+ case HIBrush:
+ err = ChkErr(HIThemeBrushCreateCGColor, entry.value, c);
+ return err;
+ case rgbColor:
+ rgba[0] = ((pixel >> 16) & 0xff) / 255.0;
+ rgba[1] = ((pixel >> 8) & 0xff) / 255.0;
+ rgba[2] = ((pixel ) & 0xff) / 255.0;
+ break;
+ case ttkBackground:
+ bgColor = [[NSColor windowBackgroundColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+ [bgColor getComponents: rgba];
+ for (int i=0; i<3; i++) {
+ rgba[i] -= entry.value*(8.0/255.0);
+ }
+ break;
+ case semantic:
+ switch (entry.value) {
+ case 0:
+ color = [[NSColor textColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
break;
- }
- case TRANSPARENT_PIXEL:
- rgba[3] = 0.0;
+ case 1:
+ color = [[NSColor selectedTextColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
break;
+ case 2:
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101000
+ color = [[NSColor labelColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+#else
+ color = [[NSColor textColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+#endif
+ break;
+ case 3:
+ color = [[NSColor controlTextColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+ break;
+ case 4:
+ color = [[NSColor disabledControlTextColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+ break;
+ case 5:
+ color = [[NSColor textBackgroundColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+ break;
+ case 6:
+ color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+ break;
+ default:
+ if ([NSApp macMinorVersion] < 10) {
+ color = [[NSColor textColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+ } else {
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
+ color = [[NSColor labelColor] colorUsingColorSpace:
+ [NSColorSpace deviceRGBColorSpace]];
+#endif
+ break;
+ }
}
+ [color getComponents: rgba];
+ break;
+ case clearColor:
+ rgba[3] = 0.0;
+ break;
- static CGColorSpaceRef deviceRGBSpace = NULL;
- if (!deviceRGBSpace) {
- deviceRGBSpace = CGColorSpaceCreateDeviceRGB();
- }
- *c = CGColorCreate(deviceRGBSpace, rgba );
+ /*
+ * There are no HITheme functions which convert Text or background colors
+ * to CGColors. (GetThemeTextColor has been removed, and it was never
+ * possible with backgrounds.) If we get one of these we return black.
+ */
+
+ case HIText:
+ case HIBackground:
+ default:
+ break;
}
+ *c = CGColorCreate(deviceRGBSpace, rgba );
return err;
}
/*
*----------------------------------------------------------------------
*
+ * TkMacOSXInDarkMode --
+ *
+ * Tests whether the given window's NSView has a DarkAqua Appearance.
+ *
+ * Results:
+ * Returns true if the NSView is in DarkMode, false if not.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+MODULE_SCOPE Bool
+TkMacOSXInDarkMode(Tk_Window tkwin) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED < 101300
+ return false;
+#else
+ static NSAppearanceName darkAqua = @"NSAppearanceNameDarkAqua";
+ TkWindow *winPtr = (TkWindow*)tkwin;
+ NSView *view = TkMacOSXDrawableView(winPtr->privatePtr);
+ if (view && [view.effectiveAppearance.name isEqualToString:darkAqua]) {
+ return True;
+ } else {
+ return false;
+ }
+#endif
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkSetMacColor --
*
- * Creates a CGColorRef from a X style pixel value.
+ * Sets the components of a CGColorRef from an XColor pixel value.
+ * The high order byte of the pixel value is used as an index into
+ * the system color table, and then SetCGColorComponents is called
+ * with the table entry and the pixel value.
*
* Results:
- * Returns false if not a real pixel, true otherwise.
+ * Returns false if the high order byte is not a valid index, true
+ * otherwise.
*
* Side effects:
* The variable macColor is set to a new CGColorRef, the caller is
@@ -299,14 +417,9 @@ TkSetMacColor(
{
CGColorRef *color = (CGColorRef*)macColor;
OSStatus err = -1;
- ThemeBrush brush;
- ThemeTextColor textColor;
- ThemeBackgroundKind background;
-
- if (GetThemeFromPixelCode((pixel >> 24) & 0xff, &brush, &textColor,
- &background)) {
- err = ChkErr(GetThemeColor, pixel, brush, textColor, background,
- color);
+ struct SystemColorMapEntry entry;
+ if (GetEntryFromPixelCode((pixel >> 24) & 0xff, &entry)) {
+ err = ChkErr(SetCGColorComponents, entry, pixel, color);
}
return (err == noErr);
}
@@ -321,7 +434,7 @@ TkSetMacColor(
* Results:
* None resp. retained CGColorRef for CopyCachedColor()
*
- * Side effects:
+ * Side effects:M
* None.
*
*----------------------------------------------------------------------
@@ -484,48 +597,54 @@ TkMacOSXSetColorInContext(
unsigned long pixel,
CGContextRef context)
{
- OSStatus err = -1;
- CGColorRef cgColor = CopyCachedColor(gc, pixel);
- ThemeBrush brush;
- ThemeTextColor textColor;
- ThemeBackgroundKind background;
-
- if (!cgColor && GetThemeFromPixelCode((pixel >> 24) & 0xff, &brush,
- &textColor, &background)) {
- if (brush) {
- err = ChkErr(HIThemeSetFill, brush, NULL, context,
+ OSStatus err = noErr;
+ CGColorRef cgColor = nil;
+ struct SystemColorMapEntry entry;
+ CGRect rect;
+ int code = (pixel >> 24) & 0xff;
+ HIThemeBackgroundDrawInfo info = { 0, kThemeStateActive, entry.value };
+ static CGColorSpaceRef deviceRGBSpace = NULL;
+ if (!deviceRGBSpace) {
+ deviceRGBSpace = CGColorSpaceCreateDeviceRGB();
+ }
+ if (code < FIRST_SEMANTIC_COLOR) {
+ cgColor = CopyCachedColor(gc, pixel);
+ }
+ if (!cgColor && GetEntryFromPixelCode(code, &entry)) {
+ switch (entry.type) {
+ case HIBrush:
+ err = ChkErr(HIThemeSetFill, entry.value, NULL, context,
kHIThemeOrientationNormal);
if (err == noErr) {
- err = ChkErr(HIThemeSetStroke, brush, NULL, context,
+ err = ChkErr(HIThemeSetStroke, entry.value, NULL, context,
kHIThemeOrientationNormal);
}
- } else if (textColor) {
- err = ChkErr(HIThemeSetTextFill, textColor, NULL, context,
+ break;
+ case HIText:
+ err = ChkErr(HIThemeSetTextFill, entry.value, NULL, context,
kHIThemeOrientationNormal);
- } else if (background) {
- CGRect rect = CGContextGetClipBoundingBox(context);
- HIThemeBackgroundDrawInfo info = { 0, kThemeStateActive,
- background };
-
+ break;
+ case HIBackground:
+ rect = CGContextGetClipBoundingBox(context);
err = ChkErr(HIThemeApplyBackground, &rect, &info,
context, kHIThemeOrientationNormal);
+ break;
+ default:
+ err = ChkErr(SetCGColorComponents, entry, pixel, &cgColor);
+ if (err == noErr) {
+ SetCachedColor(gc, pixel, cgColor);
+ }
+ break;
}
- if (err == noErr) {
- return;
- }
- err = ChkErr(GetThemeColor, pixel, brush, textColor, background,
- &cgColor);
- if (err == noErr) {
- SetCachedColor(gc, pixel, cgColor);
- }
- } else if (!cgColor) {
- TkMacOSXDbgMsg("Ignored unknown pixel value 0x%lx", pixel);
}
if (cgColor) {
CGContextSetFillColorWithColor(context, cgColor);
CGContextSetStrokeColorWithColor(context, cgColor);
CGColorRelease(cgColor);
}
+ if (err != noErr) {
+ TkMacOSXDbgMsg("Ignored unknown pixel value 0x%lx", pixel);
+ }
}
/*
@@ -549,7 +668,7 @@ TkMacOSXSetColorInContext(
TkColor *
TkpGetColor(
Tk_Window tkwin, /* Window in which color will be used. */
- Tk_Uid name) /* Name of color to allocated (in form
+ Tk_Uid name) /* Name of color to be allocated (in form
* suitable for passing to XParseColor). */
{
Display *display = tkwin != None ? Tk_Display(tkwin) : NULL;
@@ -561,6 +680,7 @@ TkpGetColor(
* Check to see if this is a system color. Otherwise, XParseColor
* will do all the work.
*/
+
if (strncasecmp(name, "system", 6) == 0) {
Tcl_Obj *strPtr = Tcl_NewStringObj(name+6, -1);
int idx, result;
@@ -572,11 +692,8 @@ TkpGetColor(
OSStatus err;
CGColorRef c;
unsigned char pixelCode = idx + MIN_PIXELCODE;
- ThemeBrush brush = systemColorMap[idx].brush;
- ThemeTextColor textColor = systemColorMap[idx].textColor;
- ThemeBackgroundKind background = systemColorMap[idx].background;
-
- err = ChkErr(GetThemeColor, 0, brush, textColor, background, &c);
+ struct SystemColorMapEntry entry = systemColorMap[idx];
+ err = ChkErr(SetCGColorComponents, entry, 0, &c);
if (err == noErr) {
const size_t n = CGColorGetNumberOfComponents(c);
const CGFloat *rgba = CGColorGetComponents(c);
diff --git a/macosx/tkMacOSXDefault.h b/macosx/tkMacOSXDefault.h
index dd6be69..61fee2d 100644
--- a/macosx/tkMacOSXDefault.h
+++ b/macosx/tkMacOSXDefault.h
@@ -23,7 +23,9 @@
/*
* The definitions below provide symbolic names for the default colors.
* NORMAL_BG - Normal background color.
+ * NORMAL_FG - Normal foreground color.
* ACTIVE_BG - Background color when widget is active.
+ * ACTIVE_FG - Foreground color when widget is active.
* SELECT_BG - Background color for selected text.
* SELECT_FG - Foreground color for selected text.
* TROUGH - Background color for troughs in scales and scrollbars.
@@ -33,12 +35,13 @@
#define BLACK "Black"
#define WHITE "White"
-#define NORMAL_BG "systemWindowBody"
-#define ACTIVE_BG "systemButtonFacePressed"
-#define ACTIVE_FG "systemPushButtonPressedText"
-#define SELECT_BG "systemHighlight"
-#define SELECT_FG NULL
-#define INACTIVE_SELECT_BG "systemHighlightSecondary"
+#define NORMAL_BG "systemTextBackgroundColor"
+#define NORMAL_FG "systemTextColor"
+#define ACTIVE_BG "systemTextBackgroundColor"
+#define ACTIVE_FG "systemTextColor"
+#define SELECT_BG "systemSelectedTextBackgroundColor"
+#define SELECT_FG "systemSelectedTextColor"
+#define INACTIVE_SELECT_BG "systemSelectedTextBackgroundColor"
#define TROUGH "#c3c3c3"
#define INDICATOR "#b03060"
#define DISABLED "#a3a3a3"
@@ -54,7 +57,7 @@
#define DEF_CHKRAD_ACTIVE_FG_COLOR DEF_BUTTON_ACTIVE_FG_COLOR
#define DEF_BUTTON_ACTIVE_FG_MONO WHITE
/* #define DEF_BUTTON_BG_COLOR "systemButtonFace"*/
-#define DEF_BUTTON_BG_COLOR WHITE
+#define DEF_BUTTON_BG_COLOR NORMAL_BG
#define DEF_BUTTON_BG_MONO WHITE
#define DEF_BUTTON_BITMAP ""
#define DEF_BUTTON_BORDER_WIDTH "2"
@@ -64,7 +67,7 @@
#define DEF_BUTTON_DEFAULT "disabled"
#define DEF_BUTTON_DISABLED_FG_COLOR DISABLED
#define DEF_BUTTON_DISABLED_FG_MONO ""
-#define DEF_BUTTON_FG "systemButtonText"
+#define DEF_BUTTON_FG NORMAL_FG
#define DEF_CHKRAD_FG DEF_BUTTON_FG
#define DEF_BUTTON_FONT "TkDefaultFont"
#define DEF_BUTTON_HEIGHT "0"
@@ -178,12 +181,12 @@
#define DEF_ENTRY_DISABLED_FG DISABLED
#define DEF_ENTRY_EXPORT_SELECTION "1"
#define DEF_ENTRY_FONT "TkTextFont"
-#define DEF_ENTRY_FG BLACK
+#define DEF_ENTRY_FG NORMAL_FG
#define DEF_ENTRY_HIGHLIGHT_BG NORMAL_BG
#define DEF_ENTRY_HIGHLIGHT BLACK
/* #define DEF_ENTRY_HIGHLIGHT_WIDTH "3" */
#define DEF_ENTRY_HIGHLIGHT_WIDTH "3"
-#define DEF_ENTRY_INSERT_BG BLACK
+#define DEF_ENTRY_INSERT_BG NORMAL_FG
#define DEF_ENTRY_INSERT_BD_COLOR "0"
#define DEF_ENTRY_INSERT_BD_MONO "0"
#define DEF_ENTRY_INSERT_OFF_TIME "300"
@@ -237,7 +240,7 @@
#define DEF_LABELFRAME_BORDER_WIDTH "2"
#define DEF_LABELFRAME_CLASS "Labelframe"
#define DEF_LABELFRAME_RELIEF "groove"
-#define DEF_LABELFRAME_FG "systemButtonText"
+#define DEF_LABELFRAME_FG NORMAL_FG
#define DEF_LABELFRAME_FONT "TkDefaultFont"
#define DEF_LABELFRAME_TEXT ""
#define DEF_LABELFRAME_LABELANCHOR "nw"
@@ -247,14 +250,14 @@
*/
#define DEF_LISTBOX_ACTIVE_STYLE "dotbox"
-#define DEF_LISTBOX_BG_COLOR WHITE
+#define DEF_LISTBOX_BG_COLOR NORMAL_BG
#define DEF_LISTBOX_BG_MONO WHITE
#define DEF_LISTBOX_BORDER_WIDTH "1"
#define DEF_LISTBOX_CURSOR ""
#define DEF_LISTBOX_DISABLED_FG DISABLED
#define DEF_LISTBOX_EXPORT_SELECTION "1"
#define DEF_LISTBOX_FONT "TkTextFont"
-#define DEF_LISTBOX_FG BLACK
+#define DEF_LISTBOX_FG NORMAL_FG
#define DEF_LISTBOX_HEIGHT "10"
#define DEF_LISTBOX_HIGHLIGHT_BG NORMAL_BG
#define DEF_LISTBOX_HIGHLIGHT BLACK
@@ -348,7 +351,7 @@
#define DEF_MENUBUTTON_DISABLED_FG_COLOR DISABLED
#define DEF_MENUBUTTON_DISABLED_FG_MONO ""
#define DEF_MENUBUTTON_FONT "TkDefaultFont"
-#define DEF_MENUBUTTON_FG BLACK
+#define DEF_MENUBUTTON_FG NORMAL_FG
#define DEF_MENUBUTTON_HEIGHT "0"
#define DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR DEF_MENUBUTTON_BG_COLOR
#define DEF_MENUBUTTON_HIGHLIGHT_BG_MONO DEF_MENUBUTTON_BG_MONO
@@ -379,7 +382,7 @@
#define DEF_MESSAGE_BG_MONO WHITE
#define DEF_MESSAGE_BORDER_WIDTH "1"
#define DEF_MESSAGE_CURSOR ""
-#define DEF_MESSAGE_FG BLACK
+#define DEF_MESSAGE_FG NORMAL_FG
#define DEF_MESSAGE_FONT "TkDefaultFont"
#define DEF_MESSAGE_HIGHLIGHT_BG NORMAL_BG
#define DEF_MESSAGE_HIGHLIGHT BLACK
@@ -443,7 +446,7 @@
#define DEF_SCALE_CURSOR ""
#define DEF_SCALE_DIGITS "0"
#define DEF_SCALE_FONT "TkDefaultFont"
-#define DEF_SCALE_FG_COLOR BLACK
+#define DEF_SCALE_FG_COLOR NORMAL_FG
#define DEF_SCALE_FG_MONO BLACK
#define DEF_SCALE_FROM "0"
#define DEF_SCALE_HIGHLIGHT_BG_COLOR DEF_SCALE_BG_COLOR
@@ -505,14 +508,14 @@
#define DEF_TEXT_BLOCK_CURSOR "0"
#define DEF_TEXT_BORDER_WIDTH "0"
#define DEF_TEXT_CURSOR "xterm"
-#define DEF_TEXT_FG BLACK
+#define DEF_TEXT_FG NORMAL_FG
#define DEF_TEXT_EXPORT_SELECTION "1"
#define DEF_TEXT_FONT "TkFixedFont"
#define DEF_TEXT_HEIGHT "24"
#define DEF_TEXT_HIGHLIGHT_BG NORMAL_BG
#define DEF_TEXT_HIGHLIGHT BLACK
#define DEF_TEXT_HIGHLIGHT_WIDTH "3"
-#define DEF_TEXT_INSERT_BG BLACK
+#define DEF_TEXT_INSERT_BG NORMAL_FG
#define DEF_TEXT_INSERT_BD_COLOR "0"
#define DEF_TEXT_INSERT_BD_MONO "0"
#define DEF_TEXT_INSERT_OFF_TIME "300"
diff --git a/macosx/tkMacOSXInt.h b/macosx/tkMacOSXInt.h
index d942286..22d7d2c 100644
--- a/macosx/tkMacOSXInt.h
+++ b/macosx/tkMacOSXInt.h
@@ -71,6 +71,7 @@ struct TkWindowPrivate {
* gone. */
struct TkWindowPrivate *toplevel;
/* Pointer to the toplevel datastruct. */
+ CGFloat fillRGBA[4]; /* Background used by the ttk FillElement */
int flags; /* Various state see defines below. */
};
typedef struct TkWindowPrivate MacDrawable;
@@ -86,6 +87,7 @@ typedef struct TkWindowPrivate MacDrawable;
#define TK_IS_PIXMAP 0x10
#define TK_IS_BW_PIXMAP 0x20
#define TK_DO_NOT_DRAW 0x40
+#define TTK_HAS_CONTRASTING_BG 0x80
/*
* I am reserving TK_EMBEDDED = 0x100 in the MacDrawable flags
@@ -201,6 +203,7 @@ MODULE_SCOPE void TkpShiftButton(NSButton *button, NSPoint delta);
MODULE_SCOPE Bool TkpAppIsDrawing(void);
MODULE_SCOPE void TkpDisplayWindow(Tk_Window tkwin);
MODULE_SCOPE Bool TkTestAppIsDrawing(void);
+MODULE_SCOPE Bool TkMacOSXInDarkMode(Tk_Window tkwin);
/*
* Include the stubbed internal platform-specific API.
@@ -209,3 +212,12 @@ MODULE_SCOPE Bool TkTestAppIsDrawing(void);
#include "tkIntPlatDecls.h"
#endif /* _TKMACINT */
+
+/*
+ * Local Variables:
+ * mode: objc
+ * c-basic-offset: 4
+ * fill-column: 79
+ * coding: utf-8
+ * End:
+ */
diff --git a/macosx/tkMacOSXPort.h b/macosx/tkMacOSXPort.h
index bd48bd5..617abda 100644
--- a/macosx/tkMacOSXPort.h
+++ b/macosx/tkMacOSXPort.h
@@ -128,17 +128,6 @@
#define TkpSync(display)
/*
- * The following macro returns the pixel value that corresponds to the
- * RGB values in the given XColor structure.
- */
-
-#define PIXEL_MAGIC ((unsigned char) 0x69)
-#define TkpGetPixel(p) ((((((PIXEL_MAGIC << 8) \
- | (((p)->red >> 8) & 0xff)) << 8) \
- | (((p)->green >> 8) & 0xff)) << 8) \
- | (((p)->blue >> 8) & 0xff))
-
-/*
* This macro stores a representation of the window handle in a string.
*/
@@ -159,19 +148,20 @@
*/
#define TRANSPARENT_PIXEL 30
-#define HIGHLIGHT_PIXEL 31
-#define HIGHLIGHT_SECONDARY_PIXEL 32
-#define HIGHLIGHT_TEXT_PIXEL 33
-#define HIGHLIGHT_ALTERNATE_PIXEL 34
-#define CONTROL_TEXT_PIXEL 35
-#define CONTROL_BODY_PIXEL 37
-#define CONTROL_FRAME_PIXEL 39
-#define WINDOW_BODY_PIXEL 41
-#define MENU_ACTIVE_PIXEL 43
-#define MENU_ACTIVE_TEXT_PIXEL 45
-#define MENU_BACKGROUND_PIXEL 47
-#define MENU_DISABLED_PIXEL 49
-#define MENU_TEXT_PIXEL 51
#define APPEARANCE_PIXEL 52
+#define PIXEL_MAGIC ((unsigned char) 0x69)
+
+/*
+ * The following macro returns the pixel value that corresponds to the
+ * 16-bit RGB values in the given XColor structure.
+ * The format is: (PIXEL_MAGIC <<< 24) | (R << 16) | (G << 8) | B
+ * where each of R, G and B is the high order byte of a 16-bit component.
+ */
+
+#define TkpGetPixel(p) ((((((PIXEL_MAGIC << 8) \
+ | (((p)->red >> 8) & 0xff)) << 8) \
+ | (((p)->green >> 8) & 0xff)) << 8) \
+ | (((p)->blue >> 8) & 0xff))
+
#endif /* _TKMACPORT */
diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c
index a1fbbde..2b4384a 100644
--- a/macosx/tkMacOSXWm.c
+++ b/macosx/tkMacOSXWm.c
@@ -10,7 +10,7 @@
* Copyright 2001-2009, Apple Inc.
* Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
* Copyright (c) 2010 Kevin Walzer/WordTech Communications LLC.
- * Copyright (c) 2017-2018 Marc Culler.
+ * Copyright (c) 2017-2019 Marc Culler.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -311,6 +311,8 @@ static int WmWinStyle(Tcl_Interp *interp, TkWindow *winPtr,
int objc, Tcl_Obj *const objv[]);
static int WmWinTabbingId(Tcl_Interp *interp, TkWindow *winPtr,
int objc, Tcl_Obj *const objv[]);
+static int WmWinAppearance(Tcl_Interp *interp, TkWindow *winPtr,
+ int objc, Tcl_Obj *const objv[]);
static void ApplyWindowAttributeFlagChanges(TkWindow *winPtr,
NSWindow *macWindow, UInt64 oldAttributes,
int oldFlags, int create, int initial);
@@ -5498,10 +5500,10 @@ TkUnsupported1ObjCmd(
Tcl_Obj *const objv[]) /* Argument objects. */
{
static const char *const subcmds[] = {
- "style", "tabbingid", NULL
+ "style", "tabbingid", "appearance", NULL
};
enum SubCmds {
- TKMWS_STYLE, TKMWS_TABID
+ TKMWS_STYLE, TKMWS_TABID, TKMWS_APPEARANCE
};
Tk_Window tkwin = clientData;
TkWindow *winPtr;
@@ -5529,16 +5531,18 @@ TkUnsupported1ObjCmd(
sizeof(char *), "option", 0, &index) != TCL_OK) {
return TCL_ERROR;
}
- if (((enum SubCmds) index) == TKMWS_STYLE) {
+ switch((enum SubCmds) index) {
+ case TKMWS_STYLE:
if ((objc < 3) || (objc > 5)) {
Tcl_WrongNumArgs(interp, 2, objv, "window ?class attributes?");
return TCL_ERROR;
}
return WmWinStyle(interp, winPtr, objc, objv);
- } else if (((enum SubCmds) index) == TKMWS_TABID) {
+ case TKMWS_TABID:
if ([NSApp macMinorVersion] < 12) {
- Tcl_AddErrorInfo(interp,
- "\n (TabbingIdentifiers only exist on OSX 10.12 or later)");
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Tabbing identifiers did not exist until OSX 10.12.", -1));
+ Tcl_SetErrorCode(interp, "TK", "WINDOWSTYLE", "TABBINGID", NULL);
return TCL_ERROR;
}
if ((objc < 3) || (objc > 4)) {
@@ -5546,9 +5550,27 @@ TkUnsupported1ObjCmd(
return TCL_ERROR;
}
return WmWinTabbingId(interp, winPtr, objc, objv);
+ case TKMWS_APPEARANCE:
+ if ([NSApp macMinorVersion] < 9) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Window appearances did not exist until OSX 10.9.", -1));
+ Tcl_SetErrorCode(interp, "TK", "WINDOWSTYLE", "APPEARANCE", NULL);
+ return TCL_ERROR;
+ }
+ if ((objc < 3) || (objc > 4)) {
+ Tcl_WrongNumArgs(interp, 2, objv, "appearance window ?appearancename?");
+ return TCL_ERROR;
+ }
+ if (objc == 4 && [NSApp macMinorVersion] < 14) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Window appearances cannot be changed before OSX 10.14.", -1));
+ Tcl_SetErrorCode(interp, "TK", "WINDOWSTYLE", "APPEARANCE", NULL);
+ return TCL_ERROR;
+ }
+ return WmWinAppearance(interp, winPtr, objc, objv);
+ default:
+ return TCL_ERROR;
}
- /* won't be reached */
- return TCL_ERROR;
}
/*
@@ -5806,6 +5828,99 @@ WmWinTabbingId(
/*
*----------------------------------------------------------------------
*
+ * WmWinAppearance --
+ *
+ * This procedure is invoked to process the
+ * "::tk::unsupported::MacWindowStyle appearance" subcommand. The command
+ * allows you to get or set the appearance for the NSWindow
+ * associated with a Tk Window. The syntax is:
+ * tk::unsupported::MacWindowStyle tabbingid window ?newAppearance?
+ * Allowed appearance names are "aqua", "darkaqua", and "auto".
+ *
+ * Results:
+ * Returns the appearance setting of the window prior to calling this
+ * function.
+ *
+ * Side effects:
+ * The underlying NSWindow's appearance property is set to the specified
+ * value if the optional newAppearance argument is supplied. Otherwise
+ * the window's appearance property is not changed. If the appearance is
+ * set to aqua or darkaqua then the window will use the associated
+ * NSAppearance even if the user has selected a different appearance with
+ * the system preferences. If it is set to auto then the appearance
+ * property is set to nil, meaning that the preferences will determine the
+ * appearance.
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+WmWinAppearance(
+ Tcl_Interp *interp, /* Current interpreter. */
+ TkWindow *winPtr, /* Window to be manipulated. */
+ int objc, /* Number of arguments. */
+ Tcl_Obj * const objv[]) /* Argument objects. */
+{
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1090
+ static const char *const appearanceStrings[] = {
+ "aqua", "darkaqua", "auto", NULL
+ };
+ enum appearances {
+ APPEARANCE_AQUA, APPEARANCE_DARKAQUA, APPEARANCE_AUTO
+ };
+ Tcl_Obj *result = NULL;
+ NSAppearanceName appearance;
+ const char *resultString;
+ NSWindow *win = TkMacOSXDrawableWindow(winPtr->window);
+ if (win) {
+ appearance = win.appearance.name;
+ if (appearance == nil) {
+ resultString = appearanceStrings[APPEARANCE_AUTO];
+ } else if (appearance == NSAppearanceNameAqua) {
+ resultString = appearanceStrings[APPEARANCE_AQUA];
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
+ } else if (appearance == NSAppearanceNameDarkAqua) {
+ resultString = appearanceStrings[APPEARANCE_DARKAQUA];
+#endif
+ } else {
+ resultString = "unrecognized";
+ }
+ result = Tcl_NewStringObj(resultString, strlen(resultString));
+ }
+ if (result == NULL) {
+ Tcl_Panic("Failed to read appearance name.");
+ }
+ if (objc == 4) {
+ int index;
+ if (Tcl_GetIndexFromObjStruct(interp, objv[3], appearanceStrings,
+ sizeof(char *), "appearancename", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
+ switch ((enum appearances) index) {
+ case APPEARANCE_AQUA:
+ win.appearance = [NSAppearance appearanceNamed:
+ NSAppearanceNameAqua];
+ break;
+ case APPEARANCE_DARKAQUA:
+ win.appearance = [NSAppearance appearanceNamed:
+ NSAppearanceNameDarkAqua];
+ break;
+ default:
+ win.appearance = nil;
+ }
+#endif
+ }
+ Tcl_SetObjResult(interp, result);
+ return TCL_OK;
+#endif
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* TkpMakeMenuWindow --
*
* Configure the window to be either a undecorated pull-down (or pop-up)
diff --git a/macosx/ttkMacOSXTheme.c b/macosx/ttkMacOSXTheme.c
index f9611c5..02dd7e4 100644
--- a/macosx/ttkMacOSXTheme.c
+++ b/macosx/ttkMacOSXTheme.c
@@ -8,6 +8,7 @@
* Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
* Copyright 2008-2009, Apple Inc.
* Copyright 2009 Kevin Walzer/WordTech Communications LLC.
+ * Copyright 2019 Marc Culler
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
@@ -32,17 +33,18 @@
#include "tkMacOSXPrivate.h"
#include "ttk/ttkTheme.h"
-
+#include <math.h>
/*
- * Use this version in the core:
+ * Macros for handling drawing contexts.
*/
#define BEGIN_DRAWING(d) { \
- TkMacOSXDrawingContext dc; \
- if (!TkMacOSXSetupDrawingContext((d), NULL, 1, &dc)) {return;}
+ TkMacOSXDrawingContext dc; \
+ if (!TkMacOSXSetupDrawingContext((d), NULL, 1, &dc)) {return;}
#define END_DRAWING \
- TkMacOSXRestoreDrawingContext(&dc); }
+ TkMacOSXRestoreDrawingContext(&dc); }
#define HIOrientation kHIThemeOrientationNormal
+#define NoThemeMetric 0xFFFFFFFF
#ifdef __LP64__
#define RangeToFactor(maximum) (((double) (INT_MAX >> 1)) / (maximum))
@@ -50,14 +52,18 @@
#define RangeToFactor(maximum) (((double) (LONG_MAX >> 1)) / (maximum))
#endif /* __LP64__ */
+#define TTK_STATE_FIRST_TAB TTK_STATE_USER1
+#define TTK_STATE_LAST_TAB TTK_STATE_USER2
+#define TTK_TREEVIEW_STATE_SORTARROW TTK_STATE_USER1
+
/*----------------------------------------------------------------------
* +++ 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 Rect relative to the containing port.
*/
static inline CGRect BoxToRect(Drawable d, Ttk_Box b)
{
@@ -93,16 +99,932 @@ static Ttk_StateTable ThemeStateTable[] = {
*/
};
+/*
+ * 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.
+ *
+ * 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)
+{
+ SInt32 height;
+ if (heightMetric != NoThemeMetric) {
+ ChkErr(GetThemeMetric, heightMetric, &height);
+ bounds.origin.y += (bounds.size.height - height)/2;
+ bounds.size.height = height;
+ }
+ return bounds;
+}
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
/*----------------------------------------------------------------------
- * +++ Button element: Used for elements drawn with DrawThemeButton.
+ * +++ Support for contrasting background colors when GroupBoxes
+ * or Tabbed panes are nested inside each other. Early versions
+ * of macOS used ridged borders, so do not need contrasting backgrounds.
*/
/*
- * Extra margins to account for drop shadow.
+ * For systems older than 10.14, [NSColor windowBackGroundColor] generates
+ * garbage when called from this function. In 10.14 it works correctly,
+ * and must be used in order to have a background color which responds
+ * to Dark Mode. So we use this hard-wired RGBA color on the older systems
+ * which don't support Dark Mode anyway.
*/
-static Ttk_Padding ButtonMargins = {2,2,2,2};
-#define NoThemeMetric 0xFFFFFFFF
+static CGFloat windowBackground[4] = {235.0/255, 235.0/255, 235.0/255, 1.0};
+static CGFloat whiteRGBA[4] = {1.0, 1.0, 1.0, 1.0};
+static CGFloat blackRGBA[4] = {0.0, 0.0, 0.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 master, or the
+ * standard ttk window background if not. 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.
+ */
+
+static void GetBackgroundColor(
+ CGContextRef context,
+ Tk_Window tkwin,
+ int contrast,
+ CGFloat *rgba)
+{
+ TkWindow *winPtr = (TkWindow *)tkwin;
+ TkWindow *masterPtr = (TkWindow *)TkGetGeomMaster(tkwin);
+ while (masterPtr != NULL) {
+ if (masterPtr->privatePtr->flags & TTK_HAS_CONTRASTING_BG) {
+ break;
+ }
+ masterPtr = (TkWindow *)TkGetGeomMaster(masterPtr);
+ }
+ if (masterPtr) {
+ for (int i = 0; i < 4; i++) {
+ rgba[i] = masterPtr->privatePtr->fillRGBA[i];
+ }
+ } else {
+ if ([NSApp macMinorVersion] > 13) {
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *windowColor = [[NSColor windowBackgroundColor]
+ colorUsingColorSpace: deviceRGB];
+ [windowColor getComponents: rgba];
+ } else {
+ for (int i = 0; i < 4; 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;
+ }
+ } else {
+ for (int i=0; i<3; i++) {
+ rgba[i] -= 8.0/255.0;
+ }
+ }
+ winPtr->privatePtr->flags |= TTK_HAS_CONTRASTING_BG;
+ for (int i = 0; i < 4; i++) {
+ winPtr->privatePtr->fillRGBA[i] = rgba[i];
+ }
+ }
+}
+
+/*
+ * DrawGroupBox --
+ *
+ * This is a standalone drawing procedure which draws the contrasting
+ * rounded rectangular box for LabelFrames and Notebook panes.
+ */
+
+static void DrawGroupBox(
+ CGRect bounds,
+ CGContextRef context,
+ Tk_Window tkwin)
+{
+ CGPathRef path;
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *borderColor, *bgColor;
+ static CGFloat border[4] = {1.0, 1.0, 1.0, 0.25};
+ CGFloat fill[4];
+ GetBackgroundColor(context, tkwin, 1, fill);
+ bgColor = [NSColor colorWithColorSpace: deviceRGB components: fill
+ count: 4];
+ CGContextSetFillColorSpace(context, deviceRGB.CGColorSpace);
+ CGContextSetFillColorWithColor(context, bgColor.CGColor);
+ path = CGPathCreateWithRoundedRect(bounds, 4, 4, NULL);
+ CGContextClipToRect(context, bounds);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextFillPath(context);
+ borderColor = [NSColor colorWithColorSpace: deviceRGB components: border
+ count: 4];
+ CGContextSetFillColorWithColor(context, borderColor.CGColor);
+ [borderColor getComponents: fill];
+ CGContextSetRGBFillColor(context, fill[0], fill[1], fill[2], fill[3]);
+
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextReplacePathWithStrokedPath(context);
+ CGContextFillPath(context);
+ CFRelease(path);
+}
+
+/* SolidFillRoundedRectangle --
+ *
+ * Fill a rounded rectangle with a specified solid color.
+ */
+
+static void SolidFillRoundedRectangle(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat radius,
+ NSColor *color)
+{
+ CGPathRef path;
+ CGContextSetFillColorWithColor(context, color.CGColor);
+ path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextFillPath(context);
+ CFRelease(path);
+}
+
+
+/* GradientFillRoundedRectangle --
+ *
+ * Fill a rounded rectangle with a specified gradient.
+ */
+
+static void GradientFillRoundedRectangle(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat radius,
+ CGFloat* colors,
+ int numColors)
+{
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ CGPathRef path;
+ CGPoint end = {bounds.origin.x,
+ bounds.origin.y + bounds.size.height};
+ CGGradientRef gradient = CGGradientCreateWithColorComponents(
+ deviceRGB.CGColorSpace, colors, NULL, numColors);
+ path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
+ CGContextBeginPath(context);
+ CGContextAddPath(context, path);
+ CGContextClip(context);
+ CGContextDrawLinearGradient(context, gradient, bounds.origin, end, 0);
+ CFRelease(path);
+ CFRelease(gradient);
+}
+
+
+static void DrawUpDownArrows(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat inset,
+ CGFloat size,
+ CGFloat *rgba)
+{
+ CGFloat x, y;
+ CGContextSetRGBStrokeColor(context, rgba[0], rgba[1], rgba[2], rgba[3]);
+ CGContextSetLineWidth(context, 1.5);
+ 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);
+ CGContextStrokePath(context);
+}
+
+static void DrawDownArrow(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat inset,
+ CGFloat size,
+ CGFloat *rgba)
+{
+ CGFloat x, y;
+ CGContextSetRGBStrokeColor(context, rgba[0], rgba[1], rgba[2], rgba[3]);
+ 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}, {x+size/2, y+size/4}, {x+size, y-size/4}};
+ CGContextAddLines(context, arrow, 3);
+ CGContextStrokePath(context);
+}
+
+static void DrawUpArrow(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat inset,
+ CGFloat size,
+ CGFloat *rgba)
+{
+ CGFloat x, y;
+ CGContextSetRGBStrokeColor(context, rgba[0], rgba[1], rgba[2], rgba[3]);
+ 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}, {x+size/2, y-size/4}, {x+size, y+size/4}};
+ CGContextAddLines(context, arrow, 3);
+ CGContextStrokePath(context);
+}
+
+/*
+ * 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,
+ CGContextRef context,
+ Tk_Window tkwin,
+ 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};
+
+ /*
+ * 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 = TkMacOSXDrawableWindow(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];
+ CGContextSaveGState(context);
+ CGContextSetShouldAntialias(context, false);
+ CGContextSetFillColorSpace(context, deviceRGB.CGColorSpace);
+ CGContextSetStrokeColorSpace(context, deviceRGB.CGColorSpace);
+ CGContextBeginPath(context);
+ CGContextSetFillColorWithColor(context, bgColor.CGColor);
+ CGContextAddRect(context, bounds);
+ CGContextFillPath(context);
+ strokeColor = [NSColor colorWithColorSpace: deviceRGB
+ components: separatorRGBA
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
+ CGContextAddLines(context, separator, 2);
+ CGContextStrokePath(context);
+ strokeColor = [NSColor colorWithColorSpace: deviceRGB
+ components: borderRGBA
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
+ 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, blackRGBA);
+ } else if (state & TTK_STATE_SELECTED) {
+ DrawDownArrow(context, arrowBounds, 3, 8, blackRGBA);
+ }
+ }
+}
+
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED > 1080 */
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+/*----------------------------------------------------------------------
+ * +++ Drawing procedures for widgets in Apple's "Dark Mode" (10.14 and up).
+ *
+ * The HIToolbox does not support Dark Mode, and apparently never will,
+ * so to make widgets look "native" we have to provide analogues of the
+ * HITheme drawing functions to be used in DarkAqua. We continue to use
+ * HITheme in Aqua, since it understands earlier versions of the OS.
+ */
+
+/*
+ * Colors and gradients used in Dark Mode.
+ */
+
+static CGFloat darkButtonFace[4] = {112.0/255, 113.0/255, 115.0/255, 1.0};
+static CGFloat 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 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};
+
+/* FillButtonBackground --
+ *
+ * Fills a rounded rectangle with a transparent black gradient.
+ */
+
+static void FillButtonBackground(
+ CGContextRef context,
+ CGRect bounds,
+ CGFloat radius)
+{
+ CGPathRef path;
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ CGGradientRef backgroundGradient = CGGradientCreateWithColorComponents(
+ deviceRGB.CGColorSpace, darkBackgroundGradient, NULL, 2);
+ CGPoint backgroundEnd = {bounds.origin.x,
+ bounds.origin.y + bounds.size.height};
+ CGContextBeginPath(context);
+ path = CGPathCreateWithRoundedRect(bounds, radius, radius, NULL);
+ CGContextAddPath(context, path);
+ CGContextClip(context);
+ CGContextDrawLinearGradient(context, backgroundGradient,
+ bounds.origin, backgroundEnd, 0);
+ CFRelease(path);
+ CFRelease(backgroundGradient);
+}
+
+/* HighlightButtonBorder --
+ *
+ * Accent the top border of a rounded rectangle with a transparent
+ * white gradient.
+ */
+
+static void HighlightButtonBorder(
+ CGContextRef context,
+ CGRect bounds)
+{
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ CGPoint topEnd = {bounds.origin.x, bounds.origin.y + 3};
+ CGGradientRef topGradient = CGGradientCreateWithColorComponents(
+ deviceRGB.CGColorSpace, darkTopGradient, NULL, 2);
+ CGContextSaveGState(context);
+ CGContextBeginPath(context);
+ CGContextAddArc(context, bounds.origin.x + 4, bounds.origin.y + 4,
+ 4, PI, 3*PI/2, 0);
+ CGContextAddArc(context, bounds.origin.x + bounds.size.width - 4,
+ bounds.origin.y + 4, 4, 3*PI/2, 0, 0);
+ CGContextReplacePathWithStrokedPath(context);
+ CGContextClip(context);
+ CGContextDrawLinearGradient(context, topGradient, bounds.origin, topEnd, 0.0);
+ CGContextRestoreGState(context);
+ CFRelease(topGradient);
+}
+
+/*
+ * DrawDarkButton --
+ *
+ * This is a standalone drawing procedure which draws PushButtons and
+ * PopupButtons in the Dark Mode style.
+ */
+
+static void DrawDarkButton(
+ CGRect bounds,
+ ThemeButtonKind kind,
+ Ttk_State state,
+ CGContextRef context)
+{
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *faceColor;
+
+ /*
+ * To match the appearance of Apple's buttons we need to increase the
+ * height by 1 pixel.
+ */
+
+ bounds.size.height += 1;
+
+ CGContextClipToRect(context, bounds);
+ FillButtonBackground(context, bounds, 5);
+
+ /*
+ * Fill the button face with the appropriate color.
+ */
+
+ bounds = CGRectInset(bounds, 1, 1);
+ if (kind == kThemePushButton && (state & TTK_STATE_PRESSED)) {
+ GradientFillRoundedRectangle(context, bounds, 4,
+ darkSelectedGradient, 2);
+ } else {
+ if (state & TTK_STATE_DISABLED) {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkDisabledButtonFace
+ count: 4];
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkButtonFace
+ count: 4];
+ }
+ SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ }
+
+ /*
+ * If this is a popup, draw the arrow button.
+ */
+
+ if (kind == kThemePopupButton | kind == kThemeComboBox) {
+ CGRect arrowBounds = bounds;
+ arrowBounds.size.width = 16;
+ arrowBounds.origin.x += bounds.size.width - 16;
+
+ /*
+ * If the toplevel is front, paint the button blue.
+ */
+
+ 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);
+ }
+ }
+
+ HighlightButtonBorder(context, bounds);
+}
+
+/*
+ * DrawDarkIncDecButton --
+ *
+ * This is a standalone drawing procedure which draws an IncDecButton
+ * (as used in a Spinbox) in the Dark Mode style.
+ */
+
+static void DrawDarkIncDecButton(
+ CGRect bounds,
+ ThemeDrawState drawState,
+ Ttk_State state,
+ CGContextRef context)
+{
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *faceColor;
+
+ bounds = CGRectInset(bounds, 0, -1);
+ CGContextClipToRect(context, bounds);
+ FillButtonBackground(context, bounds, 6);
+
+ /*
+ * Fill the button face with the appropriate color.
+ */
+
+ 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);
+
+ /*
+ * 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;
+ }
+ CGContextClipToRect(context, clip);
+ GradientFillRoundedRectangle(context, bounds, 5,
+ darkSelectedGradient, 2);
+ CGContextRestoreGState(context);
+ }
+ DrawUpDownArrows(context, bounds, 3, 5, whiteRGBA);
+ HighlightButtonBorder(context, bounds);
+}
+
+/*
+ * DrawDarkBevelButton --
+ *
+ * This is a standalone drawing procedure which draws
+ * RoundedBevelButtons in the Dark Mode style.
+ */
+
+static void DrawDarkBevelButton(
+ CGRect bounds,
+ Ttk_State state,
+ CGContextRef context)
+{
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *faceColor;
+ CGContextClipToRect(context, bounds);
+ FillButtonBackground(context, bounds, 5);
+
+ /*
+ * Fill the button face with the appropriate color.
+ */
+
+ 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];
+ }
+ SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ HighlightButtonBorder(context, bounds);
+}
+
+/*
+ * DrawDarkCheckBox --
+ *
+ * This is a standalone drawing procedure which draws Checkboxes
+ * in the Dark Mode style.
+ */
+
+static void DrawDarkCheckBox(
+ CGRect bounds,
+ Ttk_State state,
+ CGContextRef context)
+{
+ CGRect checkbounds = {{0, bounds.size.height/2 - 8},{16, 16}};
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *stroke;
+ CGFloat x, y;
+ bounds = CGRectOffset(checkbounds, bounds.origin.x, bounds.origin.y);
+ x = bounds.origin.x;
+ y = bounds.origin.y;
+
+ CGContextClipToRect(context, bounds);
+ FillButtonBackground(context, bounds, 4);
+ bounds = CGRectInset(bounds, 1, 1);
+ if (!(state & TTK_STATE_BACKGROUND) &&
+ !(state & TTK_STATE_DISABLED) &&
+ ((state & TTK_STATE_SELECTED) || (state & TTK_STATE_ALTERNATE))) {
+ 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, stroke.CGColor);
+ }
+ if (state & TTK_STATE_SELECTED) {
+ CGContextSetLineWidth(context, 1.5);
+ CGContextBeginPath(context);
+ CGPoint check[3] = {{x+4, y+8}, {x+7, y+11}, {x+11, y+4}};
+ CGContextAddLines(context, check, 3);
+ CGContextStrokePath(context);
+ } else if (state & TTK_STATE_ALTERNATE) {
+ CGContextSetLineWidth(context, 2.0);
+ CGContextBeginPath(context);
+ CGPoint bar[2] = {{x+4, y+8}, {x+12, y+8}};
+ CGContextAddLines(context, bar, 2);
+ CGContextStrokePath(context);
+ }
+}
+
+/*
+ * DrawDarkRadioButton --
+ *
+ * This is a standalone drawing procedure which draws RadioButtons
+ * in the Dark Mode style.
+ */
+
+static void DrawDarkRadioButton(
+ CGRect bounds,
+ Ttk_State state,
+ CGContextRef context)
+{
+ CGRect checkbounds = {{0, bounds.size.height/2 - 9},{18, 18}};
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *fill;
+ CGFloat x, y;
+ bounds = CGRectOffset(checkbounds, bounds.origin.x, bounds.origin.y);
+ x = bounds.origin.x;
+ y = bounds.origin.y;
+
+ CGContextClipToRect(context, bounds);
+ FillButtonBackground(context, bounds, 9);
+ bounds = CGRectInset(bounds, 1, 1);
+ if (!(state & TTK_STATE_BACKGROUND) &&
+ !(state & TTK_STATE_DISABLED) &&
+ ((state & TTK_STATE_SELECTED) || (state & TTK_STATE_ALTERNATE))) {
+ 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, fill.CGColor);
+ }
+ if (state & TTK_STATE_SELECTED) {
+ CGContextBeginPath(context);
+ CGRect dot = {{x + 6, y + 6}, {6, 6}};
+ CGContextAddEllipseInRect(context, dot);
+ CGContextFillPath(context);
+ } else if (state & TTK_STATE_ALTERNATE) {
+ CGRect bar = {{x + 5, y + 8}, {8, 2}};
+ CGContextFillRect(context, bar);
+ }
+}
+
+/*
+ * DrawDarkTab --
+ *
+ * This is a standalone drawing procedure which draws Tabbed Pane
+ * Tabs in the Dark Mode style.
+ */
+
+static void DrawDarkTab(
+ CGRect bounds,
+ Ttk_State state,
+ CGContextRef context)
+{
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *faceColor, *stroke;
+ CGRect originalBounds= bounds;
+
+ CGContextSetLineWidth(context, 1.0);
+ CGContextClipToRect(context, bounds);
+
+ /*
+ * Extend the bounds to one or both sides so the rounded part will be
+ * clipped off.
+ */
+
+ if (!(state & TTK_STATE_FIRST_TAB)) {
+ bounds.origin.x -= 10;
+ bounds.size.width += 10;
+ }
+
+ if (!(state & TTK_STATE_LAST_TAB)) {
+ bounds.size.width += 10;
+ }
+
+ /*
+ * Fill the tab face with the appropriate color or gradient. Use a
+ * solid color if the tab is not selected, otherwise use a blue or
+ * gray gradient.
+ */
+
+ bounds = CGRectInset(bounds, 1, 1);
+ if (!(state & TTK_STATE_SELECTED)) {
+ if (state & TTK_STATE_DISABLED) {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkDisabledButtonFace
+ count: 4];
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkButtonFace
+ count: 4];
+ }
+ SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+
+ /*
+ * Draw a separator line on the left side of the tab if it
+ * not first.
+ */
+
+ if (!(state & TTK_STATE_FIRST_TAB)) {
+ CGContextSaveGState(context);
+ CGContextSetShouldAntialias(context, false);
+ stroke = [NSColor colorWithColorSpace: deviceRGB
+ components: darkTabSeparator
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, stroke.CGColor);
+ CGContextBeginPath(context);
+ CGContextMoveToPoint(context, originalBounds.origin.x,
+ originalBounds.origin.y + 1);
+ CGContextAddLineToPoint(context, originalBounds.origin.x,
+ originalBounds.origin.y + originalBounds.size.height - 1);
+ CGContextStrokePath(context);
+ CGContextRestoreGState(context);
+ }
+ } else {
+
+ /*
+ * This is the selected tab; paint it blue. If it is first, cover up
+ * the separator line drawn by the second one. (The selected tab is
+ * always drawn last.)
+ */
+
+ if ((state & TTK_STATE_FIRST_TAB) && !(state & TTK_STATE_LAST_TAB)) {
+ bounds.size.width += 1;
+ }
+ if (!(state & TTK_STATE_BACKGROUND)) {
+ GradientFillRoundedRectangle(context, bounds, 4,
+ darkSelectedGradient, 2);
+ } else {
+ faceColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkInactiveSelectedTab
+ count: 4];
+ SolidFillRoundedRectangle(context, bounds, 4, faceColor);
+ }
+ HighlightButtonBorder(context, bounds);
+ }
+}
+
+/*
+ * DrawDarkSeparator --
+ *
+ * This is a standalone drawing procedure which draws a separator widget
+ * in Dark Mode.
+ */
+
+static void DrawDarkSeparator(
+ CGRect bounds,
+ CGContextRef context,
+ Tk_Window tkwin)
+{
+ static CGFloat fill[4] = {1.0, 1.0, 1.0, 0.3};
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *fillColor = [NSColor colorWithColorSpace: deviceRGB
+ components: fill
+ count:4];
+ CGContextSetFillColorWithColor(context, fillColor.CGColor);
+ CGContextFillRect(context, bounds);
+}
+
+/*
+ * DrawDarkFrame --
+ *
+ * This is a standalone drawing procedure which draws various
+ * types of borders in Dark Mode.
+ */
+
+static void DrawDarkFrame(
+ CGRect bounds,
+ CGContextRef context,
+ HIThemeFrameKind kind)
+{
+ 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}, {x + w, y}, {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}};
+ switch(kind) {
+ case kHIThemeFrameTextFieldSquare:
+ CGContextSaveGState(context);
+ CGContextSetShouldAntialias(context, false);
+ CGContextBeginPath(context);
+ stroke = [NSColor colorWithColorSpace: deviceRGB
+ components: darkFrameTop
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, stroke.CGColor);
+ CGContextAddLines(context, topPart, 4);
+ CGContextStrokePath(context);
+ stroke = [NSColor colorWithColorSpace: deviceRGB
+ components: darkFrameBottom
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, stroke.CGColor);
+ CGContextAddLines(context, bottom, 2);
+ CGContextStrokePath(context);
+ stroke = [NSColor colorWithColorSpace: deviceRGB
+ components: darkFrameAccent
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, stroke.CGColor);
+ CGContextAddLines(context, accent, 2);
+ CGContextStrokePath(context);
+ CGContextRestoreGState(context);
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * DrawListHeader --
+ *
+ * This is a standalone drawing procedure which draws column
+ * headers for a Treeview in the Dark Mode.
+ */
+
+static void DrawDarkListHeader(
+ CGRect bounds,
+ CGContextRef context,
+ Tk_Window tkwin,
+ int state)
+{
+ 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}, {x + w, y}};
+ CGPoint bottom[2] = {{x, y + h}, {x + w, y + h}};
+ CGPoint separator[2] = {{x + w, y + 3}, {x + w, y + h - 3}};
+
+ CGContextSaveGState(context);
+ CGContextSetShouldAntialias(context, false);
+ stroke = [NSColor colorWithColorSpace: deviceRGB
+ components: darkFrameBottom
+ count: 4];
+ CGContextSetStrokeColorWithColor(context, stroke.CGColor);
+ 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;
+ 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);
+ }
+ }
+}
+
+#endif /* MAC_OS_X_VERSION_MIN_REQUIRED >101300 */
+
+/*----------------------------------------------------------------------
+ * +++ 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;
@@ -110,13 +1032,13 @@ typedef struct {
} ThemeButtonParams;
static ThemeButtonParams
- PushButtonParams = { kThemePushButton, kThemeMetricPushButtonHeight },
- CheckBoxParams = { kThemeCheckBox, kThemeMetricCheckBoxHeight },
+ PushButtonParams = { kThemePushButton, kThemeMetricPushButtonHeight },
+ CheckBoxParams = { kThemeCheckBox, kThemeMetricCheckBoxHeight },
RadioButtonParams = { kThemeRadioButton, kThemeMetricRadioButtonHeight },
- BevelButtonParams = { kThemeBevelButton, NoThemeMetric },
+ BevelButtonParams = { kThemeRoundedBevelButton, NoThemeMetric },
PopupButtonParams = { kThemePopupButton, kThemeMetricPopupButtonHeight },
- DisclosureParams = { kThemeDisclosureButton, kThemeMetricDisclosureTriangleHeight },
- ListHeaderParams = { kThemeListHeaderButton, kThemeMetricListHeaderHeight };
+ DisclosureParams = { kThemeDisclosureButton, kThemeMetricDisclosureTriangleHeight },
+ ListHeaderParams = { kThemeListHeaderButton, kThemeMetricListHeaderHeight };
static Ttk_StateTable ButtonValueTable[] = {
{ kThemeButtonMixed, TTK_STATE_ALTERNATE, 0 },
@@ -126,23 +1048,41 @@ static Ttk_StateTable ButtonValueTable[] = {
};
static Ttk_StateTable ButtonAdornmentTable[] = {
- { kThemeAdornmentDefault| kThemeAdornmentFocus,
- TTK_STATE_ALTERNATE| TTK_STATE_FOCUS, 0 },
+ { kThemeAdornmentDefault | kThemeAdornmentFocus,
+ TTK_STATE_ALTERNATE | TTK_STATE_FOCUS, 0 },
{ kThemeAdornmentDefault, TTK_STATE_ALTERNATE, 0 },
+ { kThemeAdornmentNone, TTK_STATE_ALTERNATE, 0 },
{ kThemeAdornmentFocus, TTK_STATE_FOCUS, 0 },
- { kThemeAdornmentNone, 0, 0 }
-};
+ { kThemeAdornmentNone, 0, 0 }};
/*
* computeButtonDrawInfo --
- * Fill in an appearance manager HIThemeButtonDrawInfo record.
+ * Fill in an appearance manager HIThemeButtonDrawInfo record.
*/
+
static inline HIThemeButtonDrawInfo computeButtonDrawInfo(
- ThemeButtonParams *params, Ttk_State state)
+ ThemeButtonParams *params,
+ Ttk_State state,
+ Tk_Window tkwin)
{
+ /*
+ * See ButtonElementDraw for the explanation of why we always draw
+ * PushButtons in the active state.
+ */
+
+ SInt32 HIThemeState;
+ switch (params->kind) {
+ case kThemePushButton:
+ HIThemeState = kThemeStateActive;
+ break;
+ default:
+ HIThemeState = Ttk_StateTableLookup(ThemeStateTable, state);
+ break;
+ }
+
const HIThemeButtonDrawInfo info = {
.version = 0,
- .state = Ttk_StateTableLookup(ThemeStateTable, state),
+ .state = HIThemeState,
.kind = params ? params->kind : 0,
.value = Ttk_StateTableLookup(ButtonValueTable, state),
.adornment = Ttk_StateTableLookup(ButtonAdornmentTable, state),
@@ -150,54 +1090,73 @@ static inline HIThemeButtonDrawInfo computeButtonDrawInfo(
return info;
}
-static void ButtonElementSizeNoPadding(
+static void ButtonElementMinSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
ThemeButtonParams *params = clientData;
if (params->heightMetric != NoThemeMetric) {
- SInt32 height;
+ ChkErr(GetThemeMetric, params->heightMetric, minHeight);
+
+ /*
+ * The theme height does not include the 1-pixel border around
+ * the button, although it does include the 1-pixel shadow at
+ * the bottom.
+ */
+
+ *minHeight += 2;
- ChkErr(GetThemeMetric, params->heightMetric, &height);
- *heightPtr = height;
+ /*
+ * The minwidth must be 0 to force the generic ttk code to compute
+ * the correct text layout. For example, a non-zero value will cause the
+ * text to be left justified, no matter what -anchor setting is used
+ * in the style.
+ */
+
+ *minWidth = 0;
}
}
static void ButtonElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
ThemeButtonParams *params = clientData;
- const HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, 0);
+ const HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, 0, tkwin);
static const CGRect scratchBounds = {{0, 0}, {100, 100}};
- CGRect contentBounds;
+ CGRect contentBounds, backgroundBounds;
+ int verticalPad;
- ButtonElementSizeNoPadding(
- clientData, elementRecord, tkwin,
- widthPtr, heightPtr, paddingPtr);
+ ButtonElementMinSize(clientData, elementRecord, tkwin,
+ minWidth, minHeight, paddingPtr);
/*
- * To compute internal padding, query the appearance manager
- * for the content bounds of a dummy rectangle, then use
- * the difference as the padding.
+ * Given a hypothetical bounding rectangle for a button, HIToolbox will
+ * compute a bounding rectangle for the button contents and a bounding
+ * rectangle for the button background. The background bounds are large
+ * 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
+ * to draw a button with a certain bounding rectangle it draws the button
+ * centered within the rectangle.
+ *
+ * To compute the effective padding around a button we request the
+ * content and bounding rectangles for a 100x100 button and use the
+ * padding between those. However, we symmetrize the padding on the
+ * top and bottom, because that is how the button will be drawn.
*/
+
ChkErr(HIThemeGetButtonContentBounds,
&scratchBounds, &info, &contentBounds);
-
- paddingPtr->left = CGRectGetMinX(contentBounds);
- paddingPtr->top = CGRectGetMinY(contentBounds);
- paddingPtr->right = CGRectGetMaxX(scratchBounds) - CGRectGetMaxX(contentBounds) + 1;
- paddingPtr->bottom = CGRectGetMaxY(scratchBounds) - CGRectGetMaxY(contentBounds);
-
- /*
- * Now add a little extra padding to account for drop shadows.
- * @@@ SHOULD: call GetThemeButtonBackgroundBounds() instead.
- */
-
- *paddingPtr = Ttk_AddPadding(*paddingPtr, ButtonMargins);
- *widthPtr += Ttk_PaddingWidth(ButtonMargins);
- *heightPtr += Ttk_PaddingHeight(ButtonMargins);
+ ChkErr(HIThemeGetButtonBackgroundBounds,
+ &scratchBounds, &info, &backgroundBounds);
+ paddingPtr->left = contentBounds.origin.x - backgroundBounds.origin.x;
+ paddingPtr->right = (CGRectGetMaxX(backgroundBounds) -
+ CGRectGetMaxX(contentBounds));
+ verticalPad = backgroundBounds.size.height - contentBounds.size.height;
+ paddingPtr->top = paddingPtr->bottom = verticalPad / 2;
}
static void ButtonElementDraw(
@@ -205,11 +1164,64 @@ static void ButtonElementDraw(
Drawable d, Ttk_Box b, Ttk_State state)
{
ThemeButtonParams *params = clientData;
- CGRect bounds = BoxToRect(d, Ttk_PadBox(b, ButtonMargins));
- const HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, state);
-
+ CGRect bounds = BoxToRect(d, b);
+ HIThemeButtonDrawInfo info = computeButtonDrawInfo(params, state, tkwin);
+ bounds = NormalizeButtonBounds(params->heightMetric, bounds);
+
BEGIN_DRAWING(d)
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ if (TkMacOSXInDarkMode(tkwin)) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ switch (info.kind) {
+ case kThemePushButton:
+ case kThemePopupButton:
+ DrawDarkButton(bounds, info.kind, state, dc.context);
+ break;
+ case kThemeCheckBox:
+ DrawDarkCheckBox(bounds, state, dc.context);
+ break;
+ case kThemeRadioButton:
+ DrawDarkRadioButton(bounds, state, dc.context);
+ break;
+ case kThemeRoundedBevelButton:
+ DrawDarkBevelButton(bounds, state, dc.context);
+ break;
+ default:
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
+#endif
+ } else {
+ /*
+ * Apple's PushButton and PopupButton do not change their fill color
+ * when the window is inactive. However, except in 10.7 (Lion), the
+ * color of the arrow button on a PopupButton does change. For some
+ * reason HITheme fills inactive buttons with a transparent color that
+ * allows the window background to show through, leading to
+ * inconsistent behavior. We work around this by filling behind an
+ * inactive PopupButton with a text background color before asking
+ * HIToolbox to draw it. For PushButtons, we simply draw them in the
+ * active state.
+ */
+
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
+ if (info.kind == kThemePopupButton && (state & TTK_STATE_BACKGROUND)) {
+ CGRect innerBounds = CGRectInset(bounds, 1, 1);
+ NSColor *whiteRGBA = [NSColor whiteColor];
+ SolidFillRoundedRectangle(dc.context, innerBounds, 4, whiteRGBA);
+ }
+#endif
+
+ /*
+ * A BevelButton with mixed value is drawn borderless, which does make
+ * much sense for us.
+ */
+
+ if (info.kind == kThemeRoundedBevelButton &&
+ info.value == kThemeButtonMixed) {
+ info.value = kThemeButtonOff;
+ info.state = kThemeStateInactive;
+ }
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
END_DRAWING
}
@@ -225,15 +1237,12 @@ static Ttk_ElementSpec ButtonElementSpec = {
* +++ Notebook elements.
*/
-
/* Tab position logic, c.f. ttkNotebook.c TabState() */
-#define TTK_STATE_NOTEBOOK_FIRST TTK_STATE_USER1
-#define TTK_STATE_NOTEBOOK_LAST TTK_STATE_USER2
static Ttk_StateTable TabStyleTable[] = {
- { kThemeTabFrontInactive, TTK_STATE_SELECTED|TTK_STATE_BACKGROUND},
+ { kThemeTabFrontInactive, TTK_STATE_SELECTED | TTK_STATE_BACKGROUND},
{ kThemeTabNonFrontInactive, TTK_STATE_BACKGROUND},
- { kThemeTabFrontUnavailable, TTK_STATE_DISABLED|TTK_STATE_SELECTED},
+ { kThemeTabFrontUnavailable, TTK_STATE_DISABLED | TTK_STATE_SELECTED},
{ kThemeTabNonFrontUnavailable, TTK_STATE_DISABLED},
{ kThemeTabFront, TTK_STATE_SELECTED},
{ kThemeTabNonFrontPressed, TTK_STATE_PRESSED},
@@ -241,18 +1250,16 @@ static Ttk_StateTable TabStyleTable[] = {
};
static Ttk_StateTable TabAdornmentTable[] = {
- { kHIThemeTabAdornmentNone,
- TTK_STATE_NOTEBOOK_FIRST|TTK_STATE_NOTEBOOK_LAST},
- {kHIThemeTabAdornmentTrailingSeparator, TTK_STATE_NOTEBOOK_FIRST},
- {kHIThemeTabAdornmentNone, TTK_STATE_NOTEBOOK_LAST},
- {kHIThemeTabAdornmentTrailingSeparator, 0 },
+ { kHIThemeTabAdornmentNone, TTK_STATE_FIRST_TAB | TTK_STATE_LAST_TAB},
+ { kHIThemeTabAdornmentTrailingSeparator, TTK_STATE_FIRST_TAB},
+ { kHIThemeTabAdornmentNone, TTK_STATE_LAST_TAB},
+ { kHIThemeTabAdornmentTrailingSeparator, 0 },
};
static Ttk_StateTable TabPositionTable[] = {
- { kHIThemeTabPositionOnly,
- TTK_STATE_NOTEBOOK_FIRST|TTK_STATE_NOTEBOOK_LAST},
- { kHIThemeTabPositionFirst, TTK_STATE_NOTEBOOK_FIRST},
- { kHIThemeTabPositionLast, TTK_STATE_NOTEBOOK_LAST},
+ { kHIThemeTabPositionOnly, TTK_STATE_FIRST_TAB | TTK_STATE_LAST_TAB},
+ { kHIThemeTabPositionFirst, TTK_STATE_FIRST_TAB},
+ { kHIThemeTabPositionLast, TTK_STATE_LAST_TAB},
{ kHIThemeTabPositionMiddle, 0 },
};
@@ -296,9 +1303,9 @@ static Ttk_StateTable TabPositionTable[] = {
static void TabElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
- GetThemeMetric(kThemeMetricLargeTabHeight, (SInt32 *)heightPtr);
+ GetThemeMetric(kThemeMetricLargeTabHeight, (SInt32 *)minHeight);
*paddingPtr = Ttk_MakePadding(0, 0, 0, 2);
}
@@ -317,9 +1324,16 @@ static void TabElementDraw(
.kind = kHIThemeTabKindNormal,
.position = Ttk_StateTableLookup(TabPositionTable, state),
};
-
BEGIN_DRAWING(d)
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ if (TkMacOSXInDarkMode(tkwin)) {
+ DrawDarkTab(bounds, state, dc.context);
+ } else {
+ ChkErr(HIThemeDrawTab, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
+#else
ChkErr(HIThemeDrawTab, &bounds, &info, dc.context, HIOrientation, NULL);
+#endif
END_DRAWING
}
@@ -334,9 +1348,10 @@ static Ttk_ElementSpec TabElementSpec = {
/*
* Notebook panes:
*/
+
static void PaneElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
*paddingPtr = Ttk_MakePadding(9, 5, 9, 9);
}
@@ -345,7 +1360,15 @@ static void PaneElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
{
+ TkWindow *winPtr = (TkWindow *)tkwin;
+ MacDrawable *macWin = winPtr->privatePtr;
CGRect bounds = BoxToRect(d, b);
+ bounds.origin.y -= kThemeMetricTabFrameOverlap;
+ bounds.size.height += kThemeMetricTabFrameOverlap;
+ BEGIN_DRAWING(d)
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 10800
+ DrawGroupBox(bounds, dc.context, tkwin);
+#else
HIThemeTabPaneDrawInfo info = {
.version = 1,
.state = Ttk_StateTableLookup(ThemeStateTable, state),
@@ -354,12 +1377,12 @@ static void PaneElementDraw(
.kind = kHIThemeTabKindNormal,
.adornment = kHIThemeTabPaneAdornmentNormal,
};
-
bounds.origin.y -= kThemeMetricTabFrameOverlap;
bounds.size.height += kThemeMetricTabFrameOverlap;
- BEGIN_DRAWING(d)
- ChkErr(HIThemeDrawTabPane, &bounds, &info, dc.context, HIOrientation);
+ ChkErr(HIThemeDrawTabPane, &bounds, &info, dc.context, HIOrieRectntation);
+#endif
END_DRAWING
+ [TkMacOSXDrawableView(macWin) setNeedsDisplay:YES];
}
static Ttk_ElementSpec PaneElementSpec = {
@@ -380,9 +1403,10 @@ static Ttk_ElementSpec PaneElementSpec = {
* "Maximum of 2 pixels thick" is apparently a lie;
* looks more like 4 to me with shading.
*/
+
static void GroupElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
*paddingPtr = Ttk_UniformPadding(4);
}
@@ -392,14 +1416,18 @@ static void GroupElementDraw(
Drawable d, Ttk_Box b, Ttk_State state)
{
CGRect bounds = BoxToRect(d, b);
+
+ BEGIN_DRAWING(d)
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 10800
+ DrawGroupBox(bounds, dc.context, tkwin);
+#else
const HIThemeGroupBoxDrawInfo info = {
.version = 0,
.state = Ttk_StateTableLookup(ThemeStateTable, state),
.kind = kHIThemeGroupBoxKindPrimaryOpaque,
};
-
- BEGIN_DRAWING(d)
ChkErr(HIThemeDrawGroupBox, &bounds, &info, dc.context, HIOrientation);
+#endif
END_DRAWING
}
@@ -413,23 +1441,28 @@ static Ttk_ElementSpec GroupElementSpec = {
/*----------------------------------------------------------------------
* +++ Entry element --
- * 3 pixels padding for focus rectangle
- * 2 pixels padding for EditTextFrame
+ * 3 pixels padding for focus rectangle
+ * 2 pixels padding for EditTextFrame
*/
typedef struct {
Tcl_Obj *backgroundObj;
+ Tcl_Obj *fieldbackgroundObj;
} EntryElement;
+#define ENTRY_DEFAULT_BACKGROUND "systemTextBackgroundColor"
+
static Ttk_ElementOptionSpec EntryElementOptions[] = {
{ "-background", TK_OPTION_BORDER,
- Tk_Offset(EntryElement,backgroundObj), "white" },
+ Tk_Offset(EntryElement,backgroundObj), ENTRY_DEFAULT_BACKGROUND },
+ { "-fieldbackground", TK_OPTION_BORDER,
+ Tk_Offset(EntryElement,fieldbackgroundObj), ENTRY_DEFAULT_BACKGROUND },
{0}
};
static void EntryElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
*paddingPtr = Ttk_UniformPadding(5);
}
@@ -439,29 +1472,64 @@ static void EntryElementDraw(
Drawable d, Ttk_Box b, Ttk_State state)
{
EntryElement *e = elementRecord;
- Tk_3DBorder backgroundPtr = Tk_Get3DBorderFromObj(tkwin,e->backgroundObj);
Ttk_Box inner = Ttk_PadBox(b, Ttk_UniformPadding(3));
CGRect bounds = BoxToRect(d, inner);
- const HIThemeFrameDrawInfo info = {
- .version = 0,
- .kind = kHIThemeFrameTextFieldSquare,
- .state = Ttk_StateTableLookup(ThemeStateTable, state),
- .isFocused = state & TTK_STATE_FOCUS,
- };
+ NSColor *background;
+ Tk_3DBorder backgroundPtr = NULL;
+ static char *defaultBG = ENTRY_DEFAULT_BACKGROUND;
- /*
- * Erase w/background color:
- */
- XFillRectangle(Tk_Display(tkwin), d,
- Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
- inner.x,inner.y, inner.width, inner.height);
+ if (TkMacOSXInDarkMode(tkwin)) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ BEGIN_DRAWING(d)
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ CGFloat fill[4];
+ GetBackgroundColor(dc.context, tkwin, 1, fill);
+ background = [NSColor colorWithColorSpace: deviceRGB
+ components: fill
+ count: 4];
+ CGContextSetFillColorWithColor(dc.context, background.CGColor);
+ CGContextFillRect(dc.context, bounds);
+ DrawDarkFrame(bounds, dc.context, kHIThemeFrameTextFieldSquare);
+ END_DRAWING
+#endif
+ } else {
+ const HIThemeFrameDrawInfo info = {
+ .version = 0,
+ .kind = kHIThemeFrameTextFieldSquare,
+ .state = Ttk_StateTableLookup(ThemeStateTable, state),
+ .isFocused = state & TTK_STATE_FOCUS,
+ };
- BEGIN_DRAWING(d)
- ChkErr(HIThemeDrawFrame, &bounds, &info, dc.context, HIOrientation);
+ /*
+ * Earlier versions of the Aqua theme ignored the -fieldbackground
+ * option and used the -background as if it were -fieldbackground.
+ * Here we are enabling -fieldbackground. For backwards compatibility,
+ * if -fieldbackground is set to the default color and -background
+ * is set to a different color then we use -background as -fieldbackground.
+ */
+
+ if (0 != strcmp(Tcl_GetString(e->fieldbackgroundObj), defaultBG)) {
+ backgroundPtr = Tk_Get3DBorderFromObj(tkwin,e->fieldbackgroundObj);
+ } else if (0 != strcmp(Tcl_GetString(e->backgroundObj), defaultBG)) {
+ backgroundPtr = Tk_Get3DBorderFromObj(tkwin,e->backgroundObj);
+ }
+ if (backgroundPtr != NULL) {
+ XFillRectangle(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
+ inner.x, inner.y, inner.width, inner.height);
+ }
+ BEGIN_DRAWING(d)
+ if (backgroundPtr == NULL) {
+ background = [NSColor textBackgroundColor];
+ CGContextSetFillColorWithColor(dc.context, background.CGColor);
+ CGContextFillRect(dc.context, bounds);
+ }
+ ChkErr(HIThemeDrawFrame, &bounds, &info, dc.context, HIOrientation);
+ END_DRAWING
+ }
/*if (state & TTK_STATE_FOCUS) {
ChkErr(DrawThemeFocusRect, &bounds, 1);
}*/
- END_DRAWING
}
static Ttk_ElementSpec EntryElementSpec = {
@@ -476,27 +1544,39 @@ static Ttk_ElementSpec EntryElementSpec = {
* +++ Combobox:
*
* NOTES:
- * kThemeMetricComboBoxLargeDisclosureWidth -> 17
- * Padding and margins guesstimated by trial-and-error.
+ * The HIToolbox has incomplete and inconsistent support for ComboBoxes.
+ * There is no constant available to get the height of a ComboBox with
+ * GetThemeMetric. In fact, ComboBoxes are the same (fixed) height as
+ * PopupButtons and PushButtons, but they have no shadow at the bottom.
+ * As a result, they are drawn 1 pixel above the center of the bounds
+ * rectangle rather than being centered like the other buttons. One
+ * can request background bounds for a ComboBox, and it is reported with
+ * height 23, while the actual button face, including its 1-pixel border
+ * has height 21. Attempting to request the content bounds returns a
+ * 0 x 0 rectangle. Measurement indicates that the arrow button has
+ * width 18.
+ *
+ * With no help available from HIToolbox, we have to use hard-wired
+ * constants for the padding. We shift the bounding rectangle downward
+ * by 1 pixel to account for the fact that the button is not centered.
*/
-static Ttk_Padding ComboboxPadding = { 2, 3, 17, 1 };
-static Ttk_Padding ComboboxMargins = { 3, 3, 4, 4 };
+static Ttk_Padding ComboboxPadding = {4, 2, 20, 2 };
static void ComboboxElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
- *widthPtr = 0;
- *heightPtr = 0;
- *paddingPtr = Ttk_AddPadding(ComboboxMargins, ComboboxPadding);
+ *minWidth = 24;
+ *minHeight = 23;
+ *paddingPtr = ComboboxPadding;
}
static void ComboboxElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
{
- CGRect bounds = BoxToRect(d, Ttk_PadBox(b, ComboboxMargins));
+ CGRect bounds = BoxToRect(d, b);
const HIThemeButtonDrawInfo info = {
.version = 0,
.state = Ttk_StateTableLookup(ThemeStateTable, state),
@@ -506,7 +1586,24 @@ static void ComboboxElementDraw(
};
BEGIN_DRAWING(d)
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ bounds.origin.y += 1;
+ if (TkMacOSXInDarkMode(tkwin)) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ bounds.size.height += 1;
+ DrawDarkButton(bounds, info.kind, state, dc.context);
+#endif
+ } else
+ {
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
+ if ((state & TTK_STATE_BACKGROUND) &&
+ !(state & TTK_STATE_DISABLED)) {
+ NSColor *background = [NSColor textBackgroundColor];
+ CGRect innerBounds = CGRectInset(bounds, 1, 2);
+ SolidFillRoundedRectangle(dc.context, innerBounds, 4, background);
+ }
+#endif
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
END_DRAWING
}
@@ -518,69 +1615,139 @@ static Ttk_ElementSpec ComboboxElementSpec = {
ComboboxElementDraw
};
-
-
-
-
/*----------------------------------------------------------------------
* +++ Spinbuttons.
*
- * From Apple HIG, part III, section "Controls", "The Stepper Control":
- * there should be 2 pixels of space between the stepper control
- * (AKA IncDecButton, AKA "little arrows") and the text field it modifies.
+ * From Apple HIG, part III, section "Controls", "The Stepper Control":
+ * there should be 2 pixels of space between the stepper control (AKA
+ * IncDecButton, AKA "little arrows") and the text field it modifies.
+ *
+ * Ttk expects the up and down arrows to be distinct elements but HIToolbox
+ * draws them as one widget with two different pressed states. We work
+ * around this by defining them as separate elements in the layout, but
+ * making each one have a drawing method which also draws the other one.
+ * The down button does no drawing when not pressed, and when pressed draws
+ * the entire IncDecButton in its "pressed down" state. The up button draws
+ * the entire IncDecButton when not pressed and when pressed draws the
+ * IncDecButton in its "pressed up" state. NOTE: This means that when the
+ * down button is pressed the IncDecButton will be drawn twice, first
+ * in unpressed state by the up arrow and then in "pressed down" state by
+ * the down button. The drawing must be done in that order. So the up
+ * button must be listed first in the layout.
*/
-static Ttk_Padding SpinbuttonMargins = {2,0,2,0};
-static void SpinButtonElementSize(
+static Ttk_Padding SpinbuttonMargins = {0, 0, 2, 0};
+static void SpinButtonUpElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
SInt32 s;
ChkErr(GetThemeMetric, kThemeMetricLittleArrowsWidth, &s);
- *widthPtr = s + Ttk_PaddingWidth(SpinbuttonMargins);
+ *minWidth = s + Ttk_PaddingWidth(SpinbuttonMargins);
ChkErr(GetThemeMetric, kThemeMetricLittleArrowsHeight, &s);
- *heightPtr = s + Ttk_PaddingHeight(SpinbuttonMargins);
+ *minHeight = (s + Ttk_PaddingHeight(SpinbuttonMargins)) / 2;
}
-static void SpinButtonElementDraw(
+static void SpinButtonUpElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
{
CGRect bounds = BoxToRect(d, Ttk_PadBox(b, SpinbuttonMargins));
- /* @@@ can't currently distinguish PressedUp (== Pressed) from PressedDown;
- * ignore this bit for now [see #2219588]
- */
+ int infoState;
+ bounds.size.height *= 2;
+ if (state & TTK_STATE_PRESSED) {
+ infoState = kThemeStatePressedUp;
+ } else {
+ infoState = Ttk_StateTableLookup(ThemeStateTable, state);
+ }
const HIThemeButtonDrawInfo info = {
.version = 0,
- .state = Ttk_StateTableLookup(ThemeStateTable, state & ~TTK_STATE_PRESSED),
+ .state = infoState,
.kind = kThemeIncDecButton,
.value = Ttk_StateTableLookup(ButtonValueTable, state),
.adornment = kThemeAdornmentNone,
};
-
BEGIN_DRAWING(d)
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ if (TkMacOSXInDarkMode(tkwin)) {
+ DrawDarkIncDecButton(bounds, infoState, state, dc.context);
+ } else {
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
END_DRAWING
}
-static Ttk_ElementSpec SpinButtonElementSpec = {
+static Ttk_ElementSpec SpinButtonUpElementSpec = {
TK_STYLE_VERSION_2,
sizeof(NullElement),
TtkNullElementOptions,
- SpinButtonElementSize,
- SpinButtonElementDraw
+ SpinButtonUpElementSize,
+ SpinButtonUpElementDraw
};
+static void SpinButtonDownElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
+{
+ SInt32 s;
+
+ ChkErr(GetThemeMetric, kThemeMetricLittleArrowsWidth, &s);
+ *minWidth = s + Ttk_PaddingWidth(SpinbuttonMargins);
+ ChkErr(GetThemeMetric, kThemeMetricLittleArrowsHeight, &s);
+ *minHeight = (s + Ttk_PaddingHeight(SpinbuttonMargins)) / 2;
+}
+
+static void SpinButtonDownElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ CGRect bounds = BoxToRect(d, Ttk_PadBox(b, SpinbuttonMargins));
+ int infoState = 0;
+ bounds.origin.y -= bounds.size.height;
+ bounds.size.height *= 2;
+ if (state & TTK_STATE_PRESSED) {
+ infoState = kThemeStatePressedDown;
+ } else {
+ return;
+ }
+ const HIThemeButtonDrawInfo info = {
+ .version = 0,
+ .state = infoState,
+ .kind = kThemeIncDecButton,
+ .value = Ttk_StateTableLookup(ButtonValueTable, state),
+ .adornment = kThemeAdornmentNone,
+ };
+
+ BEGIN_DRAWING(d)
+ if (TkMacOSXInDarkMode(tkwin)) {
+ DrawDarkIncDecButton(bounds, infoState, state, dc.context);
+ } else {
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
+ END_DRAWING
+}
+
+static Ttk_ElementSpec SpinButtonDownElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(NullElement),
+ TtkNullElementOptions,
+ SpinButtonDownElementSize,
+ SpinButtonDownElementDraw
+};
/*----------------------------------------------------------------------
* +++ DrawThemeTrack-based elements --
- * Progress bars and scales. (See also: <<NOTE-TRACKS>>)
+ * Progress bars and scales. (See also: <<NOTE-TRACKS>>)
+ */
+
+/*
+ * Apple does not change the appearance of a slider when the window
+ * becomes inactive. So we shouldn't either.
*/
static Ttk_StateTable ThemeTrackEnableTable[] = {
{ kThemeTrackDisabled, TTK_STATE_DISABLED, 0 },
- { kThemeTrackInactive, TTK_STATE_BACKGROUND, 0 },
+ { kThemeTrackActive, TTK_STATE_BACKGROUND, 0 },
{ kThemeTrackActive, 0, 0 }
/* { kThemeTrackNothingToScroll, ?, ? }, */
};
@@ -612,13 +1779,13 @@ static Ttk_ElementOptionSpec TrackElementOptions[] = {
static void TrackElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
TrackElementData *data = clientData;
SInt32 size = 24; /* reasonable default ... */
ChkErr(GetThemeMetric, data->thicknessMetric, &size);
- *widthPtr = *heightPtr = size;
+ *minWidth = *minHeight = size;
}
static void TrackElementDraw(
@@ -653,11 +1820,28 @@ static void TrackElementDraw(
if (info.kind == kThemeSlider) {
info.trackInfo.slider.pressState = state & TTK_STATE_PRESSED ?
kThemeThumbPressed : 0;
- info.trackInfo.slider.thumbDir = kThemeThumbPlain;
+ if (state & TTK_STATE_ALTERNATE) {
+ info.trackInfo.slider.thumbDir = kThemeThumbDownward;
+ } else {
+ info.trackInfo.slider.thumbDir = kThemeThumbPlain;
+ }
}
-
-
BEGIN_DRAWING(d)
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ if (TkMacOSXInDarkMode(tkwin)) {
+ CGRect bounds = BoxToRect(d, b);
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *trackColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkTrack
+ count: 4];
+ if (orientation == TTK_ORIENT_HORIZONTAL) {
+ bounds = CGRectInset(bounds, 1, bounds.size.height/2 - 2);
+ } else {
+ bounds = CGRectInset(bounds, bounds.size.width/2 - 3, 2);
+ }
+ SolidFillRoundedRectangle(dc.context, bounds, 2, trackColor);
+ }
+#endif
ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
END_DRAWING
}
@@ -682,9 +1866,9 @@ static Ttk_ElementSpec TrackElementSpec = {
*/
static void SliderElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
- *widthPtr = *heightPtr = 24;
+ *minWidth = *minHeight = 24;
}
static Ttk_ElementSpec SliderElementSpec = {
@@ -696,7 +1880,7 @@ static Ttk_ElementSpec SliderElementSpec = {
};
/*----------------------------------------------------------------------
- * +++ Progress bar element (new):
+ * +++ Progress bar element:
*
* @@@ NOTE: According to an older revision of the Aqua reference docs,
* @@@ the 'phase' field is between 0 and 4. Newer revisions say
@@ -727,12 +1911,12 @@ static Ttk_ElementOptionSpec PbarElementOptions[] = {
static void PbarElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
SInt32 size = 24; /* @@@ Check HIG for correct default */
ChkErr(GetThemeMetric, kThemeMetricLargeProgressBarThickness, &size);
- *widthPtr = *heightPtr = size;
+ *minWidth = *minHeight = size;
}
static void PbarElementDraw(
@@ -765,6 +1949,21 @@ static void PbarElementDraw(
};
BEGIN_DRAWING(d)
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ if (TkMacOSXInDarkMode(tkwin)) {
+ CGRect bounds = BoxToRect(d, b);
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *trackColor = [NSColor colorWithColorSpace: deviceRGB
+ components: darkTrack
+ count: 4];
+ if (orientation == TTK_ORIENT_HORIZONTAL) {
+ bounds = CGRectInset(bounds, 1, bounds.size.height/2 - 3);
+ } else {
+ bounds = CGRectInset(bounds, bounds.size.width/2 - 3, 1);
+ }
+ SolidFillRoundedRectangle(dc.context, bounds, 3, trackColor);
+ }
+#endif
ChkErr(HIThemeDrawTrack, &info, NULL, dc.context, HIOrientation);
END_DRAWING
}
@@ -778,18 +1977,248 @@ static Ttk_ElementSpec PbarElementSpec = {
};
/*----------------------------------------------------------------------
+ * +++ Scrollbar element
+ */
+
+typedef struct
+{
+ Tcl_Obj *orientObj;
+} ScrollbarElement;
+
+static Ttk_ElementOptionSpec ScrollbarElementOptions[] = {
+ { "-orient", TK_OPTION_STRING,
+ Tk_Offset(ScrollbarElement,orientObj), "horizontal" },
+ {0,0,0,0}
+};
+
+static void TroughElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
+{
+ ScrollbarElement *scrollbar = elementRecord;
+ int orientation = TTK_ORIENT_HORIZONTAL;
+ SInt32 thickness = 15;
+
+ Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+ ChkErr(GetThemeMetric, kThemeMetricScrollBarWidth, &thickness);
+ if (orientation == TTK_ORIENT_HORIZONTAL) {
+ *minHeight = thickness;
+ if ([NSApp macMinorVersion] > 7) {
+ *paddingPtr = Ttk_MakePadding(4, 4, 4, 3);
+ }
+ } else {
+ *minWidth = thickness;
+ if ([NSApp macMinorVersion] > 7) {
+ *paddingPtr = Ttk_MakePadding(4, 4, 3, 4);
+ }
+ }
+}
+
+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(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ ScrollbarElement *scrollbar = elementRecord;
+ int orientation = TTK_ORIENT_HORIZONTAL;
+ CGRect bounds = BoxToRect(d, b);
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *troughColor;
+ CGFloat *rgba = TkMacOSXInDarkMode(tkwin) ? darkTrough : lightTrough;
+ Ttk_GetOrientFromObj(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 macMinorVersion] > 8) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
+ CGContextSetFillColorWithColor(dc.context, troughColor.CGColor);
+#endif
+ } else {
+ ChkErr(HIThemeSetFill, kThemeBrushDocumentWindowBackground, NULL,
+ dc.context, HIOrientation);
+ }
+ CGContextFillRect(dc.context, bounds);
+ END_DRAWING
+}
+
+static Ttk_ElementSpec TroughElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ TroughElementSize,
+ TroughElementDraw
+};
+
+static void ThumbElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
+{
+ ScrollbarElement *scrollbar = elementRecord;
+ int orientation = TTK_ORIENT_HORIZONTAL;
+
+ Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+ if (orientation == TTK_ORIENT_HORIZONTAL) {
+ *minHeight = 8;
+ } else {
+ *minWidth = 8;
+ }
+}
+
+static void ThumbElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ ScrollbarElement *scrollbar = elementRecord;
+ int orientation = TTK_ORIENT_HORIZONTAL;
+ Ttk_GetOrientFromObj(NULL, scrollbar->orientObj, &orientation);
+
+ /*
+ * In order to make ttk scrollbars work correctly it is necessary to be
+ * able to display the thumb element at the size and location which the ttk
+ * scrollbar widget requests. The algorithm that HIToolbox uses to
+ * determine the thumb geometry from the input values of min, max, value
+ * and viewSizeis, of course, undocumented. And this turns out to be a
+ * hard reverse engineering problem. A seemingly natural algorithm is
+ * implemented below, but it does not correctly compute the same thumb
+ * geometry as HITools (which also apparently does not agree with
+ * NSScrollbar). This code uses that algorithm for older OS versions,
+ * because using HITools also handles drawing the buttons and 3D thumb used
+ * on those systems. The incorrect geometry is annoying but not unusable.
+ * For newer systems the cleanest approach is to just draw the thumb
+ * directly.
+ */
+
+ if ([NSApp macMinorVersion] > 8) {
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
+ CGRect thumbBounds = BoxToRect(d, b);
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *thumbColor;
+ CGFloat *rgba;
+ if ((orientation == TTK_ORIENT_HORIZONTAL &&
+ thumbBounds.size.width >= Tk_Width(tkwin) - 8) ||
+ (orientation == TTK_ORIENT_VERTICAL &&
+ thumbBounds.size.height >= Tk_Height(tkwin) - 8)) {
+ return;
+ }
+ int isDark = TkMacOSXInDarkMode(tkwin);
+ if ((state & TTK_STATE_PRESSED) ||
+ (state & TTK_STATE_HOVER) ) {
+ rgba = isDark ? darkActiveThumb : lightActiveThumb;
+ } else {
+ rgba = isDark ? darkInactiveThumb : lightInactiveThumb;
+ }
+ thumbColor = [NSColor colorWithColorSpace: deviceRGB
+ components: rgba
+ count: 4];
+ BEGIN_DRAWING(d)
+ SolidFillRoundedRectangle(dc.context, thumbBounds, 4, thumbColor);
+ END_DRAWING
+#endif
+ } else {
+ double thumbSize, trackSize, visibleSize, viewSize;
+ MacDrawable *macWin = (MacDrawable *) Tk_WindowId(tkwin);
+ CGRect troughBounds = {{macWin->xOff, macWin->yOff},
+ {Tk_Width(tkwin), Tk_Height(tkwin)}};
+
+ /*
+ * The info struct has integer fields, which will be converted to
+ * floats in the drawing routine. All of values provided in the info
+ * struct, namely min, max, value, and viewSize are only defined up to
+ * an arbitrary scale factor. To avoid roundoff error we scale so that
+ * the viewSize is a large float which is smaller than the largest int.
+ */
+
+ viewSize = RangeToFactor(100.0);
+ HIThemeTrackDrawInfo info = {
+ .version = 0,
+ .bounds = troughBounds,
+ .min = 0,
+ .attributes = kThemeTrackShowThumb | kThemeTrackThumbRgnIsNotGhost,
+ .enableState = kThemeTrackActive
+ };
+ info.trackInfo.scrollbar.viewsize = viewSize*.8;
+ if (orientation == TTK_ORIENT_HORIZONTAL) {
+ trackSize = troughBounds.size.width;
+ thumbSize = b.width;
+ visibleSize = (thumbSize / trackSize) * viewSize;
+ info.max = viewSize - visibleSize;
+ info.value = info.max * (b.x / (trackSize - thumbSize));
+ } else {
+ thumbSize = b.height;
+ trackSize = troughBounds.size.height;
+ visibleSize = (thumbSize / trackSize) * viewSize;
+ info.max = viewSize - visibleSize;
+ info.value = info.max * (b.y / (trackSize - thumbSize));
+ }
+ if ((state & TTK_STATE_PRESSED) ||
+ (state & TTK_STATE_HOVER) ) {
+ info.trackInfo.scrollbar.pressState = kThemeThumbPressed;
+ } else {
+ info.trackInfo.scrollbar.pressState = 0;
+ }
+ if (orientation == TTK_ORIENT_HORIZONTAL) {
+ info.attributes |= kThemeTrackHorizontal;
+ } else {
+ info.attributes &= ~kThemeTrackHorizontal;
+ }
+ BEGIN_DRAWING(d)
+ HIThemeDrawTrack (&info, 0, dc.context, kHIThemeOrientationNormal);
+ END_DRAWING
+ }
+}
+
+static Ttk_ElementSpec ThumbElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ ThumbElementSize,
+ ThumbElementDraw
+};
+
+static void ArrowElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
+{
+ if ([NSApp macMinorVersion] < 8) {
+ *minHeight = *minWidth = 14;
+ } else {
+ *minHeight = *minWidth = -1;
+ }
+}
+
+static Ttk_ElementSpec ArrowElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(ScrollbarElement),
+ ScrollbarElementOptions,
+ ArrowElementSize,
+ TtkNullElementDraw
+};
+
+/*----------------------------------------------------------------------
* +++ Separator element.
*
- * DrawThemeSeparator() guesses the orientation of the line from
- * the width and height of the rectangle, so the same element can
- * can be used for horizontal, vertical, and general separators.
+ * DrawThemeSeparator() guesses the orientation of the line from the width
+ * and height of the rectangle, so the same element can can be used for
+ * horizontal, vertical, and general separators.
*/
static void SeparatorElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
- *widthPtr = *heightPtr = 1;
+ *minWidth = *minHeight = 1;
}
static void SeparatorElementDraw(
@@ -804,7 +2233,15 @@ static void SeparatorElementDraw(
};
BEGIN_DRAWING(d)
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 101300
+ if (TkMacOSXInDarkMode(tkwin)) {
+ DrawDarkSeparator(bounds, dc.context, tkwin);
+ } else {
+ ChkErr(HIThemeDrawSeparator, &bounds, &info, dc.context, HIOrientation);
+ }
+#else
ChkErr(HIThemeDrawSeparator, &bounds, &info, dc.context, HIOrientation);
+#endif
END_DRAWING
}
@@ -819,12 +2256,13 @@ static Ttk_ElementSpec SeparatorElementSpec = {
/*----------------------------------------------------------------------
* +++ Size grip element.
*/
+
static const ThemeGrowDirection sizegripGrowDirection
= kThemeGrowRight|kThemeGrowDown;
static void SizegripElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
HIThemeGrowBoxDrawInfo info = {
.version = 0,
@@ -836,8 +2274,8 @@ static void SizegripElementSize(
CGRect bounds = CGRectZero;
ChkErr(HIThemeGetGrowBoxBounds, &bounds.origin, &info, &bounds);
- *widthPtr = bounds.size.width;
- *heightPtr = bounds.size.height;
+ *minWidth = bounds.size.width;
+ *minHeight = bounds.size.height;
}
static void SizegripElementDraw(
@@ -870,38 +2308,81 @@ static Ttk_ElementSpec SizegripElementSpec = {
/*----------------------------------------------------------------------
* +++ Background and fill elements.
*
- * This isn't quite right: In Aqua, the correct background for
- * a control depends on what kind of container it belongs to,
- * and the type of the top-level window.
+ * Before drawing any ttk widget, its bounding rectangle is filled with a
+ * background color. This color must match the background color of the
+ * containing widget to avoid looking ugly. The need for care when doing
+ * this is exacerbated by the fact that ttk enforces its "native look" by
+ * not allowing user control of the background or highlight colors of ttk
+ * widgets.
*
- * Also: patterned backgrounds should be aligned with the coordinate
- * system of the top-level window. If we're drawing into an
- * off-screen graphics port this leads to alignment glitches.
+ * This job is made more complicated in recent versions of macOS by the fact
+ * that the Appkit GroupBox (used for ttk LabelFrames) and TabbedPane (used
+ * for the Notebook widget) both place their content inside a rectangle with
+ * rounded corners that has a color which contrasts with the dialog
+ * background color. Moreover, although the Apple human interface
+ * guidelines recommend against doing so, there are times when one wants to
+ * nest these widgets, for example having a GroupBox inside of a TabbedPane.
+ * To have the right contrast, each level of nesting requires a different
+ * color.
+ *
+ * Previous Tk releases used the HIThemeDrawGroupBox routine to draw
+ * GroupBoxes and TabbedPanes. This meant that the best that could be done
+ * was to set the GroupBox to be of kind kHIThemeGroupBoxKindPrimaryOpaque,
+ * and set its fill color to be the system background color. If widgets
+ * inside the box were drawn with the system background color the
+ * backgrounds would match. But this produces a GroupBox with no contrast,
+ * the only visual clue being a faint highlighting around the top of the
+ * GroupBox. Moreover, the TabbedPane does not have an Opaque version, so
+ * while it is drawn inside a contrasting rounded rectangle, the widgets
+ * inside the pane needed to be enclosed in a frame with the system
+ * background color. This added a visual artifact since the frame's
+ * background color does not match the Pane's background color. That code
+ * has now been replaced with the standalone drawing procedure
+ * macOSXDrawGroupBox, which draws a rounded rectangle with an appropriate
+ * contrasting background color.
+ *
+ * Patterned backgrounds, which are now obsolete, should be aligned with the
+ * coordinate system of the top-level window. Apparently failing to do this
+ * used to cause graphics anomalies when drawing into an off-screen graphics
+ * port. The code for handling this is currently commented out.
*/
+
static void FillElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
{
CGRect bounds = BoxToRect(d, b);
+#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
+ NSColorSpace *deviceRGB = [NSColorSpace deviceRGBColorSpace];
+ NSColor *bgColor;
+ CGFloat fill[4];
+ 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, bgColor.CGColor);
+ CGContextFillRect(dc.context, bounds);
+ END_DRAWING
+#else
ThemeBrush brush = (state & TTK_STATE_BACKGROUND)
? kThemeBrushModelessDialogBackgroundInactive
: kThemeBrushModelessDialogBackgroundActive;
-
BEGIN_DRAWING(d)
ChkErr(HIThemeSetFill, brush, NULL, dc.context, HIOrientation);
//QDSetPatternOrigin(PatternOrigin(tkwin, d));
CGContextFillRect(dc.context, bounds);
END_DRAWING
+#endif
}
static void BackgroundElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, unsigned int state)
{
- FillElementDraw(
- clientData, elementRecord, tkwin,
- d, Ttk_WinBox(tkwin), state);
+ FillElementDraw(clientData, elementRecord, tkwin, d, Ttk_WinBox(tkwin),
+ state);
}
static Ttk_ElementSpec FillElementSpec = {
@@ -923,16 +2404,17 @@ static Ttk_ElementSpec BackgroundElementSpec = {
/*----------------------------------------------------------------------
* +++ ToolbarBackground element -- toolbar style for frames.
*
- * This is very similar to the normal background element, but uses a
- * different ThemeBrush in order to get the lighter pinstripe effect
- * used in toolbars. We use SetThemeBackground() rather than
- * ApplyThemeBackground() in order to get the right style.
+ * This is very similar to the normal background element, but uses a
+ * different ThemeBrush in order to get the lighter pinstripe effect
+ * used in toolbars. We use SetThemeBackground() rather than
+ * ApplyThemeBackground() in order to get the right style.
*
* <URL: http://developer.apple.com/documentation/Carbon/Reference/
- * Appearance_Manager/appearance_manager/constant_7.html#/
- * /apple_ref/doc/uid/TP30000243/C005321>
+ * Appearance_Manager/appearance_manager/constant_7.html#/
+ * /apple_ref/doc/uid/TP30000243/C005321>
*
*/
+
static void ToolbarBackgroundElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
@@ -956,11 +2438,45 @@ static Ttk_ElementSpec ToolbarBackgroundElementSpec = {
};
/*----------------------------------------------------------------------
+ * +++ Field element:
+ * Used for the Treeview widget. This is like the BackgroundElement
+ * except that the fieldbackground color is configureable.
+ */
+
+typedef struct {
+ Tcl_Obj *backgroundObj;
+} FieldElement;
+
+static Ttk_ElementOptionSpec FieldElementOptions[] = {
+ { "-fieldbackground", TK_OPTION_BORDER,
+ Tk_Offset(FieldElement, backgroundObj), "white" },
+ { NULL, 0, 0, NULL }
+};
+
+static void FieldElementDraw(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ Drawable d, Ttk_Box b, Ttk_State state)
+{
+ FieldElement *e = elementRecord;
+ Tk_3DBorder backgroundPtr = Tk_Get3DBorderFromObj(tkwin,e->backgroundObj);
+ XFillRectangle(Tk_Display(tkwin), d,
+ Tk_3DBorderGC(tkwin, backgroundPtr, TK_3D_FLAT_GC),
+ b.x, b.y, b.width, b.height);
+}
+
+static Ttk_ElementSpec FieldElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(FieldElement),
+ FieldElementOptions,
+ TtkNullElementSize,
+ FieldElementDraw
+};
+
+/*----------------------------------------------------------------------
* +++ Treeview header
- * Redefine the header to use a kThemeListHeaderButton.
+ * Redefine the header to use a kThemeListHeaderButton.
*/
-#define TTK_TREEVIEW_STATE_SORTARROW TTK_STATE_USER1
static Ttk_StateTable TreeHeaderValueTable[] = {
{ kThemeButtonOn, TTK_STATE_ALTERNATE},
{ kThemeButtonOn, TTK_STATE_SELECTED},
@@ -977,6 +2493,41 @@ static Ttk_StateTable TreeHeaderAdornmentTable[] = {
{ kThemeAdornmentNone, 0}
};
+static void TreeAreaElementSize (
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
+{
+ /*
+ * Padding is needed to get the heading text to align correctly, since the
+ * widget expects the heading to be the same height as a row.
+ */
+
+ if ([NSApp macMinorVersion] > 8) {
+ paddingPtr->top = 4;
+ }
+}
+
+static Ttk_ElementSpec TreeAreaElementSpec = {
+ TK_STYLE_VERSION_2,
+ sizeof(NullElement),
+ TtkNullElementOptions,
+ TreeAreaElementSize,
+ TtkNullElementDraw
+};
+
+static void TreeHeaderElementSize(
+ void *clientData, void *elementRecord, Tk_Window tkwin,
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
+{
+ if ([NSApp macMinorVersion] > 8) {
+ *minHeight = 24;
+
+ } else {
+ ButtonElementSize(clientData, elementRecord, tkwin, minWidth,
+ minHeight, paddingPtr);
+ }
+}
+
static void TreeHeaderElementDraw(
void *clientData, void *elementRecord, Tk_Window tkwin,
Drawable d, Ttk_Box b, Ttk_State state)
@@ -992,7 +2543,22 @@ static void TreeHeaderElementDraw(
};
BEGIN_DRAWING(d)
- ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ if ([NSApp macMinorVersion] > 8) {
+
+ /*
+ * Compensate for the padding added in TreeHeaderElementSize, so
+ * the larger heading will be drawn at the top of the widget.
+ */
+
+ bounds.origin.y -= 4;
+ if (TkMacOSXInDarkMode(tkwin)) {
+ DrawDarkListHeader(bounds, dc.context, tkwin, state);
+ } else {
+ DrawListHeader(bounds, dc.context, tkwin, state);
+ }
+ } else {
+ ChkErr(HIThemeDrawButton, &bounds, &info, dc.context, HIOrientation, NULL);
+ }
END_DRAWING
}
@@ -1000,13 +2566,14 @@ static Ttk_ElementSpec TreeHeaderElementSpec = {
TK_STYLE_VERSION_2,
sizeof(NullElement),
TtkNullElementOptions,
- ButtonElementSizeNoPadding,
+ TreeHeaderElementSize,
TreeHeaderElementDraw
};
/*
* Disclosure triangle:
*/
+
#define TTK_TREEVIEW_STATE_OPEN TTK_STATE_USER1
#define TTK_TREEVIEW_STATE_LEAF TTK_STATE_USER2
static Ttk_StateTable DisclosureValueTable[] = {
@@ -1016,14 +2583,14 @@ static Ttk_StateTable DisclosureValueTable[] = {
static void DisclosureElementSize(
void *clientData, void *elementRecord, Tk_Window tkwin,
- int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr)
+ int *minWidth, int *minHeight, Ttk_Padding *paddingPtr)
{
SInt32 s;
ChkErr(GetThemeMetric, kThemeMetricDisclosureTriangleWidth, &s);
- *widthPtr = s;
+ *minWidth = s;
ChkErr(GetThemeMetric, kThemeMetricDisclosureTriangleHeight, &s);
- *heightPtr = s;
+ *minHeight = s;
}
static void DisclosureElementDraw(
@@ -1031,10 +2598,12 @@ static void DisclosureElementDraw(
Drawable d, Ttk_Box b, Ttk_State state)
{
if (!(state & TTK_TREEVIEW_STATE_LEAF)) {
+ int triangleState = TkMacOSXInDarkMode(tkwin) ?
+ kThemeStateInactive : kThemeStateActive;
CGRect bounds = BoxToRect(d, b);
const HIThemeButtonDrawInfo info = {
.version = 0,
- .state = Ttk_StateTableLookup(ThemeStateTable, state),
+ .state = triangleState,
.kind = kThemeDisclosureTriangle,
.value = Ttk_StateTableLookup(DisclosureValueTable, state),
.adornment = kThemeAdornmentDrawIndicatorOnly,
@@ -1084,9 +2653,9 @@ TTK_LAYOUT("TMenubutton",
TTK_NODE("Menubutton.label", TTK_PACK_LEFT))))
TTK_LAYOUT("TCombobox",
- TTK_GROUP("Combobox.button", TTK_PACK_TOP|TTK_FILL_X,
+ TTK_GROUP("Combobox.button", TTK_FILL_BOTH,
TTK_GROUP("Combobox.padding", TTK_FILL_BOTH,
- TTK_NODE("Combobox.textarea", TTK_FILL_X))))
+ TTK_NODE("Combobox.textarea", TTK_FILL_BOTH))))
/* Notebook tabs -- no focus ring */
TTK_LAYOUT("Tab",
@@ -1094,20 +2663,29 @@ TTK_LAYOUT("Tab",
TTK_GROUP("Notebook.padding", TTK_EXPAND|TTK_FILL_BOTH,
TTK_NODE("Notebook.label", TTK_EXPAND|TTK_FILL_BOTH))))
-/* Progress bars -- track only */
-TTK_LAYOUT("TSpinbox",
- TTK_NODE("Spinbox.spinbutton", TTK_PACK_RIGHT|TTK_STICK_E)
- TTK_GROUP("Spinbox.field", TTK_EXPAND|TTK_FILL_X,
- TTK_NODE("Spinbox.textarea", TTK_EXPAND|TTK_FILL_X)))
+/* Spinbox -- buttons 2px to the right of the field. */
+ TTK_LAYOUT("TSpinbox",
+ TTK_GROUP("Spinbox.buttons", TTK_PACK_RIGHT,
+ TTK_NODE("Spinbox.uparrow", TTK_PACK_TOP|TTK_STICK_E)
+ TTK_NODE("Spinbox.downarrow", TTK_PACK_BOTTOM|TTK_STICK_E))
+ TTK_GROUP("Spinbox.field", TTK_EXPAND|TTK_FILL_X,
+ TTK_NODE("Spinbox.textarea", TTK_EXPAND|TTK_FILL_X)))
+/* Progress bars -- track only */
TTK_LAYOUT("TProgressbar",
TTK_NODE("Progressbar.track", TTK_EXPAND|TTK_FILL_BOTH))
+/* Treeview -- no border. */
+TTK_LAYOUT("Treeview",
+ TTK_GROUP("Treeview.field", TTK_FILL_BOTH,
+ TTK_GROUP("Treeview.padding", TTK_FILL_BOTH,
+ TTK_NODE("Treeview.treearea", TTK_FILL_BOTH))))
+
/* Tree heading -- no border, fixed height */
TTK_LAYOUT("Heading",
- TTK_NODE("Treeheading.cell", TTK_FILL_X)
+ TTK_NODE("Treeheading.cell", TTK_FILL_BOTH)
TTK_NODE("Treeheading.image", TTK_PACK_RIGHT)
- TTK_NODE("Treeheading.text", 0))
+ TTK_NODE("Treeheading.text", TTK_PACK_TOP))
/* Tree items -- omit focus ring */
TTK_LAYOUT("Item",
@@ -1116,6 +2694,20 @@ TTK_LAYOUT("Item",
TTK_NODE("Treeitem.image", TTK_PACK_LEFT)
TTK_NODE("Treeitem.text", TTK_PACK_LEFT)))
+/* Scrollbar Layout -- Buttons at the bottom (Snow Leopard and Lion only) */
+
+TTK_LAYOUT("Vertical.TScrollbar",
+ TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y,
+ TTK_NODE("Vertical.Scrollbar.thumb", TTK_PACK_TOP|TTK_EXPAND|TTK_FILL_BOTH)
+ TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM)
+ TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_BOTTOM)))
+
+TTK_LAYOUT("Horizontal.TScrollbar",
+ TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X,
+ TTK_NODE("Horizontal.Scrollbar.thumb", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_BOTH)
+ TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT)
+ TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_RIGHT)))
+
TTK_END_LAYOUT_TABLE
/*----------------------------------------------------------------------
@@ -1135,6 +2727,7 @@ static int AquaTheme_Init(Tcl_Interp *interp)
*/
Ttk_RegisterElementSpec(themePtr, "background", &BackgroundElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "fill", &FillElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "field", &FieldElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "Toolbar.background",
&ToolbarBackgroundElementSpec, 0);
@@ -1145,11 +2738,13 @@ static int AquaTheme_Init(Tcl_Interp *interp)
Ttk_RegisterElementSpec(themePtr, "Radiobutton.button",
&ButtonElementSpec, &RadioButtonParams);
Ttk_RegisterElementSpec(themePtr, "Toolbutton.border",
- &ButtonElementSpec, &BevelButtonParams);
+ &ButtonElementSpec, &BevelButtonParams);
Ttk_RegisterElementSpec(themePtr, "Menubutton.button",
&ButtonElementSpec, &PopupButtonParams);
- Ttk_RegisterElementSpec(themePtr, "Spinbox.spinbutton",
- &SpinButtonElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "Spinbox.uparrow",
+ &SpinButtonUpElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "Spinbox.downarrow",
+ &SpinButtonDownElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "Combobox.button",
&ComboboxElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "Treeitem.indicator",
@@ -1157,33 +2752,51 @@ static int AquaTheme_Init(Tcl_Interp *interp)
Ttk_RegisterElementSpec(themePtr, "Treeheading.cell",
&TreeHeaderElementSpec, &ListHeaderParams);
+ Ttk_RegisterElementSpec(themePtr, "Treeview.treearea", &TreeAreaElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "Notebook.tab", &TabElementSpec, 0);
Ttk_RegisterElementSpec(themePtr, "Notebook.client", &PaneElementSpec, 0);
- Ttk_RegisterElementSpec(themePtr, "Labelframe.border",&GroupElementSpec,0);
- Ttk_RegisterElementSpec(themePtr, "Entry.field",&EntryElementSpec,0);
- Ttk_RegisterElementSpec(themePtr, "Spinbox.field",&EntryElementSpec,0);
+ Ttk_RegisterElementSpec(themePtr, "Labelframe.border", &GroupElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "Entry.field", &EntryElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "Spinbox.field", &EntryElementSpec, 0);
- Ttk_RegisterElementSpec(themePtr, "separator",&SeparatorElementSpec,0);
- Ttk_RegisterElementSpec(themePtr, "hseparator",&SeparatorElementSpec,0);
- Ttk_RegisterElementSpec(themePtr, "vseparator",&SeparatorElementSpec,0);
+ Ttk_RegisterElementSpec(themePtr, "separator", &SeparatorElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "hseparator", &SeparatorElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr, "vseparator", &SeparatorElementSpec, 0);
- Ttk_RegisterElementSpec(themePtr, "sizegrip",&SizegripElementSpec,0);
+ Ttk_RegisterElementSpec(themePtr, "sizegrip", &SizegripElementSpec, 0);
/*
* <<NOTE-TRACKS>>
- * The Progressbar widget adjusts the size of the pbar element.
- * In the Aqua theme, the appearance manager computes the bar geometry;
- * we do all the drawing in the ".track" element and leave the .pbar out.
+ * In some themes the Layouts for a progress bar has a trough element and a
+ * pbar element. But in our case the appearance manager draws both parts
+ * of the progress bar, so we just have a single element called ".track".
*/
- Ttk_RegisterElementSpec(themePtr,"Scale.trough",
- &TrackElementSpec, &ScaleData);
- Ttk_RegisterElementSpec(themePtr,"Scale.slider",&SliderElementSpec,0);
+
Ttk_RegisterElementSpec(themePtr,"Progressbar.track", &PbarElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Scale.trough", &TrackElementSpec,
+ &ScaleData);
+ Ttk_RegisterElementSpec(themePtr,"Scale.slider", &SliderElementSpec, 0);
+
+ Ttk_RegisterElementSpec(themePtr,"Vertical.Scrollbar.trough", &TroughElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Vertical.Scrollbar.thumb", &ThumbElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Horizontal.Scrollbar.trough", &TroughElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Horizontal.Scrollbar.thumb", &ThumbElementSpec, 0);
+
+ /*
+ * If we are not in Snow Leopard or Lion the arrows won't actually be displayed.
+ */
+
+ Ttk_RegisterElementSpec(themePtr,"Vertical.Scrollbar.uparrow", &ArrowElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Vertical.Scrollbar.downarrow", &ArrowElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Horizontal.Scrollbar.leftarrow", &ArrowElementSpec, 0);
+ Ttk_RegisterElementSpec(themePtr,"Horizontal.Scrollbar.rightarrow", &ArrowElementSpec, 0);
+
/*
* Layouts:
*/
+
Ttk_RegisterLayouts(themePtr, LayoutTable);
Tcl_PkgProvide(interp, "ttk::theme::aqua", TTK_VERSION);
@@ -1204,4 +2817,3 @@ int Ttk_MacOSXPlatformInit(Tcl_Interp *interp)
* coding: utf-8
* End:
*/
-