Skip to content

Commit 8f8b065

Browse files
committed
Add hierarchical items to NavigationView with headers, collapse/expand, and scrollable nav pane
- Add NavigationItemType enum (Item, Header, Separator) and hierarchy support to NavigationItem - Extract item management into NavigationView.Items.cs partial class - Add keyboard navigation for hierarchy (Left/Right collapse/expand, Enter/Space toggle) - Add NavigationHeaderBuilder to fluent builder API for grouped items - Add InsertContent to ColumnContainer and InsertControl to ScrollablePanelControl - Refactor DemoApp LauncherWindow to use NavigationView with categorized headers - Fix nav item wrapping bug when scrollbar visible (set Wrap=false on nav items) - Fix ButtonControl accepting clicks outside its visual bounds - Add ScrollChildIntoView guard for zero-dimension viewport - Update NavigationView docs and EXAMPLES.md with screenshot
1 parent aa7c561 commit 8f8b065

15 files changed

Lines changed: 1499 additions & 492 deletions

File tree

Examples/DemoApp/DemoWindows/LauncherWindow.cs

Lines changed: 81 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,145 +1,115 @@
11
using SharpConsoleUI;
22
using SharpConsoleUI.Builders;
33
using SharpConsoleUI.Controls;
4+
using SharpConsoleUI.Helpers;
45
using SharpConsoleUI.Layout;
6+
using SharpConsoleUI.Rendering;
57

68
namespace DemoApp.DemoWindows;
79

