Tool of Thought

APL for the Practical Man

"Vibe coding since 1987"

Menus Revisited

August 9, 2025

Now that we have a little framework for handling arguments, and have used it for our <input> components, we should probably redesign the Menu component. Let's get rid of the current Add function:

 Add←{
     m←⍺
     9=⎕NC'⍵':m AddSubmenu ⍵
     v←⍵,(≢⍵)↓'' '' '' 0 1 0
     cm←(5⊃v)/⎕UCS 10004
     c←A.New¨{'span'⍵}¨cm(0⊃v)(1⊃v)''
     i←m A.New'li'(A.New'div'c)
     i.(Label Shortcut Function Separator Active Checked)←v
     i.Function←1 A.FQP i.Function
     i.Type←'Item'
     i.tabindex←'0'
     i.Selected←i=⊃m.Content
     i.class←i.Selected/'sel-item'
     i.class,←(~i.Active)/' inactive'
     i.class,←i.Separator/' separator'
     i
 }  

This function adds a menu item or a (sub)menu to a menu. Instead, let's just have an AddItem function to add a menu item, essentially creating a MenuItem component but keeping it all under the Menu namespace:

NewItem←{
     i←(⍺ A.New'li')⎕NS(
         'Label' ''
         'Shortcut' ''
         'Function' ''
         'Separator' 0
         'Active' 1
         'Checked' 0
     )A.InitProps ⍵
     cm←i.Checked/⎕UCS 10004
     c←A.New¨{'span'⍵}¨cm i.Label i.Shortcut''
     i.Content←A.New'div'c
     i.Function←1 A.FQP i.Function
     i.Type←'Item'
     i.tabindex←'0'
     i.Selected←i=⊃⍺.Content
     i.class←i.Selected/'sel-item'
     i.class,←(~i.Active)/' inactive'
     i.class,←i.Separator/' separator'
     i
 }

Much nicer! Clearer to read and see the defaults, much more flexible to call. The Menu.New function can take over adding a (sub)menu to a menu:

New←{
     ⍺←0
     m←(A.New'menu')⎕NS(
         'Label' ''
         'Separator' 0
     )A.InitProps ⍵
     m.Content←m.Label
     m.class←'menu'
     m.popover←'auto'
     m.Toggled←0
     0=⍺:m  ⍝ Top Level menu
     b←'&gt;'
     c←A.New¨{'span'⍵}¨''(m.Content)''(⊂⊂b)
     i←⍺ A.New'li'(A.New'div'(c,m))
     i.Type←'Menu'
     i.tabindex←0
     i.class←m.Separator/'separator'
     i.Selected←0
     m.Content←''
     m
 }
~~~ 

Now we can build a menu like so:

BuildMenu←{
     NewMenu←A.Menu.New
     NewItem←A.Menu.NewItem
     m←NewMenu''
     s←m NewMenu⊂'File'
     i←s NewItem'Open' 'Ctrl+O' 'OnFileOpen'
     i←s NewItem'Save' 'Ctrl+S' 'OnFileSave'
     i←s NewItem'Save As...' 'Ctrl+A' 'OnFileSaveAs'
     s←m NewMenu⊂'Edit'
     i←s NewItem'Cut' 'Ctrl+X' 'OnCut'
     i←s NewItem'Copy' 'Ctrl+C' 'OnCopy'
     i←s NewItem'Paste' 'Ctrl+V' 'OnPaste'
     s←m NewMenu⊂'View'
     i←s NewItem'List' 'Ctrl+Q' 'OnViewList'
     s2←s NewMenu⊂'Icons'
     i←s2 NewItem'Small' 'Ctrl+L' 'OnViewLarge'
     i←s2 NewItem'Medium' 'Ctrl+M' 'OnViewMedium' ('Checked' 1)  
     i←s2 NewItem'Large' 'Ctrl+S' 'OnViewSmall' ('Active' 0)    
     m
 }