Popover.new_from_model() only creates insensitive labels, relative position is hardwired

I watch a lot of videos with various refresh rates, so I’m trying to write an extension so I can quickly change the display refresh rate from a panel icon instead of all the extra clicks of going through system settings. I need it to use a persistent popover instead of an ordinary menu because my TV enables overscan in 50Hz modes, making the panel invisible, and the TV doesn’t have an option to disable that AFAICT.

The docs encourage the use of GMenuModel, hence Gtk.Popover.new_from_model(), but it doesn’t seem to actually work. The model should have a Close button in its own unlabeled section and a labeled section for each physical display containing radio buttons for each refresh rate. Instead, it creates all the items as inert labels. As this will be an extension it doesn’t seem appropriate to use the “app” or “win” action groups, so I’ve created an action group with prefix “refreshswitch” and attached it to the parent button that I’m passing as the relative_to parameter of the constructor, using Gtk.Widget.insert_action_group() so it should be able to read the correct button roles from that, right?

At the moment I’m using a test window containing a Gtk.MenuButton to make it easier to test the popover. A second problem is that the position of the popover is always to the left and above the button, even if it has to open almost entirely off the edges of the screen. Gtk.Popover.set_position() and Gtk.MenuButton.set_direction() (the arrow is pointing downwards anyway) have no effect on this.

Here are my functions for creating the action group and menu model:

function buildActionGroup(displays) {
    actionGroup = new Gio.SimpleActionGroup();
    const action = Gio.SimpleAction.new("close", null);
    action.connect("activate", () => {
        // TODO: Close the popover
        log("Close action activated");
    });
    const vt = GLib.VariantType.new('s');
    for (const dn in displays) {
        const disp = displays[dn];
        const states = disp.modes.map((_, i) =>
                GLib.Variant.new_string(`${i}`));
        const action = Gio.SimpleAction.new_stateful(`disp_${dn}`,
                null, states[disp.current_mode]);
        action.set_state_hint(GLib.Variant.new_array(vt, states));
        action.connect("change-state", (_, value) => {
            // TODO: Check if mode has really changed and switch it
            log(`Action disp_${dn} changed state to ${value.get_string()}`);
        });
        actionGroup.add_action(action);
    }
    if (parentWidget) {
        parentWidget.insert_action_group("refreshswitch", actionGroup);
    }
}

function buildMenuModel(displays) {
    menuModel = new Gio.Menu();
    let section = new Gio.Menu();
    section.append_item(Gio.MenuItem.new("Close", "refreshswitch.close"));
    menuModel.append_section(null, section);
    for (const dn in displays) {
        const disp = displays[dn];
        section = new Gio.Menu();
        for (const rn in disp.refresh) {
            section.append_item(Gio.MenuItem.new(`${disp.refresh[rn]}Hz`,
                        `refreshswitch.disp_${dn}::${rn}`));
        }
        menuModel.append_section(disp.name, section);
    }
}

so I’ve created an action group with prefix “refreshswitch” and attached it to the parent button

I think you need to use insert_action_group() on the GtkPopover, not on the button that it’s attached to.

I think you need to use insert_action_group() on the GtkPopover, not on the button that it’s attached to.

Thanks for trying to help, but I had tried that and it didn’t help. The docs for GtkPopover say:

Actions can also be added using gtk_widget_insert_action_group() on the menus attach widget or on any of its parent widgets.

I think “menus (sic) attach widget” means the button the popover is attached to.

I have had a breakthrough though, which I only discovered from error messages after I changed the code to create Gtk.ModelButtons explicitly instead of using a MenuModel (and the fix works when I changed it back to MenuModel). Radio items have to have a parameter type matching their state type, but I had missed that and used null.

There are still problems though:

  1. The close button is still insensitive and does nothing when clicked.
  2. In Wayland the active radio has rendering glitches, becoming transparent and sometimes making other parts of the popover disappear too. In Xorg the popover doesn’t render at all. I’ve noticed similar issues with a popover in Audacity, so I think GTK popovers are simply broken at the moment.
  3. The popover now renders above its parent button instead of to its left, but it’s still hard-wired and defaults to being partly off-screen.

Anyway, I’ve realised a popover is inappropriate for a panel menu. For some reason gnome-shell uses its own toolkit called St, and only supports a PopupMenu, so I should use that. If I can’t make it stay on screen in the background and therefore have too much trouble opening it again during overscan, I’ll have to switch to using a dialog, probably opening it via prefs.js.

The compositor cannot use GTK, as GTK is a client toolkit. For that reason alone, GNOME Shell uses its own small toolkit, called St, based on top of the Clutter API, which is also used for drawing windows.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.