This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-app/nymea-app/ui/MainPage.qml
2025-12-05 13:40:20 +01:00

928 lines
39 KiB
QML

// SPDX-License-Identifier: GPL-3.0-or-later
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-app.
*
* nymea-app is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nymea-app is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with nymea-app. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Material
import QtQuick.Layouts
import QtQuick.Window
import QtCore
import Qt.labs.folderlistmodel
import Qt5Compat.GraphicalEffects
import Nymea
import "components"
import "delegates"
import "mainviews"
Page {
id: root
// Removing the background from this page only because the MainViewBase adds it again in
// a deepter layer as we need to include it in the blurring of the header and footer.
// We don't want to paint the background on the entire screen twice (overdraw is costly)
background: null
function configureViews() {
if (Configuration.hasOwnProperty("mainViewsFilter")) {
console.warn("Main views configuration is disabled by app configuration")
return
}
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
d.configOverlay = configComponent.createObject(contentContainer)
}
function goToView(viewName, data) {
// We allow separating the target by : and pass more stuff to
console.log("Going to main view", viewName, filteredContentModel.count, data)
for (var i = 0; i < filteredContentModel.count; i++) {
console.log("got", i, filteredContentModel.modelData(i, "name"))
if (filteredContentModel.modelData(i, "name") === viewName) {
console.log("activating", i)
// mainViewSettings.currentIndex = i;
// tabBar.currentIndex = i;
swipeView.setCurrentIndex(i)
swipeView.currentItem.item.handleEvent(data)
break;
}
}
}
header: Item {
id: mainHeader
height: 0
HeaderButton {
id: menuButton
imageSource: "qrc:/icons/navigation-menu.svg"
anchors { left: parent.left; top: parent.top }
onClicked: {
if (d.configOverlay != null) {
d.configOverlay.destroy();
}
app.mainMenu.open()
}
}
Row {
id: additionalIcons
anchors { right: parent.right; top: parent.top }
visible: !d.configOverlay
width: visible ? implicitWidth : 0
HeaderButton {
id: button
imageSource: "qrc:/icons/system-update.svg"
color: Style.accentColor
visible: updatesModel.count > 0 || engine.systemController.updateRunning
onClicked: pageStack.push(Qt.resolvedUrl("system/SystemUpdatePage.qml"))
RotationAnimation on rotation {
from: 0
to: 360
duration: 2000
loops: Animation.Infinite
running: engine.systemController.updateRunning
onStopped: button.rotation = 0;
}
PackagesFilterModel {
id: updatesModel
packages: engine.systemController.packages
updatesOnly: true
}
}
Repeater {
model: swipeView.currentItem != null && swipeView.currentItem.item.hasOwnProperty("headerButtons") ? swipeView.currentItem.item.headerButtons : 0
delegate: HeaderButton {
imageSource: swipeView.currentItem.item.headerButtons[index].iconSource
onClicked: swipeView.currentItem.item.headerButtons[index].trigger()
visible: swipeView.currentItem.item.headerButtons[index].visible
color: swipeView.currentItem.item.headerButtons[index].color
}
}
}
}
Connections {
target: engine.ruleManager
onAddRuleReply: (commandId, ruleError, ruleId) => {
d.editRulePage.busy = false
if (d.editRulePage) {
pageStack.pop();
d.editRulePage = null
}
}
}
QtObject {
id: d
property bool blurEnabled: PlatformHelper.deviceManufacturer !== "raspbian"
property var editRulePage: null
property var configOverlay: null
}
Settings {
id: mainViewSettings
category: engine.jsonRpcClient.currentHost.uuid
property string mainMenuContent: ""
property var sortOrder: []
// Priority for main view config:
// 1. Settings made by the user
// 2. Style mainViewsFilter as that comes with branding (for now, if a style defines main views, all of them are active by default)
// 3. Command line args
// 4. Just show "things" alone by default
property var filterList: Configuration.hasOwnProperty("mainViewsFilter") ?
Configuration.mainViewsFilter
: defaultMainViewFilter.length > 0 ?
defaultMainViewFilter.split(',')
: [Configuration.defaultMainView]
property int currentIndex: 0
}
ListModel {
id: mainMenuBaseModel
ListElement { name: "things"; source: "ThingsView"; displayName: qsTr("Things"); icon: "things"; minVersion: "0.0" }
ListElement { name: "favorites"; source: "FavoritesView"; displayName: qsTr("Favorites"); icon: "starred"; minVersion: "2.0" }
ListElement { name: "groups"; source: "GroupsView"; displayName: qsTr("Groups"); icon: "groups"; minVersion: "2.0" }
ListElement { name: "scenes"; source: "ScenesView"; displayName: qsTr("Scenes"); icon: "slideshow"; minVersion: "2.0" }
ListElement { name: "garages"; source: "GaragesView"; displayName: qsTr("Garages"); icon: "garage/garage-100"; minVersion: "2.0" }
ListElement { name: "energy"; source: "EnergyView"; displayName: qsTr("Energy"); icon: "smartmeter"; minVersion: "2.0" }
ListElement { name: "media"; source: "MediaView"; displayName: qsTr("Media"); icon: "media"; minVersion: "2.0" }
ListElement { name: "dashboard"; source: "DashboardView"; displayName: qsTr("Dashboard"); icon: "dashboard"; minVersion: "5.5" }
ListElement { name: "airconditioning"; source: "AirConditioningView"; displayName: qsTr("AC"); icon: "sensors"; minVersion: "6.2" }
}
ListModel {
id: mainMenuModel
ListElement { name: "dummy"; source: "Dummy"; displayName: ""; icon: "" }
Component.onCompleted: {
var configList = {}
var newList = {}
var newItems = 0
// Add extra views first to make them appear first in the list unless the config says otherwise
if (Configuration.hasOwnProperty("additionalMainViews")) {
for (var i = 0; i < Configuration.additionalMainViews.count; i++) {
var item = Configuration.additionalMainViews.get(i);
var idx = mainViewSettings.sortOrder.indexOf(item.name);
if (idx === -1) {
newList[newItems++] = item;
} else {
configList[idx] = item;
}
}
}
for (var i = 0; i < mainMenuBaseModel.count; i++) {
var item = mainMenuBaseModel.get(i);
if (!engine.jsonRpcClient.ensureServerVersion(item.minVersion)) {
console.log("Skipping main view", item.name, "as the minimum required server version isn't met:", engine.jsonRpcClient.jsonRpcVersion, "<", item.minVersion)
continue;
}
var idx = mainViewSettings.sortOrder.indexOf(item.name);
if (idx === -1) {
newList[newItems++] = item;
} else {
configList[idx] = item;
}
}
clear();
var brandingFilter = Configuration.hasOwnProperty("mainViewsFilter") ? Configuration.mainViewsFilter : []
for (idx in configList) {
item = configList[idx];
if (brandingFilter.length === 0 || brandingFilter.indexOf(item.name) >= 0) {
mainMenuModel.append(item)
}
}
for (idx in newList) {
item = newList[idx];
if (brandingFilter.length === 0 || brandingFilter.indexOf(item.name) >= 0) {
mainMenuModel.append(item)
}
}
swipeView.currentIndex = mainViewSettings.currentIndex
mainViewSettings.currentIndex = Qt.binding(function() { return swipeView.currentIndex; })
}
}
SortFilterProxyModel {
id: filteredContentModel
sourceModel: mainMenuModel
filterList: mainViewSettings.filterList
filterRoleName: "name"
}
Item {
id: contentContainer
anchors.fill: parent
clip: true
property int headerSize: 48
property int footerSize: app.landscape ? 48 : 64
readonly property int scrollOffset: swipeView.currentItem ? swipeView.currentItem.item.contentY : 0
readonly property int headerBlurSize: Math.min(headerSize, scrollOffset * 2)
Background {
anchors.fill: parent
}
SwipeView {
id: swipeView
anchors.fill: parent
opacity: d.configOverlay === null ? 1 : 0
visible: !engine.thingManager.fetchingData
Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
Repeater {
id: mainViewsRepeater
model: d.configOverlay != null ? null : filteredContentModel
delegate: Loader {
id: mainViewLoader
width: swipeView.width
height: swipeView.height
clip: true
source: "mainviews/" + model.source + ".qml"
visible: SwipeView.isCurrentItem || SwipeView.isNextItem || SwipeView.isPreviousItem
Binding {
target: mainViewLoader.item
property: "isCurrentItem"
value: swipeView.currentIndex == index
}
Binding {
target: mainViewLoader.item
property: "bottomMargin"
value: footer.visible ? contentContainer.footerSize : 0
}
Image {
source: "qrc:/styles/%1/logo-wide.svg".arg(styleController.currentStyle)
anchors {
top: parent.top;
topMargin: -contentContainer.scrollOffset + (contentContainer.headerSize - height) / 2
horizontalCenter: parent.horizontalCenter;
}
fillMode: Image.PreserveAspectFit
height: 28
sourceSize.height: height
antialiasing: true
z: 2
}
}
}
}
ColumnLayout {
anchors { left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter; margins: Style.margins }
spacing: Style.margins
visible: engine.thingManager.fetchingData
BusyIndicator {
Layout.alignment: Qt.AlignHCenter
running: parent.visible
}
Label {
text: qsTr("Loading data...")
font: Style.bigFont
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
}
}
}
ShaderEffectSource {
id: headerBlurSource
width: contentContainer.width
height: d.configOverlay ? contentContainer.headerSize : contentContainer.headerBlurSize
sourceItem: d.blurEnabled ? contentContainer : null
sourceRect: Qt.rect(0, 0, contentContainer.width, d.configOverlay ? contentContainer.headerSize : contentContainer.headerBlurSize)
visible: false
}
FastBlur {
anchors {
left: parent.left;
top: parent.top;
right: parent.right;
}
height: d.configOverlay ? contentContainer.headerSize : contentContainer.headerBlurSize
radius: 40
transparentBorder: true
source: d.blurEnabled ? headerBlurSource : null
visible: d.blurEnabled
}
Rectangle {
id: headerOpacityMask
anchors {
left: parent.left
top: parent.top
right: parent.right
}
height: d.configOverlay ? contentContainer.headerSize : contentContainer.headerBlurSize
gradient: Gradient {
GradientStop { position: 0.1; color: Style.backgroundColor }
GradientStop { position: 0.6; color: Qt.rgba(Style.backgroundColor.r, Style.backgroundColor.g, Style.backgroundColor.b, 0.3) }
GradientStop { position: 1; color: "transparent" }
}
}
ShaderEffectSource {
id: footerBlurSource
width: contentContainer.width
height: contentContainer.footerSize
sourceItem: d.blurEnabled ? contentContainer : null
sourceRect: Qt.rect(0, contentContainer.height - height, contentContainer.width, contentContainer.footerSize)
visible: false
enabled: d.blurEnabled && footer.shown
}
FastBlur {
anchors {
left: parent.left;
bottom: parent.bottom;
right: parent.right;
}
height: contentContainer.footerSize
radius: 40
transparentBorder: false
source: d.blurEnabled ? footerBlurSource : null
visible: d.blurEnabled && footer.shown
}
Rectangle {
id: footer
readonly property bool shown: tabsRepeater.count > 1 || d.configOverlay
visible: shown
anchors {
left: parent.left
bottom: parent.bottom
right: parent.right
}
height: contentContainer.footerSize
Behavior on height { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad }}
// color: "transparent"
gradient: Gradient {
GradientStop { position: 0; color: "transparent" }
GradientStop { position: 0.4; color: Qt.rgba(Style.backgroundColor.r, Style.backgroundColor.g, Style.backgroundColor.b, 0.7) }
GradientStop { position: 1; color: Style.backgroundColor }
}
RowLayout {
id: tabsLayout
anchors.fill: parent
spacing: 0
opacity: d.configOverlay ? 0 : 1
Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
Repeater {
id: tabsRepeater
model: d.configOverlay != null ? null : filteredContentModel
// model: filteredContentModel
delegate: MainPageTabButton {
Layout.fillWidth: true
Layout.fillHeight: true
alignment: app.landscape ? Qt.Horizontal : Qt.Vertical
checked: index === swipeView.currentIndex
// anchors.verticalCenter: parent.verticalCenter
text: model.displayName
iconSource: "qrc:/icons/" + model.icon + ".svg"
onClicked: swipeView.currentIndex = index
onPressAndHold: {
root.configureViews();
}
}
}
}
MainPageTabButton {
anchors.fill: parent
alignment: app.landscape ? Qt.Horizontal : Qt.Vertical
text: d.configOverlay ? qsTr("Done") : qsTr("Configure")
iconSource: "qrc:/icons/configure.svg"
opacity: d.configOverlay ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 200; easing.type: Easing.InOutQuad } }
visible: opacity > 0
checked: true
onClicked: {
if (d.configOverlay) {
d.configOverlay.destroy()
} else {
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
d.configOverlay = configComponent.createObject(contentContainer)
}
}
}
}
Component {
id: configComponent
Background {
id: configOverlay
width: contentContainer.width
height: contentContainer.height
ListView {
id: configListView
anchors.fill: parent
model: mainMenuModel
topMargin: contentContainer.headerSize
bottomMargin: contentContainer.footerSize
property bool dragging: draggingIndex >= 0
property int draggingIndex : -1
moveDisplaced: Transition { NumberAnimation { properties: "y" } }
delegate: NymeaItemDelegate {
id: viewConfigDelegate
width: parent.width
text: model.displayName
iconName: Qt.resolvedUrl("qrc:/icons/" + model.icon + ".svg")
progressive: false
checked: mainViewSettings.filterList.indexOf(model.name) >= 0
visible: index !== configListView.draggingIndex
additionalItem: CheckBox {
checked: viewConfigDelegate.checked
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newList = []
for (var i = 0; i < mainMenuModel.count; i++) {
var entry = mainMenuModel.get(i).name;
if (entry === model.name) {
if (!isEnabled) {
newList.push(model.name)
}
} else {
if (mainViewSettings.filterList.indexOf(entry) >= 0) {
newList.push(entry)
}
}
}
if (newList.length === 0) {
newList.push(Configuration.defaultMainView)
}
mainViewSettings.filterList = newList
}
}
}
MouseArea {
id: dndArea
anchors.fill: parent
preventStealing: configListView.dragging
property int dragOffset: 0
onPressAndHold: {
mouse.accepted = true
var mouseYInListView = configListView.contentItem.mapFromItem(dndArea, mouseX, mouseY).y;
configListView.draggingIndex = configListView.indexAt(mouseX, mouseYInListView)
var item = mainMenuModel.get(configListView.draggingIndex)
print("draggingIndex", configListView.draggingIndex)
dndItem.text = item.displayName
dndItem.iconName = item.icon
var visualItem = configListView.itemAt(mouseX, mouseYInListView)
dndItem.checked = visualItem.checked
dndArea.dragOffset = configListView.mapToItem(visualItem, mouseX, mouseY).y
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
}
onMouseYChanged: {
if (configListView.dragging) {
var mouseYInListView = configListView.contentItem.mapFromItem(dndArea, mouseX, mouseY).y;
var indexUnderMouse = configListView.indexAt(mouseX, mouseYInListView - dndArea.dragOffset / 2)
if (indexUnderMouse < 0) {
return;
}
indexUnderMouse = Math.min(Math.max(0, indexUnderMouse), configListView.count - 1)
if (configListView.draggingIndex !== indexUnderMouse) {
print("moving to", indexUnderMouse)
PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
mainMenuModel.move(configListView.draggingIndex, indexUnderMouse, 1)
configListView.draggingIndex = indexUnderMouse;
}
}
}
onReleased: {
print("released!")
var mouseYInListView = configListView.contentItem.mapFromItem(dndArea, mouseX, mouseY).y;
var clickedIndex = configListView.indexAt(mouseX, mouseYInListView)
var item = mainMenuModel.get(clickedIndex)
var isEnabled = mainViewSettings.filterList.indexOf(item.name) >= 0;
if (!configListView.dragging) {
var newList = []
for (var i = 0; i < mainMenuModel.count; i++) {
var entry = mainMenuModel.get(i).name;
if (entry === item.name) {
if (!isEnabled) {
newList.push(item.name)
}
} else {
if (mainViewSettings.filterList.indexOf(entry) >= 0) {
newList.push(entry)
}
}
}
if (newList.length === 0) {
newList.push(Configuration.defaultMainView)
}
mainViewSettings.filterList = newList
}
configListView.draggingIndex = -1;
var newSortOrder = []
for (var i = 0; i < mainMenuModel.count; i++) {
newSortOrder.push(mainMenuModel.get(i).name)
}
mainViewSettings.sortOrder = newSortOrder;
}
// Timer {
// id: scroller
// interval: 2
// repeat: true
// running: direction != 0
// property int direction: {
// if (!configListView.dragging) {
// return 0;
// }
// return dndArea.mouseY < 50 ? -2 : dndArea.mouseY > dndArea.height - 50 ? 2 : 0
// }
// onTriggered: {
// configListView.contentY = Math.min(Math.max(0, configListView.contentY + direction), configListView.contentHeight - configListView.height)
// }
// }
}
NymeaItemDelegate {
id: dndItem
visible: configListView.dragging
y: dndArea.mouseY - dndArea.dragOffset
width: configListView.width
progressive: false
additionalItem: CheckBox {
checked: dndItem.checked
anchors.verticalCenter: parent.verticalCenter
}
}
}
// NumberAnimation {
// target: configOverlay
// property: "scale"
// duration: 200
// easing.type: Easing.InOutQuad
// from: 2
// to: 1
// running: true
// }
// NumberAnimation {
// target: configOverlay
// property: "opacity"
// duration: 200
// easing.type: Easing.InOutQuad
// from: 0
// to: 1
// running: true
// }
// ListView {
// id: configListView
// model: mainMenuModel
// width: parent.width
// height: parent.height / 3
// anchors.centerIn: parent
// orientation: ListView.Horizontal
// moveDisplaced: Transition {
// NumberAnimation { properties: "x,y"; duration: 200 }
// }
// property int delegateWidth: width / 3
// property bool dragging: draggingIndex >= 0
// property int draggingIndex : -1
// MouseArea {
// id: dndArea
// anchors.fill: parent
// preventStealing: configListView.dragging
// property int dragOffset: 0
// onPressAndHold: {
// mouse.accepted = true
// var mouseXInListView = configListView.contentItem.mapFromItem(dndArea, mouseX, mouseY).x;
// configListView.draggingIndex = configListView.indexAt(mouseXInListView, mouseY)
// var item = mainMenuModel.get(configListView.draggingIndex)
// dndItem.displayName = item.displayName
// dndItem.icon = item.icon
// var visualItem = configListView.itemAt(mouseXInListView, mouseY)
// dndItem.isEnabled = visualItem.isEnabled
// dndArea.dragOffset = configListView.mapToItem(visualItem, mouseX, mouseY).x
// PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackImpact)
// }
// onMouseYChanged: {
// if (configListView.dragging) {
// var mouseXInListView = configListView.contentItem.mapFromItem(dndArea, mouseX, mouseY).x;
// var indexUnderMouse = configListView.indexAt(mouseXInListView - dndArea.dragOffset / 2, mouseY)
// indexUnderMouse = Math.min(Math.max(0, indexUnderMouse), configListView.count - 1)
// if (configListView.draggingIndex !== indexUnderMouse) {
// PlatformHelper.vibrate(PlatformHelper.HapticsFeedbackSelection)
// mainMenuModel.move(configListView.draggingIndex, indexUnderMouse, 1)
// configListView.draggingIndex = indexUnderMouse;
// }
// }
// }
// onReleased: {
// print("released!")
// var mouseXInListView = configListView.contentItem.mapFromItem(dndArea, mouseX, mouseY).x;
// var clickedIndex = configListView.indexAt(mouseXInListView, mouseY)
// var item = mainMenuModel.get(clickedIndex)
// var isEnabled = mainViewSettings.filterList.indexOf(item.name) >= 0;
// if (!configListView.dragging) {
// var newList = []
// for (var i = 0; i < mainMenuModel.count; i++) {
// var entry = mainMenuModel.get(i).name;
// if (entry === item.name) {
// if (!isEnabled) {
// newList.push(item.name)
// }
// } else {
// if (mainViewSettings.filterList.indexOf(entry) >= 0) {
// newList.push(entry)
// }
// }
// }
// if (newList.length === 0) {
// newList.push(Configuration.defaultMainView)
// }
// mainViewSettings.filterList = newList
// }
// configListView.draggingIndex = -1;
// var newSortOrder = []
// for (var i = 0; i < mainMenuModel.count; i++) {
// newSortOrder.push(mainMenuModel.get(i).name)
// }
// mainViewSettings.sortOrder = newSortOrder;
// }
// Timer {
// id: scroller
// interval: 2
// repeat: true
// running: direction != 0
// property int direction: {
// if (!configListView.dragging) {
// return 0;
// }
// return dndArea.mouseX < 50 ? -2 : dndArea.mouseX > dndArea.width - 50 ? 2 : 0
// }
// onTriggered: {
// configListView.contentX = Math.min(Math.max(0, configListView.contentX + direction), configListView.contentWidth - configListView.width)
// }
// }
// }
// delegate: BigTile {
// id: configDelegate
// width: configListView.delegateWidth
// height: configListView.height
// property bool isEnabled: mainViewSettings.filterList.indexOf(model.name) >= 0
// visible: configListView.draggingIndex !== index
// leftPadding: 0
// rightPadding: 0
// topPadding: 0
// bottomPadding: 0
// header: RowLayout {
// id: headerRow
// width: parent.width
// Label {
// text: model.displayName
// Layout.fillWidth: true
// elide: Text.ElideRight
// }
// }
// contentItem: Item {
// Layout.fillWidth: true
// implicitHeight: configListView.height - headerRow.height - Style.margins * 2
// ColorIcon {
// anchors.centerIn: parent
// width: Math.min(parent.width, parent.height) * .6
// height: width
// name: Qt.resolvedUrl("qrc:/icons/" + model.icon + ".svg")
// color: configDelegate.isEnabled ? Style.accentColor : Style.iconColor
// }
// }
// }
// Item {
// id: dndItem
// width: configListView.delegateWidth
// height: configListView.height
// property bool isEnabled: false
// property string displayName: ""
// property string icon: "things"
// visible: configListView.dragging
// x: dndArea.mouseX - dndArea.dragOffset
// onVisibleChanged: {
// if (visible) {
// dragStartAnimation.start();
// }
// }
// NumberAnimation {
// id: dragStartAnimation
// target: dndItem
// property: "scale"
// from: 1
// to: 0.95
// duration: 200
// }
// BigTile {
// id: dndTile
// anchors.fill: parent
// // anchors.margins: app.margins / 2
// Material.elevation: 2
// leftPadding: 0
// rightPadding: 0
// topPadding: 0
// bottomPadding: 0
// header: RowLayout {
// Label {
// text: dndItem.displayName
// }
// }
// contentItem: Item {
// Layout.fillWidth: true
// implicitHeight: configListView.height - header.height
// ColorIcon {
// anchors.centerIn: parent
// width: Math.min(parent.width, parent.height) * .6
// height: width
// name: Qt.resolvedUrl("qrc:/icons/" + dndItem.icon + ".svg")
// color: dndItem.isEnabled ? Style.accentColor : Style.iconColor
// }
// }
// }
// }
// }
}
}
Component {
id: connectionDialogComponent
NymeaDialog {
id: connectionDialog
title: engine.jsonRpcClient.currentHost.name
standardButtons: Dialog.NoButton
headerIcon: {
switch (engine.jsonRpcClient.currentConnection.bearerType) {
case Connection.BearerTypeLan:
case Connection.BearerTypeWan:
if (engine.jsonRpcClient.availableBearerTypes & NymeaConnection.BearerTypeEthernet != NymeaConnection.BearerTypeNone) {
return "qrc:/icons/connections/network-wired.svg"
}
return "qrc:/icons/connections/network-wifi.svg";
case Connection.BearerTypeBluetooth:
return "qrc:/icons/connections/bluetooth.svg";
case Connection.BearerTypeCloud:
return "qrc:/icons/connections/cloud.svg"
case Connection.BearerTypeLoopback:
return "qrc:/icons/connections/network-wired.svg"
}
return ""
}
Label {
Layout.fillWidth: true
text: qsTr("Connected to")
font.pixelSize: app.smallFont
elide: Text.ElideRight
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
horizontalAlignment: Text.AlignHCenter
}
Label {
Layout.fillWidth: true
text: engine.jsonRpcClient.currentHost.name
elide: Text.ElideRight
wrapMode: Text.WrapAtWordBoundaryOrAnywhere
horizontalAlignment: Text.AlignHCenter
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: app.margins
}
RowLayout {
ColumnLayout {
Label {
Layout.fillWidth: true
text: engine.jsonRpcClient.currentHost.uuid
font.pixelSize: app.smallFont
elide: Text.ElideRight
color: Material.color(Material.Grey)
// horizontalAlignment: Text.AlignHCenter
}
Label {
Layout.fillWidth: true
text: engine.jsonRpcClient.currentConnection.url
font.pixelSize: app.smallFont
elide: Text.ElideRight
color: Material.color(Material.Grey)
// horizontalAlignment: Text.AlignHCenter
}
}
ColorIcon {
Layout.preferredHeight: Style.iconSize
Layout.preferredWidth: Style.iconSize
name: engine.jsonRpcClient.currentConnection.secure ? "qrc:/icons/lock-closed.svg" : "qrc:/icons/lock-open.svg"
MouseArea {
anchors.fill: parent
onClicked: {
var component = Qt.createComponent(Qt.resolvedUrl("connection/CertificateDialog.qml"));
var popup = component.createObject(app, {serverUuid: engine.jsonRpcClient.serverUuid, issuerInfo: engine.jsonRpcClient.certificateIssuerInfo});
popup.open();
}
}
}
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: app.margins
}
RowLayout {
Layout.fillWidth: true
Button {
id: disconnectButton
text: qsTr("Disconnect")
Layout.preferredWidth: Math.max(cancelButton.implicitWidth, disconnectButton.implicitWidth)
onClicked: {
engine.jsonRpcClient.disconnectFromHost();
}
}
Item {
Layout.fillWidth: true
}
Button {
id: cancelButton
text: qsTr("OK")
Layout.preferredWidth: Math.max(cancelButton.implicitWidth, disconnectButton.implicitWidth)
onClicked: connectionDialog.close()
}
}
}
}
}