Panel System
SharpConsoleUI provides a configurable Panel system for the top and bottom bars of the screen. Panels sit outside the window area and display elements like status text, clocks, task bars, start menus, and performance metrics.
Table of Contents
- Overview
- Configuration
- Elements Factory
- Built-in Elements
- PanelBuilder
- Runtime Access
- Panel Visibility
- Complete Examples
Overview
The panel system replaces the former StatusBarOptions configuration. Instead of toggling predefined status bar features, you now compose panels from discrete elements using a fluent builder.
Each panel has three zones — Left, Center, and Right — and elements are laid out within those zones. Elements can be fixed-width or flexible (flex-grow), and the layout engine distributes space automatically.
┌─────────────────────────────────────────────────────────────┐
│ [Left elements...] [Center...] [Right elements] │ ← Top Panel
├─────────────────────────────────────────────────────────────┤
│ │
│ Window Area │
│ │
├─────────────────────────────────────────────────────────────┤
│ [Left elements...] [Center...] [Right elements] │ ← Bottom Panel
└─────────────────────────────────────────────────────────────┘
Note: For the in-window
StatusBarControl(three-zone bar with clickable items and shortcut hints), see the StatusBarControl documentation. That is a different control from the system-level panels documented here.
Configuration
Panels are configured via TopPanelConfig and BottomPanelConfig on ConsoleWindowSystemOptions. Each accepts a function that receives a PanelBuilder and returns it with elements added:
using SharpConsoleUI.Panel;
var options = new ConsoleWindowSystemOptions(
TopPanelConfig: panel => panel
.Left(Elements.StatusText("[bold cyan]My App[/]"))
.Right(Elements.Performance()),
BottomPanelConfig: panel => panel
.Left(Elements.StartMenu())
.Center(Elements.TaskBar())
.Right(Elements.Clock())
);
var windowSystem = new ConsoleWindowSystem(
new NetConsoleDriver(RenderMode.Buffer),
options: options);
Panel Visibility
Show or hide panels at configuration time:
var options = new ConsoleWindowSystemOptions(
ShowTopPanel: true, // default: true
ShowBottomPanel: false // hide bottom panel entirely
);
Or toggle at runtime:
windowSystem.PanelStateService.ShowTopPanel = false;
windowSystem.PanelStateService.ShowBottomPanel = true;
Default Panels
When no TopPanelConfig or BottomPanelConfig is provided, the system creates default panels with a status text element in each. This lets PanelStateService.TopStatus and BottomStatus work out of the box.
Elements Factory
The Elements static class provides factory methods for all built-in panel elements:
using SharpConsoleUI.Panel;
Elements.StatusText("text") // Static or markup text
Elements.Separator() // Vertical separator character
Elements.TaskBar() // Window list with Alt+N shortcuts
Elements.Clock() // Live clock display
Elements.Performance() // FPS and dirty-char metrics
Elements.StartMenu() // Start button + menu system
Elements.Custom("name") // Custom render callback
Each method returns a builder that supports fluent configuration before being passed to a PanelBuilder zone.
Built-in Elements
StatusText
Displays static or markup-formatted text. Supports click handlers.
Elements.StatusText("[bold cyan]My Application[/]")
.WithName("title")
.WithColor(Color.Cyan1)
.OnClick(() => ShowAbout())
Builder Methods:
| Method | Description |
|---|---|
WithName(string) |
Set element name for lookup |
WithColor(Color) |
Override text color |
OnClick(Action) |
Click handler |
Properties (on StatusTextElement):
| Property | Type | Description |
|---|---|---|
Text |
string |
Markup text to display (get/set) |
TextColor |
Color? |
Optional color override |
ClickHandler |
Action? |
Optional click callback |
Separator
Renders a single vertical separator character between elements.
Elements.Separator()
.WithChar('|')
.WithColor(Color.Grey50)
Builder Methods:
| Method | Description |
|---|---|
WithName(string) |
Set element name |
WithChar(char) |
Separator character (default: │) |
WithColor(Color) |
Separator color |
TaskBar
Displays a list of open windows with Alt+N keyboard shortcuts. Flexes to fill available space.
Elements.TaskBar()
.WithActiveColor(Color.Cyan1)
.WithInactiveColor(Color.Grey50)
.WithMinimizedDim(true)
Builder Methods:
| Method | Description |
|---|---|
WithName(string) |
Set element name |
WithActiveColor(Color) |
Active window highlight color |
WithInactiveColor(Color) |
Inactive window color |
WithMinimizedDim(bool) |
Dim minimized windows (default: true) |
Features:
Alt+1throughAlt+9to switch windows- Click to activate a window
- Minimized windows shown dimmed
- Automatically updates when windows are added/removed
Clock
Displays the current time with configurable format. Auto-updates.
Elements.Clock()
.WithFormat("HH:mm:ss")
.WithColor(Color.Yellow)
.WithUpdateInterval(1000)
Builder Methods:
| Method | Description |
|---|---|
WithName(string) |
Set element name |
WithFormat(string) |
Time format string (default: "HH:mm") |
WithColor(Color) |
Text color |
WithUpdateInterval(int) |
Update interval in ms (default: 1000) |
Performance
Displays real-time FPS and dirty character count. Auto-updates.
Elements.Performance()
.ShowFPS(true)
.ShowDirtyChars(true)
Builder Methods:
| Method | Description |
|---|---|
WithName(string) |
Set element name |
ShowFPS(bool) |
Show FPS counter (default: true) |
ShowDirtyChars(bool) |
Show dirty char count (default: true) |
StartMenu
Adds a Start button that opens a Windows-like menu with categorized actions, window list, and system features.
Elements.StartMenu()
.WithText("☰ Start")
.WithColors(Color.White, Color.DarkBlue)
.WithShortcutKey(ConsoleKey.Spacebar, ConsoleModifiers.Control)
.WithOptions(new StartMenuOptions
{
Layout = StartMenuLayout.TwoColumn,
AppName = "My App",
AppVersion = "1.0.0",
ShowSystemCategory = true,
ShowWindowList = true
})
Builder Methods:
| Method | Description |
|---|---|
WithName(string) |
Set element name |
WithText(string) |
Button text (default: "☰ Start") |
WithColors(Color, Color) |
Button foreground and background |
WithShortcutKey(ConsoleKey, ConsoleModifiers) |
Keyboard shortcut (default: Ctrl+Space) |
WithOptions(StartMenuOptions) |
Menu configuration |
StartMenuOptions:
| Property | Type | Default | Description |
|---|---|---|---|
Layout |
StartMenuLayout |
TwoColumn |
SingleColumn or TwoColumn |
AppName |
string? |
null |
App name in header |
AppVersion |
string? |
null |
App version in header |
ShowIcons |
bool |
true |
Show icons in header/exit |
HeaderIcon |
string |
"☰" |
Header icon |
ShowSystemCategory |
bool |
true |
Show System category |
ShowWindowList |
bool |
true |
Show Windows list |
SidebarStyle |
StartMenuSidebarStyle |
IconLabel |
Sidebar display style |
BackgroundGradient |
GradientBackground? |
null |
Menu gradient background |
Registering Actions:
Actions are registered directly on the StartMenuElement:
var startMenu = windowSystem.BottomPanel!.FindElement<StartMenuElement>("startmenu")!;
startMenu.RegisterAction("New Document", () => CreateDoc(), category: "File", order: 10);
startMenu.RegisterAction("Open File", () => OpenFile(), category: "File", order: 20);
startMenu.RegisterAction("Calculator", () => OpenCalc(), category: "Tools", order: 10);
StartMenuElement Methods:
| Method | Description |
|---|---|
RegisterAction(name, callback, category?, order?) |
Register a menu action |
UnregisterAction(name) |
Remove an action |
GetActions() |
Get all registered actions |
Show() |
Programmatically open the menu |
Custom
Create elements with custom render callbacks for full control.
Elements.Custom("myElement")
.WithFixedWidth(20)
.WithRenderCallback((buffer, x, y, width, fg, bg) =>
{
buffer.WriteString(x, y, "Custom content", fg, bg);
})
.WithClickHandler(() => HandleClick())
Builder Methods:
| Method | Description |
|---|---|
WithFixedWidth(int) |
Set fixed width |
WithFlexGrow(int) |
Set flex grow factor |
WithRenderCallback(Action<...>) |
Set render function |
WithClickHandler(Action) |
Set click handler |
PanelBuilder
The PanelBuilder provides a fluent API for composing panel layouts:
var panel = Panel.Builder()
.Left(Elements.StatusText("Left"))
.Left(Elements.Separator())
.Center(Elements.TaskBar())
.Right(Elements.Clock())
.WithBackgroundColor(Color.DarkBlue)
.WithForegroundColor(Color.White)
.Visible(true)
.Build();
Methods:
| Method | Returns | Description |
|---|---|---|
Left(IPanelElement) |
PanelBuilder |
Add element to left zone |
Left(IPanelElementBuilder) |
PanelBuilder |
Add builder's element to left zone |
Left(params object[]) |
PanelBuilder |
Add mixed elements/builders to left |
Center(...) |
PanelBuilder |
Same variants for center zone |
Right(...) |
PanelBuilder |
Same variants for right zone |
WithBackgroundColor(Color) |
PanelBuilder |
Set background color |
WithForegroundColor(Color) |
PanelBuilder |
Set foreground color |
Visible(bool) |
PanelBuilder |
Set initial visibility |
Build() |
Panel |
Build the panel |
Runtime Access
Via PanelStateService
Access panels and convenience properties through windowSystem.PanelStateService:
// Convenience status text shortcuts
windowSystem.PanelStateService.TopStatus = "[bold cyan]Connected[/]";
windowSystem.PanelStateService.BottomStatus = "Ready";
// Toggle visibility
windowSystem.PanelStateService.ShowTopPanel = false;
windowSystem.PanelStateService.ShowBottomPanel = true;
// Check dirty state
if (windowSystem.PanelStateService.IsDirty) { /* ... */ }
Via Panel References
Access panels directly for element manipulation:
// Direct panel access
var topPanel = windowSystem.PanelStateService.TopPanel;
var bottomPanel = windowSystem.PanelStateService.BottomPanel;
// Or via shorthand properties
var bottomPanel = windowSystem.BottomPanel;
// Find elements by name
var clock = bottomPanel!.FindElement<ClockElement>("clock");
// Find elements by type
var startMenu = bottomPanel!.FindElement<StartMenuElement>("startmenu");
// Check for element existence
if (bottomPanel!.HasElement<TaskBarElement>()) { /* ... */ }
// Get all elements of a type
var allText = topPanel!.FindAllElements<StatusTextElement>();
// Add/remove elements at runtime
topPanel!.AddRight(new StatusTextElement("New item"));
topPanel!.Remove("itemName");
// Clear zones
topPanel!.ClearRight();
bottomPanel!.ClearAll();
Panel Properties
| Property | Type | Description |
|---|---|---|
Visible |
bool |
Panel visibility (triggers screen redraw) |
BackgroundColor |
Color? |
Background color (falls back to theme) |
ForegroundColor |
Color? |
Foreground color (falls back to theme) |
IsDirty |
bool |
Whether panel needs redraw |
Height |
int |
1 if visible, 0 if hidden |
Panel Methods
| Method | Description |
|---|---|
AddLeft(params IPanelElement[]) |
Add elements to left zone |
AddCenter(params IPanelElement[]) |
Add elements to center zone |
AddRight(params IPanelElement[]) |
Add elements to right zone |
Remove(string name) |
Remove element by name |
Remove(IPanelElement) |
Remove specific element |
FindElement<T>(string name) |
Find and cast element by name |
HasElement<T>() |
Check if element type exists |
FindAllElements<T>() |
Get all elements of type |
ClearAll() |
Clear all zones |
ClearLeft() / ClearCenter() / ClearRight() |
Clear specific zone |
MarkDirty() |
Mark for redraw |
Complete Examples
Minimal (Default Panels)
// Default panels with status text elements
var windowSystem = new ConsoleWindowSystem(new NetConsoleDriver(RenderMode.Buffer));
windowSystem.PanelStateService.TopStatus = "[bold]My App[/]";
windowSystem.PanelStateService.BottomStatus = "Ready";
Windows-like Desktop
var options = new ConsoleWindowSystemOptions(
TopPanelConfig: panel => panel
.Left(Elements.StatusText("[bold cyan]My App[/]"))
.Right(Elements.Performance()),
BottomPanelConfig: panel => panel
.Left(Elements.StartMenu()
.WithText("☰ Start")
.WithOptions(new StartMenuOptions
{
Layout = StartMenuLayout.TwoColumn,
AppName = "My App",
AppVersion = "1.0.0"
}))
.Center(Elements.TaskBar())
.Right(Elements.Clock().WithFormat("HH:mm:ss"))
);
var windowSystem = new ConsoleWindowSystem(
new NetConsoleDriver(RenderMode.Buffer),
options: options);
// Register Start Menu actions
var startMenu = windowSystem.BottomPanel!.FindElement<StartMenuElement>("startmenu")!;
startMenu.RegisterAction("New Document", () => { /* ... */ }, category: "File", order: 10);
startMenu.RegisterAction("Settings", () => { /* ... */ }, category: "Edit", order: 10);
Dashboard (No Start Menu)
var options = new ConsoleWindowSystemOptions(
EnableFrameRateLimiting: true,
TargetFPS: 30,
TopPanelConfig: panel => panel
.Left(Elements.StatusText("[bold cyan]System Dashboard[/]"))
.Right(Elements.Performance()),
BottomPanelConfig: panel => panel
.Left(Elements.StatusText("Press [yellow]F10[/] to exit"))
);
var windowSystem = new ConsoleWindowSystem(
new NetConsoleDriver(RenderMode.Buffer),
options: options);
Full-Screen App (Top Panel Only)
var options = new ConsoleWindowSystemOptions(
ShowBottomPanel: false,
TopPanelConfig: panel => panel
.Left(Elements.StatusText("[bold]My Application[/]"))
.Right(Elements.Clock())
);
No Panels
var options = new ConsoleWindowSystemOptions(
ShowTopPanel: false,
ShowBottomPanel: false
);
Custom Element
var options = new ConsoleWindowSystemOptions(
TopPanelConfig: panel => panel
.Left(Elements.StatusText("[bold]App[/]"))
.Right(Elements.Custom("indicator")
.WithFixedWidth(12)
.WithRenderCallback((buffer, x, y, width, fg, bg) =>
{
var status = IsConnected ? "[green]●[/]" : "[red]●[/]";
buffer.WriteMarkup(x, y, $" {status} Online", fg, bg);
}))
);
Creating Custom Elements
Extend PanelElement to create reusable custom elements:
public class BatteryElement : PanelElement
{
public BatteryElement() : base("battery") { }
public override int? FixedWidth => 10;
public override void Render(CharacterBuffer buffer, int x, int y,
int width, Color fg, Color bg)
{
var level = GetBatteryLevel();
var color = level > 20 ? Color.Green : Color.Red;
buffer.WriteString(x, y, $"⚡ {level}%", color, bg);
}
}
// Use it:
var options = new ConsoleWindowSystemOptions(
TopPanelConfig: panel => panel
.Right(new BatteryElement())
);
Migration from StatusBarOptions
If you were using StatusBarOptions, here's how to migrate:
| Old (StatusBarOptions) | New (Panel System) |
|---|---|
ShowTopStatus: true |
ShowTopPanel: true |
ShowBottomStatus: true |
ShowBottomPanel: true |
ShowStartButton: true |
Add Elements.StartMenu() to a panel |
ShowTaskBar: true |
Add Elements.TaskBar() to a panel |
StartButtonLocation: Bottom |
Add StartMenu to BottomPanelConfig |
StartButtonPosition: Left |
Use .Left(Elements.StartMenu()) |
windowSystem.TopStatus = "..." |
windowSystem.PanelStateService.TopStatus = "..." |
windowSystem.BottomStatus = "..." |
windowSystem.PanelStateService.BottomStatus = "..." |
windowSystem.RegisterStartMenuAction(...) |
startMenuElement.RegisterAction(...) |
Before:
var options = new ConsoleWindowSystemOptions(
StatusBarOptions: new StatusBarOptions(
ShowStartButton: true,
StartButtonLocation: StatusBarLocation.Bottom,
ShowTaskBar: true,
StartMenu: new StartMenuOptions { AppName = "My App" }
)
);
windowSystem.TopStatus = "[bold]Title[/]";
windowSystem.RegisterStartMenuAction("New", CreateNew, "File", 10);
After:
var options = new ConsoleWindowSystemOptions(
TopPanelConfig: panel => panel
.Left(Elements.StatusText("[bold]Title[/]")),
BottomPanelConfig: panel => panel
.Left(Elements.StartMenu()
.WithOptions(new StartMenuOptions { AppName = "My App" }))
.Center(Elements.TaskBar())
);
var startMenu = windowSystem.BottomPanel!.FindElement<StartMenuElement>("startmenu")!;
startMenu.RegisterAction("New", () => CreateNew(), category: "File", order: 10);
See Also
- Configuration Guide - System configuration reference
- State Services - PanelStateService and other services
- Fluent Builders - Panel and element builder APIs
- Plugins - Plugin integration with Start Menu