[gtk/matthiasc/for-master] gtk-demo: Add a layout manager demo



commit ab511aec5cadcee126e6c54b61fe0d5f4f4770d3
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/demolayout.c       | 167 ++++++++++++++++++++++++++++++++++++++
 demos/gtk-demo/demolayout.h       |  12 +++
 demos/gtk-demo/demowidget.c       | 118 +++++++++++++++++++++++++++
 demos/gtk-demo/demowidget.h       |  11 +++
 demos/gtk-demo/layoutmanager.c    |  61 ++++++++++++++
 demos/gtk-demo/meson.build        |   6 +-
 7 files changed, 383 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/demolayout.c b/demos/gtk-demo/demolayout.c
new file mode 100644
index 0000000000..c28d31314b
--- /dev/null
+++ b/demos/gtk-demo/demolayout.c
@@ -0,0 +1,167 @@
+#include "demolayout.h"
+
+struct _DemoLayout
+{
+  GtkLayoutManager parent_instance;
+
+  float position;
+};
+
+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 *demo_layout = DEMO_LAYOUT (layout_manager);
+  GtkWidget *child;
+  int pos;
+  int child_width = 0;
+  int child_height = 0;
+  int x0, y0;
+  float r;
+  float t;
+
+  t = demo_layout->position;
+
+  for (child = gtk_widget_get_first_child (widget), pos = 0;
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child), pos++)
+    {
+      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), pos = 0;
+       child != NULL;
+       child = gtk_widget_get_next_sibling (child), pos++)
+    {
+      GtkRequisition child_req;
+      float a = pos * 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 + (pos % 4 - 2) * child_width;
+      gy = y0 + (pos / 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)
+{
+}
+
+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;
+}
diff --git a/demos/gtk-demo/demolayout.h b/demos/gtk-demo/demolayout.h
new file mode 100644
index 0000000000..5345fb8792
--- /dev/null
+++ b/demos/gtk-demo/demolayout.h
@@ -0,0 +1,12 @@
+#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);
diff --git a/demos/gtk-demo/demowidget.c b/demos/gtk-demo/demowidget.c
new file mode 100644
index 0000000000..8ca42e7c1a
--- /dev/null
+++ b/demos/gtk-demo/demowidget.c
@@ -0,0 +1,118 @@
+#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);
+      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]