GTK C++ API - How to redo/update filter in FilterListModel

I’m currently working with the C++ API of GTK, and I’m stuck on how to re-apply (or “redo”, or “update”) a Gtk::Filter in my code.

Basic description of the application

I have a page in my application that displays a “catalog of images”. These images are presented in a “grid of thumbnails”. Each thumbnail in this grid is just a smaller version of image, together with some stars (which is the “rating of that image”). In other words, the user that is using the application might rate a particular image in the catalog with 5 stars (indicating that he/she likes that image very much), or, the user might rate it with just 1 star, or, zero stars, etc.

On the top corner of the window, there is a dropdown in which the user can one or multiple “number of stars” that he would like to use to filter the images in the catalog. So, if the user selects 1 in the dropdown, then, the application should display only the images with rating of “1 star” in the grid. But, if the user selects the options 1, 2 and 4 in the dropdown, then, only images with rating of “1, 2 or 4 stars” should be displayed in the grid. Etc.

Description of the code

This page of the application is represented by the LibraryPage class, which contains a Gio::ListStore of “thumbnail objects” called ThumbnailGridViewItem (which is a class that inherits from Glib::GObject). I use this Gio::ListStore in a Gtk::GridView to present these “thumbnail objects” in a grid format.

This Gio::ListStore is connected to a Gtk::FilterListModel which is used to “filter the thumbnails” that are in this Gio::ListStore using a Gtk::BoolFilter. So, this Gtk::BoolFilter is the “core” of the filter. I know that the Gtk::BoolFilter class have a signal_changed() signal which is normally emitted when the filter changes, but I have no idea on how to emit this signal to force the filter to re-apply it’s match() function over the ThumbnailGridViewItem items of the Giio::ListStore.

The Gtk::BoolFilter is created with a Gtk::ClosureExpression, which calls a method from the LibraryPage named does_thumbnail_star_match_filter().

    auto expression = Gtk::ClosureExpression<bool>::create(
        sigc::mem_fun(*this, &LibraryPage::does_thumbnail_star_match_filter)
    );
    m_stars_filter = Gtk::BoolFilter::create(expression);

In my understanding, this method is the match() function used by the Gtk::BoolFilter to filter the thumbnails. This function is exposed below:

bool LibraryPage::does_thumbnail_star_match_filter(const Glib::RefPtr<Glib::ObjectBase>& item) {
    m_logger.info("Checking if thumbnail star index match");
    Glib::RefPtr<ThumbnailGridViewItem> thumb = std::dynamic_pointer_cast<ThumbnailGridViewItem>(item);
    if (!thumb)
        return false;

    if (m_stars_currently_active.size() == 0) {
        m_logger.info("No star option active, no need to filter.");
        return true;
    }

    for (int index: m_stars_currently_active) {
        if (thumb->m_stars == index) {
            m_logger.info("Thumbnail star index match!");
            return true;
        }
    }

    return false;
}

All that this function does is to check if the “star rating” given to the thumbnail (which is a int value stored in the m_stars variable) matches any of the “star options that are currently active in the dropdown of the page”. In other words, the LibraryPage class keeps track of all “star options” that the user selected in the dropdown through a std::vector<int> object, which is stored in the m_stars_currently_active variable.

So in order to know if a particular thumbnail matches the “star filter” that the user selected in the application, this function needs to iterate through the elements of m_stars_currently_active and check if any of them matches the star rating of the current thumbnail.

My question

How can I force the Gtk::FilterListModel to re-apply the filter over the thumbnails in the Gio::ListStore everytime that the user selects a different option in the dropdown?

Some snippets of the mentioned classes

class LibraryPage: {
public:
    /** Scrolled window where the thumbnails are displayed */
    Gtk::ScrolledWindow m_scroll_window;
    /** Grid view with the thumbnails presented at scroll window */
    Gtk::GridView m_gridview;
    /** Selection model for the view */
    Glib::RefPtr<Gtk::MultiSelection> m_multi_selection_model;
    /** Item factory for the grid view */
    Glib::RefPtr<Gtk::SignalListItemFactory> m_thumbnail_factory;
    /** Thumbnail store for the grid view. */
    Glib::RefPtr<Gio::ListStore<ThumbnailGridViewItem>> m_thumbnail_store;
    /** Expression used by the stars dropdown filter */
    Glib::RefPtr<Gtk::BoolFilter> m_stars_filter;
    /** Filter model for the view */
    Glib::RefPtr<Gtk::FilterListModel> m_filter_model;
    /** List of stars that are currently active */
    std::vector<int> m_stars_currently_active;

    /** The "match" function used by the Gtk::BoolFilter */
    bool does_thumbnail_star_match_filter(const Glib::RefPtr<Glib::ObjectBase>& item);
};


class ThumbnailGridViewItem: public Glib::Object {
    /** The number of stars associated with this thumbnail */
    int m_stars;
};

Thank you for your attention!

For anyone that finds this topic later in the future, here is how I solved this problem.

The hole problem started because my Gtk::BoolFilter object was unable to tell GTK that it needed to redo/re-apply the filter over the Gio::ListStore. Any kind of Gtk::Filter object usually do that by emitting a signal called changed to the GTK process.

Therefore, my application was unable to redo/re-apply the filter because it was not emitting this changed signal to the GTK process. And I solved this problem by creating my own custom Gtk::Filter class, and implementing my own custom “changed logic”.

Now, everytime that the application needs to redo/re-apply the filter, it calls this refilter() method from my custom GtkImageStarsFilter class, which will re-populate the m_active_stars variable with the new/current star numbers, and then, call the changed() method from Gtk::Filter to emmit the changed signal to the GTK process, which will then, re-apply the filter by calling the match_vfunc() for each item in the Gio::ListStore.

class GtkImageStarsFilter: public Gtk::Filter {
public:
    static std::shared_ptr<GtkImageStarsFilter> create(const std::vector<int> active_stars);

    void refilter(const std::vector<int> new_active_stars);
    bool match_vfunc(const Glib::RefPtr<Glib::ObjectBase> &item) override;

private:
    GtkImageStarsFilter(const std::vector<int> active_stars);
    std::vector<int> m_active_stars;
};


GtkImageStarsFilter::GtkImageStarsFilter(const std::vector<int> active_stars)
    : m_active_stars(active_stars) {}


std::shared_ptr<GtkImageStarsFilter>
GtkImageStarsFilter::create(const std::vector<int> active_stars) {
    return std::shared_ptr<GtkImageStarsFilter>(new GtkImageStarsFilter(active_stars));
}


void GtkImageStarsFilter::refilter(const std::vector<int> new_active_stars) {
    m_active_stars = new_active_stars;
    this->changed();
}

bool GtkImageStarsFilter::match_vfunc(const Glib::RefPtr<Glib::ObjectBase> &item) {
    std::cout << "Checking if thumbnail star index match\n";
    Glib::RefPtr<ThumbnailGridViewItem> thumb = std::dynamic_pointer_cast<ThumbnailGridViewItem>(item);
    if (!thumb)
        return false;

    if (m_active_stars.size() == 0) {
        std::cout << "No star option active, no need to filter.\n";
        return true;
    }

    for (int active_star: m_active_stars) {
        if (thumb->m_stars == active_star) {
            std::cout << "Thumbnail star index match!";
            return true;
        }
    }

    return false;
}