flexmenu

Difference between version 48 and 49 - Previous - Next
Last update: 2019-9-16 version 1.28

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.  I have not tried using it in my
production code as yet.

[flexmenu-ex1][flexmenu-ex2][flexmenu-ex3][flexmenu-ex4]

[flexmenu-ex5][flexmenu-ex6]

<<discussion>>Version History
======none
   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
======
<<enddiscussion>>

****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.

****Features:****

   * Checkbuttons and Radiobuttons are ttk widgets.
   * Supports left side menus (`-type menuleft`).
   * Scrolling menus (`-maxheight`).
   * Configure `-columnbreak <value>` at the menu level to automatically break every <value> 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.
* `-mode` option to select the use of frames or toplevels. Mac OS X is forced to use `-mode frame`.
* `-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. ****Item Features:**** * Any widget can be put into the menu (`.mymenu add widget -widget .mymenu.mycombobox`). * 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. * Cascades: -precommand option. Useful for dynamic menu generation. ****Notes:**** * Ignored: `-bitmap`, `-tearoff`, `-tearoffcommand`, `-title`, `-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: -mode toplevel is not supported. Mac OS X is forced into -mode frame.
* 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). '''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. 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:**** <<discussion>>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 ====== <<enddiscussion>> <<discussion>>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 ====== <<enddiscussion>> <<discussion>>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] ====== <<enddiscussion>>