Page 1 of 1

Linux: Add AppMenu in GNOME Shell bar

Posted: Sat Nov 30, 2013 3:21 am
by fsw
Since GNOME 3.10 the GNOME Shell finally seems to be usable to the point to be an enjoyable and productive desktop environment.

GNOME has introduced the AppMenu for some of its core applications starting with GNOME 3.4. This means that part of the menu or the whole menu is displayed as a drop-down on the top GNOME Shell bar.

Some applications now have two menus: an AppMenu that's displayed on the top GNOME Shell bar which is used for actions that affect the entire application and another menu that's displayed in the application window, which affects the currently focused window.

Some GNOME applications only use an AppMenu, without a secondary window menu.

Here a simple c program that shows the AppMenu (without menu separators) that shows how it looks like:

Code: Select all

#include <gtk/gtk.h>

static void preferences (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
	g_print ("This does nothing. It is only a demonstration.\n");
}

static void help (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
	g_print ("This does nothing. It is only a demonstration.\n");
}

static void about (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
	g_print ("This does nothing. It is only a demonstration.\n");
}

static void quit (GSimpleAction *action, GVariant *parameter, gpointer user_data)
{
	GApplication *application = user_data;
	g_application_quit (application);
}

static void startup (GtkApplication *application, gpointer user_data)
{
  static const GActionEntry actions[] = {
    { "preferences", preferences },
    { "help", help },
    { "about", about },
    { "quit", quit }
  };

  GMenu *menu;

  g_action_map_add_action_entries (G_ACTION_MAP (application), actions, G_N_ELEMENTS (actions), application);

  menu = g_menu_new ();
  g_menu_append (menu, "Preferences", "app.preferences");
  g_menu_append (menu, "Help",  "app.help");
  g_menu_append (menu, "About", "app.about");
  g_menu_append (menu, "Quit",  "app.quit");
  gtk_application_set_app_menu (application, G_MENU_MODEL (menu));
  g_object_unref (menu);
}

static void activate (GtkApplication *app, gpointer user_data)
{
  GtkWidget *window;

  window = gtk_application_window_new (app);

  gtk_window_set_application (GTK_WINDOW (window), GTK_APPLICATION (app));
  gtk_window_set_title (GTK_WINDOW (window), "Hello GNOME");

  gtk_widget_show_all (GTK_WIDGET (window));
}

int main (int argc, char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example",G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "startup",  
                    G_CALLBACK (startup), NULL);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);
  return status;
}
The window menu is not in a menu bar but is activated with a menu button:

Code: Select all

#include <gtk/gtk.h>

/* Callback function for the undo action */
static void item2_callback (GSimpleAction *simple, GVariant *parameter, gpointer user_data)
{
  g_print ("You clicked \"Item 2\"\n");
}

static void activate (GtkApplication *app, gpointer user_data)
{
  GMenu         *submenu;
  GtkWidget     *grid;
  GMenu         *menumodel;
  GtkWidget     *window;
  GtkWidget     *menubutton;
  GSimpleAction *item2_action;

  window = gtk_application_window_new (app);
  grid = gtk_grid_new ();

  gtk_window_set_title (GTK_WINDOW (window), "MenuButton Example");
  gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);

  menubutton = gtk_menu_button_new ();
  gtk_widget_set_size_request (menubutton, 80, 35);

  gtk_grid_attach (GTK_GRID (grid), menubutton, 0, 0, 1, 1);
  gtk_container_add (GTK_CONTAINER (window), grid);

  menumodel = g_menu_new ();
  g_menu_append (menumodel, "Item 1", "app.item1");
  g_menu_append (menumodel, "Item 2", "win.item2");

  submenu = g_menu_new ();
  g_menu_append_submenu (menumodel, "Other", G_MENU_MODEL (submenu));
  g_menu_append (submenu, "Quit", "app.quit");
  gtk_menu_button_set_menu_model (GTK_MENU_BUTTON (menubutton), G_MENU_MODEL (menumodel));

  item2_action = g_simple_action_new ("item2", NULL);
  g_signal_connect (item2_action, "activate", G_CALLBACK (item2_callback), GTK_WINDOW (window));
  g_action_map_add_action (G_ACTION_MAP (window), G_ACTION (item2_action));

  gtk_widget_show_all (window);
}


