Skip to content

Commit 6d73f10

Browse files
committed
Add HorizontalSplitterControl with visibility edge case handling
- New HorizontalSplitterControl for drag-to-resize vertically stacked controls - Mouse drag and keyboard (Up/Down, Shift for 5-row jump) support - Auto-neighbor discovery from container children, skipping invisible controls - Auto-hide splitter when fewer than 2 visible neighbors, auto-show on restore - Distinguish user-set Visible=false from auto-hide to prevent override - Add IWindowControl.Height property and layout support for explicit heights - Builder, demo window, and 48 tests including 9 visibility edge case tests
1 parent 9a26660 commit 6d73f10

23 files changed

Lines changed: 2149 additions & 15 deletions
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
using SharpConsoleUI;
2+
using SharpConsoleUI.Builders;
3+
using SharpConsoleUI.Controls;
4+
using SharpConsoleUI.Layout;
5+
using SharpConsoleUI.Helpers;
6+
using SharpConsoleUI.Rendering;
7+
8+
namespace DemoApp.DemoWindows;
9+
10+
internal static class HorizontalSplitterDemoWindow
11+
{
12+
private const int WindowWidth = 90;
13+
private const int WindowHeight = 30;
14+
15+
public static Window Create(ConsoleWindowSystem ws)
16+
{
17+
// Create a two-column layout showing different horizontal splitter scenarios
18+
var grid = Controls.HorizontalGrid()
19+
.WithAlignment(HorizontalAlignment.Stretch)
20+
.WithVerticalAlignment(VerticalAlignment.Fill)
21+
.WithMargin(1, 0, 1, 0)
22+
// LEFT COLUMN: Two Fill panels with splitter between them
23+
.Column(col =>
24+
{
25+
col.Add(Controls.Markup()
26+
.AddLine("[bold cyan]Both Fill[/]")
27+
.AddLine("[dim]Drag the ═══ bar to resize[/]")
28+
.WithMargin(0, 0, 0, 0).Build());
29+
30+
var topPanel = Controls.ScrollablePanel()
31+
.WithBorderStyle(BorderStyle.Rounded)
32+
.WithHeader("Top Panel")
33+
.WithBorderColor(Color.Green)
34+
.WithVerticalAlignment(VerticalAlignment.Fill)
35+
.WithHeight(10)
36+
.AddControl(Controls.Markup()
37+
.AddLine("[green]This panel grows/shrinks[/]")
38+
.AddLine("")
39+
.AddLine("Use the splitter bar below")
40+
.AddLine("to resize this panel.")
41+
.AddLine("")
42+
.AddLine("Try keyboard: focus the bar,")
43+
.AddLine("then Up/Down arrows.")
44+
.AddLine("Shift+Up/Down for 5-row jumps.")
45+
.WithMargin(1, 0, 1, 0).Build())
46+
.Build();
47+
col.Add(topPanel);
48+
49+
col.Add(Controls.HorizontalSplitter()
50+
.Build());
51+
52+
var bottomPanel = Controls.ScrollablePanel()
53+
.WithBorderStyle(BorderStyle.Rounded)
54+
.WithHeader("Bottom Panel")
55+
.WithBorderColor(Color.Cyan1)
56+
.WithVerticalAlignment(VerticalAlignment.Fill)
57+
.AddControl(Controls.Markup()
58+
.AddLine("[cyan]This panel adjusts too[/]")
59+
.AddLine("")
60+
.AddLine("When the top panel grows,")
61+
.AddLine("this one shrinks, and")
62+
.AddLine("vice versa.")
63+
.AddLine("")
64+
.AddLine("Min height is clamped to 3.")
65+
.WithMargin(1, 0, 1, 0).Build())
66+
.Build();
67+
col.Add(bottomPanel);
68+
})
69+
// RIGHT COLUMN: Panel with explicit height + Fill panel
70+
.Column(col =>
71+
{
72+
col.Add(Controls.Markup()
73+
.AddLine("[bold yellow]Explicit + Fill[/]")
74+
.AddLine("[dim]Top has explicit Height[/]")
75+
.WithMargin(0, 0, 0, 0).Build());
76+
77+
var fixedPanel = Controls.ScrollablePanel()
78+
.WithBorderStyle(BorderStyle.Rounded)
79+
.WithHeader("Fixed Height (8)")
80+
.WithBorderColor(Color.Yellow)
81+
.WithVerticalAlignment(VerticalAlignment.Fill)
82+
.WithHeight(8)
83+
.AddControl(Controls.Markup()
84+
.AddLine("[yellow]This panel has Height=8[/]")
85+
.AddLine("")
86+
.AddLine("Dragging the splitter")
87+
.AddLine("changes this height.")
88+
.WithMargin(1, 0, 1, 0).Build())
89+
.Build();
90+
col.Add(fixedPanel);
91+
92+
col.Add(Controls.HorizontalSplitter()
93+
.WithMinHeights(4, 4)
94+
.Build());
95+
96+
var fillPanel = Controls.ScrollablePanel()
97+
.WithBorderStyle(BorderStyle.Rounded)
98+
.WithHeader("Fill Panel")
99+
.WithBorderColor(Color.Orange1)
100+
.WithVerticalAlignment(VerticalAlignment.Fill)
101+
.AddControl(Controls.Markup()
102+
.AddLine("[orange1]This panel uses Fill[/]")
103+
.AddLine("")
104+
.AddLine("It takes remaining space.")
105+
.AddLine("Drag to see it adjust.")
106+
.WithMargin(1, 0, 1, 0).Build())
107+
.Build();
108+
col.Add(fillPanel);
109+
})
110+
.Build();
111+
112+
var gradient = ColorGradient.FromColors(
113+
new Color(20, 10, 30),
114+
new Color(10, 25, 20));
115+
116+
var bottomControl = Controls.ScrollablePanel()
117+
.WithBorderStyle(BorderStyle.Rounded)
118+
.WithHeader("Bottom Control")
119+
.WithBorderColor(Color.Magenta1)
120+
.WithVerticalAlignment(VerticalAlignment.Fill)
121+
.WithMargin(1, 0, 1, 0)
122+
.AddControl(Controls.Markup()
123+
.AddLine("[magenta1]Below the grid[/]")
124+
.AddLine("")
125+
.AddLine("This control sits below the HorizontalGrid.")
126+
.AddLine("The splitter above resizes the grid and this panel.")
127+
.WithMargin(1, 0, 1, 0).Build())
128+
.Build();
129+
130+
var splitter = Controls.HorizontalSplitter().Build();
131+
132+
return new WindowBuilder(ws)
133+
.WithTitle("Horizontal Splitter Demo")
134+
.WithSize(WindowWidth, WindowHeight)
135+
.Centered()
136+
.WithBackgroundGradient(gradient, GradientDirection.Vertical)
137+
.WithForegroundColor(Color.White)
138+
.AddControl(grid)
139+
.AddControl(splitter)
140+
.AddControl(bottomControl)
141+
.BuildAndShow();
142+
}
143+
}

