diff --git a/images/images.qrc b/images/images.qrc index 5866ef542f..bfbfb3d5cc 100644 --- a/images/images.qrc +++ b/images/images.qrc @@ -9,6 +9,9 @@ pictures/qfield-love.png + pictures/colorful.jpg + pictures/dark.jpg + pictures/lightgray.jpg themes/qfield/nodpi/ic_text_black_24dp.svg diff --git a/images/pictures/colorful.jpg b/images/pictures/colorful.jpg new file mode 100644 index 0000000000..e8217a69d8 Binary files /dev/null and b/images/pictures/colorful.jpg differ diff --git a/images/pictures/dark.jpg b/images/pictures/dark.jpg new file mode 100644 index 0000000000..90b0df794f Binary files /dev/null and b/images/pictures/dark.jpg differ diff --git a/images/pictures/lightgray.jpg b/images/pictures/lightgray.jpg new file mode 100644 index 0000000000..babead7923 Binary files /dev/null and b/images/pictures/lightgray.jpg differ diff --git a/src/qml/ProjectCreationScreen.qml b/src/qml/ProjectCreationScreen.qml new file mode 100644 index 0000000000..acaabdd1e6 --- /dev/null +++ b/src/qml/ProjectCreationScreen.qml @@ -0,0 +1,346 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Controls.Material.impl +import org.qfield +import Theme + +Page { + id: createNewProject + + signal create(var projectConfig) + + leftPadding: mainWindow.sceneLeftMargin + rightPadding: mainWindow.sceneRightMargin + + header: QfPageHeader { + title: qsTr("Create a new project") + + backgroundFill: false + showBackButton: true + showApplyButton: false + showCancelButton: false + showMenuButton: false + + topMargin: mainWindow.sceneTopMargin + titleColor: Theme.mainTextColor + titleFont: Theme.strongTitleFont + + onBack: { + createNewProject.visible = false; + } + } + + Flickable { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: createProjectButton.top + anchors.margins: 16 + contentHeight: newProjectConfigColumn.height + clip: true + + Column { + id: newProjectConfigColumn + width: parent.width + spacing: 8 + + Label { + text: qsTr("Project Name") + font: Theme.defaultFont + color: Theme.mainTextColor + wrapMode: Text.WordWrap + width: parent.width + } + + TextField { + id: projectName + width: parent.width + height: 50 + font: Theme.defaultFont + placeholderText: text === "" && !focus ? "New project name" : "" + } + + Item { + width: 1 + height: 2 + } + + QfExpandableGroupBox { + id: baseMapExpandablePanel + title: qsTr("Select your basemap") + width: parent.width + checked: true + interactive: false + content: Column { + id: basemapColumn + anchors.left: parent.left + anchors.right: parent.right + + Label { + text: qsTr("Choose a basemap for your project. Pick from the available options or provide your own link.") + font: Theme.defaultFont + color: Theme.secondaryTextColor + wrapMode: Text.WordWrap + width: parent.width + } + + Item { + width: 1 + height: 16 + } + + ListView { + id: baseMapList + width: parent.width + height: 95 + orientation: ListView.Horizontal + spacing: 4 + model: [{ + "icon": "qrc:/pictures/pictures/colorful.jpg", + "name": "Colorful " + }, { + "icon": "qrc:/pictures/pictures/dark.jpg", + "name": "Dark " + }, { + "icon": "qrc:/pictures/pictures/lightgray.jpg", + "name": "Light " + }, { + "icon": "", + "name": "Blank" + }, { + "icon": "", + "name": "Custom" + }] + + clip: true + + delegate: Rectangle { + width: 150 + height: 95 + color: modelData.name == "Blank" ? "white" : Theme.groupBoxSurfaceColor + radius: 4 + border.width: 2 + border.color: baseMapList.currentIndex === index ? Theme.mainColor : "transparent" + + Image { + id: baseMapIcon + anchors.fill: parent + anchors.margins: 2 + fillMode: Image.PreserveAspectCrop + source: modelData.icon + sourceSize.width: width * screen.devicePixelRatio + sourceSize.height: height * screen.devicePixelRatio + visible: source !== "" + } + + Rectangle { + width: 25 + height: width + radius: width / 2 + color: Theme.mainColor + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 8 + visible: baseMapList.currentIndex === index + opacity: visible + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + } + + Image { + anchors.fill: parent + anchors.margins: 4 + fillMode: Image.PreserveAspectCrop + source: "qrc:/themes/qfield/nodpi/ic_check_white_24dp.svg" + sourceSize.width: width * screen.devicePixelRatio + sourceSize.height: height * screen.devicePixelRatio + } + } + + Rectangle { + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.margins: 2 + anchors.right: parent.right + height: 20 + color: Qt.hsla(Theme.mainBackgroundColor.hslHue, Theme.mainBackgroundColor.hslSaturation, Theme.mainBackgroundColor.hslLightness, Theme.darkTheme ? 0.75 : 0.9) + + Label { + text: modelData.name + font: Theme.defaultFont + color: Theme.mainTextColor + wrapMode: Text.WordWrap + width: parent.width + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + } + } + + MouseArea { + id: baseMapItemMouseArea + anchors.fill: parent + onClicked: baseMapList.currentIndex = index + + Ripple { + clip: true + anchor: parent + width: parent.width + height: parent.height + pressed: parent.pressed + active: parent.pressed + color: Material.rippleColor + } + } + } + } + + Item { + width: 1 + height: 8 + } + + Column { + spacing: 4 + width: parent.width + visible: baseMapList.currentIndex === 4 + + Label { + text: qsTr("Custom basemap URL") + font: Theme.defaultFont + color: Theme.mainTextColor + wrapMode: Text.WordWrap + width: parent.width + } + + TextField { + id: baseMapURL + font: Theme.defaultFont + placeholderText: text == "" && !focus ? "e.g., https://your-map-service.com/{z}/" : "" + placeholderTextColor: Theme.secondaryTextColor + width: parent.width + } + } + } + } + + QfExpandableGroupBox { + id: takeNotesGroupBox + title: qsTr("Take notes?") + width: parent.width + checked: true + content: Column { + id: takeNotesColumn + anchors.left: parent.left + anchors.right: parent.right + + Label { + text: qsTr("Quickly capture notes with date, time, and comments. Optionally, attach images to enrich your notes.") + font: Theme.defaultFont + color: Theme.secondaryTextColor + wrapMode: Text.WordWrap + width: parent.width + } + + CheckBox { + id: takeMediaCheckBox + text: qsTr("Take image and video attachments") + font: Theme.defaultFont + indicator.height: 16 + indicator.width: 16 + indicator.implicitHeight: 24 + indicator.implicitWidth: 24 + leftPadding: 1 + checked: true + } + } + } + + QfExpandableGroupBox { + id: trackPositionGroupBox + title: qsTr("Track your position?") + width: parent.width + checked: true + content: Column { + id: trackPositionColumn + anchors.left: parent.left + anchors.right: parent.right + + Label { + text: qsTr("Record your location every second, along with date and time, to keep a precise track of your movements.") + font: Theme.defaultFont + color: Theme.secondaryTextColor + wrapMode: Text.WordWrap + width: parent.width + } + + CheckBox { + id: autoTrackPositionCheckBox + text: qsTr("Begin tracking automatically") + font: Theme.defaultFont + indicator.height: 16 + indicator.width: 16 + indicator.implicitHeight: 24 + indicator.implicitWidth: 24 + leftPadding: 1 + checked: true + } + } + } + + QfExpandableGroupBox { + id: qfieldCloudGroupBox + title: qsTr("Backup & collaborate?") + width: parent.width + checked: true + icon: "qrc:/themes/qfield/nodpi/ic_cloud_active_24dp.svg" + + content: Column { + id: databaseAndColabrationColumn + width: parent.width + spacing: 0 + + Label { + text: qsTr("QFieldCloud allows to synchronize and merge the data collected by your team in QField. From small individual projects to large data collection campaigns. ") + "" + qsTr("Learn more about QFieldCloud here") + "." + font: Theme.defaultFont + color: Theme.secondaryTextColor + wrapMode: Text.WordWrap + width: parent.width + textFormat: Text.RichText + onLinkActivated: link => Qt.openUrlExternally(link) + } + } + } + } + } + + QfButton { + id: createProjectButton + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 16 + text: qsTr("Create Project") + + onClicked: { + let selectedBasemap = (baseMapList.currentIndex >= 0 && baseMapList.currentIndex < baseMapList.model.length) ? baseMapList.model[baseMapList.currentIndex].name : "Colorful"; + const isCustomBasemap = (selectedBasemap === "Custom"); + selectedBasemap = isCustomBasemap ? baseMapURL.text : selectedBasemap; + var projectConfig = { + "name": projectName.text, + "isCustomBasemapSelected": isCustomBasemap, + "basemap": selectedBasemap, + "takeNotes": takeNotesGroupBox.checked, + "takeMediaAttachments": takeMediaCheckBox.checked, + "trackPosition": trackPositionGroupBox.checked, + "autoTrackPosition": autoTrackPositionCheckBox.checked, + "useCloud": qfieldCloudGroupBox.checked + }; + create(projectConfig); + } + } +} diff --git a/src/qml/WelcomeScreen.qml b/src/qml/WelcomeScreen.qml index 47949c700a..9de201b5f6 100644 --- a/src/qml/WelcomeScreen.qml +++ b/src/qml/WelcomeScreen.qml @@ -19,6 +19,7 @@ Page { signal openLocalDataPicker signal showQFieldCloudScreen signal showSettings + signal showProjectCreationScreen visible: false focus: visible @@ -459,21 +460,59 @@ Page { width: parent.width spacing: 12 - QfButton { - id: cloudProjectButton + GridLayout { Layout.fillWidth: true - text: qsTr("QFieldCloud projects") - onClicked: { - showQFieldCloudScreen(); + columns: 3 + rows: 2 + + Repeater { + id: actionsRepeater + model: [{ + "icon": Theme.getThemeVectorIcon("ic_cloud_active_24dp"), + "iconColor": "transparent", + "action": function () { + showQFieldCloudScreen(); + } + }, { + "icon": Theme.getThemeVectorIcon("ic_folder_open_black_24dp"), + "iconColor": Theme.mainColor, + "action": function () { + platformUtilities.requestStoragePermission(); + openLocalDataPicker(); + } + }, { + "icon": Theme.getThemeVectorIcon("ic_add_white_24dp"), + "iconColor": Theme.mainColor, + "action": function () { + showProjectCreationScreen(); + } + }] + + delegate: QfToolButton { + Layout.alignment: Qt.AlignHCenter + Layout.preferredWidth: welcomeActions.width / actionsRepeater.count / 1.5 + Layout.preferredHeight: Layout.preferredWidth + icon.width: width / 2.2 + icon.height: height / 2.2 + bgcolor: Theme.darkTheme ? Theme.darkGray : Theme.lightGray + round: true + iconSource: modelData.icon + iconColor: modelData.iconColor + smooth: true + onClicked: modelData.action() + } } - } - QfButton { - id: localProjectButton - Layout.fillWidth: true - text: qsTr("Open local file") - onClicked: { - platformUtilities.requestStoragePermission(); - openLocalDataPicker(); + + Repeater { + model: [qsTr("QFieldCloud\nprojects"), qsTr("Local projects and\n datasets"), qsTr("Create new\nproject")] + + delegate: Text { + Layout.preferredWidth: welcomeActions.width / 3 + text: modelData + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + color: Theme.mainTextColor + } } } diff --git a/src/qml/imports/Theme/QfExpandableGroupBox.qml b/src/qml/imports/Theme/QfExpandableGroupBox.qml new file mode 100644 index 0000000000..76bd55f686 --- /dev/null +++ b/src/qml/imports/Theme/QfExpandableGroupBox.qml @@ -0,0 +1,79 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Rectangle { + implicitHeight: checked ? header.height + body.childrenRect.height + 24 : 60 + + radius: 8 + color: Theme.groupBoxBackgroundColor + clip: true + + property alias title: headerText.text + property alias checked: enabledSwitch.checked + property alias interactive: enabledSwitch.visible + property alias icon: headerIcon.source + default property alias content: body.children + + Behavior on implicitHeight { + NumberAnimation { + duration: 200 + } + } + + Item { + id: header + anchors.left: parent.left + anchors.right: parent.right + height: 60 + anchors.margins: 24 + + Image { + id: headerIcon + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + fillMode: Image.PreserveAspectFit + smooth: true + sourceSize.width: 25 + sourceSize.height: 25 + visible: source != "" + } + + Label { + id: headerText + font: Theme.strongTitleFont + color: Theme.mainTextColor + wrapMode: Text.WordWrap + anchors.left: headerIcon.right + anchors.margins: headerIcon.visible ? 8 : 0 + anchors.right: enabledSwitch.left + anchors.verticalCenter: parent.verticalCenter + } + + QfSwitch { + id: enabledSwitch + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + } + + Rectangle { + id: splitter + width: parent.width + height: 1 + anchors.top: header.bottom + color: Theme.groupBoxSurfaceColor + } + + Item { + id: body + anchors.top: splitter.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 24 + anchors.rightMargin: 24 + anchors.topMargin: 16 + anchors.bottomMargin: 16 + } +} diff --git a/src/qml/imports/Theme/QfPageHeader.qml b/src/qml/imports/Theme/QfPageHeader.qml index fa5e994589..42b162a44e 100644 --- a/src/qml/imports/Theme/QfPageHeader.qml +++ b/src/qml/imports/Theme/QfPageHeader.qml @@ -6,6 +6,8 @@ import Theme ToolBar { property alias title: titleLabel.text + property alias titleColor: titleLabel.color + property alias titleFont: titleLabel.font property bool backgroundFill: true property bool backAsCancel: false @@ -169,7 +171,7 @@ ToolBar { visible: false iconSource: Theme.getThemeVectorIcon("ic_dot_menu_black_24dp") - iconColor: Theme.mainOverlayColor + iconColor: backgroundFill ? Theme.mainOverlayColor : Theme.mainTextColor onClicked: { openMenu(); diff --git a/src/qml/imports/Theme/Theme.qml b/src/qml/imports/Theme/Theme.qml index 3c821f9369..8bf822c740 100644 --- a/src/qml/imports/Theme/Theme.qml +++ b/src/qml/imports/Theme/Theme.qml @@ -23,7 +23,9 @@ QtObject { "toolButtonColor": "#ffffff", "toolButtonBackgroundColor": Theme.darkGray, "toolButtonBackgroundSemiOpaqueColor": Theme.darkGraySemiOpaque, - "scrollBarBackgroundColor": "#bb303030" + "scrollBarBackgroundColor": "#bb303030", + "groupBoxBackgroundColor": "#2a2a2e", + "groupBoxSurfaceColor": "#343437" } property var lightThemeColors: { @@ -44,7 +46,9 @@ QtObject { "toolButtonColor": "#ffffff", "toolButtonBackgroundColor": Theme.darkGray, "toolButtonBackgroundSemiOpaqueColor": Theme.darkGraySemiOpaque, - "scrollBarBackgroundColor": "#aaffffff" + "scrollBarBackgroundColor": "#aaffffff", + "groupBoxBackgroundColor": "#f2f0f5", + "groupBoxSurfaceColor": "#e4e1e8" } property bool darkTheme: false @@ -74,6 +78,9 @@ QtObject { property color scrollBarBackgroundColor: "#aaffffff" + property color groupBoxBackgroundColor: "#f2f0f5" + property color groupBoxSurfaceColor: "#e4e1e8" + property color darkRed: "#c0392b" property color darkGray: "#212121" property color darkGraySemiOpaque: "#4d212121" @@ -153,6 +160,10 @@ QtObject { "pointSize": systemFontPointSize * fontScale * 1.25, "weight": Font.Normal }) + property font strongTitleFont: Qt.font({ + "pointSize": systemFontPointSize * fontScale * 1.25, + "weight": Font.Bold + }) readonly property int popupScreenEdgeMargin: 40 diff --git a/src/qml/imports/Theme/qmldir b/src/qml/imports/Theme/qmldir index dfafe1e7c5..2e98c0ebc1 100644 --- a/src/qml/imports/Theme/qmldir +++ b/src/qml/imports/Theme/qmldir @@ -23,3 +23,4 @@ QfSearchBar 1.0 QfSearchBar.qml QfVisibilityFadingRow 1.0 QfVisibilityFadingRow.qml QfDialog 1.0 QfDialog.qml QfMenu 1.0 QfMenu.qml +QfExpandableGroupBox 1.0 QfExpandableGroupBox.qml diff --git a/src/qml/qgismobileapp.qml b/src/qml/qgismobileapp.qml index c44bebee53..c070f41c23 100644 --- a/src/qml/qgismobileapp.qml +++ b/src/qml/qgismobileapp.qml @@ -4312,6 +4312,10 @@ ApplicationWindow { qfieldSettings.visible = true; } + onShowProjectCreationScreen: { + projectCreationScreen.visible = true; + } + Component.onCompleted: focusstack.addFocusTaker(this) } @@ -4341,6 +4345,22 @@ ApplicationWindow { Component.onCompleted: focusstack.addFocusTaker(this) } + ProjectCreationScreen { + id: projectCreationScreen + visible: false + focus: visible + parent: Overlay.overlay + + width: parent.width + height: parent.height + + Component.onCompleted: focusstack.addFocusTaker(this) + + onCreate: projectConfig => { + console.log(JSON.stringify(projectConfig)); + } + } + QFieldCloudPackageLayersFeedback { id: cloudPackageLayersFeedback visible: false diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index e703d620fd..8b105066b9 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -44,6 +44,7 @@ processingparameterwidgets/distance.qml processingparameterwidgets/number.qml processingparameterwidgets/source.qml + ProjectCreationScreen.qml QFieldAudioRecorder.qml QFieldCamera.qml QFieldSettings.qml @@ -95,6 +96,7 @@ imports/Theme/QfActionButton.qml imports/Theme/QfComboBox.qml imports/Theme/QfDropShadow.qml + imports/Theme/QfExpandableGroupBox.qml imports/Theme/QfOpacityMask.qml imports/Theme/QfToolButton.qml imports/Theme/QfToolButtonDrawer.qml