utils.tcl 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. #
  2. # Utilities for widget implementations.
  3. #
  4. ### Focus management.
  5. #
  6. # See also: #1516479
  7. #
  8. ## ttk::takefocus --
  9. # This is the default value of the "-takefocus" option
  10. # for ttk::* widgets that participate in keyboard navigation.
  11. #
  12. # NOTES:
  13. # tk::FocusOK (called by tk_focusNext) tests [winfo viewable]
  14. # if -takefocus is 1, empty, or missing; but not if it's a
  15. # script prefix, so we have to check that here as well.
  16. #
  17. #
  18. proc ttk::takefocus {w} {
  19. expr {[$w instate !disabled] && [winfo viewable $w]}
  20. }
  21. ## ttk::GuessTakeFocus --
  22. # This routine is called as a fallback for widgets
  23. # with a missing or empty -takefocus option.
  24. #
  25. # It implements the same heuristics as tk::FocusOK.
  26. #
  27. proc ttk::GuessTakeFocus {w} {
  28. # Don't traverse to widgets with '-state disabled':
  29. #
  30. if {![catch {$w cget -state} state] && $state eq "disabled"} {
  31. return 0
  32. }
  33. # Allow traversal to widgets with explicit key or focus bindings:
  34. #
  35. if {[regexp {Key|Focus} [concat [bind $w] [bind [winfo class $w]]]]} {
  36. return 1;
  37. }
  38. # Default is nontraversable:
  39. #
  40. return 0;
  41. }
  42. ## ttk::traverseTo $w --
  43. # Set the keyboard focus to the specified window.
  44. #
  45. proc ttk::traverseTo {w} {
  46. set focus [focus]
  47. if {$focus ne ""} {
  48. event generate $focus <<TraverseOut>>
  49. }
  50. focus $w
  51. event generate $w <<TraverseIn>>
  52. }
  53. ## ttk::clickToFocus $w --
  54. # Utility routine, used in <Button-1> bindings --
  55. # Assign keyboard focus to the specified widget if -takefocus is enabled.
  56. #
  57. proc ttk::clickToFocus {w} {
  58. if {[ttk::takesFocus $w]} { focus $w }
  59. }
  60. ## ttk::takesFocus w --
  61. # Test if the widget can take keyboard focus.
  62. #
  63. # See the description of the -takefocus option in options(n)
  64. # for details.
  65. #
  66. proc ttk::takesFocus {w} {
  67. if {![winfo viewable $w]} {
  68. return 0
  69. } elseif {[catch {$w cget -takefocus} takefocus]} {
  70. return [GuessTakeFocus $w]
  71. } else {
  72. switch -- $takefocus {
  73. "" { return [GuessTakeFocus $w] }
  74. 0 { return 0 }
  75. 1 { return 1 }
  76. default {
  77. return [expr {[uplevel #0 $takefocus [list $w]] == 1}]
  78. }
  79. }
  80. }
  81. }
  82. ## ttk::focusFirst $w --
  83. # Return the first descendant of $w, in preorder traversal order,
  84. # that can take keyboard focus, "" if none do.
  85. #
  86. # See also: tk_focusNext
  87. #
  88. proc ttk::focusFirst {w} {
  89. if {[ttk::takesFocus $w]} {
  90. return $w
  91. }
  92. foreach child [winfo children $w] {
  93. if {[set c [ttk::focusFirst $child]] ne ""} {
  94. return $c
  95. }
  96. }
  97. return ""
  98. }
  99. ### Grabs.
  100. #
  101. # Rules:
  102. # Each call to [grabWindow $w] or [globalGrab $w] must be
  103. # matched with a call to [releaseGrab $w] in LIFO order.
  104. #
  105. # Do not call [grabWindow $w] for a window that currently
  106. # appears on the grab stack.
  107. #
  108. # See #1239190 and #1411983 for more discussion.
  109. #
  110. namespace eval ttk {
  111. variable Grab ;# map: window name -> grab token
  112. # grab token details:
  113. # Two-element list containing:
  114. # 1) a script to evaluate to restore the previous grab (if any);
  115. # 2) a script to evaluate to restore the focus (if any)
  116. }
  117. ## SaveGrab --
  118. # Record current grab and focus windows.
  119. #
  120. proc ttk::SaveGrab {w} {
  121. variable Grab
  122. if {[info exists Grab($w)]} {
  123. # $w is already on the grab stack.
  124. # This should not happen, but bail out in case it does anyway:
  125. #
  126. return
  127. }
  128. set restoreGrab [set restoreFocus ""]
  129. set grabbed [grab current $w]
  130. if {[winfo exists $grabbed]} {
  131. switch [grab status $grabbed] {
  132. global { set restoreGrab [list grab -global $grabbed] }
  133. local { set restoreGrab [list grab $grabbed] }
  134. none { ;# grab window is really in a different interp }
  135. }
  136. }
  137. set focus [focus]
  138. if {$focus ne ""} {
  139. set restoreFocus [list focus -force $focus]
  140. }
  141. set Grab($w) [list $restoreGrab $restoreFocus]
  142. }
  143. ## RestoreGrab --
  144. # Restore previous grab and focus windows.
  145. # If called more than once without an intervening [SaveGrab $w],
  146. # does nothing.
  147. #
  148. proc ttk::RestoreGrab {w} {
  149. variable Grab
  150. if {![info exists Grab($w)]} { # Ignore
  151. return;
  152. }
  153. # The previous grab/focus window may have been destroyed,
  154. # unmapped, or some other abnormal condition; ignore any errors.
  155. #
  156. foreach script $Grab($w) {
  157. catch $script
  158. }
  159. unset Grab($w)
  160. }
  161. ## ttk::grabWindow $w --
  162. # Records the current focus and grab windows, sets an application-modal
  163. # grab on window $w.
  164. #
  165. proc ttk::grabWindow {w} {
  166. SaveGrab $w
  167. grab $w
  168. }
  169. ## ttk::globalGrab $w --
  170. # Same as grabWindow, but sets a global grab on $w.
  171. #
  172. proc ttk::globalGrab {w} {
  173. SaveGrab $w
  174. grab -global $w
  175. }
  176. ## ttk::releaseGrab --
  177. # Release the grab previously set by [ttk::grabWindow]
  178. # or [ttk::globalGrab].
  179. #
  180. proc ttk::releaseGrab {w} {
  181. grab release $w
  182. RestoreGrab $w
  183. }
  184. ### Auto-repeat.
  185. #
  186. # NOTE: repeating widgets do not have -repeatdelay
  187. # or -repeatinterval resources as in standard Tk;
  188. # instead a single set of settings is applied application-wide.
  189. # (TODO: make this user-configurable)
  190. #
  191. # (@@@ Windows seems to use something like 500/50 milliseconds
  192. # @@@ for -repeatdelay/-repeatinterval)
  193. #
  194. namespace eval ttk {
  195. variable Repeat
  196. array set Repeat {
  197. delay 300
  198. interval 100
  199. timer {}
  200. script {}
  201. }
  202. }
  203. ## ttk::Repeatedly --
  204. # Begin auto-repeat.
  205. #
  206. proc ttk::Repeatedly {args} {
  207. variable Repeat
  208. after cancel $Repeat(timer)
  209. set script [uplevel 1 [list namespace code $args]]
  210. set Repeat(script) $script
  211. uplevel #0 $script
  212. set Repeat(timer) [after $Repeat(delay) ttk::Repeat]
  213. }
  214. ## Repeat --
  215. # Continue auto-repeat
  216. #
  217. proc ttk::Repeat {} {
  218. variable Repeat
  219. uplevel #0 $Repeat(script)
  220. set Repeat(timer) [after $Repeat(interval) ttk::Repeat]
  221. }
  222. ## ttk::CancelRepeat --
  223. # Halt auto-repeat.
  224. #
  225. proc ttk::CancelRepeat {} {
  226. variable Repeat
  227. after cancel $Repeat(timer)
  228. }
  229. ### Bindings.
  230. #
  231. ## ttk::copyBindings $from $to --
  232. # Utility routine; copies bindings from one bindtag onto another.
  233. #
  234. proc ttk::copyBindings {from to} {
  235. foreach event [bind $from] {
  236. bind $to $event [bind $from $event]
  237. }
  238. }
  239. ### Mousewheel bindings.
  240. #
  241. # Platform inconsistencies:
  242. #
  243. # On X11, the server typically maps the mouse wheel to Button4 and Button5.
  244. #
  245. # On OSX, Tk generates sensible values for the %D field in <MouseWheel> events.
  246. #
  247. # On Windows, %D must be scaled by a factor of 120.
  248. #
  249. # OSX conventionally uses Shift+MouseWheel for horizontal scrolling,
  250. # and Option+MouseWheel for accelerated scrolling.
  251. #
  252. # The Shift+MouseWheel behavior is not conventional on Windows or most
  253. # X11 toolkits, but it's useful.
  254. #
  255. # MouseWheel scrolling is accelerated on X11, which is conventional
  256. # for Tk and appears to be conventional for other toolkits (although
  257. # Gtk+ and Qt do not appear to use as large a factor).
  258. #
  259. ## ttk::bindMouseWheel $bindtag $command...
  260. # Adds basic mousewheel support to $bindtag.
  261. # $command will be passed one additional argument
  262. # specifying the mousewheel direction (-1: up, +1: down).
  263. #
  264. proc ttk::bindMouseWheel {bindtag callback} {
  265. if {[tk windowingsystem] eq "x11"} {
  266. bind $bindtag <Button-4> "$callback -1"
  267. bind $bindtag <Button-5> "$callback +1"
  268. }
  269. if {[tk windowingsystem] eq "aqua"} {
  270. bind $bindtag <MouseWheel> "$callback \[expr {-%D}\]"
  271. bind $bindtag <Option-MouseWheel> "$callback \[expr {-10*%D}\]"
  272. } else {
  273. bind $bindtag <MouseWheel> "$callback \[expr {-%D/120}\]"
  274. }
  275. }
  276. ## Mousewheel bindings for standard scrollable widgets.
  277. #
  278. # Usage: [ttk::copyBindings TtkScrollable $bindtag]
  279. #
  280. # $bindtag should be for a widget that supports the
  281. # standard scrollbar protocol.
  282. #
  283. if {[tk windowingsystem] eq "x11"} {
  284. bind TtkScrollable <Button-4> { %W yview scroll -5 units }
  285. bind TtkScrollable <Button-5> { %W yview scroll 5 units }
  286. bind TtkScrollable <Shift-Button-4> { %W xview scroll -5 units }
  287. bind TtkScrollable <Shift-Button-5> { %W xview scroll 5 units }
  288. }
  289. if {[tk windowingsystem] eq "aqua"} {
  290. bind TtkScrollable <MouseWheel> \
  291. { %W yview scroll [expr {-(%D)}] units }
  292. bind TtkScrollable <Shift-MouseWheel> \
  293. { %W xview scroll [expr {-(%D)}] units }
  294. bind TtkScrollable <Option-MouseWheel> \
  295. { %W yview scroll [expr {-10 * (%D)}] units }
  296. bind TtkScrollable <Shift-Option-MouseWheel> \
  297. { %W xview scroll [expr {-10 * (%D)}] units }
  298. } else {
  299. bind TtkScrollable <MouseWheel> \
  300. { %W yview scroll [expr {-(%D / 120)}] units }
  301. bind TtkScrollable <Shift-MouseWheel> \
  302. { %W xview scroll [expr {-(%D / 120)}] units }
  303. }
  304. #*EOF*