1# 2# $Id$ 3# 4# Bindings for Menubuttons. 5# 6# Menubuttons have three interaction modes: 7# 8# Pulldown: Press menubutton, drag over menu, release to activate menu entry 9# Popdown: Click menubutton to post menu 10# Keyboard: <Key-space> or accelerator key to post menu 11# 12# (In addition, when menu system is active, "dropdown" -- menu posts 13# on mouse-over. Ttk menubuttons don't implement this). 14# 15# For keyboard and popdown mode, we hand off to tk_popup and let 16# the built-in Tk bindings handle the rest of the interaction. 17# 18# ON X11: 19# 20# Standard Tk menubuttons use a global grab on the menubutton. 21# This won't work for Ttk menubuttons in pulldown mode, 22# since we need to process the final <ButtonRelease> event, 23# and this might be delivered to the menu. So instead we 24# rely on the passive grab that occurs on <ButtonPress> events, 25# and transition to popdown mode when the mouse is released 26# or dragged outside the menubutton. 27# 28# ON WINDOWS: 29# 30# I'm not sure what the hell is going on here. [$menu post] apparently 31# sets up some kind of internal grab for native menus. 32# On this platform, just use [tk_popup] for all menu actions. 33# 34# ON MACOS: 35# 36# Same probably applies here. 37# 38 39namespace eval ttk { 40 namespace eval menubutton { 41 variable State 42 array set State { 43 pulldown 0 44 oldcursor {} 45 } 46 } 47} 48 49bind TMenubutton <Enter> { %W instate !disabled {%W state active } } 50bind TMenubutton <Leave> { %W state !active } 51bind TMenubutton <Key-space> { ttk::menubutton::Popdown %W } 52bind TMenubutton <<Invoke>> { ttk::menubutton::Popdown %W } 53 54if {[tk windowingsystem] eq "x11"} { 55 bind TMenubutton <ButtonPress-1> { ttk::menubutton::Pulldown %W } 56 bind TMenubutton <ButtonRelease-1> { ttk::menubutton::TransferGrab %W } 57 bind TMenubutton <B1-Leave> { ttk::menubutton::TransferGrab %W } 58} else { 59 bind TMenubutton <ButtonPress-1> \ 60 { %W state pressed ; ttk::menubutton::Popdown %W } 61 bind TMenubutton <ButtonRelease-1> \ 62 { %W state !pressed } 63} 64 65# PostPosition -- 66# Returns the x and y coordinates where the menu 67# should be posted, based on the menubutton and menu size 68# and -direction option. 69# 70# TODO: adjust menu width to be at least as wide as the button 71# for -direction above, below. 72# 73proc ttk::menubutton::PostPosition {mb menu} { 74 set x [winfo rootx $mb] 75 set y [winfo rooty $mb] 76 set dir [$mb cget -direction] 77 78 set bw [winfo width $mb] 79 set bh [winfo height $mb] 80 set mw [winfo reqwidth $menu] 81 set mh [winfo reqheight $menu] 82 set sw [expr {[winfo screenwidth $menu] - $bw - $mw}] 83 set sh [expr {[winfo screenheight $menu] - $bh - $mh}] 84 85 switch -- $dir { 86 above { if {$y >= $mh} { incr y -$mh } { incr y $bh } } 87 below { if {$y <= $sh} { incr y $bh } { incr y -$mh } } 88 left { if {$x >= $mw} { incr x -$mw } { incr x $bw } } 89 right { if {$x <= $sw} { incr x $bw } { incr x -$mw } } 90 flush { 91 # post menu atop menubutton. 92 # If there's a menu entry whose label matches the 93 # menubutton -text, assume this is an optionmenu 94 # and place that entry over the menubutton. 95 set index [FindMenuEntry $menu [$mb cget -text]] 96 if {$index ne ""} { 97 incr y -[$menu yposition $index] 98 } 99 } 100 } 101 102 return [list $x $y] 103} 104 105# Popdown -- 106# Post the menu and set a grab on the menu. 107# 108proc ttk::menubutton::Popdown {mb} { 109 if {[$mb instate disabled] || [set menu [$mb cget -menu]] eq ""} { 110 return 111 } 112 foreach {x y} [PostPosition $mb $menu] { break } 113 tk_popup $menu $x $y 114} 115 116# Pulldown (X11 only) -- 117# Called when Button1 is pressed on a menubutton. 118# Posts the menu; a subsequent ButtonRelease 119# or Leave event will set a grab on the menu. 120# 121proc ttk::menubutton::Pulldown {mb} { 122 variable State 123 if {[$mb instate disabled] || [set menu [$mb cget -menu]] eq ""} { 124 return 125 } 126 foreach {x y} [PostPosition $mb $menu] { break } 127 set State(pulldown) 1 128 set State(oldcursor) [$mb cget -cursor] 129 130 $mb state pressed 131 $mb configure -cursor [$menu cget -cursor] 132 $menu post $x $y 133 tk_menuSetFocus $menu 134} 135 136# TransferGrab (X11 only) -- 137# Switch from pulldown mode (menubutton has an implicit grab) 138# to popdown mode (menu has an explicit grab). 139# 140proc ttk::menubutton::TransferGrab {mb} { 141 variable State 142 if {$State(pulldown)} { 143 $mb configure -cursor $State(oldcursor) 144 $mb state {!pressed !active} 145 set State(pulldown) 0 146 147 set menu [$mb cget -menu] 148 tk_popup $menu [winfo rootx $menu] [winfo rooty $menu] 149 } 150} 151 152# FindMenuEntry -- 153# Hack to support tk_optionMenus. 154# Returns the index of the menu entry with a matching -label, 155# -1 if not found. 156# 157proc ttk::menubutton::FindMenuEntry {menu s} { 158 set last [$menu index last] 159 if {$last eq "none"} { 160 return "" 161 } 162 for {set i 0} {$i <= $last} {incr i} { 163 if {![catch {$menu entrycget $i -label} label] 164 && ($label eq $s)} { 165 return $i 166 } 167 } 168 return "" 169} 170 171#*EOF* 172