This document captures the design and implementation details of SearchReplacePopup and its integration surface.
For end-user usage and examples, see SearchReplacePopup.
TextArea and LogControl).Visual that opens a Popup window in the app window layer.ISearchReplaceTarget implementation.SearchQuery is a value type passed to targets:
Text : string?CaseSensitive : boolWholeWord : boolUseRegex : boolFindReplaceHost controls implement ISearchReplaceTarget:
Title : stringSupportsReplace : boolSetQuery(in SearchQuery query)NextMatch() / PreviousMatch()ReplaceCurrent(string replacement)ReplaceAll(string replacement)GetStatusText() (for example "3/10" or "0 matches")GetErrorText() (for example invalid regex message)The target is responsible for match computation, highlighting, and editor navigation.
Bindable properties:
SearchText : string?ReplaceText : string?CaseSensitive : boolWholeWord : boolUseRegex : boolMode : SearchReplaceModeOther properties and methods:
IsOpen : bool (derived)ClearQueryOnClose : bool (clears the query sent to the target when closing)Query : SearchQuery (derived from the bindable fields)OpenFind(string? initialSearchText = null) : boolOpenReplace(string? initialSearchText = null) : boolClose()ResetPosition() (resets drag offsets)ArrangeWithin(in Rectangle hostRect) (host integration hook)SearchReplacePopup does not currently have a dedicated style type. It composes existing controls and styles:
TextBox for search and replace inputsSwitch for toggles (Case, Word, Regex)Button for navigation and replace actionsGroup for popup chromePopup for window-layer hostingThe error message is a TextBlock styled with a theme-derived error foreground color.
Tests:
src/XenoAtom.Terminal.UI.Tests/TextAreaSearchReplaceTests.cs (integration via TextArea, Tab behavior, navigation, replace all)src/XenoAtom.Terminal.UI.Tests/SearchReplacePopupDragTests.cs (drag repositioning)Demo:
samples/ControlsDemo/Demos/SearchReplacePopupDemo.csUser documentation:
site/docs/controls/searchreplacepopup.mdSearchReplacePopup itself is a non-focusable anchor visual. The host should:
ArrangeWithin(hostRect) during the host arrange pass.ArrangeWithin places the popup anchor at:
x = hostRect.Righty = hostRect.YThis anchor point is chosen so that a Popup with Placement = Left uses the anchor X as the popup's right edge, which
typically keeps the popup inside the host area.
When opened, SearchReplacePopup creates a Popup window with:
Anchor = thisPlacement = PopupPlacement.LeftMatchAnchorWidth = falseCloseOnTab = false (important so Tab stays inside the popup UI)IsDraggable = true and DragHandleHeight = 1 (dragging the header row)OffsetX / OffsetY initialized from the last stored offsets so the popup remembers its position while open/close cycles.Popups only work in fullscreen apps. If Popup.Show() throws, the search popup ignores it and reports failure from OpenFind/OpenReplace.
When the bindable fields change (SearchText, CaseSensitive, WholeWord, UseRegex), the popup calls:
_target.SetQuery(Query)This call is suppressed while the popup is bulk-updating state (for example while opening).
Mode to Replace is coerced back to Find.When opening, the popup stores the previously focused visual. When closing (and not in a "rebuild" close), it restores focus to the original element.
Popup-level keyboard handling (attached to the Popup):
PreviousMatch()NextMatch()Mouse:
Popup drag support).SearchReplacePopupStyle surface; customization is done by styling composed child controls or by wrapping the popup content.ResetPosition()).GetStatusText() and GetErrorText().