[gtk/matthiasc/for-master] gtk-demo: Add another demo




commit d71337513f467e6e67d33fb946fdc451c8ca42f1
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Sep 15 22:17:34 2020 -0400

    gtk-demo: Add another demo
    
    Show how to add a context menu to a custom widget,
    and how to make a GtkPicture lookalike.

 demos/gtk-demo/demo.gresource.xml |   6 +
 demos/gtk-demo/demo3widget.c      | 243 ++++++++++++++++++++++++++++++++++++++
 demos/gtk-demo/demo3widget.h      |   8 ++
 demos/gtk-demo/demo3widget.ui     |  30 +++++
 demos/gtk-demo/menu.c             |  47 ++++++++
 demos/gtk-demo/meson.build        |   4 +-
 6 files changed, 337 insertions(+), 1 deletion(-)
---
diff --git a/demos/gtk-demo/demo.gresource.xml b/demos/gtk-demo/demo.gresource.xml
index 08b90b677a..39d6ad2db8 100644
--- a/demos/gtk-demo/demo.gresource.xml
+++ b/demos/gtk-demo/demo.gresource.xml
@@ -181,6 +181,11 @@
     <file>fontify.h</file>
     <file>main.ui</file>
   </gresource>
+  <gresource prefix="/menu">
+    <file>demo3widget.c</file>
+    <file>demo3widget.h</file>
+    <file>demo3widget.ui</file>
+  </gresource>
   <gresource prefix="/shortcuts">
     <file>shortcuts.ui</file>
     <file>shortcuts-builder.ui</file>
@@ -265,6 +270,7 @@
     <file>list_store.c</file>
     <file>main.c</file>
     <file>markup.c</file>
+    <file>menu.c</file>
     <file>overlay.c</file>
     <file>overlay2.c</file>
     <file>paint.c</file>
