summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/reference/endless/Makefile.am1
-rw-r--r--endless/Makefile.am4
-rw-r--r--endless/eosactionmenu-private.h71
-rw-r--r--endless/eosactionmenu.c213
-rw-r--r--overrides/Endless.js12
-rw-r--r--test/Makefile.am5
-rw-r--r--test/run-tests.c1
-rw-r--r--test/run-tests.h1
-rw-r--r--test/smoke-tests/action-buttons.js58
-rw-r--r--test/smoke-tests/eosactionbutton.css16
-rw-r--r--test/test-action-menu.c165
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);
+}