Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ Input_keystore.keystore
CMakeLists.txt.user
.github/secrets/ios/LutraConsulting*.mobileprovision
google_play_key.json
fastlane/report.xml
fastlane/report.xml
CMakeUserPresets.json
2 changes: 2 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ set(MM_SRCS
streamingintervaltype.cpp
synchronizationerror.cpp
synchronizationmanager.cpp
valuerelationcontroller.cpp
valuerelationfeaturesmodel.cpp
variablesmanager.cpp
workspacesmodel.cpp
Expand Down Expand Up @@ -189,6 +190,7 @@ set(MM_HDRS
synchronizationerror.h
synchronizationmanager.h
synchronizationoptions.h
valuerelationcontroller.h
valuerelationfeaturesmodel.h
variablesmanager.h
workspacesmodel.h
Expand Down
12 changes: 9 additions & 3 deletions app/attributes/attributecontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,10 @@ void AttributeController::recalculateDerivedItems( bool isFormValueChange, bool
// Create context
QgsFields fields = mFeatureLayerPair.feature().fields();
QgsExpressionContext expressionContext = layer->createExpressionContext();
if ( mParentController )
{
expressionContext << QgsExpressionContextUtils::parentFormScope( mParentController->featureLayerPair().feature() );
}
expressionContext << QgsExpressionContextUtils::formScope( mFeatureLayerPair.feature() );
if ( mVariablesManager )
expressionContext << mVariablesManager->positionScope();
Expand All @@ -900,7 +904,7 @@ void AttributeController::recalculateDerivedItems( bool isFormValueChange, bool
// Evaluate HTML and Text element expressions
recalculateRichTextWidgets( changedFormItems, expressionContext );

// Evaluate tab items visiblity
// Evaluate tab items visibility
{
QVector<std::shared_ptr<TabItem>>::iterator tabItemsIterator = mTabItems.begin();
while ( tabItemsIterator != mTabItems.end() )
Expand Down Expand Up @@ -1632,7 +1636,8 @@ void AttributeController::renamePhotos()
continue;
}

const QString targetDir = InputUtils::resolveTargetDir( QgsProject::instance()->homePath(), config, mFeatureLayerPair, QgsProject::instance() );
const FeatureLayerPair parentPair = mParentController ? mParentController->featureLayerPair() : FeatureLayerPair();
const QString targetDir = InputUtils::resolveTargetDir( QgsProject::instance()->homePath(), config, mFeatureLayerPair, parentPair, QgsProject::instance() );
const QString prefix = InputUtils::resolvePrefixForRelativePath( config[ QStringLiteral( "RelativeStorage" ) ].toInt(), QgsProject::instance()->homePath(), targetDir );
const QString src = InputUtils::getAbsolutePath( mFeatureLayerPair.feature().attribute( item->fieldIndex() ).toString(), prefix );
QString newName = val.toString();
Expand Down Expand Up @@ -1675,7 +1680,8 @@ void AttributeController::saveSketches()
if ( item->rawValue().isValid() )
{
const QVariantMap config = item->editorWidgetConfig();
const QString targetDir = InputUtils::resolveTargetDir( QgsProject::instance()->homePath(), config, mFeatureLayerPair, QgsProject::instance() );
const FeatureLayerPair &parentPair = mParentController ? mParentController->featureLayerPair() : FeatureLayerPair();
const QString targetDir = InputUtils::resolveTargetDir( QgsProject::instance()->homePath(), config, mFeatureLayerPair, parentPair, QgsProject::instance() );
const QString prefix = InputUtils::resolvePrefixForRelativePath( config[ QStringLiteral( "RelativeStorage" ) ].toInt(), QgsProject::instance()->homePath(), targetDir );
const QString src = InputUtils::getAbsolutePath( mFeatureLayerPair.feature().attribute( item->fieldIndex() ).toString(), prefix );

Expand Down
10 changes: 6 additions & 4 deletions app/inpututils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -987,7 +987,7 @@ bool InputUtils::fileExists( const QString &path )
return ( check_file.exists() && check_file.isFile() );
}

QString InputUtils::resolveTargetDir( const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, QgsProject *activeProject )
QString InputUtils::resolveTargetDir( const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, const FeatureLayerPair &parentPair, QgsProject *activeProject )
{
QString expression;
QMap<QString, QVariant> collection = config.value( QStringLiteral( "PropertyCollection" ) ).toMap();
Expand All @@ -1001,7 +1001,7 @@ QString InputUtils::resolveTargetDir( const QString &homePath, const QVariantMap

if ( !expression.isEmpty() )
{
QString result = evaluateExpression( pair, activeProject, expression );
QString result = evaluateExpression( pair, parentPair, activeProject, expression );
sanitizePath( result );
return result;
}
Expand Down Expand Up @@ -1043,7 +1043,7 @@ QString InputUtils::getAbsolutePath( const QString &path, const QString &prefixP
QString InputUtils::resolvePath( const QString &path, const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, QgsProject *activeProject )
{
int relativeStorageMode = config.value( QStringLiteral( "RelativeStorage" ) ).toInt();
QString targetDir = resolveTargetDir( homePath, config, pair, activeProject );
QString targetDir = resolveTargetDir( homePath, config, pair, FeatureLayerPair(), activeProject );
QString prefixToRelativePath = resolvePrefixForRelativePath( relativeStorageMode, homePath, targetDir );

return getAbsolutePath( path, prefixToRelativePath );
Expand Down Expand Up @@ -1550,12 +1550,14 @@ QString InputUtils::dumpScreenInfo() const
return msg;
}

QString InputUtils::evaluateExpression( const FeatureLayerPair &pair, QgsProject *activeProject, const QString &expression )
QString InputUtils::evaluateExpression( const FeatureLayerPair &pair, const FeatureLayerPair &parentPair, QgsProject *activeProject, const QString &expression )
{
QList<QgsExpressionContextScope *> scopes;
scopes << QgsExpressionContextUtils::globalScope();
scopes << QgsExpressionContextUtils::projectScope( activeProject );
scopes << QgsExpressionContextUtils::layerScope( pair.layer() );
scopes << QgsExpressionContextUtils::parentFormScope( parentPair.feature() );
scopes << QgsExpressionContextUtils::formScope( pair.feature() );

QgsExpressionContext context( scopes );
context.setFeature( pair.feature() );
Expand Down
13 changes: 7 additions & 6 deletions app/inpututils.h
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,15 @@ class InputUtils: public QObject
* @param activeProject QgsProject - needed for expression evaluation
* @return Path to the image
*/
Q_INVOKABLE static QString resolvePath( const QString &path, const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, QgsProject *activeProject );
static QString resolvePath( const QString &path, const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, QgsProject *activeProject );

/**
* This evaluates the "default path" with the following order:
* 1. evaluate default path expression if defined,
* 2. use default path value if not empty,
* 3. use project home folder
*/
Q_INVOKABLE static QString resolveTargetDir( const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, QgsProject *activeProject );
Q_INVOKABLE static QString resolveTargetDir( const QString &homePath, const QVariantMap &config, const FeatureLayerPair &pair, const FeatureLayerPair &parentPair, QgsProject *activeProject );

/**
* Function used for resolving path of an image for a field with ExternalResource widget type.
Expand Down Expand Up @@ -472,12 +472,13 @@ class InputUtils: public QObject

/**
* Evaluates expression.
* \param pair Used to define a context scope.
* \param activeProject Used to define a context scope.
* \param expression
* \param pair Used to define layer & form context scope.
* \param parentPair Used to define parent form context scope.
* \param activeProject Used to define project context scope.
* \param expression Expression to evaluate
* \return Evaluated expression
*/
Q_INVOKABLE static QString evaluateExpression( const FeatureLayerPair &pair, QgsProject *activeProject, const QString &expression );
static QString evaluateExpression( const FeatureLayerPair &pair, const FeatureLayerPair &parentPair, QgsProject *activeProject, const QString &expression );

/**
* Returns the QVariant typeName of a \a field.
Expand Down
3 changes: 2 additions & 1 deletion app/layerfeaturesmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class LayerFeaturesModel : public FeaturesModel

virtual void setupFeatureRequest( QgsFeatureRequest &request );

virtual QString buildSearchExpression();

virtual void populate();
void reset() override;

Expand All @@ -104,7 +106,6 @@ class LayerFeaturesModel : public FeaturesModel
void onFutureFinished();

private:
QString buildSearchExpression();

//! Performs getFeatures on layer. Takes ownership of \a layer and tries to move it to current thread.
QgsFeatureList fetchFeatures( QgsVectorLayerFeatureSource *layer, QgsFeatureRequest req, int searchId );
Expand Down
2 changes: 2 additions & 0 deletions app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
#include "relationreferencefeaturesmodel.h"
#include "fieldvalidator.h"
#include "valuerelationfeaturesmodel.h"
#include "valuerelationcontroller.h"
#include "snaputils.h"
#include "guidelinecontroller.h"
#include "multieditmanager.h"
Expand Down Expand Up @@ -332,6 +333,7 @@ void initDeclarative()
qmlRegisterType< LayerFeaturesModel >( "mm", 1, 0, "LayerFeaturesModel" );
qmlRegisterType< RelationFeaturesModel >( "mm", 1, 0, "RelationFeaturesModel" );
qmlRegisterType< ValueRelationFeaturesModel >( "mm", 1, 0, "ValueRelationFeaturesModel" );
qmlRegisterType< ValueRelationController >( "mm", 1, 0, "ValueRelationController" );
qmlRegisterType< RelationReferenceFeaturesModel >( "mm", 1, 0, "RelationReferenceFeaturesModel" );
qmlRegisterType< BluetoothDiscoveryModel >( "mm", 1, 0, "BluetoothDiscoveryModel" );
qmlRegisterType< PositionTrackingManager >( "mm", 1, 0, "PositionTrackingManager" );
Expand Down
6 changes: 4 additions & 2 deletions app/qml/components/MMButton.qml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ Button {

implicitWidth: {
let margin = __style.margin20
if ( root.size === MMButton.Sizes.ExtraSmall ) margin = __style.margin8
if ( root.type === MMButton.Types.Tertiary ) margin = 0
else if ( root.size === MMButton.Sizes.ExtraSmall ) margin = __style.margin8
else if ( root.size === MMButton.Sizes.Small ) margin = __style.margin16
return row.paintedChildrenWidth + 2 * margin
}
Expand Down Expand Up @@ -214,7 +215,8 @@ Button {
property real paintedChildrenWidth: buttonIconLeft.paintedWidth + buttonContent.implicitWidth + buttonIconRight.paintedWidth + spacing
property real maxWidth: {
let margin = __style.margin20
if ( root.size === MMButton.Sizes.ExtraSmall ) margin = __style.margin8
if ( root.type === MMButton.Types.Tertiary ) margin = 0
else if ( root.size === MMButton.Sizes.ExtraSmall ) margin = __style.margin8
else if ( root.size === MMButton.Sizes.Small ) margin = __style.margin16
return parent.width - 2 * margin
}
Expand Down
11 changes: 9 additions & 2 deletions app/qml/components/MMDrawerHeader.qml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import QtQuick.Layouts

import "."

//! Best to use MMDrawerHeader as the header component for the MMPage object
//! Best to use MMDrawerHeader as the header component for the MMDrawer object

Rectangle {
id: root
Expand All @@ -25,6 +25,8 @@ Rectangle {

property alias closeButton: closeBtn
property alias topLeftItemContent: topLeftButtonGroup.children
property alias topLeftItem: topLeftButtonGroup
property alias titleComponent: headerTitleText

color: __style.transparentColor

Expand All @@ -36,11 +38,16 @@ Rectangle {
Item {
id: topLeftButtonGroup

x: __style.pageMargins + __style.safeAreaLeft
y: root.height / 2 - height / 2

width: childrenRect.width
height: parent.height
height: childrenRect.height
}

Text {
id: headerTitleText

property real leftMarginShift: {
return Math.max( internal.closeBtnRealWidth, topLeftButtonGroup.width ) + internal.headerSpacing + __style.pageMargins
}
Expand Down
10 changes: 9 additions & 1 deletion app/qml/components/MMListMultiselectDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ MMDrawer {

property bool withSearch: true
property bool multiSelect: false
property bool isLoading: false
property var selected: [] // in/out property, contains a list of (pre-)selected item values

property bool showFullScreen: false
Expand Down Expand Up @@ -150,7 +151,9 @@ MMDrawer {
Component {
id: defaultEmptyStateComponent

MMListEmptyLoaderDelegate {}
MMListEmptyLoaderDelegate {
isLoading: root.isLoading
}
}

// QDate/QDateTime values get parsed to JS Date objects in QML, and they do strict comparison by default, which also
Expand All @@ -173,4 +176,9 @@ MMDrawer {
root.selected = root.selected.filter( x => !isEqualDate( x, value ) )
}
}

function focusSearchBar() {
root.showFullScreen = true
searchBar.textField.forceActiveFocus()
}
}
6 changes: 0 additions & 6 deletions app/qml/filters/MMFiltersDrawer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ MMComponents.MMDrawer {
bgndColorHover: __style.grapeColor
fontColorHover: __style.negativeLightColor

anchors {
left: parent.left
leftMargin: __style.pageMargins + __style.safeAreaLeft
verticalCenter: parent.verticalCenter
}

onClicked: {
internal.filterValues = {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,10 @@ Column {
sourceComponent: MMComponents.MMListMultiselectDrawer {
drawerHeader.title: root.filterName

withSearch: uniqueValuesModel.count > 5
withSearch: uniqueValuesModel.count > 8
multiSelect: root.isMultiSelect

emptyStateDelegate: Component {
MMComponents.MMListEmptyLoaderDelegate {
isLoading: uniqueValuesModel.isLoading
}
}
isLoading: uniqueValuesModel.isLoading

list.model: MM.SearchProxyModel {
id: searchProxyModel
Expand Down
8 changes: 2 additions & 6 deletions app/qml/filters/components/MMFilterDropdownValueMapInput.qml
Original file line number Diff line number Diff line change
Expand Up @@ -69,14 +69,10 @@ Column {
sourceComponent: MMComponents.MMListMultiselectDrawer {
drawerHeader.title: root.filterName

withSearch: valueMapModel.count > 5
withSearch: valueMapModel.count > 8
multiSelect: root.isMultiSelect

emptyStateDelegate: Component {
MMComponents.MMListEmptyLoaderDelegate {
isLoading: valueMapModel.isLoading
}
}
isLoading: valueMapModel.isLoading

list.model: MM.SearchProxyModel {
id: searchProxyModel
Expand Down
37 changes: 27 additions & 10 deletions app/qml/filters/components/MMFilterDropdownValueRelationInput.qml
Original file line number Diff line number Diff line change
Expand Up @@ -65,30 +65,47 @@ Column {

active: false


sourceComponent: MMComponents.MMListMultiselectDrawer {
id: listDrawer

drawerHeader.title: root.filterName

withSearch: vrDropdownModel.count > 5
withSearch: vrDropdownModel.count > 8
multiSelect: root.isMultiSelect

emptyStateDelegate: Component {
MMComponents.MMListEmptyLoaderDelegate {
isLoading: vrDropdownModel.fetchingResults
}
}
isLoading: vrDropdownModel.fetchingResults

list.model: MM.ValueRelationFeaturesModel {
id: vrDropdownModel

config: root.widgetConfig

property bool firstFetchFinished: false

// We show search for lists with more then 8 features.
// We need to intentionally break the binding here because "count" changes
// when users search for something and that would hide the search bar
onFetchingResultsChanged: {
if ( !fetchingResults && !firstFetchFinished )
{
if ( count > 8 )
{
listDrawer.withSearch = true
}
else
{
listDrawer.withSearch = false
}

firstFetchFinished = true
}
}
}

textRole: "FeatureTitle"
valueRole: "Key"
textRole: "ValueColumn"
valueRole: "KeyColumn"

onSelectionFinished: function( selectedItems ) {

//
// Large fids could be converted to scientific notation on their way to cpp,
// so we convert them to string first in JS.
Expand Down
14 changes: 3 additions & 11 deletions app/qml/form/MMFormPage.qml
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ Page {

property var fieldActiveProject: root.project
property var fieldAssociatedRelation: model.Relation
property var fieldFeatureLayerPair: root.controller.featureLayerPair
property MM.AttributeController fieldController: root.controller
property string fieldHomePath: root.project ? root.project.homePath : "" // for photo editor

property bool fieldRememberValueSupported: root.controller.rememberAttributesController.rememberValuesAllowed && root.state === "add" && model.EditorWidget !== "Hidden" && Type === MM.FormItem.Field
Expand Down Expand Up @@ -372,19 +372,11 @@ Page {
Connections {
target: root.controller

// Important for relation form editors // <--- TODO: remove me if all works, unused
function onFeatureLayerPairChanged() {
if ( formEditorsLoader.item && formEditorsLoader.item.featureLayerPairChanged )
{
formEditorsLoader.item.featureLayerPairChanged()
}
}

// Important for value relation form editors
function onFormRecalculated() {
if ( formEditorsLoader.item && formEditorsLoader.item.reload )
if ( formEditorsLoader.item && formEditorsLoader.item.hotReload )
{
formEditorsLoader.item.reload()
formEditorsLoader.item.hotReload()
}
}
}
Expand Down
Loading
Loading