launcher project v 0.1

This commit is contained in:
2025-01-08 15:18:16 +07:00
commit 86acc9d68f
32 changed files with 2757 additions and 0 deletions
+41
View File
@@ -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" }
}
}
+2
View File
@@ -0,0 +1,2 @@
module Theme
singleton Theme 1.0 Theme.qml
+61
View File
@@ -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()
}
}
}
+37
View File
@@ -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()
}
}
+122
View File
@@ -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 }
}
}
}
}
+127
View File
@@ -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 }
}
}
}
}
}
}
}
+67
View File
@@ -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()
}
}
+151
View File
@@ -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 }
}
}
}
}
}
}
+73
View File
@@ -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
}
}
+206
View File
@@ -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()
}
}
}
+153
View File
@@ -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
}
}
}
+131
View File
@@ -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: ""
}
+19
View File
@@ -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
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

+7
View File
@@ -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
Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB