add sln picker

This commit is contained in:
Matt Parker
2025-09-30 18:21:15 +10:00
parent e10ca304ac
commit 4473ab17a6
10 changed files with 149 additions and 15 deletions

View File

@@ -45,6 +45,11 @@ public partial class BottomPanelManager : Panel
GodotGlobalEvents.BottomPanelTabSelected += OnBottomPanelTabSelected;
}
public override void _ExitTree()
{
GodotGlobalEvents.BottomPanelTabSelected -= OnBottomPanelTabSelected;
}
private async Task OnBottomPanelTabSelected(BottomPanelType? type)
{
await this.InvokeAsync(() =>

View File

@@ -0,0 +1,26 @@
using Godot;
namespace SharpIDE.Godot.Features.SlnPicker;
public partial class SlnPicker : Control
{
private FileDialog _fileDialog = null!;
public override void _Ready()
{
_fileDialog = GetNode<FileDialog>("%FileDialog");
}
public async Task<string?> GetSelectedSolutionPath()
{
var tcs = new TaskCompletionSource<string?>(TaskCreationOptions.RunContinuationsAsynchronously);
await this.InvokeAsync(() =>
{
_fileDialog.FileSelected += path => tcs.SetResult(path);
_fileDialog.Canceled += () => tcs.SetResult(null);
_fileDialog.PopupCentered();
});
var selectedPath = await tcs.Task;
return selectedPath;
}
}

View File

@@ -0,0 +1 @@
uid://bjvwb6jg6jpsi

View File

@@ -0,0 +1,21 @@
[gd_scene load_steps=2 format=3 uid="uid://cwvhbsd1mdl2x"]
[ext_resource type="Script" uid="uid://bjvwb6jg6jpsi" path="res://Features/SlnPicker/SlnPicker.cs" id="1_ciq0g"]
[node name="SlnPicker" 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_ciq0g")
[node name="FileDialog" type="FileDialog" parent="."]
unique_name_in_owner = true
oversampling_override = 1.0
title = "Open a File"
file_mode = 0
access = 2
filters = PackedStringArray("*.sln,*.slnx;Solution Files")
use_native_dialog = true

View File

@@ -15,6 +15,7 @@ namespace SharpIDE.Godot;
public partial class IdeRoot : Control
{
public IdeWindow IdeWindow { get; set; } = null!;
private Button _openSlnButton = null!;
private Button _buildSlnButton = null!;
private FileDialog _fileDialog = null!;
@@ -28,11 +29,9 @@ public partial class IdeRoot : Control
private BottomPanelManager _bottomPanelManager = null!;
private readonly PackedScene _runMenuItemScene = ResourceLoader.Load<PackedScene>("res://Features/Run/RunMenuItem.tscn");
private TaskCompletionSource _nodeReadyTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
public override void _Ready()
{
MSBuildLocator.RegisterDefaults();
GodotServiceDefaults.AddServiceDefaults();
_openSlnButton = GetNode<Button>("%OpenSlnButton");
_buildSlnButton = GetNode<Button>("%BuildSlnButton");
_runMenuPopup = GetNode<Popup>("%RunMenuPopup");
@@ -47,11 +46,12 @@ public partial class IdeRoot : Control
_runMenuButton.Pressed += OnRunMenuButtonPressed;
GodotGlobalEvents.FileSelected += OnSolutionExplorerPanelOnFileSelected;
_fileDialog.FileSelected += OnSlnFileSelected;
_openSlnButton.Pressed += () => _fileDialog.Visible = true;
_fileDialog.FileSelected += SetSlnFilePath;
_openSlnButton.Pressed += IdeWindow.PickSolution;
_buildSlnButton.Pressed += OnBuildSlnButtonPressed;
GodotGlobalEvents.BottomPanelVisibilityChangeRequested += async show => await this.InvokeAsync(() => _invertedVSplitContainer.InvertedSetCollapsed(!show));
OnSlnFileSelected(@"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\BlazorCodeBreaker.slnx");
_nodeReadyTcs.SetResult();
//OnSlnFileSelected(@"C:\Users\Matthew\Documents\Git\BlazorCodeBreaker\BlazorCodeBreaker.slnx");
}
private void OnRunMenuButtonPressed()
@@ -73,18 +73,23 @@ public partial class IdeRoot : Control
await _codeEditorPanel.SetSharpIdeFile(file, fileLinePosition);
}
private void OnSlnFileSelected(string path)
public void SetSlnFilePath(string path)
{
_ = Task.GodotRun(async () =>
{
GD.Print($"Selected: {path}");
var solutionModel = await VsPersistenceMapper.GetSolutionModel(path);
await _nodeReadyTcs.Task;
_solutionExplorerPanel.SolutionModel = solutionModel;
_codeEditorPanel.Solution = solutionModel;
_bottomPanelManager.Solution = solutionModel;
_searchWindow.Solution = solutionModel;
Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred();
RoslynAnalysis.StartSolutionAnalysis(solutionModel);
var infraProject = solutionModel.AllProjects.SingleOrDefault(s => s.Name == "WebUi");
var diFile = infraProject?.Folders.Single(s => s.Name == "Pages").Files.Single(s => s.Name == "TestPage.razor");
if (diFile != null) await this.InvokeDeferredAsync(() => GodotGlobalEvents.InvokeFileExternallySelected(diFile));
var tasks = solutionModel.AllProjects.Select(p => p.MsBuildEvaluationProjectTask).ToList();
await Task.WhenAll(tasks).ConfigureAwait(false);
@@ -100,13 +105,6 @@ public partial class IdeRoot : Control
}
_runMenuButton.Disabled = false;
});
var infraProject = solutionModel.AllProjects.Single(s => s.Name == "WebUi");
var diFile = infraProject.Folders.Single(s => s.Name == "Pages").Files.Single(s => s.Name == "TestPage.razor");
await this.InvokeDeferredAsync(() => GodotGlobalEvents.InvokeFileExternallySelected(diFile));
//var runnableProject = solutionModel.AllProjects.First(s => s.IsRunnable);
//await this.InvokeAsync(() => _runPanel.NewRunStarted(runnableProject));
});
}

