added profile and version adding through comboBox

This commit is contained in:
2026-06-09 00:43:06 +03:00
parent 5dd96fc4dc
commit d3bee159bc
4 changed files with 687 additions and 202 deletions

724
Main.qml
View File

@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Layouts 2.15
import QtQuick.Controls 2.15
import Minecraft_launcher
Window {
id: window
@@ -8,62 +9,79 @@ Window {
height: 720
visible: true
flags: Qt.Window
title: qsTr("Hello World")
title: qsTr("Minecraft Launcher")
// ── Backend ────────────────────────────────────────────────────────────
LauncherBackend {
id: backend
onLaunched: (profileName, versionName, serverUrl) => {
console.log("Запуск: профиль=" + profileName
+ " версия=" + versionName
+ " сервер=" + serverUrl)
}
onLaunchError: (message) => {
errorLabel.text = message
errorTimer.restart()
}
}
// ── Background ─────────────────────────────────────────────────────────
Image {
id: image
opacity: 0.296
visible: true
anchors.fill: parent
horizontalAlignment: Image.AlignHCenter
verticalAlignment: Image.AlignVCenter
source: "images/GovuztTW8AAHqBf.jpeg"
focus: false
activeFocusOnTab: false
state: ""
layer.enabled: false
antialiasing: false
clip: false
transformOrigin: Item.Center
scale: 1
mirror: false
mipmap: false
cache: true
autoTransform: true
asynchronous: false
sourceSize.height: 0
sourceSize.width: 0
fillMode: Image.Stretch
}
// ── Error toast ────────────────────────────────────────────────────────
Rectangle {
visible: errorLabel.text !== ""
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 40
width: errorLabel.implicitWidth + 32
height: 40
radius: 8
color: "#cc3333"
Text {
id: errorLabel
anchors.centerIn: parent
color: "#ffffff"
font.pixelSize: 14
}
Timer {
id: errorTimer
interval: 3000
onTriggered: errorLabel.text = ""
}
}
// ── Play button ────────────────────────────────────────────────────────
Button {
id: button
x: 440
y: 105
width: 335
height: 170
hoverEnabled: false
anchors.centerIn: parent
opacity: 1
hoverEnabled: false
background: Image {
id: buttonImage
source: "images/Play_Button/Play_Active.svg"
}
Connections {
target: button
function onPressed() { buttonImage.source = "images/Play_Button/Play_pressed.svg" }
}
Connections {
target: button
function onReleased() { buttonImage.source = "images/Play_Button/Play_Active.svg" }
}
onPressed: buttonImage.source = "images/Play_Button/Play_pressed.svg"
onReleased: buttonImage.source = "images/Play_Button/Play_Active.svg"
onClicked: backend.launchGame(profileBox.currentIndex, versionBox.currentIndex)
}
// ── Profile ComboBox ───────────────────────────────────────────────────
ComboBox {
id: profileBox
width: 117
y: 451
width: 146
height: 42
anchors.left: button.right
anchors.bottom: button.top
@@ -72,235 +90,541 @@ Window {
editable: false
leftPadding: -30
model: ["First","Second","Third"]
model: backend.profileNames
delegate: ItemDelegate {
id: delegate
required property var model
required property int index
width: profileBox.width
contentItem: Text {
text: delegate.model[profileBox.textRole]
color: "#ffffff"
font: profileBox.font
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
}
highlighted: profileBox.highlightedIndex === index
background: Rectangle{
color: delegate.highlighted ? "#91B315" : "#232323"
radius: 5
}
}
indicator: Image {
id: dropDownArrow
width: 10; height: 10
visible: true
horizontalAlignment: Image.AlignRight
anchors.leftMargin: 10
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
source: profileBox.down ? "images/Profile_Box/Asset_23.svg" : "images/Profile_Box/Asset_24.svg"
transformOrigin: Item.Center
sourceSize.height: 10
sourceSize.width: 10
source: profileBox.down ? "images/Profile_Box/Asset_23.svg"
: "images/Profile_Box/Asset_24.svg"
rotation: 180
sourceSize.width: 10; sourceSize.height: 10
fillMode: Image.PreserveAspectFit
autoTransform: true
width: 10
height: 10
}
/*indicator: Canvas {
id: canvas
y: profileBox.topPadding + (profileBox.availableHeight - height) / 2
width: 12
height: 8
anchors.left: parent.left
anchors.leftMargin: 117
contextType: "2d"
onPaint: {
context.reset();
context.moveTo(0, 0);
context.lineTo(width, 0);
context.lineTo(width / 2, height);
context.closePath();
context.fillStyle = profileBox.pressed ? "#ffffff" : "#232323";
context.fill();
}
}*/
Connections {
target: profileBox
function onActivated() { canvas.requestPaint(); }
}
Connections {
target: profileBox
function onActivated() { dropDownArrow.visible = true }
}
contentItem: Text {
leftPadding: 0
x: 26
width: 114
rightPadding: profileBox.indicator.width + profileBox.spacing
text: profileBox.displayText
font: profileBox.font
color: profileBox.pressed ? "#ffffff" : "#ffffff"
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 120
implicitHeight: 40
border.color: profileBox.pressed ? "#ffffff" : "#232323"
border.width: profileBox.visualFocus ? 2 : 1
implicitWidth: 120; implicitHeight: 40
color: profileBox.down ? "#91B315" : "#232323"
radius: 6
border.color: profileBox.pressed ? "#ffffff" : "#232323"
border.width: profileBox.visualFocus ? 2 : 1
}
popup: Popup {
y: profileBox.height - 1
width: profileBox.width
height: Math.min(contentItem.implicitHeight, profileBox.Window.height - topMargin - bottomMargin)
padding: 1
padding: 0
// Высота = кнопка "+" (32) + элементы списка, не более 200
height: 32 + Math.min(profileItemList.contentHeight, 200)
contentItem: ListView {
rotation: 0
clip: true
implicitHeight: contentHeight
model: profileBox.popup.visible ? profileBox.delegateModel : null
currentIndex: profileBox.highlightedIndex
contentItem: Item {
anchors.fill: parent
// ── "+" button — всегда сверху ──────────────────────────
Rectangle {
id: profileAddBtnBg
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 32
color: profileAddArea.containsPress ? "#2d2d2d" : "transparent"
ScrollIndicator.vertical: ScrollIndicator {
parent: ListView.parent
anchors.top: ListView.top
anchors.left: ListView.right
anchors.bottom: ListView.bottom
/*background: Rectangle{
opacity: 0.5
Text {
anchors.centerIn: parent
text: "+ Добавить профиль"
color: "#91B315"
}*/
font.pixelSize: 12
}
MouseArea {
id: profileAddArea
anchors.fill: parent
onClicked: {
profileBox.popup.close()
addProfileDialog.open()
}
}
}
// ── Добавленные профили — всегда ниже кнопки ───────────
ListView {
id: profileItemList
anchors.top: profileAddBtnBg.bottom
anchors.left: parent.left
anchors.right: parent.right
height: Math.min(contentHeight, 200)
clip: true
model: backend.profileNames
delegate: ItemDelegate {
id: profileItem
required property string modelData
required property int index
width: profileItemList.width
height: 40
contentItem: Text {
text: profileItem.modelData
color: "#ffffff"
font: profileBox.font
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
leftPadding: 8
}
background: Rectangle {
color: (profileBox.currentIndex === profileItem.index || profileItem.hovered)
? "#91B315" : "#232323"
radius: 5
}
onClicked: {
profileBox.currentIndex = profileItem.index
profileBox.popup.close()
}
}
ScrollIndicator.vertical: ScrollIndicator {
parent: profileItemList.parent
anchors.top: profileItemList.top
anchors.left: profileItemList.right
anchors.bottom: profileItemList.bottom
}
}
}
background: Rectangle {
border.color: "#232323"
color: "#232323"
radius: 6
border.color: "#232323"
}
}
}
// ── Version ComboBox ───────────────────────────────────────────────────
ComboBox {
id: versionBox
width: 210
x: 624
y: 451
width: 183
height: 42
anchors.right: button.right
anchors.bottom: button.top
anchors.bottomMargin: -218
editable: false
leftPadding: -30
model: backend.versionNames
indicator: Image {
width: 10; height: 10
visible: true
anchors.left: parent.left
anchors.leftMargin: 10
anchors.verticalCenter: parent.verticalCenter
source: versionBox.down ? "images/Profile_Box/Asset_23.svg"
: "images/Profile_Box/Asset_24.svg"
rotation: 180
sourceSize.width: 10; sourceSize.height: 10
fillMode: Image.PreserveAspectFit
autoTransform: true
}
contentItem: Text {
x: 26
width: 150
leftPadding: 56
rightPadding: versionBox.indicator.width + versionBox.spacing
text: versionBox.displayText
font: versionBox.font
color: "#ffffff"
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
elide: Text.ElideRight
}
background: Rectangle {
implicitWidth: 120; implicitHeight: 40
color: versionBox.down ? "#91B315" : "#232323"
radius: 6
border.color: versionBox.pressed ? "#ffffff" : "#232323"
border.width: versionBox.visualFocus ? 2 : 1
}
popup: Popup {
y: versionBox.height - 1
width: versionBox.width
height: Math.min(contentItem.implicitHeight, versionBox.Window.height - topMargin - bottomMargin)
padding: 1
contentItem: ListView {
rotation: 0
model: versionBox.popup.visible ? versionBox.delegateModel : null
implicitHeight: contentHeight
currentIndex: versionBox.highlightedIndex
clip: true
ScrollIndicator.vertical: ScrollIndicator {
anchors.left: ListView.right
anchors.top: ListView.top
anchors.bottom: ListView.bottom
parent: ListView.parent
padding: 0
height: 32 + Math.min(versionItemList.contentHeight, 200)
contentItem: Item {
anchors.fill: parent
// ── "+" button — всегда сверху ──────────────────────────
Rectangle {
id: versionAddBtnBg
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 32
color: versionAddArea.containsPress ? "#2d2d2d" : "transparent"
Text {
anchors.centerIn: parent
text: "+ Добавить версию"
color: "#91B315"
font.pixelSize: 12
}
MouseArea {
id: versionAddArea
anchors.fill: parent
onClicked: {
versionBox.popup.close()
addVersionDialog.open()
}
}
}
// ── Добавленные версии — всегда ниже кнопки ────────────
ListView {
id: versionItemList
anchors.top: versionAddBtnBg.bottom
anchors.left: parent.left
anchors.right: parent.right
height: Math.min(contentHeight, 200)
clip: true
model: backend.versionNames
delegate: ItemDelegate {
id: versionItem
required property string modelData
required property int index
width: versionItemList.width
height: 40
contentItem: Text {
text: versionItem.modelData
color: "#ffffff"
font: versionBox.font
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
leftPadding: 8
}
background: Rectangle {
color: (versionBox.currentIndex === versionItem.index || versionItem.hovered)
? "#91B315" : "#232323"
radius: 5
}
onClicked: {
versionBox.currentIndex = versionItem.index
versionBox.popup.close()
}
}
ScrollIndicator.vertical: ScrollIndicator {
parent: versionItemList.parent
anchors.top: versionItemList.top
anchors.left: versionItemList.right
anchors.bottom: versionItemList.bottom
}
}
}
background: Rectangle {
color: "#232323"
radius: 6
border.color: "#232323"
}
}
model: ["Version 1","Version 2","Version 3"]
leftPadding: -30
indicator: Image {
id: dropDownArrow1
width: 10
height: 10
visible: true
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
horizontalAlignment: Image.AlignRight
source: versionBox.down ? "images/Profile_Box/Asset_23.svg" : "images/Profile_Box/Asset_24.svg"
transformOrigin: Item.Center
sourceSize.width: 10
sourceSize.height: 10
fillMode: Image.PreserveAspectFit
autoTransform: true
}
editable: false
delegate: ItemDelegate {
id: delegate1
width: versionBox.width
required property var model
required property int index
highlighted: versionBox.highlightedIndex === index
contentItem: Text {
color: "#ffffff"
text: delegate1.model[versionBox.textRole]
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
font: versionBox.font
}
background: Rectangle {
color: delegate1.highlighted ? "#91B315" : "#232323"
radius: 5
}
}
Connections {
target: versionBox
function onActivated() { canvas.requestPaint(); }
}
// ── Add Profile Dialog ─────────────────────────────────────────────────
// Bug fix: header/footer must be Item (not Rectangle) with explicit
// implicitHeight so Dialog correctly computes its own total height.
// Rectangle.implicitHeight defaults to 0 regardless of height:.
Dialog {
id: addProfileDialog
modal: true
width: 320
x: (window.width - width) / 2
y: (window.height - height) / 2
padding: 0
background: Rectangle {
color: "#1e1e1e"
radius: 10
border.color: "#91B315"
border.width: 1
}
Connections {
target: versionBox
function onActivated() { dropDownArrow1.visible = true }
header: Item {
implicitHeight: 52 // tells Dialog how tall the header is
Text {
anchors.centerIn: parent
text: "Новый профиль"
color: "#ffffff"
font.pixelSize: 17
font.bold: true
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: "#333333"
}
}
contentItem: Text {
color: versionBox.pressed ? "#ffffff" : "#ffffff"
text: versionBox.displayText
elide: Text.ElideRight
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
rightPadding: versionBox.indicator.width + versionBox.spacing
leftPadding: 56
font: versionBox.font
contentItem: Column {
spacing: 12
topPadding: 20
bottomPadding: 20
TextField {
id: pfName
x: 20
width: parent.width - 40
placeholderText: "Имя профиля"
color: "#ffffff"
placeholderTextColor: "#666666"
background: Rectangle {
color: "#2a2a2a"
radius: 6
border.color: pfName.activeFocus ? "#91B315" : "#444444"
border.width: 1
}
}
TextField {
id: pfLogin
x: 20
width: parent.width - 40
placeholderText: "Логин"
color: "#ffffff"
placeholderTextColor: "#666666"
background: Rectangle {
color: "#2a2a2a"
radius: 6
border.color: pfLogin.activeFocus ? "#91B315" : "#444444"
border.width: 1
}
}
TextField {
id: pfPassword
x: 20
width: parent.width - 40
placeholderText: "Пароль"
echoMode: TextInput.Password
color: "#ffffff"
placeholderTextColor: "#666666"
background: Rectangle {
color: "#2a2a2a"
radius: 6
border.color: pfPassword.activeFocus ? "#91B315" : "#444444"
border.width: 1
}
}
}
background: Rectangle {
color: versionBox.down ? "#91B315" : "#232323"
radius: 6
border.color: versionBox.pressed ? "#ffffff" : "#232323"
border.width: versionBox.visualFocus ? 2 : 1
implicitWidth: 120
implicitHeight: 40
footer: Item {
implicitHeight: 60 // tells Dialog how tall the footer is
Rectangle {
anchors.top: parent.top
width: parent.width
height: 1
color: "#333333"
}
Row {
anchors.centerIn: parent
spacing: 12
Button {
text: "Отмена"
width: 110; height: 36
contentItem: Text {
text: parent.text
color: "#ffffff"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: parent.pressed ? "#444444" : "#333333"
radius: 6
}
onClicked: addProfileDialog.reject()
}
Button {
text: "Добавить"
width: 110; 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: addProfileDialog.accept()
}
}
}
onAccepted: {
const name = pfName.text.trim()
if (name !== "") {
backend.addProfile(name, pfLogin.text.trim(), pfPassword.text)
profileBox.currentIndex = backend.profileNames.length - 1
}
pfName.text = ""; pfLogin.text = ""; pfPassword.text = ""
}
onRejected: {
pfName.text = ""; pfLogin.text = ""; pfPassword.text = ""
}
}
// ── Add Version Dialog ─────────────────────────────────────────────────
Dialog {
id: addVersionDialog
modal: true
width: 320
x: (window.width - width) / 2
y: (window.height - height) / 2
padding: 0
background: Rectangle {
color: "#1e1e1e"
radius: 10
border.color: "#91B315"
border.width: 1
}
header: Item {
implicitHeight: 52
Text {
anchors.centerIn: parent
text: "Новая версия"
color: "#ffffff"
font.pixelSize: 17
font.bold: true
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: "#333333"
}
}
contentItem: Column {
spacing: 12
topPadding: 20
bottomPadding: 20
TextField {
id: verName
x: 20
width: parent.width - 40
placeholderText: "Название версии"
color: "#ffffff"
placeholderTextColor: "#666666"
background: Rectangle {
color: "#2a2a2a"
radius: 6
border.color: verName.activeFocus ? "#91B315" : "#444444"
border.width: 1
}
}
TextField {
id: verServer
x: 20
width: parent.width - 40
placeholderText: "URL сервера загрузки"
color: "#ffffff"
placeholderTextColor: "#666666"
background: Rectangle {
color: "#2a2a2a"
radius: 6
border.color: verServer.activeFocus ? "#91B315" : "#444444"
border.width: 1
}
}
}
footer: Item {
implicitHeight: 60
Rectangle {
anchors.top: parent.top
width: parent.width
height: 1
color: "#333333"
}
Row {
anchors.centerIn: parent
spacing: 12
Button {
text: "Отмена"
width: 110; height: 36
contentItem: Text {
text: parent.text
color: "#ffffff"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
color: parent.pressed ? "#444444" : "#333333"
radius: 6
}
onClicked: addVersionDialog.reject()
}
Button {
text: "Добавить"
width: 110; 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: addVersionDialog.accept()
}
}
}
onAccepted: {
const name = verName.text.trim()
if (name !== "") {
backend.addVersion(name, verServer.text.trim())
versionBox.currentIndex = backend.versionNames.length - 1
}
verName.text = ""; verServer.text = ""
}
onRejected: {
verName.text = ""; verServer.text = ""
}
}
}