# Main.qml Главное окно, оно же точка входа: `main.cpp` загружает его через `engine.loadFromModule("BiLauncher", "Main")`. Корень — `Window` 1280×720. Что в нём есть: - единственный экземпляр `LauncherBackend` и все обработчики его сигналов; - экран запуска: фон, кнопка игры по центру, список профилей, кнопка активной сборки, кнопки папки модов, настроек и сезонных сборок; - плашка сообщений и две панели прогресса ([ProgressPanel](ProgressPanel.md)); - диалоги, которые не вынесены в отдельные файлы: новый профиль, редактирование профиля, код двухфакторной аутентификации, настройки запуска. Отдельными файлами сделаны [BuildsDialog](BuildsDialog.md), [SeasonalBuildsDialog](SeasonalBuildsDialog.md), [JavaPickerDialog](JavaPickerDialog.md) и [MicrosoftLoginDialog](MicrosoftLoginDialog.md). Окно передаёт им бэкенд через свойство `backend`. Стиль Qt Quick Controls — `Basic` (выставлен в `main.cpp`), и почти у каждого контрола здесь своё оформление. ## Раскладка Всё стоит на якорях вокруг кнопки запуска: список профилей слева сверху от неё, кнопка активной сборки справа сверху, кнопки папки и настроек под списком профилей. Кнопка сезонных сборок стоит в правом нижнем углу, потому что остальные углы заняты. Обе панели прогресса (загрузки и смены сборки) стоят в левом нижнем углу на одних и тех же якорях. Одновременно они не появляются: пока лаунчер занят загрузкой, бэкенд не начнёт смену сборки, и наоборот. Панель смены сборки отменить нельзя. У заголовков и подвалов всех диалогов корнем служит `Item` с явным `implicitHeight`. У `Rectangle` `implicitHeight` всегда 0, и `Dialog` не смог бы посчитать свою высоту. ## Функции **`showToast(text, color, timeout)`** — показать сообщение в плашке. `timeout` в миллисекундах, по умолчанию 4000. `0` значит «держать до следующего сообщения». Так показываются шаги запуска, чтобы сообщение не исчезало посреди долгой операции. **`openMicrosoftLogin(url)`** — открыть окно входа Microsoft. Окно создаётся при первом вызове через `Qt.createComponent()` и хранится в `microsoftLoginDialog`. Объявить его прямо в файле нельзя: `MicrosoftLoginDialog.qml` есть в модуле только в сборках с WebEngine, и в остальных сломалось бы всё главное окно. Если компонент не создался, вход отменяется у бэкенда и показывается сообщение. **`formatMb(bytes)`** — байты в мегабайты с одним знаком после запятой, для панели загрузки. ## Сигналы бэкенда | Сигнал | Что делает окно | |--------|-----------------| | `launched` | зелёное сообщение «запущено» | | `launchProgress` | сообщение без таймаута | | `launchError` | красное сообщение на 8 секунд | | `twoFactorRequired` | открывает диалог кода | | `gameFinished` | сообщение о закрытии игры; если код не 0 или процесс упал, красное и с кодом | | `microsoftLoginUrlReady` | `openMicrosoftLogin()` | | `microsoftLoginSucceeded` | сообщение. Выбор в списке профилей не трогает: новый профиль уже выбран тем, кто его создал, а повторный вход мог быть не для последнего профиля | | `microsoftLoginFailed` | красное сообщение на 8 секунд | | `microsoftReloginRequired` | сразу `startMicrosoftLogin()` для этого профиля | | `gameOutput` | `console.log` | | `seasonalInstallFinished` | сообщение «сборка установлена, можно играть» | | `javaRuntimeInstalled` | обновляет строку Java в настройках | Кнопка запуска выключена, пока `busy` или `gameRunning`. Кнопка активной сборки выключена во время игры и смены сборки. ## Профили Выпадающий список профилей переделан целиком. Наверху всплывающей панели закреплена кнопка «+ Добавить профиль», под ней список. У каждой строки при наведении появляются карандаш (открывает редактирование через `openFor()`) и корзина (`removeProfile()`). Типы входа во всех списках идут в одном порядке: 0 — `offline`, 1 — `elyby`, 2 — `microsoft`. Модель берётся из `Loc.t.profile.authTypes`, и порядок в каталоге важен, потому что код сравнивает `currentIndex` с 1 и 2. Без WebEngine третий пункт отрезается через `.slice(0, 2)`. В окне редактирования он остаётся, если профиль уже лицензионный: иначе в сборке без WebEngine такой профиль молча стал бы офлайновым. **Новый профиль.** `addProfile()`, новый профиль выбирается в списке. Если тип «Microsoft», сразу начинается вход, потому что без него такой профиль бесполезен. Скрытые поля при сохранении не читаются: в них мог остаться текст, набранный до смены типа. **Редактирование.** Дополнительные свойства диалога: `editIndex`, `msProfile`, `msLinked` (есть ли сессия Microsoft), `msName` (ник из аккаунта). Ник показывается отдельным полем только для чтения, а не подставляется в поле логина. Иначе привязка оборвалась бы при первом же вводе в это поле у обычного профиля. Кнопка входа сначала сохраняет профиль с типом `microsoft` через `updateProfile()` и только потом вызывает `startMicrosoftLogin()`. Тип могли переключить только что, и без этого токены достались бы профилю другого типа. ## Двухфакторная аутентификация Диалог показывает имя профиля (`profileName`) и поле для кода. «Подтвердить» вызывает `submitTwoFactorCode()`, «Отмена» — `cancelPendingLaunch()`. Если отмену не вызвать, запуск так и повиснет. ## Настройки `settingsDialog.load()` читает `settings()` и `detectedJava()`, раскладывает значения по полям и открывает диалог. «Сохранить» собирает всё в одну карту и отдаёт в `updateSettings()`. - **Язык** стоит первым, потому что относится ко всему приложению, а не к запуску игры. В модели переведённые подписи, рядом лежит непереводимый список `codes` (`system`, `ru`, `en`). Язык применяется по «Сохранить» и сразу, без перезапуска. - **Папка игры**, а под полем — путь, который получится на самом деле (`resolvedGameDir`). Путь хранится в свойстве, а не записывается в текст напрямую, иначе подпись не обновилась бы при смене языка. - **Java.** Поле открывает [JavaPickerDialog](JavaPickerDialog.md) с текущим выбором и требованием активной сборки (`requiredJavaMajor()`). Крестик сбрасывает выбор, и тогда Java ищется в системе. Выбор хранится в `javaRuntimeId` и записывается только по «Сохранить». `javaRuntimeInfo` обновляется при смене `javaRuntimeId` и по `javaRuntimeInstalled`, потому что `javaRuntimeInfo()` — обычный вызов, а не привязка. Если сборка выбрана, запуск идёт ею, а путь из соседнего поля остаётся запасным вариантом. - Память, размер окна, аргументы JVM, полноэкранный режим. - Ниже для справки список найденных в системе java. Окно выбора Java объявлено рядом с диалогом настроек, а не внутри него. Оно шире настроек и центрируется по окну лаунчера. ## Кнопки - папка — `openMinecraftFolder()` (открывает `mods`); - настройки — `settingsDialog.load()`; - сезонные сборки — `seasonalDialog.openCatalog()`; - активная сборка — `buildsDialog.open()`. Все тексты окна берутся из `Loc.t`, литералов в разметке нет. --- При создании этого документа использовался ИИ.