diff --git a/BuildTime.txt b/BuildTime.txt index d02b029..e00990f 100644 --- a/BuildTime.txt +++ b/BuildTime.txt @@ -1 +1 @@ -2026-04-04 12:15:03 \ No newline at end of file +2026-04-04 13:32:14 \ No newline at end of file diff --git a/GitCommit.txt b/GitCommit.txt index 8f4061e..dba258f 100644 --- a/GitCommit.txt +++ b/GitCommit.txt @@ -1 +1 @@ -836f199 \ No newline at end of file +636f3f1 \ No newline at end of file diff --git a/Gui/Apps/Demos/GLXGears.cs b/Gui/Apps/Demos/GLXGears.cs index 6fa4fbc..ddab5e2 100644 --- a/Gui/Apps/Demos/GLXGears.cs +++ b/Gui/Apps/Demos/GLXGears.cs @@ -40,6 +40,14 @@ namespace CMLeonOS.Gui.Apps.Demos Renderer3D.DrawLine3D(window, a, b, color, FovScale, CameraDistance); } + private void Draw3DTriangle(Vec3 a, Vec3 b, Vec3 c, Color color) + { + a = TransformPoint(a); + b = TransformPoint(b); + c = TransformPoint(c); + Renderer3D.DrawTriangle3D(window, a, b, c, color, FovScale, CameraDistance); + } + private void DrawGear(Vec3 center, double outerRadius, double innerRadius, double depth, int teeth, double angle, Color color) { int ringCount = teeth * 2; @@ -70,6 +78,35 @@ namespace CMLeonOS.Gui.Apps.Demos { int n = (i + 1) % ringCount; + Color frontFace = Color.FromArgb( + color.R, + color.G, + color.B); + Color backFace = Color.FromArgb( + Math.Max(0, color.R - 32), + Math.Max(0, color.G - 32), + Math.Max(0, color.B - 32)); + Color sideFace = Color.FromArgb( + Math.Max(0, color.R - 18), + Math.Max(0, color.G - 18), + Math.Max(0, color.B - 18)); + + // Front ring face (quad -> 2 triangles) + Draw3DTriangle(frontOuter[i], frontOuter[n], frontInner[n], frontFace); + Draw3DTriangle(frontOuter[i], frontInner[n], frontInner[i], frontFace); + + // Back ring face + Draw3DTriangle(backOuter[i], backInner[n], backOuter[n], backFace); + Draw3DTriangle(backOuter[i], backInner[i], backInner[n], backFace); + + // Outer wall + Draw3DTriangle(frontOuter[i], backOuter[n], frontOuter[n], sideFace); + Draw3DTriangle(frontOuter[i], backOuter[i], backOuter[n], sideFace); + + // Inner wall + Draw3DTriangle(frontInner[i], frontInner[n], backInner[n], sideFace); + Draw3DTriangle(frontInner[i], backInner[n], backInner[i], sideFace); + Draw3DLine(frontOuter[i], frontOuter[n], color); Draw3DLine(backOuter[i], backOuter[n], color); Draw3DLine(frontOuter[i], backOuter[i], color); diff --git a/Gui/ShellComponents/Dock/AppDockIcon.cs b/Gui/ShellComponents/Dock/AppDockIcon.cs index 34aec8c..49e60d9 100644 --- a/Gui/ShellComponents/Dock/AppDockIcon.cs +++ b/Gui/ShellComponents/Dock/AppDockIcon.cs @@ -32,7 +32,7 @@ namespace CMLeonOS.Gui.ShellComponents.Dock internal override void Clicked() { - ProcessManager.GetProcess().Focus = AppWindow; + ProcessManager.GetProcess().BringToFrontAndFocus(AppWindow); } } } diff --git a/Gui/ShellComponents/Dock/Dock.cs b/Gui/ShellComponents/Dock/Dock.cs index 62d29ec..cc70985 100644 --- a/Gui/ShellComponents/Dock/Dock.cs +++ b/Gui/ShellComponents/Dock/Dock.cs @@ -49,6 +49,9 @@ namespace CMLeonOS.Gui.ShellComponents.Dock internal static readonly int IconSize = 64; internal static readonly int IconImageMaxSize = 48; + private static readonly Color DockBackground = Color.FromArgb(211, 211, 211); + private static readonly Color ActiveIconBackground = Color.FromArgb(160, 190, 255); + private static readonly Color ActiveIndicator = Color.FromArgb(36, 88, 196); private void Render() { @@ -63,11 +66,22 @@ namespace CMLeonOS.Gui.ShellComponents.Dock window.MoveAndResize((int)(wm.ScreenWidth / 2 - newDockWidth / 2), window.Y, newDockWidth, window.Height); } - window.Clear(Color.FromArgb(211, 211, 211)); + window.Clear(DockBackground); + + AppWindow focusedApp = wm.GetOwningAppWindow(wm.Focus); int x = 0; foreach (var icon in Icons) { + int iconWidth = (int)icon.Size; + bool isSelected = icon is AppDockIcon appDockIcon && appDockIcon.AppWindow == focusedApp; + + if (isSelected && iconWidth > 4 && window.Height > 4) + { + window.DrawFilledRectangle(x + 2, 2, iconWidth - 4, window.Height - 4, ActiveIconBackground); + window.DrawFilledRectangle(x + 10, window.Height - 5, Math.Max(8, iconWidth - 20), 3, ActiveIndicator); + } + if (icon.Image != null) { Bitmap resizedImage = icon.Image.ResizeWidthKeepRatio((uint)Math.Min(IconImageMaxSize, icon.Size)); @@ -76,7 +90,7 @@ namespace CMLeonOS.Gui.ShellComponents.Dock window.DrawImageAlpha(resizedImage, imageX, (int)((window.Height / 2) - (resizedImage.Height / 2))); } - x += (int)icon.Size; + x += iconWidth; } wm.Update(window); diff --git a/Gui/WindowManager.cs b/Gui/WindowManager.cs index 7118cf2..726d880 100644 --- a/Gui/WindowManager.cs +++ b/Gui/WindowManager.cs @@ -18,6 +18,7 @@ using Cosmos.System; using Cosmos.System.Graphics; using CMLeonOS; using CMLeonOS.Gui.ShellComponents; +using CMLeonOS.Gui.UILib; using CMLeonOS.Settings; using CMLeonOS.Driver; using CMLeonOS.Logger; @@ -193,6 +194,95 @@ namespace CMLeonOS.Gui UpdateDock(); } + private bool IsDescendantOf(Window window, Window ancestor) + { + Window current = window; + while (current != null) + { + if (current == ancestor) + { + return true; + } + + current = current.RelativeTo; + } + + return false; + } + + internal AppWindow GetOwningAppWindow(Window window) + { + Window current = window; + while (current != null) + { + if (current is AppWindow appWindow) + { + return appWindow; + } + + current = current.RelativeTo; + } + + return null; + } + + internal void SetFocus(Window window, bool updateDock = true) + { + if (window == null || !Windows.Contains(window)) + { + return; + } + + if (Focus != null && Focus != window) + { + Focus.OnUnfocused?.Invoke(); + } + + Focus = window; + window.OnFocused?.Invoke(); + + if (updateDock) + { + UpdateDock(); + } + } + + internal void BringToFrontAndFocus(Window window) + { + if (window == null || !Windows.Contains(window)) + { + return; + } + + List toMove = new List(); + for (int i = 0; i < Windows.Count; i++) + { + Window current = Windows[i]; + if (current == window || IsDescendantOf(current, window)) + { + toMove.Add(current); + } + } + + if (toMove.Count > 0) + { + for (int i = 0; i < toMove.Count; i++) + { + Windows.Remove(toMove[i]); + } + + for (int i = 0; i < toMove.Count; i++) + { + Windows.Add(toMove[i]); + } + } + + SetFocus(window, updateDock: false); + + RerenderAll(); + UpdateDock(); + } + internal void RemoveWindow(Window window, bool rerender = true) { Windows.Remove(window); @@ -292,13 +382,12 @@ namespace CMLeonOS.Gui else if (MouseManager.MouseState == MouseState.None && lastMouseState == MouseState.Left) { lastMouseState = MouseManager.MouseState; - - if (Focus != null && Focus != window) + Window focusTarget = GetOwningAppWindow(window) ?? window; + BringToFrontAndFocus(focusTarget); + if (window != focusTarget && Windows.Contains(window)) { - Focus.OnUnfocused?.Invoke(); + SetFocus(window); } - Focus = window; - window.OnFocused?.Invoke(); window.OnClick?.Invoke(relativeX, relativeY); diff --git a/OSGraphicDraw/Renderer3D.cs b/OSGraphicDraw/Renderer3D.cs index 2007900..29f2eef 100644 --- a/OSGraphicDraw/Renderer3D.cs +++ b/OSGraphicDraw/Renderer3D.cs @@ -34,5 +34,58 @@ namespace CMLeonOS.OSGraphicDraw target.DrawLine(ax, ay, bx, by, color); } + + private static void DrawScanline(Window target, int x1, int x2, int y, Color color) + { + if (y < 0 || y >= target.Height) return; + if (x1 > x2) + { + int t = x1; + x1 = x2; + x2 = t; + } + + if (x2 < 0 || x1 >= target.Width) return; + if (x1 < 0) x1 = 0; + if (x2 >= target.Width) x2 = target.Width - 1; + target.DrawHorizontalLine((x2 - x1) + 1, x1, y, color); + } + + private static void DrawTriangle2D(Window target, int x1, int y1, int x2, int y2, int x3, int y3, Color color) + { + if (y1 > y2) { int tx = x1; int ty = y1; x1 = x2; y1 = y2; x2 = tx; y2 = ty; } + if (y1 > y3) { int tx = x1; int ty = y1; x1 = x3; y1 = y3; x3 = tx; y3 = ty; } + if (y2 > y3) { int tx = x2; int ty = y2; x2 = x3; y2 = y3; x3 = tx; y3 = ty; } + + if (y1 == y3) return; + + for (int y = y1; y <= y3; y++) + { + bool lowerHalf = y > y2 || y2 == y1; + int segmentHeight = lowerHalf ? (y3 - y2) : (y2 - y1); + if (segmentHeight == 0) continue; + + double alpha = (double)(y - y1) / (y3 - y1); + double beta = lowerHalf + ? (double)(y - y2) / segmentHeight + : (double)(y - y1) / segmentHeight; + + int ax = x1 + (int)((x3 - x1) * alpha); + int bx = lowerHalf + ? x2 + (int)((x3 - x2) * beta) + : x1 + (int)((x2 - x1) * beta); + + DrawScanline(target, ax, bx, y, color); + } + } + + internal static void DrawTriangle3D(Window target, Vec3 a, Vec3 b, Vec3 c, Color color, double fovScale, double cameraDistance) + { + if (!Project(a, target.Width, target.Height, fovScale, cameraDistance, out int ax, out int ay)) return; + if (!Project(b, target.Width, target.Height, fovScale, cameraDistance, out int bx, out int by)) return; + if (!Project(c, target.Width, target.Height, fovScale, cameraDistance, out int cx, out int cy)) return; + + DrawTriangle2D(target, ax, ay, bx, by, cx, cy, color); + } } }