[gtkmm] Add Gtk::BuilderScope and BuilderCScope



commit 33e9759790c264c3ab2a8445463caf45cd605dda
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date:   Mon Jul 6 15:10:21 2020 +0200

    Add Gtk::BuilderScope and BuilderCScope
    
    Override the get_type_from_name() vfunc in BuilderScope, just like
    the get_type_from_name() vfunc in Builder is overridden in gtkmm3.
    Set a GtkBuilderScope with an overridden vfunc in Builder's default
    constructor. Builder::get_widget_derived() can then (like in gtkmm3)
    instantiate gtkmm-derived types without gtkmm__ prefixes in the .ui file.
    
    Fixes #61

 .gitignore                |  4 +++
 gtk/gtkmm/meson.build     |  2 ++
 gtk/src/buildable.ccg     | 70 +++++++++++++++++++++++++++++++++++++++++++++++
 gtk/src/buildable.hg      | 27 ++++++++++++++++++
 gtk/src/builder.ccg       | 19 +++++++++++++
 gtk/src/builder.hg        | 29 ++++++++++----------
 gtk/src/buildercscope.ccg | 17 ++++++++++++
 gtk/src/buildercscope.hg  | 50 +++++++++++++++++++++++++++++++++
 gtk/src/builderscope.ccg  | 66 ++++++++++++++++++++++++++++++++++++++++++++
 gtk/src/builderscope.hg   | 65 +++++++++++++++++++++++++++++++++++++++++++
 gtk/src/filelist.am       |  2 ++
 tests/builder/main.cc     |  9 ++++++
 12 files changed, 345 insertions(+), 15 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 4872f109..a4dbb8d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -179,6 +179,10 @@ gtk/gtkmm/buildable.cc
 gtk/gtkmm/buildable.h
 gtk/gtkmm/builder.cc
 gtk/gtkmm/builder.h
+gtk/gtkmm/buildercscope.cc
+gtk/gtkmm/buildercscope.h
+gtk/gtkmm/builderscope.cc
+gtk/gtkmm/builderscope.h
 gtk/gtkmm/button.cc
 gtk/gtkmm/button.h
 gtk/gtkmm/calendar.cc
diff --git a/gtk/gtkmm/meson.build b/gtk/gtkmm/meson.build
index 887ccb3f..0fe6011b 100644
--- a/gtk/gtkmm/meson.build
+++ b/gtk/gtkmm/meson.build
@@ -47,6 +47,8 @@ gtkmm_any_hg_ccg_basenames = [
   'boxlayout',
   'buildable',
   'builder',
+  'buildercscope',
+  'builderscope',
   'button',
   'calendar',
   'cellarea',
diff --git a/gtk/src/buildable.ccg b/gtk/src/buildable.ccg
index f10e0efc..d1756cc0 100644
--- a/gtk/src/buildable.ccg
+++ b/gtk/src/buildable.ccg
@@ -16,4 +16,74 @@
  */
 
 #include <gtk/gtk.h>
+#include <gtkmm/builder.h>
+#include <cstring>
 
+namespace Gtk
+{
+
+//static
+gboolean Buildable_Class::custom_tag_start_vfunc_callback(
+  GtkBuildable* buildable,
+  GtkBuilder* builder,
+  GObject* child,
+  const gchar* tagname,
+  GtkBuildableParser* parser,
+  gpointer* data)
+{
+  // If it's a TreeModel (such as ListStore or TreeStore) and it's the start
+  // of a <columns> element, inform the Builder that the get_type_from_name()
+  // vfunc in BuilderScope shall not search for gtkmm-derived types.
+  // See https://bugzilla.gnome.org/show_bug.cgi?id=742637
+  if (GTK_IS_TREE_MODEL(buildable) && std::strcmp(tagname, "columns") == 0)
+  {
+    const auto cpp_builder = dynamic_cast<Builder*>(
+      Glib::ObjectBase::_get_current_wrapper((GObject*)builder));
+    if (cpp_builder)
+      cpp_builder->set_no_gtkmm_derived_types(true);
+  }
+
+  const auto base = static_cast<BaseClassType*>(
+    g_type_interface_peek_parent( // Get the parent interface of the interface (The original underlying C 
interface).
+      g_type_interface_peek(G_OBJECT_GET_CLASS(buildable), CppObjectType::get_type()) // Get the interface.
+    )
+  );
+
+  // Call the original underlying C function:
+  if (base && base->custom_tag_start)
+    return (*base->custom_tag_start)(buildable, builder, child, tagname, parser, data);
+  return false;
+}
+
+//static
+void Buildable_Class::custom_tag_end_vfunc_callback(
+  GtkBuildable* buildable,
+  GtkBuilder* builder,
+  GObject* child,
+  const gchar* tagname,
+  gpointer data)
+{
+  const auto base = static_cast<BaseClassType*>(
+    g_type_interface_peek_parent( // Get the parent interface of the interface (The original underlying C 
interface).
+      g_type_interface_peek(G_OBJECT_GET_CLASS(buildable), CppObjectType::get_type()) // Get the interface.
+    )
+  );
+
+  // Call the original underlying C function:
+  if (base && base->custom_tag_end)
+    (*base->custom_tag_end)(buildable, builder, child, tagname, data);
+
+  // If it's a TreeModel (such as ListStore or TreeStore) and it's the end
+  // of a </columns> element, inform the Builder that the get_type_from_name()
+  // vfunc in BuilderScope shall resume search for gtkmm-derived types.
+  // See https://bugzilla.gnome.org/show_bug.cgi?id=742637
+  if (GTK_IS_TREE_MODEL(buildable) && std::strcmp(tagname, "columns") == 0)
+  {
+    const auto cpp_builder = dynamic_cast<Builder*>(
+      Glib::ObjectBase::_get_current_wrapper((GObject*)builder));
+    if (cpp_builder)
+      cpp_builder->set_no_gtkmm_derived_types(false);
+  }
+}
+
+} // namespace Gtk
diff --git a/gtk/src/buildable.hg b/gtk/src/buildable.hg
index 52ab31df..ce3aceea 100644
--- a/gtk/src/buildable.hg
+++ b/gtk/src/buildable.hg
@@ -85,6 +85,33 @@ public:
 */
 
   //TODO: Properties, signals, vfuncs.
