wire up debug panel

This commit is contained in:
Matt Parker
2025-08-29 19:01:06 +10:00
parent 850be02a52
commit 5f9439c9aa
9 changed files with 116 additions and 28 deletions

View File

@@ -12,6 +12,12 @@ public static class GlobalEvents
public static event Func<Task> StartedRunningProject = () => Task.CompletedTask;
public static void InvokeStartedRunningProject() => StartedRunningProject?.InvokeParallelFireAndForget();
public static event Func<SharpIdeProjectModel, Task> ProjectStartedDebugging = _ => Task.CompletedTask;
public static void InvokeProjectStartedDebugging(SharpIdeProjectModel project) => ProjectStartedDebugging?.InvokeParallelFireAndForget(project);
public static event Func<SharpIdeProjectModel, Task> ProjectStoppedDebugging = _ => Task.CompletedTask;
public static void InvokeProjectStoppedDebugging(SharpIdeProjectModel project) => ProjectStoppedDebugging?.InvokeParallelFireAndForget(project);
public static event Func<SharpIdeProjectModel, Task> ProjectStartedRunning = _ => Task.CompletedTask;
public static void InvokeProjectStartedRunning(SharpIdeProjectModel project) => ProjectStartedRunning?.InvokeParallelFireAndForget(project);

View File