static void item1_callback (GSimpleAction *simple, GVariant *parameter, gpointer user_data)
{
  g_print ("You clicked \"Item 1\"\n");
}

static void quit_callback (GSimpleAction *simple, GVariant *parameter, gpointer user_data)
{
  GApplication *application = user_data;
  g_application_quit (application);
}

static void startup (GApplication *app, gpointer user_data)
{
  GSimpleAction *item1_action;
  GSimpleAction *quit_action;

  item1_action = g_simple_action_new ("item1", NULL);
  g_signal_connect (item1_action, "activate", G_CALLBACK (item1_callback), app);
  g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (item1_action));

  quit_action = g_simple_action_new ("quit", NULL);
  g_signal_connect (quit_action, "activate", G_CALLBACK (quit_callback), app);
  g_action_map_add_action (G_ACTION_MAP (app), G_ACTION (quit_action));
}


int main (int argc, char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  g_signal_connect (app, "startup", G_CALLBACK (startup), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);
  return status;
}

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Sat Nov 30, 2013 5:20 pm
by uwekel
This is interresting! Did you already do an adaption into PureBasic?

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Sun Dec 01, 2013 5:39 am
by fsw
I think it's not doable without Fred's help.

This is because:

Code: Select all

gtk_application_set_app_menu(*Application, menu)
needs *Application, which is buried inside Fred's code.

And it's needed in order to attach the menu to the application.
(as it's not attached to the window...)

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Sun Dec 15, 2013 7:09 pm
by uwekel
Why not create a dummy application with

Code: Select all

gtk_application_new
?

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Mon Dec 16, 2013 4:35 pm
by fsw
Don't think it's that simple.
PureBasic has now a gtk3 subsystem but it's still a stepchild.
PureBasic is still using gtk stuff that is deprecated.
Example: still using GtkAction instead of GSimpleAction etc.
Which is fine for gtk2 but in order to use gtk3 to the full more work needs to be done...
...from the inside out. :mrgreen:

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Mon Dec 16, 2013 10:17 pm
by uwekel
Yes, that's a shame. I think the primay focus of PB is Windows, then Mac and Linux at last so a lot of stuff is just pending or not well implemented (e.g. like SpinGadget). More and more i tend to program in Python, which has very good GTK bindings. But i also love PB :?

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Sat Oct 25, 2014 1:07 pm
by uwekel
Ok, it took a while, but now i have found a solution :-)

Code: Select all

ImportC "-lgtk-3"
  gtk_application_new(Id.p-utf8, Flags=0)
  gtk_application_window_new(*application)
  gtk_application_add_window(*application, *window)
  gtk_application_set_app_menu(*application, *app_menu)
  gtk_application_set_menubar(*application, *menubar)
  gtk_builder_new()
  gtk_builder_add_from_string(*builder, buffer.p-utf8, length=-1, *error=0)
  gtk_builder_get_object(*builder, name.p-utf8)
  gtk_widget_show_all(*Wigdet)
  gtk_window_set_title(*Window, Title.p-utf8)
  gtk_window_set_default_size(*Window, Width.l, Height.l)
  gtk_window_set_position(*Window, Position.l)
EndImport
ImportC "-lgobject-2.0"
  g_signal_connect_data(*Instance, Signal.p-utf8, *Handler, *Data=0, Flags.l=0)
EndImport
ImportC "-lgio-2.0"
  g_action_map_add_action(*application, *action)
  g_application_run(*application, argc=0, *argv=0)
  g_application_quit(*application)
  g_simple_action_new(name.p-utf8, parameter_type=0)
