diff options
author | P. F. Chimento <philip.chimento@gmail.com> | 2013-06-25 14:07:04 -0700 |
---|---|---|
committer | P. F. Chimento <philip.chimento@gmail.com> | 2013-06-25 14:07:04 -0700 |
commit | db4c92e4b25380451114a7cb3c6d5838d561d52a (patch) | |
tree | 2789d93edacc3b3a3ef67284ddc789d61bc61775 | |
parent | 83476573ea208d34abc5bd9e3b431a1b903eeb64 (diff) | |
parent | faa71edb558cc8f81bc1825b1a0af06760cd49ee (diff) |
Merge pull request #115 from endlessm/issues/79
Issues/79
-rw-r--r-- | docs/reference/endless/Makefile.am | 1 | ||||
-rw-r--r-- | endless/Makefile.am | 4 | ||||
-rw-r--r-- | endless/eosactionmenu-private.h | 71 | ||||
-rw-r--r-- | endless/eosactionmenu.c | 213 | ||||
-rw-r--r-- | overrides/Endless.js | 12 | ||||
-rw-r--r-- | test/Makefile.am | 5 | ||||
-rw-r--r-- | test/run-tests.c | 1 | ||||
-rw-r--r-- | test/run-tests.h | 1 | ||||
-rw-r--r-- | test/smoke-tests/action-buttons.js | 58 | ||||
-rw-r--r-- | test/smoke-tests/eosactionbutton.css | 16 | ||||
-rw-r--r-- | test/test-action-menu.c | 165 |
11 files changed, 531 insertions, 16 deletions
diff --git a/docs/reference/endless/Makefile.am b/docs/reference/endless/Makefile.am index b1dc6c6..00a813d 100644 --- a/docs/reference/endless/Makefile.am +++ b/docs/reference/endless/Makefile.am @@ -53,6 +53,7 @@ IGNORE_HFILES= eosinit-private.h \ eostopbar-private.h \ eosmainarea-private.h \ eosactionbutton-private.h \ + eosactionmenu-private.h \ eospagemanager-private.h # Images to copy into HTML directory. diff --git a/endless/Makefile.am b/endless/Makefile.am index 3619a97..77f8c95 100644 --- a/endless/Makefile.am +++ b/endless/Makefile.am @@ -11,7 +11,8 @@ endless_private_installed_headers = \ endless/eossplashpagemanager.h \ endless/eostypes.h \ endless/eoswindow.h \ - endless/eosactionbutton-private.h + endless/eosactionbutton-private.h \ + endless/eosactionmenu-private.h endless_library_sources = \ endless/eosapplication.c \ @@ -22,6 +23,7 @@ endless_library_sources = \ endless/eossplashpagemanager.c \ endless/eostopbar.c endless/eostopbar-private.h \ endless/eosactionbutton.c \ + endless/eosactionmenu.c \ endless/eoswindow.c # Endless GUI library diff --git a/endless/eosactionmenu-private.h b/endless/eosactionmenu-private.h new file mode 100644 index 0000000..8b3755e --- /dev/null +++ b/endless/eosactionmenu-private.h @@ -0,0 +1,71 @@ +/* Copyright 2013 Endless Mobile, Inc. */ + +#ifndef EOS_ACTION_MENU_H +#define EOS_ACTION_MENU_H + +#include "eostypes.h" + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +#define EOS_TYPE_ACTION_MENU eos_action_menu_get_type() + +#define EOS_ACTION_MENU(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + EOS_TYPE_ACTION_MENU, EosActionMenu)) + +#define EOS_ACTION_MENU_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + EOS_TYPE_ACTION_MENU, EosActionMenuClass)) + +#define EOS_IS_ACTION_MENU(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + EOS_TYPE_ACTION_MENU)) + +#define EOS_IS_ACTION_MENU_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + EOS_TYPE_ACTION_MENU)) + +#define EOS_ACTION_MENU_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + EOS_TYPE_ACTION_MENU, EosActionMenuClass)) + +typedef struct _EosActionMenu EosActionMenu; +typedef struct _EosActionMenuClass EosActionMenuClass; +typedef struct _EosActionMenuPrivate EosActionMenuPrivate; + +struct _EosActionMenu +{ + GtkGrid parent; + + EosActionMenuPrivate *priv; +}; + +struct _EosActionMenuClass +{ + GtkGridClass parent_class; +}; + +GType eos_action_menu_get_type (void) G_GNUC_CONST; + +GtkWidget *eos_action_menu_new (); + +void eos_action_menu_add_action (EosActionMenu *menu, + GtkAction *action); + +GtkAction *eos_action_menu_get_action (EosActionMenu *menu, + const gchar *name); + +GList *eos_action_menu_list_actions (EosActionMenu *menu); + + +void eos_action_menu_remove_action (EosActionMenu *menu, + GtkAction *action); + +void eos_action_menu_remove_action_by_name (EosActionMenu *menu, + const gchar *name); + +G_END_DECLS + +#endif /* EOS_ACTION_MENU_H */ diff --git a/endless/eosactionmenu.c b/endless/eosactionmenu.c new file mode 100644 index 0000000..98ae77d --- /dev/null +++ b/endless/eosactionmenu.c @@ -0,0 +1,213 @@ +/* Copyright 2013 Endless Mobile, Inc. */ + +#include "config.h" +#include "eosactionmenu-private.h" + +#include "eosactionbutton-private.h" +#include <glib-object.h> +#include <gtk/gtk.h> +#include <math.h> + +#define _EOS_STYLE_CLASS_ACTION_MENU "action-menu" + +/* + * SECTION:action-menu + * @short_description: Adding actions to the page + * @title: Action Menu + */ + + +G_DEFINE_TYPE (EosActionMenu, eos_action_menu, GTK_TYPE_GRID) + +#define EOS_ACTION_MENU_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), EOS_TYPE_ACTION_MENU, EosActionMenuPrivate)) + +struct _EosActionMenuPrivate +{ + GtkActionGroup *action_group; +}; + +static void +eos_action_menu_dispose (GObject *object); + +static void +eos_action_menu_finalize (GObject *object); + +/* ******* INIT ******* */ + +static void +eos_action_menu_class_init (EosActionMenuClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (EosActionMenuPrivate)); + + object_class->dispose = eos_action_menu_dispose; + object_class->finalize = eos_action_menu_finalize; +} + +static void +eos_action_menu_init (EosActionMenu *self) +{ + EosActionMenuPrivate *priv; + GtkStyleContext *context; + + self->priv = EOS_ACTION_MENU_PRIVATE (self); + priv = self->priv; + + context = gtk_widget_get_style_context (GTK_WIDGET (self)); + gtk_style_context_add_class (context, _EOS_STYLE_CLASS_ACTION_MENU); + + // TODO : name? + priv->action_group = gtk_action_group_new ("EosActionMenu"); + + gtk_widget_set_hexpand (GTK_WIDGET (self), TRUE); + gtk_widget_set_vexpand (GTK_WIDGET (self), TRUE); + gtk_widget_set_halign (GTK_WIDGET (self), GTK_ALIGN_CENTER); + gtk_widget_set_valign (GTK_WIDGET (self), GTK_ALIGN_CENTER); +} + +/* ******* LIFECYCLE ******* */ + +/* + * eos_action_menu_new: + * + * Returns: a new instance + */ +GtkWidget * +eos_action_menu_new () +{ + return g_object_new (EOS_TYPE_ACTION_MENU, NULL); +} + +static void +eos_action_menu_dispose (GObject *object) +{ + G_OBJECT_CLASS (eos_action_menu_parent_class)->dispose (object); +} + +static void +eos_action_menu_finalize (GObject *object) +{ + G_OBJECT_CLASS (eos_action_menu_parent_class)->finalize (object); +} + +/* ******* ACTION GROUP MGMT ******* */ + +/* + * eos_action_menu_add_action: + * @menu: a #EosActionMenu + * @action: a #GtkAction: name, label, icon-name, is-important. + * + * Adds an action to the #EosActionMenu, using its name, label, icon-name and + * is-important properties. + */ +void +eos_action_menu_add_action (EosActionMenu *menu, + GtkAction *action) +{ + EosActionMenuPrivate *priv; + + g_return_if_fail (EOS_IS_ACTION_MENU (menu)); + priv = menu->priv; + + if (action) + { + gtk_action_group_add_action (priv->action_group, action); + + EosActionButtonSize size = gtk_action_get_is_important (action) ? + EOS_ACTION_BUTTON_SIZE_PRIMARY : + EOS_ACTION_BUTTON_SIZE_SECONDARY; + + GtkWidget *action_button = eos_action_button_new (size, + gtk_action_get_label (action), + gtk_action_get_icon_name (action)); + + gtk_activatable_set_related_action (GTK_ACTIVATABLE (action_button), action); + + // TODO : maybe we need a finer control, taking is-important into account? + gtk_grid_attach_next_to (GTK_GRID (menu), action_button, NULL, + GTK_POS_BOTTOM, 1, 1); + } +} + +/* + * eos_action_menu_get_action: + * @menu: an #EosActionMenu + * @name: the name of the action to retrieve + * + * Retrieves an action. + * + * Returns: (transfer none): the #GtkAction + */ +GtkAction * +eos_action_menu_get_action (EosActionMenu *menu, + const gchar *name) +{ + EosActionMenuPrivate *priv; + g_return_val_if_fail (EOS_IS_ACTION_MENU (menu), NULL); + priv = menu->priv; + + return gtk_action_group_get_action (priv->action_group, name); +} + +/* + * eos_action_menu_list_actions: + * @menu: an #EosActionMenu + * + * Returns: (element-type GList) (transfer container): an allocated list of the action objects in the action group + */ +GList * +eos_action_menu_list_actions (EosActionMenu *menu) +{ + EosActionMenuPrivate *priv; + g_return_val_if_fail (EOS_IS_ACTION_MENU (menu), NULL); + priv = menu->priv; + + return gtk_action_group_list_actions (priv->action_group); +} + +/* + * eos_action_menu_remove_action: + * @menu: an #EosActionMenu + * @action: the action to remove + * + * Removes an action + */ +void +eos_action_menu_remove_action (EosActionMenu *menu, + GtkAction *action) +{ + EosActionMenuPrivate *priv; + g_return_if_fail (EOS_IS_ACTION_MENU (menu)); + priv = menu->priv; + + gtk_action_group_remove_action(priv->action_group, action); +} + +/* + * eos_action_menu_remove_action_by_name: + * @menu: an #EosActionMenu + * @name: the name of the action to remove + * + * Removes the action with the given name + */ +void +eos_action_menu_remove_action_by_name (EosActionMenu *menu, + const gchar *name) +{ + GtkAction *action; + EosActionMenuPrivate *priv; + + g_return_if_fail (EOS_IS_ACTION_MENU (menu)); + priv = menu->priv; + + action = gtk_action_group_get_action (priv->action_group, name); + if (action) + { + gtk_action_group_remove_action (priv->action_group, action); + } +} + +/* ******* LAYOUT AND VISUALS ******* */ + diff --git a/overrides/Endless.js b/overrides/Endless.js index 00315a3..59e55ac 100644 --- a/overrides/Endless.js +++ b/overrides/Endless.js @@ -15,4 +15,16 @@ function _init() { } } } + + // Override Endless.ActionMenu.add_action() so that we hide the use of + // GtkAction from the developer, as that will be deprecated in the future. + Endless.ActionMenu.prototype._add_action_real = Endless.ActionMenu.prototype.add_action; + Endless.ActionMenu.prototype.add_action = function(dict, callback) { + let action = new Gtk.Action(dict); + this._add_action_real(action); + + if (typeof callback === "function") { + action.connect('activate', callback); + } + } } diff --git a/test/Makefile.am b/test/Makefile.am index e084fc5..ee224e0 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -12,9 +12,10 @@ test_run_tests_SOURCES = \ test/test-init.c \ test/test-hello.c \ test/test-application.c \ - test/test-page-manager.c \ + test/test-page-manager.c \ test/test-splash-page-manager.c \ - test/test-window.c + test/test-window.c \ + test/test-action-menu.c test_run_tests_CPPFLAGS = $(TEST_FLAGS) test_run_tests_LDADD = $(TEST_LIBS) diff --git a/test/run-tests.c b/test/run-tests.c index dec135a..b9ce062 100644 --- a/test/run-tests.c +++ b/test/run-tests.c @@ -92,6 +92,7 @@ main (int argc, add_window_tests (); add_page_manager_tests (); add_splash_page_manager_tests (); + add_action_menu_tests (); return g_test_run (); } diff --git a/test/run-tests.h b/test/run-tests.h index 32caa7e..39ee9b9 100644 --- a/test/run-tests.h +++ b/test/run-tests.h @@ -37,5 +37,6 @@ void add_application_tests (void); void add_window_tests (void); void add_page_manager_tests (void); void add_splash_page_manager_tests (void); +void add_action_menu_tests (void); #endif /* RUN_TESTS_H */ diff --git a/test/smoke-tests/action-buttons.js b/test/smoke-tests/action-buttons.js index 04e3ae9..7fe046b 100644 --- a/test/smoke-tests/action-buttons.js +++ b/test/smoke-tests/action-buttons.js @@ -13,22 +13,54 @@ const TestApplication = new Lang.Class ({ vfunc_startup: function() { this.parent(); - this._page = new Gtk.Grid(); + this._page = new Gtk.Grid (); - /* should be using Endless.EOS_ACTION_BUTTON_SIZE_PRIMARY */ - - this._eosButton0 = new Endless.ActionButton({size: 0, label: 'SMILE', 'icon-id': 'face-smile-symbolic' }); - this._page.attach(this._eosButton0, 0, 0, 1, 1); - - this._eosButton1 = new Endless.ActionButton({size: 1, label: 'POUT', 'icon-id': 'face-sad-symbolic' }); - this._page.attach(this._eosButton1, 0, 1, 1, 1); + this._content = new Gtk.Label ({name: 'content', label: 'Content'}); - this._eosButton2 = new Endless.ActionButton({size: 2, label: '', 'icon-id': 'edit-delete-symbolic' }); - this._page.attach(this._eosButton2, 0, 2, 1, 1); + this._menu = new Endless.ActionMenu (); - this._eosButton3 = new Endless.ActionButton({size: 3, label: '', 'icon-id': 'object-select-symbolic' }); - this._page.attach(this._eosButton3, 0, 3, 1, 1); + // put the ActionMenu in a panel, as GtkGrid doesn't expand if none of its children want to + this._menu_panel = new Gtk.Frame ({name: 'menu'}); + this._menu_panel.add (this._menu); + this._menu_panel.set_hexpand (true); + this._menu_panel.set_vexpand (true); + + // the ActionMenu takes 1/6 of the width + this._page.set_column_homogeneous (true); + this._page.attach (this._content, 0, 0, 5, 1); + this._page.attach (this._menu_panel, 5, 0, 1, 1); + this._menu.add_action ({ + name: 'select', + 'icon-name': 'object-select-symbolic', + label: 'select stuff', + 'is-important': true }, + Lang.bind(this, function () { + var md = new Gtk.MessageDialog({modal:true, title:"Information", + message_type:Gtk.MessageType.INFO, + buttons:Gtk.ButtonsType.OK, text:"Select button pressed!"}); + md.run(); + md.destroy(); + })); + + this._menu.add_action ({ + name: 'delete', + 'icon-name': 'edit-delete-symbolic', + label: 'delete stuff', + 'is-important': false }); + + this._menu.add_action ({ + name: 'smile', + 'icon-name': 'face-smile-symbolic', + label: 'smile', + 'is-important': false }); + + this._menu.add_action ({ + name: 'sadface', + 'icon-name': 'face-sad-symbolic', + label: 'sadface', + 'is-important': false }); + this._pm = new Endless.PageManager(); this._pm.add(this._page, { name: "page" }); @@ -37,7 +69,7 @@ const TestApplication = new Lang.Class ({ this._window = new Endless.Window({ application: this, - border_width: 16, + border_width: 1, page_manager: this._pm }); diff --git a/test/smoke-tests/eosactionbutton.css b/test/smoke-tests/eosactionbutton.css index 6fd657b..e4a8267 100644 --- a/test/smoke-tests/eosactionbutton.css +++ b/test/smoke-tests/eosactionbutton.css @@ -1,3 +1,5 @@ +/* ****** ACTION BUTTONS ****** */ + EosActionButton { background-color: transparent; border-color: #012345; @@ -54,3 +56,17 @@ EosActionButton:focused { EosActionButton:inconsistent { border-color: #6789AB; } + +/* ****** ACTION MENU ****** */ + +GtkFrame#menu { + background-color: #D3D7CF; + border-color: #E1E2DE; + border-width: 0 0 0 6px; +} + +GtkLabel#content { + color: #BABDB6; + font-size: 48pt; + background: #EEEEEC; +} diff --git a/test/test-action-menu.c b/test/test-action-menu.c new file mode 100644 index 0000000..afd2397 --- /dev/null +++ b/test/test-action-menu.c @@ -0,0 +1,165 @@ +#include <gtk/gtk.h> +#include <endless/endless.h> + +#include <endless/eosactionbutton-private.h> +#include <endless/eosactionmenu-private.h> + +#include "run-tests.h" + +#define ADD_ACTION_MENU_TEST(path, test_func) \ + g_test_add ((path), ActionMenuFixture, NULL, \ + am_fixture_setup, (test_func), am_fixture_teardown) + +typedef struct +{ + EosActionMenu *action_menu; + GtkAction *action1; + GtkAction *action2; + GtkAction *action3; +} ActionMenuFixture; + +static void +am_fixture_setup (ActionMenuFixture *fixture, + gconstpointer unused) +{ + fixture->action_menu = EOS_ACTION_MENU (eos_action_menu_new ()); + fixture->action1 = gtk_action_new ("1", "1", "1", "1"); + fixture->action2 = gtk_action_new ("2", "2", "2", "2"); + fixture->action3 = gtk_action_new ("3", "3", "3", "3"); + + g_object_ref (fixture->action1); + g_object_ref (fixture->action2); + g_object_ref (fixture->action3); +} + +static void +am_fixture_teardown (ActionMenuFixture *fixture, + gconstpointer unused) +{ + gtk_widget_destroy (GTK_WIDGET (fixture->action_menu)); + g_object_unref (fixture->action1); + g_object_unref (fixture->action2); + g_object_unref (fixture->action3); +} + +/* TESTS */ + +static void +test_am_add_action (ActionMenuFixture *fixture, + gconstpointer unused) +{ + gint size; + gchar *label, *icon_id; + + gtk_action_set_is_important (fixture->action1, TRUE); + gtk_action_set_icon_name (fixture->action1, "object-select-symbolic"); + + eos_action_menu_add_action (fixture->action_menu, fixture->action1); + + GtkWidget *button = gtk_grid_get_child_at (GTK_GRID (fixture->action_menu), 0, 0); + + g_assert (EOS_IS_ACTION_BUTTON (button)); + + g_object_get (button, + "size", &size, + "label", &label, + "icon-id", &icon_id, + NULL); + + g_assert ( size == EOS_ACTION_BUTTON_SIZE_PRIMARY); + g_assert ( g_strcmp0 (label, gtk_action_get_label (fixture->action1)) == 0); + g_assert ( g_strcmp0 (icon_id, gtk_action_get_icon_name (fixture->action1)) == 0); + + g_free (label); + g_free (icon_id); +} + +static void +test_am_get_action (ActionMenuFixture *fixture, + gconstpointer unused) +{ + eos_action_menu_add_action (fixture->action_menu, fixture->action1); + + GtkAction *retrieved = eos_action_menu_get_action (fixture->action_menu, "1"); + + g_assert (retrieved == fixture->action1); +} + +static void +test_am_list_actions (ActionMenuFixture *fixture, + gconstpointer unused) +{ + GList *list = eos_action_menu_list_actions (fixture->action_menu); + + g_assert (list == NULL); + + eos_action_menu_add_action (fixture->action_menu, fixture->action1); + eos_action_menu_add_action (fixture->action_menu, fixture->action2); + + list = eos_action_menu_list_actions (fixture->action_menu); + + g_assert (g_list_find (list, fixture->action1) != NULL); + g_assert (g_list_find (list, fixture->action2) != NULL); + + g_assert (g_list_find (list, fixture->action3) == NULL); +} + +static void +test_am_remove_action (ActionMenuFixture *fixture, + gconstpointer unused) +{ + GList *list; + + eos_action_menu_add_action (fixture->action_menu, fixture->action1); + eos_action_menu_add_action (fixture->action_menu, fixture->action2); + eos_action_menu_add_action (fixture->action_menu, fixture->action3); + + eos_action_menu_remove_action (fixture->action_menu, fixture->action2); + + list = eos_action_menu_list_actions (fixture->action_menu); + + g_assert (g_list_find (list, fixture->action1) != NULL); + g_assert (g_list_find (list, fixture->action2) == NULL); + g_assert (g_list_find (list, fixture->action3) != NULL); + + eos_action_menu_remove_action (fixture->action_menu, fixture->action1); + eos_action_menu_remove_action (fixture->action_menu, fixture->action3); + + list = eos_action_menu_list_actions (fixture->action_menu); + + g_assert (g_list_find (list, fixture->action1) == NULL); + g_assert (g_list_find (list, fixture->action2) == NULL); + g_assert (g_list_find (list, fixture->action3) == NULL); +} + +static void +test_am_remove_action_by_name (ActionMenuFixture *fixture, + gconstpointer unused) +{ + eos_action_menu_add_action (fixture->action_menu, fixture->action1); + eos_action_menu_add_action (fixture->action_menu, fixture->action2); + eos_action_menu_add_action (fixture->action_menu, fixture->action3); + + eos_action_menu_remove_action_by_name (fixture->action_menu, "2"); + + GList *list = eos_action_menu_list_actions (fixture->action_menu); + + g_assert (g_list_find (list, fixture->action1) != NULL); + g_assert (g_list_find (list, fixture->action2) == NULL); + g_assert (g_list_find (list, fixture->action3) != NULL); +} + +void +add_action_menu_tests (void) +{ + ADD_ACTION_MENU_TEST ("/action-menu/add-action", + test_am_add_action); + ADD_ACTION_MENU_TEST ("/action-menu/get-action", + test_am_get_action); + ADD_ACTION_MENU_TEST ("/action-menu/list-actions", + test_am_list_actions); + ADD_ACTION_MENU_TEST ("/action-menu/remove-action", + test_am_remove_action); + ADD_ACTION_MENU_TEST ("/action-menu/remove-action-by-name", + test_am_remove_action_by_name); +} |