Examples/DemoApp/DemoWindows/LauncherWindow.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ public static Window Create(ConsoleWindowSystem ws)
2323
.AddItem("IDE Layout", subtitle: "IDE-like application UI", content: MakeInfoPanel("IDE Layout"))
2424
.AddItem("File Explorer", subtitle: "Filesystem browser", content: MakeInfoPanel("File Explorer"))
2525
.AddItem("Multi-Tab Demo", subtitle: "TabControl with multiple tabs", content: MakeInfoPanel("Multi-Tab Demo"))
26-
.AddItem("WinUI Layout", subtitle: "WinUI-inspired settings layout", content: MakeInfoPanel("WinUI Layout")))
26+
.AddItem("WinUI Layout", subtitle: "WinUI-inspired settings layout", content: MakeInfoPanel("WinUI Layout"))
27+
.AddItem("Horizontal Splitter", subtitle: "Drag-to-resize horizontal bars", content: MakeInfoPanel("Horizontal Splitter")))
2728
.AddHeader("Controls", Color.Green, header => header
2829
.AddItem("Interactive Demo", subtitle: "Real-time key press handling", content: MakeInfoPanel("Interactive Demo"))
2930
.AddItem("Dropdown", subtitle: "Cascading dropdowns", content: MakeInfoPanel("Dropdown"))
@@ -119,6 +120,7 @@ private static void LaunchDemo(ConsoleWindowSystem ws, string demoName)
119120
"File Explorer" => FileExplorerWindow.Create(ws),
120121
"Multi-Tab Demo" => TabDemoWindow.Create(ws),
121122
"WinUI Layout" => WinUIDemoWindow.Create(ws),
123+
"Horizontal Splitter" => HorizontalSplitterDemoWindow.Create(ws),
122124
"Interactive Demo" => InteractiveWindow.Create(ws),
123125
"Dropdown" => DropdownWindow.Create(ws),
124126
"List View" => ListViewWindow.Create(ws),
@@ -223,6 +225,23 @@ private static void LaunchDemo(ConsoleWindowSystem ws, string demoName)
223225
" - HorizontalGridControl, MarkupControl",
224226
" - CheckboxControl, ButtonControl",
225227
},
228+
"Horizontal Splitter" => new List<string>
229+
{
230+
"[bold cyan]Horizontal Splitter[/]",
231+
"",
232+
"Drag-to-resize horizontal bar between vertically",
233+
"stacked controls. Supports mouse drag and keyboard.",
234+
"",
235+
"[dim]Features:[/]",
236+
" - Mouse drag to resize",
237+
" - Keyboard: Up/Down arrows, Shift for 5-row jump",
238+
" - Smart resize with Fill and explicit heights",
239+
" - Min height clamping (default: 3 rows)",
240+
"",
241+
"[dim]Controls used:[/]",
242+
" - HorizontalSplitterControl",
243+
" - ScrollablePanelControl, HorizontalGridControl",
244+
},
226245
"Interactive Demo" => new List<string>
227246
{
228247
"[bold cyan]Interactive Demo[/]",

0 commit comments

Comments
 (0)