1# 2# $Id$ 3# 4# Combobox bindings. 5# 6# <<NOTE-WM-TRANSIENT>>: 7# 8# Need to set [wm transient] just before mapping the popdown 9# instead of when it's created, in case a containing frame 10# has been reparented [#1818441]. 11# 12# On Windows: setting [wm transient] prevents the parent 13# toplevel from becoming inactive when the popdown is posted 14# (Tk 8.4.8+) 15# 16# On X11: WM_TRANSIENT_FOR on override-redirect windows 17# may be used by compositing managers and by EWMH-aware 18# window managers (even though the older ICCCM spec says 19# it's meaningless). 20# 21# On OSX: [wm transient] does utterly the wrong thing. 22# Instead, we use [MacWindowStyle "help" "noActivates hideOnSuspend"]. 23# The "noActivates" attribute prevents the parent toplevel 24# from deactivating when the popdown is posted, and is also 25# necessary for "help" windows to receive mouse events. 26# "hideOnSuspend" makes the popdown disappear (resp. reappear) 27# when the parent toplevel is deactivated (resp. reactivated). 28# (see [#1814778]). Also set [wm resizable 0 0], to prevent 29# TkAqua from shrinking the scrollbar to make room for a grow box 30# that isn't there. 31# 32# In order to work around other platform quirks in TkAqua, 33# [grab] and [focus] are set in <Map> bindings instead of 34# immediately after deiconifying the window. 35# 36 37namespace eval ttk::combobox { 38 variable Values ;# Values($cb) is -listvariable of listbox widget 39 variable State 40 set State(entryPress) 0 41} 42 43### Combobox bindings. 44# 45# Duplicate the Entry bindings, override if needed: 46# 47 48ttk::copyBindings TEntry TCombobox 49 50bind TCombobox <KeyPress-Down> { ttk::combobox::Post %W } 51bind TCombobox <KeyPress-Escape> { ttk::combobox::Unpost %W } 52 53bind TCombobox <ButtonPress-1> { ttk::combobox::Press "" %W %x %y } 54bind TCombobox <Shift-ButtonPress-1> { ttk::combobox::Press "s" %W %x %y } 55bind TCombobox <Double-ButtonPress-1> { ttk::combobox::Press "2" %W %x %y } 56bind TCombobox <Triple-ButtonPress-1> { ttk::combobox::Press "3" %W %x %y } 57bind TCombobox <B1-Motion> { ttk::combobox::Drag %W %x } 58bind TCombobox <Motion> { ttk::combobox::Motion %W %x %y } 59 60ttk::bindMouseWheel TCombobox [list ttk::combobox::Scroll %W] 61 62bind TCombobox <<TraverseIn>> { ttk::combobox::TraverseIn %W } 63 64### Combobox listbox bindings. 65# 66bind ComboboxListbox <ButtonRelease-1> { ttk::combobox::LBSelected %W } 67bind ComboboxListbox <KeyPress-Return> { ttk::combobox::LBSelected %W } 68bind ComboboxListbox <KeyPress-Escape> { ttk::combobox::LBCancel %W } 69bind ComboboxListbox <KeyPress-Tab> { ttk::combobox::LBTab %W next } 70bind ComboboxListbox <<PrevWindow>> { ttk::combobox::LBTab %W prev } 71bind ComboboxListbox <Destroy> { ttk::combobox::LBCleanup %W } 72bind ComboboxListbox <Motion> { ttk::combobox::LBHover %W %x %y } 73bind ComboboxListbox <Map> { focus -force %W } 74 75switch -- [tk windowingsystem] { 76 win32 { 77 # Dismiss listbox when user switches to a different application. 78 # NB: *only* do this on Windows (see #1814778) 79 bind ComboboxListbox <FocusOut> { ttk::combobox::LBCancel %W } 80 } 81} 82 83### Combobox popdown window bindings. 84# 85bind ComboboxPopdown <Map> { ttk::combobox::MapPopdown %W } 86bind ComboboxPopdown <Unmap> { ttk::combobox::UnmapPopdown %W } 87bind ComboboxPopdown <ButtonPress> \ 88 { ttk::combobox::Unpost [winfo parent %W] } 89 90### Option database settings. 91# 92 93option add *TCombobox*Listbox.font TkTextFont 94option add *TCombobox*Listbox.relief flat 95option add *TCombobox*Listbox.highlightThickness 0 96 97## Platform-specific settings. 98# 99switch -- [tk windowingsystem] { 100 x11 { 101 option add *TCombobox*Listbox.background white 102 } 103 aqua { 104 option add *TCombobox*Listbox.borderWidth 0 105 } 106} 107 108### Binding procedures. 109# 110 111## Press $mode $x $y -- ButtonPress binding for comboboxes. 112# Either post/unpost the listbox, or perform Entry widget binding, 113# depending on widget state and location of button press. 114# 115proc ttk::combobox::Press {mode w x y} { 116 variable State 117 set State(entryPress) [expr { 118 [$w instate {!readonly !disabled}] 119 && [string match *textarea [$w identify $x $y]] 120 }] 121 122 focus $w 123 if {$State(entryPress)} { 124 switch -- $mode { 125 s { ttk::entry::Shift-Press $w $x ; # Shift } 126 2 { ttk::entry::Select $w $x word ; # Double click} 127 3 { ttk::entry::Select $w $x line ; # Triple click } 128 "" - 129 default { ttk::entry::Press $w $x } 130 } 131 } else { 132 Post $w 133 } 134} 135 136## Drag -- B1-Motion binding for comboboxes. 137# If the initial ButtonPress event was handled by Entry binding, 138# perform Entry widget drag binding; otherwise nothing. 139# 140proc ttk::combobox::Drag {w x} { 141 variable State 142 if {$State(entryPress)} { 143 ttk::entry::Drag $w $x 144 } 145} 146 147## Motion -- 148# Set cursor. 149# 150proc ttk::combobox::Motion {w x y} { 151 if { [$w identify $x $y] eq "textarea" 152 && [$w instate {!readonly !disabled}] 153 } { 154 ttk::setCursor $w text 155 } else { 156 ttk::setCursor $w "" 157 } 158} 159 160## TraverseIn -- receive focus due to keyboard navigation 161# For editable comboboxes, set the selection and insert cursor. 162# 163proc ttk::combobox::TraverseIn {w} { 164 $w instate {!readonly !disabled} { 165 $w selection range 0 end 166 $w icursor end 167 } 168} 169 170## SelectEntry $cb $index -- 171# Set the combobox selection in response to a user action. 172# 173proc ttk::combobox::SelectEntry {cb index} { 174 $cb current $index 175 $cb selection range 0 end 176 $cb icursor end 177 event generate $cb <<ComboboxSelected>> -when mark 178} 179 180## Scroll -- Mousewheel binding 181# 182proc ttk::combobox::Scroll {cb dir} { 183 $cb instate disabled { return } 184 set max [llength [$cb cget -values]] 185 set current [$cb current] 186 incr current $dir 187 if {$max != 0 && $current == $current % $max} { 188 SelectEntry $cb $current 189 } 190} 191 192## LBSelected $lb -- Activation binding for listbox 193# Set the combobox value to the currently-selected listbox value 194# and unpost the listbox. 195# 196proc ttk::combobox::LBSelected {lb} { 197 set cb [LBMaster $lb] 198 LBSelect $lb 199 Unpost $cb 200 focus $cb 201} 202 203## LBCancel -- 204# Unpost the listbox. 205# 206proc ttk::combobox::LBCancel {lb} { 207 Unpost [LBMaster $lb] 208} 209 210## LBTab -- Tab key binding for combobox listbox. 211# Set the selection, and navigate to next/prev widget. 212# 213proc ttk::combobox::LBTab {lb dir} { 214 set cb [LBMaster $lb] 215 switch -- $dir { 216 next { set newFocus [tk_focusNext $cb] } 217 prev { set newFocus [tk_focusPrev $cb] } 218 } 219 220 if {$newFocus ne ""} { 221 LBSelect $lb 222 Unpost $cb 223 # The [grab release] call in [Unpost] queues events that later 224 # re-set the focus (@@@ NOTE: this might not be true anymore). 225 # Set new focus later: 226 after 0 [list ttk::traverseTo $newFocus] 227 } 228} 229 230## LBHover -- <Motion> binding for combobox listbox. 231# Follow selection on mouseover. 232# 233proc ttk::combobox::LBHover {w x y} { 234 $w selection clear 0 end 235 $w activate @$x,$y 236 $w selection set @$x,$y 237} 238 239## MapPopdown -- <Map> binding for ComboboxPopdown 240# 241proc ttk::combobox::MapPopdown {w} { 242 [winfo parent $w] state pressed 243 ttk::globalGrab $w 244} 245 246## UnmapPopdown -- <Unmap> binding for ComboboxPopdown 247# 248proc ttk::combobox::UnmapPopdown {w} { 249 [winfo parent $w] state !pressed 250 ttk::releaseGrab $w 251} 252 253### 254# 255 256namespace eval ::ttk::combobox { 257 # @@@ Until we have a proper native scrollbar on Aqua, use 258 # @@@ the regular Tk one. Use ttk::scrollbar on other platforms. 259 variable scrollbar ttk::scrollbar 260 if {[tk windowingsystem] eq "aqua"} { 261 set scrollbar ::scrollbar 262 } 263} 264 265## PopdownWindow -- 266# Returns the popdown widget associated with a combobox, 267# creating it if necessary. 268# 269proc ttk::combobox::PopdownWindow {cb} { 270 variable scrollbar 271 272 if {![winfo exists $cb.popdown]} { 273 set poplevel [PopdownToplevel $cb.popdown] 274 set popdown [ttk::frame $poplevel.f -style ComboboxPopdownFrame] 275 276 $scrollbar $popdown.sb \ 277 -orient vertical -command [list $popdown.l yview] 278 listbox $popdown.l \ 279 -listvariable ttk::combobox::Values($cb) \ 280 -yscrollcommand [list $popdown.sb set] \ 281 -exportselection false \ 282 -selectmode browse \ 283 -activestyle none \ 284 ; 285 286 bindtags $popdown.l \ 287 [list $popdown.l ComboboxListbox Listbox $popdown all] 288 289 grid $popdown.l -row 0 -column 0 -padx {1 0} -pady 1 -sticky nsew 290 grid $popdown.sb -row 0 -column 1 -padx {0 1} -pady 1 -sticky ns 291 grid columnconfigure $popdown 0 -weight 1 292 grid rowconfigure $popdown 0 -weight 1 293 294 grid $popdown -sticky news -padx 0 -pady 0 295 grid rowconfigure $poplevel 0 -weight 1 296 grid columnconfigure $poplevel 0 -weight 1 297 } 298 return $cb.popdown 299} 300 301## PopdownToplevel -- Create toplevel window for the combobox popdown 302# 303# See also <<NOTE-WM-TRANSIENT>> 304# 305proc ttk::combobox::PopdownToplevel {w} { 306 toplevel $w -class ComboboxPopdown 307 wm withdraw $w 308 switch -- [tk windowingsystem] { 309 default - 310 x11 { 311 $w configure -relief flat -borderwidth 0 312 wm attributes $w -type combo 313 wm overrideredirect $w true 314 } 315 win32 { 316 $w configure -relief flat -borderwidth 0 317 wm overrideredirect $w true 318 wm attributes $w -topmost 1 319 } 320 aqua { 321 $w configure -relief solid -borderwidth 0 322 tk::unsupported::MacWindowStyle style $w \ 323 help {noActivates hideOnSuspend} 324 wm resizable $w 0 0 325 } 326 } 327 return $w 328} 329 330## ConfigureListbox -- 331# Set listbox values, selection, height, and scrollbar visibility 332# from current combobox values. 333# 334proc ttk::combobox::ConfigureListbox {cb} { 335 variable Values 336 337 set popdown [PopdownWindow $cb].f 338 set values [$cb cget -values] 339 set current [$cb current] 340 if {$current < 0} { 341 set current 0 ;# no current entry, highlight first one 342 } 343 set Values($cb) $values 344 $popdown.l selection clear 0 end 345 $popdown.l selection set $current 346 $popdown.l activate $current 347 $popdown.l see $current 348 set height [llength $values] 349 if {$height > [$cb cget -height]} { 350 set height [$cb cget -height] 351 grid $popdown.sb 352 grid configure $popdown.l -padx {1 0} 353 } else { 354 grid remove $popdown.sb 355 grid configure $popdown.l -padx 1 356 } 357 $popdown.l configure -height $height 358} 359 360## PlacePopdown -- 361# Set popdown window geometry. 362# 363# @@@TODO: factor with menubutton::PostPosition 364# 365proc ttk::combobox::PlacePopdown {cb popdown} { 366 set x [winfo rootx $cb] 367 set y [winfo rooty $cb] 368 set w [winfo width $cb] 369 set h [winfo height $cb] 370 set postoffset [ttk::style lookup TCombobox -postoffset {} {0 0 0 0}] 371 foreach var {x y w h} delta $postoffset { 372 incr $var $delta 373 } 374 375 set H [winfo reqheight $popdown] 376 if {$y + $h + $H > [winfo screenheight $popdown]} { 377 set Y [expr {$y - $H}] 378 } else { 379 set Y [expr {$y + $h}] 380 } 381 wm geometry $popdown ${w}x${H}+${x}+${Y} 382} 383 384## Post $cb -- 385# Pop down the associated listbox. 386# 387proc ttk::combobox::Post {cb} { 388 # Don't do anything if disabled: 389 # 390 $cb instate disabled { return } 391 392 # ASSERT: ![$cb instate pressed] 393 394 # Run -postcommand callback: 395 # 396 uplevel #0 [$cb cget -postcommand] 397 398 set popdown [PopdownWindow $cb] 399 ConfigureListbox $cb 400 update idletasks ;# needed for geometry propagation. 401 PlacePopdown $cb $popdown 402 # See <<NOTE-WM-TRANSIENT>> 403 switch -- [tk windowingsystem] { 404 x11 - win32 { wm transient $popdown [winfo toplevel $cb] } 405 } 406 407 # Post the listbox: 408 # 409 wm attribute $popdown -topmost 1 410 wm deiconify $popdown 411 raise $popdown 412} 413 414## Unpost $cb -- 415# Unpost the listbox. 416# 417proc ttk::combobox::Unpost {cb} { 418 if {[winfo exists $cb.popdown]} { 419 wm withdraw $cb.popdown 420 } 421 grab release $cb.popdown ;# in case of stuck or unexpected grab [#1239190] 422} 423 424## LBMaster $lb -- 425# Return the combobox main widget that owns the listbox. 426# 427proc ttk::combobox::LBMaster {lb} { 428 winfo parent [winfo parent [winfo parent $lb]] 429} 430 431## LBSelect $lb -- 432# Transfer listbox selection to combobox value. 433# 434proc ttk::combobox::LBSelect {lb} { 435 set cb [LBMaster $lb] 436 set selection [$lb curselection] 437 if {[llength $selection] == 1} { 438 SelectEntry $cb [lindex $selection 0] 439 } 440} 441 442## LBCleanup $lb -- 443# <Destroy> binding for combobox listboxes. 444# Cleans up by unsetting the linked textvariable. 445# 446# Note: we can't just use { unset [%W cget -listvariable] } 447# because the widget command is already gone when this binding fires). 448# [winfo parent] still works, fortunately. 449# 450proc ttk::combobox::LBCleanup {lb} { 451 variable Values 452 unset Values([LBMaster $lb]) 453} 454 455#*EOF* 456