1 parent
fd14b54d8a
commit
0f370167db
9 files changed
+18
-134
No files matched your search
@@ -1,12 +1,9 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
// Комбобокс в общем тёмном стиле окна.
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
id: darkCombo
|
id: darkCombo
|
||||||
|
|
||||||
// Та же стрелка, что у комбобоксов профиля и версии на главном окне,
|
|
||||||
// вместо двойного шеврона Basic-стиля.
|
|
||||||
indicator: Image {
|
indicator: Image {
|
||||||
width: 10; height: 10
|
width: 10; height: 10
|
||||||
x: darkCombo.width - width - 12
|
x: darkCombo.width - width - 12
|
||||||
|
|||||||
+1
-20
@@ -4,22 +4,16 @@ import QtQuick
|
|||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 2.15
|
import QtQuick.Layouts 2.15
|
||||||
|
|
||||||
// Выбор сборки Java отдельным окном: слева типы, справа сами версии с поиском.
|
|
||||||
// Устроено так же, как выбор версии Minecraft, но выбранное здесь ещё и
|
|
||||||
// качается — в отличие от версий игры, где загрузку начинает сама сборка.
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: picker
|
id: picker
|
||||||
|
|
||||||
required property var backend
|
required property var backend
|
||||||
|
|
||||||
// Сборка, с которой окно открылось: по «Отмене» выбор к ней и возвращается.
|
|
||||||
property string selectedId: ""
|
property string selectedId: ""
|
||||||
property string category: "java"
|
property string category: "java"
|
||||||
property string filterText: ""
|
property string filterText: ""
|
||||||
property bool installedOnly: false
|
property bool installedOnly: false
|
||||||
|
|
||||||
// Java, ниже которой выбранной версии игры не запуститься. 0 — версия игры
|
|
||||||
// не выбрана, предупреждать не о чем.
|
|
||||||
property int requiredMajor: 0
|
property int requiredMajor: 0
|
||||||
|
|
||||||
signal runtimeChosen(string runtimeId)
|
signal runtimeChosen(string runtimeId)
|
||||||
@@ -43,8 +37,6 @@ Dialog {
|
|||||||
{ key: "jre", title: Loc.t.common.java.kind.jre, hint: Loc.t.java.filter.temurinJre }
|
{ key: "jre", title: Loc.t.common.java.kind.jre, hint: Loc.t.java.filter.temurinJre }
|
||||||
]
|
]
|
||||||
|
|
||||||
// Каталог приходит из C++ уже отсортированным (новые сверху, скачанные
|
|
||||||
// первыми), поэтому здесь только отбор — порядок не трогаем.
|
|
||||||
readonly property var visibleEntries: {
|
readonly property var visibleEntries: {
|
||||||
const query = picker.filterText.trim().toLowerCase()
|
const query = picker.filterText.trim().toLowerCase()
|
||||||
const source = picker.backend.javaCatalog
|
const source = picker.backend.javaCatalog
|
||||||
@@ -107,7 +99,6 @@ Dialog {
|
|||||||
runtimeList.positionViewAtIndex(index, ListView.Center)
|
runtimeList.positionViewAtIndex(index, ListView.Center)
|
||||||
}
|
}
|
||||||
|
|
||||||
// «Выбрать» и скачивает: сборка, которой нет на диске, запуску не поможет.
|
|
||||||
function acceptSelection() {
|
function acceptSelection() {
|
||||||
const entry = picker.selectedEntry
|
const entry = picker.selectedEntry
|
||||||
if (!entry)
|
if (!entry)
|
||||||
@@ -138,7 +129,6 @@ Dialog {
|
|||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// ── Типы сборок ─────────────────────────────────────────────────
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.preferredWidth: 170
|
Layout.preferredWidth: 170
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@@ -195,8 +185,6 @@ Dialog {
|
|||||||
|
|
||||||
Item { width: 1; height: 8 }
|
Item { width: 1; height: 8 }
|
||||||
|
|
||||||
// Требование выбранной сборки — чтобы не пришлось держать в
|
|
||||||
// голове, какая Java нужна какой версии игры.
|
|
||||||
Text {
|
Text {
|
||||||
width: parent.width - 4
|
width: parent.width - 4
|
||||||
visible: picker.requiredMajor > 0
|
visible: picker.requiredMajor > 0
|
||||||
@@ -215,7 +203,6 @@ Dialog {
|
|||||||
color: "#333333"
|
color: "#333333"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Версии ──────────────────────────────────────────────────────
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@@ -317,8 +304,7 @@ Dialog {
|
|||||||
required property int index
|
required property int index
|
||||||
|
|
||||||
readonly property bool chosen: picker.selectedId === runtimeRow.modelData.id
|
readonly property bool chosen: picker.selectedId === runtimeRow.modelData.id
|
||||||
// Сборка ниже требования игры выбирается, но помечается: она
|
|
||||||
// может пригодиться для другой сборки.
|
|
||||||
readonly property bool tooOld: picker.requiredMajor > 0
|
readonly property bool tooOld: picker.requiredMajor > 0
|
||||||
&& runtimeRow.modelData.major < picker.requiredMajor
|
&& runtimeRow.modelData.major < picker.requiredMajor
|
||||||
|
|
||||||
@@ -353,8 +339,6 @@ Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
// Колонка растянута якорями, поэтому её ширина
|
|
||||||
// задана, а не выведена из детей — цикла тут нет.
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
color: runtimeRow.chosen ? "#1e1e1e"
|
color: runtimeRow.chosen ? "#1e1e1e"
|
||||||
@@ -388,8 +372,6 @@ Dialog {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сборки весят по двести мегабайт — удалять их нужно
|
|
||||||
// прямо здесь, иначе папка лаунчера растёт молча.
|
|
||||||
Image {
|
Image {
|
||||||
visible: runtimeRow.modelData.installed
|
visible: runtimeRow.modelData.installed
|
||||||
source: "images/Trash.svg"
|
source: "images/Trash.svg"
|
||||||
@@ -430,7 +412,6 @@ Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Пустая категория, ничего не нашлось или каталог ещё не пришёл.
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: runtimeList
|
anchors.centerIn: runtimeList
|
||||||
width: runtimeList.width - 20
|
width: runtimeList.width - 20
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
// Подпись + поле ввода одной колонкой — используется в настройках.
|
|
||||||
Column {
|
Column {
|
||||||
id: labelled
|
id: labelled
|
||||||
|
|
||||||
@@ -10,7 +9,6 @@ Column {
|
|||||||
property string label: ""
|
property string label: ""
|
||||||
property string placeholder: ""
|
property string placeholder: ""
|
||||||
|
|
||||||
// Правка закончена: поле потеряло фокус или пользователь нажал Enter.
|
|
||||||
signal editingFinished()
|
signal editingFinished()
|
||||||
|
|
||||||
spacing: 3
|
spacing: 3
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
// Один модлоадер в карточке сборки: чекбокс и, когда он отмечен, список версий
|
|
||||||
// именно под выбранную версию Minecraft.
|
|
||||||
//
|
|
||||||
// Совместимость проверять не нужно: backend.loaderVersions() отдаёт список уже
|
|
||||||
// под конкретную версию игры, поэтому несовместимой строки в нём не бывает.
|
|
||||||
// Пустой список — это «лоадер не поддерживает эту версию игры», и он тоже
|
|
||||||
// показывается словами, а не пустым выпадающим списком.
|
|
||||||
Column {
|
Column {
|
||||||
id: loaderRow
|
id: loaderRow
|
||||||
|
|
||||||
@@ -15,21 +8,13 @@ Column {
|
|||||||
required property string loaderKey
|
required property string loaderKey
|
||||||
required property string title
|
required property string title
|
||||||
|
|
||||||
// Версия Minecraft, выбранная в карточке. Её смена сбрасывает выбор.
|
|
||||||
property string gameVersion: ""
|
property string gameVersion: ""
|
||||||
|
|
||||||
property alias checked: box.checked
|
property alias checked: box.checked
|
||||||
property string selectedVersion: ""
|
property string selectedVersion: ""
|
||||||
property var entries: []
|
property var entries: []
|
||||||
|
|
||||||
// Пользователь щёлкнул чекбокс — карточка снимет отметки с остальных
|
|
||||||
// лоадеров: одновременно в .minecraft может жить только один.
|
|
||||||
signal userChecked()
|
signal userChecked()
|
||||||
// Отметка или версия изменились — карточке пора сохранить сборку.
|
|
||||||
signal changed()
|
signal changed()
|
||||||
|
|
||||||
// Ставится, пока карточка заполняет строку данными сборки: в это время
|
|
||||||
// обработчики не должны считать изменения пользовательскими.
|
|
||||||
property bool applying: false
|
property bool applying: false
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
@@ -42,16 +27,9 @@ Column {
|
|||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Единственное место, где меняются entries и selectedVersion. Через него
|
|
||||||
// проходят все три пути — кэш, ответ сети и заполнение из сборки, — потому
|
|
||||||
// что выбранная версия обязана существовать в списке под текущую версию
|
|
||||||
// игры. Раньше проверял только путь из сети, и версия лоадера от прошлой
|
|
||||||
// версии игры доживала до сохранения сборки.
|
|
||||||
function applyEntries(list) {
|
function applyEntries(list) {
|
||||||
loaderRow.entries = list
|
loaderRow.entries = list
|
||||||
|
|
||||||
// Список отсортирован новыми вперёд, поэтому максимально доступная
|
|
||||||
// версия для этой версии игры — всегда первая строка.
|
|
||||||
if (loaderRow.versionIndex(loaderRow.selectedVersion) < 0) {
|
if (loaderRow.versionIndex(loaderRow.selectedVersion) < 0) {
|
||||||
const fallback = list.length > 0 ? list[0].version : ""
|
const fallback = list.length > 0 ? list[0].version : ""
|
||||||
const changedValue = loaderRow.selectedVersion !== fallback
|
const changedValue = loaderRow.selectedVersion !== fallback
|
||||||
@@ -68,26 +46,20 @@ Column {
|
|||||||
loaderRow.applyEntries([])
|
loaderRow.applyEntries([])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Сначала из кэша — список появляется мгновенно, — потом обновление сети.
|
|
||||||
loaderRow.applyEntries(loaderRow.backend.loaderVersions(loaderRow.loaderKey,
|
loaderRow.applyEntries(loaderRow.backend.loaderVersions(loaderRow.loaderKey,
|
||||||
loaderRow.gameVersion))
|
loaderRow.gameVersion))
|
||||||
loaderRow.backend.refreshLoaderVersions(loaderRow.loaderKey, loaderRow.gameVersion)
|
loaderRow.backend.refreshLoaderVersions(loaderRow.loaderKey, loaderRow.gameVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Заполнение из сохранённой сборки: без сигналов changed().
|
|
||||||
function applyBuild(loader, loaderVersion) {
|
function applyBuild(loader, loaderVersion) {
|
||||||
loaderRow.applying = true
|
loaderRow.applying = true
|
||||||
box.checked = loader === loaderRow.loaderKey
|
box.checked = loader === loaderRow.loaderKey
|
||||||
// Версию из сборки берём как пожелание: если под выбранную версию игры
|
|
||||||
// её нет, applyEntries() заменит её на максимально доступную.
|
|
||||||
loaderRow.selectedVersion = box.checked ? loaderVersion : ""
|
loaderRow.selectedVersion = box.checked ? loaderVersion : ""
|
||||||
loaderRow.reload()
|
loaderRow.reload()
|
||||||
loaderRow.applying = false
|
loaderRow.applying = false
|
||||||
}
|
}
|
||||||
|
|
||||||
onGameVersionChanged: {
|
onGameVersionChanged: {
|
||||||
// Сборка лоадера привязана к версии игры: под новой версией прежний
|
|
||||||
// выбор недействителен, и список перезапрашивается заново.
|
|
||||||
loaderRow.selectedVersion = ""
|
loaderRow.selectedVersion = ""
|
||||||
loaderRow.reload()
|
loaderRow.reload()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,12 @@ import QtQuick
|
|||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtWebEngine
|
import QtWebEngine
|
||||||
|
|
||||||
// Вход в аккаунт Microsoft — то же самое, что делает официальный лаунчер:
|
|
||||||
// страница входа показывается внутри приложения, а лаунчер ждёт, когда браузер
|
|
||||||
// уйдёт на redirect_uri с кодом авторизации в адресе.
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: msLogin
|
id: msLogin
|
||||||
|
|
||||||
required property var backend
|
required property var backend
|
||||||
|
|
||||||
// Окно ничего не знает про тосты главного окна: о неудаче сообщает сигналом.
|
|
||||||
signal failed(string message)
|
signal failed(string message)
|
||||||
|
|
||||||
// Код уже отдан backend — второй раз по тому же адресу не реагируем:
|
|
||||||
// WebEngineView успевает сообщить об urlChanged несколько раз.
|
|
||||||
property bool codeTaken: false
|
property bool codeTaken: false
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
@@ -32,8 +25,6 @@ Dialog {
|
|||||||
open()
|
open()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Разбор адреса живёт в C++: правила совпадения должны совпадать с теми,
|
|
||||||
// по которым сервис строит сам redirect_uri.
|
|
||||||
function handleUrl(url) {
|
function handleUrl(url) {
|
||||||
if (codeTaken)
|
if (codeTaken)
|
||||||
return
|
return
|
||||||
@@ -76,9 +67,6 @@ Dialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
// Профиль без storageName — значит без диска: куки живут только пока
|
|
||||||
// работает лаунчер и в общий браузер не попадают. За выбор аккаунта в
|
|
||||||
// пределах сессии отвечает prompt=select_account в адресе входа.
|
|
||||||
WebEngineProfilePrototype {
|
WebEngineProfilePrototype {
|
||||||
id: msProfile
|
id: msProfile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
// Плашка хода долгой операции в левом нижнем углу: не задевает кнопку запуска,
|
|
||||||
// сообщение по центру и кнопки папки с настройками.
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: panel
|
id: panel
|
||||||
|
|
||||||
property string title: ""
|
property string title: ""
|
||||||
property string status: ""
|
property string status: ""
|
||||||
// -1 — итог ещё неизвестен, показываем «…» вместо процентов.
|
|
||||||
property double fraction: -1
|
property double fraction: -1
|
||||||
// Необязательная вторая строка: мегабайты у загрузки, путь у архивации.
|
|
||||||
property string detail: ""
|
property string detail: ""
|
||||||
property bool cancellable: true
|
property bool cancellable: true
|
||||||
|
|
||||||
|
|||||||
@@ -3,23 +3,11 @@ pragma ComponentBehavior: Bound
|
|||||||
import QtQuick
|
import QtQuick
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
|
||||||
// Готовые сезонные сборки с сервера лаунчера: таблица со всем, что нужно
|
|
||||||
// решить, ставить её или нет, и одна кнопка, которая делает всё остальное —
|
|
||||||
// заводит сборку, ставит версию игры, модлоадер, Java и раскладывает файлы.
|
|
||||||
//
|
|
||||||
// Таблица собрана из Row фиксированных колонок, а не из TableView: в проекте
|
|
||||||
// нет ни одной модели QAbstractItemModel, а строки приходят готовыми
|
|
||||||
// QVariantMap — заводить ради семи колонок отдельную модель незачем.
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: seasons
|
id: seasons
|
||||||
|
|
||||||
required property var backend
|
required property var backend
|
||||||
|
|
||||||
// Выбранная строка. Хранится по id, а не по индексу: список обновляется
|
|
||||||
// под руками, и индекс после обновления указывал бы на другую сборку.
|
|
||||||
property string selectedId: ""
|
property string selectedId: ""
|
||||||
|
|
||||||
// Ширины колонок в одном месте: их повторяют и шапка, и делегат.
|
|
||||||
readonly property var columns: [
|
readonly property var columns: [
|
||||||
{ key: "name", title: Loc.t.common.header.name, width: 250, align: Text.AlignLeft },
|
{ key: "name", title: Loc.t.common.header.name, width: 250, align: Text.AlignLeft },
|
||||||
{ key: "minecraftVersion", title: Loc.t.common.header.version, width: 90, align: Text.AlignLeft },
|
{ key: "minecraftVersion", title: Loc.t.common.header.version, width: 90, align: Text.AlignLeft },
|
||||||
@@ -34,8 +22,6 @@ Dialog {
|
|||||||
padding: 0
|
padding: 0
|
||||||
width: 960
|
width: 960
|
||||||
height: 560
|
height: 560
|
||||||
// Пока идёт установка, окно закрывается только кнопкой: случайный щелчок
|
|
||||||
// мимо не должен спрятать единственную видимую отмену.
|
|
||||||
closePolicy: seasons.backend.seasonalInstalling
|
closePolicy: seasons.backend.seasonalInstalling
|
||||||
? Popup.NoAutoClose
|
? Popup.NoAutoClose
|
||||||
: (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
: (Popup.CloseOnEscape | Popup.CloseOnPressOutside)
|
||||||
@@ -47,8 +33,6 @@ Dialog {
|
|||||||
border.width: 1
|
border.width: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Строки приходят из C++ уже отсортированными и сведёнными с локальными
|
|
||||||
// записями — здесь только показ.
|
|
||||||
readonly property var entries: seasons.backend.seasonalCatalog
|
readonly property var entries: seasons.backend.seasonalCatalog
|
||||||
|
|
||||||
readonly property var selectedEntry: {
|
readonly property var selectedEntry: {
|
||||||
@@ -79,7 +63,6 @@ Dialog {
|
|||||||
return String(entry[key])
|
return String(entry[key])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Свежий кэш отвечает без сети, поэтому дёргаем при каждом открытии.
|
|
||||||
onAboutToShow: seasons.backend.refreshSeasonalCatalog(false)
|
onAboutToShow: seasons.backend.refreshSeasonalCatalog(false)
|
||||||
|
|
||||||
header: Item {
|
header: Item {
|
||||||
@@ -101,7 +84,6 @@ Dialog {
|
|||||||
|
|
||||||
contentItem: Item {
|
contentItem: Item {
|
||||||
|
|
||||||
// ── Шапка таблицы ───────────────────────────────────────────────
|
|
||||||
Row {
|
Row {
|
||||||
id: tableHeader
|
id: tableHeader
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
@@ -139,9 +121,6 @@ Dialog {
|
|||||||
color: "#333333"
|
color: "#333333"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Сообщение вместо таблицы ────────────────────────────────────
|
|
||||||
// Пустой список и ошибка выглядят одинаково пустыми, поэтому причина
|
|
||||||
// показывается прямо на месте строк.
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - 80
|
width: parent.width - 80
|
||||||
@@ -157,7 +136,6 @@ Dialog {
|
|||||||
: seasons.backend.seasonalCatalogError)
|
: seasons.backend.seasonalCatalogError)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Строки ──────────────────────────────────────────────────────
|
|
||||||
ListView {
|
ListView {
|
||||||
id: buildList
|
id: buildList
|
||||||
anchors.top: headerLine.bottom
|
anchors.top: headerLine.bottom
|
||||||
@@ -243,8 +221,6 @@ Dialog {
|
|||||||
color: "#333333"
|
color: "#333333"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Описание и размер выбранной сборки: они длинные и в таблицу не
|
|
||||||
// помещаются, а решение принимается по ним.
|
|
||||||
Column {
|
Column {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: 18
|
anchors.leftMargin: 18
|
||||||
@@ -314,8 +290,6 @@ Dialog {
|
|||||||
id: installButton
|
id: installButton
|
||||||
width: 150
|
width: 150
|
||||||
height: 36
|
height: 36
|
||||||
// Установка занимает и панель загрузки, и .minecraft целиком:
|
|
||||||
// пока лаунчер занят чем угодно, вторую начинать нельзя.
|
|
||||||
enabled: seasons.selectedEntry !== null && !seasons.backend.busy
|
enabled: seasons.selectedEntry !== null && !seasons.backend.busy
|
||||||
contentItem: Text {
|
contentItem: Text {
|
||||||
text: seasons.selectedEntry && seasons.selectedEntry.updateAvailable
|
text: seasons.selectedEntry && seasons.selectedEntry.updateAvailable
|
||||||
|
|||||||
@@ -4,25 +4,16 @@ import QtQuick
|
|||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 2.15
|
import QtQuick.Layouts 2.15
|
||||||
|
|
||||||
// Выбор версии Minecraft отдельным окном: слева категории, справа сами версии
|
|
||||||
// с поиском сверху.
|
|
||||||
//
|
|
||||||
// Раньше это был выпадающий список на всю тысячу версий: найти в нём бету 1.7
|
|
||||||
// можно было только поиском по точному номеру. Категории делят список на
|
|
||||||
// обозримые части, а поиск работает внутри выбранной.
|
|
||||||
Dialog {
|
Dialog {
|
||||||
id: picker
|
id: picker
|
||||||
|
|
||||||
required property var backend
|
required property var backend
|
||||||
|
|
||||||
// Версия, с которой окно открылось: по «Отмене» выбор к ней и возвращается.
|
|
||||||
property string selectedId: ""
|
property string selectedId: ""
|
||||||
property string category: "release"
|
property string category: "release"
|
||||||
property string filterText: ""
|
property string filterText: ""
|
||||||
property bool installedOnly: false
|
property bool installedOnly: false
|
||||||
|
|
||||||
// Выбранная версия подтверждена кнопкой или двойным щелчком. Имя не
|
|
||||||
// accepted(): такой сигнал у Dialog уже есть, и переопределить его нельзя.
|
|
||||||
signal versionChosen(string versionId)
|
signal versionChosen(string versionId)
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
@@ -46,8 +37,6 @@ Dialog {
|
|||||||
{ key: "other", title: Loc.t.version.filter.other }
|
{ key: "other", title: Loc.t.version.filter.other }
|
||||||
]
|
]
|
||||||
|
|
||||||
// Каталог приходит из C++ уже отсортированным (новые сверху), поэтому здесь
|
|
||||||
// только отбор — порядок не трогаем.
|
|
||||||
readonly property var visibleEntries: {
|
readonly property var visibleEntries: {
|
||||||
const query = picker.filterText.trim().toLowerCase()
|
const query = picker.filterText.trim().toLowerCase()
|
||||||
const source = picker.backend.versionCatalog
|
const source = picker.backend.versionCatalog
|
||||||
@@ -69,7 +58,6 @@ Dialog {
|
|||||||
picker.selectedId = versionId
|
picker.selectedId = versionId
|
||||||
picker.filterText = ""
|
picker.filterText = ""
|
||||||
searchField.text = ""
|
searchField.text = ""
|
||||||
// Открываемся на категории текущей версии, а не всегда на релизах.
|
|
||||||
picker.category = picker.categoryOf(versionId)
|
picker.category = picker.categoryOf(versionId)
|
||||||
picker.backend.refreshVersionCatalog()
|
picker.backend.refreshVersionCatalog()
|
||||||
picker.open()
|
picker.open()
|
||||||
@@ -117,9 +105,6 @@ Dialog {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Спрашиваем до удаления: версию придётся качать заново, а профили
|
|
||||||
// модлоадеров поверх неё — переустанавливать. Id запоминаем здесь:
|
|
||||||
// к моменту ответа строка под курсором может быть уже другой.
|
|
||||||
function askRemove(versionId) {
|
function askRemove(versionId) {
|
||||||
const info = picker.backend.versionRemovalInfo(versionId)
|
const info = picker.backend.versionRemovalInfo(versionId)
|
||||||
if (!info || info.installed !== true)
|
if (!info || info.installed !== true)
|
||||||
@@ -133,8 +118,6 @@ Dialog {
|
|||||||
|
|
||||||
function performRemove(versionId) {
|
function performRemove(versionId) {
|
||||||
picker.backend.removeVersion(versionId)
|
picker.backend.removeVersion(versionId)
|
||||||
// Профиль модлоадера был в каталоге только потому, что установлен:
|
|
||||||
// после удаления строка исчезает совсем, и выбор надо снять.
|
|
||||||
if (picker.selectedId === versionId && !picker.catalogHas(versionId))
|
if (picker.selectedId === versionId && !picker.catalogHas(versionId))
|
||||||
picker.selectedId = ""
|
picker.selectedId = ""
|
||||||
picker.revealSelected()
|
picker.revealSelected()
|
||||||
@@ -305,7 +288,6 @@ Dialog {
|
|||||||
contentItem: RowLayout {
|
contentItem: RowLayout {
|
||||||
spacing: 0
|
spacing: 0
|
||||||
|
|
||||||
// ── Категории ───────────────────────────────────────────────────
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.preferredWidth: 150
|
Layout.preferredWidth: 150
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@@ -358,7 +340,6 @@ Dialog {
|
|||||||
color: "#333333"
|
color: "#333333"
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Версии ──────────────────────────────────────────────────────
|
|
||||||
Item {
|
Item {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
@@ -493,8 +474,6 @@ Dialog {
|
|||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Версия весит от десятков мегабайт, а галочка рядом —
|
|
||||||
// единственное место, где видно, что именно скачано.
|
|
||||||
Image {
|
Image {
|
||||||
visible: versionRow.modelData.installed
|
visible: versionRow.modelData.installed
|
||||||
source: "images/Trash.svg"
|
source: "images/Trash.svg"
|
||||||
@@ -531,7 +510,6 @@ Dialog {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Пустая категория, ничего не нашлось или манифест ещё не пришёл.
|
|
||||||
Text {
|
Text {
|
||||||
anchors.centerIn: versionList
|
anchors.centerIn: versionList
|
||||||
width: versionList.width - 20
|
width: versionList.width - 20
|
||||||
|
|||||||
+17
-17
@@ -40,7 +40,7 @@
|
|||||||
"en": "Open mods folder"
|
"en": "Open mods folder"
|
||||||
},
|
},
|
||||||
"app.tooltip.seasonal": {
|
"app.tooltip.seasonal": {
|
||||||
"ru": "Сезонные сборки от команды Мельника",
|
"ru": "Сезонные сборки от команды Galeon'а",
|
||||||
"en": "Seasonal builds from Galeon team"
|
"en": "Seasonal builds from Galeon team"
|
||||||
},
|
},
|
||||||
"app.tooltip.settings": {
|
"app.tooltip.settings": {
|
||||||
@@ -156,8 +156,8 @@
|
|||||||
"en": "The Microsoft account owns the game but has no game profile yet. Sign in to the official launcher once and pick a nickname."
|
"en": "The Microsoft account owns the game but has no game profile yet. Sign in to the official launcher once and pick a nickname."
|
||||||
},
|
},
|
||||||
"auth.msa.error.noWebEngine": {
|
"auth.msa.error.noWebEngine": {
|
||||||
"ru": "Эта сборка лаунчера собрана без Qt WebEngine — показать окно входа Microsoft нечем",
|
"ru": "Эта сборка лаунчера собрана без Qt WebEngine",
|
||||||
"en": "This launcher build was made without Qt WebEngine — there is nothing to show the Microsoft sign-in window with"
|
"en": "This launcher build was made without Qt WebEngine"
|
||||||
},
|
},
|
||||||
"auth.msa.error.noXboxProfile": {
|
"auth.msa.error.noXboxProfile": {
|
||||||
"ru": "К аккаунту Microsoft не привязан профиль Xbox. Создайте его на xbox.com и повторите вход.",
|
"ru": "К аккаунту Microsoft не привязан профиль Xbox. Создайте его на xbox.com и повторите вход.",
|
||||||
@@ -196,8 +196,8 @@
|
|||||||
"en": "The Microsoft session has expired — sign in to the account again (%1)"
|
"en": "The Microsoft session has expired — sign in to the account again (%1)"
|
||||||
},
|
},
|
||||||
"auth.msa.error.windowUnavailable": {
|
"auth.msa.error.windowUnavailable": {
|
||||||
"ru": "Окно входа Microsoft недоступно: сборка без Qt WebEngine",
|
"ru": "Окно входа Microsoft недоступно",
|
||||||
"en": "The Microsoft sign-in window is unavailable: this build has no Qt WebEngine"
|
"en": "The Microsoft sign-in window is unavailable"
|
||||||
},
|
},
|
||||||
"auth.msa.error.xboxNoToken": {
|
"auth.msa.error.xboxNoToken": {
|
||||||
"ru": "Xbox Live не вернул токен",
|
"ru": "Xbox Live не вернул токен",
|
||||||
@@ -288,8 +288,8 @@
|
|||||||
"en": "Pick a version"
|
"en": "Pick a version"
|
||||||
},
|
},
|
||||||
"build.error.closeGameFilesMoving": {
|
"build.error.closeGameFilesMoving": {
|
||||||
"ru": "Сначала закройте игру — её файлы сейчас переносятся",
|
"ru": "Файлы игры в процессе переноса, сначала закройте игру",
|
||||||
"en": "Close the game first — its files are being moved right now"
|
"en": "Game files are being moved, close the game first"
|
||||||
},
|
},
|
||||||
"build.error.filesIncomplete": {
|
"build.error.filesIncomplete": {
|
||||||
"ru": "Файлы сборки установлены не полностью: %1\nПовторите установку сборки.",
|
"ru": "Файлы сборки установлены не полностью: %1\nПовторите установку сборки.",
|
||||||
@@ -328,8 +328,8 @@
|
|||||||
"en": "The build was not switched: %1"
|
"en": "The build was not switched: %1"
|
||||||
},
|
},
|
||||||
"build.error.switchFailedDirty": {
|
"build.error.switchFailedDirty": {
|
||||||
"ru": "Сборка не переключена: %1\nСодержимое .minecraft осталось разобранным — повторите переключение.",
|
"ru": "Сборка не переключена: %1\n Повторите переключение.",
|
||||||
"en": "The build was not switched: %1\nThe contents of .minecraft are left half-applied — switch again."
|
"en": "The build was not switched: %1\n Switch again."
|
||||||
},
|
},
|
||||||
"build.error.versionNotInstalled": {
|
"build.error.versionNotInstalled": {
|
||||||
"ru": "Версия «%1» из сборки «%2» не установлена в %3",
|
"ru": "Версия «%1» из сборки «%2» не установлена в %3",
|
||||||
@@ -340,8 +340,8 @@
|
|||||||
"en": "Build \"%1\" will be removed and cannot be recovered."
|
"en": "Build \"%1\" will be removed and cannot be recovered."
|
||||||
},
|
},
|
||||||
"build.hint.removingLast": {
|
"build.hint.removingLast": {
|
||||||
"ru": "Это последняя сборка. Содержимое .minecraft останется на месте.",
|
"ru": "Последняя сборка, файлы из корня игры не удаляются.",
|
||||||
"en": "This is the last build. The contents of .minecraft will stay where they are."
|
"en": "Last build. The contents of .minecraft will stay."
|
||||||
},
|
},
|
||||||
"build.label.active": {
|
"build.label.active": {
|
||||||
"ru": "активна",
|
"ru": "активна",
|
||||||
@@ -360,8 +360,8 @@
|
|||||||
"en": "Server address"
|
"en": "Server address"
|
||||||
},
|
},
|
||||||
"build.placeholder.name": {
|
"build.placeholder.name": {
|
||||||
"ru": "как её видно в списке",
|
"ru": "Введите наименование",
|
||||||
"en": "as it appears in the list"
|
"en": "Write a name"
|
||||||
},
|
},
|
||||||
"build.placeholder.serverAddress": {
|
"build.placeholder.serverAddress": {
|
||||||
"ru": "host:port (необязательно)",
|
"ru": "host:port (необязательно)",
|
||||||
@@ -384,8 +384,8 @@
|
|||||||
"en": "Build \"%1\": %2"
|
"en": "Build \"%1\": %2"
|
||||||
},
|
},
|
||||||
"build.status.ready": {
|
"build.status.ready": {
|
||||||
"ru": "Всё на месте, сборку можно запускать",
|
"ru": "Готова к запуску",
|
||||||
"en": "Everything is in place, the build can be launched"
|
"en": "Ready to launch"
|
||||||
},
|
},
|
||||||
"build.title.numbered": {
|
"build.title.numbered": {
|
||||||
"ru": "Сборка %1",
|
"ru": "Сборка %1",
|
||||||
@@ -396,8 +396,8 @@
|
|||||||
"en": "Remove the build?"
|
"en": "Remove the build?"
|
||||||
},
|
},
|
||||||
"build.warning.archiveGoesToo": {
|
"build.warning.archiveGoesToo": {
|
||||||
"ru": "Вместе с ней удалится её архив: моды, конфиги и миры этой сборки восстановить будет нельзя.",
|
"ru": "Внимание, при удалении так же очищаются пользовательские сохранённые архивы.",
|
||||||
"en": "Its archive goes with it: the mods, configs and worlds of this build cannot be recovered."
|
"en": "Attention, after removing from the list all saved files will be deleted."
|
||||||
},
|
},
|
||||||
"build.warning.archiveNotRemoved": {
|
"build.warning.archiveNotRemoved": {
|
||||||
"ru": "Сборка «%1» удалена, но её архив в %2 стереть не удалось",
|
"ru": "Сборка «%1» удалена, но её архив в %2 стереть не удалось",
|
||||||
|
|||||||
Reference in new issue
Block a user