diff options
Diffstat (limited to 'qt-mobile')
-rw-r--r-- | qt-mobile/qml/About.qml | 26 | ||||
-rw-r--r-- | qt-mobile/qml/CloudCredentials.qml | 22 | ||||
-rw-r--r-- | qt-mobile/qml/DiveDetails.qml | 71 | ||||
-rw-r--r-- | qt-mobile/qml/DiveDetailsView.qml | 22 | ||||
-rw-r--r-- | qt-mobile/qml/DiveList.qml | 18 | ||||
-rw-r--r-- | qt-mobile/qml/GpsList.qml | 2 | ||||
-rw-r--r-- | qt-mobile/qml/StartPage.qml | 6 | ||||
-rw-r--r-- | qt-mobile/qml/ThemeTest.qml | 3 | ||||
-rw-r--r-- | qt-mobile/qml/TopBar.qml | 8 | ||||
-rw-r--r-- | qt-mobile/qml/main.qml | 38 | ||||
-rw-r--r-- | qt-mobile/qml/mobile-resources.qrc | 72 | ||||
-rw-r--r-- | qt-mobile/qmlmanager.cpp | 157 | ||||
-rw-r--r-- | qt-mobile/qmlmanager.h | 14 | ||||
-rw-r--r-- | qt-mobile/qmlprofile.cpp | 51 | ||||
-rw-r--r-- | qt-mobile/qmlprofile.h | 2 |
15 files changed, 350 insertions, 162 deletions
diff --git a/qt-mobile/qml/About.qml b/qt-mobile/qml/About.qml index 9b1b69c8b..b1ca6e6bc 100644 --- a/qt-mobile/qml/About.qml +++ b/qt-mobile/qml/About.qml @@ -6,7 +6,7 @@ import org.subsurfacedivelog.mobile 1.0 Kirigami.ScrollablePage { id: aboutPage - property int pageWidth: subsurfaceTheme.columnWidth - Kirigami.Units.gridUnit + property int pageWidth: subsurfaceTheme.columnWidth - Kirigami.Units.smallSpacing title: "About Subsurface-mobile" ColumnLayout { @@ -14,26 +14,22 @@ Kirigami.ScrollablePage { width: aboutPage.width Layout.margins: Kirigami.Units.gridUnit / 2 + Kirigami.Heading { text: "About Subsurface-mobile" - Layout.margins: Kirigami.Units.largeSpacing / 2 + Layout.topMargin: Kirigami.Units.gridUnit Layout.alignment: Qt.AlignHCenter Layout.maximumWidth: pageWidth wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere } - - Rectangle { - color: "transparent" - Layout.margins: Kirigami.Units.largeSpacing - Layout.fillWidth: true - Layout.minimumHeight: childrenRect.height - Image { - id: image - source: "qrc:/qml/subsurface-mobile-icon.png" - width: parent.width - Kirigami.Units.largeSpacing - fillMode: Image.PreserveAspectFit - horizontalAlignment: Image.AlignHCenter - } + Image { + id: image + source: "qrc:/qml/subsurface-mobile-icon.png" + width: pageWidth / 2 + height: width + fillMode: Image.Stretch + Layout.alignment: Qt.AlignCenter + horizontalAlignment: Image.AlignHCenter } Kirigami.Heading { diff --git a/qt-mobile/qml/CloudCredentials.qml b/qt-mobile/qml/CloudCredentials.qml index 3eeeb16b4..4f1484ce8 100644 --- a/qt-mobile/qml/CloudCredentials.qml +++ b/qt-mobile/qml/CloudCredentials.qml @@ -12,18 +12,28 @@ Item { property string username: login.text; property string password: password.text; - property bool issave: savePassword.checked; function saveCredentials() { manager.cloudUserName = login.text manager.cloudPassword = password.text - manager.saveCloudPassword = savePassword.checked manager.saveCloudCredentials() } ColumnLayout { id: outerLayout width: subsurfaceTheme.columnWidth - 2 * Kirigami.Units.gridUnit + + onVisibleChanged: { + if (visible) { + manager.appendTextToLog("Credential scrn: show kbd was: " + (Qt.inputMethod.isVisible ? "visible" : "invisible")) + Qt.inputMethod.show() + login.forceActiveFocus() + } else { + manager.appendTextToLog("Credential scrn: hide kbd was: " + (Qt.inputMethod.isVisible ? "visible" : "invisible")) + Qt.inputMethod.hide() + } + } + Kirigami.Heading { text: "Cloud credentials" level: headingLevel @@ -68,14 +78,6 @@ Item { Kirigami.Label { text: "Show password" } - - CheckBox { - checked: manager.saveCloudPassword - id: savePassword - } - Kirigami.Label { - text: "Remember" - } } Item { width: Kirigami.Units.gridUnit; height: width } } diff --git a/qt-mobile/qml/DiveDetails.qml b/qt-mobile/qml/DiveDetails.qml index 555e5ba0c..22cffdd6d 100644 --- a/qt-mobile/qml/DiveDetails.qml +++ b/qt-mobile/qml/DiveDetails.qml @@ -38,45 +38,57 @@ Kirigami.Page { states: [ State { name: "view" - PropertyChanges { target: diveDetailsPage; contextualActions: deleteAction } - PropertyChanges { target: detailsEditScroll; opened: false } + PropertyChanges { target: diveDetailsPage; contextualActions: Qt.platform.os == "ios" ? [ deleteAction, backAction ] : [ deleteAction ] } + PropertyChanges { target: detailsEditScroll; visible: false } }, State { name: "edit" - PropertyChanges { target: diveDetailsPage; contextualActions: null } - PropertyChanges { target: detailsEditScroll; opened: true } + PropertyChanges { target: diveDetailsPage; contextualActions: Qt.platform.os == "ios" ? [ cancelAction ] : null } + PropertyChanges { target: detailsEditScroll; visible: true } }, State { name: "add" - PropertyChanges { target: diveDetailsPage; contextualActions: null } - PropertyChanges { target: detailsEditScroll; opened: true } + PropertyChanges { target: diveDetailsPage; contextualActions: Qt.platform.os == "ios" ? [ cancelAction ] : null } + PropertyChanges { target: detailsEditScroll; visible: true } } ] - function endAddMode() { - // edit was canceled - so remove the dive from the dive list - manager.addDiveAborted(dive_id) - state = "view" - Qt.inputMethod.hide() + property QtObject deleteAction: Action { + text: "Delete dive" + iconName: "trash-empty" + onTriggered: { + contextDrawer.close() + var deletedId = diveDetailsListView.currentItem.modelData.dive.id + manager.deleteDive(deletedId) + stackView.pop() + showPassiveNotification("Dive deleted", 3000, "Undo", + function() { + manager.undoDelete(deletedId) + }); + } } - property list<QtObject> deleteAction: [ - Kirigami.Action { - text: "Delete dive" - iconName: "trash-empty" - onTriggered: { - var deletedId = diveDetailsListView.currentItem.modelData.dive.id - manager.deleteDive(deletedId) - showPassiveNotification("Dive deleted", 3000, "Undo", - function() { - manager.undoDelete(deletedId) - }); - contextDrawer.close() - stackView.pop() - } + property QtObject cancelAction: Kirigami.Action { + text: state === "edit" ? "Cancel edit" : "Cancel dive add" + iconName: "dialog-cancel" + onTriggered: { + contextDrawer.close() + if (state === "add") + returnTopPage() + else + endEditMode() } - ] + } + + property QtObject backAction: Action { + text: "Back to dive list" + iconName: "go-previous" + onTriggered: { + contextDrawer.close() + returnTopPage() + } + } mainAction: Action { iconName: state !== "view" ? "document-save" : "document-edit" @@ -94,7 +106,7 @@ Kirigami.Page { endEditMode() event.accepted = true; } else if (state === "add") { - endAddMode() + endEditMode() stackView.pop() event.accepted = true; } @@ -107,7 +119,10 @@ Kirigami.Page { } function endEditMode() { - // just cancel the edit state + // if we were adding a dive, we need to remove it + if (state === "add") + manager.addDiveAborted(dive_id) + // just cancel the edit/add state state = "view"; Qt.inputMethod.hide(); } diff --git a/qt-mobile/qml/DiveDetailsView.qml b/qt-mobile/qml/DiveDetailsView.qml index bf1408755..c8502d02f 100644 --- a/qt-mobile/qml/DiveDetailsView.qml +++ b/qt-mobile/qml/DiveDetailsView.qml @@ -104,7 +104,6 @@ Item { Layout.minimumHeight: width * 0.75 Layout.columnSpan: 4 clip: false - devicePixelRatio: Kirigami.Units.devicePixelRatio Rectangle { color: "transparent" opacity: 0.6 @@ -250,10 +249,25 @@ Item { Kirigami.Label { id: txtBuddy text: dive.buddy - Layout.columnSpan: 3 wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere - Layout.maximumWidth: detailsView.col2Width + detailsView.col3Width + detailsView.col4Width - Layout.preferredWidth: detailsView.col2Width + detailsView.col3Width + detailsView.col4Width + Layout.maximumWidth: detailsView.col2Width + Layout.preferredWidth: detailsView.col2Width + } + + Kirigami.Label { + text: "SAC:" + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + opacity: 0.6 + Layout.maximumWidth: detailsView.col3Width + Layout.preferredWidth: detailsView.col3Width + Layout.alignment: Qt.AlignRight + } + Kirigami.Label { + id: txtSAC + text: dive.sac + wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere + Layout.maximumWidth: detailsView.col4Width + Layout.preferredWidth: detailsView.col4Width } Kirigami.Heading { diff --git a/qt-mobile/qml/DiveList.qml b/qt-mobile/qml/DiveList.qml index 3696d08ec..b689f0108 100644 --- a/qt-mobile/qml/DiveList.qml +++ b/qt-mobile/qml/DiveList.qml @@ -16,6 +16,11 @@ Kirigami.ScrollablePage { property int credentialStatus: manager.credentialStatus property int numDives: diveListView.count + property color textColor: subsurfaceTheme.diveListTextColor + + function scrollToTop() { + diveListView.positionViewAtBeginning() + } Component { id: diveDelegate @@ -47,6 +52,7 @@ Kirigami.ScrollablePage { font.weight: Font.Light elide: Text.ElideRight maximumLineCount: 1 // needed for elide to work at all + color: textColor anchors { left: parent.left leftMargin: horizontalPadding @@ -57,8 +63,8 @@ Kirigami.ScrollablePage { Kirigami.Label { id: dateLabel text: dive.date + " " + dive.time - opacity: 0.6 font.pointSize: subsurfaceTheme.smallPointSize + color: textColor anchors { right: parent.right top: parent.top @@ -74,30 +80,31 @@ Kirigami.ScrollablePage { } Kirigami.Label { text: 'Depth: ' - opacity: 0.6 font.pointSize: subsurfaceTheme.smallPointSize + color: textColor } Kirigami.Label { text: dive.depth width: Math.max(Kirigami.Units.gridUnit * 3, paintedWidth) // helps vertical alignment throughout listview font.pointSize: subsurfaceTheme.smallPointSize + color: textColor } Kirigami.Label { text: 'Duration: ' - opacity: 0.6 font.pointSize: subsurfaceTheme.smallPointSize + color: textColor } Kirigami.Label { text: dive.duration font.pointSize: subsurfaceTheme.smallPointSize + color: textColor } } Kirigami.Label { id: numberText text: "#" + dive.number - color: Kirigami.Theme.textColor font.pointSize: subsurfaceTheme.smallPointSize - opacity: 0.6 + color: textColor anchors { right: parent.right top: locationText.bottom @@ -135,6 +142,7 @@ Kirigami.ScrollablePage { leftMargin: Kirigami.Units.gridUnit / 2 right: parent.right } + color: textColor level: 2 } Rectangle { diff --git a/qt-mobile/qml/GpsList.qml b/qt-mobile/qml/GpsList.qml index 0a57486c5..54dc02121 100644 --- a/qt-mobile/qml/GpsList.qml +++ b/qt-mobile/qml/GpsList.qml @@ -29,7 +29,7 @@ Kirigami.ScrollablePage { */ Component { id: gpsDelegate - Kirigami.ActionsForListItem { + Kirigami.SwipeListItem { id: gpsFix enabled: true width: parent.width diff --git a/qt-mobile/qml/StartPage.qml b/qt-mobile/qml/StartPage.qml index 64b236ade..2d70cfcb3 100644 --- a/qt-mobile/qml/StartPage.qml +++ b/qt-mobile/qml/StartPage.qml @@ -9,8 +9,6 @@ ColumnLayout { id: startpage width: subsurfaceTheme.columnWidth - property int buttonWidth: width * 0.9 - function saveCredentials() { cloudCredentials.saveCredentials() } Kirigami.Heading { @@ -21,6 +19,7 @@ ColumnLayout { id: explanationText Layout.fillWidth: true Layout.margins: Kirigami.Units.gridUnit + Layout.topMargin: 0 text: "In order to use Subsurface-mobile you need to have a Subsurface cloud storage account " + "(which can be created with the Subsurface desktop application)." wrapMode: Text.WordWrap @@ -29,6 +28,7 @@ ColumnLayout { id: messageArea Layout.fillWidth: true Layout.margins: Kirigami.Units.gridUnit + Layout.topMargin: 0 text: manager.startPageText wrapMode: Text.WordWrap } @@ -36,7 +36,7 @@ ColumnLayout { id: cloudCredentials Layout.fillWidth: true Layout.margins: Kirigami.Units.gridUnit - Layout.topMargin: Kirigami.Units.gridUnit * 2 + Layout.topMargin: 0 property int headingLevel: 3 } } diff --git a/qt-mobile/qml/ThemeTest.qml b/qt-mobile/qml/ThemeTest.qml index b099b5722..c0916aea0 100644 --- a/qt-mobile/qml/ThemeTest.qml +++ b/qt-mobile/qml/ThemeTest.qml @@ -1,6 +1,7 @@ import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 import org.kde.kirigami 1.0 as Kirigami Kirigami.Page { @@ -62,7 +63,7 @@ Kirigami.Page { text: "Units.devicePixelRatio:" } Kirigami.Label { - text: Kirigami.Units.devicePixelRatio + text: Screen.devicePixelRatio } Kirigami.Heading { diff --git a/qt-mobile/qml/TopBar.qml b/qt-mobile/qml/TopBar.qml index cc99d9701..024b818b0 100644 --- a/qt-mobile/qml/TopBar.qml +++ b/qt-mobile/qml/TopBar.qml @@ -48,4 +48,12 @@ Rectangle { Layout.fillWidth: true } } + MouseArea { + anchors.fill: topPart + onClicked: { + if (stackView.depth == 1 && showingDiveList) { + scrollToTop() + } + } + } } diff --git a/qt-mobile/qml/main.qml b/qt-mobile/qml/main.qml index 675dad623..150e9769f 100644 --- a/qt-mobile/qml/main.qml +++ b/qt-mobile/qml/main.qml @@ -15,9 +15,10 @@ Kirigami.ApplicationWindow { property int oldStatus: -1 property alias accessingCloud: manager.accessingCloud property QtObject notification: null + property bool showingDiveList: false onAccessingCloudChanged: { if (accessingCloud) { - showPassiveNotification("Accessing Subsurface Cloud Storage", 5000); + showPassiveNotification("Accessing Subsurface Cloud Storage", 500000); } else { hidePassiveNotification(); } @@ -37,6 +38,10 @@ Kirigami.ApplicationWindow { detailsWindow.endEditMode() } + function scrollToTop() { + diveList.scrollToTop() + } + globalDrawer: Kirigami.GlobalDrawer { title: "Subsurface" titleIcon: "qrc:/qml/subsurface-mobile-icon.png" @@ -46,6 +51,14 @@ Kirigami.ApplicationWindow { Kirigami.Action { text: "Dive list" onTriggered: { + manager.appendTextToLog("requested dive list with credential status " + manager.credentialStatus) + if (manager.credentialStatus == QMLManager.UNKNOWN) { + // the user has asked to change credentials - if the credentials before that + // were valid, go back to dive list + if (oldStatus == QMLManager.VALID || oldStatus == QMLManager.VALID_EMAIL) { + manager.credentialStatus = oldStatus + } + } returnTopPage() globalDrawer.close() } @@ -58,7 +71,7 @@ Kirigami.ApplicationWindow { if (diveList.numDives > 0) { manager.startPageText = "Enter different credentials or return to dive list" } else { - manager.startPageText = "Enter valdi cloud storage credentials" + manager.startPageText = "Enter valid cloud storage credentials" } manager.credentialStatus = QMLManager.UNKNOWN @@ -103,6 +116,7 @@ Kirigami.ApplicationWindow { Kirigami.Action { text: "Refresh" onTriggered: { + globalDrawer.close() detailsWindow.endEditMode() manager.loadDives(); } @@ -110,6 +124,7 @@ Kirigami.ApplicationWindow { Action { text: "Upload to cloud" onTriggered: { + globalDrawer.close() detailsWindow.endEditMode() manager.saveChanges(); } @@ -178,13 +193,11 @@ Kirigami.ApplicationWindow { stackView.push(themetest) } } - Kirigami.Action { - checkable: true - checked: manager.verboseEnabled - text: checked ? "Disable verbose (for adb logcat)" : "Enable verbose (for adb logcat)" - onToggled: { - manager.verboseEnabled = checked; - } + }, + Kirigami.Action { + text: "User manual" + onTriggered: { + manager.showUserManual() } }, Kirigami.Action { @@ -240,8 +253,15 @@ Kirigami.ApplicationWindow { property color accentColor: "#2d5b9a" property color shadedColor: "#132744" property color accentTextColor: "#ececec" + property color diveListTextColor: Qt.rgba(0,0,0,0.76) // the Kirigami theme text color is too light property int columnWidth: Math.round(rootItem.width/(Kirigami.Units.gridUnit*30)) > 0 ? Math.round(rootItem.width / Math.round(rootItem.width/(Kirigami.Units.gridUnit*30))) : rootItem.width } +/* + toolBar: TopBar { + width: parent.width + height: Layout.minimumHeight + } + */ property Item stackView: pageStack pageStack.initialPage: DiveList { diff --git a/qt-mobile/qml/mobile-resources.qrc b/qt-mobile/qml/mobile-resources.qrc index f3fe82e08..a7d0013cf 100644 --- a/qt-mobile/qml/mobile-resources.qrc +++ b/qt-mobile/qml/mobile-resources.qrc @@ -24,41 +24,41 @@ <file alias="menu-back.png">icons/menu-back.png</file> </qresource> <qresource prefix="/imports"> - <file alias="org/kde/kirigami/qmldir">mobilecomponents/qmldir</file> - <file alias="org/kde/kirigami/Action.qml">mobilecomponents/Action.qml</file> - <file alias="org/kde/kirigami/ApplicationWindow.qml">mobilecomponents/ApplicationWindow.qml</file> - <file alias="org/kde/kirigami/BasicListItem.qml">mobilecomponents/BasicListItem.qml</file> - <file alias="org/kde/kirigami/GlobalDrawer.qml">mobilecomponents/GlobalDrawer.qml</file> - <file alias="org/kde/kirigami/ContextDrawer.qml">mobilecomponents/ContextDrawer.qml</file> - <file alias="org/kde/kirigami/Page.qml">mobilecomponents/Page.qml</file> - <file alias="org/kde/kirigami/ScrollablePage.qml">mobilecomponents/ScrollablePage.qml</file> - <file alias="org/kde/kirigami/Icon.qml">mobilecomponents/Icon.qml</file> - <file alias="org/kde/kirigami/Heading.qml">mobilecomponents/Heading.qml</file> - <file alias="org/kde/kirigami/OverlaySheet.qml">mobilecomponents/OverlaySheet.qml</file> - <file alias="org/kde/kirigami/ApplicationHeader.qml">mobilecomponents/ApplicationHeader.qml</file> - <file alias="org/kde/kirigami/private/PageRow.qml">mobilecomponents/private/PageRow.qml</file> - <file alias="org/kde/kirigami/Label.qml">mobilecomponents/Label.qml</file> - <file alias="org/kde/kirigami/AbstractListItem.qml">mobilecomponents/AbstractListItem.qml</file> - <file alias="org/kde/kirigami/ActionsForListItem.qml">mobilecomponents/ActionsForListItem.qml</file> - <file alias="org/kde/kirigami/OverlayDrawer.qml">mobilecomponents/OverlayDrawer.qml</file> - <file alias="org/kde/kirigami/Theme.qml">mobilecomponents/Theme.qml</file> - <file alias="org/kde/kirigami/Units.qml">mobilecomponents/Units.qml</file> - <file alias="org/kde/kirigami/private/RefreshableScrollView.qml">mobilecomponents/private/RefreshableScrollView.qml</file> - <file alias="org/kde/kirigami/private/ActionButton.qml">mobilecomponents/private/ActionButton.qml</file> - <file alias="org/kde/kirigami/private/MenuIcon.qml">mobilecomponents/private/MenuIcon.qml</file> - <file alias="org/kde/kirigami/private/ContextIcon.qml">mobilecomponents/private/ContextIcon.qml</file> - <file alias="org/kde/kirigami/private/AbstractDrawer.qml">mobilecomponents/private/AbstractDrawer.qml</file> - <file alias="org/kde/kirigami/private/PageStack.js">mobilecomponents/private/PageStack.js</file> - <file alias="org/kde/kirigami/private/PassiveNotification.qml">mobilecomponents/private/PassiveNotification.qml</file> - <file alias="org/kde/kirigami/icons/go-next.svg">mobilecomponents/icons/go-next.svg</file> - <file alias="org/kde/kirigami/icons/go-previous.svg">mobilecomponents/icons/go-previous.svg</file> - <file alias="org/kde/kirigami/icons/distribute-horizontal-x.svg">mobilecomponents/icons/distribute-horizontal-x.svg</file> - <file alias="org/kde/kirigami/icons/document-edit.svg">mobilecomponents/icons/document-edit.svg</file> - <file alias="org/kde/kirigami/icons/document-save.svg">mobilecomponents/icons/document-save.svg</file> - <file alias="org/kde/kirigami/icons/view-readermode.svg">mobilecomponents/icons/view-readermode.svg</file> - <file alias="org/kde/kirigami/icons/dialog-cancel.svg">mobilecomponents/icons/dialog-cancel.svg</file> - <file alias="org/kde/kirigami/icons/application-menu.svg">mobilecomponents/icons/application-menu.svg</file> - <file alias="org/kde/kirigami/icons/gps.svg">mobilecomponents/icons/gps.svg</file> - <file alias="org/kde/kirigami/icons/trash-empty.svg">mobilecomponents/icons/trash-empty.svg</file> + <file alias="org/kde/kirigami/qmldir">kirigami/qmldir</file> + <file alias="org/kde/kirigami/Action.qml">kirigami/Action.qml</file> + <file alias="org/kde/kirigami/ApplicationWindow.qml">kirigami/ApplicationWindow.qml</file> + <file alias="org/kde/kirigami/BasicListItem.qml">kirigami/BasicListItem.qml</file> + <file alias="org/kde/kirigami/GlobalDrawer.qml">kirigami/GlobalDrawer.qml</file> + <file alias="org/kde/kirigami/ContextDrawer.qml">kirigami/ContextDrawer.qml</file> + <file alias="org/kde/kirigami/Page.qml">kirigami/Page.qml</file> + <file alias="org/kde/kirigami/ScrollablePage.qml">kirigami/ScrollablePage.qml</file> + <file alias="org/kde/kirigami/Icon.qml">kirigami/Icon.qml</file> + <file alias="org/kde/kirigami/Heading.qml">kirigami/Heading.qml</file> + <file alias="org/kde/kirigami/OverlaySheet.qml">kirigami/OverlaySheet.qml</file> + <file alias="org/kde/kirigami/ApplicationHeader.qml">kirigami/ApplicationHeader.qml</file> + <file alias="org/kde/kirigami/private/PageRow.qml">kirigami/private/PageRow.qml</file> + <file alias="org/kde/kirigami/Label.qml">kirigami/Label.qml</file> + <file alias="org/kde/kirigami/AbstractListItem.qml">kirigami/AbstractListItem.qml</file> + <file alias="org/kde/kirigami/SwipeListItem.qml">kirigami/SwipeListItem.qml</file> + <file alias="org/kde/kirigami/OverlayDrawer.qml">kirigami/OverlayDrawer.qml</file> + <file alias="org/kde/kirigami/Theme.qml">kirigami/Theme.qml</file> + <file alias="org/kde/kirigami/Units.qml">kirigami/Units.qml</file> + <file alias="org/kde/kirigami/private/RefreshableScrollView.qml">kirigami/private/RefreshableScrollView.qml</file> + <file alias="org/kde/kirigami/private/ActionButton.qml">kirigami/private/ActionButton.qml</file> + <file alias="org/kde/kirigami/private/MenuIcon.qml">kirigami/private/MenuIcon.qml</file> + <file alias="org/kde/kirigami/private/ContextIcon.qml">kirigami/private/ContextIcon.qml</file> + <file alias="org/kde/kirigami/private/AbstractDrawer.qml">kirigami/private/AbstractDrawer.qml</file> + <file alias="org/kde/kirigami/private/PageStack.js">kirigami/private/PageStack.js</file> + <file alias="org/kde/kirigami/private/PassiveNotification.qml">kirigami/private/PassiveNotification.qml</file> + <file alias="org/kde/kirigami/icons/go-next.svg">kirigami/icons/go-next.svg</file> + <file alias="org/kde/kirigami/icons/go-previous.svg">kirigami/icons/go-previous.svg</file> + <file alias="org/kde/kirigami/icons/distribute-horizontal-x.svg">kirigami/icons/distribute-horizontal-x.svg</file> + <file alias="org/kde/kirigami/icons/document-edit.svg">kirigami/icons/document-edit.svg</file> + <file alias="org/kde/kirigami/icons/document-save.svg">kirigami/icons/document-save.svg</file> + <file alias="org/kde/kirigami/icons/view-readermode.svg">kirigami/icons/view-readermode.svg</file> + <file alias="org/kde/kirigami/icons/dialog-cancel.svg">kirigami/icons/dialog-cancel.svg</file> + <file alias="org/kde/kirigami/icons/application-menu.svg">kirigami/icons/application-menu.svg</file> + <file alias="org/kde/kirigami/icons/gps.svg">kirigami/icons/gps.svg</file> + <file alias="org/kde/kirigami/icons/trash-empty.svg">kirigami/icons/trash-empty.svg</file> </qresource> </RCC> diff --git a/qt-mobile/qmlmanager.cpp b/qt-mobile/qmlmanager.cpp index 6851ec079..301a02ce1 100644 --- a/qt-mobile/qmlmanager.cpp +++ b/qt-mobile/qmlmanager.cpp @@ -6,6 +6,7 @@ #include <QAuthenticator> #include <QDesktopServices> #include <QTextDocument> +#include <QRegularExpression> #include "qt-models/divelistmodel.h" #include <gpslistmodel.h> @@ -19,6 +20,9 @@ QMLManager *QMLManager::m_instance = NULL; +#define RED_FONT QLatin1Literal("<font color=\"red\">") +#define END_FONT QLatin1Literal("</font>") + static void appendTextToLogStandalone(const char *text) { QMLManager *self = QMLManager::instance(); @@ -42,10 +46,11 @@ extern "C" int gitProgressCB(int percent) QMLManager::QMLManager() : m_locationServiceEnabled(false), m_verboseEnabled(false), - m_credentialStatus(UNKNOWN), reply(0), deletedDive(0), - deletedTrip(0) + deletedTrip(0), + m_credentialStatus(UNKNOWN), + m_lastDevicePixelRatio(1.0) { m_instance = this; appendTextToLog(getUserAgent()); @@ -98,7 +103,6 @@ void QMLManager::finishSetup() // Initialize cloud credentials. setCloudUserName(prefs.cloud_storage_email); setCloudPassword(prefs.cloud_storage_password); - setSaveCloudPassword(prefs.save_password_local); // if the cloud credentials are valid, we should get the GPS Webservice ID as well QString url; if (!cloudUserName().isEmpty() && @@ -108,7 +112,7 @@ void QMLManager::finishSetup() } else { setCredentialStatus(INCOMPLETE); appendTextToLog(QStringLiteral("no cloud credentials")); - setStartPageText(tr("Please enter valid cloud credentials.")); + setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT); } setDistanceThreshold(prefs.distance_threshold); setTimeThreshold(prefs.time_threshold / 60); @@ -144,28 +148,22 @@ void QMLManager::saveCloudCredentials() bool cloudCredentialsChanged = false; s.beginGroup("CloudStorage"); s.setValue("email", cloudUserName()); - s.setValue("save_password_local", saveCloudPassword()); - if (saveCloudPassword()) - s.setValue("password", cloudPassword()); + s.setValue("password", cloudPassword()); s.sync(); if (!same_string(prefs.cloud_storage_email, qPrintable(cloudUserName()))) { free(prefs.cloud_storage_email); prefs.cloud_storage_email = strdup(qPrintable(cloudUserName())); cloudCredentialsChanged = true; } - if (saveCloudPassword() != prefs.save_password_local) - prefs.save_password_local = saveCloudPassword(); cloudCredentialsChanged |= !same_string(prefs.cloud_storage_password, qPrintable(cloudPassword())); - if (saveCloudPassword()) { - if (!same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()))) { - free(prefs.cloud_storage_password); - prefs.cloud_storage_password = strdup(qPrintable(cloudPassword())); - } + if (!same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()))) { + free(prefs.cloud_storage_password); + prefs.cloud_storage_password = strdup(qPrintable(cloudPassword())); } if (cloudUserName().isEmpty() || cloudPassword().isEmpty()) { - setStartPageText(tr("Please enter valid cloud credentials.")); + setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT); } else if (cloudCredentialsChanged) { free(prefs.userid); prefs.userid = NULL; @@ -209,6 +207,7 @@ void QMLManager::tryRetrieveDataFromBackend() void QMLManager::loadDives() { + setAccessingCloud(true); checkCredentialsAndExecute(&QMLManager::loadDivesWithValidCredentials); } @@ -218,7 +217,7 @@ void QMLManager::provideAuth(QNetworkReply *reply, QAuthenticator *auth) auth->password() == QString(prefs.cloud_storage_password)) { // OK, credentials have been tried and didn't work, so they are invalid appendTextToLog("Cloud credentials are invalid"); - setStartPageText(tr("Cloud credentials are invalid")); + setStartPageText(RED_FONT + tr("Cloud credentials are invalid") + END_FONT); setCredentialStatus(INVALID); reply->disconnect(); reply->abort(); @@ -231,7 +230,7 @@ void QMLManager::provideAuth(QNetworkReply *reply, QAuthenticator *auth) void QMLManager::handleSslErrors(const QList<QSslError> &errors) { - setStartPageText(tr("Cannot open cloud storage: Error creating https connection")); + setStartPageText(RED_FONT + tr("Cannot open cloud storage: Error creating https connection") + END_FONT); Q_FOREACH (QSslError e, errors) { qDebug() << e.errorString(); } @@ -244,7 +243,7 @@ void QMLManager::handleError(QNetworkReply::NetworkError nError) { QString errorString = reply->errorString(); qDebug() << "handleError" << nError << errorString; - setStartPageText(tr("Cannot open cloud storage: %1").arg(errorString)); + setStartPageText(RED_FONT + tr("Cannot open cloud storage: %1").arg(errorString) + END_FONT); reply->abort(); reply->deleteLater(); setAccessingCloud(false); @@ -253,7 +252,7 @@ void QMLManager::handleError(QNetworkReply::NetworkError nError) void QMLManager::retrieveUserid() { if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) { - appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: ") + reply->readAll()); + appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: %1").arg(QString(reply->readAll()))); setAccessingCloud(false); return; } @@ -293,7 +292,7 @@ void QMLManager::loadDivesWithValidCredentials() { if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) { appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: ") + reply->readAll()); - setStartPageText(tr("Cannot connect to cloud storage")); + setStartPageText(RED_FONT + tr("Cannot connect to cloud storage") + END_FONT); setAccessingCloud(false); return; } @@ -305,7 +304,7 @@ void QMLManager::loadDivesWithValidCredentials() if (getCloudURL(url)) { QString errorString(get_error_string()); appendTextToLog(errorString); - setStartPageText(tr("Cloud storage error: %1").arg(errorString)); + setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT); setAccessingCloud(false); return; } @@ -331,7 +330,7 @@ void QMLManager::loadDivesWithValidCredentials() report_error("failed to open file %s", fileNamePrt.data()); QString errorString(get_error_string()); appendTextToLog(errorString); - setStartPageText(tr("Cloud storage error: %1").arg(errorString)); + setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT); return; } prefs.unit_system = informational_prefs.unit_system; @@ -415,6 +414,76 @@ void QMLManager::commitChanges(QString diveId, QString date, QString location, Q date.replace(drop, ""); } newDate = QDateTime::fromString(date, format); + if (!newDate.isValid()) { + qDebug() << "unable to parse date" << date << "with the given format" << format; + QRegularExpression isoDate("\\d+-\\d+-\\d+[^\\d]+\\d+:\\d+"); + if (date.contains(isoDate)) { + newDate = QDateTime::fromString(date, "yyyy-M-d h:m:s"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date, "yy-M-d h:m:s"); + if (newDate.isValid()) + goto parsed; + } + QRegularExpression isoDateNoSecs("\\d+-\\d+-\\d+[^\\d]+\\d+"); + if (date.contains(isoDateNoSecs)) { + newDate = QDateTime::fromString(date, "yyyy-M-d h:m"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date, "yy-M-d h:m"); + if (newDate.isValid()) + goto parsed; + } + QRegularExpression usDate("\\d+/\\d+/\\d+[^\\d]+\\d+:\\d+:\\d+"); + if (date.contains(usDate)) { + newDate = QDateTime::fromString(date, "M/d/yyyy h:m:s"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date, "M/d/yy h:m:s"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date.toLower(), "M/d/yyyy h:m:sap"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date.toLower(), "M/d/yy h:m:sap"); + if (newDate.isValid()) + goto parsed; + } + QRegularExpression usDateNoSecs("\\d+/\\d+/\\d+[^\\d]+\\d+:\\d+"); + if (date.contains(usDateNoSecs)) { + newDate = QDateTime::fromString(date, "M/d/yyyy h:m"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date, "M/d/yy h:m"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date.toLower(), "M/d/yyyy h:map"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date.toLower(), "M/d/yy h:map"); + if (newDate.isValid()) + goto parsed; + } + QRegularExpression leDate("\\d+\\.\\d+\\.\\d+[^\\d]+\\d+:\\d+:\\d+"); + if (date.contains(leDate)) { + newDate = QDateTime::fromString(date, "d.M.yyyy h:m:s"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date, "d.M.yy h:m:s"); + if (newDate.isValid()) + goto parsed; + } + QRegularExpression leDateNoSecs("\\d+\\.\\d+\\.\\d+[^\\d]+\\d+:\\d+"); + if (date.contains(leDateNoSecs)) { + newDate = QDateTime::fromString(date, "d.M.yyyy h:m"); + if (newDate.isValid()) + goto parsed; + newDate = QDateTime::fromString(date, "d.M.yy h:m"); + if (newDate.isValid()) + goto parsed; + } + } +parsed: if (newDate.isValid()) { // stupid Qt... two digit years are always 19xx - WTF??? // so if adding a hundred years gets you into something before a year from now... @@ -422,6 +491,8 @@ void QMLManager::commitChanges(QString diveId, QString date, QString location, Q if (newDate.addYears(100) < QDateTime::currentDateTime().addYears(1)) newDate = newDate.addYears(100); d->dc.when = d->when = newDate.toMSecsSinceEpoch() / 1000 + gettimezoneoffset(newDate.toMSecsSinceEpoch() / 1000); + } else { + qDebug() << "none of our parsing attempts worked for the date string"; } } struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid); @@ -532,15 +603,17 @@ void QMLManager::commitChanges(QString diveId, QString date, QString location, Q diveChanged = true; d->cylinder[0].start.mbar = parsePressureToMbar(startpressure); d->cylinder[0].end.mbar = parsePressureToMbar(endpressure); + if (d->cylinder[0].end.mbar > d->cylinder[0].start.mbar) + d->cylinder[0].end.mbar = d->cylinder[0].start.mbar; } // gasmix for first cylinder if (get_gas_string(d->cylinder[0].gasmix) != gasmix) { int o2 = parseGasMixO2(gasmix); int he = parseGasMixHE(gasmix); // the QML code SHOULD only accept valid gas mixes, but just to make sure - if (o2 >= 0 && o2 <= 100 && - he >= 0 && he <= 100 && - o2 + he <= 100) { + if (o2 >= 0 && o2 <= 1000 && + he >= 0 && he <= 1000 && + o2 + he <= 1000) { diveChanged = true; d->cylinder[0].gasmix.o2.permille = o2; d->cylinder[0].gasmix.he.permille = he; @@ -623,8 +696,8 @@ void QMLManager::saveChanges() appendTextToLog(get_error_string()); return; } - - setAccessingCloud(true); + if (prefs.git_local_only == false) + setAccessingCloud(true); if (save_dives(fileName.toUtf8().data())) { appendTextToLog(get_error_string()); setAccessingCloud(false); @@ -772,16 +845,6 @@ void QMLManager::appendTextToLog(const QString &newText) emit logTextChanged(); } -bool QMLManager::saveCloudPassword() const -{ - return m_saveCloudPassword; -} - -void QMLManager::setSaveCloudPassword(bool saveCloudPassword) -{ - m_saveCloudPassword = saveCloudPassword; -} - bool QMLManager::locationServiceEnabled() const { return m_locationServiceEnabled; @@ -824,7 +887,7 @@ QString QMLManager::cloudUserName() const void QMLManager::setCloudUserName(const QString &cloudUserName) { - m_cloudUserName = cloudUserName; + m_cloudUserName = cloudUserName.toLower(); emit cloudUserNameChanged(); } @@ -906,6 +969,13 @@ void QMLManager::showMap(const QString& location) } } +#define MOBILEUSERMANUAL QUrl(QLatin1Literal("https://subsurface-divelog.org/documentation/subsurface-mobile-user-manual/")) + +void QMLManager::showUserManual() +{ + QDesktopServices::openUrl(MOBILEUSERMANUAL); +} + // where in the QML dive list is that dive? int QMLManager::getIndex(const QString &diveId) { @@ -953,3 +1023,14 @@ void QMLManager::setAccessingCloud(bool status) m_accessingCloud = status; emit accessingCloudChanged(); } + +qreal QMLManager::lastDevicePixelRatio() +{ + return m_lastDevicePixelRatio; +} + +void QMLManager::screenChanged(QScreen *screen) +{ + m_lastDevicePixelRatio = screen->devicePixelRatio(); + emit sendScreenChanged(screen); +} diff --git a/qt-mobile/qmlmanager.h b/qt-mobile/qmlmanager.h index ff3507991..2113a1dfe 100644 --- a/qt-mobile/qmlmanager.h +++ b/qt-mobile/qmlmanager.h @@ -4,6 +4,7 @@ #include <QObject> #include <QString> #include <QNetworkAccessManager> +#include <QScreen> #include "gpslocation.h" @@ -12,7 +13,6 @@ class QMLManager : public QObject { Q_ENUMS(credentialStatus_t) Q_PROPERTY(QString cloudUserName READ cloudUserName WRITE setCloudUserName NOTIFY cloudUserNameChanged) Q_PROPERTY(QString cloudPassword READ cloudPassword WRITE setCloudPassword NOTIFY cloudPasswordChanged) - Q_PROPERTY(bool saveCloudPassword READ saveCloudPassword WRITE setSaveCloudPassword NOTIFY saveCloudPasswordChanged) Q_PROPERTY(QString logText READ logText WRITE setLogText NOTIFY logTextChanged) Q_PROPERTY(bool locationServiceEnabled READ locationServiceEnabled WRITE setLocationServiceEnabled NOTIFY locationServiceEnabledChanged) Q_PROPERTY(int distanceThreshold READ distanceThreshold WRITE setDistanceThreshold NOTIFY distanceThresholdChanged) @@ -43,9 +43,6 @@ public: QString cloudPassword() const; void setCloudPassword(const QString &cloudPassword); - bool saveCloudPassword() const; - void setSaveCloudPassword(bool saveCloudPassword); - bool locationServiceEnabled() const; void setLocationServiceEnabled(bool locationServiceEnable); @@ -70,7 +67,6 @@ public: QString logText() const; void setLogText(const QString &logText); - void appendTextToLog(const QString &newText); bool accessingCloud() const; void setAccessingCloud(bool status); @@ -108,6 +104,7 @@ public slots: void finishSetup(); void openLocalThenRemote(QString url); void showMap(const QString& location); + void showUserManual(); int getIndex(const QString& diveId); QString getNumber(const QString& diveId); QString getDate(const QString& diveId); @@ -115,13 +112,15 @@ public slots: QString getVersion() const; void deleteGpsFix(quint64 when); void refreshDiveList(); + void screenChanged(QScreen *screen); + qreal lastDevicePixelRatio(); + void appendTextToLog(const QString &newText); private: QString m_cloudUserName; QString m_cloudPassword; QString m_ssrfGpsWebUserid; QString m_startPageText; - bool m_saveCloudPassword; QString m_logText; bool m_locationServiceEnabled; bool m_verboseEnabled; @@ -136,11 +135,11 @@ private: struct dive_trip *deletedTrip; bool m_accessingCloud; credentialStatus_t m_credentialStatus; + qreal m_lastDevicePixelRatio; signals: void cloudUserNameChanged(); void cloudPasswordChanged(); - void saveCloudPasswordChanged(); void locationServiceEnabledChanged(); void verboseEnabledChanged(); void logTextChanged(); @@ -150,6 +149,7 @@ signals: void startPageTextChanged(); void credentialStatusChanged(); void accessingCloudChanged(); + void sendScreenChanged(QScreen *screen); }; #endif diff --git a/qt-mobile/qmlprofile.cpp b/qt-mobile/qmlprofile.cpp index 512aa6a3a..ad686561d 100644 --- a/qt-mobile/qmlprofile.cpp +++ b/qt-mobile/qmlprofile.cpp @@ -1,7 +1,10 @@ #include "qmlprofile.h" +#include "qmlmanager.h" #include "profile-widget/profilewidget2.h" #include "subsurface-core/dive.h" +#include "subsurface-core/metrics.h" #include <QTransform> +#include <QScreen> QMLProfile::QMLProfile(QQuickItem *parent) : QQuickPaintedItem(parent), @@ -13,7 +16,8 @@ QMLProfile::QMLProfile(QQuickItem *parent) : m_profileWidget->setProfileState(); m_profileWidget->setPrintMode(true); m_profileWidget->setFontPrintScale(0.8); - //m_profileWidget->setGeometry(this->geometry()); + connect(QMLManager::instance(), &QMLManager::sendScreenChanged, this, &QMLProfile::screenChanged); + setDevicePixelRatio(QMLManager::instance()->lastDevicePixelRatio()); } QMLProfile::~QMLProfile() @@ -24,13 +28,43 @@ QMLProfile::~QMLProfile() void QMLProfile::paint(QPainter *painter) { // let's look at the intended size of the content and scale our scene accordingly - QRect rect = m_profileWidget->contentsRect(); + QRect painterRect = painter->viewport(); + QRect profileRect = m_profileWidget->viewport()->rect(); + // qDebug() << "profile viewport and painter viewport" << profileRect << painterRect; qreal sceneSize = 104; // that should give us 2% margin all around (100x100 scene) - qreal sx = rect.width() / sceneSize; - qreal sy = rect.height() / sceneSize; - QTransform profileTransform; + qreal dprComp = devicePixelRatio() * painterRect.width() / profileRect.width(); + qreal sx = painterRect.width() / sceneSize / dprComp; + qreal sy = painterRect.height() / sceneSize / dprComp; + + // next figure out the weird magic by which we need to shift the painter so the profile is shown + int dpr = rint(devicePixelRatio()); + qreal magicShiftFactor = (dpr == 2 ? 0.25 : (dpr == 3 ? 0.33 : 0.0)); + + // now set up the transformations scale the profile and + // shift the painter (taking its existing transformation into account) + QTransform profileTransform = QTransform(); profileTransform.scale(sx, sy); + QTransform painterTransform = painter->transform(); + painterTransform.translate(-painterRect.width() * magicShiftFactor ,-painterRect.height() * magicShiftFactor); + +#if PROFILE_SCALING_DEBUG + // some debugging messages to help adjust this in case the magic above is insufficient + QMLManager::instance()->appendTextToLog(QString("dpr %1 profile viewport %2 %3 painter viewport %4 %5").arg(dpr).arg(profileRect.width()).arg(profileRect.height()) + .arg(painterRect.width()).arg(painterRect.height())); + QMLManager::instance()->appendTextToLog(QString("profile matrix %1 %2 %3 %4 %5 %6 %7 %8 %9").arg(profileTransform.m11()).arg(profileTransform.m12()).arg(profileTransform.m13()) + .arg(profileTransform.m21()).arg(profileTransform.m22()).arg(profileTransform.m23()) + .arg(profileTransform.m31()).arg(profileTransform.m32()).arg(profileTransform.m33())); + QMLManager::instance()->appendTextToLog(QString("painter matrix %1 %2 %3 %4 %5 %6 %7 %8 %9").arg(painterTransform.m11()).arg(painterTransform.m12()).arg(painterTransform.m13()) + .arg(painterTransform.m21()).arg(painterTransform.m22()).arg(painterTransform.m23()) + .arg(painterTransform.m31()).arg(painterTransform.m32()).arg(painterTransform.m33())); + qDebug() << "profile scaled by" << profileTransform.m11() << profileTransform.m22() << "and translated by" << profileTransform.m31() << profileTransform.m32(); + qDebug() << "exist profile transform" << m_profileWidget->transform() << "painter transform" << painter->transform(); +#endif + // apply the transformation + painter->setTransform(painterTransform); m_profileWidget->setTransform(profileTransform); + + // finally, render the profile m_profileWidget->render(painter); } @@ -65,6 +99,13 @@ void QMLProfile::setDevicePixelRatio(qreal dpr) { if (dpr != m_devicePixelRatio) { m_devicePixelRatio = dpr; + m_profileWidget->setFontPrintScale(0.8 * dpr); + updateDevicePixelRatio(dpr); emit devicePixelRatioChanged(); } } + +void QMLProfile::screenChanged(QScreen *screen) +{ + setDevicePixelRatio(screen->devicePixelRatio()); +} diff --git a/qt-mobile/qmlprofile.h b/qt-mobile/qmlprofile.h index 35332ce8d..c8a77d700 100644 --- a/qt-mobile/qmlprofile.h +++ b/qt-mobile/qmlprofile.h @@ -24,11 +24,13 @@ public: public slots: void setMargin(int margin); + void screenChanged(QScreen *screen); private: QString m_diveId; qreal m_devicePixelRatio; int m_margin; ProfileWidget2 *m_profileWidget; + signals: void rightAlignedChanged(); void diveIdChanged(); |