From 9197dfbaa3c73f9a5117a20ae9a4981c764dc495 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Mon, 23 Mar 2026 21:34:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0GUI=E7=99=BB=E5=BD=95UI?= =?UTF-8?q?=EF=BC=8C=E6=9B=B4=E6=96=B0UILib?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BuildTime.txt | 2 +- GitCommit.txt | 2 +- Gui/ShellComponents/Lock.cs | 164 ++++++++++++++++++++++++++++++------ Gui/UILib/AppWindow.cs | 28 +++++- Gui/UILib/Button.cs | 51 +++++++++-- Gui/UILib/CheckBox.cs | 9 +- Gui/UILib/MessageBox.cs | 11 ++- Gui/UILib/PromptBox.cs | 11 ++- Gui/UILib/RangeSlider.cs | 23 +++-- Gui/UILib/Switch.cs | 35 ++++---- Gui/UILib/Table.cs | 37 ++++---- Gui/UILib/TextBox.cs | 19 +++-- Gui/UILib/UITheme.cs | 42 +++++++++ 13 files changed, 332 insertions(+), 102 deletions(-) create mode 100644 Gui/UILib/UITheme.cs diff --git a/BuildTime.txt b/BuildTime.txt index 28ed575..020ecb9 100644 --- a/BuildTime.txt +++ b/BuildTime.txt @@ -1 +1 @@ -2026-03-22 19:16:27 \ No newline at end of file +2026-03-23 21:30:51 \ No newline at end of file diff --git a/GitCommit.txt b/GitCommit.txt index 769bece..62e8da4 100644 --- a/GitCommit.txt +++ b/GitCommit.txt @@ -1 +1 @@ -e43417c \ No newline at end of file +9162e2d \ No newline at end of file diff --git a/Gui/ShellComponents/Lock.cs b/Gui/ShellComponents/Lock.cs index 2bbba88..73d5b74 100644 --- a/Gui/ShellComponents/Lock.cs +++ b/Gui/ShellComponents/Lock.cs @@ -20,6 +20,7 @@ using CMLeonOS.Gui.UILib; using CMLeonOS.Logger; using System; +using System.Collections.Generic; using System.Drawing; namespace CMLeonOS.Gui.ShellComponents @@ -33,6 +34,7 @@ namespace CMLeonOS.Gui.ShellComponents Table userTable; TextBox passwordBox; + Button logOnButton; WindowManager wm = ProcessManager.GetProcess(); @@ -43,21 +45,122 @@ namespace CMLeonOS.Gui.ShellComponents [IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.Lock.Key.bmp")] private static byte[] _iconBytes_Key; internal static Bitmap Icon_Key = new Bitmap(_iconBytes_Key); + + [IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.Lock.User.bmp")] + private static byte[] _iconBytes_User; + internal static Bitmap Icon_User = new Bitmap(_iconBytes_User); + + [IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.Lock.UserArrow.bmp")] + private static byte[] _iconBytes_UserArrow; + internal static Bitmap Icon_UserArrow = new Bitmap(_iconBytes_UserArrow); } - private const int width = 352; - private const int height = 200; - private const int padding = 12; + private const int width = 700; + private const int height = 420; + private const int leftPanelWidth = 240; + private const int rightContentPadding = 28; + private const int rightContentBottomPadding = 30; + private const int accountsLabelY = 102; + private const int tableY = 124; + private const int tableHeight = 140; + private const int passwordLabelY = tableY + tableHeight + 18; + private const int passwordY = passwordLabelY + 22; + private const int passwordHeight = 30; + private const int logOnButtonY = passwordY + 44; + private const int logOnButtonHeight = 38; + + private readonly Color windowBackground = Color.FromArgb(232, 238, 246); + private readonly Color outerBorder = Color.FromArgb(169, 181, 198); + private readonly Color leftPanel = Color.FromArgb(34, 53, 84); + private readonly Color leftPanelAccent = Color.FromArgb(74, 124, 201); + private readonly Color leftPanelSoft = Color.FromArgb(53, 77, 116); + private readonly Color rightPanel = Color.FromArgb(247, 250, 253); + private readonly Color titleColor = Color.FromArgb(28, 39, 56); + private readonly Color bodyColor = Color.FromArgb(92, 103, 119); + private readonly Color hintColor = Color.FromArgb(132, 142, 156); + private readonly Color inputBackground = Color.White; + private readonly Color inputForeground = Color.FromArgb(31, 42, 55); + private readonly Color selectionBackground = Color.FromArgb(223, 236, 255); + private readonly Color selectionBorder = Color.FromArgb(91, 140, 223); + private readonly Color primaryButton = Color.FromArgb(53, 111, 214); + private readonly Color primaryButtonBorder = Color.FromArgb(33, 83, 171); + private readonly Color primaryButtonText = Color.White; private double shakiness = 0; + private List users = new List(); + private void RenderBackground() { - window.Clear(Color.LightGray); + window.Clear(windowBackground); + window.DrawRectangle(0, 0, width, height, outerBorder); - window.DrawImageAlpha(Images.Icon_Key, padding, padding); + int leftWidth = leftPanelWidth; + int rightX = leftWidth + 1; + int rightWidth = width - rightX; - window.DrawString("Select a user and enter password,\nthen press Enter to log on", Color.Black, (int)(padding + Images.Icon_Key.Width + padding), padding); + window.DrawFilledRectangle(0, 0, leftWidth, height, leftPanel); + window.DrawFilledRectangle(leftWidth - 6, 0, 6, height, leftPanelAccent); + window.DrawFilledRectangle(rightX, 0, rightWidth, height, rightPanel); + + window.DrawFilledRectangle(20, 22, 56, 56, leftPanelSoft); + window.DrawRectangle(20, 22, 56, 56, Color.FromArgb(109, 149, 214)); + window.DrawImageAlpha(Images.Icon_Key, 32, 34); + + window.DrawString("Welcome Back", Color.White, 20, 96); + window.DrawString("CMLeonOS Desktop", Color.FromArgb(190, 208, 233), 20, 120); + + string selectedUserName = GetSelectedUser()?.Username ?? "Guest"; + window.DrawString("Selected account", Color.FromArgb(171, 190, 217), 20, 168); + window.DrawString(selectedUserName, Color.White, 20, 190); + window.DrawString(GetSelectedUserSubtitle(), Color.FromArgb(176, 197, 224), 20, 214); + + window.DrawFilledRectangle(20, height - 72, leftWidth - 40, 44, leftPanelSoft); + window.DrawRectangle(20, height - 72, leftWidth - 40, 44, Color.FromArgb(88, 118, 171)); + window.DrawString("Tip: type password.", Color.White, 32, height - 62); + window.DrawString("Press Enter to sign in.", Color.FromArgb(190, 208, 233), 32, height - 44); + + int rightContentX = leftWidth + rightContentPadding; + window.DrawString("Sign in", titleColor, rightContentX, 28); + window.DrawString("Choose an account, then enter your password.", bodyColor, rightContentX, 54); + window.DrawString("Continue to the desktop when ready.", bodyColor, rightContentX, 72); + + window.DrawString("Accounts", hintColor, rightContentX, accountsLabelY); + window.DrawString("Password", hintColor, rightContentX, passwordLabelY); + window.DrawString("Secure local session", Color.FromArgb(118, 128, 141), rightContentX, height - rightContentBottomPadding); + + } + + private User GetSelectedUser() + { + if (userTable == null || users == null || users.Count == 0) + { + return null; + } + + if (userTable.SelectedCellIndex < 0 || userTable.SelectedCellIndex >= users.Count) + { + return null; + } + + return users[userTable.SelectedCellIndex]; + } + + private string GetSelectedUserSubtitle() + { + User selectedUser = GetSelectedUser(); + if (selectedUser == null) + { + return "No local users available"; + } + + return selectedUser.Admin ? "Administrator account" : "Standard local account"; + } + + private void RefreshLayout() + { + RenderBackground(); + wm.Update(window); } private void ShowError(string text) @@ -78,12 +181,11 @@ namespace CMLeonOS.Gui.ShellComponents return; } - if (userTable.SelectedCellIndex >= UserSystem.GetUsers().Count) + if (userTable.SelectedCellIndex >= users.Count) { return; } - var users = UserSystem.GetUsers(); User selectedUser = users[userTable.SelectedCellIndex]; string password = passwordBox.Text.Trim(); @@ -145,7 +247,7 @@ namespace CMLeonOS.Gui.ShellComponents { base.Start(); - var users = UserSystem.GetUsers(); + users = UserSystem.GetUsers() ?? new List(); Logger.Logger.Instance.Info("Lock", $"Lock started. Total users: {users?.Count ?? 0}"); if (users != null) { @@ -164,21 +266,21 @@ namespace CMLeonOS.Gui.ShellComponents RenderBackground(); - int contentHeight = 110; - int contentWidth = 160; - int startX = (width - contentWidth) / 2; - int startY = (height - contentHeight) / 2; + int contentX = leftPanelWidth + rightContentPadding; + int contentWidth = width - contentX - rightContentPadding; - userTable = new Table(window, startX, startY, contentWidth, 80); - userTable.CellHeight = 25; - userTable.Background = Color.White; - userTable.Foreground = Color.Black; - userTable.Border = Color.Gray; - userTable.SelectedBackground = Color.FromArgb(221, 246, 255); - userTable.SelectedForeground = Color.Black; - userTable.SelectedBorder = Color.FromArgb(126, 205, 234); + userTable = new Table(window, contentX, tableY, contentWidth, tableHeight); + userTable.CellHeight = 40; + userTable.Background = inputBackground; + userTable.Foreground = inputForeground; + userTable.Border = Color.FromArgb(203, 212, 224); + userTable.SelectedBackground = selectionBackground; + userTable.SelectedForeground = inputForeground; + userTable.SelectedBorder = selectionBorder; userTable.AllowSelection = true; userTable.AllowDeselection = false; + userTable.ScrollbarThickness = 18; + userTable.TableCellSelected = _ => RefreshLayout(); foreach (var user in users) { @@ -193,13 +295,27 @@ namespace CMLeonOS.Gui.ShellComponents wm.AddWindow(userTable); - passwordBox = new TextBox(window, startX, startY + 90, contentWidth, 20); + passwordBox = new TextBox(window, contentX, passwordY, contentWidth, passwordHeight); passwordBox.Shield = true; - passwordBox.PlaceholderText = "Password"; + passwordBox.PlaceholderText = "Enter password"; + passwordBox.Background = inputBackground; + passwordBox.Foreground = inputForeground; + passwordBox.PlaceholderForeground = hintColor; + passwordBox.Changed = RefreshLayout; passwordBox.Submitted = LogOn; wm.AddWindow(passwordBox); - wm.Update(window); + logOnButton = new Button(window, contentX, logOnButtonY, contentWidth, logOnButtonHeight); + logOnButton.Text = "Log On"; + logOnButton.Background = primaryButton; + logOnButton.Border = primaryButtonBorder; + logOnButton.Foreground = primaryButtonText; + logOnButton.Image = Images.Icon_UserArrow; + logOnButton.ImageLocation = Button.ButtonImageLocation.Left; + logOnButton.OnClick = LogOnClick; + wm.AddWindow(logOnButton); + + RefreshLayout(); } public override void Run() diff --git a/Gui/UILib/AppWindow.cs b/Gui/UILib/AppWindow.cs index c6936f7..a11320e 100644 --- a/Gui/UILib/AppWindow.cs +++ b/Gui/UILib/AppWindow.cs @@ -236,14 +236,36 @@ namespace CMLeonOS.Gui.UILib private void RenderDecoration() { - decorationWindow.Clear(Color.FromArgb(56, 56, 71)); + decorationWindow.Clear(UITheme.WindowTitleBackground); + decorationWindow.DrawHorizontalLine(Width, 0, 0, UITheme.WindowTitleTopHighlight); + decorationWindow.DrawHorizontalLine(Width, 0, titlebarHeight - 1, UITheme.WindowTitleBottomBorder); + + int buttonSpace = 0; + if (_canClose) + { + buttonSpace += titlebarHeight; + } + if (_canResize) + { + buttonSpace += titlebarHeight; + } + if (buttonSpace > 0) + { + decorationWindow.DrawFilledRectangle(Width - buttonSpace, 1, buttonSpace, titlebarHeight - 2, UITheme.WindowButtonBackground); + } if (_smallIcon != null) { - decorationWindow.DrawImageAlpha(_smallIcon, 2, 2); + decorationWindow.DrawImageAlpha(_smallIcon, 3, 2); } - decorationWindow.DrawString(Title, Color.White, (Width / 2) - ((FontData.Width * Title.Length) / 2), 4); + int maxTitleChars = Math.Max(4, (Width - buttonSpace - 42) / FontData.Width); + string displayTitle = Title; + if (displayTitle.Length > maxTitleChars) + { + displayTitle = displayTitle.Substring(0, Math.Max(1, maxTitleChars - 1)) + "~"; + } + decorationWindow.DrawString(displayTitle, UITheme.WindowTitleText, 28, 4); if (_canClose) { diff --git a/Gui/UILib/Button.cs b/Gui/UILib/Button.cs index ff670e3..3f6cdb3 100644 --- a/Gui/UILib/Button.cs +++ b/Gui/UILib/Button.cs @@ -15,6 +15,7 @@ // along with this program. If not, see . using Cosmos.System.Graphics; +using Cosmos.System; using System; using System.Drawing; @@ -24,6 +25,11 @@ namespace CMLeonOS.Gui.UILib { public Button(Window parent, int x, int y, int width, int height) : base(parent, x, y, width, height) { + OnDown = (_, _) => + { + held = true; + Render(); + }; } internal enum ButtonImageLocation @@ -60,7 +66,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _background = Color.FromArgb(48, 48, 48); + private Color _background = UITheme.Accent; internal Color Background { get @@ -88,7 +94,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _border = Color.Black; + private Color _border = UITheme.AccentDark; internal Color Border { get @@ -116,29 +122,58 @@ namespace CMLeonOS.Gui.UILib } } + private bool held = false; + internal override void Render() { - Clear(Background); + if (held && MouseManager.MouseState != MouseState.Left) + { + held = false; + } + + Color buttonBackground = held + ? Color.FromArgb(Math.Max(0, Background.R - 24), Math.Max(0, Background.G - 24), Math.Max(0, Background.B - 24)) + : Background; + + Clear(UITheme.Surface); + DrawFilledRectangle(0, 0, Width, Height, buttonBackground); + DrawHorizontalLine(Width - 2, 1, 1, Color.FromArgb( + Math.Min(255, buttonBackground.R + 20), + Math.Min(255, buttonBackground.G + 20), + Math.Min(255, buttonBackground.B + 20))); if (_image != null) { switch (_imageLocation) { case ButtonImageLocation.Left: - DrawImageAlpha(_image, (int)((Width / 2) - ((8 / 2) * Text.Length) - 8 - _image.Width), (int)((Height / 2) - (_image.Height / 2))); - DrawString(Text, Foreground, (Width / 2) - ((8 / 2) * Text.Length), (Height / 2) - (16 / 2)); + { + int imageWidth = (int)_image.Width; + int imageHeight = (int)_image.Height; + int textWidth = 8 * Text.Length; + int contentWidth = imageWidth + 6 + textWidth; + int imageX = Math.Max(4, (Width / 2) - (contentWidth / 2)); + int imageY = (Height / 2) - (imageHeight / 2); + int textXLeft = imageX + imageWidth + 6; + int textYLeft = (Height / 2) - (16 / 2); + + DrawImageAlpha(_image, imageX, imageY); + DrawString(Text, Foreground, textXLeft, textYLeft); break; + } case ButtonImageLocation.AboveText: - DrawImageAlpha(_image, (int)((Width / 2) - (_image.Width / 2)), (int)((Height / 2) - (_image.Height / 2))); - DrawString(Text, Foreground, (Width / 2) - (4 * Text.Length), Height - 16); + { + DrawImageAlpha(_image, (int)((Width / 2) - (_image.Width / 2)), Math.Max(1, (int)((Height / 2) - (_image.Height / 2) - 4))); + DrawString(Text, Foreground, (Width / 2) - (4 * Text.Length), Height - 17); break; + } default: throw new Exception("Unrecognised image location in button."); } } else { - DrawString(Text, Foreground, (Width / 2) - (4 * Text.Length), (Height / 2) - 8); + DrawString(Text, Foreground, (Width / 2) - (4 * Text.Length), (Height / 2) - 8 + (held ? 1 : 0)); } DrawRectangle(0, 0, Width, Height, Border); diff --git a/Gui/UILib/CheckBox.cs b/Gui/UILib/CheckBox.cs index 6f16661..cd3342c 100644 --- a/Gui/UILib/CheckBox.cs +++ b/Gui/UILib/CheckBox.cs @@ -74,7 +74,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _background = Color.White; + private Color _background = UITheme.Surface; internal Color Background { get @@ -88,7 +88,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _foreground = Color.Black; + private Color _foreground = UITheme.TextPrimary; internal Color Foreground { get @@ -117,7 +117,8 @@ namespace CMLeonOS.Gui.UILib int textX = iconSize + 8; int textY = (Height / 2) - (16 / 2); - DrawFilledRectangle(iconX, iconY, iconSize, iconSize, Color.LightGray); + DrawFilledRectangle(iconX, iconY, iconSize, iconSize, _checked ? UITheme.AccentLight : UITheme.SurfaceMuted); + DrawRectangle(iconX, iconY, iconSize, iconSize, _checked ? UITheme.Accent : UITheme.SurfaceBorder); if (_checked) { DrawImageAlpha(checkBitmap, iconX, iconY); @@ -128,4 +129,4 @@ namespace CMLeonOS.Gui.UILib WM.Update(this); } } -} \ No newline at end of file +} diff --git a/Gui/UILib/MessageBox.cs b/Gui/UILib/MessageBox.cs index b90cba8..7d0ff51 100644 --- a/Gui/UILib/MessageBox.cs +++ b/Gui/UILib/MessageBox.cs @@ -44,16 +44,19 @@ namespace CMLeonOS.Gui.UILib int width = Math.Max(192, (Padding * 2) + (8 * longestLineLength)); int height = 128 + ((Message.Split('\n').Length - 1) * 16); - AppWindow window = new AppWindow(Process, (int)((wm.ScreenWidth / 2) - (height / 2)), (int)((wm.ScreenWidth / 2) - (width / 2)), width, height); + AppWindow window = new AppWindow(Process, (int)((wm.ScreenWidth / 2) - (width / 2)), (int)((wm.ScreenHeight / 2) - (height / 2)), width, height); window.Title = Title; wm.AddWindow(window); - window.Clear(Color.LightGray); - window.DrawFilledRectangle(0, window.Height - (Padding * 2) - 20, window.Width, (Padding * 2) + 20, Color.Gray); - window.DrawString(Message, Color.Black, Padding, Padding); + window.Clear(UITheme.Surface); + window.DrawRectangle(0, 0, window.Width, window.Height, UITheme.SurfaceBorder); + window.DrawFilledRectangle(0, window.Height - (Padding * 2) - 20, window.Width, (Padding * 2) + 20, UITheme.SurfaceMuted); + window.DrawString(Message, UITheme.TextPrimary, Padding, Padding); Button ok = new Button(window, window.Width - 80 - Padding, window.Height - 20 - Padding, 80, 20); ok.Text = "OK"; + ok.Background = UITheme.Accent; + ok.Border = UITheme.AccentDark; ok.OnClick = (int x, int y) => { wm.RemoveWindow(window); diff --git a/Gui/UILib/PromptBox.cs b/Gui/UILib/PromptBox.cs index cd744a7..b71ed68 100644 --- a/Gui/UILib/PromptBox.cs +++ b/Gui/UILib/PromptBox.cs @@ -42,13 +42,14 @@ namespace CMLeonOS.Gui.UILib int width = Math.Max(256, (Padding * 2) + (8 * longestLineLength)); int height = 128 + ((Message.Split('\n').Length - 1) * 16); - AppWindow window = new AppWindow(Process, (int)((wm.ScreenWidth / 2) - (height / 2)), (int)((wm.ScreenWidth / 2) - (width / 2)), width, height); + AppWindow window = new AppWindow(Process, (int)((wm.ScreenWidth / 2) - (width / 2)), (int)((wm.ScreenHeight / 2) - (height / 2)), width, height); window.Title = Title; wm.AddWindow(window); - window.Clear(Color.LightGray); - window.DrawFilledRectangle(0, window.Height - (Padding * 2) - 20, window.Width, (Padding * 2) + 20, Color.Gray); - window.DrawString(Message, Color.Black, Padding, Padding); + window.Clear(UITheme.Surface); + window.DrawRectangle(0, 0, window.Width, window.Height, UITheme.SurfaceBorder); + window.DrawFilledRectangle(0, window.Height - (Padding * 2) - 20, window.Width, (Padding * 2) + 20, UITheme.SurfaceMuted); + window.DrawString(Message, UITheme.TextPrimary, Padding, Padding); TextBox textBox = new TextBox(window, Padding, Padding + FontData.Height + 8, 192, 20); textBox.PlaceholderText = Placeholder; @@ -56,6 +57,8 @@ namespace CMLeonOS.Gui.UILib Button ok = new Button(window, window.Width - 80 - Padding, window.Height - 20 - Padding, 80, 20); ok.Text = "OK"; + ok.Background = UITheme.Accent; + ok.Border = UITheme.AccentDark; ok.OnClick = (int x, int y) => { wm.RemoveWindow(window); diff --git a/Gui/UILib/RangeSlider.cs b/Gui/UILib/RangeSlider.cs index ad8c2ad..8d1bf39 100644 --- a/Gui/UILib/RangeSlider.cs +++ b/Gui/UILib/RangeSlider.cs @@ -41,7 +41,7 @@ namespace CMLeonOS.Gui.UILib Render(); } - private Color _background = Color.White; + private Color _background = UITheme.Surface; internal Color Background { get @@ -55,7 +55,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _foreground = Color.Gray; + private Color _foreground = UITheme.TextSecondary; internal Color Foreground { get @@ -143,9 +143,9 @@ namespace CMLeonOS.Gui.UILib private bool held = false; - private static int slotHeight = 3; - private static int sliderHeight = 15; - private static int sliderWidth = 5; + private static int slotHeight = 4; + private static int sliderHeight = 16; + private static int sliderWidth = 8; private void RangeSliderDown(int x, int y) { @@ -187,7 +187,9 @@ namespace CMLeonOS.Gui.UILib } // Slot - DrawFilledRectangle(0, slotY, Width, slotHeight, Color.FromArgb(168, 168, 168)); + DrawFilledRectangle(0, slotY, Width, slotHeight, UITheme.SurfaceBorder); + int fillWidth = (int)(_value.Map((float)_minimum, (float)_maximum, 0, Width - sliderWidth)) + (sliderWidth / 2); + DrawFilledRectangle(0, slotY, Math.Max(0, fillWidth), slotHeight, UITheme.AccentLight); // Slider DrawFilledRectangle( @@ -195,7 +197,14 @@ namespace CMLeonOS.Gui.UILib sliderY, sliderWidth, sliderHeight, - held ? Color.FromArgb(0, 71, 112) : Color.FromArgb(0, 115, 186) + held ? UITheme.AccentDark : UITheme.Accent + ); + DrawRectangle( + (int)(_value.Map((float)_minimum, (float)_maximum, 0, Width - sliderWidth)), + sliderY, + sliderWidth, + sliderHeight, + UITheme.AccentDark ); if (_rangeLabels) diff --git a/Gui/UILib/Switch.cs b/Gui/UILib/Switch.cs index f53c77c..7c52736 100644 --- a/Gui/UILib/Switch.cs +++ b/Gui/UILib/Switch.cs @@ -15,8 +15,8 @@ // along with this program. If not, see . using Cosmos.System; -using Cosmos.System.Graphics; using System; +using System.Drawing; namespace CMLeonOS.Gui.UILib { @@ -28,19 +28,10 @@ namespace CMLeonOS.Gui.UILib OnClick = null; } - [IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.SwitchOff.bmp")] - private static byte[] offBytes; - private static Bitmap offBitmap = new Bitmap(offBytes); - - [IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.SwitchOn.bmp")] - private static byte[] onBytes; - private static Bitmap onBitmap = new Bitmap(onBytes); - - [IL2CPU.API.Attribs.ManifestResourceStream(ResourceName = "CMLeonOS.Gui.Resources.SwitchKnob.bmp")] - private static byte[] knobBytes; - private static Bitmap knobBitmap = new Bitmap(knobBytes); - private const int maximumToggleDrag = 4; + private const int switchWidth = 34; + private const int switchHeight = 18; + private const int knobSize = 14; private int lastMouseX = 0; private int totalDragged = 0; @@ -67,7 +58,7 @@ namespace CMLeonOS.Gui.UILib // Interpret as a drag rather than a toggle, // setting the Checked state based on where // the switch knob is. - Checked = knobX >= (offBitmap.Width / 2) - (knobBitmap.Width / 2); + Checked = knobX >= (switchWidth - knobSize) / 2d; } } @@ -76,7 +67,7 @@ namespace CMLeonOS.Gui.UILib internal override void Render() { - knobGoal = (int)(Checked ? offBitmap.Width - knobBitmap.Width : 0); + knobGoal = Checked ? switchWidth - knobSize : 0; if (held && MouseManager.MouseState != MouseState.Left) { @@ -88,7 +79,7 @@ namespace CMLeonOS.Gui.UILib int diff = (int)(MouseManager.X - lastMouseX); lastMouseX = (int)MouseManager.X; totalDragged += Math.Abs(diff); - knobX = Math.Clamp(knobX + diff, 0, offBitmap.Width - knobBitmap.Width); + knobX = Math.Clamp(knobX + diff, 0, switchWidth - knobSize); WM.UpdateQueue.Enqueue(this); } @@ -118,13 +109,17 @@ namespace CMLeonOS.Gui.UILib Clear(Background); int switchX = 0; - int switchY = (Height / 2) - ((int)offBitmap.Height / 2); + int switchY = (Height / 2) - (switchHeight / 2); - int textX = (int)(offBitmap.Width + 8); + int textX = switchWidth + 8; int textY = (Height / 2) - (16 / 2); - DrawImageAlpha(Checked ? onBitmap : offBitmap, switchX, switchY); - DrawImageAlpha(knobBitmap, (int)knobX, switchY); + DrawFilledRectangle(switchX, switchY, switchWidth, switchHeight, Checked ? UITheme.Accent : UITheme.SurfaceMuted); + DrawRectangle(switchX, switchY, switchWidth, switchHeight, Checked ? UITheme.AccentDark : UITheme.SurfaceBorder); + + int knobY = switchY + ((switchHeight - knobSize) / 2); + DrawFilledRectangle((int)knobX + 1, knobY, knobSize, knobSize, Color.White); + DrawRectangle((int)knobX + 1, knobY, knobSize, knobSize, UITheme.SurfaceBorder); DrawString(Text, Foreground, textX, textY); diff --git a/Gui/UILib/Table.cs b/Gui/UILib/Table.cs index 238f738..2f537b9 100644 --- a/Gui/UILib/Table.cs +++ b/Gui/UILib/Table.cs @@ -82,7 +82,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _background = Color.LightGray; + private Color _background = UITheme.Surface; internal Color Background { get @@ -96,7 +96,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _foreground = Color.Black; + private Color _foreground = UITheme.TextPrimary; internal Color Foreground { get @@ -110,7 +110,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _border = Color.Gray; + private Color _border = UITheme.SurfaceBorder; internal Color Border { get @@ -124,7 +124,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _selectedBackground = Color.FromArgb(221, 246, 255); + private Color _selectedBackground = UITheme.AccentLight; internal Color SelectedBackground { get @@ -138,7 +138,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _selectedForeground = Color.Black; + private Color _selectedForeground = UITheme.TextPrimary; internal Color SelectedForeground { get @@ -152,7 +152,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _selectedBorder = Color.FromArgb(126, 205, 234); + private Color _selectedBorder = UITheme.Accent; internal Color SelectedBorder { get @@ -258,7 +258,7 @@ namespace CMLeonOS.Gui.UILib if (CanScrollUp || CanScrollDown) { /* Background */ - DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, Height, _border); + DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, Height, UITheme.SurfaceMuted); /* Track */ int trackAvailableHeight = Height - (ScrollbarThickness * 2); @@ -266,22 +266,22 @@ namespace CMLeonOS.Gui.UILib double trackProgress = (double)scrollY / (double)((Cells.Count * CellHeight) - Height); int trackY = (int)(_scrollbarThickness + (((double)trackAvailableHeight - ((double)trackAvailableHeight * trackSize)) * trackProgress)); // Border - DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, Height, _border); + DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, Height, UITheme.SurfaceBorder); // Background - DrawFilledRectangle(Width - _scrollbarThickness + 1, trackY + 1, _scrollbarThickness - 2, (int)(trackSize * trackAvailableHeight) - 2, _background); + DrawFilledRectangle(Width - _scrollbarThickness + 1, trackY + 1, _scrollbarThickness - 2, (int)(trackSize * trackAvailableHeight) - 2, UITheme.AccentLight); /* Up arrow */ // Border - DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, _scrollbarThickness, _border); + DrawFilledRectangle(Width - _scrollbarThickness, 0, _scrollbarThickness, _scrollbarThickness, UITheme.SurfaceBorder); // Background - DrawFilledRectangle(Width - _scrollbarThickness + 1, 1, _scrollbarThickness - 2, _scrollbarThickness - 2, CanScrollUp ? _background : _border); + DrawFilledRectangle(Width - _scrollbarThickness + 1, 1, _scrollbarThickness - 2, _scrollbarThickness - 2, CanScrollUp ? UITheme.Surface : UITheme.SurfaceMuted); DrawImageAlpha(scrollbarUpBitmap, (int)((Width - _scrollbarThickness) + ((_scrollbarThickness / 2) - (scrollbarUpBitmap.Width / 2))), (int)((_scrollbarThickness / 2) - (scrollbarUpBitmap.Height / 2))); /* Down arrow */ // Border - DrawFilledRectangle(Width - _scrollbarThickness, Height - _scrollbarThickness, _scrollbarThickness, _scrollbarThickness, _border); + DrawFilledRectangle(Width - _scrollbarThickness, Height - _scrollbarThickness, _scrollbarThickness, _scrollbarThickness, UITheme.SurfaceBorder); // Background - DrawFilledRectangle(Width - _scrollbarThickness + 1, Height - _scrollbarThickness + 1, _scrollbarThickness - 2, _scrollbarThickness - 2, CanScrollDown ? _background : _border); + DrawFilledRectangle(Width - _scrollbarThickness + 1, Height - _scrollbarThickness + 1, _scrollbarThickness - 2, _scrollbarThickness - 2, CanScrollDown ? UITheme.Surface : UITheme.SurfaceMuted); DrawImageAlpha(scrollbarDownBitmap, (int)((Width - _scrollbarThickness) + ((_scrollbarThickness / 2) - (scrollbarUpBitmap.Width / 2))), (int)((Height - _scrollbarThickness) + ((_scrollbarThickness / 2) - (scrollbarUpBitmap.Height / 2)))); } } @@ -344,7 +344,8 @@ namespace CMLeonOS.Gui.UILib { TableCell cell = Cells[i]; bool selected = _selectedCellIndex == i; - Rectangle cellRect = new Rectangle(0, (int)((i * _cellHeight) - scrollY), Width, _cellHeight); + int contentWidth = Width - ((CanScrollUp || CanScrollDown) ? _scrollbarThickness : 0); + Rectangle cellRect = new Rectangle(0, (int)((i * _cellHeight) - scrollY), contentWidth, _cellHeight); if (cellRect.Y < -cellRect.Height || cellRect.Y > Height) { @@ -368,7 +369,7 @@ namespace CMLeonOS.Gui.UILib switch (_textAlignment) { case Alignment.Start: - textX = cellRect.X + (cell.Image != null ? (CellHeight - FontData.Height) / 2 : 0); + textX = cellRect.X + 6 + (cell.Image != null ? (CellHeight - FontData.Height) / 2 : 0); break; case Alignment.Middle: textX = cellRect.X + (cellRect.Width / 2) - (cell.Text.Length * FontData.Width / 2); @@ -385,8 +386,8 @@ namespace CMLeonOS.Gui.UILib if (cell.Image != null) { - textX += (int)cell.Image.Width; - DrawImageAlpha(cell.Image, cellRect.X, (int)(cellRect.Y + (cellRect.Height / 2) - (cell.Image.Height / 2))); + textX += (int)cell.Image.Width + 6; + DrawImageAlpha(cell.Image, cellRect.X + 4, (int)(cellRect.Y + (cellRect.Height / 2) - (cell.Image.Height / 2))); } if (cell.ForegroundColourOverride != null) @@ -397,6 +398,8 @@ namespace CMLeonOS.Gui.UILib { DrawString(cell.Text, selected ? SelectedForeground : Foreground, textX, textY); } + + DrawHorizontalLine(cellRect.Width - 2, 1, cellRect.Y + cellRect.Height - 1, UITheme.SurfaceBorder); } //DrawString($"{scrollY.ToString()} {dragging.ToString()} {scrollMax.ToString()}", Color.Red, 0, 0); diff --git a/Gui/UILib/TextBox.cs b/Gui/UILib/TextBox.cs index f54f194..eace8e2 100644 --- a/Gui/UILib/TextBox.cs +++ b/Gui/UILib/TextBox.cs @@ -87,7 +87,7 @@ namespace CMLeonOS.Gui.UILib internal bool Shield { get; set; } = false; - private Color _background = Color.White; + private Color _background = UITheme.Surface; internal Color Background { get @@ -103,7 +103,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _foreground = Color.Black; + private Color _foreground = UITheme.TextPrimary; internal Color Foreground { get @@ -118,7 +118,7 @@ namespace CMLeonOS.Gui.UILib } } - private Color _placeholderForeground = Color.Gray; + private Color _placeholderForeground = UITheme.TextSecondary; internal Color PlaceholderForeground { get @@ -339,6 +339,7 @@ namespace CMLeonOS.Gui.UILib private const int fontWidth = 8; private const int fontHeight = 16; + private const int horizontalPadding = 4; private int caretLine = -1; private int caretCol = 0; @@ -364,12 +365,12 @@ namespace CMLeonOS.Gui.UILib if (Text == string.Empty) { - DrawRectangle(0, 0, Width, Height, Color.Gray); - DrawString(PlaceholderText, PlaceholderForeground, 0, 0); + DrawRectangle(0, 0, Width, Height, caretLine == -1 ? UITheme.SurfaceBorder : UITheme.AccentDark); + DrawString(PlaceholderText, PlaceholderForeground, horizontalPadding, 2); if (caretLine == 0 && cursorVisible) { - DrawVerticalLine(fontHeight, 1, 0, Foreground); + DrawVerticalLine(fontHeight, horizontalPadding, 2, Foreground); } WM.Update(this); @@ -392,11 +393,11 @@ namespace CMLeonOS.Gui.UILib if (i < lines.Count) { string lineText = Shield ? new string('*', lines[i].Length) : lines[i]; - RenderLine(i, lineText, lineY, -scrollX); + RenderLine(i, lineText, lineY + 2, horizontalPadding - scrollX); if (caretLine == i && cursorVisible) { - DrawVerticalLine(fontHeight, ((caretCol * fontWidth) - scrollX) + 1, (caretLine * fontHeight) - scrollY, Foreground); + DrawVerticalLine(fontHeight, ((caretCol * fontWidth) - scrollX) + horizontalPadding, ((caretLine * fontHeight) - scrollY) + 2, Foreground); } } } @@ -404,7 +405,7 @@ namespace CMLeonOS.Gui.UILib markedLinesBegin = -1; markedLinesEnd = -1; - DrawRectangle(0, 0, Width, Height, Color.Gray); + DrawRectangle(0, 0, Width, Height, caretLine == -1 ? UITheme.SurfaceBorder : UITheme.AccentDark); WM.Update(this); } diff --git a/Gui/UILib/UITheme.cs b/Gui/UILib/UITheme.cs new file mode 100644 index 0000000..a5069ca --- /dev/null +++ b/Gui/UILib/UITheme.cs @@ -0,0 +1,42 @@ +// The CMLeonOS Project (https://github.com/Leonmmcoset/CMLeonOS) +// Copyright (C) 2025-present LeonOS 2 Developer Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System.Drawing; + +namespace CMLeonOS.Gui.UILib +{ + internal static class UITheme + { + internal static readonly Color WindowTitleBackground = Color.FromArgb(26, 34, 46); + internal static readonly Color WindowTitleTopHighlight = Color.FromArgb(52, 67, 90); + internal static readonly Color WindowTitleBottomBorder = Color.FromArgb(14, 18, 26); + internal static readonly Color WindowTitleText = Color.FromArgb(239, 244, 252); + internal static readonly Color WindowButtonBackground = Color.FromArgb(38, 49, 66); + + internal static readonly Color Surface = Color.FromArgb(245, 248, 252); + internal static readonly Color SurfaceMuted = Color.FromArgb(228, 235, 245); + internal static readonly Color SurfaceBorder = Color.FromArgb(149, 164, 183); + internal static readonly Color TextPrimary = Color.FromArgb(33, 43, 56); + internal static readonly Color TextSecondary = Color.FromArgb(96, 109, 128); + + internal static readonly Color Accent = Color.FromArgb(45, 117, 222); + internal static readonly Color AccentDark = Color.FromArgb(28, 89, 184); + internal static readonly Color AccentLight = Color.FromArgb(204, 225, 255); + + internal static readonly Color Success = Color.FromArgb(45, 152, 99); + internal static readonly Color Warning = Color.FromArgb(210, 139, 54); + } +}