19 KiB
Main
Обзор компонента
Minecraft_launcher — десктопный лаунчер Minecraft на Qt Quick. Main.qml — его главное и
единственное настоящее окно: точка входа приложения, которую загружает main.cpp вызовом
engine.loadFromModule("Minecraft_launcher", "Main").
Окно совмещает четыре роли. Оно держит единственный экземпляр LauncherBackend — весь остальной
интерфейс получает его от главного окна. Оно рисует сам экран запуска: фоновая картинка, большая
кнопка игры по центру, выпадающий список профилей, кнопка активной сборки, кнопки папки модов,
настроек и сезонных сборок. Оно показывает обратную связь — всплывающую плашку сообщений и две
панели хода долгих операций. И наконец, оно объявляет диалоги, которые не вынесены в отдельные
файлы: создание и редактирование профиля, ввод кода двухфакторной аутентификации и настройки
запуска.
Место в проекте и зависимости
Импортирует QtQuick, QtQuick.Layouts 2.15, QtQuick.Controls 2.15 и сам QML-модуль проекта
Minecraft_launcher, из которого приходит тип LauncherBackend.
Инстанцирует четыре компонента модуля: ProgressPanel (дважды), SeasonalBuildsDialog, BuildsDialog, JavaPickerDialog, а также DarkCombo и LabelledField внутри своих диалогов. MicrosoftLoginDialog создаётся динамически — см. ниже.
Стиль Qt Quick Controls принудительно выставлен в Basic в main.cpp, потому что нативные стили
игнорируют пользовательские contentItem и background; поэтому в этом файле почти каждый
элемент управления переопределяет своё оформление вручную.
Использует ресурсы из RESOURCES QML-модуля: фоновую картинку, три состояния кнопки запуска, по
три состояния кнопок папки и настроек, стрелки выпадающих списков, images/Trash.svg и
images/Pencil.svg.
Иерархия и роль
Корневой тип — Window размером 1280×720 px, видимое при старте. Это не переиспользуемый
компонент, а точка входа приложения, поэтому раздел с примером использования здесь неприменим.
Раскладка держится на якорях относительно центральной кнопки запуска: список профилей — слева сверху от неё, кнопка активной сборки — справа сверху, кнопки папки и настроек — под списком профилей. Кнопка сезонных сборок стоит в правом нижнем углу: это единственная свободная часть окна, потому что панели хода работ висят слева, а всё остальное собрано вокруг кнопки запуска.
Панели загрузки и смены сборки имеют одни и те же якоря — они взаимоисключающи по построению:
признак занятости бэкенда не даёт начать переключение во время установки и наоборот. Панель смены
сборки объявлена неотменяемой: отступать после очистки .minecraft некуда, операцию нужно довести
до конца.
Заголовки и подвалы всех диалогов сделаны на Item с явным implicitHeight, а не на
Rectangle: у прямоугольника implicitHeight равен нулю независимо от заданной высоты, и
Dialog не смог бы вычислить свою полную высоту.
Свойства
| Свойство | Тип | По умолчанию | Обязательное | Описание |
|---|---|---|---|---|
microsoftLoginDialog |
var |
null |
Нет | Созданный по требованию экземпляр окна входа Microsoft либо null, пока вход ни разу не запускался. Хранится в свойстве, чтобы окно создавалось один раз за сеанс. |
Внутренние диалоги и их состояние
Main.qml объявляет четыре диалога прямо в файле. Их свойства — часть состояния главного окна.
Диалог редактирования профиля (editProfileDialog):
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
editIndex |
int |
-1 |
Индекс редактируемого профиля; -1 — диалог не открыт ни для кого. |
msProfile |
bool |
false |
Открытый профиль имеет тип «Microsoft». |
msLinked |
bool |
false |
У профиля есть действующая сессия Microsoft — от этого зависит строка статуса и подпись кнопки входа. |
msName |
string |
"" |
Ник, полученный при официальной авторизации. Показывается отдельным полем только для чтения, а не подменой поля логина: привязка сломалась бы первым же вводом в поле логина обычного профиля. |
Диалог двухфакторной аутентификации (twoFactorDialog):
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
profileName |
string |
"" |
Имя профиля, для которого запрошен код; подставляется в текст просьбы. |
Диалог настроек (settingsDialog):
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
javaRuntimeId |
string |
"" |
Выбранная сборка Java из папки лаунчера. Живёт в свойстве, а не в поле ввода: её выбирают в отдельном окне, а записывается она только по «Сохранить». Пустая строка означает «искать Java в системе». |
javaRuntimeInfo |
var |
null |
Подробности выбранной сборки для строки поля. Не привязка: javaRuntimeInfo() — обычный вызов, и сам он не пересчитается, когда сборка докачается, поэтому значение обновляется по событиям. |
Типы профилей во всех выпадающих списках кодируются одинаково: позиция 0 — offline
(офлайн, без пароля), 1 — elyby (Ely.by, с логином и паролем), 2 — microsoft (лицензия).
Третья позиция показывается, только когда лаунчер собран с Qt WebEngine; в диалоге редактирования
она показывается ещё и тогда, когда профиль уже сохранён как лицензионный — иначе в сборке без
WebEngine он молча стал бы офлайновым.
Сигналы
Собственных сигналов главное окно не объявляет.
Методы
showToast(string text, color color, int timeout) : void
Показывает единую всплывающую плашку сообщений: через неё проходят сообщения о ходе запуска, ошибки и статус игры. Задаёт текст и цвет фона и перезапускает таймер скрытия.
Параметр timeout — время показа в миллисекундах. Пропущенное значение означает четыре секунды;
0 означает «держать до следующего сообщения» — так показываются промежуточные шаги запуска, чтобы
сообщение не исчезало посреди долгой операции.
openMicrosoftLogin(url) : void
Открывает окно входа в аккаунт Microsoft на переданном адресе, создавая его при первом вызове.
Окно создаётся по требованию, а не вместе с главным: MicrosoftLoginDialog.qml попадает в модуль
только в сборках с Qt WebEngine, и обычная декларация сломала бы всё главное окно в остальных.
Поэтому компонент загружается через Qt.createComponent(), и если он не готов — сборка собрана без
WebEngine, — вход отменяется у бэкенда, а пользователю показывается сообщение о том, что окно
недоступно. При успешном создании окну сразу передаётся бэкенд, а его сигнал failed
подключается к плашке сообщений.
formatMb(bytes) : string
Переводит байты в мегабайты с одним знаком после запятой. Используется в строке подробностей панели загрузки.
Взаимодействие с другими компонентами
Бэкенд
Единственный экземпляр LauncherBackend объявлен прямо в окне и передаётся всем вложенным
диалогам через их свойство backend. Главное окно — единственное место, где обрабатываются его
сигналы:
| Сигнал бэкенда | Что делает главное окно |
|---|---|
launched(profileName, buildName, serverUrl) |
показывает зелёное сообщение о запуске |
launchProgress(message) |
показывает сообщение без таймаута — до следующего шага |
launchError(message) |
показывает ошибку на восемь секунд |
twoFactorRequired(profileName) |
открывает диалог ввода кода: Ely.by отклонил пароль с пометкой two factor, и код добирается здесь, чтобы продолжить прерванный запуск |
gameFinished(exitCode, crashed) |
сообщает о закрытии игры; аварийное завершение показывается красным вместе с кодом выхода |
microsoftLoginUrlReady(url) |
вызывает openMicrosoftLogin() |
microsoftLoginSucceeded(playerName) |
сообщает об успешном входе. Выбор в списке профилей при этом не трогается: новый профиль уже выбран тем, кто его создал, а повторный вход мог быть и не в последний профиль |
microsoftLoginFailed(message) |
показывает ошибку на восемь секунд |
microsoftReloginRequired(profileIndex) |
сразу начинает вход заново для этого профиля |
gameOutput(line) |
пишет строку в консоль |
seasonalInstallFinished(seasonalId, buildName) |
сообщает, что сезонная сборка установлена и её можно запускать |
javaRuntimeInstalled(runtimeId) |
обновляет подробности выбранной сборки Java в настройках |
Привязки к свойствам бэкенда управляют доступностью интерфейса: кнопка запуска выключена, пока
лаунчер занят или игра уже идёт; кнопка активной сборки — пока идёт игра или переключение сборок;
подпись на ней берётся из activeBuildName, а список профилей — из profileNames.
Профили
Выпадающий список профилей переопределён целиком: кнопка «+ Добавить профиль» закреплена сверху
всплывающей панели, под ней список, где у строки при наведении появляются карандаш и корзина.
Карандаш открывает диалог редактирования (openFor() заполняет его через profileAt()), корзина
вызывает removeProfile().
Создание профиля вызывает addProfile(), выбирает новый профиль в списке и, если тип —
«Microsoft», сразу начинает вход: такой профиль без входа бесполезен. Скрытые поля при сохранении
не читаются — в них мог остаться текст, набранный до переключения типа профиля.
В диалоге редактирования кнопка входа перед вызовом startMicrosoftLogin() сначала сохраняет
профиль вызовом updateProfile() с типом microsoft: тип мог быть только что переключён, и без
этого бэкенд приписал бы токены профилю другого типа.
Запуск игры
Кнопка запуска вызывает launchGame() с индексом выбранного профиля и индексом активной сборки.
Дальше всё идёт через сигналы бэкенда: промежуточные шаги — в плашку сообщений, запрос кода
двухфакторной аутентификации — в отдельный диалог, где подтверждение вызывает
submitTwoFactorCode(), а отмена — cancelPendingLaunch().
Настройки
Диалог настроек открывается методом load(), который читает settings() бэкенда и раскладывает
значения по полям, а также подставляет разрешённый путь папки игры и список найденных в системе
сборок Java (detectedJava()). Сохранение собирает все поля в один QVariantMap и передаёт его
в updateSettings().
Первым пунктом диалога идёт выбор языка интерфейса — настройка уровня приложения, поэтому она
стоит над параметрами запуска. Подписи в модели переводятся, а коды (system, ru, en) лежат
рядом отдельным списком codes и не переводятся. Применяется язык по кнопке «Сохранить», как и
всё остальное в этом диалоге, и сразу же, без перезапуска: см. Localization.
Разрешённый путь папки игры хранится свойством resolvedGameDir диалога, а не присваивается
тексту напрямую — иначе подпись не пережила бы смену языка.
Поле выбора сборки Java открывает JavaPickerDialog, передавая текущий выбор
и требование активной сборки (requiredJavaMajor()); крестик справа сбрасывает выбор обратно на
поиск Java в системе. Само окно выбора объявлено рядом с настройками, а не внутри них: оно шире и
центрируется по окну лаунчера.
Выбранная сборка Java — общая настройка лаунчера: когда она задана, запуск идёт ею, а путь к Java из соседнего поля остаётся запасным вариантом.
Тексты
Все подписи, сообщения и подсказки окна берутся из синглтона Loc: Loc.t.домен.вид.имя.
Ни одного текстового литерала в разметке не осталось, qsTr не используется. Модель типов входа
в диалогах профиля — тоже ключ каталога (Loc.t.profile.authTypes), причём порядок значений
в нём значим: код сравнивает currentIndex с 1 и 2, а вариант без Microsoft получается из той же
модели через .slice(0, 2). Подробности — в Localization.
Прочие кнопки
Кнопка папки вызывает openMinecraftFolder(), кнопка настроек открывает диалог настроек, кнопка
сезонных сборок — SeasonalBuildsDialog методом openCatalog(), кнопка
активной сборки — BuildsDialog.
При создании этого документа использовался ИИ.