diff --git a/Chat/Ui/ChatPanel.cs b/Chat/Ui/ChatPanel.cs index a0dd730..17a2bdb 100644 --- a/Chat/Ui/ChatPanel.cs +++ b/Chat/Ui/ChatPanel.cs @@ -75,6 +75,7 @@ public void Dispose() ViewportResizeNotifier.Instance.OnViewportResized -= OnViewportResized; _model.OnMessageAppended -= OnMessageAppended; + LocManager.Instance.UnsubscribeToLocaleChange(OnLocaleChanged); _panelStyle.Dispose(); _inputStyle.Dispose(); _container.QueueFree(); @@ -461,6 +462,7 @@ private void CreateUi() }; _messageBuffer.AddThemeColorOverride("default_color", Colors.White); _messageBuffer.AddThemeFontSizeOverride("normal_font_size", ChatConfig.FontSize); + StsUiFonts.Apply(_messageBuffer); _vboxLayout.AddChild(_messageBuffer); _messageBuffer.MetaClicked += OnMetaClicked; @@ -489,6 +491,7 @@ private void CreateUi() _inputField.AddThemeColorOverride("font_placeholder_color", ChatConfig.PlaceholderColor); _inputField.AddThemeColorOverride("caret_color", ChatConfig.CaretColor); _inputField.AddThemeFontSizeOverride("font_size", ChatConfig.FontSize); + StsUiFonts.Apply(_inputField); _inputStyle = new StyleBoxFlat { BgColor = ChatConfig.InputBgColor }; _inputField.AddThemeStyleboxOverride("normal", _inputStyle); @@ -552,6 +555,7 @@ internal void Initialize() // 订阅窗口大小变化事件 ViewportResizeNotifier.Instance.OnViewportResized += OnViewportResized; + LocManager.Instance.SubscribeToLocaleChange(OnLocaleChanged); // 注册到 InputCapture,让其放过 ChatPanel 内部的 Alt+Click ItemInputCapture.RegisterBlockingControl(_container); @@ -563,6 +567,11 @@ private void OnViewportResized(Vector2 _) RequestCompletionRefresh(); } + private void OnLocaleChanged() + { + StsUiFonts.Refresh(_container); + } + private void ShowWelcome() { var welcomeText = new LocString("gameplay_ui", "LEMONSPIRE.chat.welcome").GetFormattedText(); diff --git a/Chat/Ui/Completion/ChatCompletionPopupController.cs b/Chat/Ui/Completion/ChatCompletionPopupController.cs index ba43004..cf5d0e3 100644 --- a/Chat/Ui/Completion/ChatCompletionPopupController.cs +++ b/Chat/Ui/Completion/ChatCompletionPopupController.cs @@ -1,5 +1,6 @@ using Godot; using lemonSpire2.Chat.Input.Model; +using lemonSpire2.util.Ui; namespace lemonSpire2.Chat.Ui.Completion; @@ -59,6 +60,7 @@ public ChatCompletionPopupController() SizeFlagsHorizontal = Control.SizeFlags.ExpandFill }; _list.AddThemeFontSizeOverride("font_size", ChatConfig.FontSize); + StsUiFonts.Apply(_list); _scroll.AddChild(_list); _panel.AddChild(_scroll); } diff --git a/PlayerStateEx/OverlayPanel/PlayerOverlayPanel.cs b/PlayerStateEx/OverlayPanel/PlayerOverlayPanel.cs index d5d8dd5..6160cc0 100644 --- a/PlayerStateEx/OverlayPanel/PlayerOverlayPanel.cs +++ b/PlayerStateEx/OverlayPanel/PlayerOverlayPanel.cs @@ -3,6 +3,8 @@ using lemonSpire2.util.Ui; using MegaCrit.Sts2.Core.Combat; using MegaCrit.Sts2.Core.Entities.Players; +using MegaCrit.Sts2.Core.Localization; +using MegaCrit.Sts2.Core.Localization.Fonts; using MegaCrit.Sts2.Core.Nodes; using MegaCrit.Sts2.Core.Platform; using MegaCrit.Sts2.Core.Rooms; @@ -55,6 +57,7 @@ public override void _Ready() { MouseFilter = MouseFilterEnum.Stop; CreateUi(); + LocManager.Instance.SubscribeToLocaleChange(OnLocaleChanged); // 注册到 InputCapture,让其放过 Panel 内部的 Alt+Click ItemInputCapture.RegisterBlockingControl(_panel); @@ -87,6 +90,8 @@ public override void _ExitTree() if (_onViewportResized != null) ViewportResizeNotifier.Instance.OnViewportResized -= _onViewportResized; + LocManager.Instance.UnsubscribeToLocaleChange(OnLocaleChanged); + CombatManager.Instance.CombatSetUp -= OnCombatSetUp; CombatManager.Instance.CombatEnded -= OnCombatEnded; @@ -157,6 +162,7 @@ private void CreateProviderContents() }; sectionTitle.AddThemeColorOverride("font_color", new Color(0.6f, 0.6f, 0.7f)); sectionTitle.AddThemeFontSizeOverride("font_size", 20); + StsUiFonts.Apply(sectionTitle, FontType.Bold); sectionContainer.AddChild(sectionTitle); _contentContainer.AddChild(sectionContainer); @@ -354,6 +360,11 @@ private void OnCloseButtonPressed() QueueFree(); } + private void OnLocaleChanged() + { + StsUiFonts.Refresh(this); + } + private void OnCombatSetUp(CombatState _) { Log.Debug("CombatSetUp, refreshing"); diff --git a/PlayerStateEx/PanelProvider/CardRewardProvider.cs b/PlayerStateEx/PanelProvider/CardRewardProvider.cs index 2fece40..41db7f0 100644 --- a/PlayerStateEx/PanelProvider/CardRewardProvider.cs +++ b/PlayerStateEx/PanelProvider/CardRewardProvider.cs @@ -218,6 +218,7 @@ private static Control CreateCardControl(Player player, CardEntry cardEntry) MouseFilter = Control.MouseFilterEnum.Ignore }; brokenLabel.AddThemeColorOverride("font_color", new Color(0.8f, 0.2f, 0.2f)); + StsUiFonts.Apply(brokenLabel); return brokenLabel; } diff --git a/PlayerStateEx/PanelProvider/HandCardProvider.cs b/PlayerStateEx/PanelProvider/HandCardProvider.cs index 36aa8d2..9b1ae40 100644 --- a/PlayerStateEx/PanelProvider/HandCardProvider.cs +++ b/PlayerStateEx/PanelProvider/HandCardProvider.cs @@ -105,6 +105,7 @@ public void UpdateContent(Player player, Control content) }; countLabel.AddThemeColorOverride("font_color", new Color(0.7f, 0.7f, 0.75f)); countLabel.AddThemeFontSizeOverride("font_size", 16); + StsUiFonts.Apply(countLabel); container.AddChild(countLabel); var groupCards = LemonSpireConfig.GroupHandCards; diff --git a/PlayerStateEx/PanelProvider/ShopProvider.cs b/PlayerStateEx/PanelProvider/ShopProvider.cs index aa0e951..fb1d562 100644 --- a/PlayerStateEx/PanelProvider/ShopProvider.cs +++ b/PlayerStateEx/PanelProvider/ShopProvider.cs @@ -103,6 +103,7 @@ public void UpdateContent(Player player, Control content) MouseFilter = Control.MouseFilterEnum.Ignore }; emptyLabel.AddThemeColorOverride("font_color", new Color(0.5f, 0.5f, 0.5f)); + StsUiFonts.Apply(emptyLabel); container.AddChild(emptyLabel); return; } @@ -195,6 +196,7 @@ private static HBoxContainer CreateGoldRow(Player player) }; goldLabel.AddThemeColorOverride("font_color", StsColors.gold); goldLabel.AddThemeFontSizeOverride("font_size", 16); + StsUiFonts.Apply(goldLabel); row.AddChild(goldLabel); var titleLabel = new Label @@ -204,6 +206,7 @@ private static HBoxContainer CreateGoldRow(Player player) }; titleLabel.AddThemeColorOverride("font_color", new Color(0.6f, 0.6f, 0.65f)); titleLabel.AddThemeFontSizeOverride("font_size", 16); + StsUiFonts.Apply(titleLabel); row.AddChild(titleLabel); return row; @@ -338,6 +341,7 @@ private static Label CreatePriceLabel(Player player, int cost, bool isOnSale = f priceLabel.AddThemeColorOverride("font_color", StsColors.cream); priceLabel.AddThemeFontSizeOverride("font_size", 14); + StsUiFonts.Apply(priceLabel); return priceLabel; } diff --git a/util/Ui/DraggableTitleBar.cs b/util/Ui/DraggableTitleBar.cs index c3c01c5..127f210 100644 --- a/util/Ui/DraggableTitleBar.cs +++ b/util/Ui/DraggableTitleBar.cs @@ -1,4 +1,5 @@ using Godot; +using MegaCrit.Sts2.Core.Localization.Fonts; namespace lemonSpire2.util.Ui; @@ -49,10 +50,12 @@ public override void _Ready() // 内部 HBoxContainer 布局 _titleLabel.AddThemeColorOverride("font_color", new Color(0.9f, 0.9f, 0.95f)); _titleLabel.AddThemeFontSizeOverride("font_size", 20); + StsUiFonts.Apply(_titleLabel, FontType.Bold); _hbox.AddChild(_titleLabel); _closeButton.AddThemeColorOverride("font_color", new Color(0.7f, 0.7f, 0.7f)); _closeButton.AddThemeColorOverride("font_hover_color", new Color(1f, 0.3f, 0.3f)); + StsUiFonts.Apply(_closeButton, FontType.Bold); _hbox.AddChild(_closeButton); AddChild(_hbox); diff --git a/util/Ui/StsUiFonts.cs b/util/Ui/StsUiFonts.cs new file mode 100644 index 0000000..ca2650a --- /dev/null +++ b/util/Ui/StsUiFonts.cs @@ -0,0 +1,91 @@ +using Godot; +using MegaCrit.Sts2.Core.Localization; +using MegaCrit.Sts2.Core.Localization.Fonts; + +namespace lemonSpire2.util.Ui; + +/// +/// Applies the same font resources and locale substitutions used by the game's UI. +/// +public static class StsUiFonts +{ + private const string RegularFontPath = "res://themes/kreon_regular_shared.tres"; + private const string BoldFontPath = "res://themes/kreon_bold_shared.tres"; + private const string ItalicFontPath = "res://themes/bitter_medium_italic_glyph_space_one.tres"; + + private static readonly StringName FontRoleMeta = "lemon_spire_font_role"; + private static readonly StringName Font = "font"; + private static readonly StringName NormalFont = "normal_font"; + private static readonly StringName BoldFont = "bold_font"; + private static readonly StringName ItalicsFont = "italics_font"; + + private static Font? _regularFont; + private static Font? _boldFont; + private static Font? _italicFont; + + public static void Apply(Control control, FontType fontType = FontType.Regular) + { + ArgumentNullException.ThrowIfNull(control); + + control.SetMeta(FontRoleMeta, (int)fontType); + + if (control is RichTextLabel richTextLabel) + { + ApplyRichTextFonts(richTextLabel); + return; + } + + control.AddThemeFontOverride(Font, GetFont(fontType)); + } + + /// + /// Reapplies fonts to controls previously registered through . + /// + public static void Refresh(Node root) + { + ArgumentNullException.ThrowIfNull(root); + + if (root is Control control && control.HasMeta(FontRoleMeta)) + { + var fontType = (FontType)control.GetMeta(FontRoleMeta).AsInt32(); + Apply(control, fontType); + } + + foreach (var child in root.GetChildren()) + Refresh(child); + } + + private static void ApplyRichTextFonts(RichTextLabel label) + { + label.AddThemeFontOverride(NormalFont, GetFont(FontType.Regular)); + label.AddThemeFontOverride(BoldFont, GetFont(FontType.Bold)); + label.AddThemeFontOverride(ItalicsFont, GetFont(FontType.Italic)); + } + + private static Font GetFont(FontType fontType) + { + var locManager = LocManager.Instance; + var substituteFont = locManager is null + ? null + : FontManager.GetSubstituteFont(locManager.Language, fontType); + + return substituteFont ?? GetDefaultFont(fontType); + } + + private static Font GetDefaultFont(FontType fontType) + { + return fontType switch + { + FontType.Regular => _regularFont ??= LoadFont(RegularFontPath), + FontType.Bold => _boldFont ??= LoadFont(BoldFontPath), + FontType.Italic => _italicFont ??= LoadFont(ItalicFontPath), + _ => throw new ArgumentOutOfRangeException(nameof(fontType), fontType, null) + }; + } + + private static Font LoadFont(string path) + { + return ResourceLoader.Load(path, null, ResourceLoader.CacheMode.Reuse) + ?? throw new InvalidOperationException($"Could not load game font resource: {path}"); + } +}