dive details navigation

This patch reworks the navigation of the dive details.

- The detailsview is now a list view with page-sized delegates. This
  allows horizontal swiping to the next and previous dive.
- The central button now allows to open the edit mode for the dive.

Original patch was done by Marco Martin, but needed to be reapplied by
hand.

Signed-off-by: Sebastian Kügler <sebas@kde.org>
This commit is contained in:
Sebastian Kügler 2016-01-12 01:15:02 +01:00
parent 9e85d76766
commit 6540e95425
6 changed files with 321 additions and 355 deletions

View file

@ -1,149 +1,85 @@
import QtQuick 2.3
import QtQuick 2.4
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.2
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1
import QtQuick.Layouts 1.2
import org.subsurfacedivelog.mobile 1.0
import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
MobileComponents.Page {
id: diveDetailsWindow
width: parent.width
objectName: "DiveDetails"
id: page
objectName: "DiveList"
property alias currentIndex: diveListView.currentIndex
mainAction: Action {
iconName: editDrawer.opened ? "dialog-cancel" : "document-edit"
onTriggered: {
if (editDrawer.opened) {
editDrawer.close();
return;
}
detailsEdit.dive_id = diveListView.currentItem.modelData.dive.id
detailsEdit.number = diveListView.currentItem.modelData.dive.number
detailsEdit.dateText = diveListView.currentItem.modelData.dive.date
detailsEdit.locationText = diveListView.currentItem.modelData.dive.location
detailsEdit.durationText = diveListView.currentItem.modelData.dive.duration
detailsEdit.depthText = diveListView.currentItem.modelData.dive.depth
detailsEdit.airtempText = diveListView.currentItem.modelData.dive.airTemp
detailsEdit.watertempText = diveListView.currentItem.modelData.dive.waterTemp
detailsEdit.suitText = diveListView.currentItem.modelData.dive.suit
detailsEdit.buddyText = diveListView.currentItem.modelData.dive.buddy
detailsEdit.divemasterText = diveListView.currentItem.modelData.dive.divemaster
detailsEdit.notesText = diveListView.currentItem.modelData.dive.notes
editDrawer.open();
}
}
property string location
property string gps
property string depth
property string dive_id
property string diveNumber
property string duration
property string airtemp
property string watertemp
property string suit
property int rating
property string buddy
property string divemaster;
property string notes;
property string date
property string number
property string weight
state: "view"
states: [
State {
name: "view"
PropertyChanges { target: detailsView; opacity: 1 }
PropertyChanges { target: detailsEdit; opacity: 0 }
},
State {
name: "edit"
PropertyChanges { target: detailsView; opacity: 0 }
PropertyChanges { target: detailsEdit; opacity: 1 }
}
]
property list<Action> viewActions: [
Action {
id: editSelector
text: "Edit"
iconName: "document-edit"
onTriggered: {
diveDetailsWindow.state = "edit"
contextDrawer.close()
}
}
]
property list<Action> editActions: [
Action {
id: cancelSelector
text: "Cancel"
iconName: "dialog-cancel"
onTriggered: {
// reset the fields in the edit screen
detailsEdit.dateText = date
detailsEdit.locationText = location
detailsEdit.durationText = duration
detailsEdit.depthText = depth
detailsEdit.airtempText = airtemp
detailsEdit.watertempText = watertemp
detailsEdit.suitText = suit
detailsEdit.buddyText = buddy
detailsEdit.divemasterText = divemaster
detailsEdit.notesText = notes
// back to view state and close the drawer
diveDetailsWindow.state = "view"
contextDrawer.close()
}
},
Action {
id: saveSelector
text: "Save"
iconName: "document-save"
onTriggered: {
// apply the changes to the dive_table
notes = manager.commitChanges(dive_id, detailsEdit.dateText, detailsEdit.locationText, detailsEdit.gpsText, detailsEdit.durationText,
detailsEdit.depthText, detailsEdit.airtempText, detailsEdit.watertempText, detailsEdit.suitText,
detailsEdit.buddyText, detailsEdit.divemasterText, detailsEdit.notesText)
// apply the changes to the dive detail view
date = detailsEdit.dateText
location = detailsEdit.locationText
duration = detailsEdit.durationText
depth = detailsEdit.depthText
airtemp = detailsEdit.airtempText
watertemp = detailsEdit.watertempText
suit = detailsEdit.suitText
buddy = detailsEdit.buddyText
divemaster = detailsEdit.divemasterText
// back to view state and close the drawer
diveDetailsWindow.state = "view"
contextDrawer.close()
}
}
]
contextualActions: diveDetailsWindow.state === "view" ? viewActions : editActions
function showDiveIndex(index) {
diveListView.currentIndex = index;
diveListView.positionViewAtIndex(diveListView.currentIndex, ListView.Beginning);
}
onWidthChanged: diveListView.positionViewAtIndex(diveListView.currentIndex, ListView.Beginning);
ScrollView {
anchors.fill: parent
Flickable {
id: flick
ListView {
id: diveListView
anchors.fill: parent
contentHeight: content.height
interactive: contentHeight > height
model: diveModel
currentIndex: -1
boundsBehavior: Flickable.StopAtBounds
maximumFlickVelocity: parent.width/4
cacheBuffer: parent.width/2
orientation: ListView.Horizontal
focus: true
clip: true
Item {
id: content
width: flick.width
height: childrenRect.height + MobileComponents.Units.smallSpacing * 2
DiveDetailsEdit {
id: detailsEdit
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: MobileComponents.Units.gridUnit / 2
snapMode: ListView.SnapOneItem
onMovementEnded: {
currentIndex = indexAt(contentX+1, 1);
}
delegate: ScrollView {
id: internalScrollView
width: diveListView.width
height: diveListView.height
property var modelData: model
Flickable {
//contentWidth: parent.width
contentHeight: diveDetails.height
DiveDetailsView {
id: diveDetails
width: internalScrollView.width
}
visible: opacity > 0
Behavior on opacity {
NumberAnimation { duration: MobileComponents.Units.shortDuration }
}
}
DiveDetailsView {
id: detailsView
anchors {
left: parent.left
right: parent.right
top: parent.top
margins: MobileComponents.Units.gridUnit / 2
}
visible: opacity > 0
Behavior on opacity {
NumberAnimation { duration: MobileComponents.Units.shortDuration }
}
}
}
}
}
MobileComponents.OverlayDrawer {
id: editDrawer
anchors.fill: parent
edge: Qt.BottomEdge
contentItem: DiveDetailsEdit {
id: detailsEdit
implicitHeight: page.height - MobileComponents.Units.gridUnit*3
}
}
}

View file

@ -7,6 +7,9 @@ import org.subsurfacedivelog.mobile 1.0
import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
Item {
id: detailsEdit
property int dive_id
property int number
property alias dateText: txtDate.text
property alias locationText: txtLocation.text
property string gpsText
@ -23,6 +26,7 @@ Item {
left: parent.left
right: parent.right
top: parent.top
margins: MobileComponents.Units.gridUnit
}
spacing: MobileComponents.Units.smallSpacing
@ -42,7 +46,6 @@ Item {
}
TextField {
id: txtDate;
text: date;
Layout.fillWidth: true
}
MobileComponents.Label {
@ -50,7 +53,7 @@ Item {
text: "Location:"
}
TextField {
id: txtLocation; text: location;
id: txtLocation;
Layout.fillWidth: true
}
@ -76,7 +79,6 @@ Item {
}
TextField {
id: txtDepth
text: depth
Layout.fillWidth: true
}
MobileComponents.Label {
@ -85,7 +87,6 @@ Item {
}
TextField {
id: txtDuration
text: duration
Layout.fillWidth: true
}
@ -95,7 +96,6 @@ Item {
}
TextField {
id: txtAirTemp
text: airtemp
Layout.fillWidth: true
}
@ -105,7 +105,6 @@ Item {
}
TextField {
id: txtWaterTemp
text: watertemp
Layout.fillWidth: true
}
@ -115,7 +114,6 @@ Item {
}
TextField {
id: txtSuit
text: suit
Layout.fillWidth: true
}
@ -125,7 +123,6 @@ Item {
}
TextField {
id: txtBuddy
text: buddy
Layout.fillWidth: true
}
@ -135,7 +132,6 @@ Item {
}
TextField {
id: txtDiveMaster
text: divemaster
Layout.fillWidth: true
}
@ -145,7 +141,6 @@ Item {
}
TextArea {
id: txtNotes
text: notes
textFormat: TextEdit.RichText
focus: true
Layout.fillWidth: true
@ -155,6 +150,28 @@ Item {
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
}
Button {
anchors.horizontalCenter: parent.horizontalCenter
text: "Save"
onClicked: {
// apply the changes to the dive_table
manager.commitChanges(dive_id, detailsEdit.dateText, detailsEdit.locationText, detailsEdit.gpsText, detailsEdit.durationText,
detailsEdit.depthText, detailsEdit.airtempText, detailsEdit.watertempText, detailsEdit.suitText,
detailsEdit.buddyText, detailsEdit.divemasterText, detailsEdit.notesText)
// apply the changes to the dive detail view
diveListView.currentItem.modelData.date = detailsEdit.dateText
diveListView.currentItem.modelData.location = detailsEdit.locationText
diveListView.currentItem.modelData.duration = detailsEdit.durationText
diveListView.currentItem.modelData.depth = detailsEdit.depthText
diveListView.currentItem.modelData.airtemp = detailsEdit.airtempText
diveListView.currentItem.modelData.watertemp = detailsEdit.watertempText
diveListView.currentItem.modelData.suit = detailsEdit.suitText
diveListView.currentItem.modelData.buddy = detailsEdit.buddyText
diveListView.currentItem.modelData.divemaster = detailsEdit.divemasterText
diveListView.currentItem.modelData.notes = detailsEdit.notesText
editDrawer.close()
}
}
Item {
height: MobileComponents.Units.gridUnit * 3
width: height // just to make sure the spacer doesn't produce scrollbars, but also isn't null

View file

@ -9,211 +9,234 @@ import QtQuick.Layouts 1.1
import org.subsurfacedivelog.mobile 1.0
import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
GridLayout {
Item {
id: detailsView
columns: 4
rowSpacing: MobileComponents.Units.smallSpacing * 2
columnSpacing: MobileComponents.Units.smallSpacing
property int labelWidth: MobileComponents.Units.gridUnit * 10
Connections {
target: diveDetailsWindow
onDive_idChanged: {
qmlProfile.diveId = diveDetailsWindow.dive_id
qmlProfile.update()
width: parent.width
height: mainLayout.implicitHeight + MobileComponents.Units.iconSizes.large
Rectangle {
z: 99
color: MobileComponents.Theme.textColor
opacity: 0.3
width: MobileComponents.Units.smallSpacing/4
anchors {
right: parent.right
top: parent.top
bottom: parent.bottom
}
}
GridLayout {
id: mainLayout
anchors {
//fill: parent
top: parent.top
left: parent.left
right: parent.right
margins: MobileComponents.Units.gridUnit
}
columns: 4
rowSpacing: MobileComponents.Units.smallSpacing * 2
columnSpacing: MobileComponents.Units.smallSpacing
MobileComponents.Heading {
id: detailsViewHeading
Layout.fillWidth: true
text: location
font.underline: gps !== ""
Layout.columnSpan: 4
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
MouseArea {
anchors.fill: parent
onClicked: {
if (gps !== "")
manager.showMap(gps)
/*Connections {
target: diveDetailsWindow
onDive_idChanged: {
qmlProfile.diveId = diveDetailsWindow.dive_id
qmlProfile.update()
}
}*/
MobileComponents.Heading {
id: detailsViewHeading
Layout.fillWidth: true
text: dive.location
font.underline: dive.gps !== ""
Layout.columnSpan: 4
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
MouseArea {
anchors.fill: parent
onClicked: {
if (gps !== "")
manager.showMap(gps)
}
}
}
}
/*
Rectangle {
id: mapView
width: parent.width
height: parents.width * 0.7
WebView {
id: webView
anchors.fill: parent
url: "http://www.google.com"
/*
Rectangle {
id: mapView
width: parent.width
height: parents.width * 0.7
WebView {
id: webView
anchors.fill: parent
url: "http://www.google.com"
}
}
}
*/
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
id: dateLabel
text: "Date: "
opacity: 0.6
}
MobileComponents.Label {
text: date
Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview
Layout.columnSpan: 3
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
id: depthLabel
text: "Depth: "
opacity: 0.6
}
MobileComponents.Label {
text: depth
Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Duration: "
opacity: 0.6
}
RowLayout {
*/
MobileComponents.Label {
text: duration
Layout.alignment: Qt.AlignRight
id: dateLabel
text: "Date: "
opacity: 0.6
}
MobileComponents.Label {
text: dive.date
Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview
Layout.columnSpan: 3
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
id: depthLabel
text: "Depth: "
opacity: 0.6
}
MobileComponents.Label {
text: dive.depth
Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Duration: "
opacity: 0.6
}
RowLayout {
MobileComponents.Label {
text: dive.duration
}
Item {
Layout.fillWidth: true
height: parent.height
}
MobileComponents.Label {
id: numberText
text: "#" + dive.number
color: MobileComponents.Theme.textColor
}
}
QMLProfile {
id: qmlProfile
Layout.fillWidth: true
Layout.minimumHeight: Layout.preferredHeight
Layout.preferredHeight: width * 0.66
Layout.columnSpan: 4
Rectangle {
color: "transparent"
opacity: 0.6
border.width: 1
border.color: MobileComponents.Theme.textColor;
anchors.fill: parent
}
//Rectangle { color: "green"; opacity: 0.4; anchors.fill: parent } // used for debugging the dive profile sizing, will be removed later
}
MobileComponents.Heading {
Layout.fillWidth: true
level: 3
text: "Dive Details"
Layout.columnSpan: 4
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Air Temp:"
opacity: 0.6
}
MobileComponents.Label {
id: txtAirTemp
text: dive.airTemp
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Water Temp:"
opacity: 0.6
}
MobileComponents.Label {
id: txtWaterTemp
text: dive.waterTemp
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Suit:"
opacity: 0.6
}
MobileComponents.Label {
id: txtSuit
text: dive.suit
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Weight:"
opacity: 0.6
}
MobileComponents.Label {
id: txtWeight
//text: dive.weights
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Buddy:"
opacity: 0.6
}
MobileComponents.Label {
id: txtBuddy
text: dive.buddy
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Dive Master:"
opacity: 0.6
}
MobileComponents.Label {
id: txtDiveMaster
text: dive.divemaster
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Heading {
Layout.fillWidth: true
level: 3
text: "Notes"
Layout.columnSpan: 4
}
MobileComponents.Label {
id: txtNotes
text: dive.notes
focus: true
Layout.columnSpan: 4
Layout.fillWidth: true
Layout.fillHeight: true
//selectByMouse: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
Item {
Layout.columnSpan: 4
Layout.fillWidth: true
height: parent.height
Layout.minimumHeight: MobileComponents.Units.gridUnit * 3
}
MobileComponents.Label {
id: numberText
text: "#" + diveNumber
color: MobileComponents.Theme.textColor
Component.onCompleted: {
qmlProfile.setMargin(MobileComponents.Units.smallSpacing)
qmlProfile.diveId = model.dive.id;
qmlProfile.update();
}
}
QMLProfile {
id: qmlProfile
Layout.fillWidth: true
Layout.preferredHeight: width * 0.66
Layout.columnSpan: 4
Rectangle {
color: "transparent"
opacity: 0.6
border.width: 1
border.color: MobileComponents.Theme.textColor;
anchors.fill: parent
}
//Rectangle { color: "green"; opacity: 0.4; anchors.fill: parent } // used for debugging the dive profile sizing, will be removed later
}
MobileComponents.Heading {
Layout.fillWidth: true
level: 3
text: "Dive Details"
Layout.columnSpan: 4
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Air Temp:"
opacity: 0.6
}
MobileComponents.Label {
id: txtAirTemp
text: airtemp
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Water Temp:"
opacity: 0.6
}
MobileComponents.Label {
id: txtWaterTemp
text: watertemp
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Suit:"
opacity: 0.6
}
MobileComponents.Label {
id: txtSuit
text: suit
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Weight:"
opacity: 0.6
}
MobileComponents.Label {
id: txtWeight
text: weight
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Buddy:"
opacity: 0.6
}
MobileComponents.Label {
id: txtBuddy
text: buddy
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Label {
Layout.alignment: Qt.AlignRight
text: "Dive Master:"
opacity: 0.6
}
MobileComponents.Label {
id: txtDiveMaster
text: divemaster
Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
MobileComponents.Heading {
Layout.fillWidth: true
level: 3
text: "Notes"
Layout.columnSpan: 4
}
MobileComponents.Label {
id: txtNotes
text: notes
focus: true
Layout.columnSpan: 4
Layout.fillWidth: true
Layout.fillHeight: true
//selectByMouse: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
}
Item {
Layout.columnSpan: 4
Layout.fillWidth: true
Layout.minimumHeight: MobileComponents.Units.gridUnit * 3
}
Component.onCompleted: {
qmlProfile.setMargin(MobileComponents.Units.smallSpacing)
}
}

View file

@ -21,28 +21,11 @@ MobileComponents.Page {
property real detailsOpacity : 0
property int horizontalPadding: MobileComponents.Units.gridUnit / 2 - MobileComponents.Units.smallSpacing + 1
//When clicked, the mode changes to details view
// When clicked, the mode changes to details view
onClicked: {
diveListView.currentIndex = model.index
detailsWindow.width = parent.width
detailsWindow.location = dive.location
detailsWindow.gps = dive.gps
detailsWindow.dive_id = dive.id
detailsWindow.diveNumber = dive.number
detailsWindow.duration = dive.duration
detailsWindow.depth = dive.depth
detailsWindow.rating = dive.rating
detailsWindow.buddy = dive.buddy
detailsWindow.suit = dive.suit
detailsWindow.airtemp = dive.airTemp
detailsWindow.watertemp = dive.waterTemp
detailsWindow.divemaster = dive.divemaster
detailsWindow.notes = dive.notes
detailsWindow.number = dive.number
detailsWindow.date = dive.date + " " + dive.time
// detailsWindow.weight = dive.weights
stackView.push(detailsWindow)
detailsWindow.showDiveIndex(index);
stackView.push(detailsWindow);
}
Item {
@ -154,6 +137,7 @@ MobileComponents.Page {
}
}
}
ScrollView {
anchors.fill: parent
ListView {
@ -161,10 +145,14 @@ MobileComponents.Page {
anchors.fill: parent
model: diveModel
currentIndex: -1
Connections {
target: detailsWindow
onCurrentIndexChanged: diveListView.currentIndex = detailsWindow.currentIndex
}
delegate: diveDelegate
boundsBehavior: Flickable.StopAtBounds
maximumFlickVelocity: parent.height * 5
cacheBuffer: parent.height * 5
cacheBuffer: Math.max(5000, parent.height * 5)
//highlight: Rectangle { color: MobileComponents.Theme.highlightColor; width: MobileComponents.Units.smallSpacing }
focus: true
clip: true

View file

@ -219,6 +219,8 @@ MobileComponents.ApplicationWindow {
DiveDetails {
id: detailsWindow
visible: false
width: parent.width
height: parent.height
}
DownloadFromDiveComputer {

View file

@ -25,7 +25,7 @@ PLATFORM=$(uname)
# to build Subsurface-mobile on the desktop change this to
# SUBSURFACE_EXECUTABLE=MobileExecutable
SUBSURFACE_EXECUTABLE=DesktopExecutable
SUBSURFACE_EXECUTABLE=MobileExecutable
if [[ ! -d "subsurface" ]] ; then