diff --git a/Prowl.Editor/Panels/ProjectPanel.cs b/Prowl.Editor/Panels/ProjectPanel.cs index 2907c21cd..87fc67edc 100644 --- a/Prowl.Editor/Panels/ProjectPanel.cs +++ b/Prowl.Editor/Panels/ProjectPanel.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using Prowl.Editor.Docking; using Prowl.Editor.Widgets; @@ -698,19 +700,7 @@ private static void OpenWithSystem(ContentItem item) private static void ShowInExplorer(ContentItem item) { string absPath = Path.Combine(Project.Current!.AssetsPath, item.RelativePath); - ShowInExplorerPath(absPath); - } - - private static void ShowInExplorerPath(string absPath) - { - try - { - if (Directory.Exists(absPath)) - System.Diagnostics.Process.Start("explorer.exe", absPath); - else if (File.Exists(absPath)) - System.Diagnostics.Process.Start("explorer.exe", $"/select,\"{absPath}\""); - } - catch { } + ReferenceOpenerService.OpenFileSystemPath(absPath); } // ================================================================ diff --git a/Prowl.Editor/Project/Project.cs b/Prowl.Editor/Project/Project.cs index b248c2c32..4daccf907 100644 --- a/Prowl.Editor/Project/Project.cs +++ b/Prowl.Editor/Project/Project.cs @@ -61,7 +61,7 @@ public static Project Create(string parentFolder, string projectName) if (!File.Exists(gitignore)) { File.WriteAllText(gitignore, - "Library/\nTemp/\nLogs/\n*.csproj\n*.sln\n.vs/\nbin/\nobj/\n"); + "Library/\nTemp/\nLogs/\n*.csproj\n*.sln\n.vs/\nbin/\nobj/\n# MacOS/\n.DS_Store/\n"); } return project; diff --git a/Prowl.Editor/Project/ProjectLauncher.cs b/Prowl.Editor/Project/ProjectLauncher.cs index 54b900bf4..db9db3333 100644 --- a/Prowl.Editor/Project/ProjectLauncher.cs +++ b/Prowl.Editor/Project/ProjectLauncher.cs @@ -52,64 +52,57 @@ public static void Draw(Paper paper, float dt, bool forceDraw = false) int h = Window.InternalWindow.Size.Y; // Full background - // paper.Box("pl_bg") - // .PositionType(PositionType.SelfDirected) - // .Position(0, 0) - // .Size(w, h) - // .BackgroundColor(EditorTheme.Neutral100) - // .OnPostLayout((handle, rect) => paper.Draw(ref handle, (canvas, r) => - // { - // float cx = w / 2f; - // float cy = h / 2f; - // float radius = Math.Max(cx, cy) * 1.2f; - // float t = _animTime * 0.05f; - // var transparent = Prowl.Vector.Color32.FromArgb(0, 0, 0, 0); - - // // Subtle background gradients (same as editor but dimmer) - // float px = cx + (float)Math.Sin(t) * cx * 0.6f; - // float py = cy + (float)Math.Sin(t * 2) * cy * 0.3f; - // var purple = Prowl.Vector.Color32.FromArgb(25, 140, 60, 200); - // canvas.SetRadialBrush(px, py, 0, radius, purple, transparent); - // canvas.BeginPath(); - // canvas.Rect(0, 0, w, h); - // canvas.Fill(); - - // float bx = cx - (float)Math.Sin(t) * cx * 0.6f; - // float by = cy - (float)Math.Sin(t * 2) * cy * 0.3f; - // var blue = Prowl.Vector.Color32.FromArgb(25, 60, 140, 220); - // canvas.SetRadialBrush(bx, by, 0, radius, blue, transparent); - // canvas.BeginPath(); - // canvas.Rect(0, 0, w, h); - // canvas.Fill(); - // })); - - // Center card - // float cardW = 600f; - // float cardH = 500f; - - float sidebarW = 200f; - - using (paper.Row("root") + paper.Box("pl_bg") .PositionType(PositionType.SelfDirected) .Position(0, 0) .Size(w, h) - .BorderColor(EditorTheme.Ink100) - .BorderWidth(1) - .BackgroundColor(EditorTheme.Neutral300) - .Enter()) + .BackgroundColor(EditorTheme.Neutral100) + .OnPostLayout((handle, rect) => paper.Draw(ref handle, (canvas, r) => + { + float cx = w / 2f; + float cy = h / 2f; + float radius = Math.Max(cx, cy) * 1.2f; + float t = _animTime * 0.05f; + var transparent = Prowl.Vector.Color32.FromArgb(0, 0, 0, 0); + + // Subtle background gradients (same as editor but dimmer) + float px = cx + (float)Math.Sin(t) * cx * 0.6f; + float py = cy + (float)Math.Sin(t * 2) * cy * 0.3f; + var purple = Prowl.Vector.Color32.FromArgb(25, 140, 60, 200); + canvas.SetRadialBrush(px, py, 0, radius, purple, transparent); + canvas.BeginPath(); + canvas.Rect(0, 0, w, h); + canvas.Fill(); + + float bx = cx - (float)Math.Sin(t) * cx * 0.6f; + float by = cy - (float)Math.Sin(t * 2) * cy * 0.3f; + var blue = Prowl.Vector.Color32.FromArgb(25, 60, 140, 220); + canvas.SetRadialBrush(bx, by, 0, radius, blue, transparent); + canvas.BeginPath(); + canvas.Rect(0, 0, w, h); + canvas.Fill(); + })); + + // Center card + float cardW = 600f; + float cardH = 500f; + + using (paper.Box("container").Size(w, h).Position(0, 0).PositionType(PositionType.SelfDirected).Enter()) { - using (paper.Column("sidebar") - .BackgroundColor(EditorTheme.Neutral400) - .Size(sidebarW, h) + using (paper.Column("pl_window") + .Margin(UnitValue.StretchOne) + .Size(cardW, cardH) .BorderColor(EditorTheme.Ink100) .BorderWidth(1) + .Rounded(EditorTheme.Roundness) + .BackgroundColor(EditorTheme.Neutral300) .Enter()) { - - // Prowl Emblem - using (paper.Row("pl_header") + // Header + using (paper.Row("header") .Height(60) - .RowBetween(12) + .RowBetween(0) + .RoundedTop(EditorTheme.Roundness) .BackgroundColor(EditorTheme.Neutral300) .BorderColor(EditorTheme.Ink100) .BorderWidth(1) @@ -117,14 +110,43 @@ public static void Draw(Paper paper, float dt, bool forceDraw = false) { paper.Box("pl_title") .Height(60) - .Margin(16, 16, 0, 8) + .Width(110) + .Margin(16, 0, 8, 0) .Text("PROWL", boldFont) .TextColor(EditorTheme.Ink500) .FontSize(28f) .Alignment(TextAlignment.MiddleLeft); - // Spacer - paper.Box("pl_spacer"); + // Links + using (paper.Row("pl_header") + .Height(UnitValue.Auto) + .RowBetween(12) + .Margin(0, 0, 28, 0) + .Enter()) + { + EditorGUI.ButtonSquareGhost(paper, "www_link", EditorIcons.Globe).OnValueChanged((_) => + { + ReferenceOpenerService.OpenUrl("https://prowlengine.com"); + }); + + EditorGUI.ButtonSquareGhost(paper, "ds_link", EditorIcons.Message).OnValueChanged((_) => + { + ReferenceOpenerService.OpenUrl("https://discord.gg/HgBsBqfSpa"); + }); + + EditorGUI.ButtonSquareGhost(paper, "yt_link", EditorIcons.Video).OnValueChanged((_) => + { + ReferenceOpenerService.OpenUrl("https://youtube.com/@prowlengine"); + }); + + EditorGUI.ButtonSquareGhost(paper, "gh_link", EditorIcons.Code).OnValueChanged((_) => + { + ReferenceOpenerService.OpenUrl("https://github.com/ProwlEngine/Prowl"); + }); + } + + paper.Box("spacer"); + paper.Box("pl_version") .Height(60) @@ -136,90 +158,61 @@ public static void Draw(Paper paper, float dt, bool forceDraw = false) .Alignment(TextAlignment.MiddleRight); } - paper.Box("spacer").Height(UnitValue.Stretch()); - // Links - using (paper.Column("pl_header") - .Height(60) - .ColBetween(12) - .Margin(16, 12) + using (paper.Column("content") + .Size(cardW, cardH - 90) .Enter()) { - EditorGUI.Button(paper, "yt_link", "YouTube").OnValueChanged((_) => - { - WebService.OpenUrl("https://youtube.com/@prowlengine"); - }); - - EditorGUI.Button(paper, "gh_link", "GitHub").OnValueChanged((_) => + // New / Open buttons + using (paper.Row("toolbar") + .Height(30) + .Margin(10, 10, 16, 0) + .RowBetween(8) + .Enter()) { - WebService.OpenUrl("https://github.com/ProwlEngine/Prowl"); - }); - } - } - - using (paper.Column("content") - .Size(w - sidebarW, h) - .Enter()) - { - // New / Open buttons - using (paper.Row("toolbar") - .Height(40) - .Margin(10, 10, 32, 0) - .RowBetween(8) - .Enter()) - { - paper.Box("tl_label") - .Width(UnitValue.Auto) - .Height(EditorTheme.RowHeight) - .Text("Projects", boldFont) - .TextColor(EditorTheme.Ink500) - .FontSize(EditorTheme.FontSize + 8) - .Alignment(TextAlignment.MiddleLeft); - - // Spacer - paper.Box("tl_tb_spacer"); + // Spacer + EditorGUI.SearchBar(paper, "search", "", "Search Projects"); - // Spacer - EditorGUI.SearchBar(paper, "search", "", "Search"); - - EditorGUI.Button(paper, "tl_btn_open", $"{EditorIcons.FolderOpen} Open Project", 130) - .OnValueChanged(_ => - { - FileDialog.Open(FileDialogMode.SelectFolder, path => + EditorGUI.Button(paper, "tl_btn_open", $"{EditorIcons.FolderOpen} Open Project", 130) + .OnValueChanged(_ => { - if (path == null) return; - TryOpenProject(path); + FileDialog.Open(FileDialogMode.SelectFolder, path => + { + if (path == null) return; + TryOpenProject(path); + }); }); - }); - using (paper.Box("tl_btn_new") - .Height(EditorTheme.RowHeight) - .Width(120) - .BackgroundColor(EditorTheme.Blue300) - .Hovered.BackgroundColor(EditorTheme.Blue400).End() - .Rounded(3) - .BorderColor(EditorTheme.Blue400) - .BorderWidth(1) - .OnClick((_) => _showNewProject = !_showNewProject) - .Enter()) { - paper.Box($"label") + using (paper.Box("tl_btn_new") .Height(EditorTheme.RowHeight) - .Margin(EditorTheme.RowHeight / 4, 0) - .Alignment(PaperUI.TextAlignment.MiddleLeft) - .Text($" {EditorIcons.Plus} New Project", EditorTheme.DefaultFont) - .TextColor(EditorTheme.Ink500) - .FontSize(EditorTheme.FontSize); + .Width(120) + .BackgroundColor(EditorTheme.Blue300) + .Hovered.BackgroundColor(EditorTheme.Blue400).End() + .Rounded(3) + .BorderColor(EditorTheme.Blue400) + .BorderWidth(1) + .OnClick((_) => _showNewProject = !_showNewProject) + .Enter()) + { + paper.Box($"label") + .Height(EditorTheme.RowHeight) + .Margin(EditorTheme.RowHeight / 4, 0) + .Alignment(PaperUI.TextAlignment.MiddleLeft) + .Text($" {EditorIcons.Plus} New Project", EditorTheme.DefaultFont) + .TextColor(EditorTheme.Ink500) + .FontSize(EditorTheme.FontSize); + } } - } - // New project panel (collapsible) - if (_showNewProject) - { - DrawNewProjectPanel(paper, font); - } + // New project panel (collapsible) + if (_showNewProject) + { + DrawNewProjectPanel(paper, font); + } - // Recent projects list - DrawRecentProjects(paper, font, w - sidebarW, h - 60 - 40 - (_showNewProject ? 80 : 0)); + // Recent projects list + DrawRecentProjects(paper, font, cardW, cardH - 60 - 46 - (_showNewProject ? 80 : 0)); + } } } } @@ -291,7 +284,7 @@ private static void DrawRecentProjects(Paper paper, Prowl.Scribe.FontFile font, { var entries = RecentProjects.Entries; - using (ScrollView.Begin(paper, "pl_scroll", width, height, colSpacing: 8, paddingLeft: 8)) + using (ScrollView.Begin(paper, "pl_scroll", width, height, colSpacing: 8, paddingLeft: 8, paddingRight: 8)) { if (entries.Count == 0) { @@ -379,6 +372,8 @@ private static void DrawRecentProjects(Paper paper, Prowl.Scribe.FontFile font, } } } + + paper.Box("spacer").Height(6); } } diff --git a/Prowl.Editor/ReferenceOpenerService.cs b/Prowl.Editor/ReferenceOpenerService.cs new file mode 100644 index 000000000..d3c679a5f --- /dev/null +++ b/Prowl.Editor/ReferenceOpenerService.cs @@ -0,0 +1,67 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; + +namespace Prowl.Editor; + +public static class ReferenceOpenerService +{ + public static void OpenUrl(string url) + { + try + { + // For Windows: Launching via shell + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); + } + // For macOS: Use the 'open' command + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Process.Start("open", url); + } + // For Linux: Use 'xdg-open' + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("xdg-open", url); + } + } + catch (Exception ex) + { + Console.WriteLine($"Unable to open link: {ex.Message}"); + } + } + + public static void OpenFileSystemPath(string absPath) + { + try + { + // For Windows: Launching via shell + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + if (Directory.Exists(absPath)) + { + Process.Start("explorer.exe", absPath); + } + else if (File.Exists(absPath)) + { + Process.Start("explorer.exe", $"/select,\"{absPath}\""); + } + } + // For macOS: Use the 'open' command + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + // The -R flag tells Finder to "reveal" the item (select it) + Process.Start("open", $"-R \"{absPath}\""); + } + // For Linux: Use 'xdg-open' + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + // Linux varies by distro; 'dbus-send' or 'xdg-open' are common alternatives + Process.Start("xdg-open", $"\"{System.IO.Path.GetDirectoryName(absPath)}\""); + } + } + catch { } + } +} \ No newline at end of file diff --git a/Prowl.Editor/WebService.cs b/Prowl.Editor/WebService.cs deleted file mode 100644 index 142fe17cc..000000000 --- a/Prowl.Editor/WebService.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace Prowl.Editor; - -public static class WebService -{ - public static void OpenUrl(string url) - { - try - { - // For Windows: Launching via shell - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }); - } - // For macOS: Use the 'open' command - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - Process.Start("open", url); - } - // For Linux: Use 'xdg-open' - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - Process.Start("xdg-open", url); - } - } - catch (Exception ex) - { - Console.WriteLine($"Unable to open link: {ex.Message}"); - } - } -} \ No newline at end of file diff --git a/Prowl.Editor/Widgets/EditorGUI.cs b/Prowl.Editor/Widgets/EditorGUI.cs index c3d9ff43e..1f310704c 100644 --- a/Prowl.Editor/Widgets/EditorGUI.cs +++ b/Prowl.Editor/Widgets/EditorGUI.cs @@ -106,7 +106,36 @@ public static WidgetResult Button(Paper paper, string id, string label, fl .BackgroundColor(EditorTheme.Ink100) .Hovered.BackgroundColor(EditorTheme.Ink200).End() .Rounded(3) - .BorderColor(EditorTheme.Ink200) + .BorderColor(EditorTheme.Ink100) + .BorderWidth(1) + .OnClick(e => userCallback?.Invoke(true)); + + if (width > 0) el.Width(width); + using (el.Enter()) + { + if (Font != null) + paper.Box($"{id}_label") + .Height(EditorTheme.RowHeight) + .Margin(EditorTheme.RowHeight/4, 0) + .Alignment(PaperUI.TextAlignment.MiddleLeft) + .Text(label, Font) + .TextColor(EditorTheme.Ink500) + .FontSize(FontSz); + } + + return new WidgetResult(cb => userCallback = cb); + } + + public static WidgetResult ButtonGhost(Paper paper, string id, string label, float width = 0) + { + Action? userCallback = null; + + var el = paper.Box(id) + .Height(EditorTheme.RowHeight) + // .BackgroundColor(EditorTheme.Ink100) + .Hovered.BackgroundColor(EditorTheme.Ink100).End() + .Rounded(3) + .BorderColor(EditorTheme.Ink100) .BorderWidth(1) .OnClick(e => userCallback?.Invoke(true)); @@ -140,7 +169,29 @@ public static WidgetResult ButtonSquare(Paper paper, string id, string ico .BackgroundColor(EditorTheme.Ink100) .Hovered.BackgroundColor(EditorTheme.Ink200).End() .Rounded(3) - .BorderColor(EditorTheme.Ink200).BorderWidth(1) + .BorderColor(EditorTheme.Ink100) + .BorderWidth(1) + .OnClick(e => userCallback?.Invoke(true)); + + return new WidgetResult(cb => userCallback = cb); + } + + public static WidgetResult ButtonSquareGhost(Paper paper, string id, string icon) + { + Action? userCallback = null; + + paper.Box(id) + .Alignment(PaperUI.TextAlignment.MiddleCenter) + .Text(icon, Font) + .TextColor(EditorTheme.Ink500) + .FontSize(FontSz) + .Height(EditorTheme.RowHeight) + .Width(EditorTheme.RowHeight) + // .BackgroundColor(EditorTheme.Ink100) + .Hovered.BackgroundColor(EditorTheme.Ink100).End() + .Rounded(3) + .BorderColor(EditorTheme.Ink100) + .BorderWidth(1) .OnClick(e => userCallback?.Invoke(true)); return new WidgetResult(cb => userCallback = cb); diff --git a/Prowl.Editor/Widgets/FileDialog.cs b/Prowl.Editor/Widgets/FileDialog.cs index 2f82f69a7..266aaaa14 100644 --- a/Prowl.Editor/Widgets/FileDialog.cs +++ b/Prowl.Editor/Widgets/FileDialog.cs @@ -244,7 +244,8 @@ public static void Draw(Paper paper) // Fullscreen blocker paper.Box("fd_overlay") - .PositionType(PositionType.SelfDirected).Position(0, 0) + .PositionType(PositionType.SelfDirected) + .Position(0, 0) .Size(UnitValue.Stretch(), UnitValue.Stretch()) .BackgroundColor(Color.FromArgb(120, 0, 0, 0)) .Layer(Layer.Overlay)