View File

@@ -0,0 +1,63 @@
using Ardalis.GuardClauses;
using Godot;
using Microsoft.Build.Locator;
using Microsoft.Extensions.Hosting;
using SharpIDE.Godot.Features.SlnPicker;
namespace SharpIDE.Godot;
/// <summary>
/// Used to hold either the main IDE scene or the solution picker scene
/// </summary>
public partial class IdeWindow : Control
{
private readonly PackedScene _solutionPickerScene = ResourceLoader.Load<PackedScene>("res://Features/SlnPicker/SlnPicker.tscn");
private readonly PackedScene _ideRootScene = ResourceLoader.Load<PackedScene>("res://IdeRoot.tscn");
private IdeRoot? _ideRoot;
private SlnPicker? _slnPicker;
public override void _Ready()
{
MSBuildLocator.RegisterDefaults();
GodotServiceDefaults.AddServiceDefaults();
PickSolution();
}
public void PickSolution()
{
if (_slnPicker is not null) throw new InvalidOperationException("Solution picker is already active");
_slnPicker = _solutionPickerScene.Instantiate<SlnPicker>();
AddChild(_slnPicker);
_ = Task.GodotRun(async () =>
{
var slnPathTask = _slnPicker.GetSelectedSolutionPath();
var ideRoot = _ideRootScene.Instantiate<IdeRoot>();
ideRoot.IdeWindow = this;
var slnPath = await slnPathTask;
if (slnPath is null)
{
ideRoot.QueueFree();
_slnPicker.QueueFree();
_slnPicker = null;
return;
}
ideRoot.SetSlnFilePath(slnPath);
await this.InvokeAsync(() =>
{
RemoveChild(_slnPicker);
_slnPicker.QueueFree();
_slnPicker = null;
if (_ideRoot is not null)
{
RemoveChild(_ideRoot);
_ideRoot.QueueFree();
}
_ideRoot = ideRoot;
AddChild(ideRoot);
});
});
}
}

View File

@@ -0,0 +1 @@
uid://bul4kkrg1yhqx

View File

@@ -0,0 +1,12 @@
[gd_scene load_steps=2 format=3 uid="uid://b70jhun5a4las"]
[ext_resource type="Script" uid="uid://bul4kkrg1yhqx" path="res://IdeWindow.cs" id="1_3do1e"]
[node name="IdeWindow" 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_3do1e")

View File

@@ -36,6 +36,13 @@ public static class NodeExtensions
{
extension(Node node)
{
public void ClearChildren()
{
foreach (var child in node.GetChildren())
{
child.QueueFree();
}
}
public Task<T> InvokeAsync<T>(Func<T> workItem)
{
var taskCompletionSource = new TaskCompletionSource<T>(TaskCreationOptions.RunContinuationsAsynchronously);

View File

@@ -11,7 +11,7 @@ config_version=5
[application]
config/name="SharpIDE.Godot"
run/main_scene="uid://b2oniigcp5ew5"
run/main_scene="uid://b70jhun5a4las"
config/features=PackedStringArray("4.5", "C#", "Forward Plus")
run/max_fps=157
run/low_processor_mode=true