More featured menu

The menu (used to launch applications) really needs some more features. The main feature would probably be a power strip; maybe integration of the places applet.
See this:

Would anybody be interested in helping develop such an applet based on the Budgie Menu code? And possibly make it the default in the future of ubuntu budgie? Thx
I say so because the current menu with only apps is minimal, but people used to Windows might not be able to find the power buttons and places applet; plus everything in one place is nice.
I think this would require nothing but smashing the code of a couple applets together (but ofc they’re written in different languages)

Think you are referring to the budge menu code, places code and the raven power strip. All are available in the upstream codebase and all are written in vala. So yes the code be ‘smashed’ together. Interesting project if someone wants a copy-paste and glue together type project.

1 Like

I don’t know vala so unfortunately I can’t contribute, I can try some attempts to ‘smash’ them together if you gimme appropriate links to each project.

Help is surely welcome by anyone!!!

I can’t find the power strip. Everything else, I found; budgie-desktop/src/applets (upstream).

1 Like

How do I create an applet? Like where do i put the files to install it

Alright here’s my code:

/*
 * This file is part of budgie-desktop
 * 
 * Copyright © 2015-2019 Budgie Desktop Developers
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

public class BudgieMenu : Budgie.Plugin, Peas.ExtensionBase
{
    public Budgie.Applet get_panel_widget(string uuid)
    {
        return new BudgieMenuApplet(uuid);
    }
}

[GtkTemplate (ui = "/com/solus-project/budgie-menu/settings.ui")]
public class BudgieMenuSettings : Gtk.Grid
{

    [GtkChild]
    private Gtk.Switch? switch_menu_label;

    [GtkChild]
    private Gtk.Switch? switch_menu_compact;

    [GtkChild]
    private Gtk.Switch? switch_menu_headers;

    [GtkChild]
    private Gtk.Switch? switch_menu_categories_hover;

    [GtkChild]
    private Gtk.Entry? entry_label;

    [GtkChild]
    private Gtk.Entry? entry_icon_pick;

    [GtkChild]
    private Gtk.Button? button_icon_pick;

    private Settings? settings;

    public BudgieMenuSettings(Settings? settings)
    {
        this.settings = settings;
        settings.bind("enable-menu-label", switch_menu_label, "active", SettingsBindFlags.DEFAULT);
        settings.bind("menu-compact", switch_menu_compact, "active", SettingsBindFlags.DEFAULT);
        settings.bind("menu-headers", switch_menu_headers, "active", SettingsBindFlags.DEFAULT);
        settings.bind("menu-categories-hover", switch_menu_categories_hover, "active", SettingsBindFlags.DEFAULT);
        settings.bind("menu-label", entry_label, "text", SettingsBindFlags.DEFAULT);
        settings.bind("menu-icon", entry_icon_pick, "text", SettingsBindFlags.DEFAULT);

        this.button_icon_pick.clicked.connect(on_pick_click);
    }

    /**
     * Handle the icon picker
     */
    void on_pick_click()
    {
        IconChooser chooser = new IconChooser(this.get_toplevel() as Gtk.Window);
        string? response = chooser.run();
        chooser.destroy();
        if (response != null) {
            this.entry_icon_pick.set_text(response);
        }
    }
}

public class BudgieMenuApplet : Budgie.Applet
{

    protected Gtk.ToggleButton widget;
    protected BudgieMenuWindow? popover;
    protected Settings settings;
    Gtk.Image img;
    Gtk.Label label;
    Budgie.PanelPosition panel_position = Budgie.PanelPosition.BOTTOM;
    int pixel_size = 32;

    private unowned Budgie.PopoverManager? manager = null;

    public string uuid { public set ; public get; }

    public override Gtk.Widget? get_settings_ui()
    {
        return new BudgieMenuSettings(this.get_applet_settings(uuid));
    }

    public override bool supports_settings()
    {
        return true;
    }

