Class CollapsiblePanel
- Namespace
- SharpConsoleUI.Controls
- Assembly
- SharpConsoleUI.dll
A container control that hosts a flat list of child controls beneath a clickable header. The header shows an indicator and an optional title, and the body can be expanded or collapsed. This is a DOM-layout container: it stores and exposes its children, and the layout engine paints them — the panel itself only paints its own header chrome.
public class CollapsiblePanel : BaseControl, IDOMPaintable, INotifyPropertyChanged, IContainer, IContainerControl, IControlHost, IMouseAwareControl, IInteractiveControl, IFocusableControl, IWindowControl, IDisposable, IFocusableContainerWithHeader, ILogicalCursorProvider, IColorRoleableControl
- Inheritance
-
CollapsiblePanel
- Implements
- Derived
- Inherited Members
- Extension Methods
Remarks
This is a partial class. Header rendering, state toggling, mouse and keyboard activation, and the optional height animation are implemented in companion partials added by later tasks.
Properties
AnimationMode
Gets or sets whether expand/collapse animates the body height. Defaults to None (instant re-layout). When set to Height, the body height tweens open/closed over CollapsiblePanelAnimationDurationMs milliseconds. In headless contexts (no parent window / animation manager) the toggle remains instant.
public CollapsibleAnimationMode AnimationMode { get; set; }
Property Value
BackgroundColor
Gets or sets the background color for the container and its child controls.
public Color BackgroundColor { get; set; }
Property Value
BorderColor
Gets or sets the color used for the header separator and the body border. When null, the resolved foreground color is used.
public Color? BorderColor { get; set; }
Property Value
CanFocusWithMouse
Whether this control can receive focus via mouse clicks
public bool CanFocusWithMouse { get; }
Property Value
CanReceiveFocus
Whether this control can receive focus
public bool CanReceiveFocus { get; }
Property Value
Remarks
A collapsible panel's header is a Tab focus stop so it can be toggled with Enter/Space. A non-collapsible panel is a transparent focus container (like a ColumnContainer): it is not itself a stop, so focus passes straight through to its focusable body children.
Children
Gets the current child controls in the order they were added.
public IReadOnlyList<IWindowControl> Children { get; }
Property Value
CollapsedIcon
Gets or sets the indicator glyph shown in the header when the panel is collapsed. When null or empty, no indicator is drawn.
public string? CollapsedIcon { get; set; }
Property Value
Collapsible
Gets or sets whether the panel can be collapsed/expanded by the user. Defaults to true (current behavior). When false the panel is locked expanded, draws no expand/collapse indicator, ignores header clicks and Enter/Space, and is not itself a Tab stop — focus passes through to its body children (the panel behaves as a plain container).
public virtual bool Collapsible { get; set; }
Property Value
ColorRole
The semantic color role. Default = no role (normal resolution).
public ColorRole ColorRole { get; set; }
Property Value
ColorRoleMode
Optional ThemeMode override for role-colour derivation. When non-null, the role's dark/light seed colours are resolved as if the theme were in this mode, regardless of the theme's own Mode. When null (the default), the active theme's mode is used.
public ThemeMode? ColorRoleMode { get; set; }
Property Value
ContentWidth
Gets the minimum width needed to display the control's content, including margins. Returns null if width cannot be determined. This is calculated based on content (text length, child controls, etc.) and represents the natural/intrinsic size.
public override int? ContentWidth { get; }
Property Value
- int?
ExpandedIcon
Gets or sets the indicator glyph shown in the header when the panel is expanded. When null or empty, no indicator is drawn.
public string? ExpandedIcon { get; set; }
Property Value
FocusedBackgroundColor
Gets or sets the header background color used when the panel has keyboard focus. When null, the color is resolved from the theme (CollapsibleHeaderFocusedBackgroundColor).
public Color? FocusedBackgroundColor { get; set; }
Property Value
FocusedForegroundColor
Gets or sets the header foreground color used when the panel has keyboard focus. When null, the color is resolved from the theme (CollapsibleHeaderFocusedForegroundColor).
public Color? FocusedForegroundColor { get; set; }
Property Value
ForegroundColor
Gets or sets the foreground (text) color for the container and its child controls.
public Color ForegroundColor { get; set; }
Property Value
GetConsoleWindowSystem
Gets the console window system instance, or null if not attached to a window system.
public ConsoleWindowSystem? GetConsoleWindowSystem { get; }
Property Value
HasFocus
public bool HasFocus { get; }
Property Value
HeaderAlignment
Gets or sets the horizontal alignment of the indicator + title within the header row. Defaults to Left.
public HorizontalAlignment HeaderAlignment { get; set; }
Property Value
HeaderStyle
Gets or sets how the panel header (and, when expanded, the body frame) is drawn. Defaults to Borderless.
public CollapsibleHeaderStyle HeaderStyle { get; set; }
Property Value
IsEnabled
Gets or sets whether this control is enabled and can receive input.
public bool IsEnabled { get; set; }
Property Value
IsExpanded
Gets or sets whether the body is expanded (visible). Default true.
public virtual bool IsExpanded { get; set; }
Property Value
MaxContentHeight
Gets or sets the maximum height in rows allotted to the expanded body before it scrolls. When null, the body is sized to its content. Consumed by the layout engine in a later task.
public int? MaxContentHeight { get; set; }
Property Value
- int?
Outline
When true and a role is set, renders outline style (role color on text + border, surface fill).
public bool Outline { get; set; }
Property Value
Padding
Gets or sets the padding applied to the body region, inside the border. Default
(0, 0, 0, 0) (no inset), so existing panels are unaffected. Body children are
arranged within the border- and padding-inset region; the measured body overhead accounts
for it too.
public Padding Padding { get; set; }
Property Value
ShowHeader
Gets or sets whether the header row is drawn. Defaults to true. When false and the panel is non-collapsible, the header is fully suppressed: a borderless panel shows only its body, and a bordered panel draws a clean box (titleless top border) around the body. A collapsible panel always shows its header regardless of this flag (see Collapsible), since the header is the only affordance for toggling it.
public virtual bool ShowHeader { get; set; }
Property Value
ShowHeaderSeparator
Gets or sets whether a horizontal separator line is drawn beneath a borderless header. Has no effect on the bordered header style. Defaults to false.
public virtual bool ShowHeaderSeparator { get; set; }
Property Value
Title
Gets or sets the header title text shown next to the expand/collapse indicator.
public string? Title { get; set; }
Property Value
Remarks
Upgraded with richer behavior in a later task; backed by SetProperty
so changes invalidate the container.
UseSafeBorder
Gets or sets whether the border is drawn with ASCII characters (+ - |) instead of
Unicode box-drawing characters. Default false. When true,
the chosen HeaderStyle (Bordered/Rounded/DoubleLine) renders with safe ASCII
chars — useful for terminals or fonts that mis-render box-drawing glyphs. Orthogonal to the
style: the style is remembered and restored when this is set back to false.
public bool UseSafeBorder { get; set; }
Property Value
WantsMouseEvents
Whether this control wants to receive mouse events
public bool WantsMouseEvents { get; }
Property Value
Methods
AddControl(IWindowControl)
Adds a child control to the host.
public void AddControl(IWindowControl control)
Parameters
controlIWindowControl
ClearControls()
Removes all child controls from the host.
public void ClearControls()
Collapse()
Collapses the body, leaving only the header visible.
public virtual void Collapse()
Expand()
Expands the body.
public virtual void Expand()
GetChildren()
Gets the direct child controls of this container. Does not recursively include grandchildren - recursion happens in caller.
public IReadOnlyList<IWindowControl> GetChildren()
Returns
- IReadOnlyList<IWindowControl>
Read-only list of direct child controls (may include other containers)
Remarks
IMPORTANT: For HorizontalGridControl, this should return columns AND splitters in order: [Column1, Splitter1, Column2, Splitter2, Column3]
Splitters are IInteractiveControl and should be included in Tab navigation.
GetLogicalCursorPosition()
Gets the logical cursor position within the control's content coordinate system. This should be the raw position without any visual adjustments for margins, scrolling, etc.
public Point? GetLogicalCursorPosition()
Returns
- Point?
Logical cursor position or null if no cursor.
GetVisibleHeightForControl(IWindowControl)
Gets the actual visible height for a control within the container viewport. Returns null if the control is not found or visibility cannot be determined.
public int? GetVisibleHeightForControl(IWindowControl control)
Parameters
controlIWindowControlThe control to check
Returns
- int?
The number of visible lines, or null if unknown
Remarks
For a direct body child, reports the arranged body region height (the real on-screen space the layout allotted the body) so scroll-aware children derive a correct viewport. Falls back to delegating to the parent container when the region is not yet known or the control is not a direct child.
Invalidate(Invalidation, IWindowControl?)
Marks this container as needing the specified work on the next frame. The request propagates up the container chain and folds into the owning window's frame-intent accumulator.
public void Invalidate(Invalidation work, IWindowControl? callerControl = null)
Parameters
workInvalidationThe kind of work requested: Repaint (appearance-only, Measure skipped) or Relayout (full layout).
callerControlIWindowControlThe control that triggered the invalidation, if any (cycle guard).
MeasureDOM(LayoutConstraints)
Measures the control's desired size given the available constraints.
public override LayoutSize MeasureDOM(LayoutConstraints constraints)
Parameters
constraintsLayoutConstraintsThe layout constraints (min/max width/height).
Returns
- LayoutSize
The desired size of the control.
PaintDOM(CharacterBuffer, LayoutRect, LayoutRect, Color, Color)
Paints the control's content directly to a CharacterBuffer.
public override void PaintDOM(CharacterBuffer buffer, LayoutRect bounds, LayoutRect clipRect, Color defaultForeground, Color defaultBackground)
Parameters
bufferCharacterBufferThe buffer to paint to.
boundsLayoutRectThe absolute bounds where the control should paint.
clipRectLayoutRectThe clipping rectangle (visible area).
defaultForegroundColorThe default foreground color from the container.
defaultBackgroundColorThe default background color from the container.
Remarks
Paints the borderless header (indicator + title + optional separator). The bordered style and the body children are painted by the layout partial added in a later task.
ProcessKey(ConsoleKeyInfo)
Processes a keyboard input event.
public bool ProcessKey(ConsoleKeyInfo key)
Parameters
keyConsoleKeyInfoThe key information for the pressed key.
Returns
- bool
True if the key was handled by this control; otherwise, false.
Remarks
Toggles the expanded state when the panel is focused and Enter or Space is pressed. Returns false for any other key (or when not focused/disabled) so the key can bubble to focus traversal.
ProcessMouseEvent(MouseEventArgs)
Processes a mouse event for this control
public bool ProcessMouseEvent(MouseEventArgs args)
Parameters
argsMouseEventArgsMouse event arguments with control-relative coordinates
Returns
- bool
True if the event was handled and should not propagate further
Remarks
Handles a left click on the header row by focusing the panel and toggling its expanded state. A click on the expanded body is forwarded to the body child under the cursor (focusing it when it is focusable) so that mouse focus reaches body controls even when the panel is hosted inside a self-painting forwarding container (e.g. a ScrollablePanelControl) whose DOM hit-test resolves the panel rather than the deep body child. When the panel is a direct DOM child of the window the dispatcher already targets the body child directly, so this forwarding is a harmless no-op there.
RemoveControl(IWindowControl)
Removes a child control from the host.
public void RemoveControl(IWindowControl control)
Parameters
controlIWindowControl
SetLogicalCursorPosition(Point)
Sets the logical cursor position within the control's content coordinate system.
public void SetLogicalCursorPosition(Point position)
Parameters
positionPoint
Toggle()
Toggles between expanded and collapsed.
public virtual void Toggle()
Events
ExpandedChanged
Fired once each time the expanded state actually changes. Arg is the new expanded value.
public event EventHandler<bool>? ExpandedChanged
Event Type
MouseClick
Fired on a left click — on a collapsible header (which also toggles the panel) or on the expanded body when no body child consumes the click.
public event EventHandler<MouseEventArgs>? MouseClick
Event Type
MouseDoubleClick
Event fired when the control is double-clicked
public event EventHandler<MouseEventArgs>? MouseDoubleClick
Event Type
MouseEnter
Event fired when the mouse enters the control area
public event EventHandler<MouseEventArgs>? MouseEnter
Event Type
MouseLeave
Event fired when the mouse leaves the control area
public event EventHandler<MouseEventArgs>? MouseLeave
Event Type
MouseMove
Event fired when the mouse moves over the control
public event EventHandler<MouseEventArgs>? MouseMove
Event Type
MouseRightClick
Event fired when the control is right-clicked (Button3)
public event EventHandler<MouseEventArgs>? MouseRightClick