810
public static class LauncherWindow
911
{
10-
private static readonly List<string> DetailPlaceholder = new()
11-
{
12-
"[bold]Welcome to SharpConsoleUI[/]",
13-
"",
14-
"Select a demo from the tree to see its description.",
15-
};
16-
1712
public static Window Create(ConsoleWindowSystem ws)
1813
{
19-
// Build controls first
20-
var demoTree = Controls.Tree()
21-
.WithGuide(TreeGuide.Line)
22-
.WithHighlightColors(Color.White, Color.Blue)
23-
.Build();
24-
25-
BuildDemoTree(demoTree);
26-
27-
var detailMarkup = Controls.Markup()
28-
.AddLines(DetailPlaceholder.ToArray())
29-
.WithMargin(1, 1, 1, 1)
30-
.Build();
31-
32-
var launchButton = Controls.Button()
33-
.WithText(" Launch Demo ")
34-
.WithMargin(1, 1, 0, 0)
35-
.WithBorder(ButtonBorderStyle.Rounded)
36-
.Build();
37-
38-
launchButton.Visible = false;
39-
40-
var detailPane = new ScrollablePanelControl();
41-
detailPane.AddControl(detailMarkup);
42-
detailPane.AddControl(launchButton);
43-
44-
// Update detail pane when tree selection changes
45-
demoTree.SelectedNodeChanged += (sender, args) =>
46-
{
47-
if (args.Node != null)
14+
var nav = Controls.NavigationView()
15+
.WithNavWidth(30)
16+
.WithPaneHeader("[bold white] SharpConsoleUI[/]")
17+
.WithContentBorder(BorderStyle.Rounded)
18+
.WithContentBorderColor(Color.Grey37)
19+
.WithContentBackground(new Color(30, 30, 40))
20+
.WithContentPadding(1, 0, 1, 0)
21+
.AddHeader("Layout & Windows", Color.Cyan1, header => header
22+
.AddItem("Border Styles", subtitle: "Explore window border styles", content: MakeInfoPanel("Border Styles"))
23+
.AddItem("IDE Layout", subtitle: "IDE-like application UI", content: MakeInfoPanel("IDE Layout"))
24+
.AddItem("File Explorer", subtitle: "Filesystem browser", content: MakeInfoPanel("File Explorer"))
25+
.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")))
27+
.AddHeader("Controls", Color.Green, header => header
28+
.AddItem("Interactive Demo", subtitle: "Real-time key press handling", content: MakeInfoPanel("Interactive Demo"))
29+
.AddItem("Dropdown", subtitle: "Cascading dropdowns", content: MakeInfoPanel("Dropdown"))
30+
.AddItem("List View", subtitle: "NuGet-style package browser", content: MakeInfoPanel("List View"))
31+
.AddItem("Table", subtitle: "Interactive employee directory", content: MakeInfoPanel("Table"))
32+
.AddItem("DataGrid", subtitle: "Virtual DataGrid with 10K rows", content: MakeInfoPanel("DataGrid"))
33+
.AddItem("Nerd Fonts", subtitle: "NerdFont icon showcase", content: MakeInfoPanel("Nerd Fonts"))
34+
.AddItem("Markup Syntax", subtitle: "Rich markup system demo", content: MakeInfoPanel("Markup Syntax"))
35+
.AddItem("International & Emoji", subtitle: "Unicode & emoji support", content: MakeInfoPanel("International & Emoji"))
36+
.AddItem("Data Binding", subtitle: "MVVM data binding", content: MakeInfoPanel("Data Binding")))
37+
.AddHeader("Data Visualization", Color.Yellow, header => header
38+
.AddItem("Graphs & Charts", subtitle: "Live sparklines & bar graphs", content: MakeInfoPanel("Graphs & Charts")))
39+
.AddHeader("Rendering", Color.Orange1, header => header
40+
.AddItem("Gradients", subtitle: "Gradient text & backgrounds", content: MakeInfoPanel("Gradients"))
41+
.AddItem("Animations", subtitle: "Window animations & easing", content: MakeInfoPanel("Animations"))
42+
.AddItem("Image Rendering", subtitle: "Pixel art with half-blocks", content: MakeInfoPanel("Image Rendering"))
43+
.AddItem("Image Viewer", subtitle: "Load & display image files", content: MakeInfoPanel("Image Viewer")))
44+
.AddHeader("Utilities", Color.Magenta1, header => header
45+
.AddItem("Built-in Dialogs", subtitle: "File pickers & system dialogs", content: MakeInfoPanel("Built-in Dialogs"))
46+
.AddItem("Digital Clock", subtitle: "FIGlet-rendered clock", content: MakeInfoPanel("Digital Clock"))
47+
.AddItem("Log Viewer", subtitle: "Real-time log display", content: MakeInfoPanel("Log Viewer"))
48+
.AddItem("Notifications", subtitle: "Notification system demo", content: MakeInfoPanel("Notifications"))
49+
.AddItem("System Info", subtitle: "OS & runtime details", content: MakeInfoPanel("System Info"))
50+
.AddItem("Terminal", subtitle: "PTY-backed terminal emulator", content: MakeInfoPanel("Terminal"))
51+
.AddItem("Welcome Banner", subtitle: "FIGlet ASCII art banner", content: MakeInfoPanel("Welcome Banner")))
52+
.OnSelectedItemChanged((sender, args) =>
4853
{
49-
var info = GetDemoInfo(args.Node.Text);
50-
if (info != null)
51-
{
52-
detailMarkup.SetContent(info);
53-
launchButton.Visible = true;
54-
}
55-
else
56-
{
57-
launchButton.Visible = false;
58-
}
59-
}
60-
};
61-
62-
// Launch demo on button click
63-
launchButton.Click += (sender, btn) => LaunchSelectedDemo(ws, demoTree);
54+
// No additional action needed — content factories handle the detail pane
55+
})
56+
.WithAlignment(HorizontalAlignment.Stretch)
57+
.Fill()
58+
.Build();
6459

65-
// Launch demo on double-click / Enter
66-
demoTree.NodeActivated += (sender, args) =>
60+
// Launch demo on Enter/Space
61+
nav.ItemInvoked += (sender, args) =>
6762
{
68-
if (args.Node != null)
69-
LaunchSelectedDemo(ws, demoTree);
63+
if (args.NewItem != null)
64+
LaunchDemo(ws, args.NewItem.Text);
7065
};
7166

72-
var grid = Controls.HorizontalGrid()
73-
.Column(col => col.Width(30).Add(demoTree))
74-
.Column(col => col.Add(detailPane))
75-
.WithSplitterAfter(0)
76-
.WithAlignment(HorizontalAlignment.Stretch)
77-
.WithVerticalAlignment(VerticalAlignment.Fill)
78-
.Build();
67+
var gradient = ColorGradient.FromColors(
68+
new Color(15, 25, 60),
69+
new Color(5, 5, 15));
7970

8071
return new WindowBuilder(ws)
8172
.WithTitle("SharpConsoleUI Demo")
8273
.WithSize(90, 30)
8374
.AtPosition(0, 0)
84-
.AddControl(grid)
75+
.WithBackgroundGradient(gradient, GradientDirection.Vertical)
76+
.AddControl(nav)
8577
.BuildAndShow();
8678
}
8779

88-
private static void BuildDemoTree(TreeControl tree)
80+
private static Action<ScrollablePanelControl> MakeInfoPanel(string demoName)
8981
{
90-
var layout = tree.AddRootNode("Layout & Windows");
91-
layout.TextColor = Color.Cyan1;
92-
layout.IsExpanded = true;
93-
layout.AddChild("Border Styles");
94-
layout.AddChild("IDE Layout");
95-
layout.AddChild("File Explorer");
96-
layout.AddChild("Multi-Tab Demo");
97-
layout.AddChild("WinUI Layout");
98-
99-
var controls = tree.AddRootNode("Controls");
100-
controls.TextColor = Color.Green;
101-
controls.IsExpanded = true;
102-
controls.AddChild("Interactive Demo");
103-
controls.AddChild("Dropdown");
104-
controls.AddChild("List View");
105-
controls.AddChild("Table");
106-
controls.AddChild("DataGrid");
107-
controls.AddChild("Nerd Fonts");
108-
controls.AddChild("Markup Syntax");
109-
controls.AddChild("International & Emoji");
110-
controls.AddChild("Data Binding");
82+
return panel =>
83+
{
84+
var info = GetDemoInfo(demoName);
85+
if (info != null)
86+
{
87+
panel.AddControl(Controls.Markup()
88+
.AddLines(info.ToArray())
89+
.WithMargin(1, 1, 1, 1)
90+
.Build());
91+
}
11192

112-
var dataViz = tree.AddRootNode("Data Visualization");
113-
dataViz.TextColor = Color.Yellow;
114-
dataViz.IsExpanded = true;
115-
dataViz.AddChild("Graphs & Charts");
93+
var launchButton = Controls.Button()
94+
.WithText(" Launch Demo ")
95+
.WithMargin(1, 1, 0, 0)
96+
.WithBorder(ButtonBorderStyle.Rounded)
97+
.Build();
11698

117-
var rendering = tree.AddRootNode("Rendering");
118-
rendering.TextColor = Color.Orange1;
119-
rendering.IsExpanded = true;
120-
rendering.AddChild("Gradients");
121-
rendering.AddChild("Animations");
122-
rendering.AddChild("Image Rendering");
123-
rendering.AddChild("Image Viewer");
99+
launchButton.Click += (_, _) =>
100+
{
101+
var ws = panel.Container?.GetConsoleWindowSystem;
102+
if (ws != null)
103+
LaunchDemo(ws, demoName);
104+
};
124105

125-
var utilities = tree.AddRootNode("Utilities");
126-
utilities.TextColor = Color.Magenta1;
127-
utilities.IsExpanded = true;
128-
utilities.AddChild("Built-in Dialogs");
129-
utilities.AddChild("Digital Clock");
130-
utilities.AddChild("Log Viewer");
131-
utilities.AddChild("Notifications");
132-
utilities.AddChild("System Info");
133-
utilities.AddChild("Terminal");
134-
utilities.AddChild("Welcome Banner");
106+
panel.AddControl(launchButton);
107+
};
135108
}
136109

137-
private static void LaunchSelectedDemo(ConsoleWindowSystem ws, TreeControl tree)
110+
private static void LaunchDemo(ConsoleWindowSystem ws, string demoName)
138111
{
139-
var node = tree.SelectedNode;
140-
if (node == null || node.Children.Count > 0) return;
141-
142-
_ = node.Text switch
112+
_ = demoName switch
143113
{
144114
"Border Styles" => BorderStyleWindow.Create(ws),
145115
"IDE Layout" => IdeLayoutWindow.Create(ws),

0 commit comments

Comments
 (0)