+
+#m4begin
+dnl //Custom-coded vfuncs:
+dnl
+  _PUSH(SECTION_CC_PRE_INCLUDES)
+    // Needed before gtkmm/private/buildable_p.h is included.
+    typedef struct _GtkBuilder GtkBuilder;
+    typedef struct _GtkBuildableParser GtkBuildableParser;
+  _SECTION(SECTION_PCC_CLASS_INIT_VFUNCS)
+    klass->custom_tag_start = &custom_tag_start_vfunc_callback;
+    klass->custom_tag_end = &custom_tag_end_vfunc_callback;
+  _SECTION(SECTION_PH_VFUNCS)
+    static gboolean custom_tag_start_vfunc_callback(
+      GtkBuildable* buildable,
+      GtkBuilder* builder,
+      GObject* child,
+      const gchar* tagname,
+      GtkBuildableParser* parser,
+      gpointer* data);
+    static void custom_tag_end_vfunc_callback(
+      GtkBuildable* buildable,
+      GtkBuilder* builder,
+      GObject* child,
+      const gchar* tagname,
+      gpointer data);
+  _POP()
+#m4end
 };
 
 } // namespace Gtk
diff --git a/gtk/src/builder.ccg b/gtk/src/builder.ccg
index 847bcecc..81b4ed34 100644
--- a/gtk/src/builder.ccg
+++ b/gtk/src/builder.ccg
@@ -17,11 +17,30 @@
 
 #include <glibmm/vectorutils.h>
 #include <gtkmm/application.h>
