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