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 } // Единственное место, где меняются 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() } combo.currentIndex = loaderRow.versionIndex(loaderRow.selectedVersion) } function reload() { if (!box.checked || loaderRow.gameVersion === "") { loaderRow.applyEntries([]) return } // Сначала из кэша — список появляется мгновенно, — потом обновление сети. loaderRow.applyEntries(loaderRow.backend.loaderVersions(loaderRow.loaderKey, loaderRow.gameVersion)) loaderRow.backend.refreshLoaderVersions(loaderRow.loaderKey, loaderRow.gameVersion) } // Заполнение из сохранённой сборки: без сигналов changed(). function applyBuild(loader, loaderVersion) { loaderRow.applying = true box.checked = loader === loaderRow.loaderKey // Версию из сборки берём как пожелание: если под выбранную версию игры // её нет, applyEntries() заменит её на максимально доступную. loaderRow.selectedVersion = box.checked ? loaderVersion : "" loaderRow.reload() loaderRow.applying = false } onGameVersionChanged: { // Сборка лоадера привязана к версии игры: под новой версией прежний // выбор недействителен, и список перезапрашивается заново. loaderRow.selectedVersion = "" loaderRow.reload() } Connections { target: loaderRow.backend function onLoaderVersionsChanged(key, game) { if (key !== loaderRow.loaderKey || game !== loaderRow.gameVersion || !box.checked) return loaderRow.applyEntries(loaderRow.backend.loaderVersions(key, game)) } } 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) ? Loc.t.loader.progress.loadingVersions.arg(loaderRow.title) : Loc.t.loader.status.unsupported.arg(loaderRow.title) .arg(loaderRow.gameVersion) } }