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 1.4
import QtQuick.Controls.Styles 1.2 import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.2
import org.subsurfacedivelog.mobile 1.0 import org.subsurfacedivelog.mobile 1.0
import org.kde.plasma.mobilecomponents 0.2 as MobileComponents import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
MobileComponents.Page { MobileComponents.Page {
id: diveDetailsWindow id: page
width: parent.width objectName: "DiveList"
objectName: "DiveDetails" 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 function showDiveIndex(index) {
property string gps diveListView.currentIndex = index;
property string depth diveListView.positionViewAtIndex(diveListView.currentIndex, ListView.Beginning);
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 }
} }
] onWidthChanged: diveListView.positionViewAtIndex(diveListView.currentIndex, ListView.Beginning);
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
ScrollView { ScrollView {
anchors.fill: parent anchors.fill: parent
Flickable { ListView {
id: flick id: diveListView
anchors.fill: parent anchors.fill: parent
contentHeight: content.height model: diveModel
interactive: contentHeight > height currentIndex: -1
boundsBehavior: Flickable.StopAtBounds
maximumFlickVelocity: parent.width/4
cacheBuffer: parent.width/2
orientation: ListView.Horizontal
focus: true
clip: true clip: true
Item { snapMode: ListView.SnapOneItem
id: content onMovementEnded: {
width: flick.width currentIndex = indexAt(contentX+1, 1);
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
}
visible: opacity > 0
Behavior on opacity {
NumberAnimation { duration: MobileComponents.Units.shortDuration }
}
} }
delegate: ScrollView {
id: internalScrollView
width: diveListView.width
height: diveListView.height
property var modelData: model
Flickable {
//contentWidth: parent.width
contentHeight: diveDetails.height
DiveDetailsView { DiveDetailsView {
id: detailsView id: diveDetails
anchors { width: internalScrollView.width
left: parent.left }
right: parent.right }
top: parent.top }
margins: MobileComponents.Units.gridUnit / 2 }
}
MobileComponents.OverlayDrawer {
id: editDrawer
anchors.fill: parent
edge: Qt.BottomEdge
contentItem: DiveDetailsEdit {
id: detailsEdit
implicitHeight: page.height - MobileComponents.Units.gridUnit*3
} }
visible: opacity > 0
Behavior on opacity {
NumberAnimation { duration: MobileComponents.Units.shortDuration }
} }
}
}
}
}
} }

View file