@@ -15,9 +15,8 @@ public class RunService
private readonly ConcurrentDictionary<SharpIdeProjectModel, SemaphoreSlim> _projectLocks = [];
public ConcurrentDictionary<SharpIdeFile, List<Breakpoint>> Breakpoints { get; } = [];
private Debugger? _debugger; // TODO: Support multiple debuggers for multiple running projects
public async Task RunProject(SharpIdeProjectModel project)
public async Task RunProject(SharpIdeProjectModel project, bool isDebug = false)
{
var isDebug = true;
Guard.Against.Null(project, nameof(project));
Guard.Against.NullOrWhiteSpace(project.FilePath, nameof(project.FilePath), "Project file path cannot be null or empty.");
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
@@ -92,9 +91,16 @@ public class RunService
project.Running = true;
project.OpenInRunPanel = true;
GlobalEvents.InvokeProjectsRunningChanged();
GlobalEvents.InvokeStartedRunningProject();
GlobalEvents.InvokeProjectStartedRunning(project);
if (isDebug)
{
GlobalEvents.InvokeProjectStartedDebugging(project);
}
else
{
GlobalEvents.InvokeProjectsRunningChanged();
GlobalEvents.InvokeStartedRunningProject();
GlobalEvents.InvokeProjectStartedRunning(project);
}
project.InvokeProjectStartedRunning();
await process.WaitForExitAsync().WaitAsync(project.RunningCancellationTokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
if (project.RunningCancellationTokenSource.IsCancellationRequested)
@@ -107,8 +113,16 @@ public class RunService
project.RunningCancellationTokenSource.Dispose();
project.RunningCancellationTokenSource = null;
project.Running = false;
GlobalEvents.InvokeProjectsRunningChanged();
GlobalEvents.InvokeProjectStoppedRunning(project);
if (isDebug)
{
GlobalEvents.InvokeProjectStoppedDebugging(project);
}
else
{
GlobalEvents.InvokeProjectsRunningChanged();
GlobalEvents.InvokeProjectStoppedRunning(project);
}
project.InvokeProjectStoppedRunning();
Console.WriteLine("Project finished running");
@@ -133,6 +147,11 @@ public class RunService
await _debugger!.StepOver(threadId);
}
public async Task GetInfoAtStopPoint()
{
await _debugger!.GetInfoAtStopPoint();
}
private string GetRunArguments(SharpIdeProjectModel project)
{
var dllFullPath = ProjectEvaluation.GetOutputDllFullPath(project);

View File

@@ -13,27 +13,28 @@ public partial class DebugPanel : Control
[Export]
public Texture2D RunningIcon { get; set; } = null!;
private PackedScene _runPanelTabScene = GD.Load<PackedScene>("res://Features/Run/RunPanelTab.tscn");
private PackedScene _debugPanelTabScene = GD.Load<PackedScene>("res://Features/Debug_/DebugPanelTab.tscn");
public override void _Ready()
{
if (RunningIcon is null) throw new Exception("RunningIcon is null in DebugPanel");
_tabBar = GetNode<TabBar>("%TabBar");
_tabBar.ClearTabs();
//_tabBar.TabClosePressed
_tabBar.TabClicked += OnTabBarTabClicked;
_tabsPanel = GetNode<Panel>("%TabsPanel");
GlobalEvents.ProjectStartedRunning += async projectModel =>
GlobalEvents.ProjectStartedDebugging += async projectModel =>
{
await this.InvokeAsync(() => ProjectStartedRunning(projectModel));
await this.InvokeAsync(() => ProjectStartedDebugging(projectModel));
};
GlobalEvents.ProjectStoppedRunning += async projectModel =>
GlobalEvents.ProjectStoppedDebugging += async projectModel =>
{
await this.InvokeAsync(() => ProjectStoppedRunning(projectModel));
await this.InvokeAsync(() => ProjectStoppedDebugging(projectModel));
};
}
private void OnTabBarTabClicked(long idx)
{
var children = _tabsPanel.GetChildren().OfType<RunPanelTab>().ToList();
var children = _tabsPanel.GetChildren().OfType<DebugPanelTab>().ToList();
foreach (var child in children)
{
child.Visible = false;
@@ -43,9 +44,9 @@ public partial class DebugPanel : Control
tab.Visible = true;
}
public void ProjectStartedRunning(SharpIdeProjectModel projectModel)
public void ProjectStartedDebugging(SharpIdeProjectModel projectModel)
{
var existingRunPanelTab = _tabsPanel.GetChildren().OfType<RunPanelTab>().SingleOrDefault(s => s.Project == projectModel);
var existingRunPanelTab = _tabsPanel.GetChildren().OfType<DebugPanelTab>().SingleOrDefault(s => s.Project == projectModel);
if (existingRunPanelTab != null)
{
_tabBar.SetTabIcon(existingRunPanelTab.TabBarTab, RunningIcon);
@@ -56,21 +57,21 @@ public partial class DebugPanel : Control
return;
}
var runPanelTab = _runPanelTabScene.Instantiate<RunPanelTab>();
runPanelTab.Project = projectModel;
var debugPanelTab = _debugPanelTabScene.Instantiate<DebugPanelTab>();
debugPanelTab.Project = projectModel;
_tabBar.AddTab(projectModel.Name);
var tabIdx = _tabBar.GetTabCount() - 1;
runPanelTab.TabBarTab = tabIdx;
_tabBar.SetTabIcon(runPanelTab.TabBarTab, RunningIcon);
_tabBar.CurrentTab = runPanelTab.TabBarTab;
_tabsPanel.AddChild(runPanelTab);
OnTabBarTabClicked(runPanelTab.TabBarTab);
runPanelTab.StartWritingFromProjectOutput();
debugPanelTab.TabBarTab = tabIdx;
_tabBar.SetTabIcon(debugPanelTab.TabBarTab, RunningIcon);
_tabBar.CurrentTab = debugPanelTab.TabBarTab;
_tabsPanel.AddChild(debugPanelTab);
OnTabBarTabClicked(debugPanelTab.TabBarTab);
debugPanelTab.StartWritingFromProjectOutput();
}
public void ProjectStoppedRunning(SharpIdeProjectModel projectModel)
public void ProjectStoppedDebugging(SharpIdeProjectModel projectModel)
{
var runPanelTab = _tabsPanel.GetChildren().OfType<RunPanelTab>().Single(s => s.Project == projectModel);
_tabBar.SetTabIcon(runPanelTab.TabBarTab, null);
var debugPanelTab = _tabsPanel.GetChildren().OfType<DebugPanelTab>().Single(s => s.Project == projectModel);
_tabBar.SetTabIcon(debugPanelTab.TabBarTab, null);
}
}

View File

@@ -1,6 +1,7 @@
[gd_scene load_steps=2 format=3 uid="uid://dkjips8oudqou"]
[gd_scene load_steps=3 format=3 uid="uid://dkjips8oudqou"]
[ext_resource type="Script" uid="uid://ddyadu54qitw4" path="res://Features/Debug_/DebugPanel.cs" id="1_h4rcc"]
[ext_resource type="Texture2D" uid="uid://0digl54lqm6p" path="res://Features/Run/Resources/Running.svg" id="2_pub1e"]
[node name="DebugPanel" type="Control"]
layout_mode = 3
@@ -10,6 +11,7 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_h4rcc")
RunningIcon = ExtResource("2_pub1e")
[node name="Label" type="Label" parent="."]
layout_mode = 1

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" class="mud-icon-root mud-svg-icon mud-icon-size-medium" focusable="false" viewBox="0 0 24 24" aria-hidden="true" role="img" style="fill: rgb(11, 186, 131);">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M20 8h-2.81c-.45-.78-1.07-1.45-1.82-1.96L17 4.41 15.59 3l-2.17 2.17C12.96 5.06 12.49 5 12 5c-.49 0-.96.06-1.41.17L8.41 3 7 4.41l1.62 1.63C7.88 6.55 7.26 7.22 6.81 8H4v2h2.09c-.05.33-.09.66-.09 1v1H4v2h2v1c0 .34.04.67.09 1H4v2h2.81c1.04 1.79 2.97 3 5.19 3s4.15-1.21 5.19-3H20v-2h-2.09c.05-.33.09-.66.09-1v-1h2v-2h-2v-1c0-.34-.04-.67-.09-1H20V8zm-6 8h-4v-2h4v2zm0-4h-4v-2h4v2z"/>
</svg>

After

Width:  |  Height:  |  Size: 672 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c7cmou8hipsvc"
path="res://.godot/imported/Debug.svg-ae946e7dd0fbc448ad458ff70295702c.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Features/Run/Resources/Debug.svg"
dest_files=["res://.godot/imported/Debug.svg-ae946e7dd0fbc448ad458ff70295702c.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
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -9,6 +9,7 @@ public partial class RunMenuItem : HBoxContainer
public SharpIdeProjectModel Project { get; set; } = null!;
private Label _label = null!;
private Button _runButton = null!;
private Button _debugButton = null!;
private Button _stopButton = null!;
public override void _Ready()
{
@@ -18,6 +19,8 @@ public partial class RunMenuItem : HBoxContainer
_runButton.Pressed += OnRunButtonPressed;
_stopButton = GetNode<Button>("StopButton");
_stopButton.Pressed += OnStopButtonPressed;
_debugButton = GetNode<Button>("DebugButton");
_debugButton.Pressed += OnDebugButtonPressed;
Project.ProjectStartedRunning += OnProjectStartedRunning;
Project.ProjectStoppedRunning += OnProjectStoppedRunning;
}
@@ -27,6 +30,7 @@ public partial class RunMenuItem : HBoxContainer
await this.InvokeAsync(() =>
{
_stopButton.Visible = false;
_debugButton.Visible = true;
_runButton.Visible = true;
});
}
@@ -36,6 +40,7 @@ public partial class RunMenuItem : HBoxContainer
await this.InvokeAsync(() =>
{
_runButton.Visible = false;
_debugButton.Visible = false;
_stopButton.Visible = true;
});
}
@@ -50,4 +55,10 @@ public partial class RunMenuItem : HBoxContainer
GodotGlobalEvents.InvokeBottomPanelTabExternallySelected(BottomPanelType.Run);
await Singletons.RunService.RunProject(Project).ConfigureAwait(false);
}
private async void OnDebugButtonPressed()
{
GodotGlobalEvents.InvokeBottomPanelTabExternallySelected(BottomPanelType.Debug);
await Singletons.RunService.RunProject(Project, true).ConfigureAwait(false);
}
}

View File

@@ -1,7 +1,8 @@
[gd_scene load_steps=4 format=3 uid="uid://d2ewm2lajutpv"]
[gd_scene load_steps=5 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://bkty6563cthj8" path="res://Features/Run/Resources/Run.svg" id="2_hxkig"]
[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"]
[node name="RunMenuItem" type="HBoxContainer"]
@@ -22,6 +23,11 @@ layout_mode = 2
size_flags_vertical = 4
icon = ExtResource("2_hxkig")
[node name="DebugButton" type="Button" parent="."]
layout_mode = 2
size_flags_vertical = 4
icon = ExtResource("3_cd138")
[node name="StopButton" type="Button" parent="."]
visible = false
layout_mode = 2

View File

@@ -18,6 +18,7 @@ public partial class RunPanel : Control
private PackedScene _runPanelTabScene = GD.Load<PackedScene>("res://Features/Run/RunPanelTab.tscn");
public override void _Ready()
{
if (RunningIcon is null) throw new Exception("RunningIcon is null in RunPanel");
_tabBar = GetNode<TabBar>("%TabBar");
_tabBar.ClearTabs();
//_tabBar.TabClosePressed