launcher project v 0.1
@@ -0,0 +1,41 @@
|
||||
pragma Singleton
|
||||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
// Цвета
|
||||
readonly property color backgroundColor: "#0A0A0A"
|
||||
readonly property color frameColor: "#1A1A1A"
|
||||
readonly property color borderColor: "#393939"
|
||||
readonly property color primaryText: "#FFB100"
|
||||
readonly property color secondaryText: "#CD8500"
|
||||
readonly property color disabledText: "#4A4A4A"
|
||||
readonly property color accentColor: "#4B0082"
|
||||
|
||||
// Размеры
|
||||
readonly property int margin: 10
|
||||
readonly property int spacing: 15
|
||||
readonly property int radius: 8
|
||||
readonly property int buttonHeight: 45
|
||||
|
||||
// Шрифты
|
||||
readonly property int titleSize: 24
|
||||
readonly property int subtitleSize: 16
|
||||
readonly property int normalSize: 14
|
||||
readonly property int smallSize: 12
|
||||
|
||||
// Градиенты
|
||||
readonly property var buttonGradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#1B3859" }
|
||||
GradientStop { position: 1.0; color: "#0A1B2A" }
|
||||
}
|
||||
|
||||
readonly property var buttonHoverGradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#2B4869" }
|
||||
GradientStop { position: 1.0; color: "#1A2B3A" }
|
||||
}
|
||||
|
||||
readonly property var buttonPressedGradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#0A1B2A" }
|
||||
GradientStop { position: 1.0; color: "#1B3859" }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
module Theme
|
||||
singleton Theme 1.0 Theme.qml
|
||||
@@ -0,0 +1,61 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
title: "О программе"
|
||||
modal: true
|
||||
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
width: 400
|
||||
height: 300
|
||||
|
||||
Material.background: Theme.Theme.frameColor
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
Image {
|
||||
source: "qml/images/logo.png"
|
||||
Layout.preferredWidth: 200
|
||||
Layout.preferredHeight: 100
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "World of Warcraft 3.3.5a Launcher"
|
||||
font.pixelSize: Theme.Theme.titleSize
|
||||
font.bold: true
|
||||
color: Theme.Theme.primaryText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Версия: " + (launcher ? launcher.version : "3.3.5")
|
||||
color: Theme.Theme.secondaryText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Label {
|
||||
text: "© 2024 Your Server Name"
|
||||
color: Theme.Theme.secondaryText
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
Button {
|
||||
text: "Закрыть"
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Menu {
|
||||
id: contextMenu
|
||||
|
||||
MenuItem {
|
||||
text: "Открыть папку с игрой"
|
||||
enabled: launcher && launcher.gamePath
|
||||
onTriggered: launcher.openGameFolder()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Проверить файлы"
|
||||
enabled: launcher && launcher.gamePath && !launcher.isDownloading
|
||||
onTriggered: launcher.verifyFiles()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "Восстановить клиент"
|
||||
enabled: launcher && launcher.gamePath && !launcher.isDownloading
|
||||
onTriggered: launcher.repairClient()
|
||||
}
|
||||
|
||||
MenuSeparator { }
|
||||
|
||||
MenuItem {
|
||||
text: "Настройки"
|
||||
onTriggered: settingsDialog.open()
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: "О программе"
|
||||
onTriggered: aboutDialog.open()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 70
|
||||
color: "transparent"
|
||||
|
||||
// Анимированный фон логотипа
|
||||
Rectangle {
|
||||
id: logoBackground
|
||||
anchors.left: parent.left
|
||||
width: logo.width
|
||||
height: parent.height
|
||||
color: "transparent"
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#00152536" }
|
||||
GradientStop { position: 0.5; color: "#20152536" }
|
||||
GradientStop { position: 1.0; color: "#00152536" }
|
||||
}
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.3; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
NumberAnimation { to: 1.0; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
source: "qml/images/logo.png"
|
||||
width: parent.height * 1.5
|
||||
height: parent.height
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
// Эффект свечения для логотипа
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#3A5570"
|
||||
opacity: 0
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.2; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
NumberAnimation { to: 0; duration: 2000; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Статистика сервера
|
||||
Row {
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
rightMargin: Theme.Theme.spacing
|
||||
}
|
||||
spacing: Theme.Theme.spacing * 2
|
||||
|
||||
// Онлайн
|
||||
StatsBox {
|
||||
label: "ОНЛАЙН"
|
||||
value: "1234"
|
||||
|
||||
// Анимация при изменении значения
|
||||
Behavior on value {
|
||||
NumberAnimation {
|
||||
duration: 500
|
||||
easing.type: Easing.OutBack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Рейты
|
||||
StatsBox {
|
||||
label: "РЕЙТЫ"
|
||||
value: "x2"
|
||||
}
|
||||
}
|
||||
|
||||
// Компонент для отображения статистики
|
||||
component StatsBox: Column {
|
||||
property string label: ""
|
||||
property string value: ""
|
||||
spacing: 2
|
||||
|
||||
Label {
|
||||
text: label
|
||||
color: Theme.Theme.secondaryText
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
font.bold: true
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
text: value
|
||||
color: "#00AAFF"
|
||||
font.pixelSize: Theme.Theme.titleSize
|
||||
font.bold: true
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
// Подсветка при наведении
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onEntered: parent.color = "#40AAFF"
|
||||
onExited: parent.color = "#00AAFF"
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 150 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Pane {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
padding: Theme.Theme.margin
|
||||
|
||||
Material.elevation: 6
|
||||
Material.background: Qt.darker(Theme.Theme.frameColor, 1.1)
|
||||
|
||||
property alias serverInfo: serverLabel.text
|
||||
property alias requirements: reqLabel.text
|
||||
|
||||
background: Rectangle {
|
||||
color: Qt.darker(Theme.Theme.frameColor, 1.1)
|
||||
radius: Theme.Theme.radius / 2
|
||||
border.color: Qt.darker(Theme.Theme.borderColor, 1.2)
|
||||
border.width: 1
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#20FFFFFF" }
|
||||
GradientStop { position: 1.0; color: "#00FFFFFF" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: Theme.Theme.spacing * 1.5
|
||||
|
||||
// Секция сервера
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
Image {
|
||||
source: "qml/images/icons/server.png"
|
||||
sourceSize: Qt.size(24, 24)
|
||||
Layout.alignment: Qt.AlignTop
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.Theme.spacing / 2
|
||||
|
||||
Label {
|
||||
text: "ИНФОРМАЦИЯ О СЕРВЕРЕ"
|
||||
color: Theme.Theme.secondaryText
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
id: serverLabel
|
||||
Layout.fillWidth: true
|
||||
color: "#77A7D1"
|
||||
font.pixelSize: Theme.Theme.normalSize
|
||||
font.bold: true
|
||||
|
||||
Behavior on text {
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: serverLabel; property: "opacity"; to: 0; duration: 100 }
|
||||
PropertyAction { target: serverLabel; property: "text" }
|
||||
NumberAnimation { target: serverLabel; property: "opacity"; to: 1; duration: 100 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
height: 1
|
||||
color: Qt.darker(Theme.Theme.borderColor, 1.2)
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
// Секция системных требований
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
Image {
|
||||
source: "qml/images/icons/requirements.png"
|
||||
sourceSize: Qt.size(24, 24)
|
||||
Layout.alignment: Qt.AlignTop
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.Theme.spacing / 2
|
||||
|
||||
Label {
|
||||
text: "СИСТЕМНЫЕ ТРЕБОВАНИЯ"
|
||||
color: Theme.Theme.secondaryText
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Label {
|
||||
id: reqLabel
|
||||
Layout.fillWidth: true
|
||||
color: Theme.Theme.secondaryText
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
Behavior on text {
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: reqLabel; property: "opacity"; to: 0; duration: 100 }
|
||||
PropertyAction { target: reqLabel; property: "text" }
|
||||
NumberAnimation { target: reqLabel; property: "opacity"; to: 1; duration: 100 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: message.width + Theme.Theme.spacing * 4
|
||||
height: message.height + Theme.Theme.spacing * 2
|
||||
radius: Theme.Theme.radius
|
||||
opacity: 0
|
||||
|
||||
property string text: ""
|
||||
property string type: "info" // info, error, success
|
||||
|
||||
color: {
|
||||
switch(type) {
|
||||
case "error": return "#4D2C2C"
|
||||
case "success": return "#2C4D2C"
|
||||
default: return Theme.Theme.frameColor
|
||||
}
|
||||
}
|
||||
|
||||
border.color: {
|
||||
switch(type) {
|
||||
case "error": return "#FF4444"
|
||||
case "success": return "#44FF44"
|
||||
default: return Theme.Theme.borderColor
|
||||
}
|
||||
}
|
||||
border.width: 1
|
||||
|
||||
Label {
|
||||
id: message
|
||||
anchors.centerIn: parent
|
||||
text: root.text
|
||||
color: Theme.Theme.primaryText
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
}
|
||||
|
||||
// Анимации
|
||||
NumberAnimation {
|
||||
id: showAnim
|
||||
target: root
|
||||
property: "opacity"
|
||||
to: 1
|
||||
duration: 200
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: hideAnim
|
||||
target: root
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: 200
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: 3000
|
||||
onTriggered: hideAnim.start()
|
||||
}
|
||||
|
||||
function show() {
|
||||
showAnim.start()
|
||||
hideTimer.restart()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Item {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 40
|
||||
visible: value > 0 || text !== ""
|
||||
|
||||
property alias value: progressBar.value
|
||||
property alias text: speedLabel.text
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 14
|
||||
from: 0
|
||||
to: 1.0
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 14
|
||||
color: "#0A0A0A"
|
||||
radius: 2
|
||||
border.width: 1
|
||||
border.color: "#2A4055"
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: "#152536"
|
||||
visible: progressBar.value > 0 && progressBar.value < 1
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
id: animatedBackground
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: "#1E3346"
|
||||
radius: parent.radius
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
running: progressBar.value > 0 && progressBar.value < 1
|
||||
NumberAnimation { to: 0.3; duration: 1000 }
|
||||
NumberAnimation { to: 0.1; duration: 1000 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: 200
|
||||
implicitHeight: 14
|
||||
clip: true
|
||||
|
||||
Rectangle {
|
||||
width: progressBar.visualPosition * parent.width
|
||||
height: parent.height
|
||||
radius: 2
|
||||
clip: true
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#1E3346" }
|
||||
GradientStop { position: 0.5; color: "#2B4869" }
|
||||
GradientStop { position: 1.0; color: "#1E3346" }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: "#3A5570"
|
||||
opacity: 0.7
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#20FFFFFF" }
|
||||
GradientStop { position: 0.5; color: "#40FFFFFF" }
|
||||
GradientStop { position: 1.0; color: "#20FFFFFF" }
|
||||
}
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
loops: Animation.Infinite
|
||||
running: progressBar.value > 0 && progressBar.value < 1
|
||||
NumberAnimation { to: 0.1; duration: 1000 }
|
||||
NumberAnimation { to: 0.3; duration: 1000 }
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on width {
|
||||
NumberAnimation {
|
||||
duration: 100
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
Label {
|
||||
id: speedLabel
|
||||
color: "#77A7D1"
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
font.bold: true
|
||||
visible: text !== ""
|
||||
|
||||
Behavior on text {
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: speedLabel; property: "opacity"; to: 0.7; duration: 100 }
|
||||
PropertyAction { target: speedLabel; property: "text" }
|
||||
NumberAnimation { target: speedLabel; property: "opacity"; to: 1.0; duration: 100 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: Math.round(progressBar.value * 100) + "%"
|
||||
color: "#77A7D1"
|
||||
font.pixelSize: Theme.Theme.smallSize
|
||||
font.bold: true
|
||||
horizontalAlignment: Text.AlignRight
|
||||
visible: progressBar.value > 0
|
||||
|
||||
Behavior on text {
|
||||
SequentialAnimation {
|
||||
NumberAnimation { target: parent; property: "opacity"; to: 0.7; duration: 100 }
|
||||
PropertyAction { target: parent; property: "text" }
|
||||
NumberAnimation { target: parent; property: "opacity"; to: 1.0; duration: 100 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Pane {
|
||||
id: root
|
||||
padding: Theme.Theme.margin
|
||||
|
||||
Material.elevation: 6
|
||||
Material.background: Theme.Theme.frameColor
|
||||
|
||||
// Анимация появления
|
||||
opacity: 0
|
||||
Component.onCompleted: appearAnimation.start()
|
||||
|
||||
NumberAnimation {
|
||||
id: appearAnimation
|
||||
target: root
|
||||
property: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: 500
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Qt.rgba(Theme.Theme.frameColor.r, Theme.Theme.frameColor.g, Theme.Theme.frameColor.b, 0.35)
|
||||
radius: Theme.Theme.radius
|
||||
|
||||
// Граница с тенью
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(Theme.Theme.borderColor.r, Theme.Theme.borderColor.g, Theme.Theme.borderColor.b, 0.4)
|
||||
border.width: 1
|
||||
|
||||
// Нижняя тень
|
||||
Rectangle {
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "#000000"
|
||||
opacity: 0.1
|
||||
}
|
||||
|
||||
// Верхний блик
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: "#FFFFFF"
|
||||
opacity: 0.1
|
||||
}
|
||||
}
|
||||
|
||||
// Внутренний градиент
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: Qt.rgba(1, 1, 1, 0.1) }
|
||||
GradientStop { position: 1.0; color: Qt.rgba(1, 1, 1, 0) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Контент
|
||||
contentItem: ColumnLayout {
|
||||
spacing: Theme.Theme.spacing
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
title: "Настройки"
|
||||
modal: true
|
||||
|
||||
x: (parent.width - width) / 2
|
||||
y: (parent.height - height) / 2
|
||||
width: 400
|
||||
height: 500
|
||||
|
||||
Material.background: Theme.Theme.frameColor
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
ColumnLayout {
|
||||
width: scrollView.availableWidth
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
// Игровые настройки
|
||||
GroupBox {
|
||||
Layout.fillWidth: true
|
||||
title: "Игровые настройки"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
// Путь к игре
|
||||
Label {
|
||||
text: "Путь к игре:"
|
||||
color: Theme.Theme.secondaryText
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: Theme.Theme.spacing
|
||||
|
||||
TextField {
|
||||
id: gamePathField
|
||||
Layout.fillWidth: true
|
||||
text: launcher ? launcher.gamePath : ""
|
||||
readOnly: true
|
||||
color: Theme.Theme.primaryText
|
||||
|
||||
background: Rectangle {
|
||||
color: Qt.darker(Theme.Theme.frameColor, 1.2)
|
||||
border.color: Theme.Theme.borderColor
|
||||
radius: 3
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Обзор"
|
||||
onClicked: launcher && launcher.selectGamePath()
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
text: "Автозапуск при старте Windows"
|
||||
checked: launcher && launcher.settings ? launcher.settings.autostart : false
|
||||
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.autostart = checked
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
text: "Закрывать лаунчер при запуске игры"
|
||||
checked: launcher && launcher.settings ? launcher.settings.closeOnLaunch : false
|
||||
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.closeOnLaunch = checked
|
||||
}
|
||||
|
||||
// Выбор эмулятора для Linux
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
visible: Qt.platform.os !== "windows" // Показываем только на Linux/Mac
|
||||
|
||||
Label {
|
||||
text: "Эмулятор Windows:"
|
||||
color: Theme.Theme.secondaryText
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.fillWidth: true
|
||||
model: ["Wine", "Lutris", "Proton", "PortProton", "CrossOver"]
|
||||
currentIndex: {
|
||||
if (launcher && launcher.settings) {
|
||||
switch(launcher.settings.linuxEmulator) {
|
||||
case "wine": return 0;
|
||||
case "lutris": return 1;
|
||||
case "proton": return 2;
|
||||
case "portproton": return 3;
|
||||
case "crossover": return 4;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
onCurrentIndexChanged: {
|
||||
if (launcher && launcher.settings) {
|
||||
switch(currentIndex) {
|
||||
case 0: launcher.settings.linuxEmulator = "wine"; break;
|
||||
case 1: launcher.settings.linuxEmulator = "lutris"; break;
|
||||
case 2: launcher.settings.linuxEmulator = "proton"; break;
|
||||
case 3: launcher.settings.linuxEmulator = "portproton"; break;
|
||||
case 4: launcher.settings.linuxEmulator = "crossover"; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Кнопка проверки эмулятора
|
||||
Button {
|
||||
text: "Проверить"
|
||||
onClicked: if (launcher) launcher.checkEmulator()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Настройки загрузки
|
||||
GroupBox {
|
||||
Layout.fillWidth: true
|
||||
title: "Настройки загрузки"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
text: "Ограничение скорости загрузки:"
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Layout.fillWidth: true
|
||||
model: ["Без ограничений", "1 Мбит/с", "2 Мбит/с", "5 Мбит/с", "10 Мбит/с"]
|
||||
currentIndex: launcher && launcher.settings ? launcher.settings.speedLimit : 0
|
||||
onCurrentIndexChanged: if (launcher && launcher.settings) launcher.settings.speedLimit = currentIndex
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
text: "Автоматически загружать обновления"
|
||||
checked: launcher && launcher.settings ? launcher.settings.autoUpdate : true
|
||||
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.autoUpdate = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Настройки интерфейса
|
||||
GroupBox {
|
||||
Layout.fillWidth: true
|
||||
title: "Настройки интерфейса"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
Label {
|
||||
text: "Интервал слайд-шоу (сек):"
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
Layout.fillWidth: true
|
||||
from: 3
|
||||
to: 15
|
||||
value: launcher && launcher.settings ? launcher.settings.slideInterval : 5
|
||||
onValueChanged: if (launcher && launcher.settings) launcher.settings.slideInterval = value
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
text: "Показывать уведомления"
|
||||
checked: launcher && launcher.settings ? launcher.settings.showNotifications : true
|
||||
onCheckedChanged: if (launcher && launcher.settings) launcher.settings.showNotifications = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: Theme.Theme.spacing * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: DialogButtonBox {
|
||||
Button {
|
||||
text: "Применить"
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.ApplyRole
|
||||
onClicked: {
|
||||
if (launcher) launcher.saveSettings()
|
||||
root.accept()
|
||||
}
|
||||
}
|
||||
Button {
|
||||
text: "Отмена"
|
||||
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
|
||||
onClicked: root.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Item {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 300
|
||||
|
||||
property var imageUrls: []
|
||||
property int interval: 5000
|
||||
|
||||
// Добавляем второе изображение для плавного перехода
|
||||
Image {
|
||||
id: fadeOutImage
|
||||
anchors.fill: parent
|
||||
fillMode: Image.PreserveAspectFit
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
Image {
|
||||
id: mainImage
|
||||
anchors.fill: parent
|
||||
source: imageUrls.length > 0 ? imageUrls[currentIndex] : ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
property int currentIndex: 0
|
||||
|
||||
Timer {
|
||||
interval: root.interval
|
||||
running: imageUrls.length > 1
|
||||
repeat: true
|
||||
onTriggered: nextSlide()
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 800; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
}
|
||||
|
||||
// Кнопки навигации
|
||||
Rectangle {
|
||||
id: prevButton
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 40; height: 40
|
||||
color: "transparent"
|
||||
opacity: prevMouse.containsMouse ? 1 : 0.3
|
||||
visible: imageUrls.length > 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "❮"
|
||||
color: "#FFFFFF"
|
||||
font.pixelSize: 24
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: prevMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: prevSlide()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: nextButton
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 40; height: 40
|
||||
color: "transparent"
|
||||
opacity: nextMouse.containsMouse ? 1 : 0.3
|
||||
visible: imageUrls.length > 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "❯"
|
||||
color: "#FFFFFF"
|
||||
font.pixelSize: 24
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: nextMouse
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: nextSlide()
|
||||
}
|
||||
}
|
||||
|
||||
// Индикаторы в стиле WoW
|
||||
Row {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottomMargin: 10
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: imageUrls.length
|
||||
|
||||
Rectangle {
|
||||
width: 30
|
||||
height: 4
|
||||
radius: 2
|
||||
color: mainImage.currentIndex === index ? "#00AAFF" : "#80FFFFFF"
|
||||
opacity: mainImage.currentIndex === index ? 1.0 : 0.5
|
||||
|
||||
Rectangle {
|
||||
visible: mainImage.currentIndex === index
|
||||
anchors.fill: parent
|
||||
color: "#40FFFFFF"
|
||||
radius: parent.radius
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: mainImage.currentIndex === index
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.2; duration: 1000 }
|
||||
NumberAnimation { to: 1.0; duration: 1000 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: showSlide(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function nextSlide() {
|
||||
fadeOutImage.source = mainImage.source
|
||||
fadeOutImage.opacity = 1
|
||||
mainImage.currentIndex = (mainImage.currentIndex + 1) % imageUrls.length
|
||||
fadeOutImage.opacity = 0
|
||||
}
|
||||
|
||||
function prevSlide() {
|
||||
fadeOutImage.source = mainImage.source
|
||||
fadeOutImage.opacity = 1
|
||||
mainImage.currentIndex = mainImage.currentIndex === 0 ? imageUrls.length - 1 : mainImage.currentIndex - 1
|
||||
fadeOutImage.opacity = 0
|
||||
}
|
||||
|
||||
function showSlide(index) {
|
||||
if (index !== mainImage.currentIndex) {
|
||||
fadeOutImage.source = mainImage.source
|
||||
fadeOutImage.opacity = 1
|
||||
mainImage.currentIndex = index
|
||||
fadeOutImage.opacity = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Controls.Material 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Button {
|
||||
id: control
|
||||
height: Theme.Theme.buttonHeight
|
||||
|
||||
background: Rectangle {
|
||||
color: {
|
||||
if (!control.enabled) return "#0A0A0A"
|
||||
if (control.pressed) return "#1A2A3A"
|
||||
if (control.hovered) return "#1E3346"
|
||||
return "#152536"
|
||||
}
|
||||
radius: 3
|
||||
|
||||
// Верхняя грань (блик)
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: {
|
||||
if (!control.enabled) return "#252525"
|
||||
if (control.pressed) return "#2A4055"
|
||||
if (control.hovered) return "#3A5570"
|
||||
return "#2A4055"
|
||||
}
|
||||
opacity: control.pressed ? 0.5 : 1
|
||||
}
|
||||
|
||||
// Нижняя грань (тень)
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
anchors.bottom: parent.bottom
|
||||
color: "#000000"
|
||||
opacity: 0.5
|
||||
}
|
||||
|
||||
// Боковые грани
|
||||
Rectangle {
|
||||
width: 1
|
||||
height: parent.height
|
||||
color: {
|
||||
if (!control.enabled) return "#252525"
|
||||
if (control.pressed) return "#2A4055"
|
||||
if (control.hovered) return "#3A5570"
|
||||
return "#2A4055"
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 1
|
||||
height: parent.height
|
||||
anchors.right: parent.right
|
||||
color: {
|
||||
if (!control.enabled) return "#252525"
|
||||
if (control.pressed) return "#2A4055"
|
||||
if (control.hovered) return "#3A5570"
|
||||
return "#2A4055"
|
||||
}
|
||||
}
|
||||
|
||||
// Эффект свечения при наведении
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#3A5570"
|
||||
opacity: control.hovered ? 0.1 : 0
|
||||
radius: parent.radius
|
||||
|
||||
SequentialAnimation on opacity {
|
||||
running: control.hovered
|
||||
loops: Animation.Infinite
|
||||
NumberAnimation { to: 0.2; duration: 1000 }
|
||||
NumberAnimation { to: 0.1; duration: 1000 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
text: control.text
|
||||
font {
|
||||
family: "Arial"
|
||||
pixelSize: Theme.Theme.normalSize
|
||||
bold: true
|
||||
capitalization: Font.AllUppercase
|
||||
}
|
||||
color: {
|
||||
if (!control.enabled) return "#4A4A4A"
|
||||
if (control.pressed) return "#88BBDD"
|
||||
if (control.hovered) return "#BDE0FF"
|
||||
return "#77A7D1"
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 150 }
|
||||
}
|
||||
}
|
||||
|
||||
// Улучшенная анимация нажатия
|
||||
states: [
|
||||
State {
|
||||
name: "pressed"
|
||||
when: control.pressed
|
||||
PropertyChanges {
|
||||
target: control
|
||||
scale: 0.97
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
NumberAnimation {
|
||||
properties: "scale"
|
||||
duration: 50
|
||||
easing.type: Easing.OutQuad
|
||||
}
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
text: parent.tooltip || ""
|
||||
visible: parent.hovered && text
|
||||
delay: 500
|
||||
}
|
||||
|
||||
property string tooltip: ""
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
import "../Theme" as Theme
|
||||
|
||||
Rectangle {
|
||||
color: Theme.Theme.frameColor
|
||||
radius: Theme.Theme.radius
|
||||
border.color: Theme.Theme.borderColor
|
||||
border.width: 2
|
||||
|
||||
default property alias content: container.children
|
||||
|
||||
ColumnLayout {
|
||||
id: container
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.Theme.margin * 2
|
||||
spacing: Theme.Theme.spacing
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 415 KiB |
|
After Width: | Height: | Size: 269 KiB |
|
After Width: | Height: | Size: 299 KiB |
@@ -0,0 +1,7 @@
|
||||
module components
|
||||
WoWPanel 1.0 WoWPanel.qml
|
||||
InfoPanel 1.0 InfoPanel.qml
|
||||
WoWButton 1.0 WoWButton.qml
|
||||
Section 1.0 Section.qml
|
||||
Header 1.0 Header.qml
|
||||
ProgressSection 1.0 ProgressSection.qml
|
||||
|
After Width: | Height: | Size: 298 KiB |
|
After Width: | Height: | Size: 11 KiB |