Move Plasma's SortFilterProxyModel into KItemModel's QML plugin
ClosedPublic

Authored by davidedmundson on Nov 15 2019, 11:36 AM.

Details

Summary

It exposes QSFPM in a usable way from QML, referring to roles by name
and exposing a way to perform JS callbacks as an advanced filter method.

Diff Detail

Repository
R275 KItemModels
Lint
Automatic diff as part of commit; lint not applicable.
Unit
Automatic diff as part of commit; unit tests not applicable.
There are a very large number of changes, so older changes are hidden. Show Older Changes
Restricted Application added a project: Frameworks. · View Herald TranscriptNov 15 2019, 11:36 AM
Restricted Application added a subscriber: kde-frameworks-devel. · View Herald Transcript
davidedmundson requested review of this revision.Nov 15 2019, 11:36 AM
mart added a subscriber: mart.Nov 15 2019, 11:38 AM

+1: i found myself many times wanting to use this but not being able because of plasma dep.
the code is used a lot in plasmoids and seems quite robust

davidedmundson edited the summary of this revision. (Show Details)Nov 15 2019, 11:41 AM
ahiemstra added inline comments.
src/qml/sortfiltermodel.h
63 ↗(On Diff #69800)

This (and sortRole below) hides the properties from QSortFilterProxy with the same name. Maybe it is a better idea to use filterRoleName/sortRoleName?

74 ↗(On Diff #69800)

Having a sortColumn property would also be useful, since that is apparently missing from QSortFilterProxy. I seem to recall that was added to the Plasma version a while ago.

src/qml/sortfiltermodel.h
63 ↗(On Diff #69800)

Or potentially we could just kill our handling of it?

broulik added inline comments.
src/qml/sortfiltermodel.cpp
84 ↗(On Diff #69800)

Can we also just have it send source_row and source_parent since we have proper QModelIndex handling now?

97 ↗(On Diff #69800)

QRegExp is deprecated, use setFilterRegularExpression taking a QRegularExpression

src/qml/sortfiltermodel.h
43 ↗(On Diff #69800)

Can this be a QRegularExpression?
I would assume a JS RegExp object (or regexp literal /syntax/) was supported (didn't test)

48 ↗(On Diff #69800)

Given this is a new class/import, remove REVISION

56 ↗(On Diff #69800)

Could this use a RESET method instead of telling people to assign null?

78 ↗(On Diff #69800)

Is this needed?

80 ↗(On Diff #69800)

Remove

117 ↗(On Diff #69800)

I don't like this. We have proper QModelIndex handling in QML now, this is a hack imho

119 ↗(On Diff #69800)

Remove, QAbstractProxyModel::mapToSource (and fromSource) is Q_INVOKABLE and we have proper QModelIndex handling in QML now

138 ↗(On Diff #69800)

Does this need a d pointer or is it just for use in QML? Perhaps rename the header to _p.h then?

davidedmundson marked 3 inline comments as done.Nov 18 2019, 10:49 AM
davidedmundson added inline comments.
src/qml/sortfiltermodel.cpp
84 ↗(On Diff #69800)

We could.

Though means everyone doing a lookup immediately, handling columns and roles properly.

More powerful, but slightly more complex for the user

There's also the option of having two proxies with different behaviours?

@mart thoughts?

src/qml/sortfiltermodel.h
43 ↗(On Diff #69800)
63 ↗(On Diff #69800)

Did some investigation we have our handling just to emit a signal when the property changes!

Another good thing to try to upstream.

Keeping the same name allows us to subtly drop the API in the future and keep clients working.

78 ↗(On Diff #69800)

I think so. We've implemented it in a lot of plasma models, presumably for some reason.

Would be a good thing to try and upstream though.

138 ↗(On Diff #69800)

I don't see why we would need a d pointer. We may as well self an allocation.

Perhaps rename the header to _p.h then?

We haven't done that universally in all frameworks, but sure I could.

davidedmundson edited the summary of this revision. (Show Details)

Only some changes done, not all
Updating now to share added unit test

broulik added inline comments.Nov 18 2019, 11:01 AM
src/qml/sortfiltermodel.h
43 ↗(On Diff #69800)

Bummer. I tried, a JS RegExp (both new RegExp() and /literal syntax/) get turned into a QRegExp. It does *not* work with QRegularExpression :(

There's a RegularExpressionValidator which uses QRegularExpression but it's in Qt 5.14. Maybe they fixed that recently - doesn't really help us here, though.

63 ↗(On Diff #69800)

Another good thing to try to upstream.

+1 given they're all QObjects already anyway

78 ↗(On Diff #69800)

presumably for some reason.

Either because someone was too lazy to use the respective property on the Repeater or ListView or before rowCount() was Q_INVOKABLE. Not sure it's upstreamable since it assumes it's a flast list. Maybe something for QAbstract*List*Model rather than QAbstract*Item*Model

138 ↗(On Diff #69800)

Was just wondering but if it's only to be used from QML and not exported, doesn't need one I suppose.

davidedmundson marked 3 inline comments as done.

asdf

davidedmundson marked an inline comment as done.Nov 18 2019, 11:07 AM
src/qml/sortfiltermodel.h
78 ↗(On Diff #69800)

rowCount being invokable seems enough in the cases I've found in Plasma, will drop this.

All changes except the parameters of filterRowCallback

Implement new arguments for filterAcceptsRow passing source_row, source_parent.

It makes for slightly heavier messier JS, but it also allows for super flexibility
beyond just having the value retried and it's familiar for Qt programmers.

Maybe we should add an invokable
sourceData(int source_row, int source_column, QModelIndex source_parent) to save a
bit of boilerplate?

broulik added inline comments.Dec 12 2019, 4:51 PM
src/qml/ksortfilterproxymodel.h
3

*Martin

45

Did you try that QRegularExpression vs QRegExp thing?

59

"but you can set it to null" - I think this thing needs a RESET method

81

needs NOTIFY signals, as do the ones below

100

override?

119

Shouldn't this be an int and then you can drop all of that role names stuff?

davidedmundson added inline comments.Dec 12 2019, 5:18 PM
src/qml/ksortfilterproxymodel.h
45

We did some research

  • Old Qt which we support doesn't convert JS to QRegularExpresssion
  • We don't want to write new code with QRegExp
  • I don't want to expose two properties and have QML doing ifdefs

That doesn't help draw a conclusion. Though is there any benefit to creating a reg exp in JS space rather than here?

59

what do we gain doing that?

You would use that with
filterRowCallback = undefined;

which is the same as now.

I think RESET is only usable if you can't represent null in the passed data type.

119

roleNames are always exported. Some people expose the roles as an enum, but not that many

I certainly don't want to encourage magic numbers being used in QML.

davidedmundson edited the summary of this revision. (Show Details)

Huge cleanup

Drop all the filter properties
Implement other changes
Extend unit tests

ahmadsamir added inline comments.
src/qml/ksortfilterproxymodel.cpp
102

In setFilterRegExp(), QSortFilterProxyModel::setFilterRegularExpression(QRegularExpression &) is used, so IIUC here it should be :
QSortFilterProxyModel::filterRegularExpression().pattern()

src/qml/ksortfilterproxymodel.h
104

Hello. Since you're using QRegularExpression, maybe rename to setFilterRegex() or setFitlerRegularExpression() (the latter matches QSortFilterProxyModel naming schema) to avoid QRegExp vs QRegularExpression confusion?

(Another pro for renaming setFilterRegExp() to setFilterRegularExpression()).

davidedmundson added inline comments.Jan 9 2020, 5:07 PM
src/qml/ksortfilterproxymodel.cpp
102

I'm not sure your comments are still valid, we switched to using the superclass QSortFilterProxyModel's methods, and don't shadow that part here.

ahmadsamir added inline comments.Jan 9 2020, 5:33 PM
src/qml/ksortfilterproxymodel.cpp
102

Right, I was looking at an old revision of this diff. Sorry about the noise.

Few comments from someone who was recently using PlasmaCore.SortFilterModel and had trouble understanding sorting :)

autotests/ksortfilterproxymodel_qml.cpp
112

Can you add a test for sortColumn? It might be useful for newcomers (like me) to understand how it works. I had real trouble understanding when it is needed and when not (ConfigEntries.qml)

src/qml/ksortfilterproxymodel.cpp
166

When using PlasmaCore.SortFilterModel sortColumn is sometimes set to -1 and sorting is not working when sortColumn is not set. If I remember correctly, -1 is the default in QSortFilterProxyModel. Is std::max(sortColumn(), 0) added to fix that? What will happen in this situation:

KSortFilterProxyModel {
  sourceModel: testModel
  sortColumn: -1
  sortRole: "user"
}

Maybe is should be documented in sortRole and sortOrder properties that these two set sortColumn to 0 (or -1 in case of empty role)?

176

Which is preferred: emit or Q_EMIT?

src/qml/ksortfilterproxymodel.h
68

can we have something similar for sorting? sortColumnCallback and use it in overridden lessThan?

ahiemstra added inline comments.Jan 29 2020, 11:40 AM
autotests/ksortfilterproxymodel_qml.cpp
112

I don't think a unit test is the right place for example code. It's probably better to either improve the documentation of the property or add an example somewhere where it makes sense.

src/qml/ksortfilterproxymodel.cpp
166

In that example, you'd be sorting on the "user" role of column 0, which seems like a reasonable default to me. I would actually suggest to make sortColumn always at least 0, I don't think there really is much of a use case for -1 as default.

src/qml/ksortfilterproxymodel.h
68

Let's keep this a bit constrained, if we add a stub implementation of lessThan(), we can later on add a property to expose a JS callback for it somehow.

kmaterka added inline comments.Jan 29 2020, 1:19 PM
autotests/ksortfilterproxymodel_qml.cpp
112

Sure, you are right, documentation first. Logic of sortRole uses sortColumn, unit test would be a nice addition :)

src/qml/ksortfilterproxymodel.cpp
166

sortColumn = -1 comes directly from QSortFilterProxyModel and it has use case (disables sorting).
In PlasmaCore.SortFilterModel this worked incorrectly (or confusingly), even if you set sortRole it is not sorting.
This is good now and I like it.

I added separate comment for documenting this.

src/qml/ksortfilterproxymodel.h
68

Agree, you are right.

87

Please add:

The default value is -1. If \a sortRole is set, the default value is 0.

Something I noticed just now while trying to use this: the filterString property has disappeared. PlasmaCore.SortFilterProxy does have this. I think we should restore it, otherwise you need to manually call QSFPM::setFilterFixedString or hope that you can set it as a regular expression.

ahiemstra added inline comments.Jan 29 2020, 3:05 PM
src/qml/ksortfilterproxymodel.cpp
86

This is using the wrong callback, it should be using m_filterColumnCallback

ahiemstra added inline comments.Jan 29 2020, 4:22 PM
src/qml/ksortfilterproxymodel.cpp
75

Suggestion: Instead of directly returning the value, do this: (Also for filterAcceptsColumn below)

auto result = const_cast<KSortFilterProxyModel *>(this)->m_filterCallback.call(args);
if (result.isError()) {
    qCWarning(KITEMMODELS_LOG) << "Row filter callback produced an error:";
    qCWarning(KITEMMODELS_LOG) << result.toString();
} else {
    return result.toBool();
}

That way at least some error will be displayed when the callback fails.

davidedmundson marked 14 inline comments as done.

a load of review comments

ahiemstra updated this revision to Diff 74601.Jan 29 2020, 5:49 PM
  • Use QQmlParserStatus to slightly delay syncing of role names
broulik accepted this revision.Feb 4 2020, 3:22 PM
This revision is now accepted and ready to land.Feb 4 2020, 3:22 PM
This revision was automatically updated to reflect the committed changes.