diff --git a/qml/Controls/ComboBox.qml b/qml/Controls/ComboBox.qml index 07cbab5..cd831f7 100644 --- a/qml/Controls/ComboBox.qml +++ b/qml/Controls/ComboBox.qml @@ -46,7 +46,8 @@ ComboBox { } onPaint: { - context.reset(); + var context = getContext("2d") + context.reset() if (control.down) { context.moveTo(0, height); @@ -77,7 +78,10 @@ ComboBox { background: Rectangle { implicitWidth: 120 implicitHeight: 46 - color: control.down ? Palette.selectedBackgroundColor : Palette.backgroundColor + color: control.enabled + ? (control.down ? Palette.selectedBackgroundColor : Palette.backgroundColor) + : Palette.hoveredBackgroundColor + border.color: Palette.borderColor border.width: 1 radius: 5 diff --git a/qml/Controls/DialogBackground.qml b/qml/Controls/DialogBackground.qml new file mode 100644 index 0000000..2a0015e --- /dev/null +++ b/qml/Controls/DialogBackground.qml @@ -0,0 +1,8 @@ +import QtQuick 2.12 + +import Utils 1.0 + +Rectangle { + color: Palette.screenBackgroundColor + radius: 6 +} diff --git a/qml/Controls/DialogHeader.qml b/qml/Controls/DialogHeader.qml new file mode 100644 index 0000000..f7eddf6 --- /dev/null +++ b/qml/Controls/DialogHeader.qml @@ -0,0 +1,75 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import Controls 1.0 as Controls +import Utils 1.0 + +Rectangle { + color: Qt.darker(Palette.hoveredBackgroundColor, 1.05) + height: 50 + radius: 6 + + property Dialog dialog: undefined + + RowLayout { + anchors.fill: parent + + Controls.SubtitleLabel { + text: root.title + leftPadding: 20 + topPadding: 0 + bottomPadding: 0 + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + Layout.preferredHeight: parent.height + + MouseArea { + anchors.fill: parent + + property int lastX: 0 + property int lastY: 0 + + onPressed: { + lastX = mouse.x + lastY = mouse.y + } + + onPositionChanged:{ + var deltaX = mouse.x - lastX + dialog.x += deltaX + lastX = mouse.x - deltaX + + var deltaY = mouse.y - lastY + dialog.y += deltaY + lastY = mouse.y - deltaY + } + } + } + + Image { + id: closeImage + source: "qrc:/Icons/close.svg" + sourceSize.width: 36 + sourceSize.height: 36 + Layout.rightMargin: 10 + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + + onPressed: { + closeImage.sourceSize.width = 32 + closeImage.sourceSize.height = 32 + } + onReleased: { + closeImage.sourceSize.width = 36 + closeImage.sourceSize.height = 36 + } + onClicked: { + root.close() + } + } + } + } +} diff --git a/qml/Controls/qmldir b/qml/Controls/qmldir index 0ebc71e..b0b9cb5 100644 --- a/qml/Controls/qmldir +++ b/qml/Controls/qmldir @@ -20,3 +20,5 @@ TabButton 1.0 TabButton.qml TabBar 1.0 TabBar.qml ChartView 1.0 ChartView.qml TextArea 1.0 TextArea.qml +DialogHeader 1.0 DialogHeader.qml +DialogBackground 1.0 DialogBackground.qml diff --git a/qml/Icons/close.svg b/qml/Icons/close.svg new file mode 100644 index 0000000..f363dcc --- /dev/null +++ b/qml/Icons/close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/qml/Icons/connection.svg b/qml/Icons/connection.svg new file mode 100644 index 0000000..94da06a --- /dev/null +++ b/qml/Icons/connection.svg @@ -0,0 +1,3 @@ + + + diff --git a/qml/Icons/info.svg b/qml/Icons/info.svg new file mode 100644 index 0000000..eebe6e9 --- /dev/null +++ b/qml/Icons/info.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/qml/Icons/warning.svg b/qml/Icons/warning.svg new file mode 100644 index 0000000..963664f --- /dev/null +++ b/qml/Icons/warning.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/qml/MainWindow.qml b/qml/MainWindow.qml index bed6f63..2daeacb 100644 --- a/qml/MainWindow.qml +++ b/qml/MainWindow.qml @@ -1,6 +1,7 @@ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 +import QtGraphicalEffects 1.0 import Controls 1.0 as Controls import Screens 1.0 as Screens @@ -106,7 +107,7 @@ ApplicationWindow { {"text": qsTr("Visualization"), "icon": "qrc:/Icons/visualization.svg"}, {"text": qsTr("History"), "icon": "qrc:/Icons/history.svg"}, {"text": qsTr("BMS service"), "icon": "qrc:/Icons/bms-service.svg"}, - {"text": qsTr("Exit"), "icon": "qrc:/Icons/exit.svg"}, +// {"text": qsTr("Exit"), "icon": "qrc:/Icons/exit.svg"}, ] delegate: ItemDelegate { @@ -151,6 +152,55 @@ ApplicationWindow { Layout.fillHeight: true Layout.fillWidth: true } + + ItemDelegate { + leftPadding: pane.minimized ? 0 : 40 + onClicked: connectionDialog.open() + + contentItem: RowLayout { + spacing: pane.minimized ? 0 : 25 + height: 50 + + Item { + visible: pane.minimized + Layout.fillWidth: true + } + + Image { + source: "qrc:/Icons/connection.svg" + sourceSize.width: 24 + sourceSize.height: 24 + Layout.alignment: Qt.AlignCenter + + ColorOverlay { + anchors.fill: parent + source: parent + color: "#FFFFFF" + } + } + + Label { + text: qsTr("Connection") + font.weight: Font.Bold + color: Palette.alternativeTextColor + visible: !pane.minimized + } + + Item { + Layout.fillWidth: true + } + } + + background: Rectangle { + color: parent.down ? Palette.pressedButtonColor : Palette.buttonColor + } + + Layout.fillWidth: true + } + + Item { + Layout.preferredHeight: 40 + } } PropertyAnimation { @@ -174,7 +224,7 @@ ApplicationWindow { qsTr("Cell monitor"), qsTr("BMS settings"), qsTr("Visualization"), - qsTr("History"), + qsTr("Information output"), qsTr("Terminal"), qsTr("Exit"), ] @@ -189,6 +239,45 @@ ApplicationWindow { Layout.fillWidth: true } + RowLayout { + ColumnLayout { + RowLayout { + Controls.ContentLabel { + id: connectionStatusLabel + text: qsTr("Disconnected") + Layout.alignment: Qt.AlignRight + } + + Controls.AvailabilityIndicator { + id: connectionStatusIndicator + enabled: false + Layout.alignment: Qt.AlignCenter + } + + Controls.ContentLabel { + text: qsTr("V1.3") + Layout.alignment: Qt.AlignRight + } + + Layout.alignment: Qt.AlignRight + } + + RowLayout { + Controls.ContentLabel { + text: qsTr("Serial number") + ":" + Layout.alignment: Qt.AlignRight + } + + Controls.SubtitleLabel { + text: qsTr("9999997") + Layout.alignment: Qt.AlignRight + } + + Layout.alignment: Qt.AlignRight + } + } + } + Layout.leftMargin: 45 Layout.rightMargin: 45 } @@ -215,7 +304,7 @@ ApplicationWindow { Screens.VisualizationScreen { } - Screens.BmsSettingsScreen { + Screens.DebugInformationScreen { } Screens.BmsServiceScreen { @@ -224,8 +313,84 @@ ApplicationWindow { } } - Screens.ConnectionScreen { - id: connectionScreen + Connections { + target: BmsInterface + + onPortConnectedChanged: { + connectionStatusLabel.text = BmsInterface.isPortConnected() ? qsTr("Connected") : qsTr("Disconnected") + connectionStatusIndicator.enabled = BmsInterface.isPortConnected() + } + + onMessageDialog: { + if (!messageDialog.visible) { + messageDialog.title = title + messageDialog.description = msg + messageDialog.good = isGood + messageDialog.open() + } else { + messageDialog.queue.push({"title": title, "description": msg, "good": isGood}) + } + } + + onStatusMessage: { + for (var i = 0; i < statusPopup.filteredStatuses.length; ++i) { + if (msg === statusPopup.filteredStatuses[i]) { + return + } + } + + if (!statusPopup.visible) { + statusPopup.text = msg + statusPopup.good = isGood + statusPopup.open() + hideStatusTimer.start() + } else { + if (statusPopup.text !== msg) { + statusPopup.queue.push({"text": msg, "good": isGood}) + } + } + } + } + + Screens.ConnectionDialog { + id: connectionDialog + } + + Screens.MessageDialog { + id: messageDialog + property var queue: [] + onClosed: { + if (queue.length > 0) { + var message = queue.pop() + messageDialog.title = message.title + messageDialog.description = message.description + messageDialog.good = message.good + messageDialog.open() + } + } + } + + Screens.StatusPopup { + id: statusPopup + + property var filteredStatuses: [qsTr("BMS configuration updated")] + property var queue: [] + + onClosed: { + if (queue.length > 0) { + var message = queue.pop() + statusPopup.text = message.text + statusPopup.good = message.good + statusPopup.open() + hideStatusTimer.start() + } + } + } + + Timer { + id: hideStatusTimer + interval: 3000 + onTriggered: statusPopup.close() } background: Rectangle { @@ -233,9 +398,9 @@ ApplicationWindow { } Component.onCompleted: { - BmsInterface.bmsConfig().loadParamsXml("://res/config.xml") - BmsInterface.infoConfig().loadParamsXml("://res/info.xml") +// BmsInterface.bmsConfig().loadParamsXml("://res/config.xml") +// BmsInterface.infoConfig().loadParamsXml("://res/info.xml") - connectionScreen.open() + connectionDialog.open() } } diff --git a/qml/Screens/ConnectionDialog.qml b/qml/Screens/ConnectionDialog.qml new file mode 100644 index 0000000..7c7cbaf --- /dev/null +++ b/qml/Screens/ConnectionDialog.qml @@ -0,0 +1,86 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import Controls 1.0 as Controls +import Cubo 1.0 +import Utils 1.0 + +Dialog { + id: root + title: qsTr("Connection screen") + width: 400 + height: 320 + modal: true + closePolicy: Popup.CloseOnEscape + + + header: Controls.DialogHeader { + dialog: root + } + + contentItem: Rectangle { + color: Palette.screenBackgroundColor + + ColumnLayout { + anchors.fill: parent + anchors.leftMargin: 45 + anchors.rightMargin: 45 + spacing: 20 + + Item { + Layout.fillHeight: true + } + + Label { + text: qsTr("Select serial port") + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom + } + + Controls.ComboBox { + id: serialBox + model: BmsInterface.serialPortNames() + Layout.fillWidth: true + Layout.alignment: Qt.AlignCenter + } + + Controls.Button { + id: connectButton + text: qsTr("Connect") + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter | Qt.AlignTop + onClicked: { + if (BmsInterface.isPortConnected()) { + BmsInterface.disconnectPort() + } else { + var currentPort = BmsInterface.serialPortNames()[serialBox.currentIndex] + if (BmsInterface.connectSerial(currentPort)) { + root.close() + } + } + } + } + + Item { + Layout.fillHeight: true + } + } + } + + background: Controls.DialogBackground {} + + onVisibleChanged: { + x = Qt.binding(function() { return (parent.width - width) / 2}) + y = Qt.binding(function() { return (parent.height - height) / 2}) + serialBox.model = BmsInterface.serialPortNames() + } + + Connections { + target: BmsInterface + onPortConnectedChanged: { + connectButton.text = BmsInterface.isPortConnected() ? qsTr("Disconnect") : qsTr("Connect") + serialBox.enabled = !BmsInterface.isPortConnected() + } + } +} diff --git a/qml/Screens/ConnectionScreen.qml b/qml/Screens/ConnectionScreen.qml deleted file mode 100644 index 335b83f..0000000 --- a/qml/Screens/ConnectionScreen.qml +++ /dev/null @@ -1,49 +0,0 @@ -import QtQuick 2.12 -import QtQuick.Controls 2.12 -import QtQuick.Dialogs 1.2 -import QtQuick.Layouts 1.12 - -import Controls 1.0 as Controls -import Cubo 1.0 -import Utils 1.0 - -Dialog { - id: root - title: qsTr("Connection Screen") - width: 300 - height: 300 - standardButtons: Dialog.NoButton - - contentItem: Rectangle { - color: Palette.screenBackgroundColor - - ColumnLayout { - anchors.fill: parent - anchors.margins: 45 - - Label { - text: qsTr("Select serial port") - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom - } - - Controls.ComboBox { - id: serialBox - model: BmsInterface.serialPortNames() - Layout.fillWidth: true - Layout.alignment: Qt.AlignCenter - } - - Controls.Button { - text: qsTr("Connect") - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter | Qt.AlignTop - onClicked: { - var currentPort = BmsInterface.serialPortNames()[serialBox.currentIndex] - if (BmsInterface.connectSerial(currentPort)) - root.close() - } - } - } - } -} diff --git a/qml/Screens/DebugInformationScreen.qml b/qml/Screens/DebugInformationScreen.qml new file mode 100644 index 0000000..6c73a46 --- /dev/null +++ b/qml/Screens/DebugInformationScreen.qml @@ -0,0 +1,35 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import Controls 1.0 as Controls +import Cubo 1.0 + +ColumnLayout { + spacing: 20 + + Controls.Frame { + ScrollView { + anchors.fill: parent + Controls.TextArea { + id: outputArea + } + } + Layout.fillWidth: true + Layout.fillHeight: true + } + + Controls.Button { + text: qsTr("Clear") + Layout.preferredWidth: 120 + onClicked: outputArea.clear() + } + + Connections { + target: BmsInterface + + onStatusMessage: { + outputArea.append(msg) + } + } +} diff --git a/qml/Screens/MessageDialog.qml b/qml/Screens/MessageDialog.qml new file mode 100644 index 0000000..198f8d0 --- /dev/null +++ b/qml/Screens/MessageDialog.qml @@ -0,0 +1,65 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 + +import Controls 1.0 as Controls +import Utils 1.0 + +Dialog { + id: root + width: 500 + height: 350 + modal: true + closePolicy: Popup.CloseOnEscape + bottomPadding: 0 + + property string description: "" + property bool good: true + + header: Controls.DialogHeader { + dialog: root + } + + contentItem: ColumnLayout { + spacing: 20 + + RowLayout { + spacing: 20 + + Image { + source: good ? "qrc:/Icons/info.svg" : "qrc:/Icons/warning.svg" + sourceSize.width: 52 + sourceSize.height: 48 + } + + Controls.SubtitleLabel { + text: root.description + maximumLineCount: 10 + wrapMode: Text.Wrap + Layout.fillWidth: true + } + + Layout.fillWidth: true + Layout.fillHeight: true + } + + RowLayout { + Controls.Button { + text: qsTr("Ok") + Layout.alignment: Qt.AlignCenter + Layout.preferredWidth: 100 + onClicked: root.close() + } + + Layout.alignment: Qt.AlignCenter + Layout.fillWidth: true + } + } + + background: Controls.DialogBackground {} + + onVisibleChanged: { + x = Qt.binding(function() { return (parent.width - width) / 2}) + y = Qt.binding(function() { return (parent.height - height) / 2}) + } +} diff --git a/qml/Screens/StatusPopup.qml b/qml/Screens/StatusPopup.qml new file mode 100644 index 0000000..5f5db03 --- /dev/null +++ b/qml/Screens/StatusPopup.qml @@ -0,0 +1,48 @@ +import QtQuick 2.12 +import QtQuick.Controls 2.12 + +import Controls 1.0 as Controls +import Utils 1.0 + +Popup { + id: root + x: (parent.width - width) / 2 + y: parent.height - height - 20 + closePolicy: Popup.NoAutoClose + + property string text: "" + property bool good: true + + Controls.SubtitleLabel { + id: label + text: root.text + maximumLineCount: 10 + wrapMode: Text.Wrap + color: Palette.alternativeTextColor + } + + background: Rectangle { + radius: 6 + color: good ? Palette.alternativeBackgroundColor : Palette.invalidColor + border.width: 1 + border.color: Palette.borderColor + } + + enter: Transition { + NumberAnimation { + property: "opacity" + from: 0.0 + to: 1.0 + duration: 300 + } + } + + exit: Transition { + NumberAnimation { + property: "opacity" + from: 1.0 + to: 0.0 + duration: 2500 + } + } +} diff --git a/qml/Screens/qmldir b/qml/Screens/qmldir index fbd6b1f..ea698af 100644 --- a/qml/Screens/qmldir +++ b/qml/Screens/qmldir @@ -1,7 +1,10 @@ module Screens -ConnectionScreen 1.0 ConnectionScreen.qml AkbMonitorScreen 1.0 AkbMonitorScreen.qml CellMonitorScreen 1.0 CellMonitorScreen.qml BmsSettingsScreen 1.0 BmsSettingsScreen.qml VisualizationScreen 1.0 VisualizationScreen.qml BmsServiceScreen 1.0 BmsServiceScreen.qml +DebugInformationScreen 1.0 DebugInformationScreen.qml +ConnectionDialog 1.0 ConnectionDialog.qml +MessageDialog 1.0 MessageDialog.qml +StatusPopup 1.0 StatusPopup.qml diff --git a/qml/Utils/Palette.qml b/qml/Utils/Palette.qml index 5664faf..f13b725 100644 --- a/qml/Utils/Palette.qml +++ b/qml/Utils/Palette.qml @@ -21,4 +21,6 @@ QtObject { property color outlineButtonColor: "#F7F8FC" property color hoveredButtonColor: "#03AC61" property color pressedButtonColor: "#057845" + + property color invalidColor: "#A31C00" } diff --git a/qml/qml_icons.qrc b/qml/qml_icons.qrc index aecde6e..42420ca 100644 --- a/qml/qml_icons.qrc +++ b/qml/qml_icons.qrc @@ -10,5 +10,9 @@ Icons/history.svg Icons/visualization.svg Icons/check-indicator.svg + Icons/connection.svg + Icons/close.svg + Icons/info.svg + Icons/warning.svg diff --git a/qml/qml_items.qrc b/qml/qml_items.qrc index 4803137..5cd7ea5 100644 --- a/qml/qml_items.qrc +++ b/qml/qml_items.qrc @@ -1,7 +1,7 @@ MainWindow.qml - Screens/ConnectionScreen.qml + Screens/ConnectionDialog.qml Screens/AkbMonitorScreen.qml Screens/CellMonitorScreen.qml Controls/ComboBox.qml @@ -32,5 +32,10 @@ Controls/ChartView.qml Screens/BmsServiceScreen.qml Controls/TextArea.qml + Screens/DebugInformationScreen.qml + Controls/DialogHeader.qml + Controls/DialogBackground.qml + Screens/MessageDialog.qml + Screens/StatusPopup.qml