// SPDX-License-Identifier: GPL-2.0 import QtQuick 2.10 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.2 import QtQuick.Controls 2.2 as Controls import org.subsurfacedivelog.mobile 1.0 import org.kde.kirigami 2.4 as Kirigami Item { id: detailsView property real gridWidth: detailsView.width - 2 * Kirigami.Units.gridUnit property real col1Width: gridWidth * 0.40 property real col2Width: gridWidth * 0.30 property real col3Width: gridWidth * 0.30 width: diveDetailsPage.width - diveDetailsPage.leftPadding - diveDetailsPage.rightPadding height: divePlate.implicitHeight + bottomLayout.implicitHeight + Kirigami.Units.iconSizes.large Rectangle { z: 99 color: subsurfaceTheme.textColor opacity: 0.3 width: Kirigami.Units.smallSpacing/4 anchors { right: parent.right top: parent.top bottom: parent.bottom } } Item { id: divePlate width: parent.width - Kirigami.Units.gridUnit height: childrenRect.height - Kirigami.Units.smallSpacing anchors.left: parent.left Controls.Label { id: locationText text: (undefined !== location && "" !== location) ? location : qsTr("") font.weight: Font.Bold font.pointSize: subsurfaceTheme.titlePointSize wrapMode: Text.WrapAtWordBoundaryOrAnywhere color: subsurfaceTheme.textColor anchors { left: parent.left top: parent.top right: parent.right margins: Math.round(Kirigami.Units.gridUnit / 2) } MouseArea { anchors.fill: parent enabled: gpsDecimal !== "" onClicked: { showMap() mapPage.centerOnDiveSite(diveSite) } } } Row { id: dateRow anchors { left: locationText.left top: locationText.bottom topMargin: Kirigami.Units.smallSpacing bottom: numberText.bottom } TemplateLabel { text: dateTime width: Math.max(locationText.width * 0.45, paintedWidth) font.pointSize: subsurfaceTheme.smallPointSize color: subsurfaceTheme.textColor } // spacer, just in case TemplateLabel { text: " " width: Kirigami.Units.largeSpacing } // let's try to show the depth / duration very compact TemplateLabel { text: depthDuration width: Math.max(Kirigami.Units.gridUnit * 3, paintedWidth) font.pointSize: subsurfaceTheme.smallPointSize color: subsurfaceTheme.textColor } } TemplateLabel { id: numberText text: "#" + number font.pointSize: subsurfaceTheme.smallPointSize color: subsurfaceTheme.textColor anchors { right: parent.right top: locationText.bottom topMargin: Kirigami.Units.smallSpacing } } Row { anchors { left: dateRow.left top: numberText.bottom topMargin: Kirigami.Units.smallSpacing } TemplateLabelSmall { id: ratingText text: qsTr("Rating:") font.pointSize: subsurfaceTheme.smallPointSize color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: ratingText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (rating >= 1) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: ratingText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (rating >= 2) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: ratingText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (rating >= 3) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: ratingText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (rating >= 4) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: ratingText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (rating === 5) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } } Row { anchors { right: numberText.right top: numberText.bottom topMargin: Kirigami.Units.smallSpacing } TemplateLabelSmall { id: visibilityText text: qsTr("Visibility:") font.pointSize: subsurfaceTheme.smallPointSize color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: visibilityText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (viz >= 1) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: visibilityText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (viz >= 2) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: visibilityText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (viz >= 3) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: visibilityText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (viz >= 4) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } Kirigami.Icon { width: height height: subsurfaceTheme.regularPointSize anchors.verticalCenter: visibilityText.verticalCenter anchors.verticalCenterOffset: height * 0.2 source: (viz === 5) ? ":/icons/ic_star.svg" : ":/icons/ic_star_border.svg" color: subsurfaceTheme.textColor } } } GridLayout { id: bottomLayout anchors { top: divePlate.bottom left: parent.left right: parent.right margins: Math.round(Kirigami.Units.gridUnit / 2) topMargin: Kirigami.Units.gridUnit } columns: 3 rowSpacing: Kirigami.Units.smallSpacing * 2 columnSpacing: Kirigami.Units.smallSpacing Rectangle { Layout.fillWidth: true Layout.preferredHeight: Layout.minimumHeight Layout.minimumHeight: width * 0.75 Layout.columnSpan: 3 clip: true QMLProfile { id: qmlProfile visible: !noDive anchors.fill: parent clip: true property real lastScale: 1.0 // final scale at the end of previous pinch Rectangle { color: "transparent" opacity: 0.6 border.width: 1 border.color: subsurfaceTheme.primaryColor anchors.fill: parent } PinchArea { anchors.fill: parent pinch.dragAxis: Pinch.XAndYAxis onPinchStarted: { // it's possible that we thought this was a pan and reduced opacity // before realizing that this is actually a pinch/zoom. So let's reset this // just in case qmlProfile.opacity = 1.0 if (manager.verboseEnabebled) manager.appendTextToLog("pinch started w/ previousScale " + qmlProfile.lastScale) } onPinchUpdated: { if (pinch.scale * qmlProfile.lastScale < 1.0) qmlProfile.lastScale = 1.0 / pinch.scale // this way we never shrink and the changes stay smooth // the underlying widget deals with the scaling, no need to send an update request qmlProfile.scale = pinch.scale * qmlProfile.lastScale if (manager.verboseEnabled) manager.appendTextToLog("pinch updated to scale " + qmlProfile.scale); } onPinchFinished: { // remember the final scale value so we can continue from there next time the user pinches qmlProfile.lastScale = pinch.scale * qmlProfile.lastScale } MouseArea { // we want to pan the profile if we are zoomed in, but we want to immediately // pass the mouse events through to the ListView if we are not. That way you // can swipe through the dive list, even if you happen to swipe the profile property bool isZoomed: qmlProfile.scale - 1.0 > 0.02 // this indicates that we are actually dragging property bool dragging: false // cursor/finger position as we start dragging property real initialX property real initialY // the offset previously used to show the profile property real oldXOffset property real oldYOffset // if the profile is not scaled in, don't start panning // but if the profile is scaled in, then start almost immediately pressAndHoldInterval: isZoomed ? 50 : 50000 // pass events through to the parent and eventually into the ListView propagateComposedEvents: true // for testing / debugging on a desktop scrollGestureEnabled: true onWheel: { manager.appendTextToLog("wheel " + wheel.angleDelta) if (wheel.angleDelta.y > 0) qmlProfile.scale += 0.2 if (wheel.angleDelta.y < 0 && qmlProfile.scale > 1.1) qmlProfile.scale -= 0.2 } anchors.fill: parent drag.target: qmlProfile drag.axis: Drag.XAndYAxis drag.smoothed: true onPressed: { if (!isZoomed) mouse.accepted = false } onPressAndHold: { dragging = true; oldXOffset = qmlProfile.xOffset oldYOffset = qmlProfile.yOffset initialX = mouse.x initialY = mouse.y if (manager.verboseEnabled) manager.appendTextToLog("press and hold at mouse" + Math.round(10 * mouse.x) / 10 + " / " + Math.round(10 * mouse.y) / 10) // give visual feedback to the user that they now can drag qmlProfile.opacity = 0.5 } onPositionChanged: { if (dragging) { var x = (mouse.x - initialX) / qmlProfile.scale var y = (mouse.y - initialY) / qmlProfile.scale if (manager.verboseEnabled) manager.appendTextToLog("drag mouse " + Math.round(10 * mouse.x) / 10 + " / " + Math.round(10 * mouse.y) / 10 + " delta " + Math.round(x) + " / " + Math.round(y)) qmlProfile.xOffset = oldXOffset + x qmlProfile.yOffset = oldYOffset + y qmlProfile.update() } else { mouse.accepted = false } } onReleased: { if (dragging) { // reset things dragging = false qmlProfile.opacity = 1.0 } mouse.accepted = false } onClicked: { // reset the position if not zoomed in if (!isZoomed) { qmlProfile.xOffset = qmlProfile.yOffset = oldXOffset = oldYOffset = 0 mouse.accepted = false } } } } } } TemplateLabelSmall { id: noProfile visible: noDive Layout.fillWidth: true Layout.columnSpan: 3 Layout.margins: Kirigami.Units.gridUnit horizontalAlignment: Text.AlignHCenter text: qsTr("No profile to show") } // first row //----------- TemplateLabelSmall { text: qsTr("Suit:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col1Width color: subsurfaceTheme.textColor } TemplateLabelSmall { text: qsTr("Air Temp:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col2Width color: subsurfaceTheme.textColor } TemplateLabelSmall { text: qsTr("Water Temp:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col3Width color: subsurfaceTheme.textColor } // second row //------------ TemplateLabelSmall { id: txtSuit text: suit wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col1Width color: subsurfaceTheme.textColor } TemplateLabelSmall { id: txtAirTemp text: airTemp wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col2Width color: subsurfaceTheme.textColor } TemplateLabelSmall { id: txtWaterTemp text: waterTemp wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col3Width color: subsurfaceTheme.textColor } Rectangle { color: subsurfaceTheme.primaryColor height: 1 opacity: 0.5 Layout.columnSpan: 3 Layout.fillWidth: true } // thrid row //------------ TemplateLabelSmall { text: qsTr("Cylinder:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col1Width Layout.bottomMargin: 0 color: subsurfaceTheme.textColor } TemplateLabelSmall { text: qsTr("Weight:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col2Width Layout.bottomMargin: 0 color: subsurfaceTheme.textColor } TemplateLabelSmall { text: qsTr("SAC:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col3Width Layout.bottomMargin: 0 color: subsurfaceTheme.textColor } // fourth row //------------ TemplateLabelSmall { id: txtCylinder text: cylinder wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col1Width color: subsurfaceTheme.textColor } TemplateLabelSmall { id: txtWeight text: sumWeight wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col2Width color: subsurfaceTheme.textColor } TemplateLabelSmall { id: txtSAC text: sac wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col3Width color: subsurfaceTheme.textColor } Rectangle { color: subsurfaceTheme.primaryColor height: 1 opacity: 0.5 Layout.columnSpan: 3 Layout.fillWidth: true } // fifth row //----------- TemplateLabelSmall { text: qsTr("Divemaster:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col1Width Layout.bottomMargin: 0 color: subsurfaceTheme.textColor } TemplateLabelSmall { text: qsTr("Buddy:") opacity: 0.6 wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.columnSpan: 2 Layout.maximumWidth: detailsView.col2Width + detailsView.col3Width Layout.bottomMargin: 0 color: subsurfaceTheme.textColor } // sixth row //----------- TemplateLabelSmall { id: txtDiveMaster text: diveMaster wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere Layout.maximumWidth: detailsView.col1Width color: subsurfaceTheme.textColor } TemplateLabelSmall { id: txtBuddy text: buddy wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.columnSpan: 2 Layout.maximumWidth: detailsView.col2Width + detailsView.col3Width color: subsurfaceTheme.textColor } Rectangle { color: subsurfaceTheme.primaryColor height: 1 opacity: 0.5 Layout.columnSpan: 3 Layout.fillWidth: true } // Notes on the bottom //-------------------- TemplateLabelSmall { Layout.fillWidth: true opacity: 0.6 text: qsTr("Notes") wrapMode: Text.WrapAtWordBoundaryOrAnywhere Layout.columnSpan: 3 color: subsurfaceTheme.textColor } Controls.Label { id: txtNotes text: notes focus: true Layout.columnSpan: 3 Layout.fillWidth: true wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere color: subsurfaceTheme.textColor } Item { Layout.columnSpan: 3 Layout.fillWidth: true Layout.minimumHeight: Kirigami.Units.gridUnit * 6 } Component.onCompleted: { qmlProfile.setMargin(Kirigami.Units.smallSpacing) qmlProfile.diveId = model.id; qmlProfile.update(); } } }