tests / macos (push) Successful in 1m18s
tests / package-smoke-linux (push) Skipped
tests / package-smoke-windows (push) Skipped
tests / package-smoke-macos (push) Skipped
tests / linux (push) Successful in 2m17s
tests / windows (push) Failing after 14m10s
Signed-off-by: galeon <dertiellis@gmail.com>
134 lines
11 KiB
Markdown
134 lines
11 KiB
Markdown
# 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`. Список профилей выключен, пока `busy`;
|
||
кнопка активной сборки — пока `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`, литералов в разметке нет.
|
||
|
||
---
|
||
|
||
При создании этого документа использовался ИИ.
|