flypig.co.uk

List items

Items from the current list are shown below.

Blog

27 Dec 2023 : Day 120 #
Over the last few days we've been looking at hiding the print window used to clone the page into when saving a PDF of a page using the print routines. We got to the point where the print succeeds, the page appears for only the briefest of moments, but the tab for the page still appears in the tab model view.

Today we're going to start work on filtering out the page from the tab view.

After some discussion online with thigg, who rightly pointed out some potential dangers with doing this, I've decided to add the functionality but using a config setting that allows it to be enabled and disabled.

Before we implement the config setting I first want to put the filter together. To do this we're going to use a QSortFilterProxyModel. This will look exactly like the original model but with the ability to filter on specific values, in our case the "hidden" flag.

The Qt docs have a nice example of using this filtering approach which we can pretty much copy. All of this is happening in the sailfish-browser code as a wrapper for DeclarativeTabModel. There are also plenty of existing examples in the sailfish-browser code, including BookmarkFilterModel and LoginFilterModel, used to search on bookmarks and logins respectively.

Following these examples we're going to call ours the DeclarativeTabFilterModel.

I've put together the filter. It's generally very simple code, to the extent that I think I can get away with posting the entire header here.
#include <QSortFilterProxyModel>

class DeclarativeTabFilterModel : public QSortFilterProxyModel
{
    Q_OBJECT
    Q_PROPERTY(bool showHidden READ showHidden WRITE setShowHidden NOTIFY
        showHiddenChanged)
public:
    DeclarativeTabFilterModel(QObject *parent = nullptr);

    Q_INVOKABLE int getIndex(int currentIndex);

    bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent)
        const override;
    void setSourceModel(QAbstractItemModel *sourceModel) override;

    bool showHidden() const;
    void setShowHidden(const bool showHidden);

signals:
    void showHiddenChanged(bool showHidden);

private:
    bool m_showHidden;
};
In essence all it's doing is accepting the model, then filtering the rows based on whether they're hidden or not. The key piece of code in the implementation is for the filterAcceptsRow() method which looks like this:
bool DeclarativeTabFilterModel::filterAcceptsRow(int sourceRow,
    const QModelIndex &sourceParent) const
{
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);

    return (m_showHidden || !sourceModel()->data
        (index, DeclarativeTabModel::HiddenRole).toBool());
}
The underlying Qt implementation does all the rest. It's very nice stuff.

I'm hoping I can drop this right in as a replacement for the model in the TabView.qml code. Or, to be more precise, as a drop in for the two models in the TabView.qml code, because there's a separate model for the persistent (standard) and private tab lists.

However, there may be a catch because the DeclarativeTabModel provides a bunch of other properties which potentially might get used for various things. I'll have to be careful not to accidentally switch out a model for the filtered model where any existing code relies on these additional properties. The additional properties won't automatically be provided by the filtered model.

Looking carefully through the code, I think it's safe though. I should be able to replace the model for the filtered proxy model close enough to the view controller that it will only use a minimal set of additional properties.

In order to actually make use of this new declarative component, we must register it with the QML typing system. Along with all of the other components built in this way we do this in the startup code of the application, most easily done in the main() function like this:
    qmlRegisterType<DeclarativeTabFilterModel>(uri, 1, 0, "TabFilterModel");
The only other thing I need to do is add the new file to the Qt project files; in this case the apps/history/history.pri file.

Having done that I'm pleased to see it compiles and builds the packages fine. But it's only a short post today and I'm not planning to test the new model now. Instead I'll pick this up and add it to the QML front-end tomorrow.

If you'd like to read any of my other gecko diary entries, they're all available on my Gecko-dev Diary page.

Comments

Uncover Disqus comments