[gtk/matthiasc/for-master] gtk-demo: Add a layout manager demo
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk/matthiasc/for-master] gtk-demo: Add a layout manager demo
- Date: Wed, 12 Aug 2020 02:23:06 +0000 (UTC)
commit 6ed781e18efddaba75c71398ef7e2253d05788cd
Author: Matthias Clasen <mclasen redhat com>
Date: Tue Aug 11 21:14:05 2020 -0400
gtk-demo: Add a layout manager demo
This is more or less a copy of the layout manager
example from clutter.
demos/gtk-demo/demo.gresource.xml | 9 ++
demos/gtk-demo/demochild.c | 72 +++++++++++++++
demos/gtk-demo/demolayout.c | 189 ++++++++++++++++++++++++++++++++++++++
demos/gtk-demo/demolayout.h | 13 +++
demos/gtk-demo/demowidget.c | 121 ++++++++++++++++++++++++
demos/gtk-demo/demowidget.h | 11 +++
demos/gtk-demo/layoutmanager.c | 61 ++++++++++++
demos/gtk-demo/meson.build | 6 +-
8 files changed, 481 insertions(+), 1 deletion(-)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index bee2c4042b..9f02e51b16 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -124,6 +124,14 @@
<file>gnome-fs-directory.png</file>
<file>gnome-fs-regular.png</file>
</gresource>
+ <gresource prefix="/layoutmanager">
+ <file>demolayout.h</file>
+ <file>demolayout.c</file>
+ <file>demowidget.h</file>
+ <file>demowidget.c</file>
+ <file>demochild.h</file>
+ <file>demochild.c</file>
+ </gresource>
<gresource prefix="/listview_filebrowser">
<file>listview_filebrowser.ui</file>
<file>listview_filebrowser.css</file>
@@ -206,6 +214,7 @@
<file>iconview_edit.c</file>
<file>images.c</file>
<file>infobar.c</file>
+ <file>layoutmanager.c</file>
<file>links.c</file>
<file>listbox.c</file>
<file>listbox2.c</file>
diff --git a/demos/gtk-demo/demochild.c b/demos/gtk-demo/demochild.c
new file mode 100644
index 0000000000..147cf606f9
--- /dev/null
+++ b/demos/gtk-demo/demochild.c
@@ -0,0 +1,72 @@
+#include "demochild.h"
+
+/* This is a trivial child widget just for demo purposes.
+ * It draws a 32x32 square in fixed color.
+ */
+
+struct _DemoChild
+{
+ GtkWidget parent_instance;
+ GdkRGBA color;
+};
+
+struct _DemoChildClass
+{
+ GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (DemoChild, demo_child, GTK_TYPE_WIDGET)
+
+static void
+demo_child_init (DemoChild *self)
+{
+}
+
+static void
+demo_child_snapshot (GtkWidget *widget,
+ GtkSnapshot *snapshot)
+{
+ DemoChild *self = DEMO_CHILD (widget);
+ int width, height;
+
+ width = gtk_widget_get_width (widget);
+ height = gtk_widget_get_height (widget);
+
+ gtk_snapshot_append_color (snapshot, &self->color,
+ &GRAPHENE_RECT_INIT(0, 0, width, height));
+}
+
+static void
+demo_child_measure (GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ *minimum = *natural = 32;
+}
+
+static void
+demo_child_class_init (DemoChildClass *class)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ widget_class->snapshot = demo_child_snapshot;
+ widget_class->measure = demo_child_measure;
+}
+
+GtkWidget *
+demo_child_new (const char *color)
+{
+ DemoChild *self;
+
+ self = g_object_new (DEMO_TYPE_CHILD,
+ "tooltip-text", color,
+ NULL);
+
+ gdk_rgba_parse (&self->color, color);
+
+ return GTK_WIDGET (self);
+}
diff --git a/demos/gtk-demo/demolayout.c b/demos/gtk-demo/demolayout.c
new file mode 100644
index 0000000000..fed244a3af
--- /dev/null
+++ b/demos/gtk-demo/demolayout.c
@@ -0,0 +1,189 @@
+#include "demolayout.h"
+
+struct _DemoLayout
+{
+ GtkLayoutManager parent_instance;
+
+ float position;
+ int pos[16];
+};
+
+struct _DemoLayoutClass
+{
+ GtkLayoutManagerClass parent_class;
+};
+
+G_DEFINE_TYPE (DemoLayout, demo_layout, GTK_TYPE_LAYOUT_MANAGER)
+
+static void
+demo_layout_measure (GtkLayoutManager *layout_manager,
+ GtkWidget *widget,
+ GtkOrientation orientation,
+ int for_size,
+ int *minimum,
+ int *natural,
+ int *minimum_baseline,
+ int *natural_baseline)
+{
+ GtkWidget *child;
+ int minimum_size = 0;
+ int natural_size = 0;
+
+ for (child = gtk_widget_get_first_child (widget);
+ child != NULL;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ int child_min = 0, child_nat = 0;
+
+ if (!gtk_widget_should_layout (child))
+ continue;
+
+ gtk_widget_measure (child, orientation, -1,
+ &child_min, &child_nat,
+ NULL, NULL);
+ minimum_size = MAX (minimum_size, child_min);
+ natural_size = MAX (natural_size, child_nat);
+ }
+
+ /* A back-of-a-napkin calculation to reserve enough
+ * space for arranging 16 children in a circle.
+ */
+ *minimum = 16 * minimum_size / G_PI + minimum_size;
+ *natural = 16 * natural_size / G_PI + natural_size;
+}
+
+static void
+demo_layout_allocate (GtkLayoutManager *layout_manager,
+ GtkWidget *widget,
+ int width,
+ int height,
+ int baseline)
+{
+ DemoLayout *self = DEMO_LAYOUT (layout_manager);
+ GtkWidget *child;
+ int i;
+ int child_width = 0;
+ int child_height = 0;
+ int x0, y0;
+ float r;
+ float t;
+
+ t = self->position;
+
+ for (child = gtk_widget_get_first_child (widget);
+ child != NULL;
+ child = gtk_widget_get_next_sibling (child))
+ {
+ GtkRequisition child_req;
+
+ if (!gtk_widget_should_layout (child))
+ continue;
+
+ gtk_widget_get_preferred_size (child, &child_req, NULL);
+
+ child_width = MAX (child_width, child_req.width);
+ child_height = MAX (child_height, child_req.height);
+ }
+
+ /* the center of our layout */
+ x0 = (width / 2);
+ y0 = (height / 2);
+
+ /* the radius for our circle of children */
+ r = 8 * child_width / G_PI;
+
+ for (child = gtk_widget_get_first_child (widget), i = 0;
+ child != NULL;
+ child = gtk_widget_get_next_sibling (child), i++)
+ {
+ GtkRequisition child_req;
+ float a = self->pos[i] * G_PI / 8;
+ int gx, gy;
+ int cx, cy;
+ int x, y;
+
+ if (!gtk_widget_should_layout (child))
+ continue;
+
+ gtk_widget_get_preferred_size (child, &child_req, NULL);
+
+ /* The grid position of child. */
+ gx = x0 + (i % 4 - 2) * child_width;
+ gy = y0 + (i / 4 - 2) * child_height;
+
+ /* The circle position of child. Note that we
+ * are adjusting the position by half the child size
+ * to place the center of child on a centered circle.
+ * This assumes that the children don't use align flags
+ * or uneven margins that would shift the center.
+ */
+ cx = x0 + sin (a) * r - child_req.width / 2;
+ cy = y0 + cos (a) * r - child_req.height / 2;
+
+ /* we interpolate between the two layouts according to
+ * the position value that has been set on the layout.
+ */
+ x = t * cx + (1 - t) * gx;
+ y = t * cy + (1 - t) * gy;
+
+ gtk_widget_size_allocate (child,
+ &(const GtkAllocation){ x, y, child_width, child_height},
+ -1);
+ }
+}
+
+static GtkSizeRequestMode
+demo_layout_get_request_mode (GtkLayoutManager *layout_manager,
+ GtkWidget *widget)
+{
+ return GTK_SIZE_REQUEST_CONSTANT_SIZE;
+}
+
+static void
+demo_layout_class_init (DemoLayoutClass *klass)
+{
+ GtkLayoutManagerClass *layout_class = GTK_LAYOUT_MANAGER_CLASS (klass);
+
+ layout_class->get_request_mode = demo_layout_get_request_mode;
+ layout_class->measure = demo_layout_measure;
+ layout_class->allocate = demo_layout_allocate;
+}
+
+static void
+demo_layout_init (DemoLayout *self)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ self->pos[i] = i;
+}
+
+GtkLayoutManager *
+demo_layout_new (void)
+{
+ return g_object_new (DEMO_TYPE_LAYOUT, NULL);
+}
+
+void
+demo_layout_set_position (DemoLayout *layout,
+ float position)
+{
+ layout->position = position;
+}
+
+/* Shuffle the circle positions of the children.
+ * Should be called when we are in the grid layout.
+ */
+void
+demo_layout_shuffle (DemoLayout *layout)
+{
+ int i, j, tmp;
+
+ for (i = 0; i < 16; i++)
+ {
+ j = g_random_int_range (0, i + 1);
+ tmp = layout->pos[i];
+ layout->pos[i] = layout->pos[j];
+ layout->pos[j] = tmp;
+ }
+}
diff --git a/demos/gtk-demo/demolayout.h b/demos/gtk-demo/demolayout.h
new file mode 100644
index 0000000000..8672ba2f3c
--- /dev/null
+++ b/demos/gtk-demo/demolayout.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define DEMO_TYPE_LAYOUT (demo_layout_get_type ())
+
+G_DECLARE_FINAL_TYPE (DemoLayout, demo_layout, DEMO, LAYOUT, GtkLayoutManager)
+
+GtkLayoutManager * demo_layout_new (void);
+
+void demo_layout_set_position (DemoLayout *layout,
+ float position);
+void demo_layout_shuffle (DemoLayout *layout);
diff --git a/demos/gtk-demo/demowidget.c b/demos/gtk-demo/demowidget.c
new file mode 100644
index 0000000000..16c5c28ad3
--- /dev/null
+++ b/demos/gtk-demo/demowidget.c
@@ -0,0 +1,121 @@
+#include "demowidget.h"
+#include "demolayout.h"
+
+/* parent widget */
+
+struct _DemoWidget
+{
+ GtkWidget parent_instance;
+
+ gboolean backward; /* whether we go 0 -> 1 or 1 -> 0 */
+ gint64 start_time; /* time the transition started */
+ guint tick_id; /* our tick cb */
+};
+
+struct _DemoWidgetClass
+{
+ GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (DemoWidget, demo_widget, GTK_TYPE_WIDGET)
+
+/* The widget is controlling the transition by calling
+ * demo_layout_set_position() in a tick callback.
+ *
+ * We take half a second to go from one layout to the other.
+ */
+
+#define DURATION (0.5 * G_TIME_SPAN_SECOND)
+
+static gboolean
+transition (GtkWidget *widget,
+ GdkFrameClock *frame_clock,
+ gpointer data)
+{
+ DemoWidget *self = DEMO_WIDGET (widget);
+ DemoLayout *demo_layout = DEMO_LAYOUT (gtk_widget_get_layout_manager (widget));
+ gint64 now = g_get_monotonic_time ();
+
+ gtk_widget_queue_allocate (widget);
+
+ if (self->backward)
+ demo_layout_set_position (demo_layout, 1.0 - (now - self->start_time) / DURATION);
+ else
+ demo_layout_set_position (demo_layout, (now - self->start_time) / DURATION);
+
+ if (now - self->start_time >= DURATION)
+ {
+ self->backward = !self->backward;
+ demo_layout_set_position (demo_layout, self->backward ? 1.0 : 0.0);
+ /* keep things interesting by shuffling the positions */
+ if (!self->backward)
+ demo_layout_shuffle (demo_layout);
+ self->tick_id = 0;
+
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+clicked (GtkGestureClick *gesture,
+ guint n_press,
+ double x,
+ double y,
+ gpointer data)
+{
+ DemoWidget *self = data;
+
+ if (self->tick_id != 0)
+ return;
+
+ self->start_time = g_get_monotonic_time ();
+ self->tick_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), transition, NULL, NULL);
+}
+
+static void
+demo_widget_init (DemoWidget *self)
+{
+ GtkGesture *gesture;
+
+ gesture = gtk_gesture_click_new ();
+ g_signal_connect (gesture, "pressed", G_CALLBACK (clicked), self);
+ gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
+}
+
+static void
+demo_widget_dispose (GObject *object)
+{
+ GtkWidget *child;
+
+ while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
+ gtk_widget_unparent (child);
+
+ G_OBJECT_CLASS (demo_widget_parent_class)->dispose (object);
+}
+
+static void
+demo_widget_class_init (DemoWidgetClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+ object_class->dispose = demo_widget_dispose;
+
+ /* here is where we use our custom layout manager */
+ gtk_widget_class_set_layout_manager_type (widget_class, DEMO_TYPE_LAYOUT);
+}
+
+GtkWidget *
+demo_widget_new (void)
+{
+ return g_object_new (DEMO_TYPE_WIDGET, NULL);
+}
+
+void
+demo_widget_add_child (DemoWidget *self,
+ GtkWidget *child)
+{
+ gtk_widget_set_parent (child, GTK_WIDGET (self));
+}
diff --git a/demos/gtk-demo/demowidget.h b/demos/gtk-demo/demowidget.h
new file mode 100644
index 0000000000..cd8a5d4778
--- /dev/null
+++ b/demos/gtk-demo/demowidget.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define DEMO_TYPE_WIDGET (demo_widget_get_type ())
+G_DECLARE_FINAL_TYPE (DemoWidget, demo_widget, DEMO, WIDGET, GtkWidget)
+
+GtkWidget * demo_widget_new (void);
+
+void demo_widget_add_child (DemoWidget *self,
+ GtkWidget *child);
diff --git a/demos/gtk-demo/layoutmanager.c b/demos/gtk-demo/layoutmanager.c
new file mode 100644
index 0000000000..3a715d0fc8
--- /dev/null
+++ b/demos/gtk-demo/layoutmanager.c
@@ -0,0 +1,61 @@
+/* Layout Manager
+ *
+ * This examples shows a simple example of a custom layout manager
+ * and a widget using it. The layout manager places the children
+ * of the widget in a grid or a circle, or something in between.
+ *
+ * The widget is animating the transition between the two layouts.
+ * Click to start the transition.
+ */
+
+#include <gtk/gtk.h>
+
+#include "demowidget.h"
+#include "demochild.h"
+
+
+GtkWidget *
+do_layoutmanager (GtkWidget *parent)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *widget;
+ GtkWidget *child;
+ const char *color[] = {
+ "red", "orange", "yellow", "green",
+ "blue", "grey", "magenta", "lime",
+ "yellow", "firebrick", "aqua", "purple",
+ "tomato", "pink", "thistle", "maroon"
+ };
+ int i;
+
+ window = gtk_window_new ();
+ gtk_window_set_title (GTK_WINDOW (window), "Layout Manager");
+ gtk_window_set_default_size (GTK_WINDOW (window), 600, 600);
+ g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+
+ widget = demo_widget_new ();
+
+ for (i = 0; i < 16; i++)
+ {
+ child = demo_child_new (color[i]);
+ gtk_widget_set_margin_start (child, 4);
+ gtk_widget_set_margin_end (child, 4);
+ gtk_widget_set_margin_top (child, 4);
+ gtk_widget_set_margin_bottom (child, 4);
+ demo_widget_add_child (DEMO_WIDGET (widget), child);
+ }
+
+ gtk_window_set_child (GTK_WINDOW (window), widget);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show (window);
+ else
+ gtk_window_destroy (GTK_WINDOW (window));
+
+ return window;
+
+}
diff --git a/demos/gtk-demo/meson.build b/demos/gtk-demo/meson.build
index 86f0b431b1..3cf3762d77 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -38,6 +38,7 @@ demos = files([
'iconview_edit.c',
'images.c',
'infobar.c',
+ 'layoutmanager.c',
'links.c',
'listbox.c',
'listbox2.c',
@@ -98,7 +99,10 @@ extra_demo_sources = files(['main.c',
'puzzlepiece.c',
'bluroverlay.c',
'demoimage.c',
- 'demotaggedentry.c'])
+ 'demotaggedentry.c',
+ 'demochild.c',
+ 'demolayout.c',
+ 'demowidget.c'])
if harfbuzz_dep.found() and pangoft_dep.found()
demos += files('font_features.c')
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]