2 # $Id: utils.tcl,v 1.6 2008/01/06 19:16:12 jenglish Exp $
4 # Utilities for widget implementations.
11 # This is the default value of the "-takefocus" option
12 # for widgets that participate in keyboard navigation.
14 # See also: tk::FocusOK
16 proc ttk
::takefocus {w
} {
17 expr {[$w instate
!disabled
] && [winfo viewable
$w]}
20 ## ttk::traverseTo $w --
21 # Set the keyboard focus to the specified window.
23 proc ttk
::traverseTo {w
} {
26 event generate
$focus <<TraverseOut
>>
29 event generate
$w <<TraverseIn
>>
32 ## ttk::clickToFocus $w --
33 # Utility routine, used in <ButtonPress-1> bindings --
34 # Assign keyboard focus to the specified widget if -takefocus is enabled.
36 proc ttk
::clickToFocus {w
} {
37 if {[ttk
::takesFocus $w]} { focus $w }
40 ## ttk::takesFocus w --
41 # Test if the widget can take keyboard focus:
43 # + widget is viewable, AND:
44 # - if -takefocus is missing or empty, return 0, OR
45 # - if -takefocus is 0 or 1, return that value, OR
46 # - append the widget name to -takefocus and evaluate it
49 # See also: tk::FocusOK
51 # Note: This routine doesn't implement the same fallback heuristics
54 proc ttk
::takesFocus {w
} {
56 if {![winfo viewable
$w]} { return 0 }
58 if {![catch {$w cget
-takefocus} takefocus
]} {
59 switch -- $takefocus {
61 1 { return $takefocus }
64 set value
[uplevel #0 $takefocus [list $w]]
65 return [expr {$value eq
1}]
73 ## ttk::focusFirst $w --
74 # Return the first descendant of $w, in preorder traversal order,
75 # that can take keyboard focus, "" if none do.
77 # See also: tk_focusNext
80 proc ttk
::focusFirst {w
} {
81 if {[ttk
::takesFocus $w]} {
84 foreach child
[winfo children
$w] {
85 if {[set c
[ttk
::focusFirst $child]] ne
""} {
95 # Each call to [grabWindow $w] or [globalGrab $w] must be
96 # matched with a call to [releaseGrab $w] in LIFO order.
98 # Do not call [grabWindow $w] for a window that currently
99 # appears on the grab stack.
101 # See #1239190 and #1411983 for more discussion.
104 variable Grab
;# map: window name -> grab token
106 # grab token details:
107 # Two-element list containing:
108 # 1) a script to evaluate to restore the previous grab (if any);
109 # 2) a script to evaluate to restore the focus (if any)
113 # Record current grab and focus windows.
115 proc ttk
::SaveGrab {w
} {
118 if {[info exists Grab
($w)]} {
119 # $w is already on the grab stack.
120 # This should not happen, but bail out in case it does anyway:
125 set restoreGrab
[set restoreFocus
""]
127 set grabbed
[grab current
$w]
128 if {[winfo exists
$grabbed]} {
129 switch [grab status
$grabbed] {
130 global { set restoreGrab
[list grab -global $grabbed] }
131 local
{ set restoreGrab
[list grab $grabbed] }
132 none
{ ;# grab window is really in a different interp }
138 set restoreFocus
[list focus -force $focus]
141 set Grab
($w) [list $restoreGrab $restoreFocus]
145 # Restore previous grab and focus windows.
146 # If called more than once without an intervening [SaveGrab $w],
149 proc ttk
::RestoreGrab {w
} {
152 if {![info exists Grab
($w)]} { # Ignore
156 # The previous grab/focus window may have been destroyed,
157 # unmapped, or some other abnormal condition; ignore any errors.
159 foreach script
$Grab($w) {
166 ## ttk::grabWindow $w --
167 # Records the current focus and grab windows, sets an application-modal
170 proc ttk
::grabWindow {w
} {
175 ## ttk::globalGrab $w --
176 # Same as grabWindow, but sets a global grab on $w.
178 proc ttk
::globalGrab {w
} {
183 ## ttk::releaseGrab --
184 # Release the grab previously set by [ttk::grabWindow]
185 # or [ttk::globalGrab].
187 proc ttk
::releaseGrab {w
} {
194 # NOTE: repeating widgets do not have -repeatdelay
195 # or -repeatinterval resources as in standard Tk;
196 # instead a single set of settings is applied application-wide.
197 # (TODO: make this user-configurable)
199 # (@@@ Windows seems to use something like 500/50 milliseconds
200 # @@@ for -repeatdelay/-repeatinterval)
213 ## ttk::Repeatedly --
216 proc ttk
::Repeatedly {args
} {
218 after cancel
$Repeat(timer
)
219 set script
[uplevel 1 [list namespace code
$args]]
220 set Repeat
(script
) $script
222 set Repeat
(timer
) [after $Repeat(delay
) ttk
::Repeat]
226 # Continue auto-repeat
228 proc ttk
::Repeat {} {
230 uplevel #0 $Repeat(script)
231 set Repeat
(timer
) [after $Repeat(interval
) ttk
::Repeat]
234 ## ttk::CancelRepeat --
237 proc ttk
::CancelRepeat {} {
239 after cancel
$Repeat(timer
)
245 ## ttk::copyBindings $from $to --
246 # Utility routine; copies bindings from one bindtag onto another.
248 proc ttk
::copyBindings {from to
} {
249 foreach event [bind $from] {
250 bind $to $event [bind $from $event]
254 ## Standard mousewheel bindings.
256 # Usage: [ttk::copyBindings TtkScrollable $bindtag]
257 # adds mousewheel support to a scrollable widget.
259 # Platform inconsistencies:
261 # On X11, the server typically maps the mouse wheel to Button4 and Button5.
263 # On OSX, Tk generates sensible values for the %D field in <MouseWheel> events.
265 # On Windows, %D must be scaled by a factor of 120.
266 # In addition, Tk redirects mousewheel events to the window with
267 # keyboard focus instead of sending them to the window under the pointer.
268 # We do not attempt to fix that here, see also TIP#171.
270 # OSX conventionally uses Shift+MouseWheel for horizontal scrolling,
271 # and Option+MouseWheel for accelerated scrolling.
273 # The Shift+MouseWheel behavior is not conventional on Windows or most
274 # X11 toolkits, but it's useful.
276 # MouseWheel scrolling is accelerated on X11, which is conventional
277 # for Tk and appears to be conventional for other toolkits (although
278 # Gtk+ and Qt do not appear to use as large a factor).
281 switch -- [tk windowingsystem
] {
283 bind TtkScrollable
<ButtonPress-4
> { %W yview scroll
-5 units
}
284 bind TtkScrollable
<ButtonPress-5
> { %W yview scroll
5 units
}
285 bind TtkScrollable
<Shift-ButtonPress-4
> { %W xview scroll
-5 units
}
286 bind TtkScrollable
<Shift-ButtonPress-5
> { %W xview scroll
5 units
}
289 bind TtkScrollable
<MouseWheel
> \
290 { %W yview scroll
[expr {-(%D
/120)}] units
}
291 bind TtkScrollable
<Shift-MouseWheel
> \
292 { %W xview scroll
[expr {-(%D
/120)}] units
}
295 bind TtkScrollable
<MouseWheel
> \
296 { %W yview scroll
[expr {-(%D
)}] units
}
297 bind TtkScrollable
<Shift-MouseWheel
> \
298 { %W xview scroll
[expr {-(%D
)}] units
}
299 bind TtkScrollable
<Option-MouseWheel
> \
300 { %W yview scroll
[expr {-10*(%D
)}] units
}
301 bind TtkScrollable
<Shift-Option-MouseWheel
> \
302 { %W xview scroll
[expr {-10*(%D
)}] units
}