Add first files of the launcher

This commit is contained in:
FullGreaM 2026-03-05 15:48:24 +03:00
parent 92f5494a72
commit afc7be6f12
21 changed files with 915 additions and 0 deletions

83
.gitignore vendored
View File

@ -88,3 +88,86 @@ compile_commands.json
*.out
*.app
# From Qt
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
*.qbs.user*
CMakeLists.txt.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe
# Directories with generated files
.moc/
.obj/
.pch/
.rcc/
.uic/
/build*/

82
CMakeLists.txt Normal file
View File

@ -0,0 +1,82 @@
cmake_minimum_required(VERSION 3.16)
project(ConanCraft-Launcher VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
set(PROJECT_SOURCES
main.cpp
welcome.cpp
welcome.h
welcome.ui
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(ConanCraft-Launcher
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET ConanCraft-Launcher APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(ConanCraft-Launcher SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(ConanCraft-Launcher
${PROJECT_SOURCES}
loading.h loading.cpp loading.ui
source.qrc
loading-worker.h loading-worker.cpp
locales.h
locales.cpp
install-java.h
install-java.cpp
network-downloader.h
network-downloader.cpp
)
endif()
endif()
target_link_libraries(ConanCraft-Launcher PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Network
)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.ConanCraft-Launcher)
endif()
set_target_properties(ConanCraft-Launcher PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
include(GNUInstallDirs)
install(TARGETS ConanCraft-Launcher
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(ConanCraft-Launcher)
endif()

172
install-java.cpp Normal file
View File

@ -0,0 +1,172 @@
#include "install-java.h"
#include "network-downloader.h"
QString checkDirHash(QString path) {
QCryptographicHash hash(QCryptographicHash::Sha256);
QDir dir(path);
if (!dir.exists()) {
qDebug() << "Directory not exists:" << path;
return QString();
}
QFileInfoList fileList = dir.entryInfoList(
QDir::Files | QDir::NoSymLinks | QDir::AllDirs | QDir::NoDotAndDotDot,
QDir::Name | QDir::DirsFirst
);
for (const QFileInfo &info : fileList) {
if (info.isDir()) {
QString subHash = checkDirHash(info.absoluteFilePath());
hash.addData(subHash.toUtf8());
} else if (info.isFile()) {
QFile file(info.absoluteFilePath());
if (file.open(QIODevice::ReadOnly)) {
while (!file.atEnd()) {
QByteArray chunk = file.read(8192);
hash.addData(chunk);
}
file.close();
}
}
}
return QString(hash.result().toHex());
}
bool checkJava () {
QString JAVA_HASH = "null";
#if defined(Q_OS_WIN)
#if defined(Q_PROCESSOR_X86_64)
//JAVA_HASH = "null";
#elif defined(Q_PROCESSOR_ARM_64)
//JAVA_HASH = "null";
#endif
#elif defined(Q_OS_LINUX)
#if defined(Q_PROCESSOR_X86_64)
//JAVA_HASH = "6268e94a0d6f37390fd33325725eb2991f9b26eb739baf2506064bfe3ea33075";
#elif defined(Q_PROCESSOR_ARM_64)
#else
//JAVA_HASH = "null";
#endif
#endif
QString hash = checkDirHash("./java");
if (JAVA_HASH == "null" || hash != JAVA_HASH)
qDebug() << "Java hash:" << hash;
return hash == JAVA_HASH;
}
void installJava(std::function<void(QString)> logCallback = nullptr) {
// Detect OS and architecture
QString javaUrl;
QString archiveType = "zip";
#if defined(Q_OS_WIN)
#if defined(Q_PROCESSOR_X86_64)
javaUrl = "https://example.com/java-windows-x64.zip";
#elif defined(Q_PROCESSOR_X86)
javaUrl = "https://example.com/java-windows-x86.zip";
#else
if (logCallback) logCallback("Unsupported Windows architecture");
return;
#endif
#elif defined(Q_OS_LINUX)
#if defined(Q_PROCESSOR_X86_64)
javaUrl = "";
archiveType = "tar.gz";
#elif defined(Q_PROCESSOR_ARM_64)
javaUrl = "";
archiveType = "tar.gz";
#else
if (logCallback) logCallback("Unsupported Linux architecture");
return;
#endif
#elif defined(Q_OS_MACOS)
#if defined(Q_PROCESSOR_X86_64)
javaUrl = "https://example.com/java-macos-x64.tar.gz";
#elif defined(Q_PROCESSOR_ARM_64)
javaUrl = "https://example.com/java-macos-arm64.tar.gz";
#else
if (logCallback) logCallback("Unsupported macOS architecture");
return;
#endif
#else
if (logCallback) logCallback("Unsupported OS");
return;
#endif
if (logCallback) logCallback("Creating directories...");
QDir tempDir("./temp");
if (!tempDir.exists()) tempDir.mkpath(".");
QDir javaDir("./java");
if (!javaDir.exists()) javaDir.mkpath(".");
QString archivePath = tempDir.filePath(QFileInfo(QUrl(javaUrl).path()).fileName());
// Download archive
downloadFromUrl(javaUrl, archivePath, logCallback);
// Extract archive
if (logCallback) logCallback("Extracting Java archive...");
#if defined(Q_OS_WIN)
// Unzip and move contents up one level
QProcess proc;
QString tempExtractDir = "./temp/java_extract";
QDir().mkpath(tempExtractDir);
proc.start("powershell", {"-Command", QString("Expand-Archive -Force -Path '%1' -DestinationPath '%2'").arg(archivePath).arg(tempExtractDir)});
proc.waitForFinished(-1);
// Move contents from subfolder to ./java
QDir extracted(tempExtractDir);
QStringList entries = extracted.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
for (const QString &e : entries) {
QString srcPath = extracted.filePath(e);
QString dstPath = javaDir.filePath(e);
if (!QFile::rename(srcPath, dstPath)) {
QDir srcDir(srcPath);
srcDir.rename(srcPath, dstPath);
}
}
QDir(tempExtractDir).removeRecursively();
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// Extract to temp folder first
QString tempExtractDir = "./temp/java_extract";
QDir().mkpath(tempExtractDir);
QProcess proc;
if (archiveType == "tar.gz") {
proc.start("tar", {"-xzf", archivePath, "-C", tempExtractDir});
} else if (archiveType == "zip") {
proc.start("unzip", {archivePath, "-d", tempExtractDir});
} else {
if (logCallback) logCallback("Unknown archive format");
return;
}
proc.waitForFinished(-1);
// Move contents of the inner folder to ./java
QDir extracted(tempExtractDir);
QStringList folders = extracted.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
QString rootFolder = folders.isEmpty() ? "" : folders.first();
QDir rootDir(tempExtractDir + "/" + rootFolder);
QStringList items = rootDir.entryList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
for (const QString &item : items) {
QString src = rootDir.filePath(item);
QString dst = javaDir.filePath(item);
QFile::rename(src, dst);
}
QDir(tempExtractDir).removeRecursively();
#endif
if (logCallback) logCallback("Extraction complete.");
// Check hash
QString hash = checkDirHash("./java");
if (logCallback) logCallback("Java installation hash: " + hash);
}

20
install-java.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef INSTALL_JAVA_H
#define INSTALL_JAVA_H
#include <QString>
#include <functional>
#include <QStandardPaths>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>
#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QDebug>
void installJava(std::function<void(QString)> logCallback);
bool checkJava ();
QString checkDirHash(QString path);
#endif // INSTALL_JAVA_H

58
loading-worker.cpp Normal file
View File

@ -0,0 +1,58 @@
#include "loading-worker.h"
#include "locales.h"
#include "install-java.h"
#include <QLocale>
#include <QDir>
#include <QFile>
#include <QCryptographicHash>
#include <QDebug>
LoadingWorker::LoadingWorker(Loading *loading, QObject *parent) {
this->loading = loading;
}
LoadingWorker::~LoadingWorker () {
//delete this->loading;
}
void LoadingWorker::process() {
std::string locale = (QLocale::system()).name().toStdString();
if (locales.find(locale) == locales.end()) {
locale = "en_US";
}
LocaleMap* localeMap = locales[locale];
// Checking updates
this->loading->changeStep(localeMap->at("loading.checkUpdates"));
// TODO: Add it later
// Checking: Is java installed
this->loading->changeStep(localeMap->at("loading.isJavaInst"));
if (!checkJava()) {
// Let's we install the java
installJava([this, localeMap](QString msg){
qDebug() << msg;
this->loading->changeStep(localeMap->at("loading.installJava") + ": " + msg.toStdString());
});
}
// Start I2P
this->loading->changeStep(localeMap->at("loading.i2p"));
// TODO: Add it later
// Getting endpoint API
this->loading->changeStep(localeMap->at("loading.gettingEndpoint"));
std::string endpointAPI = "TODO: Add it later";
// Handle data
this->loading->changeStep(localeMap->at("loading.logging"));
// TODO: Add it later
// Setup current locale for welcome form
this->loading->changeStep(localeMap->at("loading.setupLocale"));
this->loading->setupWelcomeLocale(locale);
this->loading->changeStep(localeMap->at("loading.end"));
emit finished();
this->loading->finishWorker();
}

21
loading-worker.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef LOADING_WORKER_H
#define LOADING_WORKER_H
#include "loading.h"
#include <qobject.h>
class LoadingWorker : public QObject
{
Q_OBJECT
public:
explicit LoadingWorker (Loading *loading, QObject *parent = nullptr);
~LoadingWorker();
private:
Loading *loading;
public slots:
void process();
signals:
void finished();
void progress(int value);
};
#endif // LOADING_WORKER_H

54
loading.cpp Normal file
View File

@ -0,0 +1,54 @@
#include "loading.h"
#include "loading-worker.h"
#include "locales.h"
#include "ui_loading.h"
#include <QThread>
Loading::Loading(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Loading)
{
ui->setupUi(this);
this->setWindowFlags(Qt::FramelessWindowHint);
this->startLoading();
}
Loading::~Loading()
{
delete ui;
}
void Loading::changeStep(std::string text) {
this->ui->condition->setText(QString::fromStdString(text));
}
void Loading::setupWelcomeLocale(std::string locale) {
this->welcome.setupWelcomeLocale(locale);
}
void Loading::finishWorker() {
this->hide();
this->welcome.show();
}
void Loading::startLoading()
{
QThread* thread = new QThread;
LoadingWorker* worker = new LoadingWorker(this);
worker->moveToThread(thread);
connect(thread, &QThread::started,
worker, &LoadingWorker::process);
connect(worker, &LoadingWorker::finished,
thread, &QThread::quit);
connect(worker, &LoadingWorker::finished,
worker, &QObject::deleteLater);
connect(thread, &QThread::finished,
thread, &QObject::deleteLater);
thread->start();
}

28
loading.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef LOADING_H
#define LOADING_H
#include "welcome.h"
#include <QWidget>
namespace Ui {
class Loading;
}
class Loading : public QWidget
{
Q_OBJECT
public:
explicit Loading(QWidget *parent = nullptr);
~Loading();
void changeStep(std::string text);
void setupWelcomeLocale(std::string locale);
void finishWorker();
private:
Ui::Loading *ui;
void startLoading();
Welcome welcome;
};
#endif // LOADING_H

63
loading.ui Normal file
View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Loading</class>
<widget class="QWidget" name="Loading">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>300</width>
<height>450</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>450</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>450</height>
</size>
</property>
<property name="windowTitle">
<string>ConanCraft</string>
</property>
<property name="windowIcon">
<iconset resource="source.qrc">
<normaloff>:/icons/sources/icons/96.png</normaloff>:/icons/sources/icons/96.png</iconset>
</property>
<widget class="QLabel" name="condition">
<property name="geometry">
<rect>
<x>10</x>
<y>410</y>
<width>281</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>Getting system language information...</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>301</width>
<height>411</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:16pt; font-weight:700;&quot;&gt;Conan Craft&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</widget>
<resources>
<include location="source.qrc"/>
</resources>
<connections/>
</ui>

56
locales.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "locales.h"
std::map<const std::string, LocaleMap*> locales;
// Russian
LocaleMap ru_RU = {
// Locale Info
{"locale.name", "Русский"},
{"locale.fullname", "Русский (Russian)"},
// Loading
{"loading.checkUpdates", "Проверяем обновления"},
{"loading.i2p", "Запускаем i2p"},
{"loading.isJavaInst", "Проверка установки Java"},
{"loading.installJava", "Установка Java"},
{"loading.gettingEndpoint", "Получаем Endpoint API"},
{"loading.logging", "Обрабатываем данные лаунчера"},
{"loading.setupLocale", "Устанавливаем язык лаунчера"},
{"loading.end", "Закончили. Приятной игры"},
// Welcome
{"welcome.main", "Главная"},
{"welcome.settings", "Настройки"},
{"welcome.authorise", "Авторизация"},
{"welcome.showPassword", "Показать пароль"},
{"welcome.logIn", "Войти"},
{"welcome.register", "Зарегистрироваться"},
};
// English
LocaleMap en_US = {
// Locale Info
{"locale.name", "English"},
{"locale.fullname", "English (English)"},
// Loading
{"loading.checkUpdates", "Checking updates"},
{"loading.i2p", "Starting i2p"},
{"loading.isJavaInst", "Checking Is Java installed"},
{"loading.installJava", "Java installation"},
{"loading.gettingEndpoint", "Getting Endpoint API"},
{"loading.logging", "Handle the launcher data"},
{"loading.setupLocale", "Setup the launcher's language"},
{"loading.end", "Finished. Let's start"},
// Welcome
{"welcome.main", "Home"},
{"welcome.settings", "Settings"},
{"welcome.authorise", "Authorization"},
{"welcome.showPassword", "Show the password"},
{"welcome.logIn", "LogIn"},
{"welcome.register", "Registration"},
};
void initLocales () {
locales["ru_RU"] = &ru_RU;
locales["ru_UA"] = &ru_RU;
locales["en_US"] = &en_US;
//locales["en_UK"] = &en_US;
}

11
locales.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef LOCALES_H
#define LOCALES_H
#include <map>
#include <string>
using LocaleMap = std::map<const std::string, const std::string>;
extern std::map<const std::string, LocaleMap*> locales;
extern void initLocales ();
#endif // LOCALES_H

15
main.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "loading.h"
#include "locales.h"
//#include "welcome.h"
#include <QApplication>
int main(int argc, char *argv[])
{
initLocales();
QApplication a(argc, argv);
Loading w;
//w.setWindowFlags(Qt::FramelessWindowHint);
w.show();
return a.exec();
}

56
network-downloader.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "network-downloader.h"
void downloadFromUrl(const QString &url, const QString &installPath, std::function<void(QString)> logCallback = nullptr) {
if (QFile::exists(installPath)) {
if (logCallback) logCallback("File already exists: " + installPath);
return;
}
if (logCallback) logCallback("Downloading from url...");
QNetworkAccessManager manager;
QNetworkRequest request((QUrl(url)));
QEventLoop loop;
QNetworkReply *reply = manager.get(request);
QObject::connect(reply, &QNetworkReply::downloadProgress, [&](qint64 received, qint64 total){
if (logCallback) logCallback(QString("Download progress: %1/%2 bytes").arg(received).arg(total));
});
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (!statusCode.isValid()) {
if (logCallback) logCallback("Download failed: Invalid Status code");
reply->deleteLater();
return;
}
if (reply->error() != QNetworkReply::NoError) {
if (logCallback) logCallback("Download failed: " + reply->errorString());
reply->deleteLater();
return;
}
if (statusCode.toInt() == 302 || statusCode.toInt() == 301) {
QVariant redir = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (!redir.isValid()) {
if (logCallback) logCallback("Download failed: Invalid redirect");
reply->deleteLater();
return;
}
QUrl newUrl = redir.toUrl();
downloadFromUrl(newUrl.toString(), installPath, logCallback);
return;
}
QFile file(installPath);
if (file.open(QIODevice::WriteOnly)) {
file.write(reply->readAll());
file.close();
if (logCallback) logCallback("Download complete: " + installPath);
} else {
if (logCallback) logCallback("Cannot write file: " + installPath);
}
reply->deleteLater();
}

18
network-downloader.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef NETWORK_DOWNLOADER_H
#define NETWORK_DOWNLOADER_H
#include <QString>
#include <functional>
#include <QStandardPaths>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>
#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QDebug>
void downloadFromUrl(const QString &url, const QString &installPath, std::function<void(QString)> logCallback);
#endif // NETWORK_DOWNLOADER_H

7
source.qrc Normal file
View File

@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/icons">
<file>sources/icons/96.png</file>
<file>sources/icons/32.png</file>
<file>sources/icons/16.png</file>
</qresource>
</RCC>

BIN
sources/icons/16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

BIN
sources/icons/32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

BIN
sources/icons/96.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

33
welcome.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "welcome.h"
#include "./ui_welcome.h"
#include "locales.h"
Welcome::Welcome(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::Welcome)
{
ui->setupUi(this);
}
Welcome::~Welcome()
{
delete ui;
}
void Welcome::setupWelcomeLocale(std::string locale) {
if (locales.find(locale) == locales.end()) {
locale = "en_US";
}
LocaleMap* localeMap = locales[locale];
this->ui->authForm->setTitle(QString::fromStdString(localeMap->at("welcome.authorise")));
this->ui->tabWidget->setTabText(0, QString::fromStdString(localeMap->at("welcome.main")));
this->ui->tabWidget->setTabText(1, QString::fromStdString(localeMap->at("welcome.settings")));
this->ui->showPassword->setText(QString::fromStdString(localeMap->at("welcome.showPassword")));
this->ui->loginBtn->setText(QString::fromStdString(localeMap->at("welcome.logIn")));
this->ui->registerBtn->setText(QString::fromStdString(localeMap->at("welcome.register")));
this->ui->tabWidget->setCurrentIndex(0);
}

24
welcome.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef WELCOME_H
#define WELCOME_H
#include <QMainWindow>
QT_BEGIN_NAMESPACE
namespace Ui {
class Welcome;
}
QT_END_NAMESPACE
class Welcome : public QMainWindow
{
Q_OBJECT
public:
Welcome(QWidget *parent = nullptr);
~Welcome();
void setupWelcomeLocale(std::string locale);
private:
Ui::Welcome *ui;
};
#endif // WELCOME_H

114
welcome.ui Normal file
View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Welcome</class>
<widget class="QMainWindow" name="Welcome">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>Welcome</string>
</property>
<property name="windowIcon">
<iconset resource="source.qrc">
<normaloff>:/icons/sources/icons/96.png</normaloff>:/icons/sources/icons/96.png</iconset>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QTabWidget" name="tabWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>801</width>
<height>551</height>
</rect>
</property>
<widget class="QWidget" name="main">
<attribute name="title">
<string>welcome.main</string>
</attribute>
<widget class="QGroupBox" name="authForm">
<property name="geometry">
<rect>
<x>240</x>
<y>130</y>
<width>331</width>
<height>291</height>
</rect>
</property>
<property name="title">
<string>welcome.authorise</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>30</y>
<width>311</width>
<height>251</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLineEdit" name="login"/>
</item>
<item>
<widget class="QLineEdit" name="password">
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="showPassword">
<property name="text">
<string>welcome.showPassword</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="loginBtn">
<property name="text">
<string>welcome.logIn</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="registerBtn">
<property name="text">
<string>welcome.register</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</widget>
<widget class="QWidget" name="settings">
<attribute name="title">
<string>welcome.settings</string>
</attribute>
</widget>
</widget>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>23</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources>
<include location="source.qrc"/>
</resources>
<connections/>
</ui>