summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2023-04-07 07:20:54 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2023-04-07 07:20:54 (GMT)
commitf93a36f8a9eb5fd8ccd75e6fe36f0ffc087d6f60 (patch)
treefe2f257420a8d13e5511051e5944fcdef343637b
parente68bf90122e95ba81be7b31bc29d308c32873256 (diff)
parentb43916b48c90c2c33601e1153bc07e95c757e67d (diff)
downloadtk-f93a36f8a9eb5fd8ccd75e6fe36f0ffc087d6f60.zip
tk-f93a36f8a9eb5fd8ccd75e6fe36f0ffc087d6f60.tar.gz
tk-f93a36f8a9eb5fd8ccd75e6fe36f0ffc087d6f60.tar.bz2
TIP #655 implementation: Support the styleMask property for NSWindow and NSPanel in Aqua
-rw-r--r--doc/wm.n70
-rw-r--r--generic/tkConfig.c26
-rw-r--r--library/demos/mac_styles.tcl14
-rw-r--r--library/demos/mac_tabs.tcl77
-rw-r--r--library/demos/mac_wm.tcl228
-rw-r--r--library/demos/widget7
-rw-r--r--library/ttk/aquaTheme.tcl14
-rw-r--r--macosx/tkMacOSXMouseEvent.c11
-rw-r--r--macosx/tkMacOSXPrivate.h3
-rw-r--r--macosx/tkMacOSXSubwindows.c2
-rw-r--r--macosx/tkMacOSXWm.c879
-rw-r--r--macosx/ttkMacOSXTheme.c19
-rw-r--r--macosx/ttkMacOSXTheme.h26
-rw-r--r--tests/event.test5
-rw-r--r--tests/unixWm.test6
-rw-r--r--tests/wm.test6
16 files changed, 1170 insertions, 223 deletions
diff --git a/doc/wm.n b/doc/wm.n
index eda2fa7..e53529c 100644
--- a/doc/wm.n
+++ b/doc/wm.n
@@ -94,7 +94,32 @@ value accepted by \fBTk_GetColor\fR. If the empty string is specified
2000/XP+. Where not supported, the \fB\-transparentcolor\fR value remains
at \fB{}\fR.
.PP
-On Mac OS X, the following attributes may be set.
+On MacOS, the following attributes may be set.
+.TP
+\fB\-appearance\fR
+.
+Specifies whether the window is rendered in "dark mode". Allowed
+values are \fBauto\fR, \fBaqua\fR and \fBdarkaqua\fR. If the setting
+is auto then the appearance of the window is controlled by the
+System Settings.
+.TP
+\fB\-class\fR
+.
+Specifies whether the underlying Aqua window for a toplevel is an
+object of the NSWindow class or the NSPanel class. The two allowed
+values for this option are \fBnswindow\fR and \fBnspanel\fR. It is
+not possible to change the class of the underlying Aqua window once
+that window has been instantiated, and attempting to do that is an
+error. However it is allowed to set this option for a pathname that
+does not correspond to an existing window. Doing that causes the
+class name to be cached for later use. When a toplevel with that
+pathname is eventually created, the cached class name will determine which
+class is used for the underlying Aqua window.
+.TP
+\fB\-isdark\fR
+.
+Returns a boolean value which is true if the window is currently in
+dark mode.
.TP
\fB\-modified\fR
.
@@ -106,10 +131,49 @@ proxy icon is draggable).
.
Specifies process notification state (bouncing of the application dock icon).
.TP
+\fB\-stylemask\fR
+.
+Specifies an integer to be assigned as the styleMask of the underlying
+Aqua window. (See the Apple documentation for styleMask property of
+the NSWindow class.) The value of this option should be a list of
+bitnames. Each bit named in the list will be set to 1, and all other
+bits will be set to 0. The allowed bitnames are: \fBtitled\fR,
+\fBclosable\fR, \fBminiaturizable\fR, \fBresizable\fR,
+\fBfullsizecontentview\fR, \fBdocmodal\fR, \fButility\fR,
+\fBnonactivatingpanel\fR, and \fBHUDwindow\fR. Note that a side
+effect of setting the fullsizecontentview bit is that the window title
+bar becomes transparent.
+.TP
+\fB\-tabbingid\fR
+.
+Controls how tabbed toplevel windows are grouped together. Two tabs
+in the same group must correspond to toplevels with the same
+tabbingid, which can be an arbitrary UTF8 string. In the Tk
+implementation, changing the tabbingid of a toplevel in a tab group
+will cause it to be moved into a different group, in which all tabs
+have the new tabbingid or, if the new tabbingid is unique, to become
+a normal non-tabbed toplevel. It is allowed to set the tabbingid before the
+toplevel is created. If the pathname provided in the command does not
+correspond to a toplevel, the value will be cached and used later when
+the toplevel is actually created.
+.TP
+\fB\-tabbingmode\fR
+.
+Controls whether a toplevel can be opened as a tab within a tab group.
+The allowed values are \fBauto\fR, \fBpreferred\fR or
+\fBdisallowed\fR. A toplevel can be opened as a tab in exactly two
+situations: if its tabbingmode is \fBpreferred\fR; or if its
+tabbingmode is \fBauto\fR and the user has selected "prefer tabs when
+opening documents" in the Desktop and Dock panel of the System
+Settings application. It is allowed to set the tabbingmode before the
+toplevel is created. If the pathname provided in the command does not
+correspond to a toplevel, the value will be cached and used later when
+the toplevel is actually created.
+.TP
\fB\-titlepath\fR
.
-Specifies the path of the file referenced as the window proxy icon (which
-can be dragged and dropped in lieu of the file's finder icon).
+Specifies the path of the file referenced as the window proxy icon
+(which can be dragged and dropped in lieu of the file's finder icon).
.TP
\fB\-transparent\fR
.
diff --git a/generic/tkConfig.c b/generic/tkConfig.c
index e544d05..d9a070a 100644
--- a/generic/tkConfig.c
+++ b/generic/tkConfig.c
@@ -2029,18 +2029,20 @@ GetObjectForOption(
}
break;
case TK_OPTION_INDEX:
- if (*((int *) internalPtr) == INT_MIN) {
- objPtr = TkNewIndexObj(TCL_INDEX_NONE);
- } else if (*((int *) internalPtr) == INT_MAX) {
- objPtr = Tcl_NewStringObj("end+1", TCL_INDEX_NONE);
- } else if (*((int *) internalPtr) == -1) {
- objPtr = Tcl_NewStringObj("end", TCL_INDEX_NONE);
- } else if (*((int *) internalPtr) < 0) {
- char buf[32];
- snprintf(buf, 32, "end%d", 1 + *((int *) internalPtr));
- objPtr = Tcl_NewStringObj(buf, TCL_INDEX_NONE);
- } else {
- objPtr = Tcl_NewWideIntObj(*((int *) internalPtr));
+ if (!(optionPtr->specPtr->flags & (TK_OPTION_NULL_OK|TCL_NULL_OK)) || *((int *) internalPtr) != INT_MIN) {
+ if (*((int *) internalPtr) == INT_MIN) {
+ objPtr = TkNewIndexObj(TCL_INDEX_NONE);
+ } else if (*((int *) internalPtr) == INT_MAX) {
+ objPtr = Tcl_NewStringObj("end+1", TCL_INDEX_NONE);
+ } else if (*((int *) internalPtr) == -1) {
+ objPtr = Tcl_NewStringObj("end", TCL_INDEX_NONE);
+ } else if (*((int *) internalPtr) < 0) {
+ char buf[32];
+ snprintf(buf, 32, "end%d", 1 + *((int *) internalPtr));
+ objPtr = Tcl_NewStringObj(buf, TCL_INDEX_NONE);
+ } else {
+ objPtr = Tcl_NewWideIntObj(*((int *) internalPtr));
+ }
}
break;
case TK_OPTION_DOUBLE:
diff --git a/library/demos/mac_styles.tcl b/library/demos/mac_styles.tcl
index df727df..33fa888 100644
--- a/library/demos/mac_styles.tcl
+++ b/library/demos/mac_styles.tcl
@@ -34,8 +34,8 @@ image create nsimage starry2 -source $starryImg -as file -width 96 -radius 10 -r
image create nsimage field -source $fieldImg -as file -width 96 -radius 10
image create nsimage field1 -source $fieldImg -as file -width 96 -radius 10 -pressed 1
image create nsimage field2 -source $fieldImg -as file -width 96 -radius 10 -ring 3
-image create nsimage add -source NSAddTemplate -width 11 -height 11
-image create nsimage remove -source NSRemoveTemplate -width 11 -height 11
+image create nsimage add -source NSAddTemplate -width 20 -height 20
+image create nsimage remove -source NSRemoveTemplate -width 18 -height 4
# Off state and variables for checkbuttons and radio buttons
set off {!selected !alternate}
@@ -130,9 +130,9 @@ set feather [ttk::button $buttonFrame.feather -style ImageButton -text Tk \
-image {tkfeather pressed tkfeather1}]
set gradient [ttk::frame $buttonFrame.gradient]
pack [ttk::button $buttonFrame.gradient.add -style GradientButton \
- -image add -padding 7] -side left
+ -image add -padding {2 0}] -side left
pack [ttk::button $buttonFrame.gradient.remove -style GradientButton \
- -image remove -padding 7] -side left
+ -image remove -padding {2 8}] -side left
set disclosure [ttk::checkbutton $buttonFrame.disclosure -style DisclosureButton]
set help [ttk::button $buttonFrame.help -style HelpButton];
@@ -240,20 +240,20 @@ set dark [ttk::button $appearanceFrame.dark -style ImageButton -text Dark \
-image {starry pressed starry1 selected starry2} \
-command "beDark $appearanceFrame $w"]
grid $dark -row 1 -column 2 -sticky w
-if { [::tk::unsupported::MacWindowStyle isdark $w] } {
+if { [wm attributes $w -isdark] } {
$dark state selected
} else {
$light state selected
}
proc beLight {f w} {
- ::tk::unsupported::MacWindowStyle appearance $w aqua
+ wm attributes $w -appearance aqua
$f.dark state !selected
$f.light state selected
after 10 $f.light state !hover
}
proc beDark {f w} {
- ::tk::unsupported::MacWindowStyle appearance $w darkaqua
+ wm attributes $w -appearance darkaqua
$f.light state !selected
$f.dark state selected
after 10 $f.dark state !hover
diff --git a/library/demos/mac_tabs.tcl b/library/demos/mac_tabs.tcl
new file mode 100644
index 0000000..16771a8
--- /dev/null
+++ b/library/demos/mac_tabs.tcl
@@ -0,0 +1,77 @@
+# mac_tabs.tcl --
+#
+# This demonstration script creates three tabbable windows and allows the
+# wm attributes tabbingid and tabbingmode to be manipulated for the third
+# window, to demonstrate the effects of those attributes.
+
+if {![info exists widgetDemo]} {
+ error "This script should be run from the \"widget\" demo."
+}
+
+package require Tk
+catch {font create giant -family {Times New Roman} -size 64}
+set w .mac_tabs
+catch {destroy $w}
+toplevel $w
+package require Tk
+wm title $w "Tabbed Windows in Aqua"
+wm iconname $w "mac_tabs"
+positionWindow $w
+set suffix 0
+set winlist {}
+##
+
+## See Code / Dismiss
+pack [addSeeDismiss $w.buttons $w] -side bottom -fill x
+##
+set info "\
+This demo shows 3 toplevels, A, B, and C. \
+Each of these has tabbingmode set to preferred. \
+The tabbingid of Window A is groupA, the \
+tabbingid of Window B is groupB and the tabbingid \
+of Window C is groupC. Use the menubuttons below \
+to see the effect of changing the tabbingid and \
+tabbingmode attributes for Window C. \
+"
+pack [message $w.info -text $info -width 300]
+wm geometry $w +450+350
+
+ttk::frame $w.f
+menu $w.idmenu -tearoff 0
+foreach id {groupA groupB groupC} {
+ $w.idmenu add command -label $id \
+ -command [list wm attributes $w.c -tabbingid $id]
+}
+menu $w.modemenu -tearoff 0
+foreach mode {auto preferred disallowed} {
+ $w.modemenu add command -label $mode \
+ -command [list wm attributes $w.c -tabbingmode $mode]
+}
+ttk::menubutton $w.f.idbutton -menu $w.idmenu -text "tabbingid"\
+ -direction below
+grid $w.f.idbutton -row 0 -column 0
+ttk::menubutton $w.f.modebutton -menu $w.modemenu -text "tabbingmode"\
+ -direction below
+grid $w.f.modebutton -row 1 -column 0
+pack $w.f
+
+wm attributes $w.a -tabbingid groupA
+wm attributes $w.a -tabbingmode preferred
+toplevel $w.a
+wm geometry $w.a +50+100
+wm title $w.a "Window A"
+pack [ttk::label $w.a.l -text A -font giant] -padx 100 -pady 30
+
+wm attributes $w.b -tabbingid groupB
+wm attributes $w.b -tabbingmode preferred
+toplevel $w.b
+wm geometry $w.b +400+100
+wm title $w.b "Window B"
+pack [ttk::label $w.b.l -text B -font giant] -padx 100 -pady 30
+
+wm attributes $w.c -tabbingid groupC
+wm attributes $w.c -tabbingmode preferred
+toplevel $w.c
+wm geometry $w.c +750+100
+wm title $w.c "Window C"
+pack [ttk::label $w.c.l -text C -font giant] -padx 100 -pady 30
diff --git a/library/demos/mac_wm.tcl b/library/demos/mac_wm.tcl
new file mode 100644
index 0000000..3272623
--- /dev/null
+++ b/library/demos/mac_wm.tcl
@@ -0,0 +1,228 @@
+# mac_window_styles.tcl --
+#
+# This demonstration script creates a toplevel window containing a notebook
+# whose pages provide examples of the various mac-specific widgets that are
+# provided via special values for the -style option.
+
+if {![info exists widgetDemo]} {
+ error "This script should be run from the \"widget\" demo."
+}
+
+package require Tk
+
+set w .mac_wm
+catch {destroy $w}
+toplevel $w
+package require Tk
+wm title $w "Tk Aqua Window Styles"
+wm iconname $w "mac_wm"
+positionWindow $w
+set suffix 0
+set winlist {}
+##
+
+## See Code / Dismiss
+pack [addSeeDismiss $w.buttons $w] -side bottom -fill x
+
+proc launch {name windowInfo class} {
+ if {[winfo exists $name]} {
+ wm deiconify $name
+ focus -force $name
+ return
+ }
+ wm attributes $name -class $class; toplevel $name
+ wm title $name $class
+ set f $name.f
+ ttk::frame $f
+ set t $f.t
+ text $t -background systemWindowBackgroundColor \
+ -highlightcolor systemWindowBackgroundColor \
+ -font systemDefaultFont\
+ -wrap word -width 50 -height 6
+ $t insert insert $windowInfo
+ $t configure -state disabled
+ grid columnconfigure $f 0 -weight 1
+ grid $t -row 0 -column 0 -columnspan 2 -sticky NSEW
+ ttk::labelframe $f.stylemask -text "styleMask bits"
+ # titled
+ if {$class == "nswindow"} {
+ ttk::checkbutton $f.stylemask.titled -text titled -variable $name.titled \
+ -command [list setbit $name $f.stylemask.titled titled]
+ $f.stylemask.titled state selected
+ grid $f.stylemask.titled -row 0 -column 0 -sticky w
+ }
+ # closable
+ ttk::checkbutton $f.stylemask.closable -text closable -variable $name.closable \
+ -command [list setbit $name $f.stylemask.closable closable]
+ $f.stylemask.closable state selected
+ grid $f.stylemask.closable -row 1 -column 0 -sticky w
+ # miniaturizableable
+ ttk::checkbutton $f.stylemask.miniaturizable -text miniaturizable \
+ -variable $name.miniaturizable \
+ -command [list setbit $name $f.stylemask.miniaturizable miniaturizable]
+ if {$class == "nswindow"} {
+ $f.stylemask.miniaturizable state selected
+ } else {
+ $f.stylemask.miniaturizable state !alternate
+ }
+ grid $f.stylemask.miniaturizable -row 2 -column 0 -sticky w
+ # resizable
+ ttk::checkbutton $f.stylemask.resizable -text resizable -variable $name.resizable \
+ -command [list setbit $name $f.stylemask.resizable resizable]
+ $f.stylemask.resizable state selected
+ grid $f.stylemask.resizable -row 3 -column 0 -sticky w
+ # docmodal
+ ttk::checkbutton $f.stylemask.docmodal -text docmodal -variable $name.docmodal \
+ -command [list setbit $name $f.stylemask.docmodal docmodal]
+ $f.stylemask.docmodal state !alternate
+ grid $f.stylemask.docmodal -row 4 -column 0 -sticky w
+
+ grid $f.stylemask -row 1 -column 0
+ pack $name.f -side bottom -fill both -expand 1 -padx 16 -pady 16
+}
+
+set info "The command wm attributes window -stylemask ?bitnames? can \
+be used to modify bits in the stylemask property of the NSWindow or \
+NSPanel underlying a Tk Window. Changing these bits causes the \
+style of the window to change. This demo allows you to see the \
+effects of changing the bits. (Note that buttons in the title bar \
+can also be enabled or disabled with the ::tk::unsupported::MacWindowStyle \
+command.)"
+
+set panelInfo "A toplevel based on an NSPanel has a narrower title bar\
+than one based on an NSWindow. In addition the panel remains above all\
+windows on the screen, regardless of which app is active. These are\
+intended to be used as modal windows."
+
+set windowInfo "This is a standard Apple document window, based on an\
+NSWindow. It has a larger title bar and behaves normally with respect\
+to other windows from the same or another app."
+
+## background frame
+set f $w.f
+ttk::frame $f
+set t $f.t
+text $t -background systemWindowBackgroundColor \
+ -highlightcolor systemWindowBackgroundColor \
+ -font systemDefaultFont\
+ -wrap word -width 50 -height 8
+$t insert insert $info
+$t configure -state disabled
+grid columnconfigure $f 0 -weight 1
+grid $t -row 0 -column 0 -columnspan 2 -sticky NSEW
+ttk::labelframe $f.stylemask -text "styleMask"
+grid $f.stylemask -row 1 -column 0
+grid [ttk::button $f.wbw -text "Open an NSWindow" -width 20 \
+ -command [list launch .nswindow $windowInfo nswindow]] -row 2 -column 0
+grid [ttk::button $f.wbp -text "Open an NSPanel" -width 20 \
+ -command [list launch .nspanel $panelInfo nspanel]] -row 3 -column 0
+grid [ttk::button $f.wbm -text "Open a modern window" -width 20 \
+ -command launchModernWindow] -row 4 -column 0
+pack $w.f -side bottom -fill both -expand 1 -padx 16 -pady 16
+
+proc setbit {win cb bitname} {
+ set state [$cb instate selected]
+ set bits [wm attributes $win -stylemask]
+ set index [lsearch $bits $bitname]
+ if {$index >= 0 && !$state} {
+ set bits [lreplace $bits $index $index]
+ }
+ if {$index < 0 && $state} {
+ lappend bits $bitname
+ }
+ wm attributes $win -stylemask $bits
+}
+
+set aboutText \
+"Most of the apps which ship with a contemporary version of macOS \
+feature a window similar to this one, with a left sidebar that \
+allows selecting the content to be shown on the right hand side of \
+the window. These windows do not have a (visible) titlebar.\
+\n\nApps that use such windows include the Finder and the App Store as \
+well as Notes, Messages, Books, Maps and many others.\
+\n\nTo create a window like this one in Tk simply set the fullsizecontent bit \
+in the stylemask. For example:\n\n"
+
+set aboutCode \
+"wm attributes .t -stylemask {titled \\\
+\nfullsizecontent closable miniaturizable \\\
+\nresizable}\n\n"
+
+set detailsText \
+"(1) In the Apple API, setting the fullsizecontent bit in the stylemask \
+only allows content to be drawn in the part of the window covered by \
+the titlebar. In order for that content to be visible the title bar \
+must be transparent. Since it would be pointless to draw content under \
+an opaque title bar, Tk makes the title bar transparent whenever the \
+fullsizecontent bit is set.\
+
+\n\n\(2) Each radio button in the sidebar is a standard ttk::radiobutton \
+but created with a special value for its -style option. The value of the \
+-style option used to create these buttons is SidebarButton.\n"
+
+set whichPage 1
+trace add variable whichPage write "flipPage whichPage"
+proc flipPage {varname args} {
+ global whichPage
+ set newpage [set $varname]
+ grid remove [grid content .mod.right -row 0 -column 0]
+ switch $newpage\
+ 1 {grid .mod.right.about -padx 30 -pady 30 -row 0 -column 0 -sticky nsew}\
+ 2 {grid .mod.right.details -padx 30 -pady 30 -row 0 -column 0 -sticky nsew}
+ update idletasks
+}
+
+proc launchModernWindow {} {
+ global whichPage
+ global aboutText
+ global aboutCode
+ global detailsText
+ if {[winfo exists .mod]} {
+ wm deiconify .mod
+ focus -force .mod
+ return
+ }
+ toplevel .mod
+ wm title .mod {}
+ wm attributes .mod -stylemask {titled fullsizecontent closable \
+ miniaturizable resizable}
+ .mod configure -background white
+ grid columnconfigure .mod 0 -weight 0
+ grid columnconfigure .mod 1 -weight 1
+ grid rowconfigure .mod 0 -weight 1
+ frame .mod.left -width 220 -height 400 -background systemWindowBackgroundColor
+ catch {
+ font create leftFont -family .AppleSystemUIFont -size 11
+ font create rightFont -family .AppleSystemUIFont -size 16
+ font create codeFont -family Courier -size 16
+ }
+ grid [ttk::label .mod.left.spacer -padding {220 30 0 0}] -row 0 -column 0
+ grid [ttk::radiobutton .mod.left.about -text About -style SidebarButton \
+ -variable whichPage -value 1] \
+ -row 1 -column 0 -sticky nsew -padx 14
+ grid [ttk::radiobutton .mod.left.details -text Details -style SidebarButton \
+ -variable whichPage -value 2] \
+ -row 2 -column 0 -sticky nsew -padx 14
+ grid .mod.left -row 0 -column 0 -sticky nsew
+ frame .mod.right -width 500 -background systemTextBackgroundColor
+ grid rowconfigure .mod.right 0 -weight 0
+ text .mod.right.about -highlightcolor systemTextBackgroundColor \
+ -background systemTextBackgroundColor -font rightFont \
+ -highlightthickness 0 -wrap word -width 40
+ .mod.right.about tag configure code -font codeFont
+ .mod.right.about insert end $aboutText
+ .mod.right.about insert end $aboutCode code
+ .mod.right.about configure -state disabled
+
+ text .mod.right.details -highlightcolor systemTextBackgroundColor \
+ -background systemTextBackgroundColor -font rightFont\
+ -highlightthickness 0 -wrap word -width 40
+ .mod.right.details insert end $detailsText
+ .mod.right.details configure -state disabled
+
+ grid .mod.right.about -padx 30 -pady 30 -row 0 -column 0 -sticky nsew
+ grid .mod.right -row 0 -column 1 -sticky nsew
+ wm geometry .mod 800x500
+ update idletasks
+}
+
diff --git a/library/demos/widget b/library/demos/widget
index 8ca11ad..f96e778 100644
--- a/library/demos/widget
+++ b/library/demos/widget
@@ -328,12 +328,19 @@ addFormattedText {
@@demo labelframe Labelled frames
@@demo ttkbut The simple Themed Tk widgets
}
+
if {[tk windowingsystem] eq "aqua"} {
addFormattedText {
+ @@subtitle Mac-Specific Widgets and Window Styles
@@new
@@demo mac_styles Special widgets for macOS
+ @@new
+ @@demo mac_wm Window styles for macOS
+ @@new
+ @@demo mac_tabs Tabbed Windows on macOS
}
}
+
addFormattedText {
@@subtitle Listboxes and Trees
@@demo states The 50 states
diff --git a/library/ttk/aquaTheme.tcl b/library/ttk/aquaTheme.tcl
index 00bb686..7b6dd9c 100644
--- a/library/ttk/aquaTheme.tcl
+++ b/library/ttk/aquaTheme.tcl
@@ -75,6 +75,20 @@ namespace eval ttk::theme::aqua {
pressed RecessedFont
}
+ # Sidebar (radio) button
+ font create SidebarFont -family .AppleSystemUIFont -size 11 -weight normal
+ ttk::style configure SidebarButton \
+ -foreground systemControlTextColor \
+ -font SidebarFont
+ ttk::style map SidebarButton \
+ -foreground {
+ {disabled selected} systemWindowBackgroundColor3
+ {disabled !selected} systemDisabledControlTextColor
+ selected systemTextColor
+ active systemTextColor
+ pressed systemTextColor
+ }
+
# For Entry, Combobox and Spinbox widgets the selected text background
# is the "Highlight color" selected in preferences when the widget
# has focus. It is a gray color when the widget does not have focus or
diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c
index 9599b32..68f9344 100644
--- a/macosx/tkMacOSXMouseEvent.c
+++ b/macosx/tkMacOSXMouseEvent.c
@@ -88,7 +88,7 @@ enum {
NSEventType eventType = [theEvent type];
TKContentView *contentView = [eventWindow contentView];
NSPoint location = [theEvent locationInWindow];
- NSPoint viewLocation = [contentView convertPoint:location fromView:nil];
+ //NSPoint viewLocation = [contentView convertPoint:location fromView:nil];
TkWindow *winPtr = NULL, *grabWinPtr, *scrollTarget = NULL;
Tk_Window tkwin = NULL, capture, target;
NSPoint local, global;
@@ -117,7 +117,8 @@ enum {
* window attribute set to nil.
*/
- if (![eventWindow isMemberOfClass:[TKWindow class]]) {
+ if (![eventWindow isMemberOfClass:[TKWindow class]] &&
+ ![eventWindow isMemberOfClass:[TKPanel class]]) {
if ([theEvent timestamp] == 0) {
isTestingEvent = YES;
eventWindow = [NSApp keyWindow];
@@ -130,7 +131,7 @@ enum {
if (!isTestingEvent && !isMotionEvent) {
return theEvent;
}
- } else if (!NSPointInRect(viewLocation, [contentView bounds])) {
+ } else if (!NSPointInRect(location, [contentView bounds])) {
isOutside = YES;
}
button = [theEvent buttonNumber] + Button1;
@@ -246,10 +247,10 @@ enum {
NSRect bounds = [contentView bounds];
NSRect grip = NSMakeRect(bounds.size.width - 10, 0, 10, 10);
bounds = NSInsetRect(bounds, 2.0, 2.0);
- if (!NSPointInRect(viewLocation, bounds)) {
+ if (!NSPointInRect(location, bounds)) {
return theEvent;
}
- if (NSPointInRect(viewLocation, grip)) {
+ if (NSPointInRect(location, grip)) {
return theEvent;
}
if ([NSApp tkLiveResizeEnded]) {
diff --git a/macosx/tkMacOSXPrivate.h b/macosx/tkMacOSXPrivate.h
index 71cceb9..d7cf540 100644
--- a/macosx/tkMacOSXPrivate.h
+++ b/macosx/tkMacOSXPrivate.h
@@ -465,6 +465,9 @@ VISIBILITY_HIDDEN
@property Window tkWindow;
@end
+@interface TKPanel(TKWm)
+- (void) tkLayoutChanged;
+@end
#pragma mark NSMenu & NSMenuItem Utilities
@interface NSMenu(TKUtils)
diff --git a/macosx/tkMacOSXSubwindows.c b/macosx/tkMacOSXSubwindows.c
index 49778ef..ae2b149 100644
--- a/macosx/tkMacOSXSubwindows.c
+++ b/macosx/tkMacOSXSubwindows.c
@@ -403,7 +403,7 @@ XResizeWindow(
if (w) {
if ([w styleMask] & NSFullScreenWindowMask) {
- [w tkLayoutChanged];
+ [(TKWindow *)w tkLayoutChanged];
} else {
NSRect r = [w contentRectForFrameRect:[w frame]];
diff --git a/macosx/tkMacOSXWm.c b/macosx/tkMacOSXWm.c
index c0ccd9a..39fd62f 100644
--- a/macosx/tkMacOSXWm.c
+++ b/macosx/tkMacOSXWm.c
@@ -37,7 +37,7 @@
*/
/*
- * Window attributes and classes
+ * Carbon window attributes and classes.
*/
#define WM_NSMASK_SHIFT 36
@@ -111,7 +111,8 @@ static const struct {
.defaultAttrs = kWindowHideOnSuspendAttribute,
.forceOnAttrs = kWindowNoTitleBarAttribute |
kWindowDoesNotCycleAttribute,
- .flags = WM_TOPMOST, },
+ .flags = WM_TOPMOST,
+ .styleMask = 0},
[kSheetWindowClass] = {
.validAttrs = kWindowResizableAttribute,
.forceOnAttrs = kWindowNoTitleBarAttribute |
@@ -150,19 +151,161 @@ static const struct {
(macClassAttrs[(class)].forceOnAttrs & ~kWindowResizableAttribute)))
/*
- * Data for [wm attributes] command:
+ * Structures and data for the wm attributes command (macOS 10.13 and later):
*/
+/* Hash tables for attributes which can be set before a window exists */
+static Tcl_HashTable pathnameToSubclass;
+static Tcl_HashTable pathnameToTabbingId;
+static Tcl_HashTable pathnameToTabbingMode;
+
+enum NSWindowSubclass {
+ subclassNSWindow = 0,
+ subclassNSPanel = 1
+};
+/* This array must be indexed by the enum above.*/
+static const char *subclassNames[] = {"nswindow", "nspanel", NULL};
+
+typedef struct buttonField_t {
+ unsigned zoom: 1;
+ unsigned miniaturize: 1;
+ unsigned close: 1;
+} buttonField;
+
+/* The order of these names must match the order of the bits above! */
+static const char *buttonNames[] = {"zoom", "miniaturize", "close", NULL};
+
+typedef union windowButtonState_t {
+ int intvalue;
+ buttonField bits;
+} windowButtonState;
+
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
+enum NSWindowClass {
+ NSWindowClass_any = 0,
+ NSWindowClass_window = 1,
+ NSWindowClass_panel = 2
+};
+typedef struct styleMaskBit_t {
+ const char *bitname;
+ unsigned long bitvalue;
+ enum NSWindowClass allowed;
+} styleMaskBit;
+
+static const styleMaskBit styleMaskBits[] = {
+ /* Make the titlebar visible and use round corners. */
+ {"titled", NSWindowStyleMaskTitled, NSWindowClass_window},
+ /* Enable the close button. */
+ {"closable", NSWindowStyleMaskClosable, NSWindowClass_window},
+ /* Enable the miniaturize button. */
+ {"miniaturizable", NSWindowStyleMaskMiniaturizable, NSWindowClass_window},
+ /* Allow the user to resize the window. */
+ {"resizable", NSWindowStyleMaskResizable, NSWindowClass_window},
+ /*
+ * Make the content view extend under the titlebar. We force
+ * titlebarAppearsTransparent when this bit is set. Otherwise it is
+ * pretty useless.
+ */
+ {"fullsizecontentview", NSWindowStyleMaskFullSizeContentView, NSWindowClass_window},
+ /* Rounded corners, cannot have a titlebar (overrides titled bit). */
+ {"docmodal", NSWindowStyleMaskDocModalWindow, NSWindowClass_any},
+ /* ============================================
+ * The following bits are only valid for panels.
+ */
+ /* Make the title bar thinner. */
+ {"utility", NSWindowStyleMaskUtilityWindow, NSWindowClass_panel},
+ /* Do not activate the app when the window is activated. */
+ {"nonactivatingpanel", NSWindowStyleMaskNonactivatingPanel, NSWindowClass_panel},
+ /*
+ * Requires utility. Cannot be resizable. Close button is an X; no other buttons.
+ * Cannot be a docmodal.
+ */
+ {"HUDwindow", NSWindowStyleMaskHUDWindow, NSWindowClass_panel},
+ {NULL, 0, 0}
+};
+
+typedef struct tabbingMode_t {
+ const char *modeName;
+ long modeValue;
+} tabbingMode;
+
+static const tabbingMode tabbingModes[] = {
+ {"auto", NSWindowTabbingModeAutomatic},
+ {"disallowed", NSWindowTabbingModeDisallowed},
+ {"preferred", NSWindowTabbingModePreferred},
+ {NULL, -1}
+};
+
+static const char *const appearanceStrings[] = {
+ "aqua", "auto", "darkaqua", NULL
+};
+enum appearances {
+ APPEARANCE_AQUA, APPEARANCE_AUTO, APPEARANCE_DARKAQUA
+};
+
+static Bool wantsToBeTab(NSWindow *macWindow) {
+ Bool result;
+ switch ([macWindow tabbingMode]) {
+ case NSWindowTabbingModeDisallowed:
+ result = False;
+ break;
+ case NSWindowTabbingModePreferred:
+ result = True;
+ break;
+ case NSWindowTabbingModeAutomatic:
+ result = ([NSWindow userTabbingPreference] ==
+ NSWindowUserTabbingPreferenceAlways);
+ break;
+ default:
+ result = False;
+ break;
+ }
+ return result;
+}
+
+/*
+ * Helper for the tkLayoutChanged methods. Synchronizes Tk's understanding of
+ * the bounds of a contentView with the window's. It is needed because there
+ * are situations when the window manager can change the layout of an NSWindow
+ * without having been requested to do so by Tk. Examples are when a window
+ * goes FullScreen or shows a tab bar. NSWindow methods which involve such
+ * layout changes should be overridden or protected by methods which call this.
+ */
+
+static void syncLayout(NSWindow *macWindow)
+{
+ TkWindow *winPtr = TkMacOSXGetTkWindow(macWindow);
+
+ if (winPtr) {
+ // Using screen coordinates with origin at bottom left.
+ NSRect frameRect = [macWindow frame];
+ // This accounts for the tab bar, if there is one.
+ NSRect contentRect = [macWindow contentRectForFrameRect: frameRect];
+ WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ // The parent includes the title bar, tab bar and window frame.
+ wmPtr->xInParent = frameRect.origin.x - contentRect.origin.x;
+ wmPtr->yInParent = (frameRect.origin.y + frameRect.size.height -
+ contentRect.origin.y - contentRect.size.height);
+ wmPtr->parentWidth = winPtr->changes.width + frameRect.size.width -
+ contentRect.size.width;
+ wmPtr->parentHeight = winPtr->changes.height + frameRect.size.height -
+ contentRect.size.height;
+ }
+}
+#endif
+
typedef enum {
- WMATT_ALPHA, WMATT_FULLSCREEN, WMATT_MODIFIED, WMATT_NOTIFY,
- WMATT_TITLEPATH, WMATT_TOPMOST, WMATT_TRANSPARENT,
- WMATT_TYPE, _WMATT_LAST_ATTRIBUTE
+ WMATT_ALPHA, WMATT_APPEARANCE, WMATT_BUTTONS, WMATT_FULLSCREEN,
+ WMATT_ISDARK, WMATT_MODIFIED, WMATT_NOTIFY, WMATT_TITLEPATH, WMATT_TOPMOST,
+ WMATT_TRANSPARENT, WMATT_STYLEMASK, WMATT_CLASS, WMATT_TABBINGID,
+ WMATT_TABBINGMODE, WMATT_TYPE, _WMATT_LAST_ATTRIBUTE
} WmAttribute;
static const char *const WmAttributeNames[] = {
- "-alpha", "-fullscreen", "-modified", "-notify",
- "-titlepath", "-topmost", "-transparent",
- "-type", NULL
+ "-alpha", "-appearance", "-buttons", "-fullscreen", "-isdark", "-modified",
+ "-notify", "-titlepath", "-topmost", "-transparent", "-stylemask", "-class",
+ "-tabbingid", "-tabbingmode", "-type", NULL
};
/*
@@ -193,7 +336,7 @@ static const Tk_GeomMgr wmMgrType = {
static int tkMacOSXWmAttrNotifyVal = 0;
/*
- * Forward declarations for procedures defined in this file:
+ * Declarations of static functions defined in this file:
*/
static NSRect InitialWindowBounds(TkWindow *winPtr,
@@ -309,8 +452,6 @@ static int WmWithdrawCmd(Tk_Window tkwin, TkWindow *winPtr,
static void WmUpdateGeom(WmInfo *wmPtr, TkWindow *winPtr);
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,
@@ -326,6 +467,90 @@ static void RemapWindows(TkWindow *winPtr,
MacDrawable *parentWin);
static void RemoveTransient(TkWindow *winPtr);
+#if MAC_OS_X_VERSION_MAX_ALLOWED > 101300
+
+/*
+ * Add a window as a tab in the group specified by its tabbingid, or
+ * make it a standalone window if it is the only window with that
+ * tabbingid. Adjust the window size if a tab bar appeared or
+ * disappeared.
+ */
+
+static void placeAsTab(TKWindow *macWindow) {
+ TkWindow *winPtr = NULL, *winPtr2 = NULL;
+ TKWindow *target = NULL, *sibling = NULL;
+ NSString *identifier = [macWindow tabbingIdentifier];
+ if (!wantsToBeTab(macWindow)) {
+ [macWindow moveTabToNewWindow:NSApp];
+ [(TKWindow *)target tkLayoutChanged];
+ return;
+ }
+ for (NSWindow *window in [NSApp windows]) {
+ if (window == macWindow) {
+ continue;
+ }
+ if ([identifier isEqualTo: [window tabbingIdentifier]] &&
+ wantsToBeTab(window)) {
+ target = (TKWindow*) window;
+ syncLayout(target);
+ break;
+ }
+ }
+ syncLayout(macWindow);
+ NSArray<NSWindow *> *tabs = [macWindow tabbedWindows];
+ if ([tabs count] == 2) {
+ sibling = tabs[0] == macWindow ? (TKWindow *)tabs[1] : (TKWindow *)tabs[0];
+ syncLayout(sibling);
+ winPtr2 = TkMacOSXGetTkWindow(sibling);
+ }
+ if (target) {
+ CGFloat winHeight = [macWindow contentRectForFrameRect:
+ [macWindow frame]].size.height;
+ CGFloat winDelta = 0, targetHeight, targetDelta = 0;
+ targetHeight = [target contentRectForFrameRect:
+ [target frame]].size.height;
+ [target addTabbedWindow:macWindow ordered:NSWindowAbove];
+ targetDelta = targetHeight - [target contentRectForFrameRect:
+ [target frame]].size.height;
+ winDelta = winHeight - [target contentRectForFrameRect:
+ [target frame]].size.height;
+ if (winDelta) {
+ winPtr = TkMacOSXGetTkWindow(macWindow);
+ XMoveResizeWindow(winPtr->display, winPtr->window,
+ winPtr->changes.x, winPtr->changes.y,
+ winPtr->changes.width, winPtr->changes.height + winDelta );
+ if (sibling) {
+ winPtr = TkMacOSXGetTkWindow(sibling);
+ XMoveResizeWindow(winPtr->display, winPtr->window,
+ winPtr->changes.x, winPtr->changes.y,
+ winPtr->changes.width, winPtr->changes.height - winDelta );
+ }
+ }
+ if (targetDelta) {
+ winPtr = TkMacOSXGetTkWindow(target);
+ XMoveResizeWindow(winPtr->display, winPtr->window,
+ winPtr->changes.x, winPtr->changes.y,
+ winPtr->changes.width, winPtr->changes.height + targetDelta );
+ }
+ } else {
+ CGFloat height = [macWindow contentRectForFrameRect:
+ [macWindow frame]].size.height;
+ [macWindow moveTabToNewWindow:NSApp];
+ CGFloat delta = height - [macWindow contentRectForFrameRect:
+ [macWindow frame]].size.height;
+ winPtr = TkMacOSXGetTkWindow(macWindow);
+ XMoveResizeWindow(winPtr->display, winPtr->window,
+ winPtr->changes.x, winPtr->changes.y,
+ winPtr->changes.width, winPtr->changes.height + delta);
+ if (winPtr2) {
+ XMoveResizeWindow(winPtr2->display, winPtr2->window,
+ winPtr2->changes.x, winPtr2->changes.y,
+ winPtr2->changes.width, winPtr2->changes.height + delta );
+ }
+ }
+}
+#endif
+
#pragma mark NSWindow(TKWm)
@implementation NSWindow(TKWm)
@@ -345,19 +570,25 @@ static void RemoveTransient(TkWindow *winPtr);
NSRect pointrect = {point, {0,0}};
return [self convertRectToScreen:pointrect].origin;
}
+
- (NSPoint) tkConvertPointFromScreen: (NSPoint)point
{
NSRect pointrect = {point, {0,0}};
return [self convertRectFromScreen:pointrect].origin;
}
#endif
-
@end
#pragma mark -
@implementation TKPanel: NSPanel
@synthesize tkWindow = _tkWindow;
+
+- (void) tkLayoutChanged
+{
+ syncLayout(self);
+}
+
@end
@implementation TKDrawerWindow: NSWindow
@@ -372,44 +603,12 @@ static void RemoveTransient(TkWindow *winPtr);
@implementation TKWindow(TKWm)
-/*
- * This method synchronizes Tk's understanding of the bounds of a contentView
- * with the window's. It is needed because there are situations when the
- * window manager can change the layout of an NSWindow without having been
- * requested to do so by Tk. Examples are when a window goes FullScreen or
- * shows a tab bar. NSWindow methods which involve such layout changes should
- * be overridden or protected by methods which call this.
- */
-
- (void) tkLayoutChanged
{
- TkWindow *winPtr = TkMacOSXGetTkWindow(self);
-
- if (winPtr) {
- NSRect frameRect;
-
- /*
- * This avoids including the title bar for full screen windows
- * but does include it for normal windows.
- */
-
- if ([self styleMask] & NSFullScreenWindowMask) {
- frameRect = [NSWindow frameRectForContentRect:NSZeroRect
- styleMask:[self styleMask]];
- } else {
- frameRect = [self frameRectForContentRect:NSZeroRect];
- }
-
- WmInfo *wmPtr = winPtr->wmInfoPtr;
-
- wmPtr->xInParent = -frameRect.origin.x;
- wmPtr->yInParent = frameRect.origin.y + frameRect.size.height;
- wmPtr->parentWidth = winPtr->changes.width + frameRect.size.width;
- wmPtr->parentHeight = winPtr->changes.height + frameRect.size.height;
- }
+ syncLayout(self);
}
-#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
- (void)toggleTabBar:(id)sender
{
TkWindow *winPtr = TkMacOSXGetTkWindow(self);
@@ -727,6 +926,8 @@ TkWmNewWindow(
wmPtr->window = nil;
winPtr->wmInfoPtr = wmPtr;
+ // initialize wmPtr->NSWindowSubclass here
+
UpdateVRootGeometry(wmPtr);
/*
@@ -1209,16 +1410,39 @@ Tk_WmObjCmd(
sizeof(char *), "option", 0, &index) != TCL_OK) {
return TCL_ERROR;
}
-
if (objc < 3) {
goto wrongNumArgs;
}
-
- if (TkGetWindowFromObj(interp, tkwin, objv[2], (Tk_Window *) &winPtr)
- != TCL_OK) {
+ if (index == WMOPT_ATTRIBUTES && objc == 5 &&
+ strcmp(Tcl_GetString(objv[3]), "-class") == 0) {
+ if (TkGetWindowFromObj(NULL, tkwin, objv[2], (Tk_Window *) &winPtr)
+ == TCL_OK) {
+ if (winPtr->wmInfoPtr->window != NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Cannot change the class after the mac window is created.",-1));
+ Tcl_SetErrorCode(interp, "TK", "CLASS_CHANGE", NULL);
+ return TCL_ERROR;
+ }
+ } else {
+ winPtr = NULL;
+ }
+ } else if (index == WMOPT_ATTRIBUTES && objc == 5 &&
+ strcmp(Tcl_GetString(objv[3]), "-tabbingid") == 0) {
+ if (TkGetWindowFromObj(NULL, tkwin, objv[2], (Tk_Window *) &winPtr)
+ != TCL_OK) {
+ winPtr = NULL;
+ }
+ } else if (index == WMOPT_ATTRIBUTES && objc == 5 &&
+ strcmp(Tcl_GetString(objv[3]), "-tabbingmode") == 0) {
+ if (TkGetWindowFromObj(NULL, tkwin, objv[2], (Tk_Window *) &winPtr)
+ != TCL_OK) {
+ winPtr = NULL;
+ }
+ } else if (TkGetWindowFromObj(interp, tkwin, objv[2], (Tk_Window *) &winPtr)
+ != TCL_OK) {
return TCL_ERROR;
}
- if (!Tk_IsTopLevel(winPtr)
+ if (winPtr && !Tk_IsTopLevel(winPtr)
&& (index != WMOPT_MANAGE) && (index != WMOPT_FORGET)) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"window \"%s\" isn't a top-level window", winPtr->pathName));
@@ -1398,6 +1622,7 @@ WmSetAttribute(
{
WmInfo *wmPtr = winPtr->wmInfoPtr;
int boolean;
+ NSString *identifier;
switch (attribute) {
case WMATT_ALPHA: {
@@ -1419,6 +1644,58 @@ WmSetAttribute(
[macWindow setAlphaValue:dval];
break;
}
+ case WMATT_APPEARANCE: {
+ int index;
+ if (Tcl_GetIndexFromObjStruct(interp, value, appearanceStrings,
+ sizeof(char *), "appearancename", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch ((enum appearances) index) {
+ case APPEARANCE_AQUA:
+ macWindow.appearance = [NSAppearance appearanceNamed:
+ NSAppearanceNameAqua];
+ break;
+ case APPEARANCE_DARKAQUA:
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
+ if (@available(macOS 10.14, *)) {
+ macWindow.appearance = [NSAppearance appearanceNamed:
+ NSAppearanceNameDarkAqua];
+ }
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
+ break;
+ default:
+ macWindow.appearance = nil;
+ }
+ break;
+ }
+ case WMATT_BUTTONS: {
+ windowButtonState state = {0};
+ Tcl_Obj **elements;
+ Tcl_Size nElements, i;
+ if (Tcl_ListObjGetElements(interp, value, &nElements, &elements) == TCL_OK) {
+ int index = 0;
+ for (i = 0; i < nElements; i++) {
+ if (Tcl_GetIndexFromObjStruct(interp, elements[i], buttonNames,
+ sizeof(char *), "window button name", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ } else {
+ state.intvalue |= (1 << index);
+ }
+ }
+ } else if (Tcl_GetIntFromObj(interp, value, &state.intvalue) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ NSButton *closer = [macWindow standardWindowButton:
+ NSWindowCloseButton];
+ NSButton *miniaturizer = [macWindow standardWindowButton:
+ NSWindowMiniaturizeButton];
+ NSButton *zoomer = [macWindow standardWindowButton:
+ NSWindowZoomButton];
+ closer.enabled = (state.bits.close != 0);
+ miniaturizer.enabled = (state.bits.miniaturize != 0);
+ zoomer.enabled = (state.bits.zoom != 0);
+ break;
+ }
case WMATT_FULLSCREEN:
if (Tcl_GetBooleanFromObj(interp, value, &boolean) != TCL_OK) {
return TCL_ERROR;
@@ -1456,6 +1733,121 @@ WmSetAttribute(
tkMacOSXWmAttrNotifyVal = boolean;
}
break;
+ case WMATT_STYLEMASK: {
+ unsigned long styleMaskValue = 0;
+ Tcl_Obj **elements;
+ Tcl_Size nElements, i;
+ if (Tcl_ListObjGetElements(interp, value, &nElements, &elements) == TCL_OK) {
+ int index;
+ for (i = 0; i < nElements; i++) {
+ if (Tcl_GetIndexFromObjStruct(interp, elements[i], styleMaskBits,
+ sizeof(styleMaskBit), "styleMask bit", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ } else if (![macWindow isKindOfClass: [NSPanel class]] &&
+ styleMaskBits[index].allowed == NSWindowClass_panel) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "styleMask bit \"%s\" can only be used with an NSPanel",
+ styleMaskBits[index].bitname));
+ Tcl_SetErrorCode(interp, "TK", "INVALID_STYLEMASK_BIT", NULL);
+ return TCL_ERROR;
+ } else {
+ styleMaskValue |= styleMaskBits[index].bitvalue;
+ }
+ /*
+ * Be sure not to change the fullscreen bit.
+ */
+ styleMaskValue |= (NSWindowStyleMaskFullScreen & macWindow.styleMask);
+ }
+ /*
+ * A resizable docmodal NSWindow or NSPanel does not work
+ * correctly. It cannot be resized from the top edge. Other bits,
+ * such as titled are ignored for docmodals. To be safe, we clear
+ * all other bits when the docmodal bit is set.
+ */
+ if (styleMaskValue & NSDocModalWindowMask) {
+ styleMaskValue &= ~NSWindowStyleMaskResizable;
+ }
+ if ([macWindow isKindOfClass: [NSPanel class]]) {
+ /*
+ * We always make NSPanels titled, nonactivating utility windows,
+ * even if these bits are not requested in the command.
+ */
+ if (!(styleMaskValue & NSWindowStyleMaskTitled) ) {
+ styleMaskValue |= NSWindowStyleMaskTitled;
+ styleMaskValue |= NSWindowStyleMaskUtilityWindow;
+ styleMaskValue |= NSWindowStyleMaskNonactivatingPanel;
+ }
+ }
+ if (styleMaskValue & NSWindowStyleMaskFullSizeContentView) {
+ macWindow.titlebarAppearsTransparent = YES;
+ } else {
+ macWindow.titlebarAppearsTransparent = NO;
+ }
+ } else {
+ return TCL_ERROR;
+ }
+ NSRect oldFrame = [macWindow frame];
+#ifdef DEBUG
+ fprintf(stderr, "Current styleMask: %lx\n", [macWindow styleMask]);
+ fprintf(stderr, "Setting styleMask to %lx\n", styleMaskValue);
+#endif
+ macWindow.styleMask = (unsigned long) styleMaskValue;
+ NSRect newFrame = [macWindow frame];
+ int heightDiff = newFrame.size.height - oldFrame.size.height;
+ int newHeight = heightDiff < 0 ? newFrame.size.height :
+ newFrame.size.height - heightDiff;
+ [(TKWindow *)macWindow tkLayoutChanged];
+ if (heightDiff) {
+ //Calling XMoveResizeWindow twice is a hack to force a relayout
+ //of the window.
+ XMoveResizeWindow(winPtr->display, winPtr->window,
+ winPtr->changes.x, winPtr->changes.y,
+ newFrame.size.width, newHeight - 1);
+ XMoveResizeWindow(winPtr->display, winPtr->window,
+ winPtr->changes.x, winPtr->changes.y,
+ newFrame.size.width, newHeight);
+ }
+ break;
+ }
+ case WMATT_TABBINGID: {
+ NSString *oldId = [macWindow tabbingIdentifier];
+ char *valueString;
+ int length;
+ if ([NSApp macOSVersion] < 101300) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "Tabbing identifiers require macOS 10.13", TCL_INDEX_NONE));
+ Tcl_SetErrorCode(interp, "TK", "WM", "TABBINGID", NULL);
+ return TCL_ERROR;
+ }
+ valueString = Tcl_GetStringFromObj(value, &length);
+ identifier = [NSString stringWithUTF8String:valueString];
+ [macWindow setTabbingIdentifier: identifier];
+
+ /*
+ * If the tabbingIdentifier of a tab is changed we move it into
+ * the tab group with that identifier.
+ */
+
+ if ([oldId compare:identifier] != NSOrderedSame) {
+ placeAsTab((TKWindow *)macWindow);
+ }
+ break;
+ }
+ case WMATT_TABBINGMODE: {
+ int index;
+ tabbingMode mode;
+ if (Tcl_GetIndexFromObjStruct(interp, value, tabbingModes,
+ sizeof(tabbingMode), "NSWindow Tabbing Mode", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ mode = tabbingModes[index];
+ [macWindow setTabbingMode: mode.modeValue];
+ placeAsTab((TKWindow *)macWindow);
+ break;
+ }
+ case WMATT_ISDARK: {
+ break;
+ }
case WMATT_TITLEPATH: {
const char *path = (const char *)Tcl_FSGetNativePath(value);
NSString *filename = @"";
@@ -1505,6 +1897,8 @@ WmSetAttribute(
TK_PARENT_WINDOW);
}
break;
+ case WMATT_CLASS:
+ break;
case WMATT_TYPE:
TKLog(@"The type attribute is ignored on macOS.");
break;
@@ -1539,15 +1933,88 @@ WmGetAttribute(
case WMATT_ALPHA:
result = Tcl_NewDoubleObj([macWindow alphaValue]);
break;
+ case WMATT_APPEARANCE: {
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
+ NSAppearanceName appearance;
+#else
+ NSString *appearance;
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
+ const char *resultString = "unrecognized";
+ appearance = macWindow.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 (@available(macOS 10.14, *)) {
+ if (appearance == NSAppearanceNameDarkAqua) {
+ resultString = appearanceStrings[APPEARANCE_DARKAQUA];
+ }
+#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
+ }
+ result = Tcl_NewStringObj(resultString, TCL_INDEX_NONE);
+ break;
+ }
+ case WMATT_BUTTONS: {
+ result = Tcl_NewListObj(3, NULL);
+ if ([macWindow standardWindowButton:NSWindowCloseButton].enabled) {
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("close", TCL_INDEX_NONE));
+ }
+ if ([macWindow standardWindowButton:NSWindowMiniaturizeButton].enabled) {
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("miniaturize", TCL_INDEX_NONE));
+ }
+ if ([macWindow standardWindowButton:NSWindowZoomButton].enabled) {
+ Tcl_ListObjAppendElement(NULL, result, Tcl_NewStringObj("zoom", TCL_INDEX_NONE));
+ }
+ break;
+ }
+ case WMATT_CLASS:
+ if ([macWindow isKindOfClass:[NSPanel class]]) {
+ result = Tcl_NewStringObj(subclassNames[subclassNSPanel], TCL_INDEX_NONE);
+ } else {
+ result = Tcl_NewStringObj(subclassNames[subclassNSWindow], TCL_INDEX_NONE);
+ }
+ break;
case WMATT_FULLSCREEN:
result = Tcl_NewBooleanObj([macWindow styleMask] & NSFullScreenWindowMask);
break;
+ case WMATT_ISDARK:
+ result = Tcl_NewBooleanObj(TkMacOSXInDarkMode((Tk_Window)winPtr));
+ break;
case WMATT_MODIFIED:
result = Tcl_NewBooleanObj([macWindow isDocumentEdited]);
break;
case WMATT_NOTIFY:
result = Tcl_NewBooleanObj(tkMacOSXWmAttrNotifyVal);
break;
+ case WMATT_STYLEMASK: {
+ unsigned long styleMaskValue = [macWindow styleMask];
+ const styleMaskBit *bit;
+ result = Tcl_NewListObj(9, NULL);
+ for (bit = styleMaskBits; bit->bitname != NULL; bit++) {
+ if (styleMaskValue & bit->bitvalue) {
+ Tcl_ListObjAppendElement(NULL, result,
+ Tcl_NewStringObj(bit->bitname, TCL_INDEX_NONE));
+ }
+ }
+ break;
+ }
+ case WMATT_TABBINGID:
+ result = Tcl_NewStringObj([[macWindow tabbingIdentifier] UTF8String],
+ -1);
+ break;
+ case WMATT_TABBINGMODE: {
+ long mode = [macWindow tabbingMode];
+ const char *name = "unrecognized";
+ for (const tabbingMode *m = tabbingModes; m->modeName != NULL; m++) {
+ if (m->modeValue == mode) {
+ name = m->modeName;
+ break;
+ }
+ }
+ result = Tcl_NewStringObj(name, TCL_INDEX_NONE);
+ break;
+ }
case WMATT_TITLEPATH:
result = Tcl_NewStringObj([[macWindow representedFilename] UTF8String],
TCL_INDEX_NONE);
@@ -1587,7 +2054,7 @@ WmGetAttribute(
static int
WmAttributesCmd(
- TCL_UNUSED(Tk_Window), /* Main window of the application. */
+ TCL_UNUSED(Tk_Window), /* Main window of the application. */
TkWindow *winPtr, /* Toplevel to work with */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
@@ -1595,8 +2062,58 @@ WmAttributesCmd(
{
int attribute = 0;
NSWindow *macWindow;
+ if (winPtr == NULL && objc == 5) {
+ int index, isNew = 0, length;
- if (winPtr->window == None) {
+ /*
+ * If we are setting an atttribute of a future window, save the value
+ * in a hash table so we can look it up when the window is actually
+ * created.
+ */
+
+ if (strcmp(Tcl_GetString(objv[3]), "-class") == 0) {
+ if (Tcl_GetIndexFromObjStruct(interp, objv[4], subclassNames,
+ sizeof(char *), "NSWindow subclass", 0, &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&pathnameToSubclass,
+ Tcl_GetString(objv[2]), &isNew);
+ if (hPtr) {
+ Tcl_SetHashValue(hPtr, INT2PTR(index));
+ return TCL_OK;
+ }
+ } else if (strcmp(Tcl_GetString(objv[3]), "-tabbingid") == 0) {
+ char *identifier = Tcl_GetStringFromObj(objv[4], &length);
+ char *value = ckalloc(length + 1);
+ strncpy(value, identifier, length + 1);
+ Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&pathnameToTabbingId,
+ Tcl_GetString(objv[2]), &isNew);
+ if (hPtr) {
+ Tcl_SetHashValue(hPtr, value);
+ return TCL_OK;
+ }
+ } else if (strcmp(Tcl_GetString(objv[3]), "-tabbingmode") == 0) {
+ long value = NSWindowTabbingModeAutomatic;
+ int modeIndex;
+ if (Tcl_GetIndexFromObjStruct(interp, objv[4], tabbingModes,
+ sizeof(tabbingMode), "NSWindow Tabbing Mode", 0, &modeIndex) == TCL_OK) {
+ value = tabbingModes[modeIndex].modeValue;
+ }
+ Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&pathnameToTabbingMode,
+ Tcl_GetString(objv[2]), &isNew);
+ if (hPtr) {
+ Tcl_SetHashValue(hPtr, value);
+ return TCL_OK;
+ }
+ }
+ }
+ if (!winPtr) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "Only -class, -tabbingid, or -tabbingmode can be set before the window exists."));
+ Tcl_SetErrorCode(interp, "TK", "NO_WINDOW", NULL);
+ return TCL_ERROR;
+ }
+ if (winPtr && winPtr->window == None) {
Tk_MakeWindowExist((Tk_Window)winPtr);
}
if (!TkMacOSXHostToplevelExists(winPtr)) {
@@ -3097,7 +3614,7 @@ WmMinsizeCmd(
static int
WmOverrideredirectCmd(
- TCL_UNUSED(Tk_Window), /* Main window of the application. */
+ TCL_UNUSED(Tk_Window), /* Main window of the application. */
TkWindow *winPtr, /* Toplevel to work with */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
@@ -3123,7 +3640,15 @@ WmOverrideredirectCmd(
}
atts.override_redirect = flag ? True : False;
Tk_ChangeWindowAttributes((Tk_Window)winPtr, CWOverrideRedirect, &atts);
- ApplyContainerOverrideChanges(winPtr, win);
+ if ([NSApp macOSVersion] >= 101300) {
+ if (flag) {
+ win.styleMask |= NSWindowStyleMaskDocModalWindow;
+ } else {
+ win.styleMask &= ~NSWindowStyleMaskDocModalWindow;
+ }
+ } else {
+ ApplyContainerOverrideChanges(winPtr, win);
+ }
return TCL_OK;
}
@@ -5672,10 +6197,10 @@ TkUnsupported1ObjCmd(
Tcl_Obj *const objv[]) /* Argument objects. */
{
static const char *const subcmds[] = {
- "appearance", "isdark", "style", "tabbingid", NULL
+ "appearance", "isdark", "style", NULL
};
enum SubCmds {
- TKMWS_APPEARANCE, TKMWS_ISDARK, TKMWS_STYLE, TKMWS_TABID
+ TKMWS_APPEARANCE, TKMWS_ISDARK, TKMWS_STYLE
};
Tk_Window tkwin = (Tk_Window)clientData;
TkWindow *winPtr;
@@ -5709,18 +6234,6 @@ TkUnsupported1ObjCmd(
return TCL_ERROR;
}
return WmWinStyle(interp, winPtr, objc, objv);
- case TKMWS_TABID:
- if ([NSApp macOSVersion] < 101200) {
- Tcl_SetObjResult(interp, Tcl_NewStringObj(
- "Tabbing identifiers did not exist until OSX 10.12.", TCL_INDEX_NONE));
- Tcl_SetErrorCode(interp, "TK", "WINDOWSTYLE", "TABBINGID", NULL);
- return TCL_ERROR;
- }
- if ((objc < 3) || (objc > 4)) {
- Tcl_WrongNumArgs(interp, 2, objv, "window ?newid?");
- return TCL_ERROR;
- }
- return WmWinTabbingId(interp, winPtr, objc, objv);
case TKMWS_APPEARANCE:
if ([NSApp macOSVersion] < 100900) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
@@ -5940,87 +6453,6 @@ WmWinStyle(
/*
*----------------------------------------------------------------------
*
- * WmWinTabbingId --
- *
- * This procedure is invoked to process the
- * "::tk::unsupported::MacWindowStyle tabbingid" subcommand. The command
- * allows you to get or set the tabbingIdentifier for the NSWindow
- * associated with a Tk Window. The syntax is:
- *
- * tk::unsupported::MacWindowStyle tabbingid window ?newId?
- *
- * Results:
- * Returns the tabbingIdentifier of the window prior to calling this
- * function. If the optional newId argument is omitted, the window's
- * tabbingIdentifier is not changed.
- *
- * Side effects:
- * Windows may only be grouped together as tabs if they all have the same
- * tabbingIdentifier. In particular, by giving a window a unique
- * tabbingIdentifier one can prevent it from becoming a tab in any other
- * window. Changing the tabbingIdentifier of a window which is already
- * a tab causes it to become a separate window.
- *
- *----------------------------------------------------------------------
- */
-
-static int
-WmWinTabbingId(
- 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 < 101200
- (void) interp;
- (void) winPtr;
- (void) objc;
- (void) objv;
- return TCL_OK;
-#else
- Tcl_Obj *result = NULL;
- NSString *idString;
- NSWindow *win = TkMacOSXGetNSWindowForDrawable(winPtr->window);
- if (win) {
- idString = win.tabbingIdentifier;
- result = Tcl_NewStringObj(idString.UTF8String, [idString length]);
- }
- if (result == NULL) {
- NSLog(@"Failed to read tabbing identifier; try calling update idletasks"
- " before getting/setting the tabbing identifier of the window.");
- return TCL_OK;
- }
- Tcl_SetObjResult(interp, result);
- if (objc == 3) {
- return TCL_OK;
- } else if (objc == 4) {
- int len;
- char *newId = Tcl_GetStringFromObj(objv[3], &len);
- NSString *newIdString = [NSString stringWithUTF8String:newId];
- [win setTabbingIdentifier: newIdString];
-
- /*
- * If the tabbingIdentifier of a tab is changed we also turn it into a
- * separate window so we don't violate the rule that all tabs in the
- * same frame must have the same tabbingIdentifier.
- */
-
- if ([idString compare:newIdString] != NSOrderedSame
-#if MAC_OS_X_VERSION_MIN_REQUIRED > 101200
- && [win tab]
-#endif
- ) {
- [win moveTabToNewWindow:nil];
- }
- return TCL_OK;
- }
- return TCL_ERROR;
-#endif
-}
-
-/*
- *----------------------------------------------------------------------
- *
* WmWinAppearance --
*
* This procedure is invoked to process the
@@ -6063,12 +6495,6 @@ WmWinAppearance(
(void) objv;
return TCL_OK;
#else
- static const char *const appearanceStrings[] = {
- "aqua", "auto", "darkaqua", NULL
- };
- enum appearances {
- APPEARANCE_AQUA, APPEARANCE_AUTO, APPEARANCE_DARKAQUA
- };
Tcl_Obj *result = NULL;
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
NSAppearanceName appearance;
@@ -6187,7 +6613,14 @@ TkMacOSXMakeRealWindowExist(
WmInfo *wmPtr = winPtr->wmInfoPtr;
MacDrawable *macWin;
WindowClass macClass;
+ Class winClass = nil;
Bool overrideRedirect = Tk_Attributes((Tk_Window)winPtr)->override_redirect;
+ Tcl_HashEntry *hPtr = NULL;
+ NSUInteger styleMask;
+ NSString *identifier;
+ char *tabbingId = NULL;
+ long tabbingMode = NSWindowTabbingModeAutomatic;
+ static int initialized = 0;
if (TkMacOSXHostToplevelExists(winPtr)) {
return;
@@ -6218,51 +6651,117 @@ TkMacOSXMakeRealWindowExist(
*/
}
- /*
- * If this is an override-redirect window, the NSWindow is created first as
- * a document window then converted to a simple window.
- */
+ if ([NSApp macOSVersion] >= 101300) {
+ /*
+ * Prior to macOS 10.12 the styleMask was readonly. From macOS 10.12
+ * onward, the styleMask can replace the Carbon window classes and
+ * attributes.
+ */
+ int index;
+ if (!initialized) {
+ Tcl_InitHashTable(&pathnameToSubclass, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&pathnameToTabbingId, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&pathnameToTabbingMode, TCL_STRING_KEYS);
+ initialized = 1;
+ }
+ hPtr = Tcl_FindHashEntry(&pathnameToSubclass, Tk_PathName(winPtr));
+ index = hPtr ? PTR2INT(Tcl_GetHashValue(hPtr)) : subclassNSWindow;
+ switch(index) {
+ case subclassNSPanel:
+ winClass = [TKPanel class];
+ styleMask = (NSWindowStyleMaskTitled |
+ NSWindowStyleMaskClosable |
+ NSWindowStyleMaskResizable |
+ NSWindowStyleMaskUtilityWindow |
+ NSWindowStyleMaskNonactivatingPanel );
+ break;
+ default:
+ winClass = [TKWindow class];
+ styleMask = (NSWindowStyleMaskTitled |
+ NSWindowStyleMaskClosable |
+ NSWindowStyleMaskMiniaturizable |
+ NSWindowStyleMaskResizable );
+ break;
+ }
+ if (overrideRedirect) {
+ styleMask |= NSWindowStyleMaskDocModalWindow;
+ }
+ /* Help windows (used for tooltips) should have stylemask 0. */
+ if (wmPtr->macClass == kHelpWindowClass) {
+ styleMask = 0;
+ }
+ if (hPtr) {
+ Tcl_DeleteHashEntry(hPtr);
+ }
+ hPtr = Tcl_FindHashEntry(&pathnameToTabbingId, Tk_PathName(winPtr));
+ if (hPtr) {
+ tabbingId = Tcl_GetHashValue(hPtr);
+ Tcl_DeleteHashEntry(hPtr);
+ ckfree(tabbingId);
+ }
+ hPtr = Tcl_FindHashEntry(&pathnameToTabbingMode, Tk_PathName(winPtr));
+ if (hPtr) {
+ tabbingMode = PTR2INT(Tcl_GetHashValue(hPtr));
+ Tcl_DeleteHashEntry(hPtr);
+ }
+ } else {
- if (overrideRedirect) {
- wmPtr->macClass = kDocumentWindowClass;
- }
- macClass = wmPtr->macClass;
- wmPtr->attributes &= (tkAlwaysValidAttributes |
- macClassAttrs[macClass].validAttrs);
- wmPtr->flags |= macClassAttrs[macClass].flags |
+ /*
+ * If this is an override-redirect window, the NSWindow is created first as
+ * a document window then converted to a simple window.
+ */
+
+ if (overrideRedirect) {
+ wmPtr->macClass = kDocumentWindowClass;
+ }
+ macClass = wmPtr->macClass;
+ wmPtr->attributes &= (tkAlwaysValidAttributes |
+ macClassAttrs[macClass].validAttrs);
+ wmPtr->flags |= macClassAttrs[macClass].flags |
((wmPtr->attributes & kWindowResizableAttribute) ? 0 :
- WM_WIDTH_NOT_RESIZABLE|WM_HEIGHT_NOT_RESIZABLE);
- UInt64 attributes = (wmPtr->attributes &
- ~macClassAttrs[macClass].forceOffAttrs) |
+ WM_WIDTH_NOT_RESIZABLE|WM_HEIGHT_NOT_RESIZABLE);
+ UInt64 attributes = (wmPtr->attributes &
+ ~macClassAttrs[macClass].forceOffAttrs) |
macClassAttrs[macClass].forceOnAttrs;
- NSUInteger styleMask = macClassAttrs[macClass].styleMask |
- ((attributes & kWindowNoTitleBarAttribute) ? 0 : NSTitledWindowMask) |
- ((attributes & kWindowCloseBoxAttribute) ? NSClosableWindowMask : 0) |
- ((attributes & kWindowCollapseBoxAttribute) ?
- NSMiniaturizableWindowMask : 0) |
- ((attributes & kWindowResizableAttribute) ? NSResizableWindowMask : 0) |
- ((attributes & kWindowMetalAttribute) ?
- NSTexturedBackgroundWindowMask : 0) |
- ((attributes & kWindowUnifiedTitleAndToolbarAttribute) ?
- NSUnifiedTitleAndToolbarWindowMask : 0) |
- ((attributes & kWindowSideTitlebarAttribute) ? 1 << 9 : 0) |
- (attributes >> WM_NSMASK_SHIFT);
- Class winClass = (macClass == kDrawerWindowClass ? [TKDrawerWindow class] :
- (styleMask & (NSUtilityWindowMask|NSDocModalWindowMask|
- NSNonactivatingPanelMask|NSHUDWindowMask)) ? [TKPanel class] :
- [TKWindow class]);
+ styleMask = macClassAttrs[macClass].styleMask |
+ ((attributes & kWindowNoTitleBarAttribute) ? 0 : NSTitledWindowMask) |
+ ((attributes & kWindowCloseBoxAttribute) ? NSClosableWindowMask : 0) |
+ ((attributes & kWindowCollapseBoxAttribute) ?
+ NSMiniaturizableWindowMask : 0) |
+ ((attributes & kWindowResizableAttribute) ? NSResizableWindowMask : 0) |
+ ((attributes & kWindowMetalAttribute) ?
+ NSTexturedBackgroundWindowMask : 0) |
+ ((attributes & kWindowUnifiedTitleAndToolbarAttribute) ?
+ NSUnifiedTitleAndToolbarWindowMask : 0) |
+ ((attributes & kWindowSideTitlebarAttribute) ? 1 << 9 : 0) |
+ (attributes >> WM_NSMASK_SHIFT);
+ winClass = (macClass == kDrawerWindowClass ? [TKDrawerWindow class] :
+ (styleMask & (NSUtilityWindowMask|NSDocModalWindowMask|
+ NSNonactivatingPanelMask|NSHUDWindowMask)) ?
+ [TKPanel class] : [TKWindow class]);
+ }
NSRect structureRect = [winClass frameRectForContentRect:NSZeroRect
styleMask:styleMask];
NSRect contentRect = NSMakeRect(5 - structureRect.origin.x,
TkMacOSXZeroScreenHeight() - (TkMacOSXZeroScreenTop() + 5 +
structureRect.origin.y + structureRect.size.height + 200), 200, 200);
if (wmPtr->hints.initial_state == WithdrawnState) {
+ //// ???????
}
TKWindow *window = [[winClass alloc] initWithContentRect:contentRect
styleMask:styleMask backing:NSBackingStoreBuffered defer:YES];
if (!window) {
Tcl_Panic("couldn't allocate new Mac window");
}
+#if MAC_OS_X_VERSION_MAX_ALLOWED > 101200
+ if (tabbingId) {
+ identifier = [NSString stringWithUTF8String:tabbingId];
+ } else {
+ identifier = [NSString stringWithUTF8String:Tk_PathName(winPtr)];
+ }
+ [window setTabbingIdentifier: identifier];
+ [window setTabbingMode: tabbingMode];
+#endif
TKContentView *contentView = [[TKContentView alloc]
initWithFrame:NSZeroRect];
[window setContentView:contentView];
@@ -6299,7 +6798,11 @@ TkMacOSXMakeRealWindowExist(
atts.override_redirect = True;
Tk_ChangeWindowAttributes((Tk_Window)winPtr, CWOverrideRedirect, &atts);
- ApplyContainerOverrideChanges(winPtr, NULL);
+ if ([NSApp macOSVersion] >= 101300) {
+ window.styleMask |= NSWindowStyleMaskDocModalWindow;
+ } else {
+ ApplyContainerOverrideChanges(winPtr, NULL);
+ }
}
[window display];
}
diff --git a/macosx/ttkMacOSXTheme.c b/macosx/ttkMacOSXTheme.c
index 7c33a37..504446b 100644
--- a/macosx/ttkMacOSXTheme.c
+++ b/macosx/ttkMacOSXTheme.c
@@ -263,6 +263,9 @@ static CGRect NormalizeButtonBounds(
case TkRecessedButton:
bounds.size.height -= 2;
break;
+ case TkSidebarButton:
+ bounds.size.height += 8;
+ break;
case kThemeRoundButtonHelp:
if (isDark) {
bounds.size.height = bounds.size.width = 22;
@@ -1204,6 +1207,9 @@ static void DrawButton(
case TkRecessedButton:
DrawGrayButton(context, bounds, &recessedDesign, state, tkwin);
break;
+ case TkSidebarButton:
+ DrawGrayButton(context, bounds, &sidebarDesign, state, tkwin);
+ break;
case kThemeRoundedBevelButton:
DrawGrayButton(context, bounds, &bevelDesign, state, tkwin);
break;
@@ -1689,6 +1695,9 @@ static void ButtonElementSize(
ButtonElementMinSize(clientData, minWidth, minHeight);
switch (info.kind) {
+ case TkSidebarButton:
+ *paddingPtr = Ttk_MakePadding(30, 10, 30, 10);
+ return;
case TkGradientButton:
*paddingPtr = Ttk_MakePadding(1, 1, 1, 1);
/* Fall through. */
@@ -1769,6 +1778,7 @@ static void ButtonElementDraw(
case kThemeArrowButton:
case kThemeCheckBox:
case kThemeRadioButton:
+ case TkSidebarButton:
break;
/*
@@ -3445,6 +3455,13 @@ TTK_LAYOUT("RecessedButton",
TTK_GROUP("Button.padding", TTK_FILL_BOTH,
TTK_NODE("Button.label", TTK_FILL_BOTH))))
+/* Sidebar Button - text only radio button for sidebars */
+
+TTK_LAYOUT("SidebarButton",
+ TTK_GROUP("SidebarButton.button", TTK_FILL_BOTH,
+ TTK_GROUP("Button.padding", TTK_FILL_BOTH,
+ TTK_NODE("Button.label", TTK_FILL_BOTH))))
+
/* DisclosureButton (not a triangle) -- No label, no border*/
TTK_LAYOUT("DisclosureButton",
TTK_NODE("DisclosureButton.button", TTK_FILL_BOTH))
@@ -3581,6 +3598,8 @@ static int AquaTheme_Init(
&ButtonElementSpec, &RadioButtonParams);
Ttk_RegisterElementSpec(themePtr, "RecessedButton.button",
&ButtonElementSpec, &RecessedButtonParams);
+ Ttk_RegisterElementSpec(themePtr, "SidebarButton.button",
+ &ButtonElementSpec, &SidebarButtonParams);
Ttk_RegisterElementSpec(themePtr, "Toolbutton.border",
&ButtonElementSpec, &BevelButtonParams);
Ttk_RegisterElementSpec(themePtr, "Menubutton.button",
diff --git a/macosx/ttkMacOSXTheme.h b/macosx/ttkMacOSXTheme.h
index 977c168..994cb10 100644
--- a/macosx/ttkMacOSXTheme.h
+++ b/macosx/ttkMacOSXTheme.h
@@ -302,6 +302,27 @@ static const ButtonDesign recessedDesign = {
}
};
+static const ButtonDesign sidebarDesign = {
+ .radius = 8.0,
+ .palettes = {
+ {
+ .light = {.face = 210.0, .top = 210.0, .side = 210.0, .bottom = 210.0},
+ .dark = {.face = 129.0, .top = 129.0, .side = 129.0, .bottom = 129.0},
+ .onBits = TTK_STATE_SELECTED, .offBits = 0
+ },
+ {
+ .light = {.face = 210.0, .top = 210.0, .side = 210.0, .bottom = 210.0},
+ .dark = {.face = 129.0, .top = 129.0, .side = 129.0, .bottom = 129.0},
+ .onBits = TTK_STATE_PRESSED, .offBits = 0
+ },
+ {
+ .light = {.face = 256.0, .top = 256.0, .side = 256.0, .bottom = 256.0},
+ .dark = {.face = 256.0, .top = 256.0, .side = 256.0, .bottom = 256.0},
+ .onBits = 0, .offBits = 0
+ }
+ }
+};
+
static const ButtonDesign incdecDesign = {
.radius = 5.0,
.palettes = {
@@ -474,6 +495,7 @@ static const Ttk_StateTable ButtonAdornmentTable[] = {
#define TkRoundedRectButton 0x8002
#define TkRecessedButton 0x8003
#define TkInlineButton 0x8004
+#define TkSidebarButton 0x8005
/*
* The struct passed as clientData when drawing Ttk buttons.
*/
@@ -509,8 +531,8 @@ static ThemeButtonParams
NoThemeMetric},
RecessedButtonParams = {TkRecessedButton, kThemeMetricPushButtonHeight,
NoThemeMetric},
- InlineButtonParams = {TkInlineButton, kThemeMetricPushButtonHeight,
- NoThemeMetric};
+ SidebarButtonParams = {TkSidebarButton, NoThemeMetric, NoThemeMetric},
+ InlineButtonParams = {TkInlineButton, kThemeMetricPushButtonHeight, NoThemeMetric};
/*
* Others: kThemeDisclosureRight, kThemeDisclosureDown,
diff --git a/tests/event.test b/tests/event.test
index f08bac0..4cbd529 100644
--- a/tests/event.test
+++ b/tests/event.test
@@ -856,7 +856,9 @@ test event-9.1 {enter . window by destroying a toplevel - bug b1d115fa60} -setup
_pause 200
toplevel .top2 -width 200 -height 200
wm geometry .top2 +[expr {[winfo rootx .]+50}]+[expr {[winfo rooty .]+50}]
+ update idletasks
wm deiconify .top2
+ update idletasks
raise .top2
_pause 400
event generate .top2 <Motion> -warp 1 -x 50 -y 50
@@ -864,6 +866,7 @@ test event-9.1 {enter . window by destroying a toplevel - bug b1d115fa60} -setup
bind . <Enter> {lappend res %W}
set res [list ]
destroy .top2
+ update idletasks
_pause 200
set res
} -cleanup {
@@ -884,7 +887,9 @@ test event-9.2 {enter toplevel window by destroying a toplevel - bug b1d115fa60}
_pause 200
toplevel .top2 -width 200 -height 200
wm geometry .top2 +[expr {[winfo rootx .top1]+50}]+[expr {[winfo rooty .top1]+50}]
+ update idletasks
wm deiconify .top2
+ update idletasks
raise .top2
_pause 400
event generate .top2 <Motion> -warp 1 -x 50 -y 50
diff --git a/tests/unixWm.test b/tests/unixWm.test
index 17e8bc0..f24108a 100644
--- a/tests/unixWm.test
+++ b/tests/unixWm.test
@@ -2067,7 +2067,9 @@ test unixWm-51.5 {TkWmRestackToplevel procedure, basic tests} {unix nonPortable}
} {.raise1 .raise3}
deleteWindows
test unixWm-51.6 {TkWmRestackToplevel procedure, window to be stacked isn't mapped} unix {
+ wm geometry . +300+300
destroy .t
+ update idletasks
toplevel .t -width 200 -height 200 -bg green
tkwait visibility .t
wm geometry .t +0+0
@@ -2530,9 +2532,7 @@ test unixWm-59.3 {exit processing} unix {
# the only thing we can really test here is the syntax.
#
if {[tk windowingsystem] eq "aqua"} {
- set result_60_1 {-alpha 1.0 -fullscreen 0 -modified 0 -notify 0\
- -titlepath {} -topmost 0 -transparent 0\
- -type unsupported}
+ set result_60_1 {-alpha 1.0 -appearance auto -buttons {close miniaturize zoom} -fullscreen 0 -isdark 0 -modified 0 -notify 0 -titlepath {} -topmost 0 -transparent 0 -stylemask {titled closable miniaturizable resizable} -class nswindow -tabbingid .t -tabbingmode auto -type unsupported}
} else {
set result_60_1 {-alpha 1.0 -fullscreen 0 -topmost 0 -type {} -zoomed 0}
}
diff --git a/tests/wm.test b/tests/wm.test
index f3436e1..9d60eb4 100644
--- a/tests/wm.test
+++ b/tests/wm.test
@@ -204,10 +204,12 @@ test wm-attributes-1.2.3 {usage} -constraints win -returnCodes error -body {
test wm-attributes-1.2.4 {usage} -constraints {unix notAqua} -returnCodes error -body {
wm attributes . _
} -result {bad attribute "_": must be -alpha, -fullscreen, -topmost, -type, or -zoomed}
+if {[tk windowingsystem] eq "aqua"} {
+ set result_1_2_5 {bad attribute "_": must be -alpha, -appearance, -buttons, -fullscreen, -isdark, -modified, -notify, -titlepath, -topmost, -transparent, -stylemask, -class, -tabbingid, -tabbingmode, or -type}
+} else {set result_1_2_5 {bad attribute "_": must be -alpha, -fullscreen, -modified, -notify, -titlepath, -topmost, -transparent, or -type}}
test wm-attributes-1.2.5 {usage} -constraints aqua -returnCodes error -body {
wm attributes . _
-} -result {bad attribute "_": must be -alpha, -fullscreen, -modified, -notify, -titlepath, -topmost, -transparent, or -type}
-
+} -result $result_1_2_5
### wm client ###
test wm-client-1.1 {usage} -returnCodes error -body {