    public BudgieMenuApplet(string uuid)
    {
        Object(uuid: uuid);

        settings_schema = "com.solus-project.budgie-menu";
        settings_prefix = "/com/solus-project/budgie-panel/instance/budgie-menu";

        settings = this.get_applet_settings(uuid);

        settings.changed.connect(on_settings_changed);

        widget = new Gtk.ToggleButton();
        widget.relief = Gtk.ReliefStyle.NONE;
        img = new Gtk.Image.from_icon_name("view-grid-symbolic", Gtk.IconSize.INVALID);
        img.pixel_size = pixel_size;
        img.no_show_all = true;

        var layout = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
        layout.pack_start(img, true, true, 0);
        label = new Gtk.Label("");
        label.halign = Gtk.Align.START;
        layout.pack_start(label, true, true, 3);

        widget.add(layout);

        // Better styling to fit in with the budgie-panel
        var st = widget.get_style_context();
        st.add_class("budgie-menu-launcher");
        st.add_class("panel-button");
        popover = new BudgieMenuWindow(settings, widget);
        popover.bind_property("visible", widget, "active");

        widget.button_press_event.connect((e)=> {
            if (e.button != 1) {
                return Gdk.EVENT_PROPAGATE;
            }
            if (popover.get_visible()) {
                popover.hide();
            } else {
                popover.get_child().show_all();
                this.manager.show_popover(widget);
            }
            return Gdk.EVENT_STOP;
        });

        popover.get_child().show_all();

        supported_actions = Budgie.PanelAction.MENU;

        add(widget);
        show_all();
        layout.valign = Gtk.Align.CENTER;
        valign = Gtk.Align.FILL;
        halign = Gtk.Align.FILL;
        on_settings_changed("enable-menu-label");
        on_settings_changed("menu-icon");
        on_settings_changed("menu-label");

        /* Potentially reload icon on pixel size jumps */
        panel_size_changed.connect((p,i,s)=> {
            if (this.pixel_size != i) {
                this.pixel_size = (int)i;
                this.on_settings_changed("menu-icon");
            }
        });

        popover.key_release_event.connect((e)=> {
            if (e.keyval == Gdk.Key.Escape) {
                popover.hide();
            }
            return Gdk.EVENT_PROPAGATE;
        });
    }

    public override void panel_position_changed(Budgie.PanelPosition position)
    {
        this.panel_position = position;
        bool vertical = (position == Budgie.PanelPosition.LEFT || position == Budgie.PanelPosition.RIGHT);
        int margin = vertical ? 0 : 3;
        img.set_margin_end(margin);
        on_settings_changed("enable-menu-label");
    }

    public override void invoke_action(Budgie.PanelAction action)
    {
        if ((action & Budgie.PanelAction.MENU) != 0) {
            if (popover.get_visible()) {
                popover.hide();
            } else {
                popover.get_child().show_all();
                this.manager.show_popover(widget);
            }
        }
    }

    protected void on_settings_changed(string key)
    {
        bool should_show = true;

        switch (key)
        {
            case "menu-icon":
                string? icon = settings.get_string(key);
                if ("/" in icon) {
                    try {
                        Gdk.Pixbuf pixbuf = new Gdk.Pixbuf.from_file(icon);
                        img.set_from_pixbuf(pixbuf.scale_simple(this.pixel_size, this.pixel_size, Gdk.InterpType.BILINEAR));
                    } catch (Error e) {
                        warning("Failed to update Budgie Menu applet icon: %s", e.message);
                        img.set_from_icon_name("view-grid-symbolic", Gtk.IconSize.INVALID); // Revert to view-grid-symbolic
                    }
                } else if (icon == "") {
                    should_show = false;
                } else {
                    img.set_from_icon_name(icon, Gtk.IconSize.INVALID);
                }
                img.set_pixel_size(this.pixel_size);
                img.set_visible(should_show);
                break;
            case "menu-label":
                label.set_label(settings.get_string(key));
                break;
            case "enable-menu-label":
                bool visible = (panel_position == Budgie.PanelPosition.TOP ||
                                panel_position == Budgie.PanelPosition.BOTTOM) &&
                                settings.get_boolean(key);
                label.set_visible(visible);
                break;
            default:
                break;
        }
    }

    public override void update_popovers(Budgie.PopoverManager? manager)
    {
        this.manager = manager;
        manager.register_popover(widget, popover);
    }

} // End class

