Level 1: DefaultActivateHandler and DefaultAcceptHandler Flow
This diagram shows the actual implementation of DefaultActivateHandler and DefaultAcceptHandler. Each handler resets dispatch state, calls RaiseActivating/RaiseAccepting (which runs the full Cancellable Work Pattern pipeline: OnXxx virtual → Xxx event → TryDispatchToTarget → 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:<br/> OnActivating (virtual)<br/> → Activating event<br/> → TryDispatchToTarget<br/> → TryBubbleUp"]
ra --> |handled: returns true| da_disp{"_lastDispatchOccurred?<br/>(consume-dispatch composite)"}
da_disp --> |yes| ra_act1["RaiseActivated<br/>(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<br/>(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<br/>(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:<br/> OnAccepting (virtual)<br/> → Accepting event<br/> → TryDispatchToTarget<br/> → TryBubbleUp"]
racc --> |handled: returns true| dac_disp{"_lastDispatchOccurred<br/>or Bridged?"}
dac_disp --> |yes| racc_acc1["RaiseAccepted<br/>(composite/bridge completion)"]
racc_acc1 --> ret_ta1["return true"]
dac_disp --> |no| ret_ta1
racc --> |not handled: returns false| dac_def{"!acceptWillBubble<br/>AND DefaultAcceptView exists<br/>(not this, not source)?"}
dac_def --> |yes| dac_dd["DispatchDown to DefaultAcceptView<br/>(redirected = true)"]
dac_def --> |no| dac_bup{"BubblingUp AND<br/>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<br/>or willBubble<br/>or BubblingUp<br/>or IAcceptTarget"]
Key Points:
DefaultActivateHandlerandDefaultAcceptHandlerare the real entry points from key/mouse bindings. They orchestrate everything.- RaiseActivating/RaiseAccepting runs the full CWP pipeline:
OnXxxvirtual →Xxxevent →TryDispatchToTarget→ TryBubbleUp. There is no separate "execute handler" step after the pre-event. - OnActivating/Activating (or OnAccepting/Accepting) can cancel by setting
args.Handled = true, which short-circuitsTryDispatchToTargetand TryBubbleUp. - Accept skips DefaultAcceptView redirect when
acceptWillBubble = true— the bubble path handles it, preventing double-accepted events. - Command handlers return
bool?:null(no implementation),false(raised but not handled),true(handled/consumed).
Level 2: Accept Propagation with DefaultAcceptView
This diagram shows how Accept propagates through the view hierarchy when a Dialog contains an IsDefault Button. Accept bubbles from TextField to Dialog via TryBubbleUp (inside RaiseAccepting), then Dialog.DefaultAcceptHandler redirects to the IsDefault Button via DispatchDown.
flowchart TD
input2["User input (Enter on TextField)"] --> tf["TextField.DefaultAcceptHandler"]
tf --> tf_raise["RaiseAccepting:<br/> OnAccepting → Accepting<br/> → TryDispatchToTarget<br/> → TryBubbleUp"]
tf_raise --> |"TryBubbleUp: Dialog.CommandsToBubbleUp contains Accept"| dlg_invoke["Dialog.InvokeCommand<br/>(Accept, BubblingUp)"]
tf_raise --> |"Accept will not bubble (fallback)"| tf_def{"TextField.DefaultAcceptView<br/>exists?"}
tf_def --> |yes| tf_dd["DispatchDown to<br/>TextField.DefaultAcceptView"]
tf_def --> |no| tf_accepted["TextField.RaiseAccepted"]
dlg_invoke --> dlg_handler["Dialog.DefaultAcceptHandler<br/>(Routing = BubblingUp)"]
dlg_handler --> dlg_raise["RaiseAccepting (BubblingUp):<br/>not handled → returns false"]
dlg_raise --> dlg_def{"Dialog.DefaultAcceptView<br/>= IsDefault Button?"}
dlg_def --> |yes| dlg_dd["DispatchDown(Button, ctx)<br/>(redirected = true)"]
dlg_dd --> btn["Button.DefaultAcceptHandler<br/>(Routing = DispatchingDown)"]
btn --> btn_raise["RaiseAccepting: handled<br/>(Button is IAcceptTarget)"]
btn_raise --> btn_accepted["Button.RaiseAccepted → return true"]
btn_accepted --> dlg_accepted["Dialog.RaiseAccepted<br/>(Dialog.Accepted event fires)"]
dlg_accepted --> dlg_return["Dialog returns true"]
dlg_return --> tf_return["TryBubbleUp returns true<br/>→ TextField.RaiseAccepting returns true<br/>→ TextField returns true"]
dlg_def --> |no| dlg_no_btn["Dialog.RaiseAccepted<br/>(no default button)"]
Key Points:
- Accept bubbles to Dialog via TryBubbleUp called inside TextField's RaiseAccepting, because Dialog's CommandsToBubbleUp includes Accept.
Dialog.DefaultAcceptHandlerreceives the command withRouting = BubblingUpand checks Dialog's DefaultAcceptView to find and invoke the IsDefault Button.DispatchDowncreates a new context withRouting = DispatchingDown, suppressing re-bubbling in the target and preventing infinite recursion.TextField.DefaultAcceptHandlerskips its own DefaultAcceptView redirect becauseacceptWillBubble = true— this prevents double-handling.- DefaultAcceptView is a property on each view that returns the first
IAcceptTarget { IsDefault: true }SubView (typically a Button). It is not inherited from the SuperView. - Button returns
truefromDefaultAcceptHandlerbecause it implements IAcceptTarget.
Level 3: Complete Flow with MenuBarItem, Menu, and MenuItem
This diagram illustrates command flow in the menu system. MenuBarItem (a top-level "File", "Edit" item in MenuBar) extends MenuItem : Shortcut. MenuBar extends Menu : 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:<br/> OnHandlingHotKey<br/> → HandlingHotKey event<br/> → TryBubbleUp"]
sc_pre --> |"handled: canceled"| sc_cancel["return false<br/>(key not consumed — allows text input)"]
sc_pre --> |"not handled"| sc_hkcmd["RaiseHotKeyCommand"]
sc_hkcmd --> sc_activate["InvokeCommand(Activate)<br/>(MenuBarItem override: no SetFocus before this)"]
sc_activate --> sc_default_act["DefaultActivateHandler:<br/> RaiseActivating → SetFocus → RaiseActivated"]
sc_default_act --> sc_onactivated["MenuBarItem.OnActivated:<br/> 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:<br/> OnActivating → Activating<br/> → TryDispatchToTarget<br/> → 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<br/>→ RaiseSelectedMenuItemChanged"]
nav_selected --> nav_bar["Menu.OnSelectedMenuItemChanged<br/>→ 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:<br/> OnAccepting → Accepting<br/> → TryDispatchToTarget → TryBubbleUp"]
acc_raise --> |"handled: action invoked"| acc_exec["MenuItem action executed<br/>(via OnAccepting in MenuItem)"]
acc_exec --> acc_accepted["MenuItem.RaiseAccepted"]
acc_accepted --> acc_bubble["Accepted propagates via<br/>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<br/>(MenuItem.OnAccepting opens SubMenu)"]
Key Points:
- Scenario 1 (HotKey): MenuBarItem overrides HotKey to skip SetFocus() before
InvokeCommand(Activate). This prevents OnSelectedMenuItemChanged firing prematurely when switchingMenuBarItemsvia HotKey. SetFocus() occurs insideDefaultActivateHandleras part of Activate processing, not in the HotKey handler directly. - Scenario 2 (Activate): Arrow keys navigate menu items via Activate.
DefaultActivateHandler's Direct path calls SetFocus(), which triggersMenu.RaiseSelectedMenuItemChanged. OnSelectedMenuItemChanged manages popover visibility during navigation. - Scenario 3 (Accept): Enter executes the menu item. MenuItem's OnAccepting invokes the item's action. Accepted propagates via CommandBridge (for non-containment boundaries) or CommandsToBubbleUp (for containment), eventually reaching MenuBar which closes the popover.
- MenuBarItem holds a PopoverMenu (not a
SubMenu). MenuItem holds aSubMenu(a nested Menu). - CommandBridge connects non-containment boundaries (e.g., PopoverMenu ↔ MenuBarItem) so Accepted/Activated from the remote view re-enters the owner's full command pipeline with
Routing = Bridged. - MenuBar uses consume dispatch (ConsumeDispatch = true, GetDispatchTarget →
Focused) — inner activations are consumed and do not propagate to MenuBar's SuperView.