c++windowsgtkmmgtkmm4

GTKmm popover menu items not highlighting when used with treeview


Good afternoon,

I'm trying to integrate a popover context menu to a treeview widget in GTKmm 4.

I've been successful in getting the menu to be displayed and for the respective actions to be called when clicking on the context menu options, however, I'm finding that the menu items are not being highlighted when the mouse hovers over them.

A GIF showing what I'm seeing is here:

Demo

If however, using the same code, I attach the menu and action group to another widget (such as a button or the window itself), all works as expected and the options are highlighted correctly.

Below is code for a minimal reproducible example.

Could someone help as I'm going round in circles with this??

#include <gtkmm.h>

class Window : public Gtk::Window {

public:
    Window() {
        list_store_ = Gtk::ListStore::create(model_);

        auto row = *(list_store_->append());
        row[model_.id] = 1;
        row[model_.name] = "Example 1";

        row = *(list_store_->append());
        row[model_.id] = 2;
        row[model_.name] = "Example 2";

        treeview_.set_hexpand(true);
        treeview_.set_vexpand(true);
        treeview_.set_model(list_store_);
        treeview_.append_column("ID", model_.id);
        treeview_.append_column("Name", model_.name);

        Glib::RefPtr<Gio::Menu> gmenu = Gio::Menu::create();
        gmenu->append("_Edit", "popup.edit");
        gmenu->append("_Remove", "popup.remove");

        menu_.set_parent(treeview_);
        menu_.set_menu_model(gmenu);
        menu_.set_has_arrow(false);

        Glib::RefPtr<Gio::SimpleActionGroup> action_group = Gio::SimpleActionGroup::create();
        action_group->add_action("edit", sigc::mem_fun(*this, &Window::on_popup_edit));
        action_group->add_action("remove", sigc::mem_fun(*this, &Window::on_popup_remove));

        treeview_.insert_action_group("popup", action_group);

        Glib::RefPtr<Gtk::GestureClick> gesture = Gtk::GestureClick::create();
        gesture->set_button(GDK_BUTTON_SECONDARY);
        gesture->signal_pressed().connect(sigc::mem_fun(*this, &Window::on_popup_button_pressed));
        treeview_.add_controller(gesture);

        set_child(treeview_);
    }

    ~Window() override {

    }

private:
    class ExampleModel : public Gtk::TreeModel::ColumnRecord {
    public:

        ExampleModel() {
            add(id);
            add(name);
        }

        Gtk::TreeModelColumn<int> id;
        Gtk::TreeModelColumn<Glib::ustring> name;
    };

    void on_popup_button_pressed(int, double x, double y) {
        int cx, cy;
        treeview_.convert_widget_to_bin_window_coords(x, y, cx, cy);

        Gtk::TreeModel::Path path;
        treeview_.get_path_at_pos(cx, cy, path);
        if (!path) {
            return;
        }

        const Gdk::Rectangle rect(x, y, 1, 1);
        menu_.set_pointing_to(rect);
        menu_.popup();
    }

    void on_popup_edit() { /* Implementation here */ }
    void on_popup_remove() { /* Implementation here */ }

    Gtk::TreeView treeview_;
    ExampleModel model_;
    Glib::RefPtr<Gtk::ListStore> list_store_;
    Gtk::PopoverMenu menu_;
};

int main(int argc, char** argv) {
    auto app = Gtk::Application::create("com.example.treeview");

    return app->make_window_and_run<Window>(argc, argv);
}

Solution

  • Okay, so I recently had time to come back to this project. This time I thought I'd attempt to re-write it in Rust using the gtk4-rs crate and see if the same happens and it does!

    I've managed to work around this issue by wrapping the TreeView inside of a ScrolledWindow and then setting up the menu against the ScrolledView instead of the TreeView. Inside the on_popup_button_pressed function, I'm still able to determine the path/item that was clicked.

    Updated code (untested though - as the project is now written in Rust instead) is below:

    #include <gtkmm.h>
    
    class Window : public Gtk::Window {
    
    public:
        Window() {
            list_store_ = Gtk::ListStore::create(model_);
    
            auto row = *(list_store_->append());
            row[model_.id] = 1;
            row[model_.name] = "Example 1";
    
            row = *(list_store_->append());
            row[model_.id] = 2;
            row[model_.name] = "Example 2";
    
            treeview_.set_hexpand(true);
            treeview_.set_vexpand(true);
            treeview_.set_model(list_store_);
            treeview_.append_column("ID", model_.id);
            treeview_.append_column("Name", model_.name);
    
            scrolled_window_.set_child(treeview_);
    
            Glib::RefPtr<Gio::Menu> gmenu = Gio::Menu::create();
            gmenu->append("_Edit", "popup.edit");
            gmenu->append("_Remove", "popup.remove");
    
            menu_.set_parent(scrolled_window_);
            menu_.set_menu_model(gmenu);
            menu_.set_has_arrow(false);
    
            Glib::RefPtr<Gio::SimpleActionGroup> action_group = Gio::SimpleActionGroup::create();
            action_group->add_action("edit", sigc::mem_fun(*this, &Window::on_popup_edit));
            action_group->add_action("remove", sigc::mem_fun(*this, &Window::on_popup_remove));
    
            scrolled_window.insert_action_group("popup", action_group);
    
            Glib::RefPtr<Gtk::GestureClick> gesture = Gtk::GestureClick::create();
            gesture->set_button(GDK_BUTTON_SECONDARY);
            gesture->signal_pressed().connect(sigc::mem_fun(*this, &Window::on_popup_button_pressed));
            scrolled_window_.add_controller(gesture);
    
            set_child(scrolled_window_);
        }
    
        ~Window() override {
    
        }
    
    private:
        class ExampleModel : public Gtk::TreeModel::ColumnRecord {
        public:
    
            ExampleModel() {
                add(id);
                add(name);
            }
    
            Gtk::TreeModelColumn<int> id;
            Gtk::TreeModelColumn<Glib::ustring> name;
        };
    
        void on_popup_button_pressed(int, double x, double y) {
            int cx, cy;
            treeview_.convert_widget_to_bin_window_coords(x, y, cx, cy);
    
            Gtk::TreeModel::Path path;
            treeview_.get_path_at_pos(cx, cy, path);
            if (!path) {
                return;
            }
    
            const Gdk::Rectangle rect(x, y, 1, 1);
            menu_.set_pointing_to(rect);
            menu_.popup();
        }
    
        void on_popup_edit() { /* Implementation here */ }
        void on_popup_remove() { /* Implementation here */ }
    
        Gtk::TreeView treeview_;
        Gtk::ScrolledWindow scrolled_window_;
        ExampleModel model_;
        Glib::RefPtr<Gtk::ListStore> list_store_;
        Gtk::PopoverMenu menu_;
    };
    
    int main(int argc, char** argv) {
        auto app = Gtk::Application::create("com.example.treeview");
    
        return app->make_window_and_run<Window>(argc, argv);
    }