[gtkmm] Add Gtk::WidgetCustomDraw and WidgetCustomSnapshot



commit 37c4076a9b71f42dfcb071cc8a90dbf7feea5b0e
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Wed Feb 15 10:58:03 2017 +0100

    Add Gtk::WidgetCustomDraw and WidgetCustomSnapshot
    
    The WidgetCustomDraw and WidgetCustomSnapshot classes make it possible
    to use signal_draw() and snapshot_vfunc() in gtkmm without modification
    of gtk+. Bug 775348. See also gtk+ bug 774778.

 gtk/gtkmm/filelist.am             |    6 +-
 gtk/gtkmm/widgetcustomdraw.cc     |  153 +++++++++++++++++++++++++++++++++++++
 gtk/gtkmm/widgetcustomdraw.h      |  118 ++++++++++++++++++++++++++++
 gtk/gtkmm/widgetcustomsnapshot.cc |   92 ++++++++++++++++++++++
 gtk/gtkmm/widgetcustomsnapshot.h  |   89 +++++++++++++++++++++
 5 files changed, 457 insertions(+), 1 deletions(-)
---
diff --git a/gtk/gtkmm/filelist.am b/gtk/gtkmm/filelist.am
index 04a80ae..3d3834d 100644
--- a/gtk/gtkmm/filelist.am
+++ b/gtk/gtkmm/filelist.am
@@ -17,7 +17,9 @@ gtkmm_files_extra_any_cc =            \
        styleproperty.cc \
        targetentry.cc                  \
        treemodelcolumn.cc              \
-       treeview_private.cc
+       treeview_private.cc \
+       widgetcustomdraw.cc \
+       widgetcustomsnapshot.cc
 
 gtkmm_files_extra_deprecated_cc =
 
@@ -37,6 +39,8 @@ gtkmm_files_extra_any_h =                     \
        targetentry.h                   \
        treemodelcolumn.h               \
        treeview_private.h              \
+       widgetcustomdraw.h \
+       widgetcustomsnapshot.h \
        wrap_init.h
 
 gtkmm_files_extra_deprecated_h =
