This document captures the design and implementation details of Popup.
For end-user usage and examples, see Popup.
Popup implements IModalVisual and is treated as a modal overlay by the focus/event system.Popup hosts its content inside an internal, non-focusable ScrollViewer so large content can scroll.Select<T>)MenuBar, context menus)TerminalApp window layer (fullscreen apps).Border/Group for chrome.PopupPlacement controls how a popup is positioned relative to an anchor:
Below, Above, Right, LeftKey properties:
Content : Visual? (bindable) - popup content.Anchor : Visual? - anchor visual used for positioning.AnchorRect : Rectangle? - explicit anchor rectangle (takes precedence over Anchor).Placement : PopupPlacement (bindable) - relative placement when anchored.MatchAnchorWidth : bool (bindable) - ensures popup width is at least the anchor width.AdditionalWidth : int (bindable) - extra width added after width computation.HorizontalPopupAlignment : Align (bindable) - alignment when not anchored.VerticalPopupAlignment : Align (bindable) - alignment when not anchored.OffsetX : int / OffsetY : int (bindable) - manual offsets applied after positioning.CloseOnTab : bool (bindable) - whether Tab closes the popup before focus traversal.IsDraggable : bool (bindable) - enables drag repositioning.DragHandleHeight : int (bindable) - height of the draggable region at the top of the popup.Key methods:
Show() - adds the popup to the active TerminalApp window layer.Close() - removes the popup and raises the Closed routed event.Popup.Show() calls TerminalApp.ShowWindow(this):
Popups are modal and participate in the "active modal root" logic used by TerminalApp to route keyboard input and
tab traversal.
Popup itself stretches to fill the available UI slot. This is intentional:
The actual visible popup surface is tracked as an internal _popupRect.
ArrangeCore computes a desired popup size from:
PopupStyle.PaddingContent.DesiredSizeMatchAnchorWidth and AdditionalWidthDesired size is then clamped to the layout slot size.
When AnchorRect is set, or when Anchor is set (uses Anchor.Bounds), the popup is positioned relative to the anchor:
Below / Above prefer the requested side but may flip to the other side if the requested side does not fit and the other does.Right / Left prefer the requested side but:
This "shrink on the requested side" behavior avoids the common UX issue where a right/left placement appears to not work because the popup is clamped to the opposite edge.
When there is no anchor, HorizontalPopupAlignment and VerticalPopupAlignment are used within the slot.
If horizontal alignment is Align.Stretch, the popup width becomes the slot width and the content is remeasured for that width.
If vertical alignment is Align.Stretch, the popup height becomes the slot height.
After computing the position:
OffsetX and OffsetY are applied.Popup wraps its content in an internal ScrollViewer:
Popup.RenderOverride fills _popupRect with spaces using PopupStyle.ResolveSurfaceStyle(theme).
Important: popups render their surface using blank glyphs. The resolved surface style explicitly specifies the text style so that decorations (for example underline) do not leak from underlay content into the popup surface. This is validated by tests.
Popups do not draw a border by default; border/chrome is usually provided by wrapping the content (for example Border or Group).
IsDraggable is enabled, dragging within the top DragHandleHeight rows moves the popup by updating OffsetX/OffsetY.Tab handling is implemented at the app level:
TerminalApp checks the active modal root and closes it if it is a Popup and CloseOnTab is true.Popups use:
PopupStyle (src/XenoAtom.Terminal.UI/Styling/PopupStyle.cs):
PaddingSurfaceStyle (or theme-derived popup surface fill)Notes:
PopupStyle.BorderStyle exists but is not currently used by Popup rendering.
Border/chrome is provided by wrapper visuals such as Border/Group or a custom popup template in the calling control.Tests:
src/XenoAtom.Terminal.UI.Tests/PopupTests.cs (placement, flipping/shrinking, outside click, tab close, dragging, scrolling)src/XenoAtom.Terminal.UI.Tests/OverlaySurfaceTextStyleLeakTests.cs (popup surface should not inherit text decorations)Demo:
samples/ControlsDemo/Demos/PopupDemo.csUser documentation:
site/docs/controls/popup.mdPopupStyle.BorderStyle if wrappers remain the intended pattern).