diff --git a/demos/gtk-demo/demo3widget.c b/demos/gtk-demo/demo3widget.c
new file mode 100644
index 0000000000..3a309927e8
--- /dev/null
+++ b/demos/gtk-demo/demo3widget.c
@@ -0,0 +1,243 @@
+#include <math.h>
+#include "demo3widget.h"
+
+enum
+{
+  PROP_PAINTABLE = 1,
+  PROP_SCALE
+};
+
+struct _Demo3Widget
+{
+  GtkWidget parent_instance;
+
+  GdkPaintable *paintable;
+  float scale;
+
+  GtkWidget *menu;
+};
+
+struct _Demo3WidgetClass
+{
+  GtkWidgetClass parent_class;
+};
+
+G_DEFINE_TYPE (Demo3Widget, demo3_widget, GTK_TYPE_WIDGET)
+
+static void
+demo3_widget_init (Demo3Widget *self)
+{
+  self->scale = 1.f;
+  gtk_widget_init_template (GTK_WIDGET (self));
+}
+
+static void
+demo3_widget_dispose (GObject *object)
+{
+  Demo3Widget *self = DEMO3_WIDGET (object);
+
+  g_clear_object (&self->paintable);
+  g_clear_pointer (&self->menu, gtk_widget_unparent);
+
+  G_OBJECT_CLASS (demo3_widget_parent_class)->dispose (object);
+}
+
+static void
+demo3_widget_snapshot (GtkWidget   *widget,
+                       GtkSnapshot *snapshot)
+{
+  Demo3Widget *self = DEMO3_WIDGET (widget);
+  int x, y, width, height;
+  double w, h;
+
+  width = gtk_widget_get_width (widget);
+  height = gtk_widget_get_height (widget);
+
+  w = self->scale * gdk_paintable_get_intrinsic_width (self->paintable);
+  h = self->scale * gdk_paintable_get_intrinsic_height (self->paintable);
+
+  x = MAX (0, (width - ceil (w)) / 2);
+  y = MAX (0, (height - ceil (h)) / 2);
+
+  gtk_snapshot_push_clip (snapshot, &GRAPHENE_RECT_INIT (0, 0, width, height));
+  gtk_snapshot_save (snapshot);
+  gtk_snapshot_translate (snapshot, &GRAPHENE_POINT_INIT (x, y));
+  gdk_paintable_snapshot (self->paintable, snapshot, w, h);
+  gtk_snapshot_restore (snapshot);
+  gtk_snapshot_pop (snapshot);
+}
+
+static void
+demo3_widget_measure (GtkWidget      *widget,
+                      GtkOrientation  orientation,
+                      int             for_size,
+                      int            *minimum,
+                      int            *natural,
+                      int            *minimum_baseline,
+                      int            *natural_baseline)
+{
+  Demo3Widget *self = DEMO3_WIDGET (widget);
+  int size;
+
+  if (orientation == GTK_ORIENTATION_HORIZONTAL)
+    size = gdk_paintable_get_intrinsic_width (self->paintable);
+  else
+    size = gdk_paintable_get_intrinsic_height (self->paintable);
+
+  *minimum = *natural = self->scale * size;
+}
+
+static void
+demo3_widget_size_allocate (GtkWidget *widget,
+                            int        width,
+                            int        height,
+                            int        baseline)
+{
+  Demo3Widget *self = DEMO3_WIDGET (widget);
+
+  /* Since we are not using a layout manager (who would do this
+   * for us), we need to allocate a size for our menu by calling
+   * gtk_native_check_resize().
+   */
+  gtk_native_check_resize (GTK_NATIVE (self->menu));
+}
+
+static void
+demo3_widget_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+  Demo3Widget *self = DEMO3_WIDGET (object);
+
+  switch (prop_id)
+    {
+    case PROP_PAINTABLE:
+      g_clear_object (&self->paintable);
+      self->paintable = g_value_dup_object (value);
+      gtk_widget_queue_resize (GTK_WIDGET (object));
+      break;
+
+    case PROP_SCALE:
+      self->scale = g_value_get_float (value);
+      gtk_widget_queue_resize (GTK_WIDGET (object));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+demo3_widget_get_property (GObject     *object,
+                           guint        prop_id,
+                           GValue      *value,
+                           GParamSpec  *pspec)
+{
+  Demo3Widget *self = DEMO3_WIDGET (object);
+
+  switch (prop_id)
+    {
+    case PROP_PAINTABLE:
+      g_value_set_object (value, self->paintable);
+      break;
+
+    case PROP_SCALE:
+      g_value_set_float (value, self->scale);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+pressed_cb (GtkGestureClick *gesture,
+            guint            n_press,
+            double           x,
+            double           y,
+            Demo3Widget     *self)
+{
+  /* We are placing our menu at the point where
+   * the click happened, before popping it up.
+   */
+  gtk_popover_set_pointing_to (GTK_POPOVER (self->menu),
+                               &(const GdkRectangle){ x, y, 1, 1 });
+  gtk_popover_popup (GTK_POPOVER (self->menu));
+}
+
+static void
+zoom_cb (GtkWidget  *widget,
+         const char *action_name,
+         GVariant   *parameter)
+{
+  Demo3Widget *self = DEMO3_WIDGET (widget);
+  float scale;
+
+  if (g_str_equal (action_name, "zoom.in"))
+    scale = MIN (10, self->scale * M_SQRT2);
+  else if (g_str_equal (action_name, "zoom.out"))
+    scale = MAX (0.01, self->scale / M_SQRT2);
+  else
+    scale = 1.0;
+
+  gtk_widget_action_set_enabled (widget, "zoom.in", scale < 10);
+  gtk_widget_action_set_enabled (widget, "zoom.out", scale > 0.01);
+  gtk_widget_action_set_enabled (widget, "zoom.reset", scale != 1);
+
+  g_object_set (widget, "scale", scale, NULL);
+}
+
+static void
+demo3_widget_class_init (Demo3WidgetClass *class)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (class);
+  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
+
+  object_class->dispose = demo3_widget_dispose;
+  object_class->set_property = demo3_widget_set_property;
+  object_class->get_property = demo3_widget_get_property;
+
+  widget_class->snapshot = demo3_widget_snapshot;
+  widget_class->measure = demo3_widget_measure;
+  widget_class->size_allocate = demo3_widget_size_allocate;
+
+  g_object_class_install_property (object_class, PROP_PAINTABLE,
+      g_param_spec_object ("paintable", "Paintable", "Paintable",
+                           GDK_TYPE_PAINTABLE,
+                           G_PARAM_READWRITE));
+
+  g_object_class_install_property (object_class, PROP_SCALE,
+      g_param_spec_float ("scale", "Scale", "Scale",
+                          0.0, 10.0, 1.0,
+                          G_PARAM_READWRITE));
+
+  /* These are the actions that we are using in the menu */
+  gtk_widget_class_install_action (widget_class, "zoom.in", NULL, zoom_cb);
+  gtk_widget_class_install_action (widget_class, "zoom.out", NULL, zoom_cb);
+  gtk_widget_class_install_action (widget_class, "zoom.reset", NULL, zoom_cb);
+
+  gtk_widget_class_set_template_from_resource (widget_class, "/menu/demo3widget.ui");
+  gtk_widget_class_bind_template_child (widget_class, Demo3Widget, menu);
+  gtk_widget_class_bind_template_callback (widget_class, pressed_cb);
+}
+
+GtkWidget *
+demo3_widget_new (const char *resource)
+{
+  Demo3Widget *self;
+  GdkPixbuf *pixbuf;
+  GdkPaintable *paintable;
+
+  pixbuf = gdk_pixbuf_new_from_resource (resource, NULL);
+  paintable = GDK_PAINTABLE (gdk_texture_new_for_pixbuf (pixbuf));
+
+  self = g_object_new (DEMO3_TYPE_WIDGET, "paintable", paintable, NULL);
+
+  g_object_unref (pixbuf);
+  g_object_unref (paintable);
+
+  return GTK_WIDGET (self);
+}
diff --git a/demos/gtk-demo/demo3widget.h b/demos/gtk-demo/demo3widget.h
new file mode 100644
index 0000000000..5bf86bb855
--- /dev/null
+++ b/demos/gtk-demo/demo3widget.h
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <gtk/gtk.h>
+
+#define DEMO3_TYPE_WIDGET (demo3_widget_get_type ())
+G_DECLARE_FINAL_TYPE (Demo3Widget, demo3_widget, DEMO3, WIDGET, GtkWidget)
+
+GtkWidget * demo3_widget_new (const char *resource);
diff --git a/demos/gtk-demo/demo3widget.ui b/demos/gtk-demo/demo3widget.ui
new file mode 100644
index 0000000000..71f1f57255
--- /dev/null
+++ b/demos/gtk-demo/demo3widget.ui
@@ -0,0 +1,30 @@
+<interface>
+  <menu id="model">
+    <item>
+      <attribute name="label">Zoom Out</attribute>
+      <attribute name="action">zoom.out</attribute>
+    </item>
+    <item>
+      <attribute name="label">Zoom In</attribute>
+      <attribute name="action">zoom.in</attribute>
+    </item>
+    <item>
+      <attribute name="label">1∶1</attribute>
+      <attribute name="action">zoom.reset</attribute>
+    </item>
+  </menu>
+  <template class="Demo3Widget">
+    <child>
+      <object class="GtkPopoverMenu" id="menu">
+        <property name="has-arrow">0</property>
+        <property name="menu-model">model</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkGestureClick">
+        <property name="button">3</property>
+        <signal name="pressed" handler="pressed_cb"/>
+      </object>
+    </child>
+  </template>
+</interface>
diff --git a/demos/gtk-demo/menu.c b/demos/gtk-demo/menu.c
new file mode 100644
index 0000000000..cdcfd0ed20
--- /dev/null
+++ b/demos/gtk-demo/menu.c
@@ -0,0 +1,47 @@
+/* Menu
+ * #Keywords: action, zoom
+ *
+ * Demonstrates how to add a context menu to a custom widget
+ * and connect it with widget actions.
+ *
+ * The custom widget we create here is similar to a GtkPicture,
+ * but allows setting a zoom level for the displayed paintable.
+ *
+ * Our context menu has items to change the zoom level.
+ */
+
+#include <gtk/gtk.h>
+#include "demo3widget.h"
+
+
+GtkWidget *
+do_menu (GtkWidget *do_widget)
+{
+  static GtkWidget *window = NULL;
+
+  if (!window)
+    {
+      GtkWidget *sw;
+      GtkWidget *widget;
+
+      window = gtk_window_new ();
+      gtk_window_set_title (GTK_WINDOW (window), "Menu");
+      gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
+      gtk_window_set_display (GTK_WINDOW (window),
+                              gtk_widget_get_display (do_widget));
+      g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+
+      sw = gtk_scrolled_window_new ();
+      gtk_window_set_child (GTK_WINDOW (window), sw);
+
+      widget = demo3_widget_new ("/transparent/portland-rose.jpg");
+      gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), 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 a60f121896..a32fee88e5 100644
--- a/demos/gtk-demo/meson.build
+++ b/demos/gtk-demo/meson.build
@@ -44,6 +44,7 @@ demos = files([
   'links.c',
   'listbox.c',
   'listbox2.c',
+  'menu.c',
   'flowbox.c',
   'list_store.c',
   'listview_applauncher.c',
@@ -112,7 +113,8 @@ extra_demo_sources = files(['main.c',
                             'demo2layout.c',
                             'singular_value_decomposition.c',
                             'four_point_transform.c',
-                            'demo2widget.c'])
+                            'demo2widget.c',
+                            'demo3widget.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]