[PATCH 2/3] QML UI: add planner page
Rick Walsh
rickmwalsh at gmail.com
Wed Feb 10 02:33:33 PST 2016
This page provides a basic planner input and output page with editable
ListView items for cylinders, waypoints and deco stops.
It is not yet connected to the core planner c++ and c code. The ListModels
should be replaced with Qt models, sharings as much as possible with existing
models.
Signed-off-by: Rick Walsh <rickmwalsh at gmail.com>
---
qt-mobile/qml/Planner.qml | 476 +++++++++++++++++++++++++++++++++++++
qt-mobile/qml/mobile-resources.qrc | 1 +
2 files changed, 477 insertions(+)
create mode 100644 qt-mobile/qml/Planner.qml
diff --git a/qt-mobile/qml/Planner.qml b/qt-mobile/qml/Planner.qml
new file mode 100644
index 0000000..212218f
--- /dev/null
+++ b/qt-mobile/qml/Planner.qml
@@ -0,0 +1,476 @@
+import QtQuick 2.4
+import QtQuick.Controls 1.2
+import QtQuick.Layouts 1.2
+import QtQuick.Window 2.2
+import QtQuick.Dialogs 1.2
+import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
+import org.subsurfacedivelog.mobile 1.0
+
+MobileComponents.Page {
+ id: planner
+ objectName: "DivePlanner"
+ color: MobileComponents.Theme.viewBackgroundColor
+
+ property double cylinderSizeNew
+ property double cylinderWorkingPressureNew
+ property string cylinderDescriptionNew
+ property double pcO2New
+ property double pcHeNew
+ property string pressureStartNew
+ property double pressureEndNew
+ property double gasDepthNew
+ property bool newCylinder
+ property int cylinderIndex
+ property int stdColWidth: content.width * 0.2
+ property int horizontalPadding: MobileComponents.Units.smallSpacing
+ property int labelRowHeight: MobileComponents.Units.gridUnit * 1.6
+ property int inputRowHeight: MobileComponents.Units.gridUnit * 1.8
+
+
+ ScrollView {
+ anchors.fill: parent
+ Flickable {
+ id: flick
+ anchors.fill: parent
+ contentHeight: content.height
+ interactive: contentHeight > height
+ clip: true
+
+ Column {
+ id: content
+ anchors {
+ left: parent.left
+ leftMargin: MobileComponents.Units.gridUnit * 0.5
+ right: parent.right
+ }
+
+ MobileComponents.Heading {
+ text: "Dive planner"
+ level: 2
+ }
+
+ ListView {
+ id: inputCylinders
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ height: headerItem.height + footerItem.height + labelRowHeight * Math.min(inputCylinders.count, 3)
+ model: gasModel
+ currentIndex: -1
+ delegate: cylinderListDelegate
+ boundsBehavior: Flickable.StopAtBounds
+ maximumFlickVelocity: parent.height * 5
+ cacheBuffer: parent.height * 5
+ focus: true
+ clip: true
+ header: MobileComponents.Heading {
+ height: paintedHeight + MobileComponents.Units.gridUnit / 2
+ verticalAlignment: Text.AlignBottom
+ text: "Cylinders and gases"
+ level: 4
+ }
+ footer: Button {
+ text: "Add cylinder"
+ action: addCylinder
+ }
+ }
+
+ ListView {
+ id: inputWaypoints
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ height: headerItem.height + inputRowHeight * inputPointsModel.count + MobileComponents.Units.smallSpacing
+ model: inputPointsModel
+ currentIndex: -1
+ delegate: waypointsDelegate
+ boundsBehavior: Flickable.StopAtBounds
+ maximumFlickVelocity: parent.height * 5
+ interactive: false //flickable unnecessary as listview resizes to fit. Disabling allows scrolling the whole page
+ cacheBuffer: parent.height * 5
+ focus: true
+ clip: true
+
+ header: Column {
+ height: childrenRect.height
+ MobileComponents.Heading {
+ height: paintedHeight + MobileComponents.Units.gridUnit / 2
+ Layout.bottomMargin: MobileComponents.Units.largeSpacing / 2
+ verticalAlignment: Text.AlignTop
+ text: "Waypoints"
+ level: 4
+ }
+ Row {
+ anchors {
+ left: parent.left
+ leftMargin: horizontalPadding * 2
+ right: parent.right
+ rightMargin: horizontalPadding
+ }
+ Text {
+ text: 'Depth'
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ Text {
+ text: 'Duration'
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ Text {
+ text: 'Runtime'
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ Text {
+ text: 'Gas'
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ }
+ }
+ }
+ ListView {
+ id: decoStops
+ anchors {
+ left: parent.left
+ right: parent.right
+ }
+ height: headerItem.height + inputRowHeight * Math.max(decoStopsModel.count, 8)
+ model: decoStopsModel
+ currentIndex: -1
+ delegate: decoStopsDelegate
+ boundsBehavior: Flickable.StopAtBounds
+ maximumFlickVelocity: parent.height * 5
+ interactive: false //flickable unnecessary as listview resizes to fit. Disabling allows scrolling the whole page
+ cacheBuffer: parent.height * 5
+ focus: true
+ clip: true
+
+ header: Column {
+ height: childrenRect.height
+ MobileComponents.Heading {
+ height: paintedHeight + MobileComponents.Units.gridUnit / 2
+ Layout.bottomMargin: MobileComponents.Units.largeSpacing / 2
+ verticalAlignment: Text.AlignTop
+ text: "Decompression stops"
+ level: 4
+ }
+ Row {
+ anchors {
+ left: parent.left
+ leftMargin: horizontalPadding * 2
+ right: parent.right
+ rightMargin: horizontalPadding
+ }
+ Text {
+ text: 'Depth'
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ Text {
+ text: 'Duration'
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ Text {
+ text: 'Runtime'
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ Text {
+ text: 'Gas'
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ }
+ }
+ }
+ Item {
+ width: parent.width
+ Layout.fillHeight: true
+ }
+ }
+ }
+ }
+
+// TODO use Qt models instead so we can interact directly with core planner code
+ ListModel {
+ id: gasModel
+
+ ListElement {
+ cylinderType: "D12.2 232 bar"
+ cylinderSizeMl: 24400
+ gasName: "air"
+ gasDepthMm: 66000
+ o2Pm: 210
+ hePm: 0
+ }
+ }
+ ListModel {
+ id: inputPointsModel
+ ListElement {
+ property double depth: 15000
+ property int runtime: 1200
+ property int gasIndex: 0
+ }
+ }
+
+// Dummy model for the time being
+ ListModel {
+ id: decoStopsModel
+ ListElement {
+ property double depth: 15000
+ property int runtime: 3000
+ property int gasIndex: 0
+ }
+ }
+ Component {
+ id: cylinderListDelegate
+ MobileComponents.ListItemWithActions {
+ enabled: true
+ width: parent.width
+
+ Item {
+ width: parent.width - MobileComponents.Units.gridUnit
+ height: labelRowHeight - MobileComponents.Units.smallSpacing * 2
+
+ Row {
+ anchors {
+ left: parent.left
+ leftMargin: horizontalPadding
+ right: parent.right
+ rightMargin: horizontalPadding
+ }
+ MobileComponents.Label {
+ text: cylinderType
+ width: stdColWidth * 1.5
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: 'Gas: '
+ width: stdColWidth * 0.4
+ Layout.alignment: Qt.AlignRight
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: gasName
+ width: stdColWidth * 0.8
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: 'Switch at: '
+ width: stdColWidth
+ Layout.alignment: Qt.AlignRight
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: 0.001 * gasDepthMm + "m"
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ }
+ }
+ function cylinderEdit() {
+ cylinderIndex = model.index
+ newCylinder = false
+ cylinderDetailsWindow.isPlanner = true
+ cylinderDetailsWindow.cylinderDescriptionText = gasModel.get(cylinderIndex).cylinderType
+ cylinderDetailsWindow.cylinderSizeText = 0.001 * gasModel.get(cylinderIndex).cylinderSizeMl
+ cylinderDetailsWindow.pcO2Text = 0.1 * gasModel.get(cylinderIndex).o2Pm
+ cylinderDetailsWindow.pcHeText = 0.1 * gasModel.get(cylinderIndex).hePm
+ cylinderDetailsWindow.setGas(gasModel.get(cylinderIndex).cylinderType)
+ stackView.push(cylinderDetailsWindow)
+ }
+
+ onClicked: {
+ cylinderEdit()
+ }
+ actions: [
+ Action {
+ iconName: "trash-empty"
+ onTriggered: {
+ gasModel.remove(model.index)
+ }
+ },
+ Action {
+ iconName: "document-edit"
+ onTriggered: {
+ cylinderEdit()
+ }
+ }
+
+ ]
+ }
+ }
+
+// TODO: we shouldn't permit negative durations - either sort model by runtime (in c++) or have data validation
+ Component {
+ id: waypointsDelegate
+ MobileComponents.ListItemWithActions {
+ enabled: true
+ width: parent.width
+
+ Item {
+ width: parent.width - MobileComponents.Units.gridUnit
+ height: inputRowHeight - MobileComponents.Units.smallSpacing * 2
+
+ Row {
+ anchors {
+ left: parent.left
+ leftMargin: horizontalPadding
+ right: parent.right
+ rightMargin: horizontalPadding
+ }
+ TextField {
+ id: txtDepth
+ inputMethodHints: Qt.ImhDigitsOnly
+ validator: DoubleValidator {bottom: 0; top: 200; decimals:0; notation: DoubleValidator.StandardNotation}
+ horizontalAlignment: TextInput.AlignHCenter
+ width: stdColWidth
+ font.pointSize: subsurfaceTheme.smallPointSize
+ onEditingFinished: {
+ inputPointsModel.set(model.index, {depth: 1000 * Number(text)})
+ if (model.index == inputPointsModel.count - 1)
+ inputPointsModel.append({})
+ }
+ }
+ MobileComponents.Label {
+ id: txtDuration
+ text: {
+ if (inputPointsModel.get(model.index).runtime > 0) {
+ if (model.index == 0)
+ return inputPointsModel.get(model.index).runtime / 60
+ else if (inputPointsModel.get(model.index - 1).runtime > 0)
+ return (inputPointsModel.get(model.index).runtime
+ - inputPointsModel.get(model.index - 1).runtime) / 60
+ } else {
+ return undefined
+ }
+ }
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ TextField {
+ id: txtRuntime
+ inputMethodHints: Qt.ImhDigitsOnly
+ validator: DoubleValidator {bottom: 0; top: 200; decimals:0; notation: DoubleValidator.StandardNotation}
+ horizontalAlignment: TextInput.AlignHCenter
+ width: stdColWidth
+ font.pointSize: subsurfaceTheme.smallPointSize
+ onEditingFinished: {
+ inputPointsModel.set(model.index, {runtime: 60 * Number(text)})
+ if (model.index == inputPointsModel.count - 1)
+ inputPointsModel.append({})
+ }
+ }
+ MobileComponents.Label {
+ text: "Gas"
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ }
+ }
+ actions: [
+ Action {
+ iconName: "trash-empty"
+ onTriggered: {
+ print("delete waypoint")
+ inputPointsModel.remove(model.index)
+ }
+ }
+ ]
+ }
+ }
+ Component {
+ id: decoStopsDelegate
+ MobileComponents.ListItem {
+ enabled: true
+ width: parent.width
+
+ Item {
+ width: parent.width - MobileComponents.Units.gridUnit
+ height: labelRowHeight - MobileComponents.Units.smallSpacing * 2
+
+ Row {
+ anchors {
+ left: parent.left
+ leftMargin: horizontalPadding
+ right: parent.right
+ rightMargin: horizontalPadding
+ }
+ MobileComponents.Label {
+ id: txtDecoDepth
+ text: depth * 0.001
+ horizontalAlignment: TextInput.AlignHCenter
+ width: stdColWidth
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ id: txtDuration
+ horizontalAlignment: Text.AlignHCenter
+ width: stdColWidth
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ id: txtDecoRuntime
+ text: runtime / 60
+ horizontalAlignment: TextInput.AlignHCenter
+ width: stdColWidth
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: gasModel.get(gasIndex).gasName
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ }
+ }
+ }
+ }
+ Action {
+ id: addCylinder
+ onTriggered: {
+ newCylinder = true
+ cylinderDetailsWindow.isPlanner = true
+ stackView.push(cylinderDetailsWindow)
+ }
+ }
+ CylinderDetailsEdit {
+ id: cylinderDetailsWindow
+ visible: false
+ onCylinderOk: {
+ if (newCylinder) {
+ gasModel.append({
+ cylinderType: cylinderDetailsWindow.cylinderDescriptionText,
+ o2Pm: cylinderDetailsWindow.o2Pm,
+ hePm: cylinderDetailsWindow.hePm,
+ gasName: cylinderDetailsWindow.gasNameText,
+ gasDepthMm: Number(cylinderDetailsWindow.gasDepthMm)
+ })
+ } else {
+ gasModel.set(cylinderIndex, {
+ cylinderType: cylinderDetailsWindow.cylinderDescriptionText,
+ o2Pm: cylinderDetailsWindow.o2Pm,
+ hePm: cylinderDetailsWindow.hePm,
+ gasName: cylinderDetailsWindow.gasNameText,
+ gasDepthMm: Number(cylinderDetailsWindow.gasDepthMm)
+ })
+ }
+ }
+ }
+}
diff --git a/qt-mobile/qml/mobile-resources.qrc b/qt-mobile/qml/mobile-resources.qrc
index 77ec4ff..eb8fdc5 100644
--- a/qt-mobile/qml/mobile-resources.qrc
+++ b/qt-mobile/qml/mobile-resources.qrc
@@ -9,6 +9,7 @@
<file>DiveDetailsEdit.qml</file>
<file>DiveDetailsView.qml</file>
<file>DownloadFromDiveComputer.qml</file>
+ <file>Planner.qml</file>
<file>CylinderDetailsEdit.qml</file>
<file>GpsList.qml</file>
<file>Log.qml</file>
--
2.5.0
More information about the subsurface
mailing list