[skip ci] WIP new UI

This commit is contained in:
Lars Toenning
2025-01-11 16:26:33 +01:00
parent eea8166f59
commit 1567ea6010
24 changed files with 954 additions and 0 deletions

12
src/ui/CMakeLists.txt Normal file
View File

@@ -0,0 +1,12 @@
add_executable(swift
main.cpp
QmlContextNetwork.cpp
QmlContextNetwork.h
QmlContextSimulator.h
PageController.cpp
resources.qrc
SimSelection.h
)
target_link_libraries(swift PUBLIC core misc config Qt::Core Qt::Qml Qt::Gui)

View File

@@ -0,0 +1,6 @@
//
// Created by lars on 12/23/24.
//
#include "PageController.h"

52
src/ui/PageController.h Normal file
View File

@@ -0,0 +1,52 @@
//
// Created by lars on 12/23/24.
//
#ifndef PAGECONTROLLER_H
#define PAGECONTROLLER_H
#include <iostream>
#include <ostream>
#include <QObject>
#include "core/context/contextsimulator.h"
class PageController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString component READ getComponent NOTIFY componentChanged)
public:
PageController()
{
swift::misc::CSetting<swift::core::application::TEnabledSimulator> enabledSimulator { this };
};
virtual ~PageController() {};
QString getComponent() const
{
return m_component;
}
Q_INVOKABLE void navigateToHome()
{
m_component = "../pages/MainPage.qml";
emit componentChanged();
}
Q_INVOKABLE void navigateToConnection()
{
m_component = "../pages/ConnectionPage.qml";
emit componentChanged();
}
signals:
void componentChanged();
private:
QString m_component = "../pages/MainPage.qml";
private:
};
#endif // QMLCONTEXTSIMULATOR_H

View File

@@ -0,0 +1,9 @@
//
// Created by lars on 12/23/24.
//
#include "QmlContextNetwork.h"
#include <iostream>
#include "core/context/contextnetwork.h"

View File

@@ -0,0 +1,41 @@
//
// Created by lars on 12/23/24.
//
#ifndef QMLCONTEXTNETWORK_H
#define QMLCONTEXTNETWORK_H
#include <iostream>
#include <ostream>
#include <QObject>
#include "core/context/contextnetwork.h"
namespace swift::core::context
{
class IContextNetwork;
}
class QmlContextNetwork : public QObject
{
Q_OBJECT
Q_PROPERTY(bool connectionStatus READ getConnectionStatus NOTIFY connectionStatusChanged)
public:
QmlContextNetwork(swift::core::context::IContextNetwork *network) : network_(network)
{
connect(network_, &swift::core::context::IContextNetwork::connectionStatusChanged, this,
&QmlContextNetwork::connectionStatusChanged);
}
bool getConnectionStatus() { return network_->isConnected(); }
signals:
void connectionStatusChanged();
public slots:
private:
swift::core::context::IContextNetwork *network_;
};
#endif // QMLCONTEXTNETWORK_H

View File

@@ -0,0 +1,65 @@
//
// Created by lars on 12/23/24.
//
#ifndef QMLCONTEXTSIMULATOR_H
#define QMLCONTEXTSIMULATOR_H
#include <iostream>
#include <ostream>
#include <QObject>
#include "core/context/contextsimulator.h"
class QmlContextSimulator : public QObject
{
Q_OBJECT
Q_PROPERTY(QStringList availableSimulators READ getAvailableSimulators NOTIFY availableSimulatorsChanged)
Q_PROPERTY(bool simulatorPluginSelected READ isSimulatorPluginSelected NOTIFY simulatorPluginSelected)
// Q_PROPERTY(bool connectionStatus READ getConnectionStatus NOTIFY connectionStatusChanged)
public:
QmlContextSimulator(swift::core::context::IContextSimulator *simulator) : simulator_(simulator)
{
connect(simulator_, &swift::core::context::IContextSimulator::simulatorPluginSelected, this,
&QmlContextSimulator::simulatorPluginSelected);
}
QStringList getAvailableSimulators() const
{
auto availableSimulators = simulator_->getAvailableSimulatorPlugins();
QStringList sims;
std::transform(availableSimulators.begin(), availableSimulators.end(), std::back_inserter(sims),
[](const auto &val) { return val.getName(); });
return sims;
}
bool isSimulatorPluginSelected() const { return simulator_->isSimulatorPluginSelected(); }
Q_INVOKABLE void setPluginIdentifier(const QString &identifier, bool store)
{
auto available = simulator_->getAvailableSimulatorPlugins();
auto it =
std::find_if(available.begin(), available.end(), [&](const auto &v) { return v.getName() == identifier; });
assert(it != available.end());
simulator_->setPlugin(*it);
if (store) { m_enabledSimulator.setAndSave(it->getIdentifier()); }
}
Q_INVOKABLE void clearSimulator() { m_enabledSimulator.setAndSave(""); }
// bool getConnectionStatus() { return simulator_->isConnected(); }
signals:
void availableSimulatorsChanged();
void connectionStatusChanged();
void simulatorPluginSelected();
private:
swift::core::context::IContextSimulator *simulator_;
swift::misc::CSetting<swift::core::application::TEnabledSimulator> m_enabledSimulator { this };
};
#endif // QMLCONTEXTSIMULATOR_H

