增加更多UILib控件

This commit is contained in:
2026-03-25 21:41:59 +08:00
parent d009660918
commit b964430567
7 changed files with 497 additions and 4 deletions

View File

@@ -1 +1 @@
2026-03-25 20:24:01
2026-03-25 21:37:54

View File

@@ -1 +1 @@
a8ac938
d009660

View File

@@ -145,6 +145,47 @@ namespace CMLeonOS.Gui.Apps
AddDemo(info);
}
private void ShowTabsDemo()
{
ClearPreview();
SetHeader("Tabs", "Tab strip control for switching between sections.");
Tabs tabs = new Tabs(previewHost, 20, 24, 360, 28);
tabs.Items.Add("General");
tabs.Items.Add("Appearance");
tabs.Items.Add("Advanced");
tabs.RefreshItems();
tabs.SelectionChanged = (index, text) => SetHeader("Tabs", "Selected tab: " + text);
AddDemo(tabs);
TextBlock block = new TextBlock(previewHost, 20, 70, 360, 48);
block.Text = "Tabs are ideal for settings and editors.";
block.Background = UITheme.AccentLight;
block.Foreground = UITheme.TextPrimary;
block.HorizontalAlignment = Alignment.Middle;
block.VerticalAlignment = Alignment.Middle;
AddDemo(block);
}
private void ShowNumericDemo()
{
ClearPreview();
SetHeader("NumericUpDown", "Increment and decrement integer values without typing.");
NumericUpDown numeric = new NumericUpDown(previewHost, 20, 24, 120, 32);
numeric.Minimum = 0;
numeric.Maximum = 256;
numeric.Step = 8;
numeric.Value = 64;
numeric.Changed = (value) => SetHeader("NumericUpDown", "Value changed to " + value.ToString());
AddDemo(numeric);
TextBlock hint = new TextBlock(previewHost, 160, 28, 320, 24);
hint.Text = "Click + / - to change the value.";
hint.Foreground = UITheme.TextSecondary;
AddDemo(hint);
}
private void ShowTableDemo()
{
ClearPreview();
@@ -193,6 +234,11 @@ namespace CMLeonOS.Gui.Apps
image.Image = AppManager.DefaultAppIcon.Resize(48, 48);
image.Alpha = true;
AddDemo(image);
StatusBar statusBar = new StatusBar(previewHost, 20, 132, 360, 24);
statusBar.Text = "Ready";
statusBar.DetailText = "UILib Demo";
AddDemo(statusBar);
}
private void ShowDialogsDemo()
@@ -231,6 +277,15 @@ namespace CMLeonOS.Gui.Apps
fileBrowser.Show();
};
AddDemo(fileBrowserButton);
Button notificationButton = new Button(previewHost, 20, 68, 140, 28);
notificationButton.Text = "Notification";
notificationButton.OnClick = (_, _) =>
{
Notification notification = new Notification(this, "UILib Gallery", "This is a toast notification.");
notification.Show();
};
AddDemo(notificationButton);
}
private void CategorySelected(int index)
@@ -247,12 +302,18 @@ namespace CMLeonOS.Gui.Apps
ShowDropdownDemo();
break;
case 3:
ShowTableDemo();
ShowTabsDemo();
break;
case 4:
ShowBarsDemo();
ShowNumericDemo();
break;
case 5:
ShowTableDemo();
break;
case 6:
ShowBarsDemo();
break;
case 7:
ShowDialogsDemo();
break;
}
@@ -300,6 +361,8 @@ namespace CMLeonOS.Gui.Apps
categoryTable.Cells.Add(new TableCell("Buttons"));
categoryTable.Cells.Add(new TableCell("Inputs"));
categoryTable.Cells.Add(new TableCell("Dropdown"));
categoryTable.Cells.Add(new TableCell("Tabs"));
categoryTable.Cells.Add(new TableCell("Numeric"));
categoryTable.Cells.Add(new TableCell("Table"));
categoryTable.Cells.Add(new TableCell("Bars && Blocks"));
categoryTable.Cells.Add(new TableCell("Dialogs"));

131
Gui/UILib/Notification.cs Normal file
View File

