docfx/docs/command-diagrams.md
This diagram shows the actual implementation of DefaultActivateHandler and DefaultAcceptHandler. Each handler resets dispatch state, calls xref:Terminal.Gui.ViewBase.View.RaiseActivating*/xref:Terminal.Gui.ViewBase.View.RaiseAccepting* (which runs the full Cancellable Work Pattern pipeline: OnXxx virtual → Xxx event → TryDispatchToTarget → xref:Terminal.Gui.ViewBase.View.TryBubbleUp*), then decides how to complete based on the result and routing mode.
flowchart TD
input_a["User input (Space / LeftButtonReleased)"] --> da["DefaultActivateHandler"]
da --> da_reset["Reset _lastDispatchOccurred = false"]
da_reset --> ra["RaiseActivating:
OnActivating (virtual)
→ Activating event
→ TryDispatchToTarget
→ TryBubbleUp"]
ra --> |handled: returns true| da_disp{"_lastDispatchOccurred?
(consume-dispatch composite)"}
da_disp --> |yes| ra_act1["RaiseActivated
(composite completion)"]
ra_act1 --> ret_t1["return true"]
da_disp --> |no| ret_t1
ra --> |not handled: returns false| da_bup{"Routing == BubblingUp?"}
da_bup --> |"yes, plain view (no dispatch target)"| ra_act2["RaiseActivated
(two-phase notification)"]
ra_act2 --> ret_f["return false"]
da_bup --> |"yes, relay view (has dispatch target)"| ret_f
da_bup --> |"no (Direct)"| da_sf["SetFocus (if CanFocus)"]
da_sf --> ra_act3["RaiseActivated
(if !_lastDispatchOccurred)"]
ra_act3 --> ret_t2["return true"]
input_b["User input (Enter)"] --> dac["DefaultAcceptHandler"]
dac --> dac_reset["Reset _lastDispatchOccurred = false"]
dac_reset --> racc["RaiseAccepting:
OnAccepting (virtual)
→ Accepting event
→ TryDispatchToTarget
→ TryBubbleUp"]
racc --> |handled: returns true| dac_disp{"_lastDispatchOccurred
or Bridged?"}
dac_disp --> |yes| racc_acc1["RaiseAccepted
(composite/bridge completion)"]
racc_acc1 --> ret_ta1["return true"]
dac_disp --> |no| ret_ta1
racc --> |not handled: returns false| dac_def{"!acceptWillBubble
AND DefaultAcceptView exists
(not this, not source)?"}
dac_def --> |yes| dac_dd["DispatchDown to DefaultAcceptView
(redirected = true)"]
dac_def --> |no| dac_bup{"BubblingUp AND
has dispatch target?"}
dac_dd --> dac_bup
dac_bup --> |yes| racc_acc2["RaiseAccepted → return false"]
dac_bup --> |no| racc_acc3["RaiseAccepted"]
racc_acc3 --> ret_bool["return redirected
or willBubble
or BubblingUp
or IAcceptTarget"]
Key Points:
DefaultActivateHandler and DefaultAcceptHandler are the real entry points from key/mouse bindings. They orchestrate everything.OnXxx virtual → Xxx event → TryDispatchToTarget → xref:Terminal.Gui.ViewBase.View.TryBubbleUp*. There is no separate "execute handler" step after the pre-event.args.Handled = true, which short-circuits TryDispatchToTarget and xref:Terminal.Gui.ViewBase.View.TryBubbleUp*.acceptWillBubble = true — the bubble path handles it, preventing double-accepted events.bool?: null (no implementation), false (raised but not handled), true (handled/consumed).This diagram shows how xref:Terminal.Gui.Input.Command.Accept propagates through the view hierarchy when a xref:Terminal.Gui.Views.Dialog contains an IsDefault xref:Terminal.Gui.Views.Button. Accept bubbles from xref:Terminal.Gui.Views.TextField to xref:Terminal.Gui.Views.Dialog via xref:Terminal.Gui.ViewBase.View.TryBubbleUp* (inside xref:Terminal.Gui.ViewBase.View.RaiseAccepting*), then Dialog.DefaultAcceptHandler redirects to the IsDefault xref:Terminal.Gui.Views.Button via DispatchDown.
flowchart TD
input2["User input (Enter on TextField)"] --> tf["TextField.DefaultAcceptHandler"]
tf --> tf_raise["RaiseAccepting:
OnAccepting → Accepting
→ TryDispatchToTarget
→ TryBubbleUp"]
tf_raise --> |"TryBubbleUp: Dialog.CommandsToBubbleUp contains Accept"| dlg_invoke["Dialog.InvokeCommand
(Accept, BubblingUp)"]
tf_raise --> |"Accept will not bubble (fallback)"| tf_def{"TextField.DefaultAcceptView
exists?"}
tf_def --> |yes| tf_dd["DispatchDown to
TextField.DefaultAcceptView"]
tf_def --> |no| tf_accepted["TextField.RaiseAccepted"]
dlg_invoke --> dlg_handler["Dialog.DefaultAcceptHandler
(Routing = BubblingUp)"]
dlg_handler --> dlg_raise["RaiseAccepting (BubblingUp):
not handled → returns false"]
dlg_raise --> dlg_def{"Dialog.DefaultAcceptView
= IsDefault Button?"}
dlg_def --> |yes| dlg_dd["DispatchDown(Button, ctx)
(redirected = true)"]
dlg_dd --> btn["Button.DefaultAcceptHandler
(Routing = DispatchingDown)"]
btn --> btn_raise["RaiseAccepting: handled
(Button is IAcceptTarget)"]
btn_raise --> btn_accepted["Button.RaiseAccepted → return true"]
btn_accepted --> dlg_accepted["Dialog.RaiseAccepted
(Dialog.Accepted event fires)"]
dlg_accepted --> dlg_return["Dialog returns true"]
dlg_return --> tf_return["TryBubbleUp returns true
→ TextField.RaiseAccepting returns true
→ TextField returns true"]
dlg_def --> |no| dlg_no_btn["Dialog.RaiseAccepted
(no default button)"]
Key Points:
Dialog.DefaultAcceptHandler receives the command with Routing = BubblingUp and checks xref:Terminal.Gui.Views.Dialog's xref:Terminal.Gui.ViewBase.View.DefaultAcceptView to find and invoke the IsDefault xref:Terminal.Gui.Views.Button.DispatchDown creates a new context with Routing = DispatchingDown, suppressing re-bubbling in the target and preventing infinite recursion.TextField.DefaultAcceptHandler skips its own xref:Terminal.Gui.ViewBase.View.DefaultAcceptView redirect because acceptWillBubble = true — this prevents double-handling.IAcceptTarget { IsDefault: true } SubView (typically a xref:Terminal.Gui.Views.Button). It is not inherited from the SuperView.true from DefaultAcceptHandler because it implements xref:Terminal.Gui.IAcceptTarget.This diagram illustrates command flow in the menu system. xref:Terminal.Gui.Views.MenuBarItem (a top-level "File", "Edit" item in xref:Terminal.Gui.Views.MenuBar) extends xref:Terminal.Gui.Views.MenuItem : xref:Terminal.Gui.Views.Shortcut. xref:Terminal.Gui.Views.MenuBar extends xref:Terminal.Gui.Views.Menu : xref:Terminal.Gui.Views.Bar.
flowchart TD
sc_header["=== Scenario 1: HotKey Activation (Alt+F) ==="]
sc_header --> sc_input["Alt+F pressed"]
sc_input --> sc_hotkey["MenuBarItem.InvokeCommand(HotKey)"]
sc_hotkey --> sc_pre["RaiseHandlingHotKey:
OnHandlingHotKey
→ HandlingHotKey event
→ TryBubbleUp"]
sc_pre --> |"handled: canceled"| sc_cancel["return false
(key not consumed — allows text input)"]
sc_pre --> |"not handled"| sc_hkcmd["RaiseHotKeyCommand"]
sc_hkcmd --> sc_activate["InvokeCommand(Activate)
(MenuBarItem override: no SetFocus before this)"]
sc_activate --> sc_default_act["DefaultActivateHandler:
RaiseActivating → SetFocus → RaiseActivated"]
sc_default_act --> sc_onactivated["MenuBarItem.OnActivated:
toggles PopoverMenuOpen"]
sc_onactivated --> sc_show["PopoverMenu shown (PopoverMenuOpen = true)"]
sc_show --> nav_header["=== Scenario 2: Menu Navigation (Arrow Keys) ==="]
nav_header --> nav_input["Arrow key pressed inside Menu/PopoverMenu"]
nav_input --> nav_activate["MenuItem.InvokeCommand(Activate)"]
nav_activate --> nav_raise["RaiseActivating:
OnActivating → Activating
→ TryDispatchToTarget
→ TryBubbleUp"]
nav_raise --> |"not handled (Direct)"| nav_focus["SetFocus (MenuItem gains focus)"]
nav_focus --> nav_activated["MenuItem.RaiseActivated"]
nav_activated --> nav_selected["Menu.SelectedMenuItem updated
→ RaiseSelectedMenuItemChanged"]
nav_selected --> nav_bar["Menu.OnSelectedMenuItemChanged
→ MenuBar.OnSelectedMenuItemChanged"]
nav_bar --> nav_done["Update popover visibility if needed"]
nav_done --> acc_header["=== Scenario 3: Accept Menu Item (Enter) ==="]
acc_header --> acc_input["Enter pressed on focused MenuItem"]
acc_input --> acc_raise["MenuItem.RaiseAccepting:
OnAccepting → Accepting
→ TryDispatchToTarget → TryBubbleUp"]
acc_raise --> |"handled: action invoked"| acc_exec["MenuItem action executed
(via OnAccepting in MenuItem)"]
acc_exec --> acc_accepted["MenuItem.RaiseAccepted"]
acc_accepted --> acc_bubble["Accepted propagates via
CommandBridge or CommandsToBubbleUp"]
acc_bubble --> acc_bar["MenuBar/Menu.OnAccepted"]
acc_bar --> acc_close["MenuBar hides popover, deactivates"]
acc_raise --> |"has SubMenu"| acc_sub["SubMenu shown
(MenuItem.OnAccepting opens SubMenu)"]
Key Points:
InvokeCommand(Activate). This prevents xref:Terminal.Gui.Views.Menu.OnSelectedMenuItemChanged* firing prematurely when switching MenuBarItems via HotKey. xref:Terminal.Gui.ViewBase.View.SetFocus occurs inside DefaultActivateHandler as part of Activate processing, not in the HotKey handler directly.DefaultActivateHandler's Direct path calls xref:Terminal.Gui.ViewBase.View.SetFocus, which triggers Menu.RaiseSelectedMenuItemChanged. xref:Terminal.Gui.Views.MenuBar.OnSelectedMenuItemChanged* manages popover visibility during navigation.SubMenu). xref:Terminal.Gui.Views.MenuItem holds a SubMenu (a nested xref:Terminal.Gui.Views.Menu).Routing = Bridged.Focused) — inner activations are consumed and do not propagate to xref:Terminal.Gui.Views.MenuBar's SuperView.