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_title = Export Catalog Entry
|
||||||
mods/catalog_download_url = Download URL
|
mods/catalog_download_url = Download URL
|
||||||
mods/catalog_category = Category
|
mods/catalog_category = Category
|
||||||
mods/category_story = Story
|
mod_category/story = Story
|
||||||
mods/category_characters = Characters
|
mod_category/characters = Characters
|
||||||
mods/category_gameplay = Gameplay
|
mod_category/gameplay = Gameplay
|
||||||
mods/category_visual = Visual
|
mod_category/visual = Visual
|
||||||
mods/category_audio = Audio
|
mod_category/audio = Audio
|
||||||
mods/category_ui = UI
|
mod_category/ui = UI
|
||||||
mods/category_locale = Locale
|
mod_category/locale = Locale
|
||||||
mods/category_library = Library
|
mod_category/library = Library
|
||||||
mods/category_engine = Engine
|
mod_category/engine = Engine
|
||||||
mods/category_overhaul = Overhaul
|
mod_category/overhaul = Overhaul
|
||||||
mods/category_tools = Tools
|
mod_category/tools = Tools
|
||||||
mods/category_editor = Editor
|
mod_category/editor = Editor
|
||||||
mods/category_debug = Debug
|
mod_category/debug = Debug
|
||||||
mods/category_other = Other
|
mod_category/other = Other
|
||||||
mods/catalog_tags = Tags (comma-separated)
|
mods/catalog_tags = Tags (comma-separated)
|
||||||
mods/catalog_changelog = Changelog
|
mods/catalog_changelog = Changelog
|
||||||
mods/catalog_status = Status
|
mods/catalog_status = Status
|
||||||
@@ -269,3 +269,23 @@ tools/menu_title = Tools
|
|||||||
|
|
||||||
# --- Development cover ---
|
# --- Development cover ---
|
||||||
ui/cover/in_development = In development
|
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_title = Exportar entrada del catálogo
|
||||||
mods/catalog_download_url = URL de descarga
|
mods/catalog_download_url = URL de descarga
|
||||||
mods/catalog_category = Categoría
|
mods/catalog_category = Categoría
|
||||||
mods/category_story = Historia
|
mod_category/story = Historia
|
||||||
mods/category_characters = Personajes
|
mod_category/characters = Personajes
|
||||||
mods/category_gameplay = Jugabilidad
|
mod_category/gameplay = Jugabilidad
|
||||||
mods/category_visual = Visual
|
mod_category/visual = Visual
|
||||||
mods/category_audio = Audio
|
mod_category/audio = Audio
|
||||||
mods/category_ui = Interfaz
|
mod_category/ui = Interfaz
|
||||||
mods/category_locale = Idioma
|
mod_category/locale = Idioma
|
||||||
mods/category_library = Biblioteca
|
mod_category/library = Biblioteca
|
||||||
mods/category_engine = Motor
|
mod_category/engine = Motor
|
||||||
mods/category_overhaul = Conversión total
|
mod_category/overhaul = Conversión total
|
||||||
mods/category_tools = Herramientas
|
mod_category/tools = Herramientas
|
||||||
mods/category_editor = Editor
|
mod_category/editor = Editor
|
||||||
mods/category_debug = Depuración
|
mod_category/debug = Depuración
|
||||||
mods/category_other = Otros
|
mod_category/other = Otros
|
||||||
mods/catalog_tags = Etiquetas (separadas por comas)
|
mods/catalog_tags = Etiquetas (separadas por comas)
|
||||||
mods/catalog_changelog = Cambios
|
mods/catalog_changelog = Cambios
|
||||||
mods/catalog_status = Estado
|
mods/catalog_status = Estado
|
||||||
|
|||||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = カタログに書き出す
|
|||||||
mods/catalog_title = カタログ項目を書き出す
|
mods/catalog_title = カタログ項目を書き出す
|
||||||
mods/catalog_download_url = ダウンロード URL
|
mods/catalog_download_url = ダウンロード URL
|
||||||
mods/catalog_category = カテゴリー
|
mods/catalog_category = カテゴリー
|
||||||
mods/category_story = ストーリー
|
mod_category/story = ストーリー
|
||||||
mods/category_characters = キャラクター
|
mod_category/characters = キャラクター
|
||||||
mods/category_gameplay = ゲームプレイ
|
mod_category/gameplay = ゲームプレイ
|
||||||
mods/category_visual = ビジュアル
|
mod_category/visual = ビジュアル
|
||||||
mods/category_audio = オーディオ
|
mod_category/audio = オーディオ
|
||||||
mods/category_ui = UI
|
mod_category/ui = UI
|
||||||
mods/category_locale = 言語
|
mod_category/locale = 言語
|
||||||
mods/category_library = ライブラリ
|
mod_category/library = ライブラリ
|
||||||
mods/category_engine = エンジン
|
mod_category/engine = エンジン
|
||||||
mods/category_overhaul = 大型改造
|
mod_category/overhaul = 大型改造
|
||||||
mods/category_tools = ツール
|
mod_category/tools = ツール
|
||||||
mods/category_editor = エディター
|
mod_category/editor = エディター
|
||||||
mods/category_debug = デバッグ
|
mod_category/debug = デバッグ
|
||||||
mods/category_other = その他
|
mod_category/other = その他
|
||||||
mods/catalog_tags = タグ(カンマ区切り)
|
mods/catalog_tags = タグ(カンマ区切り)
|
||||||
mods/catalog_changelog = 変更履歴
|
mods/catalog_changelog = 変更履歴
|
||||||
mods/catalog_status = 状態
|
mods/catalog_status = 状態
|
||||||
|
|||||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = 카탈로그로 내보내기
|
|||||||
mods/catalog_title = 카탈로그 항목 내보내기
|
mods/catalog_title = 카탈로그 항목 내보내기
|
||||||
mods/catalog_download_url = 다운로드 URL
|
mods/catalog_download_url = 다운로드 URL
|
||||||
mods/catalog_category = 분류
|
mods/catalog_category = 분류
|
||||||
mods/category_story = 스토리
|
mod_category/story = 스토리
|
||||||
mods/category_characters = 캐릭터
|
mod_category/characters = 캐릭터
|
||||||
mods/category_gameplay = 게임플레이
|
mod_category/gameplay = 게임플레이
|
||||||
mods/category_visual = 비주얼
|
mod_category/visual = 비주얼
|
||||||
mods/category_audio = 오디오
|
mod_category/audio = 오디오
|
||||||
mods/category_ui = UI
|
mod_category/ui = UI
|
||||||
mods/category_locale = 언어
|
mod_category/locale = 언어
|
||||||
mods/category_library = 라이브러리
|
mod_category/library = 라이브러리
|
||||||
mods/category_engine = 엔진
|
mod_category/engine = 엔진
|
||||||
mods/category_overhaul = 대규모 개편
|
mod_category/overhaul = 대규모 개편
|
||||||
mods/category_tools = 도구
|
mod_category/tools = 도구
|
||||||
mods/category_editor = 에디터
|
mod_category/editor = 에디터
|
||||||
mods/category_debug = 디버그
|
mod_category/debug = 디버그
|
||||||
mods/category_other = 기타
|
mod_category/other = 기타
|
||||||
mods/catalog_tags = 태그 (쉼표로 구분)
|
mods/catalog_tags = 태그 (쉼표로 구분)
|
||||||
mods/catalog_changelog = 변경 기록
|
mods/catalog_changelog = 변경 기록
|
||||||
mods/catalog_status = 상태
|
mods/catalog_status = 상태
|
||||||
|
|||||||
+34
-14
@@ -57,20 +57,20 @@ mods/catalog_export = Экспорт в каталог
|
|||||||
mods/catalog_title = Экспорт записи каталога
|
mods/catalog_title = Экспорт записи каталога
|
||||||
mods/catalog_download_url = URL скачивания
|
mods/catalog_download_url = URL скачивания
|
||||||
mods/catalog_category = Категория
|
mods/catalog_category = Категория
|
||||||
mods/category_story = Сюжет
|
mod_category/story = Сюжет
|
||||||
mods/category_characters = Персонажи
|
mod_category/characters = Персонажи
|
||||||
mods/category_gameplay = Геймплей
|
mod_category/gameplay = Геймплей
|
||||||
mods/category_visual = Визуал
|
mod_category/visual = Визуал
|
||||||
mods/category_audio = Аудио
|
mod_category/audio = Аудио
|
||||||
mods/category_ui = Интерфейс
|
mod_category/ui = Интерфейс
|
||||||
mods/category_locale = Перевод
|
mod_category/locale = Перевод
|
||||||
mods/category_library = Библиотека
|
mod_category/library = Библиотека
|
||||||
mods/category_engine = Движок
|
mod_category/engine = Движок
|
||||||
mods/category_overhaul = Тотальная конверсия
|
mod_category/overhaul = Тотальная конверсия
|
||||||
mods/category_tools = Инструменты
|
mod_category/tools = Инструменты
|
||||||
mods/category_editor = Редактор
|
mod_category/editor = Редактор
|
||||||
mods/category_debug = Отладка
|
mod_category/debug = Отладка
|
||||||
mods/category_other = Прочее
|
mod_category/other = Прочее
|
||||||
mods/catalog_tags = Теги (через запятую)
|
mods/catalog_tags = Теги (через запятую)
|
||||||
mods/catalog_changelog = Список изменений
|
mods/catalog_changelog = Список изменений
|
||||||
mods/catalog_status = Статус
|
mods/catalog_status = Статус
|
||||||
@@ -269,3 +269,23 @@ tools/menu_title = Инструменты
|
|||||||
|
|
||||||
# --- Development cover ---
|
# --- Development cover ---
|
||||||
ui/cover/in_development = В разработке
|
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_title = Xuất mục danh mục
|
||||||
mods/catalog_download_url = URL tải xuống
|
mods/catalog_download_url = URL tải xuống
|
||||||
mods/catalog_category = Thể loại
|
mods/catalog_category = Thể loại
|
||||||
mods/category_story = Cốt truyện
|
mod_category/story = Cốt truyện
|
||||||
mods/category_characters = Nhân vật
|
mod_category/characters = Nhân vật
|
||||||
mods/category_gameplay = Lối chơi
|
mod_category/gameplay = Lối chơi
|
||||||
mods/category_visual = Hình ảnh
|
mod_category/visual = Hình ảnh
|
||||||
mods/category_audio = Âm thanh
|
mod_category/audio = Âm thanh
|
||||||
mods/category_ui = Giao diện
|
mod_category/ui = Giao diện
|
||||||
mods/category_locale = Ngôn ngữ
|
mod_category/locale = Ngôn ngữ
|
||||||
mods/category_library = Thư viện
|
mod_category/library = Thư viện
|
||||||
mods/category_engine = Engine
|
mod_category/engine = Bộ máy
|
||||||
mods/category_overhaul = Đại tu
|
mod_category/overhaul = Đại tu
|
||||||
mods/category_tools = Công cụ
|
mod_category/tools = Công cụ
|
||||||
mods/category_editor = Trình sửa
|
mod_category/editor = Trình sửa
|
||||||
mods/category_debug = Gỡ lỗi
|
mod_category/debug = Gỡ lỗi
|
||||||
mods/category_other = Khác
|
mod_category/other = Khác
|
||||||
mods/catalog_tags = Thẻ (cách nhau bằng dấu phẩy)
|
mods/catalog_tags = Thẻ (cách nhau bằng dấu phẩy)
|
||||||
mods/catalog_changelog = Nhật ký thay đổi
|
mods/catalog_changelog = Nhật ký thay đổi
|
||||||
mods/catalog_status = Trạng thái
|
mods/catalog_status = Trạng thái
|
||||||
|
|||||||
+14
-14
@@ -57,20 +57,20 @@ mods/catalog_export = 导出到目录
|
|||||||
mods/catalog_title = 导出目录条目
|
mods/catalog_title = 导出目录条目
|
||||||
mods/catalog_download_url = 下载 URL
|
mods/catalog_download_url = 下载 URL
|
||||||
mods/catalog_category = 分类
|
mods/catalog_category = 分类
|
||||||
mods/category_story = 剧情
|
mod_category/story = 剧情
|
||||||
mods/category_characters = 角色
|
mod_category/characters = 角色
|
||||||
mods/category_gameplay = 玩法
|
mod_category/gameplay = 玩法
|
||||||
mods/category_visual = 视觉
|
mod_category/visual = 视觉
|
||||||
mods/category_audio = 音频
|
mod_category/audio = 音频
|
||||||
mods/category_ui = 界面
|
mod_category/ui = 界面
|
||||||
mods/category_locale = 语言
|
mod_category/locale = 语言
|
||||||
mods/category_library = 库
|
mod_category/library = 库
|
||||||
mods/category_engine = 引擎
|
mod_category/engine = 引擎
|
||||||
mods/category_overhaul = 大型改动
|
mod_category/overhaul = 大型改动
|
||||||
mods/category_tools = 工具
|
mod_category/tools = 工具
|
||||||
mods/category_editor = 编辑器
|
mod_category/editor = 编辑器
|
||||||
mods/category_debug = 调试
|
mod_category/debug = 调试
|
||||||
mods/category_other = 其他
|
mod_category/other = 其他
|
||||||
mods/catalog_tags = 标签(逗号分隔)
|
mods/catalog_tags = 标签(逗号分隔)
|
||||||
mods/catalog_changelog = 更新日志
|
mods/catalog_changelog = 更新日志
|
||||||
mods/catalog_status = 状态
|
mods/catalog_status = 状态
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"author": "ct",
|
"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.",
|
"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": [],
|
"depends": [],
|
||||||
"sources": [
|
"sources": [
|
||||||
"src/**/*.cs"
|
"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