Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 85 additions & 34 deletions src/SharpIDE.Godot/Features/CodeEditor/CodeEditorPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public partial class CodeEditorPanel : MarginContainer
public SharpIdeSolutionModel Solution { get; set; } = null!;
private PackedScene _sharpIdeCodeEditScene = GD.Load<PackedScene>("res://Features/CodeEditor/SharpIdeCodeEdit.tscn");
private TabContainer _tabContainer = null!;
private TextureProgressBar _loadingSpinner = null!;
private Tween? _loadingSpinnerTween;
private ConcurrentDictionary<SharpIdeProjectModel, ExecutionStopInfo> _debuggerExecutionStopInfoByProject = [];

[Inject] private readonly RunService _runService = null!;
Expand All @@ -34,6 +36,7 @@ public override void _Ready()
tabBar.TabCloseDisplayPolicy = TabBar.CloseButtonDisplayPolicy.ShowAlways;
tabBar.TabClosePressed += OnTabClosePressed;
tabBar.TabRmbClicked += OnTabRmbClicked;
_loadingSpinner = GetNode<TextureProgressBar>("%LoadingSpinner");
GlobalEvents.Instance.DebuggerExecutionStopped.Subscribe(OnDebuggerExecutionStopped);
GlobalEvents.Instance.ProjectStoppedDebugging.Subscribe(OnProjectStoppedDebugging);
}
Expand Down Expand Up @@ -155,52 +158,100 @@ private void RecordNavigationToNextSelectedTab(List<SharpIdeCodeEditContainer> a
}
}

private void ShowLoadingSpinner()
{
if (_loadingSpinner.Visible) return;

_loadingSpinnerTween = GetTree().CreateTween().SetLoops();
_loadingSpinnerTween.TweenProperty(_loadingSpinner, "radial_initial_angle", 360.0, 1.0).AsRelative();
_loadingSpinner.Show();
}

private void HideLoadingSpinner()
{
if (!_loadingSpinner.Visible) return;

_loadingSpinnerTween?.Kill();
_loadingSpinner.Hide();
}

public async Task AddSharpIdeFiles(IReadOnlyList<SharpIdeFile> files)
{
if (files.Count <= 0) return;

await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);

if (_tabContainer.GetTabCount() <= 0)
await this.InvokeAsync(ShowLoadingSpinner);

var newTabs = files.Select(file =>
{
var newTab = _sharpIdeCodeEditScene.Instantiate<SharpIdeCodeEditContainer>();
newTab.CodeEdit.Solution = Solution;

return (Tab: newTab, File: file);
})
.ToList();

await this.InvokeAsync(() =>
{
HideLoadingSpinner();

foreach (var newTab in newTabs)
{
_tabContainer.AddChild(newTab.Tab);
var newTabIndex = _tabContainer.GetTabCount() - 1;
_tabContainer.SetIconsForFileExtension(newTab.File, newTabIndex);
_tabContainer.SetTabTitle(newTabIndex, newTab.File.Name.Value);
_tabContainer.SetTabTooltip(newTabIndex, newTab.File.Path);

newTab.File.FileDeleted.Subscribe(async () => { await this.InvokeAsync(() => { CloseTabs([newTab.Tab]); }); });

var nameChanged = newTab.File.Name.Skip(1).Select(name => (name, newTab.File.IsDirty.Value));
var dirtyChanged = newTab.File.IsDirty.Skip(1).Select(isDirty => (newTab.File.Name.Value, isDirty));

nameChanged.Merge(dirtyChanged)
.SubscribeOnThreadPool()
.ObserveOnThreadPool()
.SubscribeAwait(async (x, ct) =>
{
var (name, isDirty) = x;
await UpdateTabFileName(newTab.Tab.GetIndex(), name, isDirty);
})
.AddTo(newTab.Tab); // needs to be on ui thread
}
});

foreach (var newTab in newTabs)
{
await newTab.Tab.CodeEdit.SetSharpIdeFile(newTab.File, fileLinePosition: null);
}
}

public async Task SetSharpIdeFile(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition)
{
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
var existingTab = await this.InvokeAsync(() => _tabContainer.GetChildren().OfType<SharpIdeCodeEditContainer>().FirstOrDefault(t => t.CodeEdit.SharpIdeFile == file));
if (existingTab is not null)
{
var existingTabIndex = existingTab.GetIndex();
await this.InvokeAsync(() =>
{
_tabContainer.CurrentTab = existingTabIndex;
if (fileLinePosition is not null) existingTab.CodeEdit.SetFileLinePosition(fileLinePosition.Value);
});
await SetCurrentSharpIdeFile(file, fileLinePosition);
return;
}
var newTab = _sharpIdeCodeEditScene.Instantiate<SharpIdeCodeEditContainer>();
newTab.CodeEdit.Solution = Solution;

await AddSharpIdeFiles([file]);
await SetCurrentSharpIdeFile(file, fileLinePosition);
}

private async Task SetCurrentSharpIdeFile(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition)
{
await this.InvokeAsync(() =>
{
_tabContainer.AddChild(newTab);
var newTabIndex = _tabContainer.GetTabCount() - 1;
_tabContainer.SetIconsForFileExtension(file, newTabIndex);
_tabContainer.SetTabTitle(newTabIndex, file.Name.Value);
_tabContainer.SetTabTooltip(newTabIndex, file.Path);
_tabContainer.CurrentTab = newTabIndex;

file.FileDeleted.Subscribe(async () =>
{
await this.InvokeAsync(() =>
{
CloseTabs([newTab]);
});
});
var tab = _tabContainer.GetChildren().OfType<SharpIdeCodeEditContainer>().FirstOrDefault(t => t.CodeEdit.SharpIdeFile == file);
if (tab is null) return;

var nameChanged = file.Name.Skip(1).Select(name => (name, file.IsDirty.Value));
var dirtyChanged = file.IsDirty.Skip(1).Select(isDirty => (file.Name.Value, isDirty));

nameChanged.Merge(dirtyChanged).SubscribeOnThreadPool().ObserveOnThreadPool()
.SubscribeAwait(async (x, ct) =>
{
var (name, isDirty) = x;
await UpdateTabFileName(newTab.GetIndex(), name, isDirty);
}, configureAwait: false)
.AddTo(newTab); // needs to be on ui thread
_tabContainer.CurrentTab = tab.GetIndex();
if (fileLinePosition.HasValue) tab.CodeEdit.SetFileLinePosition(fileLinePosition.Value);
});

await newTab.CodeEdit.SetSharpIdeFile(file, fileLinePosition);
}

private async Task UpdateTabFileName(int tabIndex, string name, bool isDirty)
Expand Down
14 changes: 14 additions & 0 deletions src/SharpIDE.Godot/Features/CodeEditor/CodeEditorPanel.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[ext_resource type="Script" uid="uid://cy7erscaagrtj" path="res://Features/CodeEditor/CodeEditorPanel.cs" id="1_eraxv"]
[ext_resource type="PackedScene" uid="uid://cinaqbdghcvoi" path="res://Features/CodeEditor/SharpIdeCodeEdit.tscn" id="1_y4okr"]
[ext_resource type="Texture2D" uid="uid://do0edciarrnp0" path="res://Features/SolutionExplorer/Resources/CsharpFile.svg" id="2_dbtmr"]
[ext_resource type="Texture2D" uid="uid://qqpwpn4f6r4j" path="res://Features/CodeEditor/Resources/LoadingSpinner.svg" id="3_dbtmr"]

[node name="CodeEditorPanel" type="MarginContainer" unique_id=154728753]
anchors_preset = 15
Expand All @@ -25,3 +26,16 @@ drag_to_rearrange_enabled = true
[node name="SharpIdeCodeEdit" parent="TabContainer" unique_id=170225652 instance=ExtResource("1_y4okr")]
layout_mode = 2
metadata/_tab_index = 0

[node name="LoadingSpinner" type="TextureProgressBar" parent="." unique_id=33022366]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(50, 50)
layout_mode = 2
size_flags_horizontal = 4
size_flags_vertical = 4
value = 100.0
fill_mode = 4
radial_fill_degrees = 180.0
nine_patch_stretch = true
texture_progress = ExtResource("3_dbtmr")
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[remap]

importer="svg"
type="DPITexture"
uid="uid://qqpwpn4f6r4j"
path="res://.godot/imported/LoadingSpinner.svg-678c8f11ed23b35cd2310c9f8c60f1fa.dpitex"

[deps]

source_file="res://Features/CodeEditor/Resources/LoadingSpinner.svg"
dest_files=["res://.godot/imported/LoadingSpinner.svg-678c8f11ed23b35cd2310c9f8c60f1fa.dpitex"]

[params]

base_scale=1.0
saturation=1.0
color_map={}
compress=true
5 changes: 1 addition & 4 deletions src/SharpIDE.Godot/IdeRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,7 @@ public void SetSlnFilePath(string path)
_ = Task.GodotRun(async () =>
{
// Preserves order of tabs
foreach (var (file, linePosition, isSelected) in filesToOpen)
{
await GodotGlobalEvents.Instance.FileExternallySelected.InvokeParallelAsync(file, linePosition);
}
await _codeEditorPanel.AddSharpIdeFiles(filesToOpen.Select(file => file.file).ToList());
_navigationHistoryService.StartRecording();
// Select the selected tab
var selectedFile = filesToOpen.SingleOrDefault(f => f.isSelected);
Expand Down