diff --git a/locale/catalog_export_en.properties b/locale/catalog_export_en.properties new file mode 100644 index 0000000..9f3c41a --- /dev/null +++ b/locale/catalog_export_en.properties @@ -0,0 +1,2 @@ +# Catalog export tool — extra keys +mods/catalog_select_mod = Select a mod: diff --git a/locale/catalog_export_ru.properties b/locale/catalog_export_ru.properties new file mode 100644 index 0000000..9c721f3 --- /dev/null +++ b/locale/catalog_export_ru.properties @@ -0,0 +1,2 @@ +# Catalog export tool — дополнительные ключи +mods/catalog_select_mod = Выберите мод: diff --git a/locale/ui_en.properties b/locale/ui_en.properties index 0e624d4..9ea2844 100644 --- a/locale/ui_en.properties +++ b/locale/ui_en.properties @@ -86,6 +86,8 @@ tools/scenario_editor/unsaved_switch_text = You have unsaved changes. Switch fil tools/unsaved_title = Unsaved changes tools/unsaved_text = You have unsaved changes. Close without saving? +mods/catalog_select_mod = Select a mod: + tools/scenario_editor/play = ▶ Play tools/scenario_editor/play_tooltip = Run scenario in test mode tools/scenario_editor/stop = ⏹ Stop diff --git a/manifest.json b/manifest.json index b31c28f..c2f2b18 100644 --- a/manifest.json +++ b/manifest.json @@ -4,9 +4,9 @@ "version": "1.0.0", "author": "ct", "description": "Development tools", - "category": "tools", + "category": "tools, editor", "sources": [ "src/**/*.cs" ], - "depends": ["core>=1"] + "depends": ["visual_novel_core>=1"] } diff --git a/src/CatalogExport/CatalogExportToolProvider.cs b/src/CatalogExport/CatalogExportToolProvider.cs new file mode 100644 index 0000000..042575e --- /dev/null +++ b/src/CatalogExport/CatalogExportToolProvider.cs @@ -0,0 +1,23 @@ +using Cthangover.Core.UI.Tool; +using Godot; + +namespace Cthangover.Core.UI.Tool.CatalogExport +{ + /// + /// Registers the catalog export window as a developer tool. Moved here from + /// the old mods menu when mod management UI was delegated to the + /// mod_manager mod — exporting a catalog entry is an authoring task, + /// not part of day-to-day mod management. + /// + public class CatalogExportToolProvider : IToolProvider + { + /// Unique tool identifier. + public string Id => "catalog_export"; + + /// Translation key for the tool's display name. + public string LocaleKey => "mods/catalog_title"; + + /// Creates the export window instance. + public Window CreateWindow() => new CatalogExportWindow(); + } +} diff --git a/src/CatalogExport/CatalogExportWindow.cs b/src/CatalogExport/CatalogExportWindow.cs new file mode 100644 index 0000000..c009ad9 --- /dev/null +++ b/src/CatalogExport/CatalogExportWindow.cs @@ -0,0 +1,499 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Cthangover.Core.Mods; +using Cthangover.Core.UI.Theming; +using Cthangover.Core.UI.Tool; +using Cthangover.Core.Utils; +using Godot; + +namespace Cthangover.Core.UI.Tool.CatalogExport +{ + /// + /// Developer tool that builds a catalog entry (JSON for the online mod + /// catalog) from a locally installed mod's manifest. Ported out of the old + /// mods menu so that mod-management UI can live in the mod_manager + /// mod while this authoring utility stays a tool. + /// + /// The window lets the author pick any discovered mod, fill in the + /// catalog-only metadata (download URL, category, tags, changelog, status, + /// minimum game version, icon URL, localized names/descriptions), preview + /// the resulting JSON, copy it to the clipboard, or export it next to the + /// mod's zip via . + /// + public partial class CatalogExportWindow : ToolWindow + { + private static readonly string[] Statuses = { "stable", "beta", "deprecated", "archived" }; + private static readonly CategoryType[] Categories = Enum.GetValues(); + + private OptionButton _modSelect; + private VBoxContainer _formHost; + + private ModInfo _modInfo; + private ModManifest _manifest; + private LineEdit _urlEdit; + private ItemList _categoryList; + private LineEdit _tagsEdit; + private TextEdit _changelogEdit; + private OptionButton _statusOpt; + private LineEdit _minGameVerEdit; + private LineEdit _iconUrlEdit; + private VBoxContainer _localeContainer; + private TextEdit _previewEdit; + + public CatalogExportWindow() : base("mods/catalog_title") + { + Size = new Vector2I(620, 720); + CthangoverTheme.Apply(this); + + var vbox = CreateFillContainer(); + vbox.AddThemeConstantOverride("separation", 8); + AddChild(vbox); + + var selectRow = new HBoxContainer(); + selectRow.AddThemeConstantOverride("separation", 8); + var selectLabel = new Label { Text = TranslationServer.Translate("mods/catalog_select_mod") }; + selectLabel.AddThemeColorOverride("font_color", UiTokens.Muted); + selectRow.AddChild(selectLabel); + + _modSelect = new OptionButton + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + _modSelect.ItemSelected += OnModSelected; + selectRow.AddChild(_modSelect); + vbox.AddChild(selectRow); + + var scroll = new ScrollContainer + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + SizeFlagsVertical = Control.SizeFlags.ExpandFill, + HorizontalScrollMode = ScrollContainer.ScrollMode.Disabled, + }; + vbox.AddChild(scroll); + + _formHost = new VBoxContainer + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + _formHost.AddThemeConstantOverride("separation", 8); + scroll.AddChild(_formHost); + + PopulateModList(); + } + + private void PopulateModList() + { + var ordered = ModRegistry.Instance.GetOrderedModIds(filterDisabled: false); + foreach (var id in ordered) + { + var info = ModRegistry.Instance.GetMod(id); + if (info == null) + continue; + + _modSelect.AddItem(info.DisplayTitle); + _modSelect.SetItemMetadata(_modSelect.ItemCount - 1, id); + } + + if (_modSelect.ItemCount > 0) + { + _modSelect.Selected = 0; + BuildForm(_modSelect.GetItemMetadata(0).AsString()); + } + } + + private void OnModSelected(long index) + { + BuildForm(_modSelect.GetItemMetadata((int)index).AsString()); + } + + private void BuildForm(string modId) + { + foreach (var child in _formHost.GetChildren()) + child.QueueFree(); + + _modInfo = ModRegistry.Instance.GetMod(modId) as ModInfo; + _manifest = _modInfo?.Manifest; + if (_modInfo == null || _manifest == null) + return; + + AddSectionLabel(_formHost, "Mod Info"); + AddReadOnlyField(_formHost, "mods/id", _modInfo.Id); + AddReadOnlyField(_formHost, "mods/name", _modInfo.DisplayTitle); + AddReadOnlyField(_formHost, "mods/version", _modInfo.Version ?? "—"); + AddReadOnlyField(_formHost, "mods/author", _modInfo.Author ?? "—"); + + AddSectionLabel(_formHost, "Catalog Metadata"); + _urlEdit = AddLineEdit(_formHost, "mods/catalog_download_url"); + + _categoryList = new ItemList + { + CustomMinimumSize = new Vector2(0, 130), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + SelectMode = ItemList.SelectModeEnum.Toggle, + }; + var preselected = _manifest.Category ?? CategoryType.Other; + for (var i = 0; i < Categories.Length; i++) + { + _categoryList.AddItem(GetCategoryDisplayName(Categories[i])); + if ((preselected & Categories[i]) != 0) + _categoryList.Select(i, false); + } + AddLabeledControl(_formHost, "mods/catalog_category", _categoryList); + + _tagsEdit = AddLineEdit(_formHost, "mods/catalog_tags"); + + _changelogEdit = new TextEdit + { + CustomMinimumSize = new Vector2(0, 60), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + WrapMode = TextEdit.LineWrappingMode.Boundary, + }; + AddLabeledControl(_formHost, "mods/catalog_changelog", _changelogEdit); + + _statusOpt = AddOptionButton(_formHost, "mods/catalog_status", Statuses); + + _minGameVerEdit = AddLineEdit(_formHost, "mods/catalog_min_game_version"); + _iconUrlEdit = AddLineEdit(_formHost, "mods/catalog_icon_url"); + + AddSectionLabel(_formHost, "JSON Preview"); + _previewEdit = new TextEdit + { + CustomMinimumSize = new Vector2(0, 160), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + Editable = false, + WrapMode = TextEdit.LineWrappingMode.None, + }; + _previewEdit.AddThemeFontSizeOverride("font_size", UiTokens.FontSmall); + _formHost.AddChild(_previewEdit); + + var btnRow = new HBoxContainer + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + btnRow.AddThemeConstantOverride("separation", 8); + + var copyBtn = new Button + { + Text = TranslationServer.Translate("mods/catalog_copy"), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + copyBtn.Pressed += OnCopyPressed; + btnRow.AddChild(copyBtn); + + var exportBtn = new Button + { + Text = TranslationServer.Translate("mods/catalog_export_file"), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + exportBtn.Pressed += OnExportPressed; + btnRow.AddChild(exportBtn); + + var closeBtn = new Button + { + Text = TranslationServer.Translate("mods/catalog_close"), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + closeBtn.Pressed += () => QueueFree(); + btnRow.AddChild(closeBtn); + + _formHost.AddChild(btnRow); + + AddSectionLabel(_formHost, "Locales"); + _localeContainer = new VBoxContainer + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + _localeContainer.AddThemeConstantOverride("separation", 4); + _formHost.AddChild(_localeContainer); + + var addLocaleBtn = new Button + { + Text = TranslationServer.Translate("mods/catalog_add_locale"), + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + addLocaleBtn.Pressed += () => AddLocaleRow(string.Empty, string.Empty, string.Empty); + _formHost.AddChild(addLocaleBtn); + + _urlEdit.TextChanged += _ => UpdatePreview(); + _categoryList.ItemSelected += _ => UpdatePreview(); + _categoryList.MultiSelected += (_, _) => UpdatePreview(); + _tagsEdit.TextChanged += _ => UpdatePreview(); + _changelogEdit.TextChanged += UpdatePreview; + _statusOpt.ItemSelected += _ => UpdatePreview(); + _minGameVerEdit.TextChanged += _ => UpdatePreview(); + _iconUrlEdit.TextChanged += _ => UpdatePreview(); + + UpdatePreview(); + } + + private static string GetCategoryDisplayName(CategoryType cat) => cat switch + { + CategoryType.Story => TranslationServer.Translate("mods/category_story"), + CategoryType.Characters => TranslationServer.Translate("mods/category_characters"), + CategoryType.Gameplay => TranslationServer.Translate("mods/category_gameplay"), + CategoryType.Visual => TranslationServer.Translate("mods/category_visual"), + CategoryType.Audio => TranslationServer.Translate("mods/category_audio"), + CategoryType.UI => TranslationServer.Translate("mods/category_ui"), + CategoryType.Locale => TranslationServer.Translate("mods/category_locale"), + CategoryType.Library => TranslationServer.Translate("mods/category_library"), + CategoryType.Engine => TranslationServer.Translate("mods/category_engine"), + CategoryType.Overhaul => TranslationServer.Translate("mods/category_overhaul"), + CategoryType.Tools => TranslationServer.Translate("mods/category_tools"), + CategoryType.Editor => TranslationServer.Translate("mods/category_editor"), + CategoryType.Debug => TranslationServer.Translate("mods/category_debug"), + CategoryType.Other => TranslationServer.Translate("mods/category_other"), + _ => cat.ToString(), + }; + + private void AddLocaleRow(string lang, string name, string description) + { + var row = new HBoxContainer + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + }; + row.AddThemeConstantOverride("separation", 4); + + var langEdit = new LineEdit + { + CustomMinimumSize = new Vector2(44, 0), + PlaceholderText = "en", + MaxLength = 5, + Text = lang, + }; + row.AddChild(langEdit); + + var nameEdit = new LineEdit + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + PlaceholderText = TranslationServer.Translate("mods/catalog_locale_name"), + Text = name, + }; + row.AddChild(nameEdit); + + var descEdit = new LineEdit + { + SizeFlagsHorizontal = Control.SizeFlags.ExpandFill, + PlaceholderText = TranslationServer.Translate("mods/catalog_locale_desc"), + Text = description, + }; + row.AddChild(descEdit); + + var removeBtn = new Button { Text = "×", CustomMinimumSize = new Vector2(28, 0) }; + removeBtn.Pressed += () => RemoveLocaleRow(row); + row.AddChild(removeBtn); + + langEdit.TextChanged += _ => UpdatePreview(); + nameEdit.TextChanged += _ => UpdatePreview(); + descEdit.TextChanged += _ => UpdatePreview(); + + _localeContainer.AddChild(row); + } + + private void RemoveLocaleRow(HBoxContainer row) + { + _localeContainer.RemoveChild(row); + row.QueueFree(); + UpdatePreview(); + } + + private void UpdatePreview() + { + if (_previewEdit == null || _modInfo == null || _manifest == null) + return; + + var entry = BuildEntryFromManifest(_manifest); + entry.Downloads = new List + { + new() + { + Version = entry.Version, + Urls = new List { _urlEdit.Text.Trim() }, + SizeBytes = 0, + ArchiveSha256 = "—", + InstallSha256 = "—", + }, + }; + entry.Category = CollectSelectedCategories(_categoryList, Categories); + entry.Tags = ParseTags(_tagsEdit.Text); + entry.Downloads[0].Changelog = NullIfEmpty(_changelogEdit.Text); + entry.Status = Statuses[_statusOpt.Selected]; + entry.MinGameVersion = NullIfEmpty(_minGameVerEdit.Text); + entry.IconUrl = NullIfEmpty(_iconUrlEdit.Text); + entry.Locale = CollectLocales(); + _previewEdit.Text = JsonSerializer.Serialize(entry, JsonOptionsProvider.WriteIndentedIgnoreNullOptions); + } + + private List CollectLocales() + { + var list = new List(); + foreach (var child in _localeContainer.GetChildren()) + { + if (child is not HBoxContainer row || row.GetChildCount() < 3) + continue; + + var lang = ((LineEdit)row.GetChild(0)).Text.Trim(); + var name = ((LineEdit)row.GetChild(1)).Text.Trim(); + var description = ((LineEdit)row.GetChild(2)).Text.Trim(); + if (lang.Length == 0) + continue; + + list.Add(new CatalogLocaleEntry + { + Lang = lang, + Name = NullIfEmpty(name), + Description = NullIfEmpty(description), + }); + } + + if (list.Count == 0 && _manifest != null) + { + list.Add(new CatalogLocaleEntry + { + Lang = "en", + Name = _manifest.Name, + Description = _manifest.Description, + }); + } + + return list; + } + + private CatalogExportEntry BuildExportEntry() + { + var entry = BuildEntryFromManifest(_manifest); + entry.Downloads = new List + { + new() + { + Version = entry.Version, + Urls = new List { _urlEdit.Text.Trim() }, + SizeBytes = 0, + }, + }; + entry.Category = CollectSelectedCategories(_categoryList, Categories); + entry.Tags = ParseTags(_tagsEdit.Text); + entry.Downloads[0].Changelog = NullIfEmpty(_changelogEdit.Text); + entry.Status = Statuses[_statusOpt.Selected]; + entry.MinGameVersion = NullIfEmpty(_minGameVerEdit.Text); + entry.IconUrl = NullIfEmpty(_iconUrlEdit.Text); + entry.Locale = CollectLocales(); + return entry; + } + + private void OnCopyPressed() + { + DisplayServer.ClipboardSet(_previewEdit.Text); + GameLogger.Log("CATALOG_EXPORT", $"Copied catalog JSON for '{_modInfo?.Id}' to clipboard"); + } + + private void OnExportPressed() + { + if (_modInfo == null) + return; + + var entry = BuildExportEntry(); + var modsRoot = ModRegistry.ModsPath; + var isZip = _modInfo.FileProvider.RootPath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase); + if (isZip) + ModCatalogExporter.ExportFromZip(_modInfo, entry, modsRoot); + else + ModCatalogExporter.ExportFromFolder(_modInfo, entry, modsRoot); + + QueueFree(); + } + + private static List ParseTags(string text) + { + if (string.IsNullOrWhiteSpace(text)) + return null; + return text.Split(',').Select(t => t.Trim()).Where(t => t.Length > 0).ToList(); + } + + private static string NullIfEmpty(string text) + { + var trimmed = text?.Trim(); + return string.IsNullOrEmpty(trimmed) ? null : trimmed; + } + + private static CatalogExportEntry BuildEntryFromManifest(ModManifest manifest) + { + var depends = manifest.Depends?.Select(d => d.ToString()).Where(s => s.Length > 0).ToList(); + + return new CatalogExportEntry + { + Id = manifest.Id, + Name = manifest.Name, + Author = manifest.Author, + Version = manifest.Version, + Description = manifest.Description, + Category = manifest.Category ?? CategoryType.Other, + Depends = depends, + ConflictsWith = manifest.ConflictsWith, + }; + } + + private static CategoryType CollectSelectedCategories(ItemList list, CategoryType[] categories) + { + var selected = (CategoryType)0; + for (var i = 0; i < categories.Length && i < list.ItemCount; i++) + { + if (list.IsSelected(i)) + selected |= categories[i]; + } + return selected != 0 ? selected : CategoryType.Other; + } + + private static void AddLabeledControl(Container parent, string labelKey, Control control) + { + var box = new VBoxContainer { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill }; + var label = new Label + { + Text = TranslationServer.Translate(labelKey), + }; + label.AddThemeFontSizeOverride("font_size", UiTokens.FontBodySmall); + label.AddThemeColorOverride("font_color", UiTokens.Muted); + box.AddChild(label); + box.AddChild(control); + parent.AddChild(box); + } + + private static void AddReadOnlyField(Container parent, string labelKey, string value) + { + var label = new Label + { + Text = $"{TranslationServer.Translate(labelKey)}: {value}", + }; + label.AddThemeFontSizeOverride("font_size", UiTokens.FontBody); + label.AddThemeColorOverride("font_color", UiTokens.Body); + parent.AddChild(label); + } + + private static LineEdit AddLineEdit(Container parent, string labelKey) + { + var edit = new LineEdit { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill }; + AddLabeledControl(parent, labelKey, edit); + return edit; + } + + private static OptionButton AddOptionButton(Container parent, string labelKey, string[] items) + { + var opt = new OptionButton { SizeFlagsHorizontal = Control.SizeFlags.ExpandFill }; + foreach (var item in items) + opt.AddItem(item); + AddLabeledControl(parent, labelKey, opt); + return opt; + } + + private static void AddSectionLabel(Container parent, string text) + { + var sep = new HSeparator(); + parent.AddChild(sep); + var label = new Label { Text = text }; + label.AddThemeFontSizeOverride("font_size", UiTokens.FontBodyLarge); + label.AddThemeColorOverride("font_color", UiTokens.Ink); + parent.AddChild(label); + } + } +} diff --git a/src/InteractiveEditor/InteractiveEditorWindow.cs b/src/InteractiveEditor/InteractiveEditorWindow.cs index 362e935..7c7ead4 100644 --- a/src/InteractiveEditor/InteractiveEditorWindow.cs +++ b/src/InteractiveEditor/InteractiveEditorWindow.cs @@ -1,9 +1,10 @@ -using System; +using System; using System.Collections.Generic; using Cthangover.Core.Interactive; using Cthangover.Core.Mods; using Cthangover.Core.Mods.Resolvers; using Cthangover.Core.Factories.Impls; +using Cthangover.Core.UI.Theming; using Cthangover.Core.UI.Tool; using Cthangover.Core.Utils; using Cthangover.Tools.Services; @@ -13,13 +14,13 @@ namespace Cthangover.Tools.InteractiveEditor { public partial class InteractiveEditorWindow : ToolWindow { - private static readonly Color _rectColor = new(0f, 1f, 0f, 0.6f); - private static readonly Color _circleColor = new(0f, 0.5f, 1f, 0.6f); - private static readonly Color _vertexColor = new(1f, 0.8f, 0f, 0.8f); - private static readonly Color _colliderOverlayColor = new(0f, 1f, 0f, 0.15f); - private static readonly Color _colliderCircleOverlayColor = new(0f, 0.5f, 1f, 0.15f); - private static readonly Color _colliderPolyOverlayColor = new(1f, 0.5f, 0f, 0.15f); - private static readonly Color _colliderPolyOutlineColor = new(1f, 0.5f, 0f, 0.6f); + private static readonly Color _rectColor = UiTokens.Alpha(UiTokens.Success, 0.6f); + private static readonly Color _circleColor = UiTokens.Alpha(UiTokens.Info, 0.6f); + private static readonly Color _vertexColor = UiTokens.Alpha(UiTokens.AccentBright, 0.8f); + private static readonly Color _colliderOverlayColor = UiTokens.Alpha(UiTokens.Success, 0.15f); + private static readonly Color _colliderCircleOverlayColor = UiTokens.Alpha(UiTokens.Info, 0.15f); + private static readonly Color _colliderPolyOverlayColor = UiTokens.Alpha(UiTokens.Accent, 0.15f); + private static readonly Color _colliderPolyOutlineColor = UiTokens.Alpha(UiTokens.Accent, 0.6f); private InteractiveEditorController _controller = new(); @@ -56,6 +57,7 @@ namespace Cthangover.Tools.InteractiveEditor public InteractiveEditorWindow() : base("tools/interactive_editor/title") { Size = new Vector2I(1280, 820); + CthangoverTheme.Apply(this); var outer = CreateFillContainer(); AddChild(outer); @@ -215,7 +217,7 @@ namespace Cthangover.Tools.InteractiveEditor AddLabel(parent, "tools/interactive_editor/label_highlight_color"); _highlightColorBtn = new ColorPickerButton { - Color = new Color(1f, 1f, 0f, 0.2f), + Color = UiTokens.Alpha(UiTokens.Accent, 0.2f), CustomMinimumSize = new Vector2(40, 30) }; _highlightColorBtn.ColorChanged += _ => OnParamChanged(); @@ -262,7 +264,7 @@ namespace Cthangover.Tools.InteractiveEditor private void AddLabel(Control parent, string locKey) { var label = new Label { Text = Tr(locKey) }; - label.AddThemeColorOverride("font_color", new Color(0.7f, 0.7f, 0.7f)); + label.AddThemeColorOverride("font_color", UiTokens.Muted); parent.AddChild(label); } @@ -850,3 +852,4 @@ namespace Cthangover.Tools.InteractiveEditor } } } + diff --git a/src/LightEditor/LightEditorController.cs b/src/LightEditor/LightEditorController.cs index 7c9c56d..28919aa 100644 --- a/src/LightEditor/LightEditorController.cs +++ b/src/LightEditor/LightEditorController.cs @@ -3,6 +3,7 @@ using System.Text.Json; using Cthangover.Core.Mods; using Cthangover.Core.Mods.Resolvers; using Cthangover.Core.UI.Lights; +using Cthangover.Core.UI.Theming; using Godot; namespace Cthangover.Core.UI.Tool.LightEditor @@ -36,7 +37,7 @@ namespace Cthangover.Core.UI.Tool.LightEditor previewPositions[i] = new Vector2(-1000f, -1000f); previewRadii[i] = 0f; previewInfluence[i] = 0f; - previewColors[i] = Colors.White; + previewColors[i] = UiTokens.White; } } @@ -122,7 +123,7 @@ namespace Cthangover.Core.UI.Tool.LightEditor previewPositions[i] = new Vector2(-1000f, -1000f); previewRadii[i] = 0f; previewInfluence[i] = 0f; - previewColors[i] = Colors.White; + previewColors[i] = UiTokens.White; } int count = Mathf.Min(Lights.Count, 10); diff --git a/src/LightEditor/LightEditorWindow.cs b/src/LightEditor/LightEditorWindow.cs index c37131d..6cdd0e4 100644 --- a/src/LightEditor/LightEditorWindow.cs +++ b/src/LightEditor/LightEditorWindow.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Text.Json; using Cthangover.Core.Factories.Impls; using Cthangover.Core.UI.Lights; +using Cthangover.Core.UI.Theming; using Cthangover.Core.Utils; using Cthangover.Tools.Services; using Godot; @@ -40,6 +41,7 @@ namespace Cthangover.Core.UI.Tool.LightEditor public LightEditorWindow() : base("tools/light_editor/title") { controller = new LightEditorController(); + CthangoverTheme.Apply(this); BuildUI(); LoadBackgroundList(); } diff --git a/src/ScenarioEditor/ScenarioEditorWindow.cs b/src/ScenarioEditor/ScenarioEditorWindow.cs index f2c8973..050c211 100644 --- a/src/ScenarioEditor/ScenarioEditorWindow.cs +++ b/src/ScenarioEditor/ScenarioEditorWindow.cs @@ -1,5 +1,6 @@ using System; using Cthangover.Core.Scenes; +using Cthangover.Core.UI.Theming; using Cthangover.Core.Utils; using Cthangover.Tools.Services; using Godot; @@ -27,6 +28,7 @@ namespace Cthangover.Core.UI.Tool.ScenarioEditor public ScenarioEditorWindow() : base("tools/scenario_editor/title") { + CthangoverTheme.Apply(this); BuildUI(); } diff --git a/src/ScenarioEditor/ScenarioFileTreePanel.cs b/src/ScenarioEditor/ScenarioFileTreePanel.cs index b36d6ff..8d68d16 100644 --- a/src/ScenarioEditor/ScenarioFileTreePanel.cs +++ b/src/ScenarioEditor/ScenarioFileTreePanel.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Cthangover.Core.UI.Theming; using Cthangover.Tools.Services; using Godot; @@ -36,7 +37,7 @@ namespace Cthangover.Core.UI.Tool.ScenarioEditor var modItem = _fileTree.CreateItem(root); modItem.SetText(0, $"[{mod.ModId}]"); modItem.SetSelectable(0, false); - modItem.SetCustomColor(0, new Color(0.5f, 0.75f, 1f)); + modItem.SetCustomColor(0, UiTokens.Info); foreach (var group in mod.Groups) { diff --git a/src/ScenarioEditor/ScenarioGraphView.cs b/src/ScenarioEditor/ScenarioGraphView.cs index 8df0d16..45b5e9a 100644 --- a/src/ScenarioEditor/ScenarioGraphView.cs +++ b/src/ScenarioEditor/ScenarioGraphView.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Cthangover.Core.UI.Theming; using Cthangover.Tools.Services; using Godot; @@ -102,18 +103,18 @@ namespace Cthangover.Core.UI.Tool.ScenarioEditor (bgCount > 1 ? $" +{bgCount - 1}" : "") : "") }); - var color = Colors.White; + var color = UiTokens.White; if (nodeData.IsStartScene) { - color = new Color(0.3f, 1f, 0.3f); - gn.AddThemeStyleboxOverride("titlebar", CreateTitlebarStyle(new Color(0.2f, 0.5f, 0.15f))); + color = UiTokens.Success; + gn.AddThemeStyleboxOverride("titlebar", CreateTitlebarStyle(UiTokens.Alpha(UiTokens.Success, 0.3f))); } else if (nodeData.HasErrors) { - color = new Color(1f, 0.3f, 0.3f); - gn.AddThemeStyleboxOverride("titlebar", CreateTitlebarStyle(new Color(0.5f, 0.1f, 0.1f))); + color = UiTokens.DangerText; + gn.AddThemeStyleboxOverride("titlebar", CreateTitlebarStyle(UiTokens.Alpha(UiTokens.Danger, 0.5f))); } - gn.SetSlot(0, true, 0, Colors.White, true, 0, color); + gn.SetSlot(0, true, 0, UiTokens.White, true, 0, color); _graph.AddChild(gn); _nodes[key] = gn; @@ -148,14 +149,10 @@ namespace Cthangover.Core.UI.Tool.ScenarioEditor private static StyleBoxFlat CreateTitlebarStyle(Color bgColor) { - return new StyleBoxFlat - { - BgColor = bgColor, - CornerRadiusTopLeft = 6, - CornerRadiusTopRight = 6, - CornerRadiusBottomLeft = 0, - CornerRadiusBottomRight = 0 - }; + var style = UiKit.Frame(UiTokens.RadiusMd, bgColor); + style.CornerRadiusBottomLeft = 0; + style.CornerRadiusBottomRight = 0; + return style; } private void ClearGraph() diff --git a/src/SceneBuilder/InspectorPanel.cs b/src/SceneBuilder/InspectorPanel.cs index 8025099..ed8133d 100644 --- a/src/SceneBuilder/InspectorPanel.cs +++ b/src/SceneBuilder/InspectorPanel.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Cthangover.Core.UI; +using Cthangover.Core.UI.Theming; using Godot; namespace Cthangover.Core.UI.Tool.SceneBuilder @@ -65,7 +66,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder _banner = new Label(); _banner.AutowrapMode = TextServer.AutowrapMode.WordSmart; _banner.SizeFlagsHorizontal = Control.SizeFlags.ExpandFill; - _banner.Modulate = new Color(1f, 0.75f, 0.3f); + _banner.Modulate = UiTokens.Warning; _banner.Visible = false; root.AddChild(_banner); @@ -125,7 +126,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder foreach (var entry in InspectorPropertyCatalog.Build(node, insideContainer)) { var categoryLabel = new Label { Text = TranslationServer.Translate(entry.CategoryKey) }; - categoryLabel.Modulate = new Color(1f, 0.85f, 0.4f); + categoryLabel.Modulate = UiTokens.AccentBright; _rows.AddChild(categoryLabel); foreach (var property in entry.Properties) @@ -367,7 +368,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder private Color ReadColor(string key) { var v = _node.Get(key); - return v.VariantType == Variant.Type.Nil ? Colors.White : v.AsColor(); + return v.VariantType == Variant.Type.Nil ? UiTokens.White : v.AsColor(); } private Vector2 ReadVector2(string key) diff --git a/src/SceneBuilder/SceneBuilderController.cs b/src/SceneBuilder/SceneBuilderController.cs index a546142..d424ba7 100644 --- a/src/SceneBuilder/SceneBuilderController.cs +++ b/src/SceneBuilder/SceneBuilderController.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using Cthangover.Core.Mods; using Cthangover.Core.Scenes; +using Cthangover.Core.UI.Theming; using Godot; namespace Cthangover.Core.UI.Tool.SceneBuilder @@ -240,7 +241,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder private void AttachHighlight(Node root) { _highlight = new ColorRect(); - _highlight.Color = new Color(1f, 0.7f, 0f, 0.35f); + _highlight.Color = UiTokens.Alpha(UiTokens.Accent, 0.35f); _highlight.Visible = false; _highlight.MouseFilter = Control.MouseFilterEnum.Ignore; _highlight.ZIndex = 4096; @@ -335,7 +336,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder _highlight.Position = pos; _highlight.Size = new Vector2(20, 20); - _highlight.Color = new Color(1f, 0.3f, 0.3f, 0.5f); + _highlight.Color = UiTokens.Alpha(UiTokens.Danger, 0.5f); _highlight.Visible = true; } } @@ -424,7 +425,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder { if (_highlight != null) _highlight.Visible = false; - _highlight.Color = new Color(1f, 0.7f, 0f, 0.35f); + _highlight.Color = UiTokens.Alpha(UiTokens.Accent, 0.35f); } } } diff --git a/src/SceneBuilder/SceneBuilderWindow.cs b/src/SceneBuilder/SceneBuilderWindow.cs index e9d9191..dc4dba1 100644 --- a/src/SceneBuilder/SceneBuilderWindow.cs +++ b/src/SceneBuilder/SceneBuilderWindow.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Cthangover.Core.UI.Theming; using Cthangover.Core.Utils; using Godot; @@ -56,6 +57,7 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder /// Constructs the window, creates the controller, builds UI, and populates selectors. public SceneBuilderWindow() : base("tools/scene_builder/title") { + CthangoverTheme.Apply(this); _controller = new SceneBuilderController(); BuildUI(); PopulateBaseList(); diff --git a/src/SceneBuilder/SelectionOverlay.cs b/src/SceneBuilder/SelectionOverlay.cs index 9d3cb04..e90769b 100644 --- a/src/SceneBuilder/SelectionOverlay.cs +++ b/src/SceneBuilder/SelectionOverlay.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Cthangover.Core.UI; +using Cthangover.Core.UI.Theming; using Godot; namespace Cthangover.Core.UI.Tool.SceneBuilder @@ -87,13 +88,13 @@ namespace Cthangover.Core.UI.Tool.SceneBuilder Resized += Refresh; for (var i = 0; i < 4; i++) - _borderRects.Add(CreateMarker(new Color(1f, 0.85f, 0.35f, 0.95f))); + _borderRects.Add(CreateMarker(UiTokens.Alpha(UiTokens.AccentBright, 0.95f))); for (var i = 0; i < ResizeMarkerCount; i++) - _handleRects.Add(CreateMarker(new Color(1f, 0.62f, 0.1f, 1f))); + _handleRects.Add(CreateMarker(UiTokens.Accent)); for (var i = 0; i < AnchorDotCount * 2; i++) - _anchorLines.Add(CreateMarker(new Color(0.35f, 0.75f, 1f, 0.55f))); + _anchorLines.Add(CreateMarker(UiTokens.Alpha(UiTokens.Info, 0.55f))); for (var i = 0; i < AnchorDotCount; i++) - _anchorDots.Add(CreateMarker(new Color(0.45f, 0.85f, 1f, 1f))); + _anchorDots.Add(CreateMarker(UiTokens.Info)); GuiInput += OnOverlayGui; } diff --git a/src/Services/ScenarioSyntaxService.cs b/src/Services/ScenarioSyntaxService.cs index 08468d9..725c711 100644 --- a/src/Services/ScenarioSyntaxService.cs +++ b/src/Services/ScenarioSyntaxService.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Cthangover.Core.Scenarios; using Cthangover.Core.Factories.Impls; +using Cthangover.Core.UI.Theming; using Godot; namespace Cthangover.Tools.Services @@ -18,19 +19,19 @@ namespace Cthangover.Tools.Services { /// /// Builds a for scenario DSL syntax. - /// Comments (lines starting with #) are green, strings are orange, - /// command names are blue, and argument keywords (e.g. key, - /// wait, quest_id) are amber. + /// Comments (lines starting with #) are green, strings are bright + /// gold, command names are blue, and argument keywords (e.g. key, + /// wait, quest_id) are amber — all from . /// public static CodeHighlighter CreateHighlighter() { var hl = new CodeHighlighter(); - hl.AddColorRegion("#", null, new Color(0.45f, 0.75f, 0.45f), true); - hl.AddColorRegion("\"", "\"", new Color(1f, 0.6f, 0.4f), false); - hl.NumberColor = new Color(0.7f, 0.7f, 1f); - hl.SymbolColor = new Color(0.8f, 0.8f, 0.8f); + hl.AddColorRegion("#", null, UiTokens.Success, true); + hl.AddColorRegion("\"", "\"", UiTokens.AccentBright, false); + hl.NumberColor = UiTokens.Info; + hl.SymbolColor = UiTokens.Muted; - var cmdColor = new Color(0.4f, 0.7f, 1f); + var cmdColor = UiTokens.Info; hl.KeywordColors = new Godot.Collections.Dictionary(); foreach (var cmd in ScenarioCommandStrategyFactory.GetAllCommandNames()) @@ -40,7 +41,7 @@ namespace Cthangover.Tools.Services "option", "select" }) hl.KeywordColors[cmd] = cmdColor; - var argColor = new Color(0.8f, 0.6f, 0.3f); + var argColor = UiTokens.Accent; foreach (var kw in new[] { "key", "first", "second", "hide_color", "wait", "speed", "hidden", "sprites", "loop", "target", "quest_id", "show", "hide", "duration"