@@ -0,0 +1,131 @@
using CMLeonOS;
using CMLeonOS.UILib.Animations;
using System.Drawing;
namespace CMLeonOS.Gui.UILib
{
internal class Notification
{
internal Notification(Process process, string title, string message)
{
owner = process;
Title = title;
Message = message;
}
private readonly Process owner;
internal string Title { get; }
internal string Message { get; }
internal void Show()
{
ProcessManager.AddProcess(owner, new NotificationProcess(owner, Title, Message)).Start();
}
private class NotificationProcess : Process
{
internal NotificationProcess(Process parent, string title, string message) : base("Notification", ProcessType.Background, parent)
{
this.title = title;
this.message = message;
}
private readonly string title;
private readonly string message;
private Window window;
private Button closeButton;
private readonly WindowManager wm = ProcessManager.GetProcess<WindowManager>();
private bool closeAnimationRunning = false;
private int targetX;
private int targetY;
public override void Start()
{
base.Start();
int width = 280;
int height = 84;
targetX = (int)wm.ScreenWidth - width - 16;
targetY = (int)wm.ScreenHeight - height - 44;
int startX = (int)wm.ScreenWidth + 12;
window = new Window(this, startX, targetY, width, height);
window.Clear(UITheme.WindowTitleBackground);
window.DrawFilledRectangle(0, 0, window.Width, window.Height, UITheme.WindowTitleBackground);
window.DrawRectangle(0, 0, window.Width, window.Height, UITheme.SurfaceBorder);
window.DrawFilledRectangle(0, 0, window.Width, 22, UITheme.Accent);
window.DrawString(title, UITheme.WindowTitleText, 10, 4);
window.DrawString(message, Color.White, 10, 30);
wm.AddWindow(window);
closeButton = new Button(window, window.Width - 54, window.Height - 28, 44, 20);
closeButton.Text = "Close";
closeButton.OnClick = (_, _) => BeginCloseAnimation();
wm.AddWindow(closeButton);
wm.Update(window);
StartOpenAnimation(startX, targetY, width, height);
}
private void StartOpenAnimation(int startX, int startY, int width, int height)
{
MovementAnimation animation = new MovementAnimation(window)
{
From = new Rectangle(startX, startY, width, height),
To = new Rectangle(targetX, targetY, width, height),
Duration = 10,
EasingType = EasingType.Sine,
EasingDirection = EasingDirection.Out
};
animation.Completed = () =>
{
wm.Update(window);
closeButton.Render();
};
animation.Start();
}
private void BeginCloseAnimation()
{
if (closeAnimationRunning || window == null)
{
return;
}
closeAnimationRunning = true;
int endX = (int)wm.ScreenWidth + 12;
int width = window.Width;
int height = window.Height;
MovementAnimation animation = new MovementAnimation(window)
{
From = new Rectangle(window.X, window.Y, width, height),
To = new Rectangle(endX, targetY, width, height),
Duration = 9,
EasingType = EasingType.Sine,
EasingDirection = EasingDirection.In
};
animation.Completed = TryStop;
animation.Start();
}
public override void Run()
{
}
public override void Stop()
{
base.Stop();
if (closeButton != null)
{
wm.RemoveWindow(closeButton);
}
if (window != null)
{
wm.RemoveWindow(window);
}
}
}
}
}

110
Gui/UILib/NumericUpDown.cs Normal file
View File

@@ -0,0 +1,110 @@
using System;
using System.Drawing;
namespace CMLeonOS.Gui.UILib
{
internal class NumericUpDown : Control
{
public NumericUpDown(Window parent, int x, int y, int width, int height) : base(parent, x, y, width, height)
{
OnClick = NumericUpDownClicked;
}
internal Action<int> Changed;
private Color _background = UITheme.Surface;
internal Color Background
{
get { return _background; }
set { _background = value; Render(); }
}
private Color _foreground = UITheme.TextPrimary;
internal Color Foreground
{
get { return _foreground; }
set { _foreground = value; Render(); }
}
private Color _border = UITheme.SurfaceBorder;
internal Color Border
{
get { return _border; }
set { _border = value; Render(); }
}
private int _minimum = 0;
internal int Minimum
{
get { return _minimum; }
set { _minimum = value; if (_value < _minimum) { _value = _minimum; } Render(); }
}
private int _maximum = 100;
internal int Maximum
{
get { return _maximum; }
set { _maximum = value; if (_value > _maximum) { _value = _maximum; } Render(); }
}
private int _step = 1;
internal int Step
{
get { return _step; }
set { _step = Math.Max(1, value); Render(); }
}
private int _value = 0;
internal int Value
{
get { return _value; }
set
{
int newValue = Math.Clamp(value, _minimum, _maximum);
if (_value != newValue)
{
_value = newValue;
Render();
Changed?.Invoke(_value);
}
}
}
private void NumericUpDownClicked(int x, int y)
{
int buttonWidth = 22;
if (x >= Width - buttonWidth)
{
if (y < Height / 2)
{
Value += _step;
}
else
{
Value -= _step;
}
}
}
internal override void Render()
{
int buttonWidth = 22;
Clear(Background);
DrawFilledRectangle(0, 0, Width, Height, Background);
DrawRectangle(0, 0, Width, Height, Border);
DrawFilledRectangle(Width - buttonWidth, 0, buttonWidth, Height, UITheme.SurfaceMuted);
DrawRectangle(Width - buttonWidth, 0, buttonWidth, Height, Border);
DrawHorizontalLine(buttonWidth, Width - buttonWidth, Height / 2, Border);
string text = _value.ToString();
DrawString(text, Foreground, 6, (Height / 2) - 8);
DrawString("+", Foreground, Width - buttonWidth + 7, 2);
DrawString("-", Foreground, Width - buttonWidth + 7, (Height / 2) - 2);
WM.Update(this);
}
}
}