[ModuleInit]
public void peas_register_types(TypeModule module)
{
    // boilerplate - all modules need this
    var objmodule = module as Peas.ObjectModule;
    objmodule.register_extension_type(typeof(Budgie.Plugin), typeof(BudgieMenu));
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=4 tabstop=4 expandtab:
 * :indentSize=4:tabSize=4:noTabs=true:
 */

 /*
 * This file is part of budgie-desktop
 *
 * Copyright © 2015-2019 Budgie Desktop Developers
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

public class PlacesIndicator : Budgie.Plugin, Peas.ExtensionBase
{
    public Budgie.Applet get_panel_widget(string uuid) {
        return new PlacesIndicatorApplet(uuid);
    }
}

[GtkTemplate (ui = "/com/solus-project/places-indicator/settings.ui")]
public class PlacesIndicatorSettings : Gtk.Grid
{
    [GtkChild]
    private Gtk.Switch? switch_label;

    [GtkChild]
    private Gtk.Switch? switch_places;

    [GtkChild]
    private Gtk.Switch? switch_expand_places;

    [GtkChild]
    private Gtk.Switch? switch_drives;

    [GtkChild]
    private Gtk.Switch? switch_networks;

    private GLib.Settings? settings;

    public PlacesIndicatorSettings(GLib.Settings? settings)
    {
        this.settings = settings;
        settings.bind("show-label", switch_label, "active", GLib.SettingsBindFlags.DEFAULT);
        settings.bind("expand-places", switch_expand_places, "active", GLib.SettingsBindFlags.DEFAULT);
        settings.bind("show-places", switch_places, "active", GLib.SettingsBindFlags.DEFAULT);
        settings.bind("show-drives", switch_drives, "active", GLib.SettingsBindFlags.DEFAULT);
        settings.bind("show-networks", switch_networks, "active", GLib.SettingsBindFlags.DEFAULT);
    }
}

public class PlacesIndicatorApplet : Budgie.Applet
{
    private Gtk.EventBox? ebox;
    private PlacesIndicatorWindow? popover;
    private Gtk.Label label;
    private Gtk.Image image;
    private Budgie.PanelPosition panel_position = Budgie.PanelPosition.BOTTOM;

    private unowned Budgie.PopoverManager? manager = null;
    private GLib.Settings settings;
    public string uuid { public set ; public get; }

    public override Gtk.Widget? get_settings_ui() {
        return new PlacesIndicatorSettings(get_applet_settings(uuid));
    }

    public override bool supports_settings() {
        return true;
    }

    public PlacesIndicatorApplet(string uuid)
    {
        Object(uuid: uuid);

        settings_schema = "com.solus-project.places-indicator";
        settings_prefix = "/com/solus-project/budgie-panel/instance/places-indicator";

        settings = get_applet_settings(uuid);
        settings.changed.connect(on_settings_changed);

        ebox = new Gtk.EventBox();
        Gtk.Box layout = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
        ebox.add(layout);
        image = new Gtk.Image.from_icon_name("folder-symbolic", Gtk.IconSize.MENU);
        layout.pack_start(image, true, true, 3);
        label = new Gtk.Label(_("Places"));
        label.halign = Gtk.Align.START;
        layout.pack_start(label, true, true, 3);

        popover = new PlacesIndicatorWindow(image);

        ebox.button_press_event.connect((e)=> {
            if (e.button != 1) {
                return Gdk.EVENT_PROPAGATE;
            }
            toggle_popover();
            return Gdk.EVENT_STOP;
        });

        popover.get_child().show_all();

        add(ebox);
        show_all();

        on_settings_changed("show-label");
        on_settings_changed("expand-places");
        on_settings_changed("show-places");
        on_settings_changed("show-drives");
        on_settings_changed("show-networks");
    }

    public override void panel_position_changed(Budgie.PanelPosition position)
    {
        this.panel_position = position;
        on_settings_changed("show-label");
    }

    public void toggle_popover()
    {
        if (popover.get_visible()) {
            popover.hide();
        } else {
            popover.get_child().show_all();
            this.manager.show_popover(ebox);
        }
    }

    public override void invoke_action(Budgie.PanelAction action) {
        toggle_popover();
    }

    public override void update_popovers(Budgie.PopoverManager? manager)
    {
        this.manager = manager;
        manager.register_popover(ebox, popover);
    }

    protected void on_settings_changed(string key)
    {
        switch (key)
        {
            case "show-label":
                bool visible = (panel_position == Budgie.PanelPosition.TOP ||
                                panel_position == Budgie.PanelPosition.BOTTOM) &&
                                settings.get_boolean(key);
                label.set_visible(visible);
                break;
            case "expand-places":
                popover.expand_places = settings.get_boolean(key);
                break;
            case "show-places":
                popover.show_places = settings.get_boolean(key);
                break;
            case "show-drives":
                popover.show_drives = settings.get_boolean(key);
                break;
            case "show-networks":
                popover.show_networks = settings.get_boolean(key);
                break;
            default:
                break;
        }
    }
}

[ModuleInit]
public void peas_register_types(TypeModule module)
{
    // boilerplate - all modules need this
    var objmodule = module as Peas.ObjectModule;
    objmodule.register_extension_type(typeof(Budgie.Plugin), typeof(PlacesIndicator));
}

Doesn’t show up in Budgie Desktop Settings. budgie-panel --replace throws:

** (budgie-panel:3392): WARNING **: 13:31:15.501: BudgieMenuWindow.vala:190: Software has no parent directory, not adding to menu

I have it in the /usr/lib… dir (along with all other necessary files, ofc). What’s wrong with this @fossfreedom ?
Line 190: } (with a ton of leading spaces that markdown doesn’t wanna show)

( kind of related Proposal for a new menu applet for the desktop ? )

1 Like

That is the applet fossfreedom is working on!
And yes it does work great!
Did not know he published it, thx

Yeah… but the under-text is : « why working on another app-menu » ?

Why not make actual Budgie-AppMenu-Applet better by fixing it and improving it with new features ?

Each DE out there has its own app-menu but in the end they all do the same things, none is really better than another, and most if not all lack the idea of « recent activities ». Those recent activities are so convenient to have all in one place at one click reach… much more useful than « just » your installed app’s.

Yes true maybe comment on that thread;
Well I dont think Solus upstream is gonna use this

reading various topics in and around solus it appears there is little appetite for wholescale changes - everything seems to be “budgie 11” at the moment.

So probably tinkering around the edges may be acceptable but adding “recent activities” and other stuff mentioned elsewhere may be considered nugatory effort.