2026-08-27 09:21:51 +03:00
|
|
|
pragma ComponentBehavior: Bound
|
|
|
|
|
|
|
|
|
|
import QtQuick
|
|
|
|
|
import QtQuick.Controls 2.15
|
|
|
|
|
import QtQuick.Layouts 2.15
|
|
|
|
|
|
|
|
|
|
// Пользовательские сборки одним окном: слева список со сменой активной,
|
|
|
|
|
// справа карточка выбранной — имя, сервер, версия Minecraft и модлоадеры.
|
|
|
|
|
//
|
|
|
|
|
// Карточка сохраняет правки по ходу редактирования, отдельной кнопки
|
|
|
|
|
// «Сохранить» нет: иначе появляется неочевидное несохранённое состояние,
|
|
|
|
|
// пока пользователь переключается между сборками в левом списке.
|
|
|
|
|
Dialog {
|
|
|
|
|
id: buildsDialog
|
|
|
|
|
|
|
|
|
|
required property var backend
|
|
|
|
|
|
|
|
|
|
// Сборка, открытая в карточке справа. С активной не связана: активную
|
|
|
|
|
// меняет отдельная кнопка, чтобы случайный клик по списку не запускал
|
|
|
|
|
// архивацию .minecraft.
|
|
|
|
|
property int editIndex: -1
|
|
|
|
|
// Пока карточка заполняется, обработчики полей не должны писать обратно.
|
|
|
|
|
property bool loading: false
|
|
|
|
|
|
|
|
|
|
modal: true
|
|
|
|
|
padding: 0
|
|
|
|
|
width: 880
|
|
|
|
|
height: 600
|
|
|
|
|
closePolicy: buildsDialog.backend.switching ? Popup.NoAutoClose
|
|
|
|
|
: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
|
|
|
|
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: "#1e1e1e"
|
|
|
|
|
radius: 10
|
|
|
|
|
border.color: "#91B315"
|
|
|
|
|
border.width: 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function selectBuild(index) {
|
|
|
|
|
const data = buildsDialog.backend.customBuildAt(index)
|
|
|
|
|
buildsDialog.loading = true
|
|
|
|
|
buildsDialog.editIndex = index
|
|
|
|
|
bdName.text = data.name || ""
|
|
|
|
|
bdServer.text = data.serverUrl || ""
|
|
|
|
|
bdMinecraft.selectedId = data.minecraftVersion || ""
|
|
|
|
|
loaderPanel.applyBuild(data.loader || "", data.loaderVersion || "")
|
|
|
|
|
buildsDialog.loading = false
|
|
|
|
|
buildsDialog.refreshStatus()
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 16:44:42 +03:00
|
|
|
// Удаление необратимо и уносит с собой архив сборки, поэтому сначала
|
|
|
|
|
// спрашиваем. Индекс запоминаем здесь: к моменту ответа строка списка под
|
|
|
|
|
// курсором может быть уже другой.
|
|
|
|
|
function askRemove(index) {
|
|
|
|
|
const info = buildsDialog.backend.customBuildRemovalInfo(index)
|
|
|
|
|
if (!info || info.name === undefined)
|
|
|
|
|
return
|
|
|
|
|
removeConfirm.buildIndex = index
|
|
|
|
|
removeConfirm.buildName = info.name
|
|
|
|
|
removeConfirm.hasArchive = info.hasArchive === true
|
|
|
|
|
removeConfirm.active = info.active === true
|
|
|
|
|
removeConfirm.lastOne = info.lastOne === true
|
|
|
|
|
removeConfirm.open()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function performRemove(index) {
|
|
|
|
|
buildsDialog.backend.removeCustomBuild(index)
|
|
|
|
|
const left = buildsDialog.backend.customBuildNames.length
|
|
|
|
|
if (left === 0)
|
|
|
|
|
buildsDialog.editIndex = -1
|
|
|
|
|
else
|
|
|
|
|
buildsDialog.selectBuild(Math.min(index, left - 1))
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 09:21:51 +03:00
|
|
|
function commit(fields) {
|
|
|
|
|
if (buildsDialog.loading || buildsDialog.editIndex < 0)
|
|
|
|
|
return
|
|
|
|
|
buildsDialog.backend.updateCustomBuild(buildsDialog.editIndex, fields)
|
|
|
|
|
buildsDialog.refreshStatus()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshStatus() {
|
|
|
|
|
if (buildsDialog.editIndex < 0) {
|
|
|
|
|
bdStatus.text = ""
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const problems = buildsDialog.backend.checkInstallation(buildsDialog.editIndex)
|
|
|
|
|
bdStatus.ok = problems.length === 0
|
|
|
|
|
bdStatus.text = problems.length === 0
|
|
|
|
|
? qsTr("Всё на месте, сборку можно запускать")
|
|
|
|
|
: qsTr("Не хватает файлов (%1): %2").arg(problems.length).arg(problems[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function newBuildName() {
|
|
|
|
|
const taken = buildsDialog.backend.customBuildNames
|
|
|
|
|
for (var n = 1; ; ++n) {
|
|
|
|
|
const candidate = qsTr("Сборка %1").arg(n)
|
|
|
|
|
if (taken.indexOf(candidate) === -1)
|
|
|
|
|
return candidate
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 16:44:42 +03:00
|
|
|
Dialog {
|
|
|
|
|
id: removeConfirm
|
|
|
|
|
|
|
|
|
|
property int buildIndex: -1
|
|
|
|
|
property string buildName: ""
|
|
|
|
|
property bool hasArchive: false
|
|
|
|
|
property bool active: false
|
|
|
|
|
property bool lastOne: false
|
|
|
|
|
|
|
|
|
|
modal: true
|
|
|
|
|
padding: 0
|
|
|
|
|
width: 420
|
|
|
|
|
parent: buildsDialog.parent
|
|
|
|
|
x: (buildsDialog.parent.width - width) / 2
|
|
|
|
|
y: (buildsDialog.parent.height - height) / 2
|
|
|
|
|
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
|
|
|
|
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: "#1e1e1e"
|
|
|
|
|
radius: 10
|
|
|
|
|
border.color: "#cc6666"
|
|
|
|
|
border.width: 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header: Item {
|
|
|
|
|
implicitHeight: 52
|
|
|
|
|
Text {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
text: qsTr("Удалить сборку?")
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
font.pixelSize: 17
|
|
|
|
|
font.bold: true
|
|
|
|
|
}
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: 1
|
|
|
|
|
color: "#333333"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contentItem: Column {
|
|
|
|
|
spacing: 10
|
|
|
|
|
topPadding: 18
|
|
|
|
|
bottomPadding: 18
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
x: 20
|
|
|
|
|
width: parent.width - 40
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
font.pixelSize: 13
|
|
|
|
|
text: qsTr("Сборка «%1» будет удалена без возможности восстановления.")
|
|
|
|
|
.arg(removeConfirm.buildName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
x: 20
|
|
|
|
|
width: parent.width - 40
|
|
|
|
|
visible: removeConfirm.hasArchive
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
color: "#cc6666"
|
|
|
|
|
font.pixelSize: 12
|
|
|
|
|
text: qsTr("Вместе с ней удалится её архив: моды, конфиги и миры этой сборки восстановить будет нельзя.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
x: 20
|
|
|
|
|
width: parent.width - 40
|
|
|
|
|
visible: removeConfirm.active && !removeConfirm.lastOne
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
color: "#cc6666"
|
|
|
|
|
font.pixelSize: 12
|
|
|
|
|
text: qsTr("Сборка сейчас активна: содержимое .minecraft принадлежит ей и будет очищено, а на его место развернётся следующая сборка.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
x: 20
|
|
|
|
|
width: parent.width - 40
|
|
|
|
|
visible: removeConfirm.lastOne
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
color: "#888888"
|
|
|
|
|
font.pixelSize: 12
|
|
|
|
|
text: qsTr("Это последняя сборка. Содержимое .minecraft останется на месте.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
footer: Item {
|
|
|
|
|
implicitHeight: 60
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.top: parent.top
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: 1
|
|
|
|
|
color: "#333333"
|
|
|
|
|
}
|
|
|
|
|
Row {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
spacing: 12
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Отмена")
|
|
|
|
|
width: 130; height: 36
|
|
|
|
|
contentItem: Text {
|
|
|
|
|
text: parent.text
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: parent.pressed ? "#6a8510" : "#91B315"
|
|
|
|
|
radius: 6
|
|
|
|
|
}
|
|
|
|
|
onClicked: removeConfirm.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Удалить")
|
|
|
|
|
width: 130; height: 36
|
|
|
|
|
contentItem: Text {
|
|
|
|
|
text: parent.text
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: parent.pressed ? "#8a3a3a" : "#663333"
|
|
|
|
|
radius: 6
|
|
|
|
|
border.color: "#cc6666"
|
|
|
|
|
border.width: 1
|
|
|
|
|
}
|
|
|
|
|
onClicked: {
|
|
|
|
|
const index = removeConfirm.buildIndex
|
|
|
|
|
removeConfirm.close()
|
|
|
|
|
buildsDialog.performRemove(index)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onClosed: removeConfirm.buildIndex = -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VersionPickerDialog {
|
|
|
|
|
id: versionPicker
|
|
|
|
|
backend: buildsDialog.backend
|
|
|
|
|
parent: buildsDialog.parent
|
|
|
|
|
x: (buildsDialog.parent.width - width) / 2
|
|
|
|
|
y: (buildsDialog.parent.height - height) / 2
|
|
|
|
|
onVersionChosen: (versionId) => bdMinecraft.selectedId = versionId
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 09:21:51 +03:00
|
|
|
// Установка версии или модлоадера меняет комплектность сборки — строка
|
|
|
|
|
// состояния должна это заметить, не дожидаясь переоткрытия окна.
|
|
|
|
|
Connections {
|
|
|
|
|
target: buildsDialog.backend
|
|
|
|
|
function onInstalledVersionsChanged() { buildsDialog.refreshStatus() }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onAboutToShow: {
|
|
|
|
|
buildsDialog.backend.refreshVersionCatalog()
|
|
|
|
|
const names = buildsDialog.backend.customBuildNames
|
|
|
|
|
if (names.length === 0)
|
|
|
|
|
buildsDialog.editIndex = -1
|
|
|
|
|
else
|
|
|
|
|
buildsDialog.selectBuild(Math.max(0, buildsDialog.backend.activeBuildIndex))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header: Item {
|
|
|
|
|
implicitHeight: 52
|
|
|
|
|
Text {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
text: qsTr("Пользовательские сборки")
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
font.pixelSize: 17
|
|
|
|
|
font.bold: true
|
|
|
|
|
}
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: 1
|
|
|
|
|
color: "#333333"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contentItem: RowLayout {
|
|
|
|
|
spacing: 0
|
|
|
|
|
|
|
|
|
|
// ── Список сборок ───────────────────────────────────────────────
|
|
|
|
|
Item {
|
|
|
|
|
Layout.preferredWidth: 260
|
|
|
|
|
Layout.fillHeight: true
|
|
|
|
|
enabled: !buildsDialog.backend.switching
|
|
|
|
|
|
|
|
|
|
ListView {
|
|
|
|
|
id: buildList
|
|
|
|
|
anchors.top: parent.top
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.bottom: addBuildRow.top
|
|
|
|
|
anchors.margins: 8
|
|
|
|
|
clip: true
|
|
|
|
|
spacing: 2
|
|
|
|
|
model: buildsDialog.backend.customBuildNames
|
|
|
|
|
|
|
|
|
|
delegate: ItemDelegate {
|
|
|
|
|
id: buildRow
|
|
|
|
|
required property string modelData
|
|
|
|
|
required property int index
|
|
|
|
|
|
|
|
|
|
width: buildList.width
|
|
|
|
|
height: 40
|
|
|
|
|
|
|
|
|
|
contentItem: Item {
|
|
|
|
|
Text {
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.leftMargin: 8
|
|
|
|
|
anchors.right: activeBadge.left
|
|
|
|
|
anchors.rightMargin: 6
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
text: buildRow.modelData
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
font.pixelSize: 13
|
|
|
|
|
}
|
|
|
|
|
Text {
|
|
|
|
|
id: activeBadge
|
|
|
|
|
anchors.right: buildTrash.left
|
|
|
|
|
anchors.rightMargin: 8
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
visible: buildRow.index === buildsDialog.backend.activeBuildIndex
|
|
|
|
|
text: qsTr("активна")
|
|
|
|
|
color: "#91B315"
|
|
|
|
|
font.pixelSize: 10
|
|
|
|
|
}
|
|
|
|
|
Image {
|
|
|
|
|
id: buildTrash
|
|
|
|
|
width: 16; height: 16
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 8
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
visible: buildRow.hovered
|
|
|
|
|
opacity: buildTrashArea.containsMouse ? 1.0 : 0.7
|
|
|
|
|
source: "images/Trash.svg"
|
|
|
|
|
sourceSize.width: 16; sourceSize.height: 16
|
|
|
|
|
fillMode: Image.PreserveAspectFit
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
id: buildTrashArea
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: -6
|
|
|
|
|
hoverEnabled: true
|
2026-08-27 16:44:42 +03:00
|
|
|
onClicked: buildsDialog.askRemove(buildRow.index)
|
2026-08-27 09:21:51 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: (buildsDialog.editIndex === buildRow.index || buildRow.hovered)
|
|
|
|
|
? "#91B315" : "#232323"
|
|
|
|
|
radius: 5
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onClicked: buildsDialog.selectBuild(buildRow.index)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ScrollIndicator.vertical: ScrollIndicator {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
id: addBuildRow
|
|
|
|
|
anchors.bottom: parent.bottom
|
|
|
|
|
anchors.left: parent.left
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.margins: 8
|
|
|
|
|
height: 34
|
|
|
|
|
radius: 5
|
|
|
|
|
color: addBuildArea.containsPress ? "#2d2d2d" : "transparent"
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
text: qsTr("+ Новая сборка")
|
|
|
|
|
color: "#91B315"
|
|
|
|
|
font.pixelSize: 12
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
id: addBuildArea
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
onClicked: {
|
|
|
|
|
buildsDialog.backend.addCustomBuild(buildsDialog.newBuildName(), "", "")
|
|
|
|
|
buildsDialog.selectBuild(buildsDialog.backend.customBuildNames.length - 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Rectangle {
|
|
|
|
|
Layout.preferredWidth: 1
|
|
|
|
|
Layout.fillHeight: true
|
|
|
|
|
color: "#333333"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ── Карточка выбранной сборки ───────────────────────────────────
|
|
|
|
|
Item {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.fillHeight: true
|
|
|
|
|
enabled: buildsDialog.editIndex >= 0 && !buildsDialog.backend.switching
|
|
|
|
|
opacity: enabled ? 1.0 : 0.4
|
|
|
|
|
|
|
|
|
|
Flickable {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
anchors.margins: 20
|
|
|
|
|
contentHeight: detailsColumn.height
|
|
|
|
|
clip: true
|
|
|
|
|
boundsBehavior: Flickable.StopAtBounds
|
|
|
|
|
ScrollIndicator.vertical: ScrollIndicator {}
|
|
|
|
|
|
|
|
|
|
Column {
|
|
|
|
|
id: detailsColumn
|
|
|
|
|
width: parent.width
|
|
|
|
|
spacing: 12
|
|
|
|
|
|
|
|
|
|
LabelledField {
|
|
|
|
|
id: bdName
|
|
|
|
|
width: parent.width
|
|
|
|
|
label: qsTr("Название сборки")
|
|
|
|
|
placeholder: qsTr("как её видно в списке")
|
|
|
|
|
onEditingFinished: buildsDialog.commit({"name": bdName.text.trim()})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LabelledField {
|
|
|
|
|
id: bdServer
|
|
|
|
|
width: parent.width
|
|
|
|
|
label: qsTr("Адрес сервера")
|
|
|
|
|
placeholder: qsTr("host:port (необязательно)")
|
|
|
|
|
onEditingFinished: buildsDialog.commit({"serverUrl": bdServer.text.trim()})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
text: qsTr("Версия Minecraft")
|
|
|
|
|
color: "#aaaaaa"
|
|
|
|
|
font.pixelSize: 11
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 16:44:42 +03:00
|
|
|
// Само поле только показывает выбор: версий около тысячи, и
|
|
|
|
|
// разбираться в них удобнее в отдельном окне с категориями.
|
|
|
|
|
Rectangle {
|
2026-08-27 09:21:51 +03:00
|
|
|
id: bdMinecraft
|
2026-08-27 16:44:42 +03:00
|
|
|
property string selectedId: ""
|
|
|
|
|
|
2026-08-27 09:21:51 +03:00
|
|
|
width: parent.width
|
2026-08-27 16:44:42 +03:00
|
|
|
height: 36
|
|
|
|
|
radius: 6
|
|
|
|
|
color: "#2a2a2a"
|
|
|
|
|
border.color: versionFieldArea.containsMouse ? "#91B315" : "#444444"
|
|
|
|
|
border.width: 1
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
leftPadding: 10
|
|
|
|
|
rightPadding: 110
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
elide: Text.ElideRight
|
|
|
|
|
color: bdMinecraft.selectedId === "" ? "#666666" : "#ffffff"
|
|
|
|
|
text: bdMinecraft.selectedId === "" ? qsTr("Выберите версию")
|
|
|
|
|
: bdMinecraft.selectedId
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.rightMargin: 12
|
|
|
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
|
text: qsTr("Выбрать…")
|
|
|
|
|
color: "#91B315"
|
|
|
|
|
font.pixelSize: 12
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseArea {
|
|
|
|
|
id: versionFieldArea
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
hoverEnabled: true
|
|
|
|
|
onClicked: versionPicker.openFor(bdMinecraft.selectedId)
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 09:21:51 +03:00
|
|
|
onSelectedIdChanged: buildsDialog.commit({"minecraftVersion":
|
|
|
|
|
bdMinecraft.selectedId})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
text: qsTr("Модлоадер")
|
|
|
|
|
color: "#aaaaaa"
|
|
|
|
|
font.pixelSize: 11
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Лоадеры несовместимы между собой: игра запускается ровно с
|
|
|
|
|
// одним профилем versions/<id>. Поэтому отметка одного снимает
|
|
|
|
|
// остальные, а «ничего не отмечено» — это чистая ваниль.
|
|
|
|
|
Column {
|
|
|
|
|
id: loaderPanel
|
|
|
|
|
width: parent.width
|
|
|
|
|
spacing: 8
|
|
|
|
|
|
|
|
|
|
property var rows: [bdForge, bdFabric, bdNeoForge, bdQuilt]
|
|
|
|
|
|
|
|
|
|
function applyBuild(loader, loaderVersion) {
|
|
|
|
|
for (var i = 0; i < loaderPanel.rows.length; ++i)
|
|
|
|
|
loaderPanel.rows[i].applyBuild(loader, loaderVersion)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function selectedRow() {
|
|
|
|
|
for (var i = 0; i < loaderPanel.rows.length; ++i) {
|
|
|
|
|
if (loaderPanel.rows[i].checked)
|
|
|
|
|
return loaderPanel.rows[i]
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function keepOnly(row) {
|
|
|
|
|
for (var i = 0; i < loaderPanel.rows.length; ++i) {
|
|
|
|
|
if (loaderPanel.rows[i] === row)
|
|
|
|
|
continue
|
|
|
|
|
loaderPanel.rows[i].applyBuild("", "")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function commitSelection() {
|
|
|
|
|
const row = loaderPanel.selectedRow()
|
|
|
|
|
buildsDialog.commit({
|
|
|
|
|
"loader": row ? row.loaderKey : "",
|
|
|
|
|
"loaderVersion": row ? row.selectedVersion : "",
|
|
|
|
|
// Профиль появится только после установки: пока
|
|
|
|
|
// сборка запускается на чистой ванили.
|
|
|
|
|
"resolvedVersionId": ""
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LoaderRow {
|
|
|
|
|
id: bdForge
|
|
|
|
|
width: parent.width
|
|
|
|
|
backend: buildsDialog.backend
|
|
|
|
|
loaderKey: "forge"
|
|
|
|
|
title: qsTr("Minecraft Forge")
|
|
|
|
|
gameVersion: bdMinecraft.selectedId
|
|
|
|
|
onUserChecked: loaderPanel.keepOnly(bdForge)
|
|
|
|
|
onChanged: loaderPanel.commitSelection()
|
|
|
|
|
}
|
|
|
|
|
LoaderRow {
|
|
|
|
|
id: bdFabric
|
|
|
|
|
width: parent.width
|
|
|
|
|
backend: buildsDialog.backend
|
|
|
|
|
loaderKey: "fabric"
|
|
|
|
|
title: qsTr("Fabric Loader")
|
|
|
|
|
gameVersion: bdMinecraft.selectedId
|
|
|
|
|
onUserChecked: loaderPanel.keepOnly(bdFabric)
|
|
|
|
|
onChanged: loaderPanel.commitSelection()
|
|
|
|
|
}
|
|
|
|
|
LoaderRow {
|
|
|
|
|
id: bdNeoForge
|
|
|
|
|
width: parent.width
|
|
|
|
|
backend: buildsDialog.backend
|
|
|
|
|
loaderKey: "neoforge"
|
|
|
|
|
title: qsTr("NeoForge")
|
|
|
|
|
gameVersion: bdMinecraft.selectedId
|
|
|
|
|
onUserChecked: loaderPanel.keepOnly(bdNeoForge)
|
|
|
|
|
onChanged: loaderPanel.commitSelection()
|
|
|
|
|
}
|
|
|
|
|
LoaderRow {
|
|
|
|
|
id: bdQuilt
|
|
|
|
|
width: parent.width
|
|
|
|
|
backend: buildsDialog.backend
|
|
|
|
|
loaderKey: "quilt"
|
|
|
|
|
title: qsTr("Quilt Loader")
|
|
|
|
|
gameVersion: bdMinecraft.selectedId
|
|
|
|
|
onUserChecked: loaderPanel.keepOnly(bdQuilt)
|
|
|
|
|
onChanged: loaderPanel.commitSelection()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Text {
|
|
|
|
|
id: bdStatus
|
|
|
|
|
property bool ok: false
|
|
|
|
|
width: parent.width
|
|
|
|
|
color: bdStatus.ok ? "#8fbf5a" : "#cc6666"
|
|
|
|
|
font.pixelSize: 11
|
|
|
|
|
wrapMode: Text.Wrap
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
footer: Item {
|
|
|
|
|
implicitHeight: 60
|
|
|
|
|
Rectangle {
|
|
|
|
|
anchors.top: parent.top
|
|
|
|
|
width: parent.width
|
|
|
|
|
height: 1
|
|
|
|
|
color: "#333333"
|
|
|
|
|
}
|
|
|
|
|
Row {
|
|
|
|
|
anchors.centerIn: parent
|
|
|
|
|
spacing: 12
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Установить")
|
|
|
|
|
width: 140; height: 36
|
|
|
|
|
enabled: buildsDialog.editIndex >= 0 && !buildsDialog.backend.busy
|
|
|
|
|
opacity: enabled ? 1.0 : 0.45
|
|
|
|
|
contentItem: Text {
|
|
|
|
|
text: parent.text
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: parent.pressed ? "#444444" : "#333333"
|
|
|
|
|
radius: 6
|
|
|
|
|
}
|
|
|
|
|
onClicked: buildsDialog.backend.installCustomBuild(buildsDialog.editIndex)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Сделать активной")
|
|
|
|
|
width: 170; height: 36
|
|
|
|
|
enabled: buildsDialog.editIndex >= 0
|
|
|
|
|
&& buildsDialog.editIndex !== buildsDialog.backend.activeBuildIndex
|
|
|
|
|
&& !buildsDialog.backend.busy
|
|
|
|
|
opacity: enabled ? 1.0 : 0.45
|
|
|
|
|
contentItem: Text {
|
|
|
|
|
text: parent.text
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: parent.pressed ? "#6a8510" : "#91B315"
|
|
|
|
|
radius: 6
|
|
|
|
|
}
|
|
|
|
|
onClicked: buildsDialog.backend.activeBuildIndex = buildsDialog.editIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Button {
|
|
|
|
|
text: qsTr("Закрыть")
|
|
|
|
|
width: 120; height: 36
|
|
|
|
|
enabled: !buildsDialog.backend.switching
|
|
|
|
|
opacity: enabled ? 1.0 : 0.45
|
|
|
|
|
contentItem: Text {
|
|
|
|
|
text: parent.text
|
|
|
|
|
color: "#ffffff"
|
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
|
verticalAlignment: Text.AlignVCenter
|
|
|
|
|
}
|
|
|
|
|
background: Rectangle {
|
|
|
|
|
color: parent.pressed ? "#444444" : "#333333"
|
|
|
|
|
radius: 6
|
|
|
|
|
}
|
|
|
|
|
onClicked: buildsDialog.close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|