Last update: 2020-2-25 version 1.41 Sourceforge: https://sourceforge.net/projects/tcl-flexmenu/ [bll] 2018-9-20: An alternative menu system. 2018-9-21: There seems to be a little interest in this. I wrote it for a couple of rather minor reasons (one of which I'm not sure is valid). Though I do really like having real checkbuttons and radiobuttons in the menu, and I like the -keepopen option. [bll] 2019-12-7: In production as of 2019-11-25. [flexmenu-ex1][flexmenu-ex2][flexmenu-ex3][flexmenu-ex4] [flexmenu-ex5][flexmenu-ex6][flexmenu-ex7] [flexmenu-ex8] [flexmenu-flexfb-img] <>Version History ======none 1.41 2020-2-25 fix bugs with cascades introduced in 1.40 (-precommand, -yalign, -xalign) 1.40 2020-2-24 popup menus: fix bug: would immediately close after initial open. styling issue: fix bug with non-unique styles. clone command is now working. The widget entry type cannot be cloned. Added tearoff entry type. It is a standard label, the text or image can be configured. The -tearoff option automatically adds a tearoff item (backwards compatibility). -tearoffcommand is available. -title is available. Default title is empty. I expect this is not fully backwards compatible. tearoff command : create your own interface to tear off menus. 1.39 2020-1-10 Prevent a outofrange crash in updownhandler. 1.38 2019-12-25 Fixed a bug in the delete() method. Fixed a typo bug in _unpostMenu. Fixed a bug in invokeitem setting the wrong state. 1.37 2019-12-3 Add appearance/isdark call back in using a temporary toplevel. 1.36 2019-10-31 Remove appearance/isdark call. Only works on existing windows. 1.35 2019-10-17 Fixed close check handling. Fixed combobox click completely outside of menu window. Added -clicktoopen option. 1.34 2019-10-17 Fixed mouse handling issues with overlapping menus. 1.33 2019-10-15 Fix a bug in delete. 1.32 2019-9-29 Fix a bug in deactivateitem, when the item is not present. 1.31 2019-9-29 Fix a bug win movement check when there was no prior movement. 1.30 2019-9-25 More MacOS color fixes. Fix color/styling issues. 1.29 2019-9-18 Change -relief default to always be raised. Fix bug deleting items when the layout is not set up. Fix per-menu configuration handling for -font, -background, -foreground. More MacOS color fixes. Protect grab calls. 1.28 2019-9-16 Fix gap on delete. Fix reconfigure on insert or delete. 1.27 2019-9-16 Fix column weight issues. MacOS: toplevels are ok for 10.14+ MacOS: revert to old dark mode test for 10.14. 1.26 2019-9-10 Internal cleanup. Fix sizing issues. 1.25 Change color defaults for Mac OS. 1.24 Mac OS dark mode colors. 1.23 Fixed cascade unpost. 1.22 Handle change of item selection on insert/add/delete. 1.21 2018-12-03 Fix grab handling issue. Fix close check handling. 1.20 2018-12-03 Resolve issues with menu display and configuration. 1.19 2018-12-02 Added -bindaccelerators option. Added -stickycascade option. Fixed display when moving off of active item in certain cases. Fixed initial display of menu. 1.18 2018-12-02 Mac OS X: Force -mode frame, as toplevels cannot be supported at this time. 1.17 2018-11-29 Change manual invoke processing to work when the menu is inactive. This matches the original menu function. May create some odd states if run on a sub-cascade. Fixed overlapping menu handling. 1.16 2018-11-29 Allow vertical separators in menubar. Add new -gap option for items. 1.15 2018-10-25 Fix mac os x: enter/leave processing does not work properly. 1.14 2018-10-22 Fix various bugs with user 'invoke' method. 1.13 2018-10-22 Turn off mouse activation of menus in the wait state so that overlapping menus work (changed in 1.12). -xalign and -yalign can have tag[+-]offset Fix menutop/menuleft display (call postprocess). 1.12 2018-10-21 Initialize styles once only. Fix x and y offsets. Fix borderwidth and relief on menubar/menuleft menus. Fix placement of active frame in scrolling window. Fix enter/leave handling. Fix accelerator keys. Fix F10. Fix left arrow item positioning. 1.11 2018-10-1 Fix mistake. 1.10 2018-10-1 Rewrite styling and active state handling (pass 1). Some option cleanup. 1.9 2018-10-1 Fix incorrect default for -borderwidth. Fix bad copy/paste in enterLeaveHandler. Fix column spans with -hidemargin. Fix non-global grab. Fix enter/leave handling. Fix press handler/menu.enter/grab. Fix column weighting with -hidemargin. 1.8 2018-10-1 Fix crash when window closed. 1.7 2018-10-1 Remove tailcall. Rewrite grab handling. Rewrite enter/leave handling. Fix invoke of item when -keepopen is false. 1.6 2018-9-30 Set menu colors from the current theme. Fix -font handling. Fix menus not closing on checkbutton select. Button press will activate the menu and item. 1.5 2018-9-30 Fixed -borderwidth and -relief options for items. Added -mode {toplevel|frame} option. Cleaned up some stacking order issues (fixes the activeframe covering menu items). 1.4 2018-9-29 Added -precommand for cascades. Added -yalign for cascades. Added -xalign for cascades. Fixed bug in entrycget. Fixed 'delete' operation to match 'menu'. Fixed bug where the width was lost on a redisplay. Fixed display when initial number of items did not exceed -maxheight. Fixed various bugs with empty menus. 1.3 2018-9-22 Fixed initialization to only execute on mac os x. Fixed incorrect state for menus after invoking an item. Brought state table documentation up to date. 1.2 2018-9-21 (broken for non mac os x) Fixed -padx/-pady so they will work for the main menubar Set the -acceleratorprefix to \u2318 for Mac OS X. This is converted to 'Meta' (hope that's right). Changed default -acceleratorprefix to 'Alt-' (due to mac os x changes). Fixed the colors on mac os x. Dark mode and graphite themes are supported. Documentation 1.1 2018-9-20 Fixed active highlight for scrolled menus 1.0 2018-9-20 initial release ====== <> ****Disadvantages:**** * Does not work with `.toplevel configure -menu .mymenu`. Tk uses the internal menu API to attach menus. flexmenu cannot work with the `-menu` option. * Must use pack or grid (or place) to attach the menu. Converting an existing program to use flexmenu could be quite painful. * The main menu must be created with either `-type menubar` or `-type menuleft`. * It has not been tested much. ****Advantages:**** * Allows multiple menus to be active at the same time. You can have a main menu, a couple of toolbars and a side menu active all at the same time. * Easier to create toolbars. ****Features:**** * Checkbuttons and Radiobuttons are ttk widgets. * Supports left side menus (`-type menuleft`). * Scrolling menus (`-maxheight`). * Configure `-columnbreak ` at the menu level to automatically break every items. * `-keepopen` option will leave the menu open after invoking an item. * `-acceleratorfont`, `-acceleratorforeground` and `-acceleratoractiveforeground` options. * `-activerelief` option. * `-hidearrows` option. * `-hideaccelerators` option. * `-acceleratorprefix` option to set the default accelerator prefix. * `-padx`, `-pady` options to change the padding for menu items. * `-bindaccelerators` option. * `-stickycascade` option. * `-clicktoopen` option. * `-mode` option to select the use of frames or toplevels. (Mac OS X is forced to use `-mode frame` prior to version 10.14). * Is a little more dynamic than the standard `menu`. Many things can be reconfigured and the changes will be picked up. * Cascades: -precommand option. Useful for dynamic menu generation. ****Item Features:**** * Any widget can be put into the menu (`.mymenu add widget -widget .mymenu.mycombobox`). * tearoff item type. (The -tearoff 1 option will automatically add a tearoff item). The tearoff item is a label and the text and image may be configured. * Margin images (`-marginimage`). It is quite common nowadays to use small icons on the left margin of the menu as an aid for the user. * Accelerator labels are automatically generated based on either an `&` prefix in the label, the `-underline` option, or the `-accelerator` option. * Accelerator bindings are automatically generated. * -activerelief option. * -gap option. * Cascades: -yalign, -xalign options to change cascade alignment. ****Notes:**** * Ignored: `-bitmap`, `-selectcolor`. * `-hidemargin` works properly on a per-entry basis. `menu` seems to treat it as a menu option even though it is specified per entry. I think `-hidemargin` would be better off as a menu option rather than an item option, but backwards compatibility is an issue. * Mac OS X prior to 10.14: -mode toplevel is not supported. Mac OS X is forced into -mode frame. ****Problems:**** * May be overeager in generating accelerator labels. The user may not want accelerator labels displayed for every item. * Has not been tested much. * At this time, flexmenu does not check to see if the entire menu is visible, and does not do any relocation of the menu. * Could use better documentation. ****Known Issues:**** '''Fixed in 1.18''': Mac OS X is not working at this time (2018-10-22). Mac OS X works, but only with -mode frame. -mode frame is forced for Mac OS X (2018-11-02). (-mode toplevel works with Mojave). '''Fixed in 1.1''': Active item highlighting for scrolling menus is not working right. '''Fixed in 1.2''': The accelerator key prefix is set to `Alt`. This needs to be set correctly for Mac OS X, but I do not recall which meta key the Mac sends. '''Fixed in 1.41''': The `clone` command has only a very basic implementation. Flicker with `-selectimage`. Currently, the menu layout is reapplied when a `-image` is changed to a `-selectimage`. This is just in case the `-selectimage` is a different size. If the assumption can be made that the image sizes are identical, this redraw could be removed. ****Examples:**** <>Example 1 ====== package require Tk source flexmenu.tcl # standard checkbutton widgets # -keepopen set flexmenu .mcb -keepopen true .mcb add checkbutton -variable ::x -text Check -onvalue 0 -offvalue 1 \ -accelerator Ctrl-4 .mcb add checkbutton -variable ::x -text Check -onvalue 0 -offvalue 1 \ -accelerator Ctrl-5 -indicatoron false .mcb add checkbutton -variable ::x -text Check -onvalue 0 -offvalue 1 \ -state disabled # standard radiobutton widgets flexmenu .mrb -keepopen true .mrb add radiobutton -variable ::x -value 0 -text Radio-0 \ -accelerator Ctrl-2 .mrb add radiobutton -variable ::x -value 1 -text Radio-1 \ -accelerator Ctrl-3 .mrb add radiobutton -variable ::x -value 2 -text Radio-2 \ -accelerator Ctrl-4 -state disabled # automatic column break every fifth item. # frame widgets in the menu set clist {#000000 #ff0000 #00ff00 #0000ff #ff8000 #ff0080 #80ff00 #00ff80 #8000ff #0080ff #ffff00 #ff00ff #00ffff #ffff80 #ff80ff #80ffff #ffffff #ff4040 #40ff40 #4040ff #404040 #808080 #ffff40 #ff40ff #40ffff} set col [flexmenu .col -columnbreak 5] set count 0 foreach {c} $clist { set tf [frame .col.x$c \ -background $c -relief raised -borderwidth 2 \ -width 20 -height 20 ] .col add widget -widget $tf -hidemargin 1 incr count } # scrolling menu # frame widgets in the menu set clist {#000000 #ff0000 #00ff00 #0000ff #ff8000 #ff0080 #80ff00 #00ff80 #8000ff #0080ff #ffff00 #ff00ff #00ffff #ffff80 #ff80ff #80ffff #ffffff #ff4040 #40ff40 #4040ff #404040 #808080 #ffff40 #ff40ff #40ffff} set colsc [flexmenu .colsc -keepopen true -maxheight 5] foreach {c} $clist { set tf [frame .colsc.bbb$c \ -background $c -relief raised -borderwidth 2 \ -width 20 -height 20 ] .colsc add widget -widget $tf -hidemargin 1 } # combobox widget flexmenu .mw ttk::combobox .mw.cb -values {aa bb cc dd ee ff gg} \ -textvariable ::y -state readonly -width 5 .mw add widget -widget .mw.cb # main cascade flexmenu .m .m add cascade -menu .col -label {Colors} .m add cascade -menu .colsc -label {Scrolling Colors} .m add cascade -menu .mcb -label {Check Buttons} .m add cascade -menu .mrb -label {Radio Buttons} .m add cascade -menu .mw -label {Widgets} .m add command -label E&xit -command exit flexmenu .mtop -type menubar .mtop add cascade -label Test -menu .m .mtop add command -label E&xit -command exit pack .mtop -side top -fill x -expand 1 -anchor nw frame .f -width 200 -height 200 pack .f ====== <> <>Example 2 : -type menuleft ====== #!/usr/bin/tclsh # This has not been tested on windows. package require Tk source flexmenu.tcl set ::lsortcmd [list ::lsort] try { package require collate set ::lsortcmd [list ::lsort -command collate] } on error {err res} { } variable vars proc attachmenu { pm dir } { if { [catch {$pm entrycget active -menu}] } { set m [mkmenu $dir] $pm entryconfigure active -menu $m } else { set m [$pm entrycget active -menu] refreshmenu $m $dir } } proc refreshmenu { nm dir } { variable vars set flist {} set tm [clock milliseconds] if { ! [info exists vars(cache.$dir)] || ($tm - $vars(cache.ts.$dir)) > 60000 } { try { set flist [{*}$::lsortcmd [glob -directory $dir -tails *]] } on error {err res} { } set vars(cache.$dir) $flist set vars(cache.ts.$dir) [clock milliseconds] $nm delete 0 end set count 0 foreach {f} $flist { set ff [file join $dir $f] if { [file isdirectory $ff] } { $nm add cascade \ -label $f \ -yalign menutop \ -precommand [list ::attachmenu $nm $ff] } else { $nm add command -label $f } incr count if { $count > 20 } { break } } } } proc mkmenu { dir args } { variable vars set nm [flexmenu .m$vars(menu.counter) \ -keepopen true \ -padx 4p \ -pady 4p \ -maxheight 10 \ {*}$args] refreshmenu $nm $dir set vars(dir.$nm) $dir incr vars(menu.counter) return $nm } proc main { } { variable vars set dir [lindex $::argv 0] if { $dir eq {} } { set dir / } set vars(menu.counter) 1 set mm [mkmenu $dir -type menuleft] $mm configure grid $mm -sticky news grid rowconfigure . 0 -weight 1 grid columnconfigure . 0 -weight 1 ttk::frame .f -width 400 -height 300 grid .f -sticky se -row 0 -column 1 lower .f . configure -background [ttk::style lookup . -background] after 100 update } main ====== <> <>Example 3 : menubar with clock ====== package require Tk source ../code/flexmenu.tcl set clock {} proc doclock { } { global clock set clock [clock format [clock seconds] \ -format "%Y-%M-%d %r" -locale system] after 500 ::doclock } flexmenu .mtop -type menubar .mtop add command -label E&xit -command exit .mtop add separator .mtop add command -label Exit -underline 0 -command exit .mtop add command -label Help -underline 0 -command exit -gap true doclock ttk::style map Clock.TLabel -foreground [list disabled darkblue] ttk::label .clock -textvariable clock -style Clock.TLabel .mtop add widget -widget .clock -hidemargin true -state disabled pack .mtop -side top -fill x -expand 1 -anchor nw ttk::frame .f -width 400 -height 300 pack .f lower .f . configure -background [ttk::style lookup . -background] ====== <>