From feffc0d7a88256d72c1f0cc7df859af89154864d Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Mon, 25 Aug 2025 18:42:46 +1000 Subject: [PATCH] run panel tabs --- .../Features/Debugging/DebuggingService.cs | 2 +- .../Features/Events/GlobalEvents.cs | 10 +++- .../Features/Run/RunService.cs | 2 + .../Features/Run/{ => Resources}/Play.png | Bin .../Run/{ => Resources}/Play.png.import | 6 +-- .../Features/Run/Resources/Running.png | Bin 0 -> 743 bytes .../Features/Run/Resources/Running.png.import | 34 ++++++++++++ .../Features/Run/{ => Resources}/Stop.png | Bin .../Run/{ => Resources}/Stop.png.import | 6 +-- .../Features/Run/RunMenuItem.tscn | 4 +- src/SharpIDE.Godot/Features/Run/RunPanel.cs | 49 +++++++++++++----- src/SharpIDE.Godot/Features/Run/RunPanel.tscn | 22 ++++++-- .../Features/Run/RunPanelTab.cs | 24 +++++++++ .../Features/Run/RunPanelTab.cs.uid | 1 + .../Features/Run/RunPanelTab.tscn | 20 +++++++ src/SharpIDE.Godot/IdeRoot.tscn | 2 +- src/SharpIDE.Godot/NodeExtensions.cs | 11 +++- 17 files changed, 163 insertions(+), 30 deletions(-) rename src/SharpIDE.Godot/Features/Run/{ => Resources}/Play.png (100%) rename src/SharpIDE.Godot/Features/Run/{ => Resources}/Play.png.import (72%) create mode 100644 src/SharpIDE.Godot/Features/Run/Resources/Running.png create mode 100644 src/SharpIDE.Godot/Features/Run/Resources/Running.png.import rename src/SharpIDE.Godot/Features/Run/{ => Resources}/Stop.png (100%) rename src/SharpIDE.Godot/Features/Run/{ => Resources}/Stop.png.import (72%) create mode 100644 src/SharpIDE.Godot/Features/Run/RunPanelTab.cs create mode 100644 src/SharpIDE.Godot/Features/Run/RunPanelTab.cs.uid create mode 100644 src/SharpIDE.Godot/Features/Run/RunPanelTab.tscn diff --git a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs index 96a46e7..8945b1a 100644 --- a/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs +++ b/src/SharpIDE.Application/Features/Debugging/DebuggingService.cs @@ -34,7 +34,7 @@ public class DebuggingService var debugProtocolHost = new DebugProtocolHost(process.StandardInput.BaseStream, process.StandardOutput.BaseStream, false); debugProtocolHost.LogMessage += (sender, args) => { - Console.WriteLine($"Log message: {args.Message}"); + //Console.WriteLine($"Log message: {args.Message}"); }; debugProtocolHost.EventReceived += (sender, args) => { diff --git a/src/SharpIDE.Application/Features/Events/GlobalEvents.cs b/src/SharpIDE.Application/Features/Events/GlobalEvents.cs index 0c0003f..ecaeadb 100644 --- a/src/SharpIDE.Application/Features/Events/GlobalEvents.cs +++ b/src/SharpIDE.Application/Features/Events/GlobalEvents.cs @@ -1,4 +1,6 @@ -namespace SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; + +namespace SharpIDE.Application.Features.Events; public static class GlobalEvents { @@ -7,4 +9,10 @@ public static class GlobalEvents public static event Func StartedRunningProject = () => Task.CompletedTask; public static void InvokeStartedRunningProject() => StartedRunningProject?.Invoke(); + + public static event Func ProjectStartedRunning = _ => Task.CompletedTask; + public static void InvokeProjectStartedRunning(SharpIdeProjectModel project) => ProjectStartedRunning?.Invoke(project); + + public static event Func ProjectStoppedRunning = _ => Task.CompletedTask; + public static void InvokeProjectStoppedRunning(SharpIdeProjectModel project) => ProjectStoppedRunning?.Invoke(project); } diff --git a/src/SharpIDE.Application/Features/Run/RunService.cs b/src/SharpIDE.Application/Features/Run/RunService.cs index 9c26549..d549565 100644 --- a/src/SharpIDE.Application/Features/Run/RunService.cs +++ b/src/SharpIDE.Application/Features/Run/RunService.cs @@ -89,6 +89,7 @@ public class RunService project.OpenInRunPanel = true; GlobalEvents.InvokeProjectsRunningChanged(); GlobalEvents.InvokeStartedRunningProject(); + GlobalEvents.InvokeProjectStartedRunning(project); project.InvokeProjectStartedRunning(); await process.WaitForExitAsync().WaitAsync(project.RunningCancellationTokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); if (project.RunningCancellationTokenSource.IsCancellationRequested) @@ -118,6 +119,7 @@ public class RunService if (project.RunningCancellationTokenSource is null) throw new InvalidOperationException($"Project {project.Name} does not have a running cancellation token source."); await project.RunningCancellationTokenSource.CancelAsync().ConfigureAwait(false); + GlobalEvents.InvokeProjectStoppedRunning(project); } private string GetRunArguments(SharpIdeProjectModel project) diff --git a/src/SharpIDE.Godot/Features/Run/Play.png b/src/SharpIDE.Godot/Features/Run/Resources/Play.png similarity index 100% rename from src/SharpIDE.Godot/Features/Run/Play.png rename to src/SharpIDE.Godot/Features/Run/Resources/Play.png diff --git a/src/SharpIDE.Godot/Features/Run/Play.png.import b/src/SharpIDE.Godot/Features/Run/Resources/Play.png.import similarity index 72% rename from src/SharpIDE.Godot/Features/Run/Play.png.import rename to src/SharpIDE.Godot/Features/Run/Resources/Play.png.import index 78e2392..08a5766 100644 --- a/src/SharpIDE.Godot/Features/Run/Play.png.import +++ b/src/SharpIDE.Godot/Features/Run/Resources/Play.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://bnprs1lvjyaji" -path="res://.godot/imported/Play.png-f33bbb4899ca2148d51910992d85a9eb.ctex" +path="res://.godot/imported/Play.png-3438a21a8b920e7517d04626c592e8b6.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://Features/Run/Play.png" -dest_files=["res://.godot/imported/Play.png-f33bbb4899ca2148d51910992d85a9eb.ctex"] +source_file="res://Features/Run/Resources/Play.png" +dest_files=["res://.godot/imported/Play.png-3438a21a8b920e7517d04626c592e8b6.ctex"] [params] diff --git a/src/SharpIDE.Godot/Features/Run/Resources/Running.png b/src/SharpIDE.Godot/Features/Run/Resources/Running.png new file mode 100644 index 0000000000000000000000000000000000000000..eafea9f39bc57839893d96ab4b4668faeec46c45 GIT binary patch literal 743 zcmeAS@N?(olHy`uVBq!ia0vp^N+8U^1|+TAxeoy;#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!hW5;mh>{3jAFJg2T)o7U{G?R9irfOAY6b=y z`-+0Zwic-?7f?V97Du6s&rHqo20xNy} z^73-Ma$~*xqI7*jOG`_A10#JSBVC{h-Qvo;lEez#ykcdj0WPV<$wiq3C7Jno3Lpa$ zlk!VTY?Vq&GgGY664OkRQc_HHlM;Szwvs znT>z1^K`cBbr*PtGcat=c_1#XvwEI=ZFlI2uNxdUcUQ78uvGSb{di>a#2*=LN2(f^6`<7S>FVdQ&MBb@0IVqtn*aa+ literal 0 HcmV?d00001 diff --git a/src/SharpIDE.Godot/Features/Run/Resources/Running.png.import b/src/SharpIDE.Godot/Features/Run/Resources/Running.png.import new file mode 100644 index 0000000..72aadd1 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/Resources/Running.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://76nkes2imlf2" +path="res://.godot/imported/Running.png-d687d5fa2c32934f935f7cb594c7886d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Features/Run/Resources/Running.png" +dest_files=["res://.godot/imported/Running.png-d687d5fa2c32934f935f7cb594c7886d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/src/SharpIDE.Godot/Features/Run/Stop.png b/src/SharpIDE.Godot/Features/Run/Resources/Stop.png similarity index 100% rename from src/SharpIDE.Godot/Features/Run/Stop.png rename to src/SharpIDE.Godot/Features/Run/Resources/Stop.png diff --git a/src/SharpIDE.Godot/Features/Run/Stop.png.import b/src/SharpIDE.Godot/Features/Run/Resources/Stop.png.import similarity index 72% rename from src/SharpIDE.Godot/Features/Run/Stop.png.import rename to src/SharpIDE.Godot/Features/Run/Resources/Stop.png.import index 0bf2769..e9caf9e 100644 --- a/src/SharpIDE.Godot/Features/Run/Stop.png.import +++ b/src/SharpIDE.Godot/Features/Run/Resources/Stop.png.import @@ -3,15 +3,15 @@ importer="texture" type="CompressedTexture2D" uid="uid://csj1r7s3u10ch" -path="res://.godot/imported/Stop.png-369cb830460ee73ee5ab96abbb3f5c92.ctex" +path="res://.godot/imported/Stop.png-57d1375a399a106e4fdece5825762ac9.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://Features/Run/Stop.png" -dest_files=["res://.godot/imported/Stop.png-369cb830460ee73ee5ab96abbb3f5c92.ctex"] +source_file="res://Features/Run/Resources/Stop.png" +dest_files=["res://.godot/imported/Stop.png-57d1375a399a106e4fdece5825762ac9.ctex"] [params] diff --git a/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn b/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn index 4e17973..e6dac83 100644 --- a/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn +++ b/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn @@ -1,8 +1,8 @@ [gd_scene load_steps=4 format=3 uid="uid://d2ewm2lajutpv"] [ext_resource type="Script" uid="uid://btsnapfx0dlbb" path="res://Features/Run/RunMenuItem.cs" id="1_syj0f"] -[ext_resource type="Texture2D" uid="uid://bnprs1lvjyaji" path="res://Features/Run/Play.png" id="1_xfxpu"] -[ext_resource type="Texture2D" uid="uid://csj1r7s3u10ch" path="res://Features/Run/Stop.png" id="4_cd138"] +[ext_resource type="Texture2D" uid="uid://bnprs1lvjyaji" path="res://Features/Run/Resources/Play.png" id="1_xfxpu"] +[ext_resource type="Texture2D" uid="uid://csj1r7s3u10ch" path="res://Features/Run/Resources/Stop.png" id="4_cd138"] [node name="RunMenuItem" type="HBoxContainer"] offset_right = 40.0 diff --git a/src/SharpIDE.Godot/Features/Run/RunPanel.cs b/src/SharpIDE.Godot/Features/Run/RunPanel.cs index 0036fea..1a6cac5 100644 --- a/src/SharpIDE.Godot/Features/Run/RunPanel.cs +++ b/src/SharpIDE.Godot/Features/Run/RunPanel.cs @@ -1,33 +1,58 @@ using System.Collections.Generic; +using System.Linq; using GDExtensionBindgen; using Godot; +using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Godot.Features.Run; public partial class RunPanel : Control { - private Terminal _terminal = null!; private TabBar _tabBar = null!; private Panel _tabsPanel = null!; + + [Export] + public Texture2D RunningIcon { get; set; } = null!; + + private PackedScene _runPanelTabScene = GD.Load("res://Features/Run/RunPanelTab.tscn"); public override void _Ready() { _tabBar = GetNode("%TabBar"); + _tabBar.ClearTabs(); _tabsPanel = GetNode("%TabsPanel"); - var test = GetNode("VBoxContainer/TabsPanel/Terminal"); - _terminal = new Terminal(test); - _terminal.Write("Hello from SharpIDE.Godot!\n"); + GlobalEvents.ProjectStartedRunning += async projectModel => + { + await this.InvokeAsync(() => ProjectStartedRunning(projectModel)); + }; + GlobalEvents.ProjectStoppedRunning += async projectModel => + { + await this.InvokeAsync(() => ProjectStoppedRunning(projectModel)); + }; } - public override void _Process(double delta) + public void ProjectStartedRunning(SharpIdeProjectModel projectModel) { - //_terminal.Write("a"); - } - - public void NewRunStarted(SharpIdeProjectModel projectModel) - { - var terminal = new Terminal(); + var existingRunPanelTab = _tabsPanel.GetChildren().OfType().SingleOrDefault(s => s.Project == projectModel); + if (existingRunPanelTab != null) + { + _tabBar.SetTabIcon(existingRunPanelTab.TabBarTab, RunningIcon); + existingRunPanelTab.ClearTerminal(); + return; + } + + var runPanelTab = _runPanelTabScene.Instantiate(); + runPanelTab.Project = projectModel; _tabBar.AddTab(projectModel.Name); - _tabsPanel.AddChild(terminal); + var tabIdx = _tabBar.GetTabCount() - 1; + runPanelTab.TabBarTab = tabIdx; + _tabBar.SetTabIcon(runPanelTab.TabBarTab, RunningIcon); + _tabsPanel.AddChild(runPanelTab); + } + + public void ProjectStoppedRunning(SharpIdeProjectModel projectModel) + { + var runPanelTab = _tabsPanel.GetChildren().OfType().Single(s => s.Project == projectModel); + _tabBar.SetTabIcon(runPanelTab.TabBarTab, null); } } \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/Run/RunPanel.tscn b/src/SharpIDE.Godot/Features/Run/RunPanel.tscn index 5f0e4f8..4c6d435 100644 --- a/src/SharpIDE.Godot/Features/Run/RunPanel.tscn +++ b/src/SharpIDE.Godot/Features/Run/RunPanel.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=2 format=3 uid="uid://bcoytt3bw0gpe"] +[gd_scene load_steps=3 format=3 uid="uid://bcoytt3bw0gpe"] [ext_resource type="Script" uid="uid://ddivigavjclyb" path="res://Features/Run/RunPanel.cs" id="1_sq1l4"] +[ext_resource type="Texture2D" uid="uid://76nkes2imlf2" path="res://Features/Run/Resources/Running.png" id="2_tu4jg"] [node name="RunPanel" type="Control"] layout_mode = 3 @@ -10,8 +11,11 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_sq1l4") +RunningIcon = ExtResource("2_tu4jg") [node name="VBoxContainer" type="VBoxContainer" parent="."] +z_index = 100 +y_sort_enabled = true layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 @@ -33,10 +37,18 @@ unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 3 -[node name="Terminal" type="Terminal" parent="VBoxContainer/TabsPanel"] +[node name="Label" type="Label" parent="."] +visible = false layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -64.0 +offset_top = -11.5 +offset_right = 64.0 +offset_bottom = 11.5 grow_horizontal = 2 grow_vertical = 2 +text = "Nothing to show" diff --git a/src/SharpIDE.Godot/Features/Run/RunPanelTab.cs b/src/SharpIDE.Godot/Features/Run/RunPanelTab.cs new file mode 100644 index 0000000..c7fc132 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/RunPanelTab.cs @@ -0,0 +1,24 @@ +using GDExtensionBindgen; +using Godot; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; + +namespace SharpIDE.Godot.Features.Run; + +public partial class RunPanelTab : Control +{ + private Terminal _terminal = null!; + + public SharpIdeProjectModel Project { get; set; } = null!; + public int TabBarTab { get; set; } + + public override void _Ready() + { + _terminal = new Terminal(); + AddChild(_terminal); + } + + public void ClearTerminal() + { + _terminal.Clear(); + } +} \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/Run/RunPanelTab.cs.uid b/src/SharpIDE.Godot/Features/Run/RunPanelTab.cs.uid new file mode 100644 index 0000000..a5b4406 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/RunPanelTab.cs.uid @@ -0,0 +1 @@ +uid://ouskvcd0yaub diff --git a/src/SharpIDE.Godot/Features/Run/RunPanelTab.tscn b/src/SharpIDE.Godot/Features/Run/RunPanelTab.tscn new file mode 100644 index 0000000..cbba373 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/RunPanelTab.tscn @@ -0,0 +1,20 @@ +[gd_scene load_steps=2 format=3 uid="uid://cjopkm4osnp27"] + +[ext_resource type="Script" uid="uid://ouskvcd0yaub" path="res://Features/Run/RunPanelTab.cs" id="1_dx3i6"] + +[node name="RunPanelTab" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_dx3i6") + +[node name="Terminal" type="Terminal" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/src/SharpIDE.Godot/IdeRoot.tscn b/src/SharpIDE.Godot/IdeRoot.tscn index c1fb50f..3cf3854 100644 --- a/src/SharpIDE.Godot/IdeRoot.tscn +++ b/src/SharpIDE.Godot/IdeRoot.tscn @@ -1,7 +1,7 @@ [gd_scene load_steps=8 format=3 uid="uid://b2oniigcp5ew5"] [ext_resource type="Script" uid="uid://bavypuy7b375x" path="res://IdeRoot.cs" id="1_whawi"] -[ext_resource type="Texture2D" uid="uid://bnprs1lvjyaji" path="res://Features/Run/Play.png" id="2_8x8ub"] +[ext_resource type="Texture2D" uid="uid://bnprs1lvjyaji" path="res://Features/Run/Resources/Play.png" id="2_8x8ub"] [ext_resource type="Script" uid="uid://du2lt7r1p1qfy" path="res://SharpIdeCodeEdit.cs" id="2_qjf5e"] [ext_resource type="FontFile" uid="uid://7jc0nj310cu6" path="res://CascadiaCode.ttf" id="2_rk34b"] [ext_resource type="Script" uid="uid://bai53k7ongbxw" path="res://SolutionExplorerPanel.cs" id="2_tcy02"] diff --git a/src/SharpIDE.Godot/NodeExtensions.cs b/src/SharpIDE.Godot/NodeExtensions.cs index cb20451..3ca88c5 100644 --- a/src/SharpIDE.Godot/NodeExtensions.cs +++ b/src/SharpIDE.Godot/NodeExtensions.cs @@ -12,8 +12,15 @@ public static class NodeExtensions //WorkerThreadPool.AddTask(); Callable.From(() => { - workItem(); - taskCompletionSource.SetResult(); + try + { + workItem(); + taskCompletionSource.SetResult(); + } + catch (Exception ex) + { + taskCompletionSource.SetException(ex); + } }).CallDeferred(); return taskCompletionSource.Task; }