diff --git a/gtk/gtkmm/widgetcustomdraw.cc b/gtk/gtkmm/widgetcustomdraw.cc
new file mode 100644
index 0000000..3d71e7b
--- /dev/null
+++ b/gtk/gtkmm/widgetcustomdraw.cc
@@ -0,0 +1,153 @@
+/* Copyright (C) 2017 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/widgetcustomdraw.h>
+#include <glibmm/exceptionhandler.h>
+#include <gtk/gtk.h>
+
+namespace
+{
+using CppObjectType = Gtk::WidgetCustomDraw;
+using BaseObjectType = GtkWidget;
+using BaseClassType = GtkWidgetClass;
+
+static gboolean WidgetCustomDraw_signal_draw_callback(GtkWidget* self, cairo_t* cr,void* data)
+{
+  using SlotType = sigc::slot<bool(const ::Cairo::RefPtr< ::Cairo::Context>&)>;
+
+  auto obj = dynamic_cast<CppObjectType*>(Glib::ObjectBase::_get_current_wrapper((GObject*)self));
+  // Do not try to call a signal on a disassociated wrapper.
+  if (obj)
+  {
+    try
+    {
+      if (const auto slot = Glib::SignalProxyNormal::data_to_slot(data))
+        return static_cast<int>((*static_cast<SlotType*>(slot))(
+          ::Cairo::RefPtr< ::Cairo::Context>(new ::Cairo::Context(cr, false /* has_reference */))));
+    }
+    catch (...)
+    {
+       Glib::exception_handlers_invoke();
+    }
+  }
+  return false;
+}
+
+static gboolean WidgetCustomDraw_signal_draw_notify_callback(GtkWidget* self, cairo_t* cr, void* data)
+{
+  using SlotType = sigc::slot<void(const ::Cairo::RefPtr< ::Cairo::Context>&)>;
+
+  auto obj = dynamic_cast<CppObjectType*>(Glib::ObjectBase::_get_current_wrapper((GObject*)self));
+  // Do not try to call a signal on a disassociated wrapper.
+  if (obj)
+  {
+    try
+    {
+      if (const auto slot = Glib::SignalProxyNormal::data_to_slot(data))
+        (*static_cast<SlotType*>(slot))(
+          ::Cairo::RefPtr< ::Cairo::Context>(new ::Cairo::Context(cr, false /* has_reference */)));
+    }
+    catch(...)
+    {
+      Glib::exception_handlers_invoke();
+    }
+  }
+  return false;
+}
+
+static const Glib::SignalProxyInfo WidgetCustomDraw_signal_draw_info =
+{
+  "draw",
+  (GCallback) &WidgetCustomDraw_signal_draw_callback,
+  (GCallback) &WidgetCustomDraw_signal_draw_notify_callback
+};
+
+} // anonymous namespace
+
+namespace Gtk
+{
+WidgetCustomDraw::WidgetCustomDraw()
+:
+Glib::ExtraClassInit(class_init_function)
+{
+}
+
+Glib::SignalProxy<bool(const ::Cairo::RefPtr< ::Cairo::Context>&)> WidgetCustomDraw::signal_draw()
+{
+  return Glib::SignalProxy<bool(const ::Cairo::RefPtr< ::Cairo::Context>&) >(this, 
&WidgetCustomDraw_signal_draw_info);
+}
+
+bool WidgetCustomDraw::on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr)
+{
+  const auto base = static_cast<BaseClassType*>(
+    g_type_class_peek_parent(G_OBJECT_GET_CLASS(gobject_))); // Get the parent class of the object class 
(The original underlying C class).
+
+  if (base && base->draw)
+    return (*base->draw)((BaseObjectType*)gobject_, cr->cobj());
+
+  return false;
+}
+
+// static
+gboolean WidgetCustomDraw::draw_callback(GtkWidget* self, cairo_t* cr)
+{
+  const auto obj_base = static_cast<Glib::ObjectBase*>(
+      Glib::ObjectBase::_get_current_wrapper((GObject*)self));
+
+  // Non-gtkmmproc-generated custom classes implicitly call the default
+  // Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
+  // generated classes can use this optimisation, which avoids the unnecessary
+  // parameter conversions if there is no possibility of the virtual function
+  // being overridden:
+  if (obj_base && obj_base->is_derived_())
+  {
+    const auto obj = dynamic_cast<CppObjectType* const>(obj_base);
+    if (obj) // This can be nullptr during destruction.
+    {
+      try // Trap C++ exceptions which would normally be lost because this is a C callback.
+      {
+        // Call the virtual member method, which derived classes might override.
+        return static_cast<int>(obj->on_draw(
+          ::Cairo::RefPtr< ::Cairo::Context>(new ::Cairo::Context(cr, false /* has_reference */))));
+      }
+      catch (...)
+      {
+        Glib::exception_handlers_invoke();
+      }
+    }
+  }
+
+  const auto base = static_cast<BaseClassType*>(
+    g_type_class_peek_parent(G_OBJECT_GET_CLASS(self))); // Get the parent class of the object class (The 
original underlying C class).
+
+  // Call the original underlying C function:
+  if (base && base->draw)
+    return (*base->draw)(self, cr);
+
+  return false;
+}
+
+// Addition to Gtk::Widget's class init function
+// static
+void WidgetCustomDraw::class_init_function(void* g_class, void* /* class_data */)
+{
+  g_return_if_fail(GTK_IS_WIDGET_CLASS(g_class));
+
+  const auto klass = static_cast<BaseClassType*>(g_class);
+  klass->draw = &draw_callback;
+}
+
+} // namespace Gtk
diff --git a/gtk/gtkmm/widgetcustomdraw.h b/gtk/gtkmm/widgetcustomdraw.h
new file mode 100644
index 0000000..d559b3c
--- /dev/null
+++ b/gtk/gtkmm/widgetcustomdraw.h
@@ -0,0 +1,118 @@
+/* Copyright (C) 2017 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GTKMM_WIDGETCUSTOMDRAW_H
+#define _GTKMM_WIDGETCUSTOMDRAW_H
+
+#include <glibmm/extraclassinit.h>
+#include <glibmm/signalproxy.h>
+#include <cairomm/context.h>
+
+using GtkWidget = struct _GtkWidget;
+
+namespace Gtk
+{
+
+/** %Widget with signal_draw().
+ *
+ * Because of the way gtk+4 renders widgets, Gtk::Widget can't wrap the draw signal.
+ * If you make a custom widget that uses the draw signal, you must derive your
+ * custom widget from both %WidgetCustomDraw and the relevant widget class.
+ * E.g. if you make a custom widget that you want to derive from Gtk::Widget,
+ * and you want to connect to signal_draw() or override the on_draw() default
+ * signal handler:
+ * @code
+ * #include <gtkmm/widgetcustomdraw.h>
+ *
+ * class MyWidget : public Gtk::WidgetCustomDraw, public Gtk::Widget
+ * {
+ * public:
+ *   MyWidget()
+ *   :
+ *   // The GType name will be gtkmm__CustomObject_MyMidget
+ *   Glib::ObjectBase("MyWidget"), // Unique class name
+ *   Gtk::WidgetCustomDraw(),
+ *   Gtk::Widget()
+ *   {
+ *     // ...
+ *   }
+ *   // ...
+ * protected:
+ *   bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
+ *   // ...
+ * };
+ * @endcode
+ *
+ * %WidgetCustomDraw must be listed @e before the widget class (Gtk::Widget in
+ * the example) in the list of base classes.
+ *
+ * Do not use %WidgetCustomDraw for drawing in a Gtk::DrawingArea.
+ * %Gtk::DrawingArea uses a draw function instead of %signal_draw().
+ * See Gtk::DrawingArea::set_draw_func().
+ *
+ * Don't derive from both %WidgetCustomDraw and WidgetCustomSnapshot in the
+ * same class. It will compile, but probably at most one of signal_draw() and
+ * snapshot_vfunc() will work.
+ *
+ * @newin{3,90}
+ * @ingroup Widgets
+ */
+class WidgetCustomDraw : public Glib::ExtraClassInit
+{
+public:
+  WidgetCustomDraw();
+
+  /**
+   * @par Slot Prototype:
+   * <tt>bool on_my_%draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr)</tt>
+   *
+   * This signal is emitted when a widget is supposed to render itself.
+   * The widget's top left corner must be painted at the origin of
+   * the passed in context and be sized to the values returned by
+   * Gtk::Widget::get_allocated_width() and
+   * Gtk::Widget::get_allocated_height().
+   *
+   * Signal handlers connected to this signal can modify the cairo
+   * context passed as @a cr in any way they like and don't need to
+   * restore it. The signal emission takes care of calling cairo_save()
+   * before and cairo_restore() after invoking the handler.
+   *
+   * The signal handler will get a @a cr with a clip region already set to the
+   * widget's dirty region, i.e. to the area that needs repainting.  Complicated
+   * widgets that want to avoid redrawing themselves completely can get the full
+   * extents of the clip region with Cairo::Context::get_clip_extents(), or they can
+   * get a finer-grained representation of the dirty region with
+   * Cairo::Context::copy_clip_rectangle_list().
+   *
+   * @param cr The cairo context to draw to.
+   * @return <tt>true</tt> to stop other handlers from being invoked for the event.
+   *         <tt>false</tt> to propagate the event further.
+   */
+  Glib::SignalProxy<bool(const ::Cairo::RefPtr< ::Cairo::Context>&)> signal_draw();
+
+protected:
+  /// This is a default handler for signal_draw().
+  virtual bool on_draw(const ::Cairo::RefPtr< ::Cairo::Context>& cr);
+
+private:
+  static gboolean draw_callback(GtkWidget* self, cairo_t* cr);
+  static void class_init_function(void* g_class, void* class_data);
+
+}; // end WidgetCustomDraw
+
+} // namespace Gtk
+
+#endif // _GTKMM_WIDGETCUSTOMDRAW_H
diff --git a/gtk/gtkmm/widgetcustomsnapshot.cc b/gtk/gtkmm/widgetcustomsnapshot.cc
new file mode 100644
index 0000000..1813c56
--- /dev/null
+++ b/gtk/gtkmm/widgetcustomsnapshot.cc
@@ -0,0 +1,92 @@
+/* Copyright (C) 2017 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/widgetcustomsnapshot.h>
+#include <glibmm/exceptionhandler.h>
+#include <gtk/gtk.h>
+
+using CppObjectType = Gtk::WidgetCustomSnapshot;
+using BaseObjectType = GtkWidget;
+using BaseClassType = GtkWidgetClass;
+
+namespace Gtk
+{
+WidgetCustomSnapshot::WidgetCustomSnapshot()
+:
+Glib::ExtraClassInit(class_init_function)
+{
+}
+
+void WidgetCustomSnapshot::snapshot_vfunc(Snapshot& snapshot)
+{
+  const auto base = static_cast<BaseClassType*>(
+      g_type_class_peek_parent(G_OBJECT_GET_CLASS(gobject_)) // Get the parent class of the object class 
(The original underlying C class).
+  );
+
+  if (base && base->snapshot)
+    (*base->snapshot)((BaseObjectType*)gobject_, snapshot.gobj());
+}
+
+// static
+void WidgetCustomSnapshot::snapshot_vfunc_callback(GtkWidget* self, GtkSnapshot* snapshot)
+{
+  const auto obj_base = static_cast<Glib::ObjectBase*>(
+      Glib::ObjectBase::_get_current_wrapper((GObject*)self));
+
+  // Non-gtkmmproc-generated custom classes implicitly call the default
+  // Glib::ObjectBase constructor, which sets is_derived_. But gtkmmproc-
+  // generated classes can use this optimisation, which avoids the unnecessary
+  // parameter conversions if there is no possibility of the virtual function
+  // being overridden:
+  if (obj_base && obj_base->is_derived_())
+  {
+    const auto obj = dynamic_cast<CppObjectType* const>(obj_base);
+    if (obj) // This can be nullptr during destruction.
+    {
+      try // Trap C++ exceptions which would normally be lost because this is a C callback.
+      {
+        // Call the virtual member method, which derived classes must override.
+        obj->snapshot_vfunc(*Glib::wrap(snapshot));
+        return;
+      }
+      catch (...)
+      {
+        Glib::exception_handlers_invoke();
+      }
+    }
+  }
+
+  BaseClassType *const base = static_cast<BaseClassType*>(
+      g_type_class_peek_parent(G_OBJECT_GET_CLASS(self)) // Get the parent class of the object class (The 
original underlying C class).
+  );
+
+  // Call the original underlying C function:
+  if (base && base->snapshot)
+    (*base->snapshot)(self, snapshot);
+}
+
+// Addition to Gtk::Widget's class init function
+// static
+void WidgetCustomSnapshot::class_init_function(void* g_class, void* /* class_data */)
+{
+  g_return_if_fail(GTK_IS_WIDGET_CLASS(g_class));
+
+  const auto klass = static_cast<BaseClassType*>(g_class);
+  klass->snapshot = &snapshot_vfunc_callback;
+}
+
+
+} // namespace Gtk
diff --git a/gtk/gtkmm/widgetcustomsnapshot.h b/gtk/gtkmm/widgetcustomsnapshot.h
new file mode 100644
index 0000000..d115c5e
--- /dev/null
+++ b/gtk/gtkmm/widgetcustomsnapshot.h
@@ -0,0 +1,89 @@
+/* Copyright (C) 2017 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GTKMM_WIDGETCUSTOMSNAPSHOT_H
+#define _GTKMM_WIDGETCUSTOMSNAPSHOT_H
+
+#include <glibmm/extraclassinit.h>
+#include <gtkmm/snapshot.h>
+
+using GtkWidget = struct _GtkWidget;
+using GtkSnapshot = struct _GtkSnapshot;
+
+namespace Gtk
+{
+
+/** %Widget with snapshot_vfunc().
+ *
+ * Because of the way gtk+4 renders widgets, Gtk::Widget can't wrap the snapshot virtual function.
+ * If you make a custom widget that uses the snapshot vfunc, you must derive your
+ * custom widget from both %WidgetCustomSnapshot and the relevant widget class.
+ * E.g. if you make a custom widget that you want to derive from Gtk::Widget,
+ * and you want snapshot_vfunc() to be called:
+ * @code
+ * #include <gtkmm/widgetcustomsnapshot.h>
+ *
+ * class MyWidget : public Gtk::WidgetCustomSnapshot, public Gtk::Widget
+ * {
+ * public:
+ *   MyWidget()
+ *   :
+ *   // The GType name will be gtkmm__CustomObject_MyMidget
+ *   Glib::ObjectBase("MyWidget"), // Unique class name
+ *   Gtk::WidgetCustomSnapshot(),
+ *   Gtk::Widget()
+ *   {
+ *     // ...
+ *   }
+ *   // ...
+ * protected:
+ *   void snapshot_vfunc(Gtk::Snapshot& snapshot) override;
+ *   // ...
+ * };
+ * @endcode
+ *
+ * %WidgetCustomSnapshot must be listed @e before the widget class (Gtk::Widget in
+ * the example) in the list of base classes.
+ *
+ * Do not use %WidgetCustomSnapshot for drawing in a Gtk::DrawingArea.
+ * %Gtk::DrawingArea uses a draw function instead of %snapshot_vfunc().
+ * See Gtk::DrawingArea::set_draw_func().
+ *
+ * Don't derive from both WidgetCustomDraw and %WidgetCustomSnapshot in the
+ * same class. It will compile, but probably at most one of signal_draw() and
+ * snapshot_vfunc() will work.
+ *
+ * @newin{3,90}
+ * @ingroup Widgets
+ */
+class WidgetCustomSnapshot : public Glib::ExtraClassInit
+{
+public:
+  WidgetCustomSnapshot();
+
+protected:
+  /// Called when a widget is supposed to create a snapshot of itself.
+  virtual void snapshot_vfunc(Snapshot& snapshot);
+
+private:
+  static void snapshot_vfunc_callback(GtkWidget* self, GtkSnapshot* snapshot);
+  static void class_init_function(void* g_class, void* class_data);
+
+}; // end WidgetCustomSnapshot
+
+} // namespace Gtk
+
+#endif // _GTKMM_WIDGETCUSTOMSNAPSHOT_H


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]