diff --git a/client/amnezia_application.cpp b/client/amnezia_application.cpp index 588854d3..3fb76be2 100644 --- a/client/amnezia_application.cpp +++ b/client/amnezia_application.cpp @@ -92,7 +92,7 @@ void AmneziaApplication::init() m_engine = new QQmlApplicationEngine; m_uiLogic = new UiLogic(m_settings, m_configurator, m_serverController); - const QUrl url(QStringLiteral("qrc:/ui/qml/main.qml")); + const QUrl url(QStringLiteral("qrc:/ui/qml/main2.qml")); QObject::connect(m_engine, &QQmlApplicationEngine::objectCreated, this, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) diff --git a/client/images/amneziaBigLogo.png b/client/images/amneziaBigLogo.png new file mode 100644 index 00000000..35a45f3b Binary files /dev/null and b/client/images/amneziaBigLogo.png differ diff --git a/client/images/amneziaBigLogo.svg b/client/images/amneziaBigLogo.svg new file mode 100644 index 00000000..c50c7743 --- /dev/null +++ b/client/images/amneziaBigLogo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/images/controls/arrow-left.svg b/client/images/controls/arrow-left.svg new file mode 100644 index 00000000..98c9950b --- /dev/null +++ b/client/images/controls/arrow-left.svg @@ -0,0 +1,4 @@ + + + + diff --git a/client/resources.qrc b/client/resources.qrc index bda89bf2..f95ef585 100644 --- a/client/resources.qrc +++ b/client/resources.qrc @@ -176,5 +176,17 @@ ui/qml/Controls2/CardType.qml ui/qml/Controls2/CheckBoxType.qml images/controls/check.svg + ui/qml/Controls2/DropDownType.qml + ui/qml/Pages2/PageStart.qml + ui/qml/main2.qml + ui/qml/PageLoader.qml + images/amneziaBigLogo.png + images/amneziaBigLogo.svg + ui/qml/Controls2/BodyTextType.qml + ui/qml/Controls2/FlickableType.qml + ui/qml/Controls2/Header2TextType.qml + ui/qml/Pages2/PageCredentials.qml + ui/qml/Controls2/HeaderTextType.qml + images/controls/arrow-left.svg diff --git a/client/ui/pages.h b/client/ui/pages.h index a639d63b..c33289bb 100644 --- a/client/ui/pages.h +++ b/client/ui/pages.h @@ -24,7 +24,7 @@ enum class Page {Start = 0, NewServer, NewServerProtocols, Vpn, Wizard, WizardLow, WizardMedium, WizardHigh, WizardVpnMode, ServerConfiguringProgress, GeneralSettings, AppSettings, NetworkSettings, ServerSettings, ServerContainers, ServersList, ShareConnection, Sites, - ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About, ViewConfig, AdvancedServerSettings, Test}; + ProtocolSettings, ProtocolShare, QrDecoder, QrDecoderIos, About, ViewConfig, AdvancedServerSettings, Test, Credentials}; Q_ENUM_NS(Page) static void declareQmlPageEnum() { diff --git a/client/ui/qml/Controls2/BodyTextType.qml b/client/ui/qml/Controls2/BodyTextType.qml new file mode 100644 index 00000000..9d789385 --- /dev/null +++ b/client/ui/qml/Controls2/BodyTextType.qml @@ -0,0 +1,12 @@ +import QtQuick + +Text { + text: root.bodyText + wrapMode: Text.WordWrap + color: "#D7D8DB" + font.pixelSize: 16 + font.weight: 400 + font.family: "PT Root UI VF" + + height: 24 +} diff --git a/client/ui/qml/Controls2/DropDownType.qml b/client/ui/qml/Controls2/DropDownType.qml new file mode 100644 index 00000000..5560aee7 --- /dev/null +++ b/client/ui/qml/Controls2/DropDownType.qml @@ -0,0 +1,5 @@ +import QtQuick + +Item { + +} diff --git a/client/ui/qml/Controls2/FlickableType.qml b/client/ui/qml/Controls2/FlickableType.qml new file mode 100644 index 00000000..b7c1203f --- /dev/null +++ b/client/ui/qml/Controls2/FlickableType.qml @@ -0,0 +1,23 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import "../Config" + +Flickable { + id: fl + + clip: true + width: parent.width + + anchors.bottom: parent.bottom + anchors.left: root.left + anchors.right: root.right + anchors.rightMargin: 1 + + Keys.onUpPressed: scrollBar.decrease() + Keys.onDownPressed: scrollBar.increase() + + ScrollBar.vertical: ScrollBar { + id: scrollBar + policy: fl.height >= fl.contentHeight ? ScrollBar.AlwaysOff : ScrollBar.AlwaysOn + } +} diff --git a/client/ui/qml/Controls2/Header2TextType.qml b/client/ui/qml/Controls2/Header2TextType.qml new file mode 100644 index 00000000..4bbbc0d6 --- /dev/null +++ b/client/ui/qml/Controls2/Header2TextType.qml @@ -0,0 +1,10 @@ +import QtQuick + +Text { + color: "#D7D8DB" + font.pixelSize: 25 + font.weight: 700 + font.family: "PT Root UI VF" + + height: 30 +} diff --git a/client/ui/qml/Controls2/HeaderTextType.qml b/client/ui/qml/Controls2/HeaderTextType.qml new file mode 100644 index 00000000..60fa4e95 --- /dev/null +++ b/client/ui/qml/Controls2/HeaderTextType.qml @@ -0,0 +1,40 @@ +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: root + + property string buttonImage + property string headerText + property var wrapMode + + ImageButtonType { + id: button + + Layout.leftMargin: -6 + + hoverEnabled: false + image: root.buttonImage + onClicked: { + if (onClickedFunc && typeof onClickedFunc === "function") { + onClickedFunc() + } + } + } + + Text { + id: header + + text: root.headerText + + color: "#D7D8DB" + font.pixelSize: 36 + font.weight: 700 + font.family: "PT Root UI VF" + font.letterSpacing: -0.03 + + wrapMode: Text.WordWrap + + height: 38 + } +} diff --git a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml index 18f62344..dd0d2907 100644 --- a/client/ui/qml/Controls2/TextFieldWithHeaderType.qml +++ b/client/ui/qml/Controls2/TextFieldWithHeaderType.qml @@ -7,6 +7,7 @@ Item { property string headerText property string textFieldText + property string textFieldPlaceholderText property bool textFieldEditable: true implicitWidth: 328 @@ -45,6 +46,9 @@ Item { enabled: root.textFieldEditable text: root.textFieldText color: "#d7d8db" + + placeholderText: textFieldPlaceholderText + font.pixelSize: 16 font.weight: 400 font.family: "PT Root UI VF" diff --git a/client/ui/qml/PageLoader.qml b/client/ui/qml/PageLoader.qml new file mode 100644 index 00000000..5f2fc3c2 --- /dev/null +++ b/client/ui/qml/PageLoader.qml @@ -0,0 +1,57 @@ +import QtQuick + +import Qt.labs.folderlistmodel + +import PageType 1.0 + +Item { + property var pages: ({}) + + signal finished() + + FolderListModel { + id: folderModelPages + folder: "qrc:/ui/qml/Pages2/" + nameFilters: ["*.qml"] + showDirs: false + + onStatusChanged: { + if (status == FolderListModel.Ready) { + for (var i = 0; i < folderModelPages.count; i++) { + createPagesObjects(folderModelPages.get(i, "filePath"), PageType.Basic); + } + finished() + } + } + + function createPagesObjects(file, type) { + if (file.indexOf("Base") !== -1) { + return; // skip Base Pages + } + + var c = Qt.createComponent("qrc" + file); + + var finishCreation = function(component) { + if (component.status === Component.Ready) { + var obj = component.createObject(root); + if (obj === null) { + console.debug("Error creating object " + component.url); + } else { + obj.visible = false + if (type === PageType.Basic) { + pages[obj.page] = obj + } + } + } else if (component.status === Component.Error) { + console.debug("Error loading component:", component.errorString()); + } + } + + if (c.status === Component.Ready) { + finishCreation(c); + } else { + console.debug("Warning: " + file + " page components are not ready " + c.errorString()); + } + } + } +} diff --git a/client/ui/qml/Pages/PageTest.qml b/client/ui/qml/Pages/PageTest.qml index 9f820389..4c571ae0 100644 --- a/client/ui/qml/Pages/PageTest.qml +++ b/client/ui/qml/Pages/PageTest.qml @@ -13,7 +13,6 @@ PageBase { logic: ViewConfigLogic Rectangle { - y: 0 anchors.fill: parent color: "#0E0E11" } diff --git a/client/ui/qml/Pages2/PageCredentials.qml b/client/ui/qml/Pages2/PageCredentials.qml new file mode 100644 index 00000000..509ee6a6 --- /dev/null +++ b/client/ui/qml/Pages2/PageCredentials.qml @@ -0,0 +1,83 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import PageEnum 1.0 + +import "./" +import "../Pages" +import "../Controls2" +import "../Config" + +PageBase { + id: root + page: PageEnum.Credentials + + FlickableType { + id: fl + anchors.top: root.top + anchors.bottom: root.bottom + contentHeight: content.height + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + spacing: 16 + + HeaderTextType { + Layout.fillWidth: true + Layout.bottomMargin: 16 + Layout.topMargin: 66 + + Layout.preferredWidth: 328 + + buttonImage: "qrc:/images/controls/arrow-left.svg" + + headerText: "Подключение к серверу" + wrapMode: Text.WordWrap + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + headerText: "Server IP adress [:port]" + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + headerText: "Login to connect via SSH" + } + + TextFieldWithHeaderType { + Layout.fillWidth: true + headerText: "Password / Private key" + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 40 + + text: qsTr("Настроить сервер простым образом") + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 8 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(255, 255, 255, 0.08) + pressedColor: Qt.rgba(255, 255, 255, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: qsTr("Выбрать протокол для установки") + } + } + } +} diff --git a/client/ui/qml/Pages2/PageStart.qml b/client/ui/qml/Pages2/PageStart.qml new file mode 100644 index 00000000..6f8f0356 --- /dev/null +++ b/client/ui/qml/Pages2/PageStart.qml @@ -0,0 +1,163 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import PageEnum 1.0 + +import "./" +import "../Pages" +import "../Controls2" +import "../Config" + +PageBase { + id: root + page: PageEnum.Start + + FlickableType { + id: fl + anchors.top: root.top + anchors.bottom: root.bottom + contentHeight: content.height + + ColumnLayout { + id: content + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + Image { + id: image + source: "qrc:/images/amneziaBigLogo.png" + + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 80 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + Layout.preferredWidth: 344 + Layout.preferredHeight: 279 + } + + BodyTextType { + Layout.fillWidth: true + Layout.topMargin: 50 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + text: "Бесплатный сервис для создания личного VPN на вашем сервере. Помогаем получать доступ к заблокированному контенту, не раскрывая конфиденциальность даже провайдерам VPN." + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 32 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + text: qsTr("У меня есть данные для подключения") + + onClicked: { + drawer.visible = true + } + } + + BasicButtonType { + Layout.fillWidth: true + Layout.topMargin: 8 + Layout.leftMargin: 16 + Layout.rightMargin: 16 + + defaultColor: "transparent" + hoveredColor: Qt.rgba(255, 255, 255, 0.08) + pressedColor: Qt.rgba(255, 255, 255, 0.12) + disabledColor: "#878B91" + textColor: "#D7D8DB" + borderWidth: 1 + + text: qsTr("У меня ничего нет") + +// onClicked: { +// UiLogic.goToPage(PageEnum.Start) +// } + } + } + + Drawer { + id: drawer + + y: 0 + x: 0 + edge: Qt.BottomEdge + width: parent.width + height: parent.height * 0.4375 + + clip: true + + background: Rectangle { + anchors.fill: parent + anchors.bottomMargin: -radius + radius: 16 + color: "#1C1D21" + } + + modal: true + //interactive: activeFocus + +// onAboutToHide: { +// pageLoader.focus = true +// } +// onAboutToShow: { +// tfSshLog.focus = true +// } + + ColumnLayout { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + + anchors.rightMargin: 16 + anchors.leftMargin: 16 + + Header2TextType { + Layout.fillWidth: true + Layout.topMargin: 24 + Layout.alignment: Qt.AlignHCenter + + text: "Данные для подключения" + } + + LabelWithButtonType { + id: ip + Layout.fillWidth: true + Layout.topMargin: 32 + + text: "IP, логин и пароль от сервера" + buttonImage: "qrc:/images/controls/chevron-right.svg" + + onClickedFunc: function() { + UiLogic.goToPage(PageEnum.Credentials) + } + } + Rectangle { + Layout.fillWidth: true + height: 1 + color: "#2C2D30" + } + LabelWithButtonType { + Layout.fillWidth: true + + text: "QR-код, ключ или файл настроек" + buttonImage: "qrc:/images/controls/chevron-right.svg" + + // onClickedFunc: function() { + // UiLogic.goToPage(PageEnum.Start) + // } + } + Rectangle { + Layout.fillWidth: true + height: 1 + color: "#2C2D30" + } + } + } + } +} diff --git a/client/ui/qml/main.qml b/client/ui/qml/main.qml index b8758384..5194073e 100644 --- a/client/ui/qml/main.qml +++ b/client/ui/qml/main.qml @@ -85,7 +85,6 @@ Window { } Rectangle { - y: 0 anchors.fill: parent color: "white" } diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml new file mode 100644 index 00000000..deb64e89 --- /dev/null +++ b/client/ui/qml/main2.qml @@ -0,0 +1,142 @@ +import QtQuick +import QtQuick.Window +import QtQuick.Controls +import QtQuick.Layouts + +import PageType 1.0 + +import "Config" + +Window { + id: root + visible: true + width: GC.screenWidth + height: GC.screenHeight + minimumWidth: GC.isDesktop() ? 360 : 0 + minimumHeight: GC.isDesktop() ? 640 : 0 + onClosing: function() { + console.debug("QML onClosing signal") + UiLogic.onCloseWindow() + } + + title: "AmneziaVPN" + + function gotoPage(type, page, reset, slide) { + let p_obj; + if (type === PageType.Basic) p_obj = pageLoader.pages[page] + else if (type === PageType.Proto) p_obj = protocolPages[page] + else if (type === PageType.ShareProto) p_obj = sharePages[page] + else return + + if (pageStackView.depth > 0) { + pageStackView.currentItem.deactivated() + } + + if (slide) { + pageStackView.push(p_obj, {}, StackView.PushTransition) + } else { + pageStackView.push(p_obj, {}, StackView.Immediate) + } + +// if (reset) { +// p_obj.logic.onUpdatePage(); +// } + + p_obj.activated(reset) + } + + function closePage() { + if (pageStackView.depth <= 1) { + return + } + pageStackView.currentItem.deactivated() + pageStackView.pop() + } + + function setStartPage(page, slide) { + if (pageStackView.depth > 0) { + pageStackView.currentItem.deactivated() + } + + pageStackView.clear() + if (slide) { + pageStackView.push(pages[page], {}, StackView.PushTransition) + } else { + pageStackView.push(pages[page], {}, StackView.Immediate) + } + if (page === PageEnum.Start) { + UiLogic.pushButtonBackFromStartVisible = !pageStackView.empty + UiLogic.onUpdatePage(); + } + } + + Rectangle { + anchors.fill: parent + color: "#0E0E11" + } + + StackView { + id: pageStackView + anchors.fill: parent + focus: true + + onCurrentItemChanged: function() { + UiLogic.currentPageValue = currentItem.page + } + + onDepthChanged: function() { + UiLogic.pagesStackDepth = depth + } + + Keys.onPressed: function(event) { + UiLogic.keyPressEvent(event.key) + event.accepted = true + } + } + + Connections { + target: UiLogic + function onGoToPage(page, reset, slide) { + root.gotoPage(PageType.Basic, page, reset, slide) + } + + function onGoToProtocolPage(protocol, reset, slide) { + root.gotoPage(PageType.Proto, protocol, reset, slide) + } + + function onGoToShareProtocolPage(protocol, reset, slide) { + root.gotoPage(PageType.ShareProto, protocol, reset, slide) + } + + function onClosePage() { + root.closePage() + } + + function onSetStartPage(page, slide) { + root.setStartPage(page, slide) + } + + function onShow() { + root.show() + } + + function onHide() { + root.hide() + } + + function onRaise() { + root.show() + root.raise() + root.requestActivate() + } + } + + PageLoader { + id: pageLoader + + onFinished: { + UiLogic.initalizeUiLogic() + } + } + +}