From b2137155fda9f87e517b904885a6d55ed529e264 Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 02:43:40 -0500 Subject: [PATCH 01/14] Updated IdeSolutionState with LastStartupProject Added variable to store LastStartupProject for new Run UI. --- src/SharpIDE.Godot/Features/IdeSettings/IdeSolutionState.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SharpIDE.Godot/Features/IdeSettings/IdeSolutionState.cs b/src/SharpIDE.Godot/Features/IdeSettings/IdeSolutionState.cs index f7c1c61f..1f934ab6 100644 --- a/src/SharpIDE.Godot/Features/IdeSettings/IdeSolutionState.cs +++ b/src/SharpIDE.Godot/Features/IdeSettings/IdeSolutionState.cs @@ -3,6 +3,7 @@ public class IdeSolutionState { public List OpenTabs { get; set; } = []; + public string LastStartupProject { get; set; } = ""; } public class OpenTab From 716630c1707a8cc84efff052b9588e2ba4909db2 Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 02:46:28 -0500 Subject: [PATCH 02/14] Updated RunMenuItem for new Run system Added Pressed event handler for when item is pressed. Added Handlers for MouseEntered and Exited for highlighting of the item. Added GuiInput override to handle Pressed events for Item. Added StyleBoxFlat to Label, to show highlighting feedback of the item. Removed Spacer, as it isn't needed. Updated Label to take full rect of space available for the Label, adjusted Vertical Alignment to be center. --- .../Features/Run/RunMenuItem.cs | 30 ++++++++++++++++++- .../Features/Run/RunMenuItem.tscn | 14 +++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs b/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs index 565aacbd..deb77d17 100644 --- a/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs +++ b/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs @@ -4,8 +4,11 @@ namespace SharpIDE.Godot.Features.Run; -public partial class RunMenuItem : HBoxContainer +public partial class RunMenuItem : Control { + [Signal] + public delegate void PressedEventHandler(); + public SharpIdeProjectModel Project { get; set; } = null!; private Label _label = null!; private Button _runButton = null!; @@ -30,6 +33,31 @@ public override void _Ready() Project.ProjectStartedRunning.Subscribe(OnProjectStartedRunning); Project.ProjectStoppedRunning.Subscribe(OnProjectStoppedRunning); Project.ProjectRunFailed.Subscribe(OnProjectRunFailed); + MouseEntered += HandleMouseEntered; + MouseExited += HandleMouseExited; + } + + public override void _GuiInput(InputEvent @event) + { + if (@event is not InputEventMouseButton iemb) return; + if (iemb is {Pressed: true, ButtonIndex: MouseButton.Left}) + EmitSignalPressed(); + } + + private void HandleMouseEntered() + { + var sb = (StyleBoxFlat)_label.GetThemeStylebox("normal"); + var color = sb.BgColor; + color.A = 255; + sb.BgColor = color; + } + + private void HandleMouseExited() + { + var sb = (StyleBoxFlat)_label.GetThemeStylebox("normal"); + var color = sb.BgColor; + color.A = 0; + sb.BgColor = color; } private async Task OnProjectRunFailed() diff --git a/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn b/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn index 7e890c24..0ed5cedc 100644 --- a/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn +++ b/src/SharpIDE.Godot/Features/Run/RunMenuItem.tscn @@ -6,6 +6,10 @@ [ext_resource type="Texture2D" uid="uid://c7cmou8hipsvc" path="res://Features/Run/Resources/Debug.svg" id="3_cd138"] [ext_resource type="Texture2D" uid="uid://debdmtqgw5dhf" path="res://Features/Run/Resources/Stop.svg" id="3_hxkig"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8lles"] +resource_local_to_scene = true +bg_color = Color(0.4745098, 0.4745098, 0.4745098, 0) + [sub_resource type="Animation" id="Animation_8lles"] resource_name = "BuildingAnimation" length = 0.8 @@ -45,17 +49,17 @@ _data = { } [node name="RunMenuItem" type="HBoxContainer" unique_id=1305347884] -offset_right = 40.0 +offset_right = 181.0 offset_bottom = 40.0 script = ExtResource("1_syj0f") [node name="Label" type="Label" parent="." unique_id=1727711838] layout_mode = 2 -text = "Project Name" - -[node name="Spacer" type="Control" parent="." unique_id=1831433607] -layout_mode = 2 size_flags_horizontal = 3 +size_flags_vertical = 1 +theme_override_styles/normal = SubResource("StyleBoxFlat_8lles") +text = "Project Name" +vertical_alignment = 1 [node name="AnimatedTextureParentControl" type="Control" parent="." unique_id=1016714895] unique_name_in_owner = true From 3c4243bf2bf9523d12d4087e06372f9f225d7100 Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 02:47:24 -0500 Subject: [PATCH 03/14] Created new Custom Run Button UI Created new Custom Run Button UI, to handle Selecting a Startup Project to run, while still offering a Menu to allow launching of multiple projects. --- .../Features/Run/CustomRunButton.cs | 203 ++++++++++++++++++ .../Features/Run/CustomRunButton.cs.uid | 1 + .../Features/Run/CustomRunButton.tscn | 51 +++++ 3 files changed, 255 insertions(+) create mode 100644 src/SharpIDE.Godot/Features/Run/CustomRunButton.cs create mode 100644 src/SharpIDE.Godot/Features/Run/CustomRunButton.cs.uid create mode 100644 src/SharpIDE.Godot/Features/Run/CustomRunButton.tscn diff --git a/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs b/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs new file mode 100644 index 00000000..af3a91f5 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs @@ -0,0 +1,203 @@ +using Godot; +using System; +using SharpIDE.Application.Features.SolutionDiscovery; +using SharpIDE.Godot; +using SharpIDE.Godot.Features.Run; + +public partial class CustomRunButton : Button +{ + [Signal] + public delegate void StartupProjectChangedEventHandler(); + + private readonly PackedScene _runMenuItemScene = ResourceLoader.Load("res://Features/Run/RunMenuItem.tscn"); + private Popup _runMenuPopup = null!; + private VBoxContainer _runOptions = null!; + + private RunOption _currentRunOption = null!; + + public RunOption CurrentRunOption + { + get => _currentRunOption; + set + { + _currentRunOption = value; + Callable.From((name) => Text = name).CallDeferred(_currentRunOption?.Name ?? ""); + } + } + + public List Options = []; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + _runMenuPopup = GetNode("%RunMenuPopup"); + _runOptions = GetNode("%RunOptions"); + UpdateMinimumSize(); + if (CurrentRunOption != null) + Text = CurrentRunOption.Name; + + foreach (var option in Options) + { + AddOption(option.Model); + } + + Pressed += HandlePopupMenu; + + if (GetParent() is not BoxContainer parent) return; + parent.Resized += HandleSizeChanged; + } + + public void AddOption(SharpIdeProjectModel model) => AddOption(model.Name.Value, model); + + public void AddOption(string name, SharpIdeProjectModel model) + { + var ro = new RunOption(name, model); + Options.Add(ro); + var item = _runMenuItemScene.Instantiate(); + item.Project = model; + item.Pressed += () => HandleOptionPressed(ro); + _runOptions.AddChild(item); + UpdateMinimumSize(); + } + + public void RemoveOption(SharpIdeProjectModel model) + { + RunOption foundOption = null!; + foreach (var option in Options.Where(option => option.Model == model)) + { + foreach (var menuItem in _runOptions.GetChildren()) + { + if (menuItem is not RunMenuItem rmi) continue; + if (rmi.Project != model) continue; + rmi.QueueFree(); + break; + } + foundOption = option; + } + + Options.Remove(foundOption); + UpdateMinimumSize(); + } + + public void RemoveOption(RunOption option) + { + foreach (var menuItem in _runOptions.GetChildren()) + { + if (menuItem is not RunMenuItem rmi) continue; + if (rmi.Project != option.Model) continue; + rmi.QueueFree(); + } + + Options.Remove(option); + UpdateMinimumSize(); + } + + public void RemoveOption(string name) + { + RunOption foundOption = Options.Where(option => option.Name == name).FirstOrDefault(); + if (foundOption != null) + RemoveOption(foundOption); + } + + public void SelectOption(SharpIdeProjectModel model) + { + CurrentRunOption = Options.FirstOrDefault(option => option.Model == model)!; + } + + public void SelectOption(string name = "", string filePath = "") + { + if (name == "" && filePath == "") return; + if (name != "") + CurrentRunOption = Options.FirstOrDefault(option => option.Name == name)!; + else + CurrentRunOption = Options.FirstOrDefault(option => option.Model.FilePath == filePath)!; + } + + public void SelectOption(int index) + { + if (index >= 0 && index < Options.Count) + CurrentRunOption = Options[index]; + } + + public void SelectOption(RunOption option) => CurrentRunOption = option; + + public void RunProject() + { + + } + + public void DebugProject() + { + + } + + public void StopProject() + { + + } + + + private void UpdateMinimumSize() + { + var font = GetThemeDefaultFont(); + var size = GetThemeDefaultFontSize(); + var maxWidth = 0f; + foreach (var option in Options) + { + var measure = font.GetStringSize(option.Name, fontSize: size); + if (measure.X + 42 > maxWidth) + maxWidth = measure.X + 42; + } + + CustomMinimumSize = new Vector2(maxWidth, 0); + } + + private void HandleSizeChanged() + { + if (GetParent() is not Control parent) return; + var space = parent.Size; + var popupSize = new Vector2I((int)space.X+3, _runMenuPopup.Size.Y); + _runMenuPopup.MinSize = popupSize; + _runMenuPopup.Size = popupSize; + } + + private void HandlePopupMenu() + { + var popupMenuPosition = GlobalPosition; + const int buttonHeight = 39; + _runMenuPopup.Position = new Vector2I((int)popupMenuPosition.X, (int)popupMenuPosition.Y + buttonHeight); + _runMenuPopup.Popup(); + } + + private void HandleOptionPressed(RunOption option) + { + CurrentRunOption = option; + Text = option.Name; + _runMenuPopup.Hide(); + EmitSignalStartupProjectChanged(); + } +} + + +public class RunOption +{ + public string Name = null!; + public SharpIdeProjectModel Model = null!; + + public RunOption(SharpIdeProjectModel model) + { + Model = model; + Name = model.Name.Value; + } + + public RunOption(string name, SharpIdeProjectModel model) + { + Model = model; + Name = name; + } + + public RunOption() + { + + } +} \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs.uid b/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs.uid new file mode 100644 index 00000000..4c4a4179 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs.uid @@ -0,0 +1 @@ +uid://nwepdo8njw4l diff --git a/src/SharpIDE.Godot/Features/Run/CustomRunButton.tscn b/src/SharpIDE.Godot/Features/Run/CustomRunButton.tscn new file mode 100644 index 00000000..fbe18259 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/CustomRunButton.tscn @@ -0,0 +1,51 @@ +[gd_scene format=3 uid="uid://du1371oogv5dj"] + +[ext_resource type="Script" uid="uid://nwepdo8njw4l" path="res://Features/Run/CustomRunButton.cs" id="1_rlcoe"] + +[sub_resource type="DPITexture" id="DPITexture_l37ox"] +_source = " +" + +[node name="CustomRunButton" type="Button" unique_id=1336831619] +custom_minimum_size = Vector2(143, 0) +offset_right = 143.0 +offset_bottom = 31.0 +text = "" +alignment = 0 +script = ExtResource("1_rlcoe") + +[node name="TextureRect" type="TextureRect" parent="." unique_id=256296248] +layout_mode = 1 +anchors_preset = 6 +anchor_left = 1.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +offset_left = -21.0 +offset_top = -8.0 +offset_right = -5.0 +offset_bottom = 8.0 +grow_horizontal = 0 +grow_vertical = 2 +texture = SubResource("DPITexture_l37ox") + +[node name="RunMenuPopup" type="Popup" parent="." unique_id=1494309304] +unique_name_in_owner = true +oversampling_override = 1.0 +position = Vector2i(0, 37) +size = Vector2i(151, 100) + +[node name="MarginContainer" type="MarginContainer" parent="RunMenuPopup" unique_id=2114816769] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 5 +theme_override_constants/margin_top = 5 +theme_override_constants/margin_right = 5 +theme_override_constants/margin_bottom = 5 + +[node name="RunOptions" type="VBoxContainer" parent="RunMenuPopup/MarginContainer" unique_id=232179440] +unique_name_in_owner = true +layout_mode = 2 From d25f8d62f10d0cfc34c077a4140ed76f9562702e Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 02:49:07 -0500 Subject: [PATCH 04/14] Updated IdeRoot with new Run UI Added new controls for Building, Running, Debugging, and Stopping the Startup project. Implemented logic to handle when there is a Change in the Startup Project, and save it to the IdeSolutionState for the last Startup Project setup. --- src/SharpIDE.Godot/IdeRoot.cs | 31 ++++++++++++++++++++---- src/SharpIDE.Godot/IdeRoot.tscn | 43 +++++++++++++++++++++++++++++---- 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/SharpIDE.Godot/IdeRoot.cs b/src/SharpIDE.Godot/IdeRoot.cs index 1972e64b..c047374e 100644 --- a/src/SharpIDE.Godot/IdeRoot.cs +++ b/src/SharpIDE.Godot/IdeRoot.cs @@ -38,6 +38,8 @@ public partial class IdeRoot : Control private Button _runMenuButton = null!; private Popup _runMenuPopup = null!; private BottomPanelManager _bottomPanelManager = null!; + + private CustomRunButton _projectList = null!; private readonly PackedScene _runMenuItemScene = ResourceLoader.Load("res://Features/Run/RunMenuItem.tscn"); private TaskCompletionSource _nodeReadyTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); @@ -84,6 +86,9 @@ public override void _Ready() _invertedVSplitContainer = GetNode("%InvertedVSplitContainer"); _bottomPanelManager = GetNode("%BottomPanel"); + //_projectList = GetNode("%ProjectList"); + _projectList = GetNode("%ProjectList"); + _runMenuButton.Pressed += OnRunMenuButtonPressed; GodotGlobalEvents.Instance.FileSelected.Subscribe(OnSolutionExplorerPanelOnFileSelected); _openSlnButton.Pressed += () => IdeWindow.PickSolution(); @@ -97,6 +102,7 @@ public override void _Ready() GodotGlobalEvents.Instance.BottomPanelVisibilityChangeRequested.Subscribe(async show => await this.InvokeAsync(() => _invertedVSplitContainer.InvertedSetCollapsed(!show))); GetTree().GetRoot().FocusExited += OnFocusExited; _nodeReadyTcs.SetResult(); + _projectList.StartupProjectChanged += OnStartupProjectChanged; } private async Task OnBuildStarted(BuildStartedFlags flags) => await OnBuildRunningStateChanged(true, flags); @@ -201,20 +207,35 @@ public void SetSlnFilePath(string path) var tasks = solutionModel.AllProjects.Select(p => p.MsBuildEvaluationProjectTask).ToList(); await Task.WhenAll(tasks).ConfigureAwait(false); + var runnableProjects = solutionModel.AllProjects.Where(p => p.IsLoaded && p.IsRunnable).ToList(); + // Startup Project + var cproj = Singletons.AppState.RecentSlns.Single(s => s.FilePath == solutionModel.FilePath).IdeSolutionState.LastStartupProject; + await this.InvokeAsync(() => { - var runMenuPopupVbox = _runMenuPopup.GetNode("MarginContainer/VBoxContainer"); foreach (var project in runnableProjects) { - var runMenuItem = _runMenuItemScene.Instantiate(); - runMenuItem.Project = project; - runMenuPopupVbox.AddChild(runMenuItem); + _projectList.AddOption(project); + if (project.FilePath == cproj) + _projectList.SelectOption(project); } - _runMenuButton.Disabled = false; }); + + if (_projectList.CurrentRunOption == null) + _projectList.SelectOption(0); }); } + + private void OnStartupProjectChanged() + { + var solutionModel = _solutionExplorerPanel.SolutionModel; + var runnableProjects = solutionModel.AllProjects.Where(p => p.IsRunnable).ToList(); + var cproj = _projectList.CurrentRunOption.Model; + if (!runnableProjects.Contains(cproj)) return; + + Singletons.AppState.RecentSlns.Single(s => s.FilePath == solutionModel.FilePath).IdeSolutionState.LastStartupProject = cproj.FilePath; + } public override void _UnhandledKeyInput(InputEvent @event) { diff --git a/src/SharpIDE.Godot/IdeRoot.tscn b/src/SharpIDE.Godot/IdeRoot.tscn index 82f60fc1..0ad46c6a 100644 --- a/src/SharpIDE.Godot/IdeRoot.tscn +++ b/src/SharpIDE.Godot/IdeRoot.tscn @@ -14,8 +14,12 @@ [ext_resource type="PackedScene" uid="uid://bcoytt3bw0gpe" path="res://Features/Run/RunPanel.tscn" id="5_y3aoi"] [ext_resource type="Script" uid="uid://cvvgp42r3nml8" path="res://Features/BottomPanel/BottomPanelManager.cs" id="7_i62lx"] [ext_resource type="PackedScene" uid="uid://c5dlwgcx3ubyp" path="res://Features/CodeEditor/CodeEditorPanel.tscn" id="8_7ptyn"] +[ext_resource type="Texture2D" uid="uid://da8cx83rohk50" path="res://Features/LeftSideBar/Resources/Build.svg" id="8_tmssf"] +[ext_resource type="PackedScene" uid="uid://du1371oogv5dj" path="res://Features/Run/CustomRunButton.tscn" id="9_rl2d5"] [ext_resource type="PackedScene" uid="uid://co6dkhdolriej" path="res://Features/Build/BuildPanel.tscn" id="9_rllbf"] +[ext_resource type="Texture2D" uid="uid://debdmtqgw5dhf" path="res://Features/Run/Resources/Stop.svg" id="10_r1ap7"] [ext_resource type="PackedScene" uid="uid://tqpmww430cor" path="res://Features/Problems/ProblemsPanel.tscn" id="11_b7c1a"] +[ext_resource type="Texture2D" uid="uid://c7cmou8hipsvc" path="res://Features/Run/Resources/Debug.svg" id="11_rl2d5"] [ext_resource type="PackedScene" uid="uid://dkjips8oudqou" path="res://Features/Debug_/DebugPanel.tscn" id="11_s2dv6"] [ext_resource type="PackedScene" uid="uid://8lk0qj233a7p" path="res://Features/Search/SearchInFiles/SearchWindow.tscn" id="13_7ptyn"] [ext_resource type="PackedScene" uid="uid://b0tjuqq3bca5e" path="res://Features/IdeDiagnostics/IdeDiagnosticsPanel.tscn" id="13_woo5i"] @@ -111,19 +115,35 @@ stretch_mode = 5 layout_mode = 2 size_flags_horizontal = 3 -[node name="RunMenuButton" type="Button" parent="VBoxContainer/Panel/HBoxContainer" unique_id=193947069] +[node name="BuildProject" type="Button" parent="VBoxContainer/Panel/HBoxContainer" unique_id=63620535] +unique_name_in_owner = true +custom_minimum_size = Vector2(32, 32) +layout_mode = 2 +size_flags_vertical = 4 +icon = ExtResource("8_tmssf") +expand_icon = true + +[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/Panel/HBoxContainer" unique_id=1654824922] +layout_mode = 2 + +[node name="ProjectList" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer" unique_id=1336831619 instance=ExtResource("9_rl2d5")] +unique_name_in_owner = true +custom_minimum_size = Vector2(120, 0) +layout_mode = 2 +size_flags_vertical = 4 + +[node name="RunMenuButton" type="Button" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer" unique_id=193947069] unique_name_in_owner = true layout_mode = 2 size_flags_vertical = 4 -disabled = true icon = ExtResource("2_8x8ub") icon_alignment = 1 -[node name="RunMenuPopup" type="Popup" parent="VBoxContainer/Panel/HBoxContainer/RunMenuButton" unique_id=92504102] +[node name="RunMenuPopup" type="Popup" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer/RunMenuButton" unique_id=92504102] unique_name_in_owner = true size = Vector2i(151, 100) -[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/Panel/HBoxContainer/RunMenuButton/RunMenuPopup" unique_id=911955929] +[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer/RunMenuButton/RunMenuPopup" unique_id=911955929] anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 @@ -134,8 +154,21 @@ theme_override_constants/margin_top = 5 theme_override_constants/margin_right = 5 theme_override_constants/margin_bottom = 5 -[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Panel/HBoxContainer/RunMenuButton/RunMenuPopup/MarginContainer" unique_id=1372084780] +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer/RunMenuButton/RunMenuPopup/MarginContainer" unique_id=1372084780] +layout_mode = 2 + +[node name="StopButton" type="Button" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer" unique_id=263543484] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_vertical = 4 +icon = ExtResource("10_r1ap7") + +[node name="DebugButton" type="Button" parent="VBoxContainer/Panel/HBoxContainer/HBoxContainer" unique_id=495543979] +unique_name_in_owner = true layout_mode = 2 +size_flags_vertical = 4 +icon = ExtResource("11_rl2d5") [node name="SettingsButton" parent="VBoxContainer/Panel/HBoxContainer" unique_id=1946991037 instance=ExtResource("5_pm5al")] layout_mode = 2 From 60df5cdad1b0adff7644899ce7b051270894d4da Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 21:49:42 -0500 Subject: [PATCH 05/14] Updated RunMenuItem with RunRequested signal Added signal for when the Run Button is pressed, to sync startup project status. --- src/SharpIDE.Godot/Features/Run/RunMenuItem.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs b/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs index deb77d17..0f21520b 100644 --- a/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs +++ b/src/SharpIDE.Godot/Features/Run/RunMenuItem.cs @@ -8,6 +8,9 @@ public partial class RunMenuItem : Control { [Signal] public delegate void PressedEventHandler(); + + [Signal] + public delegate void RunRequestedEventHandler(RunMenuItem sender); public SharpIdeProjectModel Project { get; set; } = null!; private Label _label = null!; @@ -103,6 +106,7 @@ private async void OnStopButtonPressed() private async void OnRunButtonPressed() { SetAttemptingRunState(); + EmitSignalRunRequested(this); await _runService.RunProject(Project).ConfigureAwait(false); } @@ -114,6 +118,7 @@ private async void OnDebugButtonPressed() DebuggerExecutablePath = Singletons.AppState.IdeSettings.DebuggerExecutablePath }; SetAttemptingRunState(); + EmitSignalRunRequested(this); await _runService.RunProject(Project, true, debuggerExecutableInfo).ConfigureAwait(false); } From 5e5bb7415b93eaab347309a66bbb7f3b9f115fb8 Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 21:51:53 -0500 Subject: [PATCH 06/14] Updated signals Renamed signal StartupProjectChanged to ProjectChanged. Added Signal ItemAdded to allow Project signals to be connected up the chain. Added Signal RunRequested to sync UI for Startup Project being launched from menu. Removed empty functions as going a different route for design. --- .../Features/Run/CustomRunButton.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs b/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs index af3a91f5..de4e5233 100644 --- a/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs +++ b/src/SharpIDE.Godot/Features/Run/CustomRunButton.cs @@ -7,7 +7,13 @@ public partial class CustomRunButton : Button { [Signal] - public delegate void StartupProjectChangedEventHandler(); + public delegate void ProjectChangedEventHandler(); + + [Signal] + public delegate void ItemAddedEventHandler(int index); + + [Signal] + public delegate void RunRequestedEventHandler(RunMenuItem item); private readonly PackedScene _runMenuItemScene = ResourceLoader.Load("res://Features/Run/RunMenuItem.tscn"); private Popup _runMenuPopup = null!; @@ -51,13 +57,16 @@ public override void _Ready() public void AddOption(string name, SharpIdeProjectModel model) { + var indx = Options.Count; var ro = new RunOption(name, model); Options.Add(ro); var item = _runMenuItemScene.Instantiate(); item.Project = model; item.Pressed += () => HandleOptionPressed(ro); + item.RunRequested += EmitSignalRunRequested; _runOptions.AddChild(item); UpdateMinimumSize(); + EmitSignalItemAdded(indx); } public void RemoveOption(SharpIdeProjectModel model) @@ -121,22 +130,6 @@ public void SelectOption(int index) public void SelectOption(RunOption option) => CurrentRunOption = option; - public void RunProject() - { - - } - - public void DebugProject() - { - - } - - public void StopProject() - { - - } - - private void UpdateMinimumSize() { var font = GetThemeDefaultFont(); @@ -174,7 +167,7 @@ private void HandleOptionPressed(RunOption option) CurrentRunOption = option; Text = option.Name; _runMenuPopup.Hide(); - EmitSignalStartupProjectChanged(); + EmitSignalProjectChanged(); } } From 1348fb97a7c00a86f4b2904f93baad2eb213078c Mon Sep 17 00:00:00 2001 From: Mario Steele Date: Fri, 1 May 2026 21:53:30 -0500 Subject: [PATCH 07/14] Created StartupProject Control Moved HBoxContainer into StartupProject control, to group together the ProjectList Options, with the Run and Debug button controls for startup project. --- .../Features/Run/StartupProject.cs | 132 ++++++++++++++++++ .../Features/Run/StartupProject.cs.uid | 1 + .../Features/Run/StartupProject.tscn | 104 ++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 src/SharpIDE.Godot/Features/Run/StartupProject.cs create mode 100644 src/SharpIDE.Godot/Features/Run/StartupProject.cs.uid create mode 100644 src/SharpIDE.Godot/Features/Run/StartupProject.tscn diff --git a/src/SharpIDE.Godot/Features/Run/StartupProject.cs b/src/SharpIDE.Godot/Features/Run/StartupProject.cs new file mode 100644 index 00000000..40924057 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Run/StartupProject.cs @@ -0,0 +1,132 @@ +using Godot; +using SharpIDE.Application.Features.Run; +using SharpIDE.Application.Features.SolutionDiscovery; +using SharpIDE.Godot; + +public partial class StartupProject : HBoxContainer +{ + [Signal] + public delegate void ProjectChangedEventHandler(); + + private CustomRunButton _projectList = null!; + private Control _spacer = null!; + private Control _animatedBuilding = null!; + private Button _runButton = null!; + private Button _debugButton = null!; + private Button _stopButton = null!; + private AnimationPlayer _buildingAnim = null!; + + public CustomRunButton ProjectList => _projectList; + + [Inject] private readonly RunService _runService = null!; + + // Called when the node enters the scene tree for the first time. + public override void _Ready() + { + _projectList = GetNode("%ProjectList"); + _spacer = GetNode("%Spacer"); + _animatedBuilding = GetNode("%AnimatedTextureParentControl"); + _runButton = GetNode