18
src/ui/SimSelection.h Normal file
View File

@@ -0,0 +1,18 @@
//
// Created by lars on 12/31/24.
//
#ifndef SIMSELECTION_H
#define SIMSELECTION_H
struct SimSelection : public QObject
{
Q_OBJECT
Q_PROPERTY(QString simName MEMBER m_simName)
Q_PROPERTY(bool remember MEMBER m_remember)
public:
QString m_simName;
bool m_remember = false;
};
#endif //SIMSELECTION_H

View File

@@ -0,0 +1,124 @@
import QtQuick 2.15
import QtQuick.Layouts 2.15
import QtQuick.Controls 2.15
Rectangle {
color: "black"
ColumnLayout {
anchors.fill: parent
RowLayout {
AvionicButton {
Layout.leftMargin: 10
Layout.rightMargin: 2
labelText: "CTR"
active: true
}
AvionicButton {
Layout.leftMargin: 2
Layout.rightMargin: 2
labelText: "APP"
}
AvionicButton {
Layout.leftMargin: 2
Layout.rightMargin: 2
labelText: "TWR"
}
AvionicButton {
Layout.leftMargin: 2
Layout.rightMargin: 2
labelText: "GND"
}
AvionicButton {
Layout.leftMargin: 2
Layout.rightMargin: 2
labelText: "DEL"
}
AvionicButton {
Layout.leftMargin: 2
Layout.rightMargin: 2
labelText: "OTHER"
}
AvionicButton {
Layout.leftMargin: 2
Layout.rightMargin: 2
labelText: "ATIS"
}
Item {
Layout.fillWidth: true
}
}
Rectangle {
implicitWidth: parent.width - 20
Layout.leftMargin: 10
Layout.rightMargin: 10
height: 2
color: "gray"
}
Rectangle {
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.bottomMargin: 10
Layout.fillHeight: true
Layout.fillWidth: true
Rectangle {
width: parent.width * 0.4
height: parent.height
id: atcList
color: "black"
ScrollView {
clip: true
anchors.fill: parent
ListView {
id: atcListView
anchors.fill: parent
boundsMovement: Flickable.StopAtBounds
model: TestModel {}
property int selectedIndex: -1
delegate: Rectangle {
width: atcListView.width
height: label.implicitHeight
color: index === atcListView.selectedIndex ? "green" : "black"
Text {
id: label
color: "white"
font.family: "Mono" // TODO Use correct font
text: name + " - " + frequency
}
MouseArea {
anchors.fill: parent
onClicked: atcListView.selectedIndex = index
}
}
}
}
}
Rectangle {
id: atisView
border.color: "gray"
anchors.left: atcList.right
width: parent.width * 0.6
height: parent.height
color: "black"
Text {
anchors.margins: 5
anchors.fill: parent
text: "Joe Doe\nCallsign BREMEN GROUND\nAirport Briefing vats.im/edww | NO PDC/DCL\nSubmit your feedback at feedback.vatger.de"
color: "white"
font.family: "Mono" // TODO Use correct font
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
property string labelText: ""
property bool active: false
id: control
implicitWidth: contentLabel.implicitWidth * 1.1
implicitHeight: contentLabel.implicitHeight * 1.1
font.family: "Mono" // TODO Use correct font
contentItem: Label {
id: contentLabel
text: labelText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: active ? "#00ff00" : "#ffffff"
}
background: Rectangle {
implicitWidth: contentLabel.implicitWidth
implicitHeight: contentLabel.implicitHeight
color: "#555555"
}
}

View File

@@ -0,0 +1,79 @@
import QtQuick 2.15
import QtQuick.Layouts 2.15
Rectangle {
width: parent.width
height: parent.height * 0.2
color: "black"
RowLayout {
anchors.fill: parent
Text {
Layout.leftMargin: 10
Layout.rightMargin: 10
text: "COM1"
color: "white"
font.family: "Mono" // TODO Use correct font
font.pointSize: 20
}
Text {
text: "122.800"
color: "white"
font.family: "Mono" // TODO Use correct font
font.pointSize: 20
}
ColumnLayout {
Text {
text: "TX"
color: "white"
font.family: "Mono" // TODO Use correct font
}
Text {
text: "RX"
color: "white"
font.family: "Mono" // TODO Use correct font
}
}
Item {
Layout.fillWidth: true
}
Text {
text: "COM2"
color: "white"
Layout.leftMargin: 10
Layout.rightMargin: 10
font.family: "Mono" // TODO Use correct font
font.pointSize: 20
}
Text {
text: "121.500"
color: "white"
font.family: "Mono" // TODO Use correct font
font.pointSize: 20
}
ColumnLayout {
Layout.rightMargin: 10
Text {
text: "TX"
color: "white"
font.family: "Mono" // TODO Use correct font
}
Text {
text: "RX"
color: "white"
font.family: "Mono" // TODO Use correct font
}
}
}
}

View File

@@ -0,0 +1,97 @@
import QtQuick 2.15
import QtQuick.Layouts 2.15
import QtQuick.Controls 2.15
Rectangle {
color: "black"
width: parent.width
height: parent.height * 0.4
ColumnLayout {
anchors.fill: parent
RowLayout {
AvionicButton {
labelText: "LOG"
Layout.leftMargin: 10
Layout.rightMargin: 2
active: true
}
AvionicButton {
labelText: "COM"
Layout.leftMargin: 2
Layout.rightMargin: 2
}
AvionicButton {
labelText: "DLH123"
Layout.leftMargin: 2
Layout.rightMargin: 2
}
Item {
Layout.fillWidth: true
}
}
Rectangle {
implicitWidth: parent.width - 20
implicitHeight: 2
color: "gray"
Layout.leftMargin: 10
Layout.rightMargin: 10
}
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.bottomMargin: 5
width: parent.width * 0.4
height: parent.height
id: atcList
color: "black"
ScrollView {
clip: true
anchors.fill: parent
ListView {
id: atcListView
anchors.fill: parent
boundsMovement: Flickable.StopAtBounds
model: TestModelLogs {}
verticalLayoutDirection: ListView.BottomToTop
property int selectedIndex: -1
delegate: Text {
id: label
color: "white"
font.family: "Mono" // TODO Use correct font
text: timestamp + ": " + log
}
}
}
}
Rectangle {
height: 20
Layout.fillWidth: true
color: "#242424"
border.color: chatInput.activeFocus ? "green" : "white"
Layout.leftMargin: 10
Layout.rightMargin: 10
Layout.topMargin: 0
Layout.bottomMargin: 10
TextInput {
id: chatInput
anchors.leftMargin: 5
anchors.rightMargin: 5
anchors.topMargin: 1
anchors.bottomMargin: 1
anchors.fill: parent
color: "white"
font.family: "Mono" // TODO Use correct font
}
}
}
}

View File

@@ -0,0 +1,60 @@
import QtQuick 2.15
import QtQuick.Layouts 2.15
import QtQuick.Controls 2.15
Rectangle {
id: bottombar
color: "#2f2f2f"
RowLayout {
anchors.fill: parent
anchors.horizontalCenter: parent.horizontalCenter
StatusLight {
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
labelText: "CONN"
active: controller.component === "../pages/ConnectionPage.qml"
onClick: controller.navigateToConnection()
}
StatusLight {
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
labelText: "MAIN"
active: controller.component === "../pages/MainPage.qml"
onClick: controller.navigateToHome()
}
StatusLight {
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
labelText: "CSTM"
}
StatusLight {
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
labelText: "SETT"
}
StatusLight {
Layout.fillWidth: true
Layout.leftMargin: 10
Layout.rightMargin: 10
labelText: "ADV"
onClick: advancedMenu.popup()
}
Menu {
id: advancedMenu
MenuItem { text: "FPL" }
MenuItem { text: "RADAR" }
MenuItem { text: "SIM" }
}
}
}

View File

@@ -0,0 +1,22 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
property string labelText: ""
id: control
implicitWidth: parent.width * 0.5
font.family: "Mono" // TODO Use correct font
contentItem: Label {
text: labelText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: "white"
}
background: Rectangle {
implicitWidth: parent.width * 0.5
implicitHeight: 20
color: control.down ? "#111111" : "#000000"
}
}

View File

@@ -0,0 +1,73 @@
import QtQuick 2.15
import QtQuick.Layouts 2.15
import QtQuick.Controls 2.15
Rectangle {
id: topbar
color: "#2f2f2f"
RowLayout {
anchors.fill: parent
anchors.verticalCenter: parent.verticalCenter
StatusLight {
Layout.margins: 10
labelText: "S"
disabledColor: "#323232"
}
StatusLight {
labelText: "SIM"
disabledColor: "#323232"
Layout.margins: 10
}
StatusLight {
labelText: "NET"
disabledColor: "#323232"
active: network.connectionStatus
Layout.margins: 10
}
StatusLight {
labelText: "PTT"
disabledColor: "#323232"
Layout.margins: 10
}
StatusLight {
labelText: "STS"
disabledColor: "#323232"
Layout.margins: 10
}
Text {
id: callsign
text: "-----"
color: "white"
font.bold: true
Layout.margins: 10
}
Item {
Layout.fillWidth: true
}
Rectangle {
Layout.alignment: Qt.AlignRight
Layout.margins: 10
Layout.preferredWidth: 25
Layout.preferredHeight: 25
color: "red"
Text {
anchors.centerIn: parent
text: "X"
font.pointSize: 16
}
MouseArea {
anchors.fill: parent
onClicked: Qt.quit()
}
}
}
}

View File

@@ -0,0 +1,29 @@
import QtQuick 2.15
Rectangle {
width: text.text.length === 1 ? text.width * 2.5 : text.width * 1.2;
height: 30
property string labelText: ""
property string disabledColor: "white"
property bool active: false
signal click()
color: "black"
MouseArea {
id: area
anchors.fill: parent
onClicked: parent.click()
}
Text {
id: text
anchors.margins: 20
anchors.centerIn: parent
text: labelText
color: active ? "#26ff00" : disabledColor
font.pointSize: 12
font.family: "Mono" // TODO Use correct font
}
}

View File

@@ -0,0 +1,36 @@
import QtQuick
ListModel {
ListElement {
name: "EDGG_1_CTR"
frequency: "135.655"
}
ListElement {
name: "EDGG_2_CTR"
frequency: "122.800"
}
ListElement {
name: "EDGG_3_CTR"
frequency: "122.805"
}
ListElement {
name: "EDGG_4_CTR"
frequency: "122.810"
}
ListElement {
name: "EDGG_5_CTR"
frequency: "122.815"
}
ListElement {
name: "EDGG_6_CTR"
frequency: "122.825"
}
ListElement {
name: "EDGG_7_CTR"
frequency: "122.830"
}
ListElement {
name: "EDGG_8_CTR"
frequency: "122.835"
}
}

View File

@@ -0,0 +1,12 @@
import QtQuick
ListModel {
ListElement {
log: "Plugin loaded"
timestamp: "08:12:23"
}
ListElement {
log: "swift started"
timestamp: "08:12:22"
}
}

48
src/ui/main.cpp Normal file
View File

@@ -0,0 +1,48 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QSettings>
#include "PageController.h"
#include "QmlContextNetwork.h"
#include "QmlContextSimulator.h"
#include "SimSelection.h"
#include "core/application.h"
#include "core/context/context.h"
#include "core/context/contextnetwork.h"
#include "core/context/contextsimulator.h"
#include "core/pluginmanagersimulator.h"
int main(int argc, char *argv[])
{
QCoreApplication::setOrganizationName("swift project");
QCoreApplication::setApplicationName("pilotclient");
QGuiApplication app(argc, argv);
// Init swift core
swift::core::CApplication swiftApp(swift::misc::CApplicationInfo::PilotClient);
// swiftApp.addDBusAddressOption() // TODO Core mode
swiftApp.addAudioOptions();
swiftApp.addNetworkOptions();
swiftApp.parseCommandLineArgsAndLoadSetup();
swift::core::CCoreFacadeConfig config(swift::core::CCoreFacadeConfig::Local);
swiftApp.initContextsAndStartCoreFacade(config);
PageController controller;
QmlContextNetwork net(swiftApp.getIContextNetwork());
QmlContextSimulator sim(swiftApp.getIContextSimulator());
QQmlApplicationEngine engine;
// Setup context for QML
engine.rootContext()->setContextProperty("network", &net);
engine.rootContext()->setContextProperty("simulator", &sim);
engine.rootContext()->setContextProperty("controller", &controller);
engine.load(QUrl(QStringLiteral("qrc:/main/main.qml")));
if (engine.rootObjects().isEmpty()) { return -1; }
return app.exec();
}

View File

@@ -0,0 +1,31 @@
import QtQuick 2.15
import "../components"
import "../pages"
import QtQuick.Layouts 2.15
ColumnLayout {
visible: false
spacing: 0
anchors.fill: parent
StatusBar {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 50
id: topbar
}
Rectangle {
id: main
Layout.preferredWidth: parent.width
Layout.preferredHeight: parent.height - topbar.Layout.preferredHeight - bottombar.Layout.preferredHeight
color: "#000000"
Loader {
id: dataArea
anchors.fill: parent
source: controller.component
}
}
NavigationBar {
id: bottombar
Layout.fillWidth: true
Layout.preferredHeight: 50
}
}

View File

@@ -0,0 +1,36 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 2.15
import "../components"
Rectangle {
anchors.fill: parent
color: "#323232"
ColumnLayout {
anchors.fill: parent
Text {
Layout.alignment: Qt.AlignCenter
id: simselecttext
text: "SELECT YOUR SIMULATOR"
color: "white"
font.family: "Mono" // TODO Use correct font
}
Repeater {
model: simulator.availableSimulators
SimulatorButton {
required property string modelData
labelText: modelData
Layout.alignment: Qt.AlignCenter
onClicked: {
simulator.setPluginIdentifier(modelData, false)
}
}
}
}
}

22
src/ui/main/main.qml Normal file
View File

@@ -0,0 +1,22 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
id: root
width: 700
height: 500
title: "swift"
SimSelection {
anchors.fill: parent
visible: !simulator.simulatorPluginSelected
}
MainLayout {
anchors.fill: parent
visible: simulator.simulatorPluginSelected
}
}

View File

@@ -0,0 +1,13 @@
import QtQuick 2.15
Rectangle {
color: "black"
anchors.fill: parent
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
text: "Connection"
color: "white"
}
}

23
src/ui/pages/MainPage.qml Normal file
View File

@@ -0,0 +1,23 @@
import QtQuick 2.15
import "../components"
Rectangle {
color: "black"
anchors.fill: parent
ComPanel {
id: comPanel
}
AtcPanel {
id: atcPanel
anchors.top: comPanel.bottom
width: parent.width
height: parent.height * 0.4
}
LogPanel {
anchors.top: atcPanel.bottom
id: logPanel
}
}

21
src/ui/resources.qrc Normal file
View File

@@ -0,0 +1,21 @@
<RCC>
<qresource prefix="/">
<file>main/main.qml</file>
<file>main/SimSelection.qml</file>
<file>main/MainLayout.qml</file>
<file>components/StatusLight.qml</file>
<file>components/NavigationBar.qml</file>
<file>components/ComPanel.qml</file>
<file>components/AtcPanel.qml</file>
<file>components/LogPanel.qml</file>
<file>components/AvionicButton.qml</file>
<file>components/TestModel.qml</file>
<file>components/TestModelLogs.qml</file>
<file>components/SimulatorButton.qml</file>
<file>components/StatusBar.qml</file>
<file>pages/MainPage.qml</file>
<file>pages/ConnectionPage.qml</file>
</qresource>
</RCC>