feat: use authored category names for mods
Store categories as authored strings. Trim and deduplicate without changing case, compare without case sensitivity, and keep all observed categories available to filters and editors.
This commit is contained in:
1 parent
73478b7031
commit
1995aae77d
11 files changed
+406
-99
No files matched your search
+34
-14
@@ -57,20 +57,20 @@ mods/catalog_export = Export to Catalog
|
||||
mods/catalog_title = Export Catalog Entry
|
||||
mods/catalog_download_url = Download URL
|
||||
mods/catalog_category = Category
|
||||
mods/category_story = Story
|
||||
mods/category_characters = Characters
|
||||
mods/category_gameplay = Gameplay
|
||||
mods/category_visual = Visual
|
||||
mods/category_audio = Audio
|
||||
mods/category_ui = UI
|
||||
mods/category_locale = Locale
|
||||
mods/category_library = Library
|
||||
mods/category_engine = Engine
|
||||
mods/category_overhaul = Overhaul
|
||||
mods/category_tools = Tools
|
||||
mods/category_editor = Editor
|
||||
mods/category_debug = Debug
|
||||
mods/category_other = Other
|
||||
mod_category/story = Story
|
||||
mod_category/characters = Characters
|
||||
mod_category/gameplay = Gameplay
|
||||
mod_category/visual = Visual
|
||||
mod_category/audio = Audio
|
||||
mod_category/ui = UI
|
||||
mod_category/locale = Locale
|
||||
mod_category/library = Library
|
||||
mod_category/engine = Engine
|
||||
mod_category/overhaul = Overhaul
|
||||
mod_category/tools = Tools
|
||||
mod_category/editor = Editor
|
||||
mod_category/debug = Debug
|
||||
mod_category/other = Other
|
||||
mods/catalog_tags = Tags (comma-separated)
|
||||
mods/catalog_changelog = Changelog
|
||||
mods/catalog_status = Status
|
||||
@@ -269,3 +269,23 @@ tools/menu_title = Tools
|
||||
|
||||
# --- Development cover ---
|
||||
ui/cover/in_development = In development
|
||||
|
||||
mod_categories/title = Categories
|
||||
mod_categories/code = Category, e.g. RPG — Gear
|
||||
mod_categories/add = Add
|
||||
mod_categories/primary = Primary category
|
||||
mod_categories/suggestions = Choose a category…
|
||||
mod_categories/save = Save categories
|
||||
mod_categories/saved = Categories saved to manifest.json.
|
||||
mod_categories/development = Saved in the development mod before packaging.
|
||||
mod_categories/readonly = Categories of installed packages and catalog entries are read-only. Edit the development folder.
|
||||
mod_categories/pending = Add the typed category or clear the input before continuing.
|
||||
|
||||
mod_categories/hint_code = Enter any category name; spelling is preserved.
|
||||
mod_categories/hint_suggestions = Choose a category from installed mods, catalogs or the base suggestions.
|
||||
mod_categories/hint_primary = The primary category appears on the card and determines its group. It is saved first.
|
||||
mod_categories/hint_add = Add the entered category.
|
||||
mod_categories/hint_base = Include or remove this category.
|
||||
mod_categories/hint_remove = Remove this custom category.
|
||||
|
||||
mod_categories/hint_filter = Show mods with this category:
|
||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = Exportar al catálogo
|
||||
mods/catalog_title = Exportar entrada del catálogo
|
||||
mods/catalog_download_url = URL de descarga
|
||||
mods/catalog_category = Categoría
|
||||
mods/category_story = Historia
|
||||
mods/category_characters = Personajes
|
||||
mods/category_gameplay = Jugabilidad
|
||||
mods/category_visual = Visual
|
||||
mods/category_audio = Audio
|
||||
mods/category_ui = Interfaz
|
||||
mods/category_locale = Idioma
|
||||
mods/category_library = Biblioteca
|
||||
mods/category_engine = Motor
|
||||
mods/category_overhaul = Conversión total
|
||||
mods/category_tools = Herramientas
|
||||
mods/category_editor = Editor
|
||||
mods/category_debug = Depuración
|
||||
mods/category_other = Otros
|
||||
mod_category/story = Historia
|
||||
mod_category/characters = Personajes
|
||||
mod_category/gameplay = Jugabilidad
|
||||
mod_category/visual = Visual
|
||||
mod_category/audio = Audio
|
||||
mod_category/ui = Interfaz
|
||||
mod_category/locale = Idioma
|
||||
mod_category/library = Biblioteca
|
||||
mod_category/engine = Motor
|
||||
mod_category/overhaul = Conversión total
|
||||
mod_category/tools = Herramientas
|
||||
mod_category/editor = Editor
|
||||
mod_category/debug = Depuración
|
||||
mod_category/other = Otros
|
||||
mods/catalog_tags = Etiquetas (separadas por comas)
|
||||
mods/catalog_changelog = Cambios
|
||||
mods/catalog_status = Estado
|
||||
|
||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = カタログに書き出す
|
||||
mods/catalog_title = カタログ項目を書き出す
|
||||
mods/catalog_download_url = ダウンロード URL
|
||||
mods/catalog_category = カテゴリー
|
||||
mods/category_story = ストーリー
|
||||
mods/category_characters = キャラクター
|
||||
mods/category_gameplay = ゲームプレイ
|
||||
mods/category_visual = ビジュアル
|
||||
mods/category_audio = オーディオ
|
||||
mods/category_ui = UI
|
||||
mods/category_locale = 言語
|
||||
mods/category_library = ライブラリ
|
||||
mods/category_engine = エンジン
|
||||
mods/category_overhaul = 大型改造
|
||||
mods/category_tools = ツール
|
||||
mods/category_editor = エディター
|
||||
mods/category_debug = デバッグ
|
||||
mods/category_other = その他
|
||||
mod_category/story = ストーリー
|
||||
mod_category/characters = キャラクター
|
||||
mod_category/gameplay = ゲームプレイ
|
||||
mod_category/visual = ビジュアル
|
||||
mod_category/audio = オーディオ
|
||||
mod_category/ui = UI
|
||||
mod_category/locale = 言語
|
||||
mod_category/library = ライブラリ
|
||||
mod_category/engine = エンジン
|
||||
mod_category/overhaul = 大型改造
|
||||
mod_category/tools = ツール
|
||||
mod_category/editor = エディター
|
||||
mod_category/debug = デバッグ
|
||||
mod_category/other = その他
|
||||
mods/catalog_tags = タグ(カンマ区切り)
|
||||
mods/catalog_changelog = 変更履歴
|
||||
mods/catalog_status = 状態
|
||||
|
||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = 카탈로그로 내보내기
|
||||
mods/catalog_title = 카탈로그 항목 내보내기
|
||||
mods/catalog_download_url = 다운로드 URL
|
||||
mods/catalog_category = 분류
|
||||
mods/category_story = 스토리
|
||||
mods/category_characters = 캐릭터
|
||||
mods/category_gameplay = 게임플레이
|
||||
mods/category_visual = 비주얼
|
||||
mods/category_audio = 오디오
|
||||
mods/category_ui = UI
|
||||
mods/category_locale = 언어
|
||||
mods/category_library = 라이브러리
|
||||
mods/category_engine = 엔진
|
||||
mods/category_overhaul = 대규모 개편
|
||||
mods/category_tools = 도구
|
||||
mods/category_editor = 에디터
|
||||
mods/category_debug = 디버그
|
||||
mods/category_other = 기타
|
||||
mod_category/story = 스토리
|
||||
mod_category/characters = 캐릭터
|
||||
mod_category/gameplay = 게임플레이
|
||||
mod_category/visual = 비주얼
|
||||
mod_category/audio = 오디오
|
||||
mod_category/ui = UI
|
||||
mod_category/locale = 언어
|
||||
mod_category/library = 라이브러리
|
||||
mod_category/engine = 엔진
|
||||
mod_category/overhaul = 대규모 개편
|
||||
mod_category/tools = 도구
|
||||
mod_category/editor = 에디터
|
||||
mod_category/debug = 디버그
|
||||
mod_category/other = 기타
|
||||
mods/catalog_tags = 태그 (쉼표로 구분)
|
||||
mods/catalog_changelog = 변경 기록
|
||||
mods/catalog_status = 상태
|
||||
|
||||
+34
-14
@@ -57,20 +57,20 @@ mods/catalog_export = Экспорт в каталог
|
||||
mods/catalog_title = Экспорт записи каталога
|
||||
mods/catalog_download_url = URL скачивания
|
||||
mods/catalog_category = Категория
|
||||
mods/category_story = Сюжет
|
||||
mods/category_characters = Персонажи
|
||||
mods/category_gameplay = Геймплей
|
||||
mods/category_visual = Визуал
|
||||
mods/category_audio = Аудио
|
||||
mods/category_ui = Интерфейс
|
||||
mods/category_locale = Перевод
|
||||
mods/category_library = Библиотека
|
||||
mods/category_engine = Движок
|
||||
mods/category_overhaul = Тотальная конверсия
|
||||
mods/category_tools = Инструменты
|
||||
mods/category_editor = Редактор
|
||||
mods/category_debug = Отладка
|
||||
mods/category_other = Прочее
|
||||
mod_category/story = Сюжет
|
||||
mod_category/characters = Персонажи
|
||||
mod_category/gameplay = Геймплей
|
||||
mod_category/visual = Визуал
|
||||
mod_category/audio = Аудио
|
||||
mod_category/ui = Интерфейс
|
||||
mod_category/locale = Перевод
|
||||
mod_category/library = Библиотека
|
||||
mod_category/engine = Движок
|
||||
mod_category/overhaul = Тотальная конверсия
|
||||
mod_category/tools = Инструменты
|
||||
mod_category/editor = Редактор
|
||||
mod_category/debug = Отладка
|
||||
mod_category/other = Прочее
|
||||
mods/catalog_tags = Теги (через запятую)
|
||||
mods/catalog_changelog = Список изменений
|
||||
mods/catalog_status = Статус
|
||||
@@ -269,3 +269,23 @@ tools/menu_title = Инструменты
|
||||
|
||||
# --- Development cover ---
|
||||
ui/cover/in_development = В разработке
|
||||
|
||||
mod_categories/title = Категории
|
||||
mod_categories/code = Категория, например RPG — оружие
|
||||
mod_categories/add = Добавить
|
||||
mod_categories/primary = Основная категория
|
||||
mod_categories/suggestions = Выбрать категорию…
|
||||
mod_categories/save = Сохранить категории
|
||||
mod_categories/saved = Категории сохранены в manifest.json.
|
||||
mod_categories/development = Сохраняются в папке разработки мода до упаковки.
|
||||
mod_categories/readonly = Категории установленных пакетов и записей каталога доступны для чтения. Правьте папку разработки.
|
||||
mod_categories/pending = Добавьте набранную категорию или очистите поле перед продолжением.
|
||||
|
||||
mod_categories/hint_code = Введите любое название категории; написание сохраняется.
|
||||
mod_categories/hint_suggestions = Выберите категорию из установленных модов, каталогов или базовых подсказок.
|
||||
mod_categories/hint_primary = Основная категория показывается в карточке и определяет её группу. Сохраняется первой.
|
||||
mod_categories/hint_add = Добавить введённую категорию.
|
||||
mod_categories/hint_base = Добавить или убрать эту категорию.
|
||||
mod_categories/hint_remove = Убрать эту свою категорию.
|
||||
|
||||
mod_categories/hint_filter = Показать моды с этой категорией:
|
||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = Xuất sang danh mục
|
||||
mods/catalog_title = Xuất mục danh mục
|
||||
mods/catalog_download_url = URL tải xuống
|
||||
mods/catalog_category = Thể loại
|
||||
mods/category_story = Cốt truyện
|
||||
mods/category_characters = Nhân vật
|
||||
mods/category_gameplay = Lối chơi
|
||||
mods/category_visual = Hình ảnh
|
||||
mods/category_audio = Âm thanh
|
||||
mods/category_ui = Giao diện
|
||||
mods/category_locale = Ngôn ngữ
|
||||
mods/category_library = Thư viện
|
||||
mods/category_engine = Engine
|
||||
mods/category_overhaul = Đại tu
|
||||
mods/category_tools = Công cụ
|
||||
mods/category_editor = Trình sửa
|
||||
mods/category_debug = Gỡ lỗi
|
||||
mods/category_other = Khác
|
||||
mod_category/story = Cốt truyện
|
||||
mod_category/characters = Nhân vật
|
||||
mod_category/gameplay = Lối chơi
|
||||
mod_category/visual = Hình ảnh
|
||||
mod_category/audio = Âm thanh
|
||||
mod_category/ui = Giao diện
|
||||
mod_category/locale = Ngôn ngữ
|
||||
mod_category/library = Thư viện
|
||||
mod_category/engine = Bộ máy
|
||||
mod_category/overhaul = Đại tu
|
||||
mod_category/tools = Công cụ
|
||||
mod_category/editor = Trình sửa
|
||||
mod_category/debug = Gỡ lỗi
|
||||
mod_category/other = Khác
|
||||
mods/catalog_tags = Thẻ (cách nhau bằng dấu phẩy)
|
||||
mods/catalog_changelog = Nhật ký thay đổi
|
||||
mods/catalog_status = Trạng thái
|
||||
|
||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = 导出到目录
|
||||
mods/catalog_title = 导出目录条目
|
||||
mods/catalog_download_url = 下载 URL
|
||||
mods/catalog_category = 分类
|
||||
mods/category_story = 剧情
|
||||
mods/category_characters = 角色
|
||||
mods/category_gameplay = 玩法
|
||||
mods/category_visual = 视觉
|
||||
mods/category_audio = 音频
|
||||
mods/category_ui = 界面
|
||||
mods/category_locale = 语言
|
||||
mods/category_library = 库
|
||||
mods/category_engine = 引擎
|
||||
mods/category_overhaul = 大型改动
|
||||
mods/category_tools = 工具
|
||||
mods/category_editor = 编辑器
|
||||
mods/category_debug = 调试
|
||||
mods/category_other = 其他
|
||||
mod_category/story = 剧情
|
||||
mod_category/characters = 角色
|
||||
mod_category/gameplay = 玩法
|
||||
mod_category/visual = 视觉
|
||||
mod_category/audio = 音频
|
||||
mod_category/ui = 界面
|
||||
mod_category/locale = 语言
|
||||
mod_category/library = 库
|
||||
mod_category/engine = 引擎
|
||||
mod_category/overhaul = 大型改动
|
||||
mod_category/tools = 工具
|
||||
mod_category/editor = 编辑器
|
||||
mod_category/debug = 调试
|
||||
mod_category/other = 其他
|
||||
mods/catalog_tags = 标签(逗号分隔)
|
||||
mods/catalog_changelog = 更新日志
|
||||
mods/catalog_status = 状态
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
"version": "0.1.0",
|
||||
"author": "ct",
|
||||
"description": "The interface layer every game shares: the toolbar, the in-game menu, the save indicator, notifications and the common look of panels, buttons and lists.",
|
||||
"category": "ui",
|
||||
"categories": ["ui"],
|
||||
"depends": [],
|
||||
"sources": [
|
||||
"src/**/*.cs"
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="src/View/ModCategoryEditor.cs" id="1"]
|
||||
|
||||
[node name="ModCategoryEditor" type="Control"]
|
||||
custom_minimum_size = Vector2(440, 260)
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="Layout" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/separation = 4
|
||||
|
||||
[node name="Caption" type="Label" parent="Layout"]
|
||||
layout_mode = 2
|
||||
text = "mod_categories/title"
|
||||
|
||||
[node name="Base" type="GridContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
columns = 4
|
||||
|
||||
[node name="Custom" type="HFlowContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Input" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Code" type="LineEdit" parent="Layout/Input"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "mod_categories/code"
|
||||
|
||||
[node name="Add" type="Button" parent="Layout/Input"]
|
||||
layout_mode = 2
|
||||
text = "mod_categories/add"
|
||||
|
||||
[node name="Suggestions" type="OptionButton" parent="Layout"]
|
||||
layout_mode = 2
|
||||
fit_to_longest_item = false
|
||||
|
||||
[node name="PrimaryRow" type="HBoxContainer" parent="Layout"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Caption" type="Label" parent="Layout/PrimaryRow"]
|
||||
layout_mode = 2
|
||||
text = "mod_categories/primary"
|
||||
|
||||
[node name="Primary" type="OptionButton" parent="Layout/PrimaryRow"]
|
||||
layout_mode = 2
|
||||
fit_to_longest_item = false
|
||||
text_overrun_behavior = 3
|
||||
size_flags_horizontal = 3
|
||||
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cthangover.Core.Mods;
|
||||
using Cthangover.Core.Scenes;
|
||||
using Cthangover.Core.Utils;
|
||||
using Cthangover.Mods.Interface.Base.Lists;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.Mods.Interface.View
|
||||
{
|
||||
|
||||
/// <summary>Shared ordered category editor. Base checkboxes, custom chips, code completion and primary choice edit the same list.</summary>
|
||||
public partial class ModCategoryEditor : ModWidget
|
||||
{
|
||||
private readonly List<string> selected = new();
|
||||
private readonly List<string> known = new();
|
||||
private readonly Dictionary<string, CheckBox> checks = new(ModCategories.Comparer);
|
||||
private LineEdit input;
|
||||
private OptionButton suggestions;
|
||||
private OptionButton primary;
|
||||
private bool refreshing;
|
||||
private bool editable = true;
|
||||
|
||||
public event Action Changed;
|
||||
/// <summary>Snapshot in publishing order; choosing the primary moves it to the first position.</summary>
|
||||
public List<string> Categories => new(selected);
|
||||
public bool HasPendingInput => !string.IsNullOrWhiteSpace(input?.Text);
|
||||
|
||||
public static ModCategoryEditor Create(IEnumerable<string> categories)
|
||||
{
|
||||
var editor = TscnScenes.LoadAndBuild("scenes/components/mod_category_editor.tscn") as ModCategoryEditor
|
||||
?? throw new InvalidOperationException("Category editor scene unavailable");
|
||||
editor.SetCategories(categories);
|
||||
return editor;
|
||||
}
|
||||
|
||||
public static string ProjectRoot => GameEnvironment.IsRuntime ? GamePaths.Globalize("res://") : System.IO.Directory.GetCurrentDirectory();
|
||||
|
||||
public void SetCategories(IEnumerable<string> categories)
|
||||
{
|
||||
EnsureConstructed();
|
||||
selected.Clear();
|
||||
selected.AddRange(ModCategories.Effective(categories));
|
||||
known.Clear();
|
||||
known.AddRange(ModCategories.Normalize(ModCategoryNames.Known().Concat(selected)));
|
||||
Refresh();
|
||||
}
|
||||
|
||||
public void SetEditable(bool value)
|
||||
{
|
||||
editable = value;
|
||||
EnsureConstructed();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
protected override void Construct()
|
||||
{
|
||||
input = GetNode<LineEdit>("Layout/Input/Code");
|
||||
suggestions = GetNode<OptionButton>("Layout/Suggestions");
|
||||
primary = GetNode<OptionButton>("Layout/PrimaryRow/Primary");
|
||||
input.TooltipText = Tr("mod_categories/hint_code");
|
||||
suggestions.TooltipText = Tr("mod_categories/hint_suggestions");
|
||||
primary.TooltipText = Tr("mod_categories/hint_primary");
|
||||
GetNode<Button>("Layout/Input/Add").TooltipText = Tr("mod_categories/hint_add");
|
||||
GetNode<Button>("Layout/Input/Add").Pressed += OnAdd;
|
||||
input.TextSubmitted += OnSubmitted;
|
||||
input.TextChanged += OnInput;
|
||||
suggestions.ItemSelected += OnSuggestion;
|
||||
primary.ItemSelected += OnPrimary;
|
||||
foreach (var code in ModCategories.Base)
|
||||
{
|
||||
var check = new CheckBox { Name = code, Text = ModCategoryNames.Translate(code), TooltipText = Tr("mod_categories/hint_base") + " · " + code };
|
||||
check.Toggled += value => OnToggle(code, value);
|
||||
GetNode<GridContainer>("Layout/Base").AddChild(check);
|
||||
checks.Add(code, check);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnToggle(string code, bool value)
|
||||
{
|
||||
if (refreshing) return;
|
||||
if (value && !selected.Contains(code, ModCategories.Comparer)) selected.Add(code);
|
||||
else if (!value) selected.RemoveAll(category => ModCategories.Comparer.Equals(category, code));
|
||||
Refresh();
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
private void OnAdd()
|
||||
{
|
||||
var code = input.Text.Trim();
|
||||
if (code.Length == 0) return;
|
||||
if (!selected.Contains(code, ModCategories.Comparer)) selected.Add(code);
|
||||
if (!known.Contains(code, ModCategories.Comparer)) known.Add(code);
|
||||
input.Text = "";
|
||||
Refresh();
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
private void OnSubmitted(string text) => OnAdd();
|
||||
private void OnInput(string text)
|
||||
{
|
||||
GetNode<Button>("Layout/Input/Add").Disabled = !editable || string.IsNullOrWhiteSpace(text);
|
||||
RefreshSuggestions();
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
private void RefreshSuggestions()
|
||||
{
|
||||
suggestions.Clear();
|
||||
suggestions.AddItem(Tr("mod_categories/suggestions"));
|
||||
foreach (var code in known.Where(code => !selected.Contains(code, ModCategories.Comparer) && code.Contains(input.Text.Trim(), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
suggestions.AddItem(ModCategoryNames.Translate(code) + " · " + code);
|
||||
suggestions.SetItemMetadata(suggestions.ItemCount - 1, code);
|
||||
}
|
||||
suggestions.Select(0);
|
||||
suggestions.Disabled = !editable || suggestions.ItemCount == 1;
|
||||
}
|
||||
|
||||
private void OnSuggestion(long index)
|
||||
{
|
||||
if (index > 0) input.Text = suggestions.GetItemMetadata((int)index).AsString();
|
||||
}
|
||||
|
||||
private void OnPrimary(long index)
|
||||
{
|
||||
if (refreshing || index < 0 || index >= selected.Count) return;
|
||||
var code = selected[(int)index];
|
||||
selected.RemoveAt((int)index);
|
||||
selected.Insert(0, code);
|
||||
Refresh();
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
refreshing = true;
|
||||
foreach (var pair in checks)
|
||||
{
|
||||
pair.Value.SetPressedNoSignal(selected.Contains(pair.Key, ModCategories.Comparer));
|
||||
pair.Value.Disabled = !editable;
|
||||
}
|
||||
var custom = GetNode<HFlowContainer>("Layout/Custom");
|
||||
foreach (var child in custom.GetChildren()) { custom.RemoveChild(child); child.QueueFree(); }
|
||||
foreach (var code in selected.Where(code => !ModCategories.Base.Contains(code, ModCategories.Comparer)))
|
||||
{
|
||||
var chip = new Button { Text = ModCategoryNames.Translate(code) + " ×", TooltipText = Tr("mod_categories/hint_remove") + " · " + code, Disabled = !editable, ClipText = true, TextOverrunBehavior = TextServer.OverrunBehavior.TrimEllipsis, CustomMinimumSize = new Vector2(160, 0) };
|
||||
chip.SetMeta("category", code);
|
||||
chip.Pressed += () => OnToggle(code, false);
|
||||
custom.AddChild(chip);
|
||||
}
|
||||
primary.Clear();
|
||||
foreach (var code in selected) primary.AddItem(ModCategoryNames.Translate(code));
|
||||
if (selected.Count == 0) primary.AddItem(ModCategoryNames.Translate(ModCategories.Other));
|
||||
primary.Select(0);
|
||||
primary.Disabled = !editable || selected.Count < 2;
|
||||
input.Editable = editable;
|
||||
GetNode<Button>("Layout/Input/Add").Disabled = !editable || string.IsNullOrWhiteSpace(input.Text);
|
||||
RefreshSuggestions();
|
||||
CustomMinimumSize = new Vector2(Math.Max(440, GetNode<Control>("Layout").GetCombinedMinimumSize().X), GetNode<Control>("Layout").GetCombinedMinimumSize().Y);
|
||||
refreshing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cthangover.Core.Mods;
|
||||
using Cthangover.Core.Utils;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.Mods.Interface.View
|
||||
{
|
||||
/// <summary>Shared presentation keeps the first authored spelling unless a translation is available.</summary>
|
||||
public static class ModCategoryNames
|
||||
{
|
||||
public static string Translate(string category)
|
||||
{
|
||||
category = category?.Trim() ?? ModCategories.Other;
|
||||
var baseName = ModCategories.Base.FirstOrDefault(name => ModCategories.Comparer.Equals(name, category));
|
||||
var key = "mod_category/" + (baseName ?? category);
|
||||
if (!GameEnvironment.IsRuntime) return category;
|
||||
var translated = TranslationServer.Translate(key).ToString();
|
||||
if (translated != key) return translated;
|
||||
// Translation lookup may use a conventional lowercase key; the stored/display fallback stays authored.
|
||||
var fallbackKey = "mod_category/" + category.ToLowerInvariant();
|
||||
translated = TranslationServer.Translate(fallbackKey).ToString();
|
||||
return translated != fallbackKey ? translated : category;
|
||||
}
|
||||
|
||||
/// <summary>Alphabetical display-name order is stable while counts change; Other is always last.</summary>
|
||||
public static List<string> Order(IEnumerable<string> categories)
|
||||
=> ModCategories.Normalize(categories)
|
||||
.OrderBy(category => ModCategories.Comparer.Equals(category, ModCategories.Other) ? 1 : 0)
|
||||
.ThenBy(Translate, StringComparer.OrdinalIgnoreCase).ToList();
|
||||
|
||||
/// <summary>Completion includes installed mods, all cached catalogs and the translated base suggestions.</summary>
|
||||
public static List<string> Known()
|
||||
{
|
||||
var categories = new List<string>();
|
||||
foreach (var info in ModRegistry.Instance.AllDiscoveredMods.Values.OfType<ModInfo>())
|
||||
categories.AddRange(ModCategories.Normalize(info.Manifest?.Categories));
|
||||
foreach (var entry in CatalogService.CachedEntries ?? new List<CatalogExportEntry>())
|
||||
categories.AddRange(ModCategories.Normalize(entry.Categories));
|
||||
categories.AddRange(ModCategories.Base);
|
||||
return Order(categories);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user