@ -7,6 +7,9 @@ import org.subsurfacedivelog.mobile 1.0
import org.kde.plasma.mobilecomponents 0.2 as MobileComponents import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
Item { Item {
id: detailsEdit
property int dive_id
property int number
property alias dateText: txtDate.text property alias dateText: txtDate.text
property alias locationText: txtLocation.text property alias locationText: txtLocation.text
property string gpsText property string gpsText
@ -23,6 +26,7 @@ Item {
left: parent.left left: parent.left
right: parent.right right: parent.right
top: parent.top top: parent.top
margins: MobileComponents.Units.gridUnit
} }
spacing: MobileComponents.Units.smallSpacing spacing: MobileComponents.Units.smallSpacing
@ -42,7 +46,6 @@ Item {
} }
TextField { TextField {
id: txtDate; id: txtDate;
text: date;
Layout.fillWidth: true Layout.fillWidth: true
} }
MobileComponents.Label { MobileComponents.Label {
@ -50,7 +53,7 @@ Item {
text: "Location:" text: "Location:"
} }
TextField { TextField {
id: txtLocation; text: location; id: txtLocation;
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -76,7 +79,6 @@ Item {
} }
TextField { TextField {
id: txtDepth id: txtDepth
text: depth
Layout.fillWidth: true Layout.fillWidth: true
} }
MobileComponents.Label { MobileComponents.Label {
@ -85,7 +87,6 @@ Item {
} }
TextField { TextField {
id: txtDuration id: txtDuration
text: duration
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -95,7 +96,6 @@ Item {
} }
TextField { TextField {
id: txtAirTemp id: txtAirTemp
text: airtemp
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -105,7 +105,6 @@ Item {
} }
TextField { TextField {
id: txtWaterTemp id: txtWaterTemp
text: watertemp
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -115,7 +114,6 @@ Item {
} }
TextField { TextField {
id: txtSuit id: txtSuit
text: suit
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -125,7 +123,6 @@ Item {
} }
TextField { TextField {
id: txtBuddy id: txtBuddy
text: buddy
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -135,7 +132,6 @@ Item {
} }
TextField { TextField {
id: txtDiveMaster id: txtDiveMaster
text: divemaster
Layout.fillWidth: true Layout.fillWidth: true
} }
@ -145,7 +141,6 @@ Item {
} }
TextArea { TextArea {
id: txtNotes id: txtNotes
text: notes
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
focus: true focus: true
Layout.fillWidth: true Layout.fillWidth: true
@ -155,6 +150,28 @@ Item {
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere 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 { Item {
height: MobileComponents.Units.gridUnit * 3 height: MobileComponents.Units.gridUnit * 3
width: height // just to make sure the spacer doesn't produce scrollbars, but also isn't null width: height // just to make sure the spacer doesn't produce scrollbars, but also isn't null

View file

@ -9,28 +9,48 @@ import QtQuick.Layouts 1.1
import org.subsurfacedivelog.mobile 1.0 import org.subsurfacedivelog.mobile 1.0
import org.kde.plasma.mobilecomponents 0.2 as MobileComponents import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
GridLayout { Item {
id: detailsView id: detailsView
property int labelWidth: MobileComponents.Units.gridUnit * 10
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 columns: 4
rowSpacing: MobileComponents.Units.smallSpacing * 2 rowSpacing: MobileComponents.Units.smallSpacing * 2
columnSpacing: MobileComponents.Units.smallSpacing columnSpacing: MobileComponents.Units.smallSpacing
property int labelWidth: MobileComponents.Units.gridUnit * 10 /*Connections {
Connections {
target: diveDetailsWindow target: diveDetailsWindow
onDive_idChanged: { onDive_idChanged: {
qmlProfile.diveId = diveDetailsWindow.dive_id qmlProfile.diveId = diveDetailsWindow.dive_id
qmlProfile.update() qmlProfile.update()
} }
} }*/
MobileComponents.Heading { MobileComponents.Heading {
id: detailsViewHeading id: detailsViewHeading
Layout.fillWidth: true Layout.fillWidth: true
text: location text: dive.location
font.underline: gps !== "" font.underline: dive.gps !== ""
Layout.columnSpan: 4 Layout.columnSpan: 4
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
MouseArea { MouseArea {
@ -52,7 +72,7 @@ GridLayout {
url: "http://www.google.com" url: "http://www.google.com"
} }
} }
*/ */
MobileComponents.Label { MobileComponents.Label {
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
id: dateLabel id: dateLabel
@ -60,7 +80,7 @@ GridLayout {
opacity: 0.6 opacity: 0.6
} }
MobileComponents.Label { MobileComponents.Label {
text: date text: dive.date
Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview
Layout.columnSpan: 3 Layout.columnSpan: 3
} }
@ -72,7 +92,7 @@ GridLayout {
opacity: 0.6 opacity: 0.6
} }
MobileComponents.Label { MobileComponents.Label {
text: depth text: dive.depth
Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview Layout.minimumWidth: Math.max(MobileComponents.Units.gridUnit * 4, paintedWidth) // helps vertical alignment throughout listview
} }
MobileComponents.Label { MobileComponents.Label {
@ -82,7 +102,7 @@ GridLayout {
} }
RowLayout { RowLayout {
MobileComponents.Label { MobileComponents.Label {
text: duration text: dive.duration
} }
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
@ -90,7 +110,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: numberText id: numberText
text: "#" + diveNumber text: "#" + dive.number
color: MobileComponents.Theme.textColor color: MobileComponents.Theme.textColor
} }
} }
@ -98,9 +118,9 @@ GridLayout {
QMLProfile { QMLProfile {
id: qmlProfile id: qmlProfile
Layout.fillWidth: true Layout.fillWidth: true
Layout.minimumHeight: Layout.preferredHeight
Layout.preferredHeight: width * 0.66 Layout.preferredHeight: width * 0.66
Layout.columnSpan: 4 Layout.columnSpan: 4
Rectangle { Rectangle {
color: "transparent" color: "transparent"
opacity: 0.6 opacity: 0.6
@ -126,7 +146,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: txtAirTemp id: txtAirTemp
text: airtemp text: dive.airTemp
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
} }
@ -138,7 +158,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: txtWaterTemp id: txtWaterTemp
text: watertemp text: dive.waterTemp
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
} }
@ -150,7 +170,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: txtSuit id: txtSuit
text: suit text: dive.suit
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
} }
@ -162,7 +182,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: txtWeight id: txtWeight
text: weight //text: dive.weights
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
} }
@ -174,7 +194,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: txtBuddy id: txtBuddy
text: buddy text: dive.buddy
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
} }
@ -186,7 +206,7 @@ GridLayout {
} }
MobileComponents.Label { MobileComponents.Label {
id: txtDiveMaster id: txtDiveMaster
text: divemaster text: dive.divemaster
Layout.fillWidth: true Layout.fillWidth: true
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
} }
@ -200,7 +220,7 @@ GridLayout {
MobileComponents.Label { MobileComponents.Label {
id: txtNotes id: txtNotes
text: notes text: dive.notes
focus: true focus: true
Layout.columnSpan: 4 Layout.columnSpan: 4
Layout.fillWidth: true Layout.fillWidth: true
@ -215,5 +235,8 @@ GridLayout {
} }
Component.onCompleted: { Component.onCompleted: {
qmlProfile.setMargin(MobileComponents.Units.smallSpacing) qmlProfile.setMargin(MobileComponents.Units.smallSpacing)
qmlProfile.diveId = model.dive.id;
qmlProfile.update();
}
} }
} }

View file

@ -21,28 +21,11 @@ MobileComponents.Page {
property real detailsOpacity : 0 property real detailsOpacity : 0
property int horizontalPadding: MobileComponents.Units.gridUnit / 2 - MobileComponents.Units.smallSpacing + 1 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: { onClicked: {
diveListView.currentIndex = model.index diveListView.currentIndex = model.index
detailsWindow.width = parent.width detailsWindow.showDiveIndex(index);
detailsWindow.location = dive.location stackView.push(detailsWindow);
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)
} }
Item { Item {
@ -154,6 +137,7 @@ MobileComponents.Page {
} }
} }
} }
ScrollView { ScrollView {
anchors.fill: parent anchors.fill: parent
ListView { ListView {
@ -161,10 +145,14 @@ MobileComponents.Page {
anchors.fill: parent anchors.fill: parent
model: diveModel model: diveModel
currentIndex: -1 currentIndex: -1
Connections {
target: detailsWindow
onCurrentIndexChanged: diveListView.currentIndex = detailsWindow.currentIndex
}
delegate: diveDelegate delegate: diveDelegate
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
maximumFlickVelocity: parent.height * 5 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 } //highlight: Rectangle { color: MobileComponents.Theme.highlightColor; width: MobileComponents.Units.smallSpacing }
focus: true focus: true
clip: true clip: true

View file

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

View file

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