+#include <gtkmm/buildercscope.h>
 
 #include <gtk/gtk.h>
 
 namespace Gtk
 {
+// Called from Buildable
+void Builder::set_no_gtkmm_derived_types(bool status)
+{
+  no_gtkmm_derived_types = status;
+}
+
+// Called from BuilderScope
+bool Builder::get_no_gtkmm_derived_types() const
+{
+  return no_gtkmm_derived_types;
+}
+
+Builder::Builder()
+: // Set a scope that can find gtkmm-derived GTypes.
+  _CONSTRUCT("scope", BuilderCScope::create()->gobj())
+{  
+}
+
 // static
 Glib::RefPtr<Builder> Builder::create_from_file(const std::string& filename)
 {
diff --git a/gtk/src/builder.hg b/gtk/src/builder.hg
index 58ab8423..4ce3fff0 100644
--- a/gtk/src/builder.hg
+++ b/gtk/src/builder.hg
@@ -78,8 +78,8 @@ class GTKMM_API Builder : public Glib::Object
   _CLASS_GOBJECT(Builder, GtkBuilder, GTK_BUILDER, Glib::Object, GObject, , , GTKMM_API)
 
 protected:
- _CTOR_DEFAULT
- _IGNORE(gtk_builder_new)
+  Builder();
+  _IGNORE(gtk_builder_new)
 
 public:
   /** Creates a new builder object.
@@ -548,18 +548,6 @@ public:
    * auto pDialog2 = Gtk::Builder::get_widget_derived<MyDerivedDialog>(refBuilder, "mydialog2", "A storm is 
imminent!", true);
    * @endcode
    *
-   * If your derived class overrides vfuncs (virtual functions) or default signal
-   * handlers which are part of the wrapped C API, its class name in the XML UI file
-   * must have a @a gtkmm__ prefix, for instance
-   * @code
-   *   <object class="gtkmm__GtkDialog" id="mydialog3">
-   * @endcode
-   * gtkmm__GtkDialog is a subclass of GtkDialog in the GType system. In most
-   * respects a GtkSomeClass instance acts identically to a gtkmm__GtkSomeClass
-   * instance, but C++ vfuncs and default signal handlers are not called for
-   * GtkSomeClass instances. (Signal handlers, connected with signal_some_signal().connect(),
-   * are called for both gtkmm__GtkSomeClass and GtkSomeClass instances.)
-   *
    * @note
    * If get_widget_derived() is called more than once for the same widget (the
    * same @a name), only the first call will call the widget's constructor.
@@ -642,14 +630,25 @@ public:
   _WRAP_METHOD(void set_translation_domain(const Glib::ustring& domain), gtk_builder_set_translation_domain)
   _WRAP_METHOD(Glib::ustring get_translation_domain() const, gtk_builder_get_translation_domain)
 
-  //We ignore gtk_builder_get_type_from_name() because it only seems useful when implementing GtkBuildable 
for widgets.
+  // We ignore gtk_builder_get_type_from_name() because it only seems useful when implementing GtkBuildable 
for widgets.
   _IGNORE(gtk_builder_get_type_from_name)
+  // We ignore BuilderScope methods and property because it seems unlikely
+  // that a user of Builder wants to change the scope.
+  _IGNORE(gtk_builder_set_scope, gtk_builder_get_scope)
+  _IGNORE_PROPERTY(scope)
 
   _WRAP_PROPERTY("translation-domain", Glib::ustring)
 
 protected:
   Gtk::Widget* get_widget_checked(const Glib::ustring& name, GType type);
   GtkWidget* get_cwidget(const Glib::ustring& name);
+
+private:
+  bool no_gtkmm_derived_types {false};
+  void set_no_gtkmm_derived_types(bool status);
+  bool get_no_gtkmm_derived_types() const;
+  friend class GTKMM_API Buildable_Class;
+  friend class GTKMM_API BuilderScope_Class;
 };
 
 } // namespace Gtk
diff --git a/gtk/src/buildercscope.ccg b/gtk/src/buildercscope.ccg
new file mode 100644
index 00000000..57b87fdc
--- /dev/null
+++ b/gtk/src/buildercscope.ccg
@@ -0,0 +1,17 @@
+/* Copyright (C) 2020 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 <gtk/gtk.h>
diff --git a/gtk/src/buildercscope.hg b/gtk/src/buildercscope.hg
new file mode 100644
index 00000000..188c2c12
--- /dev/null
+++ b/gtk/src/buildercscope.hg
@@ -0,0 +1,50 @@
+/* Copyright (C) 2020 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 <glibmm/object.h>
+#include <gtkmm/builderscope.h>
+
+_DEFS(gtkmm,gtk)
+_PINCLUDE(glibmm/private/object_p.h)
+
+namespace Gtk
+{
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+/** Bindings for Gtk::Builder.
+ *
+ * gtkmm applications probably don't need this class.
+ *
+ * %Gtk::BuilderCScope provides support to Gtk::Builder, primarily
+ * for looking up programming-language-specific values for strings that are
+ * given in a Gtk::Builder UI file.
+ *
+ * @see Gtk::Builder
+ * @newin{3,98}
+ */
+class GTKMM_API BuilderCScope : public Glib::Object, public BuilderScope
+{
+  _CLASS_GOBJECT(BuilderCScope, GtkBuilderCScope, GTK_BUILDER_CSCOPE, Glib::Object, GObject, , , GTKMM_API)
+  _IMPLEMENTS_INTERFACE(BuilderScope)
+
+protected:
+  _CTOR_DEFAULT()
+
+public:
+  _WRAP_CREATE()
+};
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+} // namespace Gtk
diff --git a/gtk/src/builderscope.ccg b/gtk/src/builderscope.ccg
new file mode 100644
index 00000000..663d80ee
--- /dev/null
+++ b/gtk/src/builderscope.ccg
@@ -0,0 +1,66 @@
+/* Copyright (C) 2020 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 <gtk/gtk.h>
+#include <gtkmm/builder.h>
+
+namespace Gtk
+{
+
+// Allow GtkBuilder to instantiate a gtkmm derived GType instead of the regular
+// GTK GType, so we can, for instance, use our vfuncs and default signal handlers.
+// static
+GType BuilderScope_Class::get_type_from_name_vfunc_callback(
+  GtkBuilderScope* self, GtkBuilder* builder, const char* type_name)
+{
+  if (!type_name)
+    return G_TYPE_INVALID;
+
+  GType gtype = G_TYPE_INVALID;
+
+  // If a TreeModel (such as ListStore or TreeStore) is being built,
+  // Buildable_Class may have requested no search for gtkmm-derived types.
+  // See https://bugzilla.gnome.org/show_bug.cgi?id=742637
+  const auto obj = dynamic_cast<Builder*>(
+    Glib::ObjectBase::_get_current_wrapper((GObject*)builder));
+  if (!(obj && obj->no_gtkmm_derived_types))
+  {
+    // See if there is a gtkmm version of the gclass:
+    Glib::ustring classname_prefixed ("gtkmm__"); // gtkmm uses a prefix
+    classname_prefixed += type_name;
+
+    gtype = g_type_from_name(classname_prefixed.c_str());
+  }
+
+  if (gtype == G_TYPE_INVALID) // If it's not a registered typename
+  {
+    const auto base = static_cast<BaseClassType*>(
+      // Get the parent interface of the interface (The original underlying C interface).
+      g_type_interface_peek_parent(
+      // Get the interface.
+      g_type_interface_peek(G_OBJECT_GET_CLASS(self), CppObjectType::get_type())));
+
+    // Call the original underlying C function.
+    if (base && base->get_type_from_name)
+      gtype = (*base->get_type_from_name)(self, builder, type_name);
+    else // If that's not possible, just use the normal GType.
+      gtype = g_type_from_name(type_name);
+  }
+
+  return gtype;
+}
+
+} // namespace Gtk
diff --git a/gtk/src/builderscope.hg b/gtk/src/builderscope.hg
new file mode 100644
index 00000000..08abd3fe
--- /dev/null
+++ b/gtk/src/builderscope.hg
@@ -0,0 +1,65 @@
+/* Copyright (C) 2020 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/>.
+ */
+
+_CONFIGINCLUDE(gtkmmconfig.h)
+
+#include <glibmm/interface.h>
+
+_DEFS(gtkmm,gtk)
+_PINCLUDE(glibmm/private/interface_p.h)
+_PINCLUDE(gtk/gtk.h)
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+extern "C"
+{
+typedef struct _GtkBuilderScopeInterface GtkBuilderScopeInterface;
+}
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+namespace Gtk
+{
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+/** Bindings for Gtk::Builder.
+ *
+ * gtkmm applications probably don't need this interface.
+ *
+ * %Gtk::BuilderScope is an interface to provide support to Gtk::Builder, primarily
+ * for looking up programming-language-specific values for strings that are
+ * given in a Gtk::Builder UI file.
+ *
+ * @see Gtk::Builder
+ * @newin{3,98}
+ */
+class GTKMM_API BuilderScope : public Glib::Interface
+{
+  _CLASS_INTERFACE(BuilderScope, GtkBuilderScope, GTK_BUILDER_SCOPE, GtkBuilderScopeInterface, , , GTKMM_API)
+
+protected:
+
+#m4begin
+dnl Custom-coded vfunc:
+dnl
+  _PUSH(SECTION_PCC_CLASS_INIT_VFUNCS)
+  klass->get_type_from_name = &get_type_from_name_vfunc_callback;
+  _SECTION(SECTION_PH_VFUNCS)
+  static GType get_type_from_name_vfunc_callback(
+    GtkBuilderScope* self, GtkBuilder* builder, const char* type_name);
+  _POP()
+#m4end
+};
+#endif /* DOXYGEN_SHOULD_SKIP_THIS */
+
+} // namespace Gtk
diff --git a/gtk/src/filelist.am b/gtk/src/filelist.am
index 353613cd..5963a1df 100644
--- a/gtk/src/filelist.am
+++ b/gtk/src/filelist.am
@@ -32,6 +32,8 @@ gtkmm_files_any_hg =          \
        boxlayout.hg \
        buildable.hg            \
        builder.hg              \
+       buildercscope.hg \
+       builderscope.hg \
        button.hg               \
        calendar.hg             \
        cellarea.hg             \
diff --git a/tests/builder/main.cc b/tests/builder/main.cc
index 3d8d096b..68a13db8 100644
--- a/tests/builder/main.cc
+++ b/tests/builder/main.cc
@@ -169,6 +169,15 @@ public:
     m_pStandardButton->add_destroy_notify_callback(nullptr, on_managed_button_deleted);
   }
 
+  // A default signal handler in Gtk::Window shall be called if the
+  // class name in the UI file is GtkWindow, although the real GType name
+  // is gtkmm__GtkWindow. https://gitlab.gnome.org/GNOME/gtkmm/-/issues/61
+  bool on_close_request() override
+  {
+    std::cout << "MainWindow::on_close_request()" << std::endl;
+    return Gtk::Window::on_close_request(); // Call the base class
+  }
+
   virtual ~MainWindow()
   {
     std::cout << "MainWindow::dtor" << std::endl;


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