launcher project v 0.1
64
README.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|

|
||||||
|
|
||||||
|
# WoW Launcher
|
||||||
|
|
||||||
|
Современный лаунчер для World of Warcraft 3.3.5a, написанный на Python и QML.
|
||||||
|
|
||||||
|
## Возможности
|
||||||
|
|
||||||
|
- Современный интерфейс в стиле World of Warcraft
|
||||||
|
- Загрузка и проверка файлов клиента
|
||||||
|
- Поддержка Windows и Linux (через Wine/Lutris/Proton/PortProton)
|
||||||
|
- Автоматическое обновление
|
||||||
|
- Слайд-шоу с новостями
|
||||||
|
- Мониторинг состояния серверов
|
||||||
|
- Система уведомлений
|
||||||
|
- Настраиваемые параметры
|
||||||
|
- Загрузка файлов сегментами
|
||||||
|
- Сворачивание в трей
|
||||||
|
- Возможность запускать игру через Wine, Lutris, Proton, PortProton
|
||||||
|
|
||||||
|
## Требования
|
||||||
|
|
||||||
|
- Python 3.8+
|
||||||
|
- PyQt5
|
||||||
|
- Requests
|
||||||
|
|
||||||
|
## Установка
|
||||||
|
|
||||||
|
1. Клонируйте репозиторий:
|
||||||
|
```
|
||||||
|
git clone https://github.com/ваш-username/wow-launcher.git
|
||||||
|
cd wow-launcher
|
||||||
|
```
|
||||||
|
2. Установите зависимости:
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
3. Запустите лаунчер:
|
||||||
|
```
|
||||||
|
python backup3.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Настройка
|
||||||
|
|
||||||
|
1. При первом запуске выберите папку для установки игры
|
||||||
|
2. Настройте параметры в меню настроек
|
||||||
|
3. Для Linux пользователей: выберите предпочитаемый эмулятор Windows
|
||||||
|
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
|
||||||
|
Проект использует:
|
||||||
|
- Python для бэкенда
|
||||||
|
- QML для интерфейса
|
||||||
|
- Qt Quick Controls 2 для компонентов
|
||||||
|
- Material Design для стилизации
|
||||||
|
|
||||||
|
## Лицензия
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
## Авторы
|
||||||
|
|
||||||
|
JumpyLion87
|
||||||
1
README.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
||||||
180
clien.json
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
{
|
||||||
|
"files": {
|
||||||
|
"Data/ruRU/Interface/Cinematics/Logo_1024.avi": {
|
||||||
|
"size": 6518380,
|
||||||
|
"hash": "8ca8b2ef476be8d69a678ac34f75498f3c5cc02b5f618d467b9c78e6edae815e"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/Logo_800.avi": {
|
||||||
|
"size": 4967302,
|
||||||
|
"hash": "9f3c345a99073f7f06adb1b9bf37c2ffc7e37773d07a4cc72a0cd163db07b765"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_FotLK_1024.avi": {
|
||||||
|
"size": 66007136,
|
||||||
|
"hash": "1afe738ad4ad1203f357eb15b1a15926e54ffbd3ad9b7b5ca9fac73dad058292"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_FotLK_800.avi": {
|
||||||
|
"size": 57244820,
|
||||||
|
"hash": "52cb90f10863c909d613f84c99f0d2da5139793c2998f2b2905c49395b489821"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Intro_1024.avi": {
|
||||||
|
"size": 48319392,
|
||||||
|
"hash": "697fad32b005def2bb6908fb3a77f43be134d661fd03ffa29d2c6739ec5cd7b7"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Intro_800.avi": {
|
||||||
|
"size": 41883066,
|
||||||
|
"hash": "0d62fcd314daf93add75510f644e04a4192effcd323db0c791df01609bf3e77b"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Intro_BC_1024.avi": {
|
||||||
|
"size": 47575820,
|
||||||
|
"hash": "0277fba0da2a8d0ea61121996390ab3d718729c7802c31b9d0f9447233b0dda0"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Intro_BC_800.avi": {
|
||||||
|
"size": 41261394,
|
||||||
|
"hash": "ed5511a783734bf214cc084fde253b530b868336f774b61ec090c8891627b880"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Intro_LK_1024.avi": {
|
||||||
|
"size": 56177056,
|
||||||
|
"hash": "be3dc55b635da30ffddc84898a241b1551f1e32b1a6a0c97768afdc4a078a51d"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Intro_LK_800.avi": {
|
||||||
|
"size": 48820376,
|
||||||
|
"hash": "fcd5e4576be7e7d2e97a436f13e37734e72adfa3457d1fbbcc9e6173aea7f7e5"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Wrathgate_1024.avi": {
|
||||||
|
"size": 75685834,
|
||||||
|
"hash": "4b40199be93942548f77fb3cfeaf001738bccee0211940c8ed4ff6f0fb068897"
|
||||||
|
},
|
||||||
|
"Data/ruRU/Interface/Cinematics/WOW_Wrathgate_800.avi": {
|
||||||
|
"size": 65683896,
|
||||||
|
"hash": "acececbffff5e1b2e23f16259fe662df3a02823d4890d3ac3a4d96b3a401c729"
|
||||||
|
},
|
||||||
|
"Data/ruRU/backup-ruRU.MPQ": {
|
||||||
|
"size": 158243976,
|
||||||
|
"hash": "b2acbf9da5adb20926b42194fcc026bdfcf0a5074d5e058fa24c6da57593a8f0"
|
||||||
|
},
|
||||||
|
"Data/ruRU/base-ruRU.MPQ": {
|
||||||
|
"size": 22975058,
|
||||||
|
"hash": "f361e6706aab57cce50e8a7ba8a08f312ba3c328a5219cd6f72db1894d4c1148"
|
||||||
|
},
|
||||||
|
"Data/ruRU/eula.html": {
|
||||||
|
"size": 36274,
|
||||||
|
"hash": "e784b78eb85d4231dec97870e6cfd585d0b2563b1af350ba34ab75545b478b3f"
|
||||||
|
},
|
||||||
|
"Data/ruRU/expansion-locale-ruRU.MPQ": {
|
||||||
|
"size": 16919739,
|
||||||
|
"hash": "4a565f808c1c4b413dcc1bbfa9980ca700500f220506704ecbb4734883d6ee8d"
|
||||||
|
},
|
||||||
|
"Data/ruRU/expansion-speech-ruRU.MPQ": {
|
||||||
|
"size": 269445963,
|
||||||
|
"hash": "69a5057a064fc22efa008acc03faf78fa1abc8b7f2320367e53569b7b2dea3c9"
|
||||||
|
},
|
||||||
|
"Data/ruRU/lichking-locale-ruRU.MPQ": {
|
||||||
|
"size": 12354378,
|
||||||
|
"hash": "89b1eba015927e321683a3867be1b8a76e593c438527eb42f4934d03484f1b04"
|
||||||
|
},
|
||||||
|
"Data/ruRU/lichking-speech-ruRU.MPQ": {
|
||||||
|
"size": 6772364,
|
||||||
|
"hash": "99afb35d5f092a19b760815bb0a21b8eb4b74d7e9361498dc13d4a65cc951b59"
|
||||||
|
},
|
||||||
|
"Data/ruRU/locale-ruRU.MPQ": {
|
||||||
|
"size": 190129109,
|
||||||
|
"hash": "dd587f0a673924d3320c30812d90476fc72a240e30056d0656c8c6c5e32dde1f"
|
||||||
|
},
|
||||||
|
"Data/ruRU/patch-ruRU-2.MPQ": {
|
||||||
|
"size": 270846455,
|
||||||
|
"hash": "25365c0319d186e7c0a09b8f0b63c1de765f4387bd3d532709fe163170c439e3"
|
||||||
|
},
|
||||||
|
"Data/ruRU/patch-ruRU-3.MPQ": {
|
||||||
|
"size": 109883630,
|
||||||
|
"hash": "792f87603eb77000a23c56f85c6250796e4f4032d0cad671a70c0fa300a2a0c3"
|
||||||
|
},
|
||||||
|
"Data/ruRU/patch-ruRU.MPQ": {
|
||||||
|
"size": 613796676,
|
||||||
|
"hash": "af99e71afc6eec6f6087a8a43948210a30c15283e71dc0c49ea0a6543bee69ff"
|
||||||
|
},
|
||||||
|
"Data/ruRU/patch-ruRU-8.MPQ": {
|
||||||
|
"size": 115949,
|
||||||
|
"hash": "3dcc6776a3a1995b833e216c1517522ed5787ec9389c4ebbe206b6d4f7df1ce2"
|
||||||
|
},
|
||||||
|
"Data/ruRU/speech-ruRU.MPQ": {
|
||||||
|
"size": 445791052,
|
||||||
|
"hash": "a76376c43b90633648269fe94e2b89454b00bc096f6acc11302492fd2102727e"
|
||||||
|
},
|
||||||
|
"Data/ruRU/termination.html": {
|
||||||
|
"size": 4882,
|
||||||
|
"hash": "2e2dd7c567c96bb0b3d2aa59ca2822cbc4dedfa110eaddbbbe9294076e987c04"
|
||||||
|
},
|
||||||
|
"Data/ruRU/tos.html": {
|
||||||
|
"size": 96614,
|
||||||
|
"hash": "a7bdc9a6faa1098ed3a844a1a79ed85d3dd0e2a34dcd10cd73e2da5305dc7f45"
|
||||||
|
},
|
||||||
|
"Data/common-2.MPQ": {
|
||||||
|
"size": 1756781838,
|
||||||
|
"hash": "dc0f8fe0607754de306eba74e9972ab6d91eb467076f34b26407ace3446f3f57"
|
||||||
|
},
|
||||||
|
"Data/common.MPQ": {
|
||||||
|
"size": 2856083241,
|
||||||
|
"hash": "63bac342322bbc1f88b3344b1a73e67a84ed527fbbe9c728e2528e8660346399"
|
||||||
|
},
|
||||||
|
"Data/expansion.MPQ": {
|
||||||
|
"size": 1899508519,
|
||||||
|
"hash": "edf04e46a28fee2727d29a61c2f866e4b3dce41468ebd41f34c115fae25a5abf"
|
||||||
|
},
|
||||||
|
"Data/lichking.MPQ": {
|
||||||
|
"size": 2553955175,
|
||||||
|
"hash": "7be69de4ae1328026cf1de021fde70666367c255573e283302089658cdd56cfb"
|
||||||
|
},
|
||||||
|
"Data/patch-2.MPQ": {
|
||||||
|
"size": 1401729059,
|
||||||
|
"hash": "c8b78bb75bcf5773e9ae99e11bdfcabc2a37aed3da947367d8af808d4d3c23c6"
|
||||||
|
},
|
||||||
|
"Data/patch-3.MPQ": {
|
||||||
|
"size": 605089137,
|
||||||
|
"hash": "56dbbfc8f9ce7182ca88538d75284e738020138a7ca4217d72d8168865510dd3"
|
||||||
|
},
|
||||||
|
"Data/patch.MPQ": {
|
||||||
|
"size": 4004713057,
|
||||||
|
"hash": "92b4a94a6c7a23c0b9fd88c47823e41792879a7ee1f70c2349a255153ca25d54"
|
||||||
|
},
|
||||||
|
"Battle.net.dll": {
|
||||||
|
"size": 15588224,
|
||||||
|
"hash": "88116951b04723cbb1073a1a0ff72dfdc40367f6649dffd08c602caa3ef1141f"
|
||||||
|
},
|
||||||
|
"DivxDecoder.dll": {
|
||||||
|
"size": 413696,
|
||||||
|
"hash": "ed34d37b575c91a56704218eb9f6abbefda8b7de0e2ed44c96191abd0f9915a5"
|
||||||
|
},
|
||||||
|
"Microsoft.VC80.CRT.manifest": {
|
||||||
|
"size": 1870,
|
||||||
|
"hash": "a50941352cb9d8f7ba6fbf7db5c8af95fb5ab76fc5d60cfd0984e558678908cc"
|
||||||
|
},
|
||||||
|
"Scan.dll": {
|
||||||
|
"size": 51972,
|
||||||
|
"hash": "22bafdab4e842aa675f089672a366a164c735bd63026eea8704778085200bf3f"
|
||||||
|
},
|
||||||
|
"Wow.exe": {
|
||||||
|
"size": 7704216,
|
||||||
|
"hash": "28287cd94e6ab2d6e68865f49314b456d2750dcb5d0b8ecca12eeddf72b7bf80"
|
||||||
|
},
|
||||||
|
"WowError.exe": {
|
||||||
|
"size": 350360,
|
||||||
|
"hash": "964eb9ed647c4614f3a3ecf977955750223ecbc8ff0a3bb4078e4dff779244f8"
|
||||||
|
},
|
||||||
|
"dbghelp.dll": {
|
||||||
|
"size": 1039728,
|
||||||
|
"hash": "3f9ad6986eb1f3a09eb0b5cad888ba656f9148b3339399076f2541b3bc59213c"
|
||||||
|
},
|
||||||
|
"ijl15.dll": {
|
||||||
|
"size": 372736,
|
||||||
|
"hash": "334aa12f7dee453d1c6cb1b661a3bb3494d3e4cc9c2ff3f9002064c78404e43a"
|
||||||
|
},
|
||||||
|
"msvcr80.dll": {
|
||||||
|
"size": 632656,
|
||||||
|
"hash": "9382aaed2db19cd75a70e38964f06c63f19f63c9dfb5a33b0c2d445bb41b6e46"
|
||||||
|
},
|
||||||
|
"unicows.dll": {
|
||||||
|
"size": 245408,
|
||||||
|
"hash": "22f23cc65698741184ec34f46e6f69717644e0b5aabf5d5bd015101f2d72e56e"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
313
main.qml
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Window 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Controls.Material 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import "./qml/Theme" as Theme
|
||||||
|
import "./qml/components" as Components
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: mainWindow
|
||||||
|
visible: true
|
||||||
|
width: 1010
|
||||||
|
height: 650
|
||||||
|
minimumWidth: 1010
|
||||||
|
minimumHeight: 650
|
||||||
|
maximumWidth: 1010
|
||||||
|
maximumHeight: 650
|
||||||
|
title: "World of Warcraft 3.3.5a Launcher"
|
||||||
|
|
||||||
|
// Добавляем фоновое изображение
|
||||||
|
background: Rectangle {
|
||||||
|
color: Theme.Theme.backgroundColor
|
||||||
|
|
||||||
|
Image {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: "qml/images/background.jpg" // Путь к изображению
|
||||||
|
fillMode: Image.PreserveAspectCrop
|
||||||
|
opacity: 0.3
|
||||||
|
|
||||||
|
// Добавляем затемнение
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: "#000000"
|
||||||
|
opacity: 0.6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Material.theme: Material.Dark
|
||||||
|
Material.accent: Theme.Theme.accentColor
|
||||||
|
Material.background: Theme.Theme.backgroundColor
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
// Основной контент
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.margins: Theme.Theme.margin
|
||||||
|
spacing: Theme.Theme.spacing
|
||||||
|
|
||||||
|
// Левая панель
|
||||||
|
Components.Section {
|
||||||
|
Layout.preferredWidth: 300
|
||||||
|
Layout.minimumWidth: 300
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
// Заголовок
|
||||||
|
Components.Header {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 70
|
||||||
|
}
|
||||||
|
|
||||||
|
// Информационная панель
|
||||||
|
Components.InfoPanel {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 150
|
||||||
|
serverInfo: "x2 WotLK"
|
||||||
|
requirements: "• OS: Windows 7/8/10/11\n• CPU: 2.4 GHz\n• RAM: 2 GB\n• HDD: 15 GB"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Растягивающийся элемент
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Статус текст
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 50
|
||||||
|
text: launcher ? launcher.statusText : "Пожалуйста, выберите папку с игрой"
|
||||||
|
color: Theme.Theme.primaryText
|
||||||
|
font.pixelSize: Theme.Theme.normalSize
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 1
|
||||||
|
color: Qt.darker(Theme.Theme.borderColor, 1.2)
|
||||||
|
Layout.margins: Theme.Theme.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кнопки
|
||||||
|
Components.WoWButton {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: Theme.Theme.buttonHeight
|
||||||
|
text: "Выбрать папку с игрой"
|
||||||
|
visible: launcher ? !launcher.gamePath : true
|
||||||
|
onClicked: launcher && launcher.selectGamePath()
|
||||||
|
tooltip: "Выберите папку, где будет установлена игра"
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.WoWButton {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: Theme.Theme.buttonHeight
|
||||||
|
text: launcher && launcher.isDownloading ? "Остановить загрузку" : "Скачать клиент"
|
||||||
|
onClicked: launcher && launcher.startDownload()
|
||||||
|
tooltip: launcher && launcher.isDownloading ?
|
||||||
|
"Остановить текущую загрузку" :
|
||||||
|
"Загрузить клиент World of Warcraft"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем отступ после кнопок
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: Theme.Theme.spacing * 2 // Отступ снизу
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Правая панель
|
||||||
|
Components.Section {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumWidth: 400
|
||||||
|
|
||||||
|
// Слайд-шоу
|
||||||
|
Components.SlideShow {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumHeight: 300
|
||||||
|
imageUrls: [
|
||||||
|
"qml/images/slide1.jpg",
|
||||||
|
"qml/images/slide2.jpg",
|
||||||
|
"qml/images/slide3.jpg",
|
||||||
|
"qml/images/slide4.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
height: 1
|
||||||
|
color: Qt.darker(Theme.Theme.borderColor, 1.2)
|
||||||
|
Layout.margins: Theme.Theme.spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Нижняя часть с прогрессом и кнопкой
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.margins: Theme.Theme.spacing
|
||||||
|
spacing: Theme.Theme.spacing
|
||||||
|
|
||||||
|
// Прогресс-бар и имя файла
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.Theme.spacing / 2
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
text: launcher ? launcher.currentFileName : ""
|
||||||
|
color: Theme.Theme.primaryText
|
||||||
|
font.pixelSize: Theme.Theme.smallSize
|
||||||
|
visible: text !== ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.ProgressSection {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 40
|
||||||
|
Layout.leftMargin: 1
|
||||||
|
Layout.rightMargin: 1
|
||||||
|
Layout.topMargin: 1
|
||||||
|
Layout.bottomMargin: 1
|
||||||
|
value: launcher ? launcher.downloadProgress : 0
|
||||||
|
text: {
|
||||||
|
if (launcher) {
|
||||||
|
if (launcher.downloadSpeed && launcher.downloadSizeInfo) {
|
||||||
|
return launcher.downloadSpeed + " / " + launcher.downloadSizeInfo
|
||||||
|
}
|
||||||
|
return launcher.downloadSpeed
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кнопка запуска
|
||||||
|
Components.WoWButton {
|
||||||
|
Layout.alignment: Qt.AlignBottom
|
||||||
|
Layout.minimumWidth: 120
|
||||||
|
Layout.minimumHeight: Theme.Theme.buttonHeight
|
||||||
|
text: "ИГРАТЬ"
|
||||||
|
enabled: launcher ? launcher.canPlay : false
|
||||||
|
onClicked: launcher && launcher.launchGame()
|
||||||
|
tooltip: enabled ?
|
||||||
|
"Запустить World of Warcraft" :
|
||||||
|
"Сначала установите игру"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Статус бар
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumHeight: 30
|
||||||
|
height: 30
|
||||||
|
color: Qt.rgba(Theme.Theme.frameColor.r, Theme.Theme.frameColor.g, Theme.Theme.frameColor.b, 0.35)
|
||||||
|
border.color: Qt.rgba(Theme.Theme.borderColor.r, Theme.Theme.borderColor.g, Theme.Theme.borderColor.b, 0.4)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors {
|
||||||
|
fill: parent
|
||||||
|
leftMargin: Theme.Theme.margin
|
||||||
|
rightMargin: Theme.Theme.margin
|
||||||
|
topMargin: Theme.Theme.margin / 2
|
||||||
|
bottomMargin: Theme.Theme.margin / 2
|
||||||
|
}
|
||||||
|
spacing: Theme.Theme.spacing
|
||||||
|
|
||||||
|
// Статус сервера
|
||||||
|
Label {
|
||||||
|
Layout.minimumWidth: 100
|
||||||
|
text: launcher ? launcher.serverStatus : "⚫ Offline"
|
||||||
|
color: launcher && launcher.isServerOnline ? Theme.Theme.primaryText : Theme.Theme.disabledText
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
ToolTip.visible: serverMouseArea.containsMouse
|
||||||
|
ToolTip.text: launcher && launcher.isServerOnline ?
|
||||||
|
"Сервер доступен" :
|
||||||
|
"Сервер недоступен"
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: serverMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Растягивающийся элемент
|
||||||
|
Item { Layout.fillWidth: true }
|
||||||
|
|
||||||
|
// Версия
|
||||||
|
Label {
|
||||||
|
Layout.minimumWidth: 80
|
||||||
|
text: "Версия: " + (launcher ? launcher.version : "3.3.5")
|
||||||
|
color: Theme.Theme.secondaryText
|
||||||
|
}
|
||||||
|
|
||||||
|
// Кнопка настроек
|
||||||
|
ToolButton {
|
||||||
|
Layout.preferredWidth: 30
|
||||||
|
Layout.preferredHeight: 30
|
||||||
|
icon.source: "qml/components/qml/images/icons/settings.png"
|
||||||
|
icon.color: Theme.Theme.secondaryText
|
||||||
|
icon.width: 16
|
||||||
|
icon.height: 16
|
||||||
|
ToolTip.visible: hovered
|
||||||
|
ToolTip.text: "Настройки"
|
||||||
|
onClicked: settingsDialog.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.Notification {
|
||||||
|
id: notification
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: Theme.Theme.margin * 2
|
||||||
|
}
|
||||||
|
z: 999
|
||||||
|
}
|
||||||
|
|
||||||
|
// Функция для показа уведомлений
|
||||||
|
function showNotification(message, type) {
|
||||||
|
notification.type = type || "info"
|
||||||
|
notification.text = message
|
||||||
|
notification.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.SettingsDialog {
|
||||||
|
id: settingsDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.AboutDialog {
|
||||||
|
id: aboutDialog
|
||||||
|
}
|
||||||
|
|
||||||
|
Components.ContextMenu {
|
||||||
|
id: contextMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавить MouseArea для всего окна
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onClicked: {
|
||||||
|
if (mouse.button === Qt.RightButton)
|
||||||
|
contextMenu.popup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавляем обработку закрытия окна
|
||||||
|
onClosing: function(close) {
|
||||||
|
if (launcher.settings.closeToTray) {
|
||||||
|
close.accepted = false
|
||||||
|
launcher.minimizeToTray()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
qml/Theme/Theme.qml
Normal 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
qml/Theme/qmldir
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
module Theme
|
||||||
|
singleton Theme 1.0 Theme.qml
|
||||||
61
qml/components/AboutDialog.qml
Normal 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
qml/components/ContextMenu.qml
Normal 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
qml/components/Header.qml
Normal 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
qml/components/InfoPanel.qml
Normal 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
qml/components/Notification.qml
Normal 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
qml/components/ProgressSection.qml
Normal 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
qml/components/Section.qml
Normal 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
qml/components/SettingsDialog.qml
Normal 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
qml/components/SlideShow.qml
Normal 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
qml/components/WoWButton.qml
Normal 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
qml/components/WoWPanel.qml
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
qml/components/qml/images/icons/requirements.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
qml/components/qml/images/icons/server.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
qml/components/qml/images/icons/settings.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
qml/components/qml/images/logo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
qml/components/qml/images/requirements.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
qml/components/qml/images/slide1.jpg
Normal file
|
After Width: | Height: | Size: 320 KiB |
BIN
qml/components/qml/images/slide2.jpg
Normal file
|
After Width: | Height: | Size: 415 KiB |
BIN
qml/components/qml/images/slide3.jpg
Normal file
|
After Width: | Height: | Size: 269 KiB |
BIN
qml/components/qml/images/slide4.jpg
Normal file
|
After Width: | Height: | Size: 299 KiB |
7
qml/components/qmldir
Normal 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
|
||||||
BIN
qml/images/background.jpg
Normal file
|
After Width: | Height: | Size: 298 KiB |
BIN
qml/images/icons/tray.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
PyQt5>=5.15.0
|
||||||
|
requests>=2.25.1
|
||||||
BIN
screenshots/image.png
Normal file
|
After Width: | Height: | Size: 662 KiB |