From 9ce6f1d26041f5231c38c1d8b9206281df0defd9 Mon Sep 17 00:00:00 2001 From: Tom Pohorily Date: Mon, 2 Mar 2026 23:56:59 -0600 Subject: [PATCH 1/2] Ignore .vscode files in all subfolders --- .gitignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 7597f90d..31e2cffb 100644 --- a/.gitignore +++ b/.gitignore @@ -368,8 +368,8 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd -.vscode/launch.json -.vscode/tasks.json +**/.vscode/launch.json +**/.vscode/tasks.json # Rider .idea/ From aa261639aa1db03d6bbe3e0eca111a3c7a47e95a Mon Sep 17 00:00:00 2001 From: Tom Pohorily Date: Tue, 3 Mar 2026 00:53:46 -0600 Subject: [PATCH 2/2] Add background image with new settings --- .../Features/CodeEditor/CodeEditorPanel.cs | 73 ++++++++++++++ .../CodeEditor/CustomSyntaxHighlighter.cs | 11 ++- .../Features/CodeEditor/SharpIdeCodeEdit.cs | 3 + .../CodeEditor/SharpIdeCodeEdit_Theme.cs | 39 ++++++++ .../Features/IdeSettings/AppState.cs | 4 + .../Features/Settings/SettingsWindow.cs | 70 ++++++++++++- .../Features/Settings/SettingsWindow.tscn | 98 +++++++++++++++++++ src/SharpIDE.Godot/GodotGlobalEvents.cs | 4 + 8 files changed, 299 insertions(+), 3 deletions(-) diff --git a/src/SharpIDE.Godot/Features/CodeEditor/CodeEditorPanel.cs b/src/SharpIDE.Godot/Features/CodeEditor/CodeEditorPanel.cs index d93fe59c..93025e41 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/CodeEditorPanel.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/CodeEditorPanel.cs @@ -21,9 +21,11 @@ public partial class CodeEditorPanel : MarginContainer private PackedScene _sharpIdeCodeEditScene = GD.Load("res://Features/CodeEditor/SharpIdeCodeEdit.tscn"); private TabContainer _tabContainer = null!; private ConcurrentDictionary _debuggerExecutionStopInfoByProject = []; + private TextureRect? _backgroundTextureRect; [Inject] private readonly RunService _runService = null!; [Inject] private readonly SharpIdeMetadataAsSourceService _sharpIdeMetadataAsSourceService = null!; + public override void _Ready() { _tabContainer = GetNode("TabContainer"); @@ -34,6 +36,9 @@ public override void _Ready() tabBar.TabClosePressed += OnTabClosePressed; GlobalEvents.Instance.DebuggerExecutionStopped.Subscribe(OnDebuggerExecutionStopped); GlobalEvents.Instance.ProjectStoppedDebugging.Subscribe(OnProjectStoppedDebugging); + GodotGlobalEvents.Instance.BackgroundImageChanged.Subscribe(OnBackgroundImageChanged); + GodotGlobalEvents.Instance.BackgroundTransparencyChanged.Subscribe(OnBackgroundTransparencyChanged); + InitializeBackgroundImage(); } public override void _GuiInput(InputEvent @event) @@ -242,6 +247,74 @@ await this.InvokeAsync(() => tabForStopInfo.SetLineColour(godotLine); }); } + + private void InitializeBackgroundImage() + { + var imagePath = Singletons.AppState.IdeSettings.BackgroundImagePath; + if (string.IsNullOrEmpty(imagePath)) return; + + UpdateBackgroundImage(imagePath); + } + + private Task OnBackgroundImageChanged(string imagePath) + { + UpdateBackgroundImage(imagePath); + return Task.CompletedTask; + } + + private void UpdateBackgroundImage(string imagePath) + { + // Remove existing background if any + if (_backgroundTextureRect != null) + { + RemoveChild(_backgroundTextureRect); + _backgroundTextureRect.QueueFree(); + _backgroundTextureRect = null; + } + + if (string.IsNullOrEmpty(imagePath)) return; + + // Load the texture + Texture2D? tex = null; + if (File.Exists(imagePath)) + { + var image = Image.LoadFromFile(imagePath); + if (image != null) + { + tex = ImageTexture.CreateFromImage(image); + } + } + + if (tex == null) return; + + _backgroundTextureRect = new TextureRect + { + Texture = tex, + ExpandMode = TextureRect.ExpandModeEnum.IgnoreSize, + StretchMode = TextureRect.StretchModeEnum.KeepAspectCovered, + MouseFilter = MouseFilterEnum.Ignore + }; + + AddChild(_backgroundTextureRect); + MoveChild(_backgroundTextureRect, 0); // Move behind TabContainer + + UpdateBackgroundTransparency(Singletons.AppState.IdeSettings.BackgroundImageTransparency); + } + + private Task OnBackgroundTransparencyChanged(double transparency) + { + UpdateBackgroundTransparency(transparency); + return Task.CompletedTask; + } + + private void UpdateBackgroundTransparency(double transparency) + { + if (_backgroundTextureRect == null) return; + + var color = _backgroundTextureRect.Modulate; + color.A = 1 - (float)transparency; + _backgroundTextureRect.Modulate = color; + } } file static class TabContainerExtensions diff --git a/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs b/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs index bcac3838..e73badf7 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/CustomSyntaxHighlighter.cs @@ -29,6 +29,13 @@ public void UpdateThemeColorCache(LightOrDarkTheme themeType) }; } + private Color ApplyTransparency(Color color) + { + var transparency = (float)Singletons.AppState.IdeSettings.CodeBackgroundTransparency; + color.A = 1f - transparency; + return color; + } + public void SetHighlightingData(ImmutableArray classifiedSpans, ImmutableArray razorClassifiedSpans) { @@ -160,7 +167,7 @@ private Dictionary MapRazorClassifiedSpansToHighlights(int line) var highlightInfo = new Dictionary { - { ColorStringName, GetColorForRazorSpanKind(razorSpan.Kind, razorSpan.CodeClassificationType, razorSpan.VsSemanticRangeType) } + { ColorStringName, ApplyTransparency(GetColorForRazorSpanKind(razorSpan.Kind, razorSpan.CodeClassificationType, razorSpan.VsSemanticRangeType)) } }; highlights[columnIndex] = highlightInfo; @@ -226,7 +233,7 @@ private Dictionary MapClassifiedSpansToHighlights(int line) // Build the highlight entry var highlightInfo = new Dictionary { - { ColorStringName, ClassificationToColorMapper.GetColorForClassification(ColourSetForTheme, classifiedSpans.Single().ClassificationType) } + { ColorStringName, ApplyTransparency(ClassificationToColorMapper.GetColorForClassification(ColourSetForTheme, classifiedSpans.Single().ClassificationType)) } }; highlights[columnIndex] = highlightInfo; diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index d4cc468a..00f710d2 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -89,6 +89,9 @@ public override void _Ready() LinesEditedFrom += OnLinesEditedFrom; GlobalEvents.Instance.SolutionAltered.Subscribe(OnSolutionAltered); GodotGlobalEvents.Instance.TextEditorThemeChanged.Subscribe(UpdateEditorThemeAsync); + GodotGlobalEvents.Instance.BackgroundTransparencyChanged.Subscribe(OnBackgroundTransparencyChangedAsync); + GodotGlobalEvents.Instance.CodeBackgroundTransparencyChanged.Subscribe(OnCodeBackgroundTransparencyChangedAsync); + GodotGlobalEvents.Instance.CurrentLineHighlightColorChanged.Subscribe(OnCurrentLineHighlightColorChangedAsync); SetCodeRegionTags("#region", "#endregion"); //AddGitGutter(); var hScrollBar = GetHScrollBar(); diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs index 9c2cd16c..3f55c040 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs @@ -7,6 +7,7 @@ public partial class SharpIdeCodeEdit { private static readonly StringName ThemeInfoStringName = "ThemeInfo"; private static readonly StringName IsLight1OrDark2StringName = "IsLight1OrDark2"; + private static readonly StringName CurrentLineColorStringName = "current_line_color"; private void UpdateEditorThemeForCurrentTheme() { @@ -25,5 +26,43 @@ private void UpdateEditorTheme(LightOrDarkTheme lightOrDarkTheme) _syntaxHighlighter.UpdateThemeColorCache(lightOrDarkTheme); SyntaxHighlighter = null; SyntaxHighlighter = _syntaxHighlighter; // Reassign to trigger redraw + MakeEditorTransparent(); + ApplyCurrentLineHighlightColor(); + } + + private void MakeEditorTransparent() + { + var codeEditStyle = (StyleBoxFlat)GetThemeStylebox("normal"); + var bgColor = codeEditStyle.BgColor; + bgColor.A = 0f; + codeEditStyle.BgColor = bgColor; + } + + private Task OnBackgroundTransparencyChangedAsync(double transparency) + { + MakeEditorTransparent(); + return Task.CompletedTask; + } + + private Task OnCodeBackgroundTransparencyChangedAsync(double transparency) + { + // Force the syntax highlighter to redraw itself + var syntaxHighlighter = SyntaxHighlighter; + SyntaxHighlighter = null; + SyntaxHighlighter = syntaxHighlighter; + return Task.CompletedTask; + } + + private Task OnCurrentLineHighlightColorChangedAsync(Color color) + { + ApplyCurrentLineHighlightColor(); + return Task.CompletedTask; + } + + private void ApplyCurrentLineHighlightColor() + { + var colorString = Singletons.AppState.IdeSettings.CurrentLineHighlightColor; + var color = new Color(colorString); + AddThemeColorOverride(CurrentLineColorStringName, color); } } \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs b/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs index bf5f7526..3f289177 100644 --- a/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs +++ b/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs @@ -16,6 +16,10 @@ public class IdeSettings public bool DebuggerUseSharpDbg { get; set; } = true; public float UiScale { get; set; } = 1.0f; public LightOrDarkTheme Theme { get; set; } = LightOrDarkTheme.Dark; + public string BackgroundImagePath { get; set; } = ""; + public double BackgroundImageTransparency { get; set; } = 0.7; + public double CodeBackgroundTransparency { get; set; } = 0.05; + public string CurrentLineHighlightColor { get; set; } = "#0f0f0f"; } [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs b/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs index c4dcc87c..a1795aa7 100644 --- a/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs +++ b/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs @@ -1,4 +1,5 @@ using Godot; +using System; using SharpIDE.Godot.Features.IdeSettings; namespace SharpIDE.Godot.Features.Settings; @@ -9,6 +10,12 @@ public partial class SettingsWindow : Window private LineEdit _debuggerFilePathLineEdit = null!; private CheckButton _debuggerUseSharpDbgCheckButton = null!; private OptionButton _themeOptionButton = null!; + private LineEdit _backgroundImageLineEdit = null!; + private Button _backgroundImageSelectBtn = null!; + private HSlider _backgroundImageTransparencySlider = null!; + private HSlider _codeBackgroundTransparencySlider = null!; + private FileDialog _fileDialog = null!; + private ColorPickerButton _currentLineHighlightColorPickerButton = null!; public override void _Ready() { @@ -17,11 +24,30 @@ public override void _Ready() _debuggerFilePathLineEdit = GetNode("%DebuggerFilePathLineEdit"); _debuggerUseSharpDbgCheckButton = GetNode("%DebuggerUseSharpDbgCheckButton"); _themeOptionButton = GetNode("%ThemeOptionButton"); + _backgroundImageLineEdit = GetNode("%BackgroundImageLineEdit"); + _backgroundImageSelectBtn = GetNode