Skip to content

Commit e92fa69

Browse files
committed
feat: add AlphaBlendingDemoWindow to DemoApp
Five-zone alpha compositing showcase over an animated gradient background: - Zone 1: alpha ladder (8 orange panels, α=0→255) - Zone 2: fade-to-transparent strip via ColorGradient.Interpolate() - Zone 3: glass panels (DodgerBlue at 25/50/75/100% opacity) - Zone 4: live Color.Blend() compositor driven by a 0–255 slider - Zone 5: sin-wave-pulsing panel (~2s period) via async thread Async thread at 50ms drives pulse animation and background gradient direction cycling (checkbox-controlled). Registered in LauncherWindow under the Rendering header.
1 parent 535dced commit e92fa69

2 files changed

Lines changed: 281 additions & 1 deletion

File tree

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
using SharpConsoleUI;
2+
using SharpConsoleUI.Builders;
3+
using SharpConsoleUI.Controls;
4+
using SharpConsoleUI.Helpers;
5+
using SharpConsoleUI.Layout;
6+
using SharpConsoleUI.Rendering;
7+
8+
namespace DemoApp.DemoWindows;
9+
10+
internal static class AlphaBlendingDemoWindow
11+
{
12+
private const int WindowWidth = 110;
13+
private const int WindowHeight = 38;
14+
15+
public static Window Create(ConsoleWindowSystem ws)
16+
{
17+
var animToggle = Controls.Checkbox("Animate background gradient")
18+
.WithName("animToggle")
19+
.Checked(true)
20+
.Build();
21+
22+
var topBar = Controls.HorizontalGrid()
23+
.WithAlignment(HorizontalAlignment.Stretch)
24+
.Column(col => col
25+
.Flex(3)
26+
.Add(Controls.Markup()
27+
.AddLine("[bold]Alpha Blending Showcase[/] [dim]— five zones, one compositor[/]")
28+
.Build()))
29+
.Column(col => col
30+
.Flex(1)
31+
.Add(animToggle))
32+
.Build();
33+
34+
var zone1Heading = Controls.Markup()
35+
.AddLine("[bold]1. Alpha Ladder[/] [dim]same color, eight opacity levels[/]")
36+
.Build();
37+
38+
byte[] alphaLevels = { 0, 36, 73, 109, 146, 182, 219, 255 };
39+
40+
var zone1GridBuilder = Controls.HorizontalGrid()
41+
.WithAlignment(HorizontalAlignment.Stretch);
42+
43+
foreach (var alpha in alphaLevels)
44+
{
45+
var capturedAlpha = alpha;
46+
var panel = Controls.ScrollablePanel()
47+
.WithBackgroundColor(new Color(255, 140, 0, capturedAlpha))
48+
.WithBorderStyle(BorderStyle.None)
49+
.Build();
50+
panel.AddControl(Controls.Markup()
51+
.AddLine($"α={capturedAlpha}")
52+
.Build());
53+
zone1GridBuilder = zone1GridBuilder.Column(col => col.Flex(1).Add(panel));
54+
}
55+
56+
var zone1Grid = zone1GridBuilder.Build();
57+
58+
var zone2Heading = Controls.Markup()
59+
.AddLine("[bold]2. Fade to Transparent[/] [dim]ColorGradient interpolates alpha, not just RGB[/]")
60+
.Build();
61+
62+
// Build the fade strip: 60 '█' chars sampled from opaque→transparent cyan gradient
63+
var fadeGradient = ColorGradient.FromColors(
64+
new Color(0, 220, 220, 255), // opaque cyan
65+
new Color(0, 220, 220, 0)); // transparent cyan
66+
67+
var sb = new System.Text.StringBuilder();
68+
for (int i = 0; i < 60; i++)
69+
{
70+
var c = fadeGradient.Interpolate((double)i / 59);
71+
sb.Append($"[#{c.R:X2}{c.G:X2}{c.B:X2}{c.A:X2}]█[/]");
72+
}
73+
74+
var zone2Strip = Controls.Markup()
75+
.AddLine(sb.ToString())
76+
.Build();
77+
78+
var zone3Heading = Controls.Markup()
79+
.AddLine("[bold]3. Glass Panels[/] [dim]Color.WithAlpha() at 25 / 50 / 75 / 100 %[/]")
80+
.Build();
81+
82+
var zone3Grid = Controls.HorizontalGrid()
83+
.WithAlignment(HorizontalAlignment.Stretch)
84+
.WithVerticalAlignment(VerticalAlignment.Fill)
85+
.Column(col => col.Flex(1).Add(MakeGlassPanel(64, "25%")))
86+
.Column(col => col.Flex(1).Add(MakeGlassPanel(128, "50%")))
87+
.Column(col => col.Flex(1).Add(MakeGlassPanel(192, "75%")))
88+
.Column(col => col.Flex(1).Add(MakeGlassPanel(255, "100%")))
89+
.Build();
90+
91+
var zone4Heading = Controls.Markup()
92+
.AddLine("[bold]4. Live Compositor[/] [dim]Color.Blend(src, dst) — drag to change source alpha[/]")
93+
.Build();
94+
95+
var alphaSlider = Controls.Slider()
96+
.Horizontal()
97+
.WithRange(0, 255)
98+
.WithValue(128)
99+
.WithName("alphaSlider")
100+
.WithStep(1)
101+
.Build();
102+
103+
var alphaLabel = Controls.Markup()
104+
.WithName("alphaLabel")
105+
.Build();
106+
107+
var blendPreview = Controls.Markup()
108+
.WithName("blendPreview")
109+
.Build();
110+
111+
// Helper to compute and format the three-row blend preview.
112+
// Each string is one line — MarkupControl renders each list element as a separate row.
113+
static List<string> MakeBlendPreview(int alpha)
114+
{
115+
var src = new Color(255, 100, 50, (byte)alpha);
116+
var dst = new Color(30, 144, 255, 255);
117+
var blended = Color.Blend(src, dst);
118+
119+
string Swatch(Color c) => $"[#{c.R:X2}{c.G:X2}{c.B:X2}{c.A:X2}]████[/]";
120+
121+
return new List<string>
122+
{
123+
$"src {Swatch(src)} rgba(255,100,50,{alpha})",
124+
$"dst {Swatch(dst)} rgba(30,144,255,255)",
125+
$"out {Swatch(blended)} Color.Blend(src, dst)",
126+
};
127+
}
128+
129+
// Initial state
130+
alphaLabel.SetContent(new List<string> { "Alpha: 128 / 255" });
131+
blendPreview.SetContent(MakeBlendPreview(128));
132+
133+
// Wire value-changed after controls are built
134+
alphaSlider.ValueChanged += (_, e) =>
135+
{
136+
int a = (int)alphaSlider.Value;
137+
alphaLabel.SetContent(new List<string> { $"Alpha: {a} / 255" });
138+
blendPreview.SetContent(MakeBlendPreview(a));
139+
};
140+
141+
var zone4Controls = Controls.HorizontalGrid()
142+
.WithAlignment(HorizontalAlignment.Stretch)
143+
.Column(col => col
144+
.Flex(1)
145+
.Add(alphaSlider)
146+
.Add(alphaLabel))
147+
.Column(col => col
148+
.Flex(2)
149+
.Add(blendPreview))
150+
.Build();
151+
152+
var zone5Heading = Controls.Markup()
153+
.AddLine("[bold]5. Pulse Panel[/] [dim]alpha animated 0 → 255 → 0 via async thread[/]")
154+
.Build();
155+
156+
var pulsePanel = Controls.ScrollablePanel()
157+
.WithName("pulsePanel")
158+
.WithBorderStyle(BorderStyle.Rounded)
159+
.WithVerticalAlignment(VerticalAlignment.Fill)
160+
.Build();
161+
pulsePanel.AddControl(Controls.Markup().AddLine("background alpha pulses").Build());
162+
163+
// Left column: zone 3
164+
var leftCol = Controls.ScrollablePanel()
165+
.WithVerticalAlignment(VerticalAlignment.Fill)
166+
.Build();
167+
leftCol.AddControl(zone3Heading);
168+
leftCol.AddControl(zone3Grid);
169+
170+
// Right column: zones 4 and 5
171+
var rightCol = Controls.ScrollablePanel()
172+
.WithVerticalAlignment(VerticalAlignment.Fill)
173+
.Build();
174+
rightCol.AddControl(zone4Heading);
175+
rightCol.AddControl(zone4Controls);
176+
rightCol.AddControl(zone5Heading);
177+
rightCol.AddControl(pulsePanel);
178+
179+
var zonesBottomRow = Controls.HorizontalGrid()
180+
.WithAlignment(HorizontalAlignment.Stretch)
181+
.WithVerticalAlignment(VerticalAlignment.Fill)
182+
.Column(col => col.Flex(1).Add(leftCol))
183+
.Column(col => col.Flex(1).Add(rightCol))
184+
.Build();
185+
186+
var window = new WindowBuilder(ws)
187+
.WithTitle("Alpha Blending")
188+
.WithSize(WindowWidth, WindowHeight)
189+
.Centered()
190+
.WithBackgroundGradient(
191+
ColorGradient.FromColors(Color.Blue, Color.MediumPurple, Color.Orange1),
192+
GradientDirection.DiagonalDown)
193+
.OnKeyPressed((s, e) =>
194+
{
195+
if (e.KeyInfo.Key == ConsoleKey.Escape)
196+
{
197+
ws.CloseWindow((Window)s!);
198+
e.Handled = true;
199+
}
200+
})
201+
.WithAsyncWindowThread(async (win, ct) =>
202+
{
203+
var directions = new[]
204+
{
205+
GradientDirection.Horizontal,
206+
GradientDirection.DiagonalDown,
207+
GradientDirection.Vertical,
208+
GradientDirection.DiagonalUp,
209+
};
210+
int dirIndex = 0;
211+
var lastDirChange = DateTime.Now;
212+
var startTime = DateTime.Now;
213+
214+
while (!ct.IsCancellationRequested)
215+
{
216+
await Task.Delay(50, ct);
217+
218+
// Pulse panel
219+
var pulse = win.FindControl<ScrollablePanelControl>("pulsePanel");
220+
if (pulse != null)
221+
{
222+
double t = (DateTime.Now - startTime).TotalSeconds;
223+
byte a = (byte)((Math.Sin(t * Math.PI) + 1.0) / 2.0 * 255);
224+
pulse.BackgroundColor = new Color(255, 50, 100, a);
225+
}
226+
227+
// Background animation
228+
var toggle = win.FindControl<CheckboxControl>("animToggle");
229+
if (toggle?.Checked == true &&
230+
(DateTime.Now - lastDirChange).TotalSeconds >= 1.5)
231+
{
232+
dirIndex = (dirIndex + 1) % directions.Length;
233+
win.BackgroundGradient = new GradientBackground(
234+
ColorGradient.FromColors(Color.Blue, Color.MediumPurple, Color.Orange1),
235+
directions[dirIndex]);
236+
lastDirChange = DateTime.Now;
237+
}
238+
}
239+
})
240+
.AddControl(topBar)
241+
.AddControl(zone1Heading)
242+
.AddControl(zone1Grid)
243+
.AddControl(zone2Heading)
244+
.AddControl(zone2Strip)
245+
.AddControl(zonesBottomRow)
246+
.BuildAndShow();
247+
248+
return window;
249+
}
250+
251+
private static ScrollablePanelControl MakeGlassPanel(byte alpha, string label)
252+
{
253+
var panel = Controls.ScrollablePanel()
254+
.WithBackgroundColor(new Color(30, 144, 255, alpha))
255+
.WithBorderStyle(BorderStyle.Rounded)
256+
.WithVerticalAlignment(VerticalAlignment.Fill)
257+
.Build();
258+
panel.AddControl(Controls.Markup().AddLine(label).Build());
259+
return panel;
260+
}
261+
}

