From ccf55440eda31a2619f0ff6f9dc18bcf6a5c40ba Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:16:25 +1000 Subject: [PATCH] set text editor theme on startup and change --- .../Features/CodeEditor/SharpIdeCodeEdit.cs | 3 ++ .../CodeEditor/SharpIdeCodeEdit_Theme.cs | 29 +++++++++++++++++++ .../CodeEditor/SharpIdeCodeEdit_Theme.cs.uid | 1 + .../Features/IdeSettings/AppState.cs | 9 ++++-- .../Features/Settings/SetTheme.cs | 7 +++-- .../Features/Settings/SetTheme.cs.uid | 1 + .../Features/Settings/SettingsWindow.cs | 14 +++++++-- src/SharpIDE.Godot/GodotGlobalEvents.cs | 2 ++ 8 files changed, 58 insertions(+), 8 deletions(-) create mode 100644 src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs create mode 100644 src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs.uid create mode 100644 src/SharpIDE.Godot/Features/Settings/SetTheme.cs.uid diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs index 95ce3f3..96c74b4 100644 --- a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit.cs @@ -63,6 +63,7 @@ public partial class SharpIdeCodeEdit : CodeEdit public override void _Ready() { + UpdateEditorThemeForCurrentTheme(); SyntaxHighlighter = _syntaxHighlighter; _popupMenu = GetNode("CodeFixesMenu"); _aboveCanvasItem = GetNode("%AboveCanvasItem"); @@ -80,6 +81,7 @@ public partial class SharpIdeCodeEdit : CodeEdit SymbolLookup += OnSymbolLookup; LinesEditedFrom += OnLinesEditedFrom; GlobalEvents.Instance.SolutionAltered.Subscribe(OnSolutionAltered); + GodotGlobalEvents.Instance.TextEditorThemeChanged.Subscribe(UpdateEditorThemeAsync); SetCodeRegionTags("#region", "#endregion"); //AddGitGutter(); } @@ -162,6 +164,7 @@ public partial class SharpIdeCodeEdit : CodeEdit _currentFile?.FileDeleted.Unsubscribe(OnFileDeleted); _projectDiagnosticsObserveDisposable?.Dispose(); GlobalEvents.Instance.SolutionAltered.Unsubscribe(OnSolutionAltered); + GodotGlobalEvents.Instance.TextEditorThemeChanged.Unsubscribe(UpdateEditorThemeAsync); if (_currentFile is not null) _openTabsFileManager.CloseFile(_currentFile); } diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs new file mode 100644 index 0000000..9c2cd16 --- /dev/null +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs @@ -0,0 +1,29 @@ +using Godot; +using SharpIDE.Godot.Features.IdeSettings; + +namespace SharpIDE.Godot.Features.CodeEditor; + +public partial class SharpIdeCodeEdit +{ + private static readonly StringName ThemeInfoStringName = "ThemeInfo"; + private static readonly StringName IsLight1OrDark2StringName = "IsLight1OrDark2"; + + private void UpdateEditorThemeForCurrentTheme() + { + var ideTheme = Singletons.AppState.IdeSettings.Theme; + UpdateEditorTheme(ideTheme); + } + + // Only async for the EventWrapper subscription + private Task UpdateEditorThemeAsync(LightOrDarkTheme lightOrDarkTheme) + { + UpdateEditorTheme(lightOrDarkTheme); + return Task.CompletedTask; + } + private void UpdateEditorTheme(LightOrDarkTheme lightOrDarkTheme) + { + _syntaxHighlighter.UpdateThemeColorCache(lightOrDarkTheme); + SyntaxHighlighter = null; + SyntaxHighlighter = _syntaxHighlighter; // Reassign to trigger redraw + } +} \ No newline at end of file diff --git a/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs.uid b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs.uid new file mode 100644 index 0000000..a7f03f1 --- /dev/null +++ b/src/SharpIDE.Godot/Features/CodeEditor/SharpIdeCodeEdit_Theme.cs.uid @@ -0,0 +1 @@ +uid://fk3w6vud5tlx diff --git a/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs b/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs index 0e97bb9..bf5f752 100644 --- a/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs +++ b/src/SharpIDE.Godot/Features/IdeSettings/AppState.cs @@ -1,4 +1,6 @@ -namespace SharpIDE.Godot.Features.IdeSettings; +using System.Text.Json.Serialization; + +namespace SharpIDE.Godot.Features.IdeSettings; public class AppState { @@ -13,9 +15,12 @@ public class IdeSettings public string? DebuggerExecutablePath { get; set; } public bool DebuggerUseSharpDbg { get; set; } = true; public float UiScale { get; set; } = 1.0f; - public string Theme { get; set; } = "Dark"; + public LightOrDarkTheme Theme { get; set; } = LightOrDarkTheme.Dark; } +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum LightOrDarkTheme { Light, Dark } + public record RecentSln { public required string Name { get; set; } diff --git a/src/SharpIDE.Godot/Features/Settings/SetTheme.cs b/src/SharpIDE.Godot/Features/Settings/SetTheme.cs index c78b3d4..e8b3495 100644 --- a/src/SharpIDE.Godot/Features/Settings/SetTheme.cs +++ b/src/SharpIDE.Godot/Features/Settings/SetTheme.cs @@ -1,4 +1,5 @@ using Godot; +using SharpIDE.Godot.Features.IdeSettings; namespace SharpIDE.Godot.Features.Settings; @@ -9,15 +10,15 @@ public static class SetTheme private static readonly Theme DarkTheme = ResourceLoader.Load("uid://epmt8kq6efrs"); private static readonly Color DarkThemeClearColor = new Color("4d4d4d"); - public static void SetIdeTheme(this Node node, string theme) + public static void SetIdeTheme(this Node node, LightOrDarkTheme theme) { var rootWindow = node.GetTree().GetRoot(); - if (theme is "Light") + if (theme is LightOrDarkTheme.Light) { RenderingServer.Singleton.SetDefaultClearColor(LightThemeClearColor); rootWindow.Theme = LightTheme; } - else if (theme is "Dark") + else if (theme is LightOrDarkTheme.Dark) { RenderingServer.Singleton.SetDefaultClearColor(DarkThemeClearColor); rootWindow.Theme = DarkTheme; diff --git a/src/SharpIDE.Godot/Features/Settings/SetTheme.cs.uid b/src/SharpIDE.Godot/Features/Settings/SetTheme.cs.uid new file mode 100644 index 0000000..3ae0c72 --- /dev/null +++ b/src/SharpIDE.Godot/Features/Settings/SetTheme.cs.uid @@ -0,0 +1 @@ +uid://qs50t6nvwar1 diff --git a/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs b/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs index f2e9af9..c4dcc87 100644 --- a/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs +++ b/src/SharpIDE.Godot/Features/Settings/SettingsWindow.cs @@ -1,4 +1,5 @@ using Godot; +using SharpIDE.Godot.Features.IdeSettings; namespace SharpIDE.Godot.Features.Settings; @@ -28,7 +29,7 @@ public partial class SettingsWindow : Window _uiScaleSpinBox.Value = Singletons.AppState.IdeSettings.UiScale; _debuggerFilePathLineEdit.Text = Singletons.AppState.IdeSettings.DebuggerExecutablePath; _debuggerUseSharpDbgCheckButton.ButtonPressed = Singletons.AppState.IdeSettings.DebuggerUseSharpDbg; - var themeOptionIndex = _themeOptionButton.GetOptionIndexOrNullForString(Singletons.AppState.IdeSettings.Theme); + var themeOptionIndex = _themeOptionButton.GetOptionIndexOrNullForString(Singletons.AppState.IdeSettings.Theme.ToString()); if (themeOptionIndex is not null) _themeOptionButton.Selected = themeOptionIndex.Value; } @@ -54,7 +55,14 @@ public partial class SettingsWindow : Window private void OnThemeItemSelected(long index) { var selectedTheme = _themeOptionButton.GetItemText((int)index); - Singletons.AppState.IdeSettings.Theme = selectedTheme; - this.SetIdeTheme(selectedTheme); + var lightOrDarkTheme = selectedTheme switch + { + "Light" => LightOrDarkTheme.Light, + "Dark" => LightOrDarkTheme.Dark, + _ => throw new InvalidOperationException($"Unknown theme selected: {selectedTheme}") + }; + Singletons.AppState.IdeSettings.Theme = lightOrDarkTheme; + this.SetIdeTheme(lightOrDarkTheme); + GodotGlobalEvents.Instance.TextEditorThemeChanged.InvokeParallelFireAndForget(lightOrDarkTheme); } } \ No newline at end of file diff --git a/src/SharpIDE.Godot/GodotGlobalEvents.cs b/src/SharpIDE.Godot/GodotGlobalEvents.cs index 35e7602..a593775 100644 --- a/src/SharpIDE.Godot/GodotGlobalEvents.cs +++ b/src/SharpIDE.Godot/GodotGlobalEvents.cs @@ -2,6 +2,7 @@ using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Godot.Features.BottomPanel; +using SharpIDE.Godot.Features.IdeSettings; namespace SharpIDE.Godot; @@ -13,4 +14,5 @@ public class GodotGlobalEvents public EventWrapper BottomPanelVisibilityChangeRequested { get; } = new(_ => Task.CompletedTask); public EventWrapper FileSelected { get; } = new((_, _) => Task.CompletedTask); public EventWrapper FileExternallySelected { get; } = new((_, _) => Task.CompletedTask); + public EventWrapper TextEditorThemeChanged { get; } = new(_ => Task.CompletedTask); } \ No newline at end of file