General design overhaul and added lots of new languages (finnish swedish norwegian turkish)
Build Linux and Android / build (push) Successful in 2m28s
@@ -1,6 +1,6 @@
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Quick WebSockets LinguistTools Charts)
|
||||
|
||||
qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de)
|
||||
qt_standard_project_setup(REQUIRES 6.6 I18N_TRANSLATED_LANGUAGES de sv fi nb tr)
|
||||
|
||||
qt_add_executable(evcharger-app WIN32 MACOSX_BUNDLE
|
||||
appsettings.cpp
|
||||
@@ -50,6 +50,8 @@ if (ANDROID)
|
||||
)
|
||||
endif()
|
||||
|
||||
set_source_files_properties(qml/AppTheme.qml qml/Constants.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
|
||||
|
||||
qt_add_qml_module(evcharger-app
|
||||
URI EVChargerApp
|
||||
VERSION 1.0
|
||||
@@ -65,6 +67,7 @@ qt_add_qml_module(evcharger-app
|
||||
qml/ApiSettingsPage.qml
|
||||
qml/AppInstance.qml
|
||||
qml/AppSettingsPage.qml
|
||||
qml/AppTheme.qml
|
||||
qml/BaseNavigationPage.qml
|
||||
qml/CablePage.qml
|
||||
qml/CarPage.qml
|
||||
@@ -106,6 +109,7 @@ qt_add_qml_module(evcharger-app
|
||||
qml/InformationsTabPage.qml
|
||||
qml/InformationChartPanel.qml
|
||||
qml/KwhLimitPage.qml
|
||||
qml/LanguageSelector.qml
|
||||
qml/LedPage.qml
|
||||
qml/LicensesPage.qml
|
||||
qml/LoadBalancingPage.qml
|
||||
@@ -131,6 +135,9 @@ qt_add_qml_module(evcharger-app
|
||||
qml/SelectPhaseSwitchModeItem.qml
|
||||
qml/SendMessageButton.qml
|
||||
qml/SensorsConfigurationPage.qml
|
||||
qml/SettingsCard.qml
|
||||
qml/SettingsLabel.qml
|
||||
qml/SettingsSectionLabel.qml
|
||||
qml/SetPriceLimitPage.qml
|
||||
qml/SettingsTabPage.qml
|
||||
qml/SetValueHelper.qml
|
||||
@@ -166,14 +173,20 @@ qt_add_qml_module(evcharger-app
|
||||
qml/images/geminiFix.png
|
||||
qml/js/qrcode.min.js
|
||||
qml/material-icons/add.svg
|
||||
qml/material-icons/arrow_back.svg
|
||||
qml/material-icons/grid_guides.svg
|
||||
qml/material-icons/open_in_new.svg
|
||||
qml/material-icons/settings.svg
|
||||
qml/flags/de.svg
|
||||
qml/flags/fi.svg
|
||||
qml/flags/gb.svg
|
||||
qml/flags/no.svg
|
||||
qml/flags/se.svg
|
||||
qml/flags/system.svg
|
||||
qml/flags/tr.svg
|
||||
qml/ui-icons/MaterialIcons-Regular.ttf
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/Constants.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
|
||||
|
||||
target_link_libraries(evcharger-app PUBLIC
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
|
||||
@@ -3,6 +3,49 @@
|
||||
#include <QFile>
|
||||
#include <QQmlFile>
|
||||
|
||||
QLocale::Language AppSettings::uiLanguage() const
|
||||
{
|
||||
const QVariant storedLanguage = value("uiLanguage");
|
||||
bool isNumber = false;
|
||||
const int language = storedLanguage.toInt(&isNumber);
|
||||
|
||||
if (isNumber && language >= QLocale::AnyLanguage && language <= QLocale::LastLanguage)
|
||||
return static_cast<QLocale::Language>(language);
|
||||
|
||||
// Also understand string values written by development versions.
|
||||
if (storedLanguage.toString() == QStringLiteral("de"))
|
||||
return QLocale::German;
|
||||
if (storedLanguage.toString() == QStringLiteral("en"))
|
||||
return QLocale::English;
|
||||
if (storedLanguage.toString() == QStringLiteral("sv"))
|
||||
return QLocale::Swedish;
|
||||
if (storedLanguage.toString() == QStringLiteral("fi"))
|
||||
return QLocale::Finnish;
|
||||
if (storedLanguage.toString() == QStringLiteral("nb")
|
||||
|| storedLanguage.toString() == QStringLiteral("no"))
|
||||
return QLocale::NorwegianBokmal;
|
||||
if (storedLanguage.toString() == QStringLiteral("tr"))
|
||||
return QLocale::Turkish;
|
||||
|
||||
return QLocale::AnyLanguage;
|
||||
}
|
||||
|
||||
void AppSettings::setUiLanguage(QLocale::Language uiLanguage)
|
||||
{
|
||||
if (uiLanguage < QLocale::AnyLanguage || uiLanguage > QLocale::LastLanguage)
|
||||
return;
|
||||
|
||||
if (uiLanguage == this->uiLanguage())
|
||||
return;
|
||||
|
||||
if (uiLanguage == QLocale::AnyLanguage)
|
||||
remove("uiLanguage");
|
||||
else
|
||||
setValue("uiLanguage", static_cast<int>(uiLanguage));
|
||||
|
||||
emit uiLanguageChanged(uiLanguage);
|
||||
}
|
||||
|
||||
bool AppSettings::loadSolalawebKey(const QString &url)
|
||||
{
|
||||
QFile file{QQmlFile::urlToLocalFileOrQrc(url)};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QLocale>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
|
||||
#include "goesettings.h"
|
||||
@@ -8,8 +9,26 @@ class AppSettings : public GoeSettings
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
Q_PROPERTY(QLocale::Language uiLanguage READ uiLanguage WRITE setUiLanguage NOTIFY uiLanguageChanged FINAL)
|
||||
|
||||
public:
|
||||
enum UiLanguageValue {
|
||||
SystemLanguage = QLocale::AnyLanguage,
|
||||
GermanLanguage = QLocale::German,
|
||||
EnglishLanguage = QLocale::English,
|
||||
SwedishLanguage = QLocale::Swedish,
|
||||
FinnishLanguage = QLocale::Finnish,
|
||||
NorwegianLanguage = QLocale::NorwegianBokmal,
|
||||
TurkishLanguage = QLocale::Turkish,
|
||||
};
|
||||
Q_ENUM(UiLanguageValue)
|
||||
|
||||
QLocale::Language uiLanguage() const;
|
||||
void setUiLanguage(QLocale::Language uiLanguage);
|
||||
|
||||
Q_INVOKABLE bool loadSolalawebKey(const QString &url);
|
||||
Q_INVOKABLE bool loadSolalawebCert(const QString &url);
|
||||
|
||||
signals:
|
||||
void uiLanguageChanged(QLocale::Language uiLanguage);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#include <QApplication>
|
||||
#include <QTranslator>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
#include <QApplication>
|
||||
#include <QLocale>
|
||||
#include <QTranslator>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQmlContext>
|
||||
|
||||
#include "appsettings.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@@ -20,15 +23,38 @@ int main(int argc, char *argv[])
|
||||
QCoreApplication::setOrganizationDomain("brunner.ninja");
|
||||
QCoreApplication::setApplicationName("evcharger-app");
|
||||
|
||||
QApplication app(argc, argv);
|
||||
|
||||
QTranslator translator;
|
||||
if (!translator.load(QLocale(), "qml", "_", ":/EVChargerApp/i18n/"))
|
||||
qWarning("could not load translations!");
|
||||
app.installTranslator(&translator);
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
|
||||
QApplication app(argc, argv);
|
||||
|
||||
AppSettings settings;
|
||||
QTranslator translator;
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
const auto applyUiLanguage = [&](QLocale::Language language) {
|
||||
app.removeTranslator(&translator);
|
||||
|
||||
const QLocale translationLocale = language == QLocale::AnyLanguage
|
||||
? QLocale::system()
|
||||
: QLocale(language);
|
||||
|
||||
// English is the source language. Missing catalogs naturally fall back
|
||||
// to English. QLocale's formatting locale remains untouched.
|
||||
if (translationLocale.language() != QLocale::English
|
||||
&& translator.load(translationLocale,
|
||||
QStringLiteral("qml"),
|
||||
QStringLiteral("_"),
|
||||
QStringLiteral(":/EVChargerApp/i18n/")))
|
||||
app.installTranslator(&translator);
|
||||
|
||||
if (!engine.rootObjects().isEmpty())
|
||||
engine.retranslate();
|
||||
};
|
||||
|
||||
applyUiLanguage(settings.uiLanguage());
|
||||
QObject::connect(&settings, &AppSettings::uiLanguageChanged,
|
||||
&engine, applyUiLanguage);
|
||||
|
||||
engine.rootContext()->setContextProperty(QStringLiteral("theSettings"), &settings);
|
||||
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed,
|
||||
[](const QUrl &url){ qFatal("object creation failed: %s", qPrintable(url.toString()));
|
||||
});
|
||||
|
||||
|
||||
@@ -28,12 +28,10 @@ RowLayout {
|
||||
apiKey: row.apiKey
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.preferredWidth: 125
|
||||
Layout.maximumWidth: 125
|
||||
SettingsLabel {
|
||||
Layout.preferredWidth: Math.min(125, row.width * 0.42)
|
||||
Layout.maximumWidth: Math.min(125, row.width * 0.42)
|
||||
text: row.label
|
||||
font.bold: true
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Text {
|
||||
|
||||
@@ -11,6 +11,13 @@ NavigationPage {
|
||||
return false
|
||||
}
|
||||
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("Language")
|
||||
}
|
||||
|
||||
LanguageSelector {
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
|
||||
QtObject {
|
||||
readonly property bool settingsLabelBold: true
|
||||
readonly property int cardRadius: 8
|
||||
readonly property color cardColor: "white"
|
||||
readonly property color cardPressedColor: "#f1f1f1"
|
||||
readonly property color sectionLabelColor: "#5f6368"
|
||||
}
|
||||
@@ -18,15 +18,26 @@ Item {
|
||||
id: headerLayout
|
||||
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
Button {
|
||||
ToolButton {
|
||||
text: qsTr("Back")
|
||||
icon.source: "material-icons/arrow_back.svg"
|
||||
display: AbstractButton.IconOnly
|
||||
visible: stackView.depth > 1
|
||||
Layout.preferredWidth: 48
|
||||
Layout.preferredHeight: 48
|
||||
Accessible.name: text
|
||||
Accessible.description: qsTr("Return to the previous screen")
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: text
|
||||
onClicked: stackView.pop()
|
||||
}
|
||||
|
||||
TitleText {
|
||||
Layout.fillWidth: true
|
||||
leftPadding: 8
|
||||
rightPadding: 8
|
||||
|
||||
id: titleText
|
||||
}
|
||||
|
||||
@@ -10,69 +10,46 @@ NavigationPage {
|
||||
apiKey: "cwe"
|
||||
text: qsTr("Enable cloud connection")
|
||||
dialogTitle: qsTr("Are you sure?")
|
||||
dialogText: qsTr("Disabling could connection could make your device unreachable from other places than home!")
|
||||
dialogText: qsTr("Disabling the cloud connection could make your device unreachable from outside your home network!")
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
text: qsTr("Features like flexible energy tariffs, time sync and app connection are unavilable when \"Enable cloud connection\" is disabled")
|
||||
text: qsTr("Features such as flexible energy tariffs, time synchronization, and remote app access are unavailable when the cloud connection is disabled.")
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
SettingsCard {
|
||||
ApiStatusRow {
|
||||
apiKey: "cws"
|
||||
label: qsTr("Trying to connect")
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
ApiStatusRow {
|
||||
apiKey: "cwsc"
|
||||
label: qsTr("Connected")
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Trying to connect:")
|
||||
font.bold: true
|
||||
}
|
||||
// TODO cwsca
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "cws"
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "chr"
|
||||
label: qsTr("Hello received")
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Is connected:")
|
||||
font.bold: true
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "qsc"
|
||||
label: qsTr("Cloud queue size")
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "cwsc"
|
||||
}
|
||||
SettingsLabel {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Last error")
|
||||
}
|
||||
|
||||
// TODO cwsca
|
||||
|
||||
Text {
|
||||
text: qsTr("Hello received:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "chr"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Queue size cloud:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "qsc"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Last error:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "cle"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
ApiKeyValueItem {
|
||||
apiKey: "cle"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,6 @@ WhiteCheckDelegate {
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Component.onCompleted: {
|
||||
contentItem.children[0].wrapMode = Text.Wrap
|
||||
}
|
||||
|
||||
ApiKeyValueHelper {
|
||||
id: apiKeyValueHelper
|
||||
deviceConnection: theDeviceConnection
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp
|
||||
import QtQuick
|
||||
import QtQuick.Controls.Material
|
||||
import EVChargerApp
|
||||
|
||||
ApplicationWindow {
|
||||
id: window
|
||||
@@ -12,11 +11,7 @@ ApplicationWindow {
|
||||
title: qsTr("EVcharger App")
|
||||
color: "#F3F2F7"
|
||||
|
||||
AppSettings {
|
||||
id: theSettings
|
||||
}
|
||||
|
||||
DevicesModel {
|
||||
DevicesModel {
|
||||
id: devicesModel
|
||||
|
||||
settings: theSettings
|
||||
@@ -29,12 +24,13 @@ ApplicationWindow {
|
||||
source: "ui-icons/MaterialIcons-Regular.ttf"
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: view
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
currentIndex: indicator.currentIndex
|
||||
SwipeView {
|
||||
id: view
|
||||
|
||||
anchors.fill: parent
|
||||
// App instances are switched explicitly with three fingers. This keeps
|
||||
// one-finger horizontal gestures available to controls inside them.
|
||||
interactive: false
|
||||
|
||||
Repeater {
|
||||
model: theSettings.numberOfAppInstances
|
||||
@@ -46,8 +42,8 @@ ApplicationWindow {
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicator {
|
||||
id: indicator
|
||||
PageIndicator {
|
||||
id: indicator
|
||||
|
||||
visible: view.count > 1
|
||||
|
||||
@@ -55,18 +51,133 @@ ApplicationWindow {
|
||||
currentIndex: view.currentIndex
|
||||
|
||||
anchors.top: view.top
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
function backPressed() {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
// Two fingers switch the UI language; three fingers switch app instances.
|
||||
// Exact point counts keep one-finger gestures available to page content.
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
z: 1000
|
||||
|
||||
DragHandler {
|
||||
id: languageSwipe
|
||||
|
||||
target: null
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
minimumPointCount: 2
|
||||
maximumPointCount: 2
|
||||
|
||||
property real lastHorizontalDistance: 0
|
||||
property real lastVerticalDistance: 0
|
||||
property bool gestureCanceled: false
|
||||
|
||||
onActiveTranslationChanged: {
|
||||
if (active) {
|
||||
lastHorizontalDistance = activeTranslation.x
|
||||
lastVerticalDistance = activeTranslation.y
|
||||
}
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
lastHorizontalDistance = 0
|
||||
lastVerticalDistance = 0
|
||||
gestureCanceled = false
|
||||
return
|
||||
}
|
||||
|
||||
if (!gestureCanceled
|
||||
&& Math.abs(lastHorizontalDistance) >= 72
|
||||
&& Math.abs(lastHorizontalDistance) > Math.abs(lastVerticalDistance) * 1.5)
|
||||
window.cycleUiLanguage(lastHorizontalDistance)
|
||||
}
|
||||
|
||||
onCanceled: (point) => {
|
||||
gestureCanceled = true
|
||||
}
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: appInstanceSwipe
|
||||
|
||||
target: null
|
||||
enabled: view.count > 1
|
||||
acceptedDevices: PointerDevice.TouchScreen
|
||||
minimumPointCount: 3
|
||||
maximumPointCount: 3
|
||||
|
||||
property real lastHorizontalDistance: 0
|
||||
property real lastVerticalDistance: 0
|
||||
property bool gestureCanceled: false
|
||||
|
||||
onActiveTranslationChanged: {
|
||||
if (active) {
|
||||
lastHorizontalDistance = activeTranslation.x
|
||||
lastVerticalDistance = activeTranslation.y
|
||||
}
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
lastHorizontalDistance = 0
|
||||
lastVerticalDistance = 0
|
||||
gestureCanceled = false
|
||||
return
|
||||
}
|
||||
|
||||
if (!gestureCanceled
|
||||
&& Math.abs(lastHorizontalDistance) >= 72
|
||||
&& Math.abs(lastHorizontalDistance) > Math.abs(lastVerticalDistance) * 1.5)
|
||||
window.switchAppInstance(lastHorizontalDistance)
|
||||
}
|
||||
|
||||
onCanceled: (point) => {
|
||||
gestureCanceled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function effectiveUiLanguage() {
|
||||
if (theSettings.uiLanguage !== AppSettings.SystemLanguage)
|
||||
return theSettings.uiLanguage
|
||||
const languageCode = Qt.locale().name.substring(0, 2).toLowerCase()
|
||||
switch (languageCode) {
|
||||
case "de": return AppSettings.GermanLanguage
|
||||
case "sv": return AppSettings.SwedishLanguage
|
||||
case "fi": return AppSettings.FinnishLanguage
|
||||
case "nb":
|
||||
case "nn":
|
||||
case "no": return AppSettings.NorwegianLanguage
|
||||
case "tr": return AppSettings.TurkishLanguage
|
||||
default: return AppSettings.EnglishLanguage
|
||||
}
|
||||
}
|
||||
|
||||
function cycleUiLanguage(horizontalDistance) {
|
||||
const languages = [AppSettings.GermanLanguage, AppSettings.EnglishLanguage,
|
||||
AppSettings.SwedishLanguage, AppSettings.FinnishLanguage,
|
||||
AppSettings.NorwegianLanguage, AppSettings.TurkishLanguage]
|
||||
const currentIndex = languages.indexOf(effectiveUiLanguage())
|
||||
const direction = horizontalDistance < 0 ? 1 : -1
|
||||
const nextIndex = (currentIndex + direction + languages.length) % languages.length
|
||||
theSettings.uiLanguage = languages[nextIndex]
|
||||
}
|
||||
|
||||
function switchAppInstance(horizontalDistance) {
|
||||
const direction = horizontalDistance < 0 ? 1 : -1
|
||||
view.currentIndex = Math.max(0, Math.min(view.count - 1,
|
||||
view.currentIndex + direction))
|
||||
}
|
||||
|
||||
function backPressed() {
|
||||
if (view.currentItem.backPressed())
|
||||
return true
|
||||
|
||||
if (view.currentIndex > 0) {
|
||||
view.currentIndex = 0
|
||||
view.currentIndex = Qt.binding(() => indicator.currentIndex)
|
||||
return true
|
||||
}
|
||||
if (view.currentIndex > 0) {
|
||||
view.currentIndex = 0
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -29,10 +29,9 @@ WhiteItemDelegate {
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
Text {
|
||||
SettingsLabel {
|
||||
wrapMode: Text.Wrap
|
||||
Layout.fillWidth: true
|
||||
font.bold: true
|
||||
text: itemDelegate.text
|
||||
}
|
||||
|
||||
|
||||
@@ -41,47 +41,34 @@ NavigationPage {
|
||||
text: checked ? qsTr("Ethernet on") : qsTr("Ethernet off")
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: connectionStatus.implicitHeight + 20
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("Connection")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: connectionStatus
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
SettingsCard {
|
||||
ApiStatusRow {
|
||||
apiKey: "cce"
|
||||
label: qsTr("Address")
|
||||
valueFormatter: function(value) { return connectionText(value) }
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Connection")
|
||||
font.bold: true
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "cce"
|
||||
label: qsTr("Link")
|
||||
valueFormatter: function(value) { return linkText(value) }
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "cce"
|
||||
label: qsTr("Address")
|
||||
valueFormatter: function(value) { return connectionText(value) }
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "cce"
|
||||
label: qsTr("Link")
|
||||
valueFormatter: function(value) { return linkText(value) }
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "cce"
|
||||
label: qsTr("Gateway")
|
||||
valueFormatter: function(value) {
|
||||
return value && value.gw ? value.gw : "—"
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "cce"
|
||||
label: qsTr("Gateway")
|
||||
valueFormatter: function(value) {
|
||||
return value && value.gw ? value.gw : "—"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("IPv4 configuration")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
GeneralOnOffSwitch {
|
||||
@@ -171,39 +158,30 @@ NavigationPage {
|
||||
}
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: ethernetDetails.implicitHeight + 20
|
||||
SettingsSectionLabel {
|
||||
visible: ethernetHostname.exists || ethernetMac.exists || ethernetInit.exists
|
||||
text: qsTr("Interface details")
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: ethernetHostname.exists || ethernetMac.exists || ethernetInit.exists
|
||||
|
||||
ColumnLayout {
|
||||
id: ethernetDetails
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
ApiStatusRow {
|
||||
id: ethernetHostname
|
||||
apiKey: "hste"
|
||||
label: qsTr("Hostname")
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Interface details")
|
||||
font.bold: true
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: ethernetMac
|
||||
apiKey: "mace"
|
||||
label: qsTr("MAC address")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: ethernetHostname
|
||||
apiKey: "hste"
|
||||
label: qsTr("Hostname")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: ethernetMac
|
||||
apiKey: "mace"
|
||||
label: qsTr("MAC address")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: ethernetInit
|
||||
apiKey: "eis"
|
||||
label: qsTr("Initialization")
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: ethernetInit
|
||||
apiKey: "eis"
|
||||
label: qsTr("Initialization")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,6 @@ WhiteCheckDelegate {
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Component.onCompleted: {
|
||||
contentItem.children[0].wrapMode = Text.Wrap
|
||||
}
|
||||
|
||||
ApiKeyValueHelper {
|
||||
id: valueHelper
|
||||
deviceConnection: theDeviceConnection
|
||||
|
||||
@@ -12,10 +12,8 @@ NavigationPage {
|
||||
text: checked ? qsTr("Hotspot on") : qsTr("Hotspot off")
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("Configuration")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
GeneralOnOffSwitch {
|
||||
@@ -77,33 +75,24 @@ NavigationPage {
|
||||
}
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: hotspotStatus.implicitHeight + 20
|
||||
SettingsSectionLabel {
|
||||
visible: hotspotHostname.exists || hotspotMac.exists
|
||||
text: qsTr("Hotspot identity")
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: hotspotHostname.exists || hotspotMac.exists
|
||||
|
||||
ColumnLayout {
|
||||
id: hotspotStatus
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
ApiStatusRow {
|
||||
id: hotspotHostname
|
||||
apiKey: "hsta"
|
||||
label: qsTr("Hostname")
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Hotspot identity")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: hotspotHostname
|
||||
apiKey: "hsta"
|
||||
label: qsTr("Hostname")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: hotspotMac
|
||||
apiKey: "maca"
|
||||
label: qsTr("MAC address")
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: hotspotMac
|
||||
apiKey: "maca"
|
||||
label: qsTr("MAC address")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp
|
||||
import EVChargerApp as App
|
||||
|
||||
SettingsCard {
|
||||
id: selector
|
||||
|
||||
function languageForLocale(localeName) {
|
||||
const languageCode = localeName.substring(0, 2).toLowerCase()
|
||||
switch (languageCode) {
|
||||
case "de": return AppSettings.GermanLanguage
|
||||
case "sv": return AppSettings.SwedishLanguage
|
||||
case "fi": return AppSettings.FinnishLanguage
|
||||
case "nb":
|
||||
case "nn":
|
||||
case "no": return AppSettings.NorwegianLanguage
|
||||
case "tr": return AppSettings.TurkishLanguage
|
||||
default: return AppSettings.EnglishLanguage
|
||||
}
|
||||
}
|
||||
|
||||
function languageName(language) {
|
||||
switch (language) {
|
||||
case AppSettings.GermanLanguage: return qsTr("German")
|
||||
case AppSettings.SwedishLanguage: return qsTr("Swedish")
|
||||
case AppSettings.FinnishLanguage: return qsTr("Finnish")
|
||||
case AppSettings.NorwegianLanguage: return qsTr("Norwegian")
|
||||
case AppSettings.TurkishLanguage: return qsTr("Turkish")
|
||||
default: return qsTr("English")
|
||||
}
|
||||
}
|
||||
|
||||
readonly property int systemLanguage: languageForLocale(Qt.locale().name)
|
||||
readonly property var languageChoices: [
|
||||
{
|
||||
value: AppSettings.SystemLanguage,
|
||||
text: qsTr("System default (%1)").arg(languageName(systemLanguage)),
|
||||
icon: "flags/system.svg"
|
||||
},
|
||||
{ value: AppSettings.GermanLanguage, text: qsTr("German"), icon: "flags/de.svg" },
|
||||
{ value: AppSettings.EnglishLanguage, text: qsTr("English"), icon: "flags/gb.svg" },
|
||||
{ value: AppSettings.SwedishLanguage, text: qsTr("Swedish"), icon: "flags/se.svg" },
|
||||
{ value: AppSettings.FinnishLanguage, text: qsTr("Finnish"), icon: "flags/fi.svg" },
|
||||
{ value: AppSettings.NorwegianLanguage, text: qsTr("Norwegian"), icon: "flags/no.svg" },
|
||||
{ value: AppSettings.TurkishLanguage, text: qsTr("Turkish"), icon: "flags/tr.svg" }
|
||||
]
|
||||
|
||||
SettingsLabel {
|
||||
text: qsTr("App language")
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: languageCombo
|
||||
|
||||
Layout.fillWidth: true
|
||||
model: selector.languageChoices
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
|
||||
function synchronizeLanguage() {
|
||||
const selectedIndex = indexOfValue(theSettings.uiLanguage)
|
||||
if (selectedIndex >= 0)
|
||||
currentIndex = selectedIndex
|
||||
}
|
||||
|
||||
Component.onCompleted: synchronizeLanguage()
|
||||
onModelChanged: synchronizeLanguage()
|
||||
onActivated: theSettings.uiLanguage = currentValue
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: languageDelegate
|
||||
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
width: languageCombo.width
|
||||
highlighted: languageCombo.highlightedIndex === index
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 10
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 28
|
||||
Layout.preferredHeight: 20
|
||||
source: languageDelegate.modelData.icon
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: languageDelegate.modelData.text
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 10
|
||||
|
||||
Image {
|
||||
Layout.preferredWidth: 28
|
||||
Layout.preferredHeight: 20
|
||||
source: languageCombo.currentIndex >= 0
|
||||
? selector.languageChoices[languageCombo.currentIndex].icon : ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: languageCombo.displayText
|
||||
elide: Text.ElideRight
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: theSettings
|
||||
function onUiLanguageChanged() { languageCombo.synchronizeLanguage() }
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Numbers, dates, and times continue to use your system settings.")
|
||||
color: App.AppTheme.sectionLabelColor
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ NavigationPage {
|
||||
}
|
||||
|
||||
EditValueItem {
|
||||
text: qsTr("Broker url")
|
||||
text: qsTr("Broker URL")
|
||||
apiKey: "mcu"
|
||||
fullWidth: true
|
||||
editableItem: TextField {
|
||||
@@ -25,95 +25,62 @@ NavigationPage {
|
||||
url: qsTr("https://github.com/goecharger/go-eCharger-API-v2/blob/main/mqtt-en.md")
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
SettingsCard {
|
||||
ApiStatusRow {
|
||||
apiKey: "mcs"
|
||||
label: qsTr("Trying to connect")
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
anchors.fill: parent
|
||||
columns: 2
|
||||
ApiStatusRow {
|
||||
apiKey: "mcc"
|
||||
label: qsTr("Connected")
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Trying to connect:")
|
||||
font.bold: true
|
||||
ApiStatusRow {
|
||||
apiKey: "mcca"
|
||||
label: qsTr("Connected since")
|
||||
valueFormatter: function(value) {
|
||||
return value ? formatDuration(value - rebootTime.value) : "—"
|
||||
}
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "mcs"
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "mcsl"
|
||||
label: qsTr("Command counter")
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Is connected:")
|
||||
font.bold: true
|
||||
}
|
||||
SettingsLabel {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Last error")
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "mcc"
|
||||
}
|
||||
ApiKeyValueItem {
|
||||
apiKey: "mlr"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Connected since:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueHelper {
|
||||
id: connectedSince
|
||||
deviceConnection: theDeviceConnection
|
||||
apiKey: "mcca"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: connectedSince.value ? formatDuration(connectedSince.value - rebootTime.value) : ""
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Command Counter:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "mcsl"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Last error:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueItem {
|
||||
apiKey: "mlr"
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Text {
|
||||
text: qsTr("Last error age:")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
ApiKeyValueHelper {
|
||||
id: lastErrorAge
|
||||
deviceConnection: theDeviceConnection
|
||||
apiKey: "mlra"
|
||||
}
|
||||
|
||||
Text {
|
||||
text: lastErrorAge.value ? formatDuration(lastErrorAge.value - rebootTime.value) : ""
|
||||
ApiStatusRow {
|
||||
apiKey: "mlra"
|
||||
label: qsTr("Last error age")
|
||||
valueFormatter: function(value) {
|
||||
return value ? formatDuration(value - rebootTime.value) : "—"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GeneralOnOffSwitch {
|
||||
apiKey: "mqg"
|
||||
text: qsTr("Use global CA store")
|
||||
text: qsTr("Use global CA certificate store")
|
||||
}
|
||||
|
||||
GeneralOnOffSwitch {
|
||||
apiKey: "mqcn"
|
||||
text: qsTr("Skip cert commonName check")
|
||||
text: qsTr("Skip certificate common name check")
|
||||
}
|
||||
|
||||
GeneralOnOffSwitch {
|
||||
apiKey: "mqss"
|
||||
text: qsTr("Skip server cert verification")
|
||||
text: qsTr("Skip server certificate verification")
|
||||
}
|
||||
|
||||
// TODO clientKey
|
||||
@@ -124,7 +91,7 @@ NavigationPage {
|
||||
|
||||
GeneralOnOffSwitch {
|
||||
apiKey: "mcr"
|
||||
text: qsTr("MQTT readonly")
|
||||
text: qsTr("MQTT read-only")
|
||||
}
|
||||
|
||||
EditValueItem {
|
||||
@@ -141,13 +108,13 @@ NavigationPage {
|
||||
GeneralOnOffSwitch {
|
||||
id: homeassistant
|
||||
apiKey: "mhe"
|
||||
text: qsTr("Homeassistant autodiscover")
|
||||
text: qsTr("Home Assistant auto-discovery")
|
||||
}
|
||||
|
||||
EditValueItem {
|
||||
visible: homeassistant.checked
|
||||
// TODO allow set to null to reset to default
|
||||
text: qsTr("Homeassistant topic prefix")
|
||||
text: qsTr("Home Assistant topic prefix")
|
||||
apiKey: "mht"
|
||||
fullWidth: true
|
||||
editableItem: TextField {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
WhiteBox {
|
||||
default property alias contents: contentLayout.data
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: contentLayout
|
||||
spacing: 6
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import EVChargerApp as App
|
||||
|
||||
Label {
|
||||
font.bold: App.AppTheme.settingsLabelBold
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp
|
||||
import EVChargerApp as App
|
||||
|
||||
SettingsLabel {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
color: App.AppTheme.sectionLabelColor
|
||||
}
|
||||
@@ -5,9 +5,6 @@ import QtQuick.Layouts
|
||||
NavigationPage {
|
||||
title: qsTr("Switch Language")
|
||||
|
||||
Text {
|
||||
text: "TODO"
|
||||
|
||||
Layout.fillHeight: true
|
||||
LanguageSelector {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp
|
||||
import EVChargerApp as App
|
||||
|
||||
Pane {
|
||||
Component.onCompleted: {
|
||||
background.color = "white"
|
||||
background.radius = 5
|
||||
background: Rectangle {
|
||||
color: App.AppTheme.cardColor
|
||||
radius: App.AppTheme.cardRadius
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,22 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp as App
|
||||
|
||||
CheckDelegate {
|
||||
id: control
|
||||
|
||||
font.bold: App.AppTheme.settingsLabelBold
|
||||
|
||||
Component.onCompleted: {
|
||||
background.color = "white"
|
||||
background.radius = 5
|
||||
// The style-owned IconLabel does not expose its wrapping mode. Keep the
|
||||
// workaround here so every derived settings switch behaves alike.
|
||||
if (contentItem.children.length > 0)
|
||||
contentItem.children[0].wrapMode = Text.Wrap
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: 48
|
||||
color: control.down ? App.AppTheme.cardPressedColor : App.AppTheme.cardColor
|
||||
radius: App.AppTheme.cardRadius
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp as App
|
||||
|
||||
ItemDelegate {
|
||||
Component.onCompleted: {
|
||||
background.radius = 5
|
||||
background.color = "white"
|
||||
id: control
|
||||
|
||||
font.bold: App.AppTheme.settingsLabelBold
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: 48
|
||||
color: control.down ? App.AppTheme.cardPressedColor : App.AppTheme.cardColor
|
||||
radius: App.AppTheme.cardRadius
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import EVChargerApp as App
|
||||
|
||||
SwipeDelegate {
|
||||
Component.onCompleted: {
|
||||
background.color = "white"
|
||||
background.radius = 5
|
||||
id: control
|
||||
|
||||
background: Rectangle {
|
||||
color: control.down ? App.AppTheme.cardPressedColor : App.AppTheme.cardColor
|
||||
radius: App.AppTheme.cardRadius
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,62 +216,51 @@ NavigationPage {
|
||||
dialogText: qsTr("Disabling Wi-Fi could make your device unreachable from your local homenetwork or the cloud!")
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: connectionStatusColumn.implicitHeight + 20
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("Connection")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: connectionStatusColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
SettingsCard {
|
||||
ApiStatusRow {
|
||||
apiKey: "wst"
|
||||
label: qsTr("Status")
|
||||
valueFormatter: function(value) { return stationStatusName(value) }
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Connection")
|
||||
font.bold: true
|
||||
ApiStatusRow {
|
||||
apiKey: "ccw"
|
||||
label: qsTr("Network")
|
||||
valueFormatter: function(value) {
|
||||
if (value === null || value === undefined)
|
||||
return qsTr("Not connected")
|
||||
const address = value.ip ? " • " + value.ip : ""
|
||||
return (value.ssid || qsTr("Unknown network")) + address
|
||||
}
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "wst"
|
||||
label: qsTr("Status")
|
||||
valueFormatter: function(value) { return stationStatusName(value) }
|
||||
ApiStatusRow {
|
||||
apiKey: "rssi"
|
||||
label: qsTr("Signal")
|
||||
valueFormatter: function(value) {
|
||||
return value === null || value === undefined
|
||||
? "—"
|
||||
: signalBars(value) + " " + value + " dBm"
|
||||
}
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "ccw"
|
||||
label: qsTr("Network")
|
||||
valueFormatter: function(value) {
|
||||
if (value === null || value === undefined)
|
||||
return qsTr("Not connected")
|
||||
const address = value.ip ? " • " + value.ip : ""
|
||||
return (value.ssid || qsTr("Unknown network")) + address
|
||||
}
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "scas"
|
||||
label: qsTr("Wi-Fi scan")
|
||||
valueFormatter: function(value) { return scanStatusName(value) }
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "rssi"
|
||||
label: qsTr("Signal")
|
||||
valueFormatter: function(value) {
|
||||
return value === null || value === undefined
|
||||
? "—"
|
||||
: signalBars(value) + " " + value + " dBm"
|
||||
}
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "scas"
|
||||
label: qsTr("Wi-Fi scan")
|
||||
valueFormatter: function(value) { return scanStatusName(value) }
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
apiKey: "scaa"
|
||||
label: qsTr("Last scan")
|
||||
valueFormatter: function(value) {
|
||||
return value === null || value === undefined
|
||||
? qsTr("Never")
|
||||
: formatDuration(value - rebootTime.value)
|
||||
}
|
||||
ApiStatusRow {
|
||||
apiKey: "scaa"
|
||||
label: qsTr("Last scan")
|
||||
valueFormatter: function(value) {
|
||||
return value === null || value === undefined
|
||||
? qsTr("Never")
|
||||
: formatDuration(value - rebootTime.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,10 +268,9 @@ NavigationPage {
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
SettingsSectionLabel {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Configured networks")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
BusyIndicator {
|
||||
@@ -313,15 +301,12 @@ NavigationPage {
|
||||
id: configuredRepeater
|
||||
model: configuredNetworks()
|
||||
|
||||
delegate: WhiteBox {
|
||||
delegate: SettingsCard {
|
||||
required property var modelData
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: configuredRow.implicitHeight + 20
|
||||
|
||||
RowLayout {
|
||||
id: configuredRow
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
@@ -359,11 +344,8 @@ NavigationPage {
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("Available networks")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Repeater {
|
||||
@@ -451,11 +433,8 @@ NavigationPage {
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 10
|
||||
SettingsSectionLabel {
|
||||
text: qsTr("Advanced Wi-Fi settings")
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
EditValueItem {
|
||||
@@ -479,77 +458,69 @@ NavigationPage {
|
||||
text: qsTr("Require protected management frames")
|
||||
}
|
||||
|
||||
WhiteBox {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: wifiDetailsColumn.implicitHeight + 20
|
||||
SettingsSectionLabel {
|
||||
visible: wifiAddressing.exists || wifiAccessPoint.exists || wifiConnectionProcess.exists
|
||||
|| wifiMac.exists || wifiHostname.exists || wifiErrorCount.exists
|
||||
text: qsTr("Technical details")
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
visible: wifiAddressing.exists || wifiAccessPoint.exists || wifiConnectionProcess.exists
|
||||
|| wifiMac.exists || wifiHostname.exists || wifiErrorCount.exists
|
||||
|
||||
ColumnLayout {
|
||||
id: wifiDetailsColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: 10
|
||||
|
||||
Text {
|
||||
Layout.fillWidth: true
|
||||
text: qsTr("Technical details")
|
||||
font.bold: true
|
||||
ApiStatusRow {
|
||||
id: wifiAddressing
|
||||
apiKey: "ccw"
|
||||
label: qsTr("Addressing")
|
||||
valueFormatter: function(value) {
|
||||
if (value === null || value === undefined)
|
||||
return "—"
|
||||
const parts = []
|
||||
if (value.ip) parts.push(qsTr("IP %1").arg(value.ip))
|
||||
if (value.netmask) parts.push(qsTr("mask %1").arg(value.netmask))
|
||||
if (value.gw) parts.push(qsTr("gateway %1").arg(value.gw))
|
||||
return parts.length > 0 ? parts.join(" • ") : "—"
|
||||
}
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: wifiAddressing
|
||||
apiKey: "ccw"
|
||||
label: qsTr("Addressing")
|
||||
valueFormatter: function(value) {
|
||||
if (value === null || value === undefined)
|
||||
return "—"
|
||||
const parts = []
|
||||
if (value.ip) parts.push(qsTr("IP %1").arg(value.ip))
|
||||
if (value.netmask) parts.push(qsTr("mask %1").arg(value.netmask))
|
||||
if (value.gw) parts.push(qsTr("gateway %1").arg(value.gw))
|
||||
return parts.length > 0 ? parts.join(" • ") : "—"
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: wifiAccessPoint
|
||||
apiKey: "ccw"
|
||||
label: qsTr("Access point")
|
||||
valueFormatter: function(value) {
|
||||
if (value === null || value === undefined)
|
||||
return "—"
|
||||
const channel = value.channel !== undefined ? qsTr("channel %1").arg(value.channel) : ""
|
||||
return [value.bssid, channel].filter(Boolean).join(" • ")
|
||||
}
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: wifiAccessPoint
|
||||
apiKey: "ccw"
|
||||
label: qsTr("Access point")
|
||||
valueFormatter: function(value) {
|
||||
if (value === null || value === undefined)
|
||||
return "—"
|
||||
const channel = value.channel !== undefined ? qsTr("channel %1").arg(value.channel) : ""
|
||||
return [value.bssid, channel].filter(Boolean).join(" • ")
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: wifiConnectionProcess
|
||||
apiKey: "wsms"
|
||||
label: qsTr("Connection process")
|
||||
valueFormatter: function(value) {
|
||||
const names = [qsTr("Idle"), qsTr("Scanning"), qsTr("Connecting"), qsTr("Connected")]
|
||||
return value >= 0 && value < names.length ? names[value] : qsTr("Unknown")
|
||||
}
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: wifiConnectionProcess
|
||||
apiKey: "wsms"
|
||||
label: qsTr("Connection process")
|
||||
valueFormatter: function(value) {
|
||||
const names = [qsTr("Idle"), qsTr("Scanning"), qsTr("Connecting"), qsTr("Connected")]
|
||||
return value >= 0 && value < names.length ? names[value] : qsTr("Unknown")
|
||||
}
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: wifiMac
|
||||
apiKey: "macs"
|
||||
label: qsTr("MAC address")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: wifiMac
|
||||
apiKey: "macs"
|
||||
label: qsTr("MAC address")
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: wifiHostname
|
||||
apiKey: "hsts"
|
||||
label: qsTr("Hostname")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: wifiHostname
|
||||
apiKey: "hsts"
|
||||
label: qsTr("Hostname")
|
||||
}
|
||||
|
||||
ApiStatusRow {
|
||||
id: wifiErrorCount
|
||||
apiKey: "wsc"
|
||||
label: qsTr("Connection errors")
|
||||
}
|
||||
ApiStatusRow {
|
||||
id: wifiErrorCount
|
||||
apiKey: "wsc"
|
||||
label: qsTr("Connection errors")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20">
|
||||
<rect width="30" height="20" rx="1.5" fill="#ffce00"/>
|
||||
<rect width="30" height="13.333" rx="1.5" fill="#dd0000"/>
|
||||
<rect width="30" height="6.667" rx="1.5" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 246 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 20">
|
||||
<rect width="32" height="20" rx="1.5" fill="#fff"/>
|
||||
<path d="M0 8h32M10 0v20" stroke="#003580" stroke-width="4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 186 B |
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 40">
|
||||
<rect width="60" height="40" rx="3" fill="#012169"/>
|
||||
<path d="M0 0 60 40M60 0 0 40" stroke="#fff" stroke-width="8"/>
|
||||
<path d="M0 0 60 40M60 0 0 40" stroke="#c8102e" stroke-width="3"/>
|
||||
<path d="M30 0v40M0 20h60" stroke="#fff" stroke-width="13"/>
|
||||
<path d="M30 0v40M0 20h60" stroke="#c8102e" stroke-width="7"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 386 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 20">
|
||||
<rect width="32" height="20" rx="1.5" fill="#ba0c2f"/>
|
||||
<path d="M0 8h32M10 0v20" stroke="#fff" stroke-width="5"/>
|
||||
<path d="M0 8h32M10 0v20" stroke="#00205b" stroke-width="2.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 252 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 20">
|
||||
<rect width="32" height="20" rx="1.5" fill="#006aa7"/>
|
||||
<path d="M0 8h32M10 0v20" stroke="#fecc02" stroke-width="4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 189 B |
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="9" fill="none" stroke="#5f6368" stroke-width="2"/>
|
||||
<path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18" fill="none" stroke="#5f6368" stroke-width="1.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 258 B |
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 20">
|
||||
<rect width="30" height="20" rx="1.5" fill="#e30a17"/>
|
||||
<circle cx="12" cy="10" r="5" fill="#fff"/>
|
||||
<circle cx="13.5" cy="10" r="4" fill="#e30a17"/>
|
||||
<path d="m18.6 7.4.7 1.8 2 .1-1.6 1.2.5 1.9-1.6-1.1-1.7 1.1.6-1.9-1.6-1.2 2-.1z" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 319 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#000000"><path d="m313-440 224 224-57 56-320-320 320-320 57 56-224 224h487v80H313Z"/></svg>
|
||||
|
After Width: | Height: | Size: 190 B |