ui stule update
This commit is contained in:
1 parent
6af7a03808
commit
7521959cc0
17 files changed
+588
-48
No files matched your search
@@ -0,0 +1,2 @@
|
||||
# Catalog export tool — extra keys
|
||||
mods/catalog_select_mod = Select a mod:
|
||||
@@ -0,0 +1,2 @@
|
||||
# Catalog export tool — дополнительные ключи
|
||||
mods/catalog_select_mod = Выберите мод:
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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"]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using Cthangover.Core.UI.Tool;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.Core.UI.Tool.CatalogExport
|
||||
{
|
||||
/// <summary>
|
||||
/// Registers the catalog export window as a developer tool. Moved here from
|
||||
/// the old mods menu when mod management UI was delegated to the
|
||||
/// <c>mod_manager</c> mod — exporting a catalog entry is an authoring task,
|
||||
/// not part of day-to-day mod management.
|
||||
/// </summary>
|
||||
public class CatalogExportToolProvider : IToolProvider
|
||||
{
|
||||
/// <summary>Unique tool identifier.</summary>
|
||||
public string Id => "catalog_export";
|
||||
|
||||
/// <summary>Translation key for the tool's display name.</summary>
|
||||
public string LocaleKey => "mods/catalog_title";
|
||||
|
||||
/// <summary>Creates the export window instance.</summary>
|
||||
public Window CreateWindow() => new CatalogExportWindow();
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// 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 <c>mod_manager</c>
|
||||
/// mod while this authoring utility stays a tool.
|
||||
/// <para/>
|
||||
/// 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 <see cref="ModCatalogExporter"/>.
|
||||
/// </summary>
|
||||
public partial class CatalogExportWindow : ToolWindow
|
||||
{
|
||||
private static readonly string[] Statuses = { "stable", "beta", "deprecated", "archived" };
|
||||
private static readonly CategoryType[] Categories = Enum.GetValues<CategoryType>();
|
||||
|
||||
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<CatalogEntryDownload>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Version = entry.Version,
|
||||
Urls = new List<string> { _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<CatalogLocaleEntry> CollectLocales()
|
||||
{
|
||||
var list = new List<CatalogLocaleEntry>();
|
||||
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<CatalogEntryDownload>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Version = entry.Version,
|
||||
Urls = new List<string> { _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<string> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
/// <summary>Constructs the window, creates the controller, builds UI, and populates selectors.</summary>
|
||||
public SceneBuilderWindow() : base("tools/scene_builder/title")
|
||||
{
|
||||
CthangoverTheme.Apply(this);
|
||||
_controller = new SceneBuilderController();
|
||||
BuildUI();
|
||||
PopulateBaseList();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a <see cref="CodeHighlighter"/> for scenario DSL syntax.
|
||||
/// Comments (lines starting with <c>#</c>) are green, strings are orange,
|
||||
/// command names are blue, and argument keywords (e.g. <c>key</c>,
|
||||
/// <c>wait</c>, <c>quest_id</c>) are amber.
|
||||
/// Comments (lines starting with <c>#</c>) are green, strings are bright
|
||||
/// gold, command names are blue, and argument keywords (e.g. <c>key</c>,
|
||||
/// <c>wait</c>, <c>quest_id</c>) are amber — all from <see cref="UiTokens"/>.
|
||||
/// </summary>
|
||||
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"
|
||||
|
||||
Reference in new issue
Block a user