Examples/DemoApp/DemoWindows/LauncherWindow.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ public static Window Create(ConsoleWindowSystem ws)
5555
.AddItem("Gradients", subtitle: "Gradient text & backgrounds", content: MakeInfoPanel("Gradients"))
5656
.AddItem("Animations", subtitle: "Window animations & easing", content: MakeInfoPanel("Animations"))
5757
.AddItem("Image Rendering", subtitle: "Pixel art with half-blocks", content: MakeInfoPanel("Image Rendering"))
58-
.AddItem("Image Viewer", subtitle: "Load & display image files", content: MakeInfoPanel("Image Viewer")))
58+
.AddItem("Image Viewer", subtitle: "Load & display image files", content: MakeInfoPanel("Image Viewer"))
59+
.AddItem("Alpha Blending", subtitle: "Compositing, glass panels, live blend preview",
60+
content: MakeInfoPanel("Alpha Blending")))
5961
.AddHeader("Utilities", Color.Magenta1, header => header
6062
.AddItem("Built-in Dialogs", subtitle: "File pickers & system dialogs", content: MakeInfoPanel("Built-in Dialogs"))
6163
.AddItem("Digital Clock", subtitle: "FIGlet-rendered clock", content: MakeInfoPanel("Digital Clock"))
@@ -170,6 +172,7 @@ private static void LaunchDemo(ConsoleWindowSystem ws, string demoName)
170172
"Terminal" => TerminalWindow.Create(ws),
171173
"Toolbar" => ToolbarDemoWindow.Create(ws),
172174
"Welcome Banner" => WelcomeWindow.Create(ws),
175+
"Alpha Blending" => AlphaBlendingDemoWindow.Create(ws),
173176
_ => (Window?)null
174177
};
175178
}
@@ -652,6 +655,22 @@ private static void LaunchDemo(ConsoleWindowSystem ws, string demoName)
652655
"[dim]Controls used:[/]",
653656
" - FigleControl, MarkupControl",
654657
},
658+
"Alpha Blending" => new List<string>
659+
{
660+
"[bold cyan]Alpha Blending[/]",
661+
"",
662+
"Per-cell alpha compositing across five interactive zones.",
663+
"Drag a slider to preview Color.Blend() live, watch a panel",
664+
"pulse via sin-wave alpha, and toggle animated gradient direction.",
665+
"",
666+
"[dim]Features:[/]",
667+
" - Alpha ladder (8 opacity levels)",
668+
" - Fade-to-transparent gradient strip",
669+
" - Glass panels at 25/50/75/100% alpha",
670+
" - Live Color.Blend() compositor with slider",
671+
" - Animated pulse panel (sin-wave alpha)",
672+
" - Animated background gradient (checkbox-controlled)",
673+
},
655674
_ => null
656675
};
657676
}

0 commit comments

Comments
 (0)