EndImport

Global *app, *win

ProcedureC PreferencesActivated()
  Debug "PreferencesActivated"
EndProcedure
ProcedureC QuitActivated()
  Debug "QuitActivated"
  g_application_quit(*app)
EndProcedure
ProcedureC Startup(*app)
  Debug("Startup")
  
  *win = gtk_application_window_new(*app)
  gtk_window_set_default_size(*win, 275, 275)
  gtk_window_set_position(*win, #GTK_WIN_POS_CENTER)
  gtk_window_set_title(*win, "GTK3-Test")
  gtk_application_add_window(*app, *win)
  
  s.s = "<?xml version='1.0'?>" +
  "<interface>" +
  "<menu id='appmenu'>" +
  "  <section>" +
  "  <item>" +
  "    <attribute name='label' translatable='yes'>Einstellungen</attribute>" +
  "    <attribute name='action'>app.preferences</attribute>" +
  "  </item>" +
  "  </section>" +
  "  <section>" +
  "  <item>" +
  "    <attribute name='label' translatable='yes'>Beenden</attribute>" +
  "    <attribute name='action'>app.quit</attribute>" +
  "    <attribute name='accel'><Primary>q</attribute>" +
  "  </item>" +
  "  </section>" +
  "</menu>" +
  "</interface>"
  
  *builder = gtk_builder_new()
  gtk_builder_add_from_string(*builder, s)
  *appmenu = gtk_builder_get_object(*builder, "appmenu")
  gtk_application_set_app_menu(*app, *appmenu)
  
  ;*menubar = gtk_builder_get_object(*builder, "menubar")
  ;gtk_application_set_menubar(*app, *menubar)
  
  *actpref = g_simple_action_new("preferences")
  g_signal_connect_data(*actpref, "activate", @PreferencesActivated())
  g_action_map_add_action(*app, *actpref)

  *actquit = g_simple_action_new("quit")
  g_signal_connect_data(*actquit, "activate", @QuitActivated())
  g_action_map_add_action(*app, *actquit)
  
EndProcedure
ProcedureC Activate(*app)
  Debug("Activate")
  gtk_widget_show_all(*win)
EndProcedure
ProcedureC Shutdown(*app)
  Debug("Shutdown")
EndProcedure
Procedure Main()
  *app = gtk_application_new("purebasic.gnome")
  g_signal_connect_data(*app, "startup", @Startup())
  g_signal_connect_data(*app, "activate", @Activate())
  g_signal_connect_data(*app, "shutdown", @Shutdown()) 
  g_application_run(*app)
EndProcedure

Main()

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Sat Oct 25, 2014 1:22 pm
by bembulak
Wow! That's interesting. Thank you for sharing. However I must say, that looks quite complicated to me.

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Thu Oct 30, 2014 9:44 pm
by fsw
Just saw this. 8)
Will test it out as soon as I can.
(although I'm little upset with Gnome 3 as it always makes me install a full sh*t load of Gnome apps I don't want.)

On the other hand (AFAIK) Gnome 3 is the only GTK3 based Desktop that will play nice with Wayland in the future...
(or I should take another look at the Budgie-Desktop...)

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Fri Oct 31, 2014 6:25 pm
by bembulak
Budgie looks interesting, yes!

Re: Linux: Add AppMenu in GNOME Shell bar

Posted: Fri Oct 31, 2014 8:53 pm
by uwekel
fsw wrote:...although I'm little upset with Gnome 3 as it always makes me install a full sh*t load of Gnome apps I don't want...
I did a script to get rid of some pakets:

Code: Select all

yum -y erase orca libreoffice*
And to install Purebasic dependancies as well:

Code: Select all

yum -y install gcc libstdc++-devel libstdc++-static gtk2-devel libgnome-devel libgnomeprintui22 libgnomeprintui22-devel SDL-devel xine-lib-devel unixODBC unixODBC-devel
So the system is setup in a couple of seconds :)