From 011fffcedfa2845179355052f69a2f0ceabd5852 Mon Sep 17 00:00:00 2001 From: Matt Watson Date: Mon, 13 Jan 2014 15:39:25 -0800 Subject: Added EosCustomContainer C class for gjs containers forall cannot be overridden in gjs. There's an upstream bug here https://bugzilla.gnome.org/show_bug.cgi?id=701567 but that does not look like it will be fixed soon. So for now added a small c class that take care of GtkContainers add, remove and forall methods. This makes it possible to write generic containers in gjs. See docs for an example [endlessm/eos-sdk#481] --- docs/reference/endless/endless-docs.xml | 1 + docs/reference/endless/endless-sections.txt | 15 ++++ endless/Makefile.am | 2 + endless/endless.h | 1 + endless/eoscustomcontainer.c | 114 ++++++++++++++++++++++++++++ endless/eoscustomcontainer.h | 67 ++++++++++++++++ test/endless/Makefile.am.inc | 1 + test/endless/run-tests.c | 1 + test/endless/run-tests.h | 1 + test/endless/test-custom-container.c | 83 ++++++++++++++++++++ test/smoke-tests/custom-container.js | 44 +++++++++++ 11 files changed, 330 insertions(+) create mode 100644 endless/eoscustomcontainer.c create mode 100644 endless/eoscustomcontainer.h create mode 100644 test/endless/test-custom-container.c create mode 100644 test/smoke-tests/custom-container.js diff --git a/docs/reference/endless/endless-docs.xml b/docs/reference/endless/endless-docs.xml index c602ed2..f946d1f 100644 --- a/docs/reference/endless/endless-docs.xml +++ b/docs/reference/endless/endless-docs.xml @@ -23,6 +23,7 @@ + diff --git a/docs/reference/endless/endless-sections.txt b/docs/reference/endless/endless-sections.txt index 4e4887a..963629f 100644 --- a/docs/reference/endless/endless-sections.txt +++ b/docs/reference/endless/endless-sections.txt @@ -192,3 +192,18 @@ eos_flexy_grid_get_type eos_flexy_grid_cell_get_type eos_flexy_shape_get_type + +
+custom-container +EosCustomContainer +eos_custom_container_new + +EOS_CUSTOM_CONTAINER +EOS_CUSTOM_CONTAINER_CLASS +EOS_CUSTOM_CONTAINER_GET_CLASS +EOS_IS_CUSTOM_CONTAINER +EOS_IS_CUSTOM_CONTAINER_CLASS +EOS_TYPE_CUSTOM_CONTAINER +EosCustomContainerClass +eos_custom_container_get_type +
diff --git a/endless/Makefile.am b/endless/Makefile.am index 3a5b95c..74d8b3c 100644 --- a/endless/Makefile.am +++ b/endless/Makefile.am @@ -28,6 +28,7 @@ endless_private_installed_headers = \ endless/eosversion.h \ endless/eosactionbutton.h \ endless/eosapplication.h \ + endless/eoscustomcontainer.h \ endless/eosenums.h \ endless/eosmacros.h \ endless/eospagemanager.h \ @@ -39,6 +40,7 @@ endless_private_installed_headers = \ endless_library_sources = \ endless/eosapplication.c \ + endless/eoscustomcontainer.c \ endless/eoshello.c \ endless/eosinit.c endless/eosinit-private.h \ endless/eospagemanager.c endless/eospagemanager-private.h \ diff --git a/endless/endless.h b/endless/endless.h index 9efbe6e..f3f2061 100644 --- a/endless/endless.h +++ b/endless/endless.h @@ -18,6 +18,7 @@ G_BEGIN_DECLS #include "eospagemanager.h" #include "eossplashpagemanager.h" #include "eoswindow.h" +#include "eoscustomcontainer.h" #undef _EOS_SDK_INSIDE_ENDLESS_H diff --git a/endless/eoscustomcontainer.c b/endless/eoscustomcontainer.c new file mode 100644 index 0000000..a68745b --- /dev/null +++ b/endless/eoscustomcontainer.c @@ -0,0 +1,114 @@ +/* Copyright 2014 Endless Mobile, Inc. */ + +#include "config.h" +#include "eoscustomcontainer.h" + +#include + +/** + * SECTION:custom-container + * @short_description: For gjs container implementations + * @title: Custom Container + * + * This container allows for implementing a custom size allocate routine in + * gjs. This container implements the bare minimum of virtual functions from + * GtkContainer, add, remove and forall. Add and remove simply append to and + * remove from an internal list, and forall iterates over that list. Forall + * cannot be implemented in gjs, it's not supported by gobject-introspection, + * so this is needed for custom gjs containers. This class will not + * size_allocate any children or ever queue_resize, so that is up to + * subclasses in gjs. + * + * Here's an example gjs program which allocates a GtkFrame the top right + * quarter of it's allocation. + * |[ + * const TestContainer = Lang.Class({ + * Name: 'TestContainer', + * Extends: Endless.CustomContainer, + * + * _init: function() { + * this.parent(); + * + * this._frame = new Gtk.Frame(); + * this.add(this._frame); + * }, + * + * vfunc_size_allocate: function (alloc) { + * this.parent(alloc); + * alloc.width = alloc.width / 2; + * alloc.height = alloc.height / 2; + * this._frame.size_allocate(alloc); + * } + * }); + * ]| + */ + +typedef struct { + GList *children; +} EosCustomContainerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (EosCustomContainer, eos_custom_container, GTK_TYPE_CONTAINER) + +static void +eos_custom_container_add (GtkContainer *container, + GtkWidget *child) +{ + EosCustomContainer *self = EOS_CUSTOM_CONTAINER (container); + EosCustomContainerPrivate *priv = eos_custom_container_get_instance_private (self); + + priv->children = g_list_prepend (priv->children, child); + gtk_widget_set_parent (child, GTK_WIDGET (container)); +} + +static void +eos_custom_container_remove (GtkContainer *container, + GtkWidget *child) +{ + EosCustomContainer *self = EOS_CUSTOM_CONTAINER (container); + EosCustomContainerPrivate *priv = eos_custom_container_get_instance_private (self); + + priv->children = g_list_remove (priv->children, child); + gtk_widget_unparent (child); +} + +static void +eos_custom_container_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + EosCustomContainer *self = EOS_CUSTOM_CONTAINER (container); + EosCustomContainerPrivate *priv = eos_custom_container_get_instance_private (self); + + g_list_foreach (priv->children, (GFunc)callback, callback_data); +} + +static void +eos_custom_container_class_init (EosCustomContainerClass *klass) +{ + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + container_class->add = eos_custom_container_add; + container_class->remove = eos_custom_container_remove; + container_class->forall = eos_custom_container_forall; +} + +static void +eos_custom_container_init (EosCustomContainer *self) +{ + GtkWidget *widget = GTK_WIDGET (self); + gtk_widget_set_has_window (widget, FALSE); +} + +/** + * eos_custom_container_new: + * + * Creates a new custom container. + * + * Returns: the custom container. + */ +GtkWidget * +eos_custom_container_new (void) +{ + return g_object_new (EOS_TYPE_CUSTOM_CONTAINER, NULL); +} diff --git a/endless/eoscustomcontainer.h b/endless/eoscustomcontainer.h new file mode 100644 index 0000000..743bc59 --- /dev/null +++ b/endless/eoscustomcontainer.h @@ -0,0 +1,67 @@ +/* Copyright 2014 Endless Mobile, Inc. */ + +#ifndef EOS_CUSTOM_CONTAINER_H +#define EOS_CUSTOM_CONTAINER_H + +#if !(defined(_EOS_SDK_INSIDE_ENDLESS_H) || defined(COMPILING_EOS_SDK)) +#error "Please do not include this header file directly." +#endif + +#include "eostypes.h" + +#include + +G_BEGIN_DECLS + +#define EOS_TYPE_CUSTOM_CONTAINER eos_custom_container_get_type() + +#define EOS_CUSTOM_CONTAINER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + EOS_TYPE_CUSTOM_CONTAINER, EosCustomContainer)) + +#define EOS_CUSTOM_CONTAINER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + EOS_TYPE_CUSTOM_CONTAINER, EosCustomContainerClass)) + +#define EOS_IS_CUSTOM_CONTAINER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + EOS_TYPE_CUSTOM_CONTAINER)) + +#define EOS_IS_CUSTOM_CONTAINER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), \ + EOS_TYPE_CUSTOM_CONTAINER)) + +#define EOS_CUSTOM_CONTAINER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + EOS_TYPE_CUSTOM_CONTAINER, EosCustomContainerClass)) + +typedef struct _EosCustomContainer EosCustomContainer; +typedef struct _EosCustomContainerClass EosCustomContainerClass; + +/** + * EosCustomContainer: + * + * This structure contains no public members. + */ +struct _EosCustomContainer +{ + GtkContainer parent; +}; + +struct _EosCustomContainerClass +{ + GtkContainerClass parent_class; + + /* For further expansion */ + gpointer _padding[8]; +}; + +EOS_SDK_ALL_API_VERSIONS +GType eos_custom_container_get_type (void) G_GNUC_CONST; + +EOS_SDK_ALL_API_VERSIONS +GtkWidget *eos_custom_container_new (void); + +G_END_DECLS + +#endif /* EOS_CUSTOM_CONTAINER_H */ diff --git a/test/endless/Makefile.am.inc b/test/endless/Makefile.am.inc index 2835934..8b78baf 100644 --- a/test/endless/Makefile.am.inc +++ b/test/endless/Makefile.am.inc @@ -12,6 +12,7 @@ test_endless_run_tests_SOURCES = \ $(ENDLESS_TESTS_DIRECTORY)/endless/test-action-menu.c \ $(ENDLESS_TESTS_DIRECTORY)/endless/test-action-button.c \ $(ENDLESS_TESTS_DIRECTORY)/endless/test-flexy-grid.c \ + $(ENDLESS_TESTS_DIRECTORY)/endless/test-custom-container.c \ $(NULL) test_endless_run_tests_CPPFLAGS = $(TEST_FLAGS) test_endless_run_tests_LDADD = $(TEST_LIBS) diff --git a/test/endless/run-tests.c b/test/endless/run-tests.c index db9c289..4bc4006 100644 --- a/test/endless/run-tests.c +++ b/test/endless/run-tests.c @@ -111,6 +111,7 @@ main (int argc, add_action_menu_tests (); add_action_button_tests (); add_flexy_grid_test (); + add_custom_container_tests (); return g_test_run (); } diff --git a/test/endless/run-tests.h b/test/endless/run-tests.h index 8947a5a..cb52ab7 100644 --- a/test/endless/run-tests.h +++ b/test/endless/run-tests.h @@ -41,5 +41,6 @@ void add_splash_page_manager_tests (void); void add_action_menu_tests (void); void add_action_button_tests (void); void add_flexy_grid_test (void); +void add_custom_container_tests (void); #endif /* RUN_TESTS_H */ diff --git a/test/endless/test-custom-container.c b/test/endless/test-custom-container.c new file mode 100644 index 0000000..42cf99f --- /dev/null +++ b/test/endless/test-custom-container.c @@ -0,0 +1,83 @@ +/* Copyright 2014 Endless Mobile, Inc. */ + +#include +#include + +#include "run-tests.h" + +typedef struct +{ + GtkContainer *container; + GtkWidget *child1; + GtkWidget *child2; + GtkWidget *child3; +} CustomContainerFixture; + +#define ADD_CUSTOM_CONTAINER_TEST(path, test_func) \ + g_test_add ((path), CustomContainerFixture, NULL, \ + custom_container_fixture_setup, \ + (test_func), \ + custom_container_fixture_teardown) + + +static void +custom_container_fixture_setup (CustomContainerFixture *fixture, + gconstpointer unused G_GNUC_UNUSED) +{ + // We acquire the widget ref so they don't automatically get destroyed after + // being removed from the container. + fixture->child1 = g_object_ref_sink (gtk_label_new ("1")); + fixture->child2 = g_object_ref_sink (gtk_label_new ("2")); + fixture->child3 = g_object_ref_sink (gtk_label_new ("3")); + fixture->container = GTK_CONTAINER (eos_custom_container_new ()); +} + +static void +custom_container_fixture_teardown (CustomContainerFixture *fixture, + gconstpointer unused G_GNUC_UNUSED) +{ + gtk_widget_destroy (fixture->child1); + gtk_widget_destroy (fixture->child2); + gtk_widget_destroy (fixture->child3); + gtk_widget_destroy ((GtkWidget *) fixture->container); + g_object_unref (fixture->child1); + g_object_unref (fixture->child2); + g_object_unref (fixture->child3); +} + +static void +test_custom_container_add (CustomContainerFixture *fixture, + gconstpointer unused G_GNUC_UNUSED) +{ + gtk_container_add (fixture->container, fixture->child1); + gtk_container_add (fixture->container, fixture->child2); + gtk_container_add (fixture->container, fixture->child3); + + g_assert (gtk_widget_get_parent (fixture->child1) == GTK_WIDGET (fixture->container)); + GList *children = gtk_container_get_children (fixture->container); + g_assert (g_list_length (children) == 3); + g_assert (g_list_find (children, fixture->child1) != NULL); + g_assert (g_list_find (children, fixture->child2) != NULL); + g_assert (g_list_find (children, fixture->child3) != NULL); +} + +static void +test_custom_container_remove (CustomContainerFixture *fixture, + gconstpointer unused G_GNUC_UNUSED) +{ + gtk_container_add (fixture->container, fixture->child1); + gtk_container_add (fixture->container, fixture->child2); + gtk_container_add (fixture->container, fixture->child3); + gtk_container_remove (fixture->container, fixture->child2); + + g_assert (gtk_widget_get_parent (fixture->child2) != GTK_WIDGET (fixture->container)); + GList *children = gtk_container_get_children (fixture->container); + g_assert (g_list_find (children, fixture->child2) == NULL); +} + +void +add_custom_container_tests (void) +{ + ADD_CUSTOM_CONTAINER_TEST ("/custom-container/add", test_custom_container_add); + ADD_CUSTOM_CONTAINER_TEST ("/custom-container/remove", test_custom_container_remove); +} diff --git a/test/smoke-tests/custom-container.js b/test/smoke-tests/custom-container.js new file mode 100644 index 0000000..679e0be --- /dev/null +++ b/test/smoke-tests/custom-container.js @@ -0,0 +1,44 @@ +// Copyright 2014 Endless Mobile, Inc. + +const Lang = imports.lang; +const Endless = imports.gi.Endless; +const Gtk = imports.gi.Gtk; +const GObject = imports.gi.GObject; + +const TEST_APPLICATION_ID = 'com.endlessm.example.test'; + +const TestContainer = Lang.Class({ + Name: 'TestContainer', + Extends: Endless.CustomContainer, + + _init: function() { + this.parent(); + + this._frame = new Gtk.Frame(); + this.add(this._frame); + }, + + vfunc_size_allocate: function (alloc) { + this.parent(alloc); + alloc.width = alloc.width / 2; + alloc.height = alloc.height / 2; + this._frame.size_allocate(alloc); + } +}); + +const TestApplication = new Lang.Class ({ + Name: 'TestApplication', + Extends: Gtk.Application, + + vfunc_startup: function() { + this.parent(); + let window = new Gtk.Window(); + window.add(new TestContainer()); + window.show_all(); + this.add_window(window); + } +}); + +let app = new TestApplication({ application_id: TEST_APPLICATION_ID, + flags: 0 }); +app.run(ARGV); -- cgit v1.2.3