2026-08-27 09:21:51 +03:00
|
|
|
import QtQuick
|
|
|
|
|
import QtQuick.Controls 2.15
|
|
|
|
|
|
|
|
|
|
// Один модлоадер в карточке сборки: чекбокс и, когда он отмечен, список версий
|
|
|
|
|
// именно под выбранную версию Minecraft.
|
|
|
|
|
//
|
|
|
|
|
// Совместимость проверять не нужно: backend.loaderVersions() отдаёт список уже
|
|
|
|
|
// под конкретную версию игры, поэтому несовместимой строки в нём не бывает.
|
|
|
|
|
// Пустой список — это «лоадер не поддерживает эту версию игры», и он тоже
|
|
|
|
|
// показывается словами, а не пустым выпадающим списком.
|
|
|
|
|
Column {
|
|
|
|
|
id: loaderRow
|
|
|
|
|
|
|
|
|
|
required property var backend
|
|
|
|
|
required property string loaderKey
|
|
|
|
|
required property string title
|
|
|
|
|
|
|
|
|
|
// Версия Minecraft, выбранная в карточке. Её смена сбрасывает выбор.
|
|
|
|
|
property string gameVersion: ""
|
|
|
|
|
|
|
|
|
|
property alias checked: box.checked
|
|
|
|
|
property string selectedVersion: ""
|
|
|
|
|
property var entries: []
|
|
|
|
|
|
|
|
|
|
// Пользователь щёлкнул чекбокс — карточка снимет отметки с остальных
|
|
|
|
|
// лоадеров: одновременно в .minecraft может жить только один.
|
|
|
|
|
signal userChecked()
|
|
|
|
|
// Отметка или версия изменились — карточке пора сохранить сборку.
|
|
|
|
|
signal changed()
|
|
|
|
|
|
|
|
|
|
// Ставится, пока карточка заполняет строку данными сборки: в это время
|
|
|
|
|
// обработчики не должны считать изменения пользовательскими.
|
|
|
|
|
property bool applying: false
|
|
|
|
|
|
|
|
|
|
spacing: 4
|
|
|
|
|
|
|
|
|
|
function versionIndex(version) {
|
|
|
|
|
for (var i = 0; i < loaderRow.entries.length; ++i) {
|
|
|
|
|
if (loaderRow.entries[i].version === version)
|
|
|
|
|
return i
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 16:44:42 +03:00
|
|
|
// Единственное место, где меняются entries и selectedVersion. Через него
|
|
|
|
|
// проходят все три пути — кэш, ответ сети и заполнение из сборки, — потому
|
|
|
|
|
// что выбранная версия обязана существовать в списке под текущую версию
|
|
|
|
|
// игры. Раньше проверял только путь из сети, и версия лоадера от прошлой
|
|
|
|
|
// версии игры доживала до сохранения сборки.
|
|
|
|
|
function applyEntries(list) {
|
|
|
|
|
loaderRow.entries = list
|
|
|
|
|
|
|
|
|
|
// Список отсортирован новыми вперёд, поэтому максимально доступная
|
|
|
|
|
// версия для этой версии игры — всегда первая строка.
|
|
|
|
|
if (loaderRow.versionIndex(loaderRow.selectedVersion) < 0) {
|
|
|
|
|
const fallback = list.length > 0 ? list[0].version : ""
|
|
|
|
|
const changedValue = loaderRow.selectedVersion !== fallback
|
|
|
|
|
loaderRow.selectedVersion = fallback
|
|
|
|
|
if (changedValue && !loaderRow.applying)
|
|
|
|
|
loaderRow.changed()
|
2026-08-27 09:21:51 +03:00
|
|
|
}
|
|
|
|
|
|
2026-08-27 16:44:42 +03:00
|
|
|
combo.currentIndex = loaderRow.versionIndex(loaderRow.selectedVersion)
|
2026-08-27 09:21:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function reload() {
|
|
|
|
|
if (!box.checked || loaderRow.gameVersion === "") {
|
2026-08-27 16:44:42 +03:00
|
|
|
loaderRow.applyEntries([])
|
2026-08-27 09:21:51 +03:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// Сначала из кэша — список появляется мгновенно, — потом обновление сети.
|
2026-08-27 16:44:42 +03:00
|
|
|
loaderRow.applyEntries(loaderRow.backend.loaderVersions(loaderRow.loaderKey,
|
|
|
|
|
loaderRow.gameVersion))
|
2026-08-27 09:21:51 +03:00
|
|
|
loaderRow.backend.refreshLoaderVersions(loaderRow.loaderKey, loaderRow.gameVersion)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Заполнение из сохранённой сборки: без сигналов changed().
|
|
|
|
|
function applyBuild(loader, loaderVersion) {
|
|
|
|
|
loaderRow.applying = true
|
|
|
|
|
box.checked = loader === loaderRow.loaderKey
|
2026-08-27 16:44:42 +03:00
|
|
|
// Версию из сборки берём как пожелание: если под выбранную версию игры
|
|
|
|
|
// её нет, applyEntries() заменит её на максимально доступную.
|
2026-08-27 09:21:51 +03:00
|
|
|
loaderRow.selectedVersion = box.checked ? loaderVersion : ""
|
|
|
|
|
loaderRow.reload()
|
|
|
|
|
loaderRow.applying = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onGameVersionChanged: {
|
2026-08-27 16:44:42 +03:00
|
|
|
// Сборка лоадера привязана к версии игры: под новой версией прежний
|
|
|
|
|
// выбор недействителен, и список перезапрашивается заново.
|
2026-08-27 09:21:51 +03:00
|
|
|
loaderRow.selectedVersion = ""
|
|
|
|
|
loaderRow.reload()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
|
target: loaderRow.backend
|
|
|
|
|
function onLoaderVersionsChanged(key, game) {
|
|
|
|
|
if (key !== loaderRow.loaderKey || game !== loaderRow.gameVersion || !box.checked)
|
|
|
|
|
return
|
2026-08-27 16:44:42 +03:00
|
|
|
loaderRow.applyEntries(loaderRow.backend.loaderVersions(key, game))
|
2026-08-27 09:21:51 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CheckBox {
|
|
|
|
|
id: box
|
|
|
|
|
text: loaderRow.title
|
|
|
|
|
enabled: loaderRow.gameVersion !== ""
|
|
|
|
|
|
|
|
|
|
indicator: Rectangle {
|
|
|
|
|
implicitWidth: 18
|
|
|
|
|
implicitHeight: 18
|
|
|
|
|
x: 0
|
|
|
|
|
y: (box.height - height) / 2
|
|
|
|
|
radius: 4
|
|
|
|
|
color: box.checked ? "#91B315" : "#2a2a2a"
|
|
|
|
|
border.color: box.checked ? "#91B315" : "#444444"
|
|
|
|
|
border.width: 1
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
visible: box.checked
|
|
|
|
|
text: "✓"
|
|
|
|
|
color: "#1e1e1e"
|
|
|
|
|
font.pixelSize: 13
|
|
|
|
|
font.bold: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contentItem: Text {
|
|
|
|
|
leftPadding: box.indicator.width + 8
|
|
|
|
|
text: box.text
|
|
|
|
|
color: box.enabled ? "#ffffff" : "#666666"
|
|
|
|
|
font.pixelSize: 12
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onToggled: {
|
|
|
|
|
if (box.checked)
|
|
|
|
|
loaderRow.userChecked()
|
|
|
|
|
else
|
|
|
|
|
loaderRow.selectedVersion = ""
|
|
|
|
|
loaderRow.reload()
|
|
|
|
|
loaderRow.changed()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DarkCombo {
|
|
|
|
|
id: combo
|
|
|
|
|
visible: box.checked && loaderRow.entries.length > 0
|
|
|
|
|
width: loaderRow.width - 26
|
|
|
|
|
height: 32
|
|
|
|
|
x: 26
|
|
|
|
|
model: loaderRow.entries.map(entry => entry.label)
|
|
|
|
|
|
|
|
|
|
onActivated: (index) => {
|
|
|
|
|
if (index < 0 || index >= loaderRow.entries.length)
|
|
|
|
|
return
|
|
|
|
|
loaderRow.selectedVersion = loaderRow.entries[index].version
|
|
|
|
|
loaderRow.changed()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
visible: box.checked && loaderRow.entries.length === 0
|
|
|
|
|
x: 26
|
|
|
|
|
width: loaderRow.width - 26
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
font.pixelSize: 11
|
|
|
|
|
color: loaderRow.backend.loaderVersionsLoading(loaderRow.loaderKey, loaderRow.gameVersion)
|
|
|
|
|
? "#888888" : "#cc6666"
|
|
|
|
|
text: loaderRow.backend.loaderVersionsLoading(loaderRow.loaderKey, loaderRow.gameVersion)
|
|
|
|
|
? qsTr("Загрузка списка версий %1…").arg(loaderRow.title)
|
|
|
|
|
: qsTr("%1 не поддерживает Minecraft %2").arg(loaderRow.title)
|
|
|
|
|
.arg(loaderRow.gameVersion)
|
|
|
|
|
}
|
|
|
|
|
}
|