# VersionPickerDialog ## Обзор компонента `Minecraft_launcher` — десктопный лаунчер Minecraft на Qt Quick. Каталог версий Mojang — это около тысячи записей: релизы, снапшоты, старые беты и альфы. Раньше выбор версии был обычным выпадающим списком на всю эту тысячу, и найти в нём, скажем, бету 1.7 можно было только поиском по точному номеру. `VersionPickerDialog` заменил тот список отдельным окном: слева категории, справа сами версии с поиском сверху. Категории делят каталог на обозримые части, а поиск работает внутри выбранной. Помимо выбора окно умеет удалять уже скачанные версии — с предупреждением о последствиях, потому что версия весит десятки мегабайт, а поверх неё могут стоять профили модлоадеров. Окно открывается из карточки сборки, когда пользователь выбирает, на какой версии Minecraft собирается играть. ## Место в проекте и зависимости В начале файла объявлена `pragma ComponentBehavior: Bound`. Импортирует `QtQuick`, `QtQuick.Controls 2.15` и `QtQuick.Layouts 2.15`. Работает с C++-типом `LauncherBackend` (`launcherbackend.h`, `QML_ELEMENT` в модуле `Minecraft_launcher`), передаваемым снаружи в свойство `backend`. Читает у него свойства `versionCatalog` и `catalogLoading`, вызывает `refreshVersionCatalog()`, `versionRemovalInfo()` и `removeVersion()`. Объявлен в `QML_FILES` модуля (`CMakeLists.txt`); использует ресурс `images/Trash.svg` из `RESOURCES` того же модуля. Инстанцируется в [BuildsDialog](BuildsDialog.md). Каталог приходит из C++ уже отсортированным (новые сверху), поэтому окно только отбирает записи и порядок не трогает. Каждая запись каталога — объект с полями `id` (идентификатор версии), `label` (подпись строки), `category` (ключ категории), `installed` (скачана ли) и `search` (предвычисленная строка для поиска в нижнем регистре). ## Иерархия и роль Корневой тип — `Dialog`: модальный, 720×480 px, нулевой внутренний отступ, закрывается по Escape и щелчку мимо. Оформление задано вручную — тёмный фон со скруглением и акцентной рамкой. Содержимое — `RowLayout` из двух частей, разделённых вертикальной линией: колонка категорий шириной 150 px (`Repeater` по `categories`) и область версий. В области версий сверху поле поиска, внизу флажок «Только установленные», между ними `ListView` со строками версий. Строка показывает подпись, галочку для установленной версии и корзину удаления; галочка рядом со строкой — единственное место, где видно, что именно скачано. Отдельно объявлен вложенный `Dialog` подтверждения удаления шириной 420 px с красной рамкой. Он привязан к тому же родителю, что и само окно, и центрируется в нём вручную, чтобы не оказаться внутри области выбора. Пустое состояние списка объясняется текстом по центру, который различает три случая: каталог ещё грузится, каталог пуст (нет соединения) и по фильтру ничего не найдено. ## Свойства | Свойство | Тип | По умолчанию | Обязательное | Описание | |----------|-----|--------------|--------------|----------| | `backend` | `var` | — | **Да** | Экземпляр `LauncherBackend` — источник каталога версий и исполнитель удаления. | | `selectedId` | `string` | `""` | Нет | Идентификатор выбранной версии. Окно открывается с текущей версией, и по «Отмене» выбор возвращается к ней, потому что результат уходит наружу только через сигнал. Пустая строка — версия не выбрана, кнопка подтверждения выключена. | | `category` | `string` | `"release"` | Нет | Ключ активной категории. Допустимые значения: `release` (релизы), `snapshot` (снапшоты), `old_beta` (беты), `old_alpha` (альфы), `other` (прочие — сюда попадают в том числе установленные профили модлоадеров). | | `filterText` | `string` | `""` | Нет | Текст поиска. Сравнивается в нижнем регистре без учёта регистра с полем `search` записи каталога; поиск идёт внутри активной категории. | | `installedOnly` | `bool` | `false` | Нет | Показывать только скачанные версии. | | `categories` | `var` (только чтение) | список из пяти записей | Нет | Описание колонки категорий: массив объектов с полями `key` и переведённым `title`. Задаёт и порядок пунктов, и набор допустимых значений `category`. | | `visibleEntries` | `var` (только чтение) | вычисляется | Нет | Отобранные строки каталога: записи активной категории, прошедшие флажок «только установленные» и текст поиска. Порядок наследуется от каталога. Служит моделью списка. | ## Сигналы #### versionChosen(string versionId) Версия подтверждена — кнопкой «Выбрать», двойным щелчком по строке или клавишей Enter в поле поиска. В параметре приходит идентификатор версии. Имя не `accepted()` сознательно: такой сигнал у `Dialog` уже есть и переопределить его нельзя. Обработчик — карточка сборки — записывает выбранную версию в сборку. К моменту вызова обработчика окно уже закрыто. При отмене сигнал не испускается, поэтому снаружи ничего откатывать не нужно. ## Методы #### openFor(string versionId) : void Открывает окно на переданной версии. Запоминает её в `selectedId`, очищает поиск, переключается на категорию именно этой версии (а не всегда на релизы), просит бэкенд обновить каталог и прокручивает список к выбранной строке. #### categoryOf(string versionId) : string Возвращает ключ категории версии по каталогу; для неизвестной версии — `release`. #### indexOfSelected() : int Позиция выбранной версии в `visibleEntries` или `-1`, если под текущим фильтром её не видно. #### revealSelected() : void Выставляет текущий индекс списка на выбранную версию и прокручивает список так, чтобы строка оказалась по центру. Вызывается после каждой смены фильтра, категории или удаления. #### acceptSelection() : void Подтверждает выбор: испускает `versionChosen()` и закрывает окно. При пустом `selectedId` не делает ничего. #### catalogHas(string versionId) : bool Есть ли версия в каталоге. Нужен после удаления: профиль модлоадера присутствовал в каталоге только потому, что был установлен, и после удаления строка исчезает совсем. #### askRemove(string versionId) : void Спрашивает подтверждение перед удалением. Запрашивает у бэкенда `versionRemovalInfo()` и, если версия действительно установлена, наполняет окно подтверждения: занимаемый объём, список зависящих профилей модлоадеров и список сборок, которые эту версию используют. Идентификатор запоминается в самом окне подтверждения, потому что к моменту ответа строка под курсором может быть уже другой. Окно подтверждения объясняет три вещи: файлы удалятся из `versions/` и освободится столько-то мегабайт; профили модлоадеров поверх этой версии без неё не запустятся; библиотеки и ресурсы в `libraries/` и `assets/` общие для всех версий и остаются на месте. #### performRemove(string versionId) : void Удаляет версию через бэкенд. Если удалена была именно выбранная версия и её больше нет в каталоге, снимает выбор. В конце обновляет позицию списка. ## Взаимодействие с другими компонентами **Со стороны родителя.** [BuildsDialog](BuildsDialog.md) задаёт `backend`, открывает окно вызовом `openFor()` с текущей версией сборки и подписывается на `versionChosen()`, чтобы записать выбор. Никаких других точек входа у окна нет — прямая запись `selectedId` снаружи не предполагается. **Со стороны бэкенда.** `versionCatalog` и `catalogLoading` — привязки, от которых зависят и модель списка, и текст пустого состояния: пришедшее обновление каталога пересчитывает `visibleEntries` само. `refreshVersionCatalog()` вызывается при открытии окна, `removeVersion()` — после подтверждения удаления. **Клавиатура.** Фокус при открытии уходит в поле поиска. Стрелки вверх и вниз двигают выбор по списку функцией `step()`, Enter подтверждает выбор, Escape закрывает окно. ## Пример использования ```qml VersionPickerDialog { id: versionPicker parent: Overlay.overlay backend: launcherBackend onVersionChosen: (versionId) => buildCard.gameVersion = versionId } Button { text: buildCard.gameVersion || qsTr("Выбрать версию") onClicked: versionPicker.openFor(buildCard.gameVersion) } ``` --- При создании этого документа использовался ИИ.