Skip to content

Commit 7e0447f

Browse files
Filter floating chip (#4427)
1 parent a7214a2 commit 7e0447f

7 files changed

Lines changed: 131 additions & 103 deletions

File tree

app/icons/Filter.svg

Lines changed: 1 addition & 1 deletion
Loading

app/icons/FilterFilled.svg

Lines changed: 0 additions & 3 deletions
This file was deleted.

app/icons/icons.qrc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
<file>Features.svg</file>
2929
<file>FeaturesFilled.svg</file>
3030
<file>Filter.svg</file>
31-
<file>FilterFilled.svg</file>
3231
<file>GPSAntennaHeight.svg</file>
3332
<file>GPSIcon.svg</file>
3433
<file>GPSSatellite.svg</file>

app/mmstyle.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,6 @@ class MMStyle: public QObject
129129
Q_PROPERTY( QUrl facebookIcon READ facebookIcon CONSTANT )
130130
Q_PROPERTY( QUrl featuresIcon READ featuresIcon CONSTANT )
131131
Q_PROPERTY( QUrl filterIcon READ filterIcon CONSTANT )
132-
Q_PROPERTY( QUrl filterFilledIcon READ filterFilledIcon CONSTANT )
133132
Q_PROPERTY( QUrl globeIcon READ globeIcon CONSTANT )
134133
Q_PROPERTY( QUrl globalIcon READ globalIcon CONSTANT )
135134
Q_PROPERTY( QUrl gpsIcon READ gpsIcon CONSTANT )
@@ -435,7 +434,6 @@ class MMStyle: public QObject
435434
QUrl deleteIcon() const {return QUrl( "qrc:/Delete.svg" );}
436435
QUrl featuresIcon() const {return QUrl( "qrc:/Features.svg" );}
437436
QUrl filterIcon() const {return QUrl( "qrc:/Filter.svg" );}
438-
QUrl filterFilledIcon() const {return QUrl( "qrc:/FilterFilled.svg" );}
439437
QUrl downloadIcon() const {return QUrl( "qrc:/Download.svg" );}
440438
QUrl uploadIcon() const {return QUrl( "qrc:/Upload.svg" );}
441439
QUrl editIcon() const {return QUrl( "qrc:/Edit.svg" );}

app/qml/filters/MMFilterLayerSection.qml

Lines changed: 104 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Column {
3838

3939
property var fieldInfo: modelData
4040
property string fieldName: fieldInfo ? fieldInfo.name : ""
41-
property string fieldDisplayName: fieldInfo ? (fieldInfo.displayName || fieldInfo.name) : ""
41+
property string fieldDisplayName: fieldInfo ? ( fieldInfo.displayName || fieldInfo.name ) : ""
4242
property string filterType: fieldInfo ? fieldInfo.filterType : "text"
4343
property var currentValue: fieldInfo ? fieldInfo.currentValue : null
4444
property var currentValueTo: fieldInfo ? fieldInfo.currentValueTo : null
@@ -64,11 +64,11 @@ Column {
6464
spacing: __style.margin12
6565
visible: fieldDelegate.filterType === "number"
6666

67-
property bool rangeInvalid: {
68-
let fromVal = parseFloat(fromNumberInput.text)
69-
let toVal = parseFloat(toNumberInput.text)
70-
return !isNaN(fromVal) && !isNaN(toVal) && fromVal > toVal
71-
}
67+
property bool rangeInvalid: {
68+
let fromVal = parseFloat( fromNumberInput.text )
69+
let toVal = parseFloat( toNumberInput.text )
70+
return !isNaN( fromVal ) && !isNaN( toVal ) && fromVal > toVal
71+
}
7272

7373
MMFilterTextInput {
7474
id: fromNumberInput
@@ -112,14 +112,14 @@ Column {
112112
spacing: __style.margin12
113113
visible: fieldDelegate.filterType === "date"
114114

115-
property bool rangeInvalid: {
116-
if (!fromDateInput.selectedDate || !toDateInput.selectedDate) return false
117-
return fromDateInput.selectedDate > toDateInput.selectedDate
118-
}
115+
property bool rangeInvalid: {
116+
if ( !fromDateInput.selectedDate || !toDateInput.selectedDate ) return false
117+
return fromDateInput.selectedDate > toDateInput.selectedDate
118+
}
119119

120-
Item {
121-
width: (parent.width - __style.margin12) / 2
122-
height: fromDateInput.height
120+
Item {
121+
width: ( parent.width - __style.margin12 ) / 2
122+
height: fromDateInput.height
123123

124124
MMFilterTextInput {
125125
id: fromDateInput
@@ -130,25 +130,32 @@ Column {
130130

131131
checked: selectedDate !== null
132132

133-
Component.onCompleted: {
134-
let val = fieldDelegate.currentValue
135-
if (val !== null && val !== undefined) {
136-
let d = new Date(val)
137-
if (!isNaN(d.getTime())) selectedDate = d
133+
Component.onCompleted: {
134+
let val = fieldDelegate.currentValue
135+
if ( val !== null && val !== undefined ) {
136+
let d = new Date( val )
137+
if ( !isNaN( d.getTime() ) ) selectedDate = d
138+
}
138139
}
139-
}
140140

141-
placeholderText: qsTr("From")
142-
text: {
143-
if (!selectedDate) return ""
144-
if (fieldDelegate.hasTime) return Qt.formatDateTime(selectedDate, Qt.DefaultLocaleShortDate)
145-
return Qt.formatDate(selectedDate, Qt.DefaultLocaleShortDate)
146-
}
147-
errorMsg: dateRangeRow.rangeInvalid ? qsTr("\"From\" must be less than \"To\"") : ""
141+
placeholderText: qsTr( "From" )
142+
text: {
143+
if ( !selectedDate ) return ""
144+
if ( fieldDelegate.hasTime ) return Qt.formatDateTime( selectedDate, Qt.DefaultLocaleShortDate )
145+
return Qt.formatDate( selectedDate, Qt.DefaultLocaleShortDate )
146+
}
147+
textField.readOnly: true
148+
errorMsg: dateRangeRow.rangeInvalid ? qsTr( "\"From\" must be less than \"To\"" ) : ""
149+
150+
rightContent: MMIcon {
151+
size: __style.icon24
152+
source: fromDateInput.selectedDate ? __style.closeIcon : __style.calendarIcon
153+
color: fromDateInput.iconColor
154+
}
148155

149156
onTextClicked: fromCalendarLoader.active = true
150157
onRightContentClicked: {
151-
if (fromDateInput.selectedDate) {
158+
if ( fromDateInput.selectedDate ) {
152159
fromDateInput.selectedDate = null
153160
let toDate = toDateInput.selectedDate ? toDateInput.selectedDate : null
154161
__activeProject.filterController.setDateFilter(root.layerId, fieldDelegate.fieldName, null, toDate, fieldDelegate.hasTime)
@@ -186,9 +193,9 @@ Column {
186193
}
187194
}
188195

189-
Item {
190-
width: (parent.width - __style.margin12) / 2
191-
height: toDateInput.height
196+
Item {
197+
width: ( parent.width - __style.margin12 ) / 2
198+
height: toDateInput.height
192199

193200
MMFilterTextInput {
194201
id: toDateInput
@@ -199,24 +206,31 @@ Column {
199206

200207
checked: selectedDate !== null
201208

202-
Component.onCompleted: {
203-
let val = fieldDelegate.currentValueTo
204-
if (val !== null && val !== undefined) {
205-
let d = new Date(val)
206-
if (!isNaN(d.getTime())) selectedDate = d
209+
Component.onCompleted: {
210+
let val = fieldDelegate.currentValueTo
211+
if ( val !== null && val !== undefined ) {
212+
let d = new Date( val )
213+
if ( !isNaN( d.getTime() ) ) selectedDate = d
214+
}
207215
}
208-
}
209216

210-
placeholderText: qsTr("To")
211-
text: {
212-
if (!selectedDate) return ""
213-
if (fieldDelegate.hasTime) return Qt.formatDateTime(selectedDate, Qt.DefaultLocaleShortDate)
214-
return Qt.formatDate(selectedDate, Qt.DefaultLocaleShortDate)
215-
}
217+
placeholderText: qsTr( "To" )
218+
text: {
219+
if ( !selectedDate ) return ""
220+
if ( fieldDelegate.hasTime ) return Qt.formatDateTime( selectedDate, Qt.DefaultLocaleShortDate )
221+
return Qt.formatDate( selectedDate, Qt.DefaultLocaleShortDate )
222+
}
223+
textField.readOnly: true
224+
225+
rightContent: MMIcon {
226+
size: __style.icon24
227+
source: toDateInput.selectedDate ? __style.closeIcon : __style.calendarIcon
228+
color: toDateInput.iconColor
229+
}
216230

217231
onTextClicked: toCalendarLoader.active = true
218232
onRightContentClicked: {
219-
if (toDateInput.selectedDate) {
233+
if ( toDateInput.selectedDate ) {
220234
toDateInput.selectedDate = null
221235
let fromDate = fromDateInput.selectedDate ? fromDateInput.selectedDate : null
222236
__activeProject.filterController.setDateFilter(root.layerId, fieldDelegate.fieldName, fromDate, null, fieldDelegate.hasTime)
@@ -261,19 +275,21 @@ Column {
261275
visible: fieldDelegate.filterType === "text"
262276
type: "text"
263277
title: fieldDelegate.fieldDisplayName
264-
placeholderText: qsTr("Type to filter...")
278+
placeholderText: qsTr( "Type to filter..." )
265279

266280
text: {
267281
let val = fieldDelegate.currentValue
268-
if (val !== null && val !== undefined && val !== "") return String(val)
282+
if ( val !== null && val !== undefined && val !== "" ) {
283+
return String( val )
284+
}
269285
return ""
270286
}
271287

272288
property bool initialized: false
273289
Component.onCompleted: initialized = true
274290

275291
onTextChanged: {
276-
if (!initialized) return
292+
if ( !initialized ) return
277293
// Pass raw text to C++ - validation happens there
278294
__activeProject.filterController.setTextFilter(root.layerId, fieldDelegate.fieldName, text)
279295
}
@@ -356,13 +372,21 @@ Column {
356372
type: "dropdown"
357373
checked: text !== ""
358374

359-
placeholderText: qsTr("Select...")
360-
text: {
361-
let texts = fieldDelegate.currentValueTexts
362-
if (!texts || texts.length === 0) return ""
363-
if (fieldDelegate.multiSelect && texts.length > 1) return qsTr("%1 selected").arg(texts.length)
364-
return texts.join(", ")
365-
}
375+
placeholderText: qsTr( "Select..." )
376+
text: {
377+
let texts = fieldDelegate.currentValueTexts
378+
if ( !texts || texts.length === 0 ) return ""
379+
if ( fieldDelegate.multiSelect && texts.length > 1 ) {
380+
return qsTr( "%1 selected" ).arg( texts.length )
381+
}
382+
return texts.join( ", " )
383+
}
384+
385+
rightContent: MMIcon {
386+
size: __style.icon24
387+
source: dropdownInput.text !== "" ? __style.closeIcon : __style.arrowDownIcon
388+
color: dropdownInput.iconColor
389+
}
366390

367391
onTextClicked: dropdownDrawerLoader.active = true
368392
onRightContentClicked: {
@@ -394,10 +418,10 @@ Column {
394418

395419
list.model: ListModel { id: dropdownListModel }
396420

397-
onSearchTextChanged: function(searchText) {
398-
internal.pendingSearchText = searchText
399-
searchDebounceTimer.restart()
400-
}
421+
onSearchTextChanged: function( searchText ) {
422+
internal.pendingSearchText = searchText
423+
searchDebounceTimer.restart()
424+
}
401425

402426
onSelectionFinished: function(selectedItems) {
403427
__activeProject.filterController.setDropdownFilter(root.layerId, fieldDelegate.fieldName, selectedItems, fieldDelegate.multiSelect)
@@ -413,39 +437,44 @@ Column {
413437
property string pendingSearchText: ""
414438
}
415439

416-
Timer {
417-
id: searchDebounceTimer
418-
interval: 300
419-
repeat: false
420-
onTriggered: populateOptions(internal.pendingSearchText)
421-
}
440+
Timer {
441+
id: searchDebounceTimer
442+
interval: 300
443+
repeat: false
444+
onTriggered: {
445+
populateOptions( internal.pendingSearchText )
446+
}
447+
}
422448

423449
function populateOptions(searchText) {
424450
let options = __activeProject.filterController.getDropdownOptions(root.vectorLayer, fieldDelegate.fieldName, searchText, 100)
425451
dropdownListModel.clear()
426-
for (let i = 0; i < options.length; i++) {
427-
dropdownListModel.append(options[i])
452+
for ( let i = 0; i < options.length; i++ ) {
453+
dropdownListModel.append( options[i] )
428454
}
429455
}
430456

431-
Component.onCompleted: {
432-
// QStringList from C++ does not support JS includes(), convert first
433-
let val = fieldDelegate.currentValue
434-
if (val && val.length > 0) {
435-
let arr = []
436-
for (let i = 0; i < val.length; i++) {
437-
arr.push(String(val[i]))
457+
Component.onCompleted: {
458+
// Set selected imperatively — QStringList from C++ needs
459+
// conversion to a plain JS array for includes() to work
460+
let val = fieldDelegate.currentValue
461+
if ( val && val.length > 0 ) {
462+
let arr = []
463+
for ( let i = 0; i < val.length; i++ ) {
464+
arr.push( String( val[i] ) )
465+
}
466+
selected = arr
438467
}
439-
selected = arr
468+
populateOptions( "" )
469+
open()
440470
}
441-
populateOptions("")
442-
open()
443471
}
444472
}
445473
}
446474
}
447475
}
448476
}
477+
}
449478

450479
// bottom spacer
451480
Item {

app/qml/filters/MMFiltersPanel.qml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ MMComponents.MMDrawer {
9090

9191
drawerContent: Item {
9292
width: parent.width
93-
height: root.maxHeightHit ? root.drawerContentAvailableHeight : (contentColumn.implicitHeight + __style.margin12 + showResultsButton.height)
93+
height: root.maxHeightHit ? root.drawerContentAvailableHeight : ( contentColumn.implicitHeight + __style.margin12 + showResultsButton.height )
9494

9595
MMComponents.MMScrollView {
9696
id: scrollView
@@ -170,7 +170,7 @@ MMComponents.MMDrawer {
170170
Connections {
171171
target: __activeProject
172172

173-
function onProjectReloaded(qgsProject) {
173+
function onProjectReloaded( qgsProject ) {
174174
internal.refreshLayers()
175175
}
176176
}

app/qml/map/MMMapController.qml

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -588,25 +588,6 @@ Item {
588588
}
589589
}
590590

591-
// Filter indicator button - left side, 20% from top
592-
MMMapButton {
593-
id: filterIndicatorButton
594-
595-
visible: root.state === "view" && __activeProject.filterController && (__activeProject.filterController.hasActiveFilters || AppSettings.alwaysShowFilterButton)
596-
iconSource: __activeProject.filterController && __activeProject.filterController.hasActiveFilters ? __style.filterFilledIcon : __style.filterIcon
597-
bgndColor: __activeProject.filterController && __activeProject.filterController.hasActiveFilters ? __style.sandColor : __style.polarColor
598-
599-
anchors {
600-
left: parent.left
601-
top: parent.top
602-
topMargin: parent.height * 0.2
603-
}
604-
605-
onClicked: {
606-
root.openFiltersPanel()
607-
}
608-
}
609-
610591
Item {
611592
// bottom buttons group
612593
width: parent.width
@@ -876,6 +857,30 @@ Item {
876857
}
877858
}
878859

860+
MMMapButton {
861+
id: filterIndicatorButton
862+
863+
visible: root.state === "view" && __activeProject.filterController?.hasActiveFilters
864+
iconSource: __style.filterIcon
865+
bgndColor: __activeProject.filterController?.filtersEnabled ? __style.positiveColor : __style.polarColor
866+
867+
onClicked: {
868+
root.openFiltersPanel()
869+
}
870+
871+
onClickAndHold: {
872+
if ( __activeProject.filterController ) {
873+
const enabling = !__activeProject.filterController.filtersEnabled
874+
__activeProject.filterController.filtersEnabled = enabling
875+
if ( enabling ) {
876+
__notificationModel.addSuccess( qsTr( "All filters have been re-enabled" ) )
877+
} else {
878+
__notificationModel.addWarning( qsTr( "All filters have been temporarily disabled. Press and hold to re-enable them" ) )
879+
}
880+
}
881+
}
882+
}
883+
879884
MMMapButton {
880885
id: gpsButton
881886

0 commit comments

Comments
 (0)