62
Gui/UILib/StatusBar.cs Normal file
View File

@@ -0,0 +1,62 @@
using System.Drawing;
namespace CMLeonOS.Gui.UILib
{
internal class StatusBar : Control
{
public StatusBar(Window parent, int x, int y, int width, int height) : base(parent, x, y, width, height)
{
}
private string _text = "Ready";
internal string Text
{
get { return _text; }
set { _text = value ?? string.Empty; Render(); }
}
private string _detailText = string.Empty;
internal string DetailText
{
get { return _detailText; }
set { _detailText = value ?? string.Empty; Render(); }
}
private Color _background = UITheme.SurfaceMuted;
internal Color Background
{
get { return _background; }
set { _background = value; Render(); }
}
private Color _foreground = UITheme.TextPrimary;
internal Color Foreground
{
get { return _foreground; }
set { _foreground = value; Render(); }
}
private Color _border = UITheme.SurfaceBorder;
internal Color Border
{
get { return _border; }
set { _border = value; Render(); }
}
internal override void Render()
{
Clear(Background);
DrawFilledRectangle(0, 0, Width, Height, Background);
DrawHorizontalLine(Width, 0, 0, Border);
DrawString(Text, Foreground, 6, (Height / 2) - 8);
if (!string.IsNullOrWhiteSpace(DetailText))
{
DrawString(DetailText, Foreground, Width - (DetailText.Length * 8) - 6, (Height / 2) - 8);
}
WM.Update(this);
}
}
}

127
Gui/UILib/Tabs.cs Normal file
View File

@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Drawing;
namespace CMLeonOS.Gui.UILib
{
internal class Tabs : Control
{
public Tabs(Window parent, int x, int y, int width, int height) : base(parent, x, y, width, height)
{
OnClick = TabsClicked;
}
internal Action<int, string> SelectionChanged;
internal List<string> Items { get; } = new List<string>();
private Color _background = UITheme.Surface;
internal Color Background
{
get { return _background; }
set { _background = value; Render(); }
}
private Color _foreground = UITheme.TextPrimary;
internal Color Foreground
{
get { return _foreground; }
set { _foreground = value; Render(); }
}
private Color _border = UITheme.SurfaceBorder;
internal Color Border
{
get { return _border; }
set { _border = value; Render(); }
}
private int _selectedIndex = 0;
internal int SelectedIndex
{
get { return _selectedIndex; }
set
{
int newIndex = Math.Clamp(value, 0, Math.Max(0, Items.Count - 1));
if (_selectedIndex != newIndex)
{
_selectedIndex = newIndex;
Render();
if (Items.Count > 0)
{
SelectionChanged?.Invoke(_selectedIndex, Items[_selectedIndex]);
}
}
}
}
internal string SelectedText
{
get
{
if (Items.Count == 0 || _selectedIndex < 0 || _selectedIndex >= Items.Count)
{
return string.Empty;
}
return Items[_selectedIndex];
}
}
internal void RefreshItems()
{
if (_selectedIndex >= Items.Count)
{
_selectedIndex = Math.Max(0, Items.Count - 1);
}
Render();
}
private void TabsClicked(int x, int y)
{
if (Items.Count == 0)
{
return;
}
int tabWidth = Math.Max(1, Width / Items.Count);
int index = Math.Min(Items.Count - 1, Math.Max(0, x / tabWidth));
SelectedIndex = index;
}
internal override void Render()
{
Clear(Background);
DrawFilledRectangle(0, 0, Width, Height, Background);
DrawRectangle(0, 0, Width, Height, Border);
if (Items.Count == 0)
{
WM.Update(this);
return;
}
int tabWidth = Math.Max(1, Width / Items.Count);
for (int i = 0; i < Items.Count; i++)
{
int tabX = i * tabWidth;
int actualWidth = i == Items.Count - 1 ? Width - tabX : tabWidth;
bool selected = i == _selectedIndex;
DrawFilledRectangle(tabX, 0, actualWidth, Height, selected ? UITheme.AccentLight : UITheme.SurfaceMuted);
DrawRectangle(tabX, 0, actualWidth, Height, selected ? UITheme.Accent : Border);
string text = Items[i];
int maxChars = Math.Max(1, (actualWidth - 8) / 8);
if (text.Length > maxChars)
{
text = text.Substring(0, Math.Max(1, maxChars - 1)) + "~";
}
DrawString(text, Foreground, tabX + (actualWidth / 2) - (text.Length * 4), (Height / 2) - 8);
}
WM.Update(this);
}
}
}