# LoaderRow ## Обзор компонента `Minecraft_launcher` — десктопный лаунчер Minecraft на Qt Quick. Кроме чистой игры он умеет ставить модлоадеры: Forge, Fabric, NeoForge и Quilt. В карточке сборки лоадер выбирается чекбоксом, а под ним — конкретная версия лоадера. `LoaderRow` — одна такая строка: чекбокс с названием лоадера и, когда он отмечен, выпадающий список версий именно под выбранную версию Minecraft. Компонент берёт на себя всю возню со списком версий: подтягивает его из кэша, обновляет по сети, следит, чтобы выбранная версия всегда существовала под текущую версию игры, и словами объясняет случай «лоадер эту версию игры не поддерживает» вместо показа пустого списка. Совместимость компонент не проверяет и проверять не должен: бэкенд отдаёт список, уже собранный под конкретную версию игры, поэтому несовместимой строки в нём не бывает. Пустой список — это и есть отсутствие поддержки. ## Место в проекте и зависимости Импортирует `QtQuick` и `QtQuick.Controls 2.15`. Использует компонент [DarkCombo](DarkCombo.md) из того же QML-модуля — выпадающий список версий в тёмном стиле окна. Работает с C++-типом `LauncherBackend` (`launcherbackend.h`, зарегистрирован через `QML_ELEMENT` в модуле `Minecraft_launcher`), который передаётся снаружи в свойство `backend`. Из него компонент вызывает `loaderVersions()`, `refreshLoaderVersions()` и `loaderVersionsLoading()`, а также слушает сигнал `loaderVersionsChanged`. Объявлен в `QML_FILES` модуля `Minecraft_launcher` (`CMakeLists.txt`). Инстанцируется в [BuildsDialog](BuildsDialog.md) — по одной строке на каждый поддерживаемый лоадер. ## Иерархия и роль Корневой тип — `Column` с расстоянием 4 px. Внутри три потомка, видимость которых взаимоисключающая по нижней части: - `CheckBox` с полностью переопределённым индикатором (квадрат со скруглением и галочкой) и подписью. Выключен, пока не выбрана версия игры. - [DarkCombo](DarkCombo.md) со списком версий лоадера — виден, только когда чекбокс отмечен и список непустой. - Текстовая строка на месте списка — видна, когда чекбокс отмечен, а список пуст. Пока идёт запрос, она серая и говорит о загрузке; когда запрос закончен, она красноватая и сообщает, что лоадер не поддерживает выбранную версию Minecraft. Ширина внутренних элементов считается от ширины колонки, поэтому снаружи достаточно задать `width`. ## Свойства | Свойство | Тип | По умолчанию | Обязательное | Описание | |----------|-----|--------------|--------------|----------| | `backend` | `var` | — | **Да** | Экземпляр `LauncherBackend`. Через него запрашиваются и обновляются списки версий лоадера. | | `loaderKey` | `string` | — | **Да** | Ключ лоадера, которым он опознаётся в бэкенде и в сохранённой сборке: `forge`, `fabric`, `neoforge`, `quilt`. | | `title` | `string` | — | **Да** | Человекочитаемое название лоадера рядом с чекбоксом; оно же подставляется в сообщения о загрузке и об отсутствии поддержки. | | `gameVersion` | `string` | `""` | Нет | Версия Minecraft, выбранная в карточке. Пустая строка выключает чекбокс. Смена значения сбрасывает выбранную версию лоадера и перезапрашивает список. | | `checked` | `bool` (алиас на чекбокс) | `false` | Нет | Отмечен ли лоадер. Чтение даёт текущее состояние, запись переключает чекбокс программно — без сигнала `userChecked()`. | | `selectedVersion` | `string` | `""` | Нет | Выбранная версия лоадера. Устанавливается только через `applyEntries()`, поэтому всегда либо пуста, либо присутствует в текущем списке. | | `entries` | `var` | `[]` | Нет | Текущий список версий лоадера — массив записей, у каждой есть поля `version` (значение) и `label` (подпись для списка). Заполняется из бэкенда. | | `applying` | `bool` | `false` | Нет | Признак того, что строку сейчас заполняет карточка данными сохранённой сборки. Пока он выставлен, изменения не считаются пользовательскими и сигнал `changed()` не испускается. | ## Сигналы #### userChecked() Пользователь сам отметил чекбокс (не программная установка `checked`). Карточка сборки в ответ снимает отметки с остальных строк лоадеров: одновременно в `.minecraft` может жить только один лоадер. #### changed() Отметка или версия лоадера изменились и это изменение пользовательское. Обработчик — карточка сборки — сохраняет сборку с новыми значениями. Сигнал сознательно не испускается, пока выставлен `applying`, то есть при заполнении строки из уже сохранённой сборки: иначе загрузка карточки сразу же приводила бы к её перезаписи. ## Методы #### versionIndex(string version) : int Возвращает позицию версии в текущем массиве `entries` или `-1`, если такой версии в списке нет. Вспомогательная функция для синхронизации выбранного значения с выпадающим списком. #### applyEntries(var list) : void Единственное место, где меняются `entries` и `selectedVersion`. Через него проходят все три пути получения списка — кэш, ответ сети и заполнение из сохранённой сборки, — потому что выбранная версия обязана существовать в списке под текущую версию игры. Записывает новый список, а затем проверяет выбранную версию: если её в списке нет, подставляет первую строку (список отсортирован новыми вперёд, поэтому первая — максимально доступная под эту версию игры) или пустую строку для пустого списка. Если подстановка изменила значение и строка не находится в режиме `applying`, испускает `changed()`. В конце синхронизирует `currentIndex` выпадающего списка. #### reload() : void Перезапрашивает список версий. Если лоадер не отмечен или версия игры не выбрана, очищает список через `applyEntries([])`. Иначе сначала берёт список из кэша бэкенда (`loaderVersions()`) — он появляется мгновенно, — а затем просит обновление по сети (`refreshLoaderVersions()`), результат которого придёт позже сигналом. #### applyBuild(string loader, string loaderVersion) : void Заполняет строку данными сохранённой сборки, не испуская `changed()`: на время работы выставляет `applying`. Отмечает чекбокс, если ключ лоадера сборки совпадает с `loaderKey`, подставляет версию из сборки как пожелание и вызывает `reload()`. Если под выбранную версию игры такой версии лоадера нет, `applyEntries()` заменит её на максимально доступную. ## Взаимодействие с другими компонентами **Что приходит извне.** Карточка сборки в [BuildsDialog](BuildsDialog.md) задаёт `backend`, `loaderKey`, `title` и привязывает `gameVersion` к версии Minecraft, выбранной в карточке. Заполнение сохранённой сборкой идёт вызовом `applyBuild()` снаружи. **Что уходит наружу.** По `userChecked()` карточка снимает отметки с остальных строк — набор строк она держит в собственном списке. По `changed()` карточка сохраняет сборку, читая `checked` и `selectedVersion`. **Бэкенд.** Компонент сам подписан на сигнал `loaderVersionsChanged(key, game)` через `Connections`: пришедшее обновление принимается, только если ключ и версия игры совпадают с текущими и чекбокс отмечен, — иначе ответ относится к другой строке или устарел. Текст в пустом состоянии опрашивает `loaderVersionsLoading()`, чтобы отличать «ещё грузим» от «не поддерживается». **Реакция на смену версии игры.** Обработчик `onGameVersionChanged` сбрасывает `selectedVersion` и вызывает `reload()`: сборка лоадера привязана к версии игры, поэтому под новой версией прежний выбор недействителен. ## Пример использования ```qml LoaderRow { id: forgeRow width: parent.width backend: launcherBackend loaderKey: "forge" title: "Forge" gameVersion: buildCard.gameVersion onUserChecked: buildCard.keepOnly(forgeRow) onChanged: buildCard.commitSelection() } ``` --- При создании этого документа использовался ИИ.