# SeasonalBuildsDialog ## Обзор компонента `Minecraft_launcher` — десктопный лаунчер Minecraft на Qt Quick. Кроме сборок, которые пользователь собирает сам, он умеет ставить готовые сезонные сборки с сервера лаунчера: набор модов под конкретную версию игры и модлоадер, подготовленный заранее и выдаваемый целиком. `SeasonalBuildsDialog` — окно этого каталога: таблица со всем, что нужно знать, чтобы решить, ставить сборку или нет, и одна кнопка, которая делает всё остальное — заводит сборку, ставит версию игры, модлоадер, Java и раскладывает файлы. Окно также показывает, что установленная сборка устарела, и предлагает обновить её до свежей ревизии. Таблица собрана из строк `Row` с фиксированными колонками, а не из `TableView`: в проекте нет ни одной модели `QAbstractItemModel`, а строки приходят готовыми `QVariantMap` — заводить ради семи колонок отдельную модель незачем. ## Место в проекте и зависимости В начале файла объявлена `pragma ComponentBehavior: Bound`. Импортирует `QtQuick` и `QtQuick.Controls 2.15` — слои `QtQuick.Layouts` здесь не нужны, вся раскладка на якорях и фиксированных ширинах колонок. Работает с C++-типом `LauncherBackend` (`launcherbackend.h`, `QML_ELEMENT` в модуле `Minecraft_launcher`), передаваемым снаружи в свойство `backend`. Читает свойства `seasonalCatalog`, `seasonalCatalogLoading`, `seasonalCatalogError`, `seasonalInstalling` и `busy`, вызывает `refreshSeasonalCatalog()` и `installSeasonalBuild()`. Объявлен в `QML_FILES` модуля (`CMakeLists.txt`). Инстанцируется в [Main](Main.md). Строки каталога приходят из C++ уже отсортированными и сведёнными с локальными записями — окно показывает статус, не считая ничего само. Запись содержит поля `id`, `name`, `description`, `minecraftVersion`, `loaderTitle`, `loaderVersion`, `modCount`, `seasonStart`, `seasonEnd`, `status`, `revision`, `installedRevision`, `updateAvailable`, `sizeBytes` и `serverUrl`. ## Иерархия и роль Корневой тип — `Dialog`: модальный, 960×560 px, нулевой внутренний отступ, тёмный фон со скруглением и акцентной рамкой. Политика закрытия зависит от состояния: пока идёт установка, окно закрывается только кнопкой (`Popup.NoAutoClose`), потому что случайный щелчок мимо не должен спрятать единственную видимую отмену; в остальное время работают Escape и щелчок мимо. Содержимое — шапка таблицы (`Row` с `Repeater` по `columns`), разделительная линия, список строк и сообщение по центру для пустого состояния. Строка списка — `Rectangle` высотой 36 px с вложенным `Row`, который повторяет тот же набор колонок; ширины берутся из общего описания `columns`, поэтому шапка и строки не могут разъехаться. Подвал высотой 76 px несёт описание и размер выбранной сборки (они длинные и в таблицу не помещаются, а решение принимается именно по ним) и две кнопки — обновления списка и установки. ## Свойства | Свойство | Тип | По умолчанию | Обязательное | Описание | |----------|-----|--------------|--------------|----------| | `backend` | `var` | — | **Да** | Экземпляр `LauncherBackend` — источник каталога сезонных сборок и исполнитель установки. | | `selectedId` | `string` | `""` | Нет | Идентификатор выбранной строки. Хранится по `id`, а не по индексу: список обновляется под руками, и индекс после обновления указывал бы на другую сборку. | | `columns` | `var` (только чтение) | список из семи колонок | Нет | Описание таблицы: массив объектов с полями `key` (поле записи), `title` (заголовок), `width` (ширина в пикселях) и `align` (выравнивание). Колонки: название, версия, загрузчик, число модов, начало и конец сезона, статус. Ширины собраны в одном месте, потому что их повторяют и шапка, и делегат строки. | | `entries` | `var` (только чтение) | `backend.seasonalCatalog` | Нет | Строки каталога напрямую из бэкенда. Отбора и сортировки в окне нет. | | `selectedEntry` | `var` (только чтение) | вычисляется | Нет | Полная запись выбранной сборки или `null`, если ничего не выбрано. От неё зависят подвал и доступность кнопки установки. | ## Сигналы Собственных сигналов компонент не объявляет. Результат работы окна виден через состояние бэкенда: установка меняет список сборок и запускает загрузку, за ходом которой следит главное окно. ## Методы #### openCatalog() : void Открывает окно. Обновление каталога происходит само в обработчике `onAboutToShow`, который просит у бэкенда `refreshSeasonalCatalog(false)` — без принудительного обхода кэша: свежий кэш отвечает без сети, поэтому вызов при каждом открытии ничего не стоит. #### formatMb(bytes) : string Переводит размер в байтах в строку с мегабайтами и одним знаком после запятой. Для нулевого или отсутствующего значения возвращает пустую строку, чтобы размер просто не попал в строку подвала. #### cellText(entry, string key) : string Возвращает текст ячейки для записи и ключа колонки. Для всех колонок это значение одноимённого поля записи, приведённое к строке; исключение — колонка загрузчика, где название и версия склеиваются в одну подпись, а при пустой версии остаётся только название. #### installSelected() : void Ставит выбранную сборку. Ничего не делает, если строка не выбрана или лаунчер занят другой операцией: установка занимает и панель загрузки, и `.minecraft` целиком, поэтому вторую начинать нельзя. Иначе вызывает `installSeasonalBuild()` у бэкенда. Вызывается кнопкой установки и двойным щелчком по строке. ## Взаимодействие с другими компонентами **Со стороны родителя.** [Main](Main.md) задаёт `backend` и открывает окно вызовом `openCatalog()`. Обратной связи наружу через сигналы нет. **Со стороны бэкенда.** Всё содержимое таблицы — привязка к `seasonalCatalog`, поэтому обновление каталога и изменение статуса установленной сборки перерисовывают окно сами. Пустое состояние различает три случая по `seasonalCatalogLoading` и `seasonalCatalogError`: идёт загрузка, сборок пока нет, произошла ошибка — её текст показывается прямо на месте строк, потому что пустой список и ошибка выглядят одинаково пустыми. **Занятость.** Кнопка установки выключается по общему признаку `busy`, кнопка обновления списка — по `seasonalCatalogLoading` (её подпись при этом меняется на «Обновление…»). Политика закрытия окна завязана на `seasonalInstalling`. **Обновление ревизии.** Если у записи выставлен `updateAvailable`, статус в таблице подсвечивается акцентным цветом, в подвале дописывается установленная ревизия, а кнопка установки называется «Обновить». Отдельного пути обновления нет — это тот же вызов `installSeasonalBuild()`. **Ход установки.** Окно не показывает прогресс: за это отвечает плашка [ProgressPanel](ProgressPanel.md) в главном окне, а отмена — метод `cancelSeasonalInstall()` бэкенда. ## Пример использования ```qml SeasonalBuildsDialog { id: seasonalDialog parent: Overlay.overlay backend: launcherBackend } Button { text: qsTr("Сезонные сборки") onClicked: seasonalDialog.openCatalog() } ``` --- При создании этого документа использовался ИИ.