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 <[email protected]> --- 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 _______________________________________________ subsurface mailing list [email protected] http://lists.subsurface-divelog.org/cgi-bin/mailman/listinfo/subsurface
