Setting custom Glib::Property values in Gtk::Builder .ui XML

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
Short of having to write an underlying class in C, is there any way to have custom derived widgets with custom Glib::Property, and set those custom properties through a Gtk::Builder .ui?

Presuming the answer is no, because the properties are only registered with GType when the instance is constructed - not when the class is constructed, which GtkBuilder won't do anyway if it's C++ - is there any way this could possibly work in future, or discussion I can read?

Basically, I'd like to move a tonne of code that does widget packing and property-setting out of .cpp files into .ui files, but it doesn't really seem worth doing if the added properties of my custom widgets would all need set/get through .cpp files still. I'd like to move everything that could be expressed statically/declaratively to .ui files, or nothing. I don't think moving only the base Widget bits to .ui but having to keep the rest in .cpp is better overall, but worse.

So far I can derive a subclass of e.g. Gtk::Button, add whatever Glib::Property, and see/change its value in the GTK Inspector. Even this isn't very well documented; it doesn't seem to be included in the main gtkmm doc book at all, for instance, so we might want to fix that.

Anyway, this registers properties per-instance when the C++ constructor is called, so GtkBuilder doesn't actually apply the values of custom properties, and the GTK Inspector gives warnings about invalid property IDs when you try to check/change customer properties in a .ui

I'm guessing this has been discussed and just can't be done, but I'd just like to check anyway.

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
On 6/9/19 11:04 AM, Daniel Boles via gtkmm-list wrote:
> Short of having to write an underlying class in C, is there any way to
> have custom derived widgets with custom Glib::Property, and set those
> custom properties through a Gtk::Builder .ui?
>
> Presuming the answer is no, because the properties are only registered
> with GType when the instance is constructed - not when the class is
> constructed, which GtkBuilder won't do anyway if it's C++ - is there
> any way this could possibly work in future, or discussion I can read?
>
I think this has not been possible previously, but what about
Glib::ExtraClassInit, available since glibmm 2.60.0? Do you think it
will help? It makes it possible to add code to the class init func in
your custom widget. If you register properties in the class init func,
like C classes do, you shall probably not use Glib::Property<>, but
rather code that resembles what _WRAP_PROPERTY() generates.

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
On Mon, 10 Jun 2019 at 08:44, Kjell Ahlstedt <[hidden email]> wrote:
On 6/9/19 11:04 AM, Daniel Boles via gtkmm-list wrote:
> Short of having to write an underlying class in C, is there any way to
> have custom derived widgets with custom Glib::Property, and set those
> custom properties through a Gtk::Builder .ui?
>
> Presuming the answer is no, because the properties are only registered
> with GType when the instance is constructed - not when the class is
> constructed, which GtkBuilder won't do anyway if it's C++ - is there
> any way this could possibly work in future, or discussion I can read?
>
I think this has not been possible previously, but what about
Glib::ExtraClassInit, available since glibmm 2.60.0? Do you think it
will help? It makes it possible to add code to the class init func in
your custom widget. If you register properties in the class init func,
like C classes do, you shall probably not use Glib::Property<>, but
rather code that resembles what _WRAP_PROPERTY() generates.


Thanks! I kind of forgot this had gotten arrived in a stable release (but was otherwise wondering if I could trick GObject into doing it anyway!)

I imagine this can probably work, so I'll give it a shot. Do you think it would be possible, or worth thinking about, to add ways to do this (and probably custom signals) in C++? It would be nice if we could add properties and signals to the class without having to go into C (or rather, by having glibmm do that for us). Properties especially, to make them usable with GtkBuilder .ui files as mentioned.

If such properties can be added, could they still be used with Glib::PropertyProxy, or does it require its target property to have been fully created by Glib::Property?


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
A related question here is: How can we ensure the derived type is registered with GObject before GtkBuilder would need it? Will a simple g_type_ensure( g_type_from_name("gtkmm__Custom_Whatever") ) work? I imagine not, as the GObject class won't exist until C++ constructs the first instance, right? Then it would seem we must instead create a dummy C++ instance (and ensure it isn't optimised out) before trying anything that would require GObject to know about it...

Excuse me if these are basic questions, but I've only just started thinking about non-trivial subclassing in glibmm/gtkmm. Previously I've either used composition or done trivial subclassing (no extra properties, etc.). So, while I'm relatively comfortable with how GObject registers classes and their features, I'm still figuring out how glibmm layers on top of that!


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
In reply to this post by Gtkmm mailing list
On 6/10/19 10:25 AM, Daniel Boles via gtkmm-list wrote:
 Do you think it would be possible, or worth thinking about, to add ways to do this (and probably custom signals) in C++? It would be nice if we could add properties and signals to the class without having to go into C (or rather, by having glibmm do that for us). Properties especially, to make them usable with GtkBuilder .ui files as mentioned.
It's probably possible, but I don't know how difficult it will be. I don't volunteer, at least not for the foreseeable future.

If such properties can be added, could they still be used with Glib::PropertyProxy, or does it require its target property to have been fully created by Glib::Property?

_WRAP_PROPERTY("visible", bool) in Gtk::Widget generates the code

Glib::PropertyProxy<bool> Widget::property_visible()
{  return Glib::PropertyProxy<bool>(this, "visible"); }

Glib::PropertyProxy_ReadOnly< bool > Widget::property_visible() const
{  return Glib::PropertyProxy_ReadOnly<bool>(this, "visible"); }

If custom properties are registered like glib/gtk+ register properties, I see no reason why similar code should not work for the custom properties.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
On Mon, 10 Jun 2019 at 15:52, Kjell Ahlstedt <[hidden email]> wrote:
On 6/10/19 10:25 AM, Daniel Boles via gtkmm-list wrote:
 Do you think it would be possible, or worth thinking about, to add ways to do this
It's probably possible, but I don't know how difficult it will be. I don't volunteer, at least not for the foreseeable future.

Of course! Nor do I, yet... but we'll see how this goes :-)
 
If such properties can be added, could they still be used with Glib::PropertyProxy, or does it require its target property to have been fully created by Glib::Property?
_WRAP_PROPERTY("visible", bool) in Gtk::Widget generates the code

Glib::PropertyProxy<bool> Widget::property_visible()
{  return Glib::PropertyProxy<bool>(this, "visible"); }

Glib::PropertyProxy_ReadOnly< bool > Widget::property_visible() const
{  return Glib::PropertyProxy_ReadOnly<bool>(this, "visible"); }

If custom properties are registered like glib/gtk+ register properties, I see no reason why similar code should not work for the custom properties.

Yeah, that was my hope. Since all the Proxy cares about is the instance (C++, from which it can get the C gob() ) and property name, hopefully it doesn't care and will work just as well here.


Thanks for the ideas so far. I'll see how this goes. If anyone else has any thoughts, I'd be interested of course.

 

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
In reply to this post by Gtkmm mailing list
On 6/10/19 10:39 AM, Daniel Boles via gtkmm-list wrote:
> A related question here is: How can we ensure the derived type is
> registered with GObject before GtkBuilder would need it? Will a simple
> g_type_ensure( g_type_from_name("gtkmm__Custom_Whatever") ) work? I
> imagine not, as the GObject class won't exist until C++ constructs the
> first instance, right? Then it would seem we must instead create a
> dummy C++ instance (and ensure it isn't optimised out) before trying
> anything that would require GObject to know about it...
>
I think you're right. The only way to have a custom widget's type
registered in the GType system is to create an instance. All the
ordinary gtkmm types are registered with calls to the static get_type()
methods. That's done in wrap_init(). But there is no corresponding
method to call for custom types.
_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
On 6/10/19 6:50 PM, Kjell Ahlstedt wrote:

> On 6/10/19 10:39 AM, Daniel Boles via gtkmm-list wrote:
>> A related question here is: How can we ensure the derived type is
>> registered with GObject before GtkBuilder would need it? Will a
>> simple g_type_ensure( g_type_from_name("gtkmm__Custom_Whatever") )
>> work? I imagine not, as the GObject class won't exist until C++
>> constructs the first instance, right? Then it would seem we must
>> instead create a dummy C++ instance (and ensure it isn't optimised
>> out) before trying anything that would require GObject to know about
>> it...
>>
> I think you're right. The only way to have a custom widget's type
> registered in the GType system is to create an instance. All the
> ordinary gtkmm types are registered with calls to the static
> get_type() methods. That's done in wrap_init(). But there is no
> corresponding method to call for custom types.
Now that I've thought a second time, I wonder if it's really necessary
to use Glib::ExtraClassInit, and install the custom properties in the
class init func. Glib::Property<> installs properties with a call to
g_object_class_install_property(), but it's not done in the class init
func. Does it matter if it's done later, as long as it's done before
GtkBuilder reads the .ui file? I mean, if you anyway have to create an
instance of the custom widget in order to get the class registered in
the GType system.
_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
I feel like I already tried that, since it was the easy route:
  • installing properties in C++ instance constructor,
  • constructing a dummy instance to register the type, and then
  • loading from a Builder .ui
...and got an error something like

> Invalid property ID 1 for gtkmm__CustomType_Button2 property name "color"

as my trivial example (I don't really want a Button:color property... don't panic or excommunicate me please)

However, I'll double-check later whether that does/doesn't work, and post a sample of what I've tried in any case.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
So far the only thing I've managed to get working is to use ExtraClassInit to do all the property installation and get/set handling in C... looking a bit like this, assuming a derived GtkButton called Button2, with a new property called :style-class.

I think the crux of the problem is 2fold:
 * We can't handle such properties in C++ because they need to work with C layers like GtkBuilder, which will never run a C++ constructor or signal or etc.
 * We need to handle them using the good old C [gs]et_property vfuncs, otherwise the base G_OBJECT_WARN_INVALID_PROPERTY_ID() gets hit.

with the result being that I think the best we can currently do is some hybrid of C/C++, where the properties must be implemented in C and then just wrapped later in C++ for easier consumption.

This is arguably still a bit nicer than doing the whole lot in C, but it remains to be seen whether it's sustainable enough to be worthwhile, i.e. whether the benefit of being able to shift programmatic ui generation into Builder definitions outweights having to add the layer of C boilerplate to every class.

Of course, I might still be missing something obvious - so I'd definitely welcome corrections or proof-of-concepts contrary to what I've said/shown so far!


```c
class ExtraClassInit_Button2: public Glib::ExtraClassInit {
public:
  ExtraClassInit_Button2()
  : Glib::ExtraClassInit(class_init, nullptr, instance_init)
  {}

private:
  enum { PROP_0, PROP_STYLE_CLASS, PROP_COUNT };

  static GParamSpec* s_paramSpecs[PROP_COUNT];

  static void
  class_init(void* const g_class, void* const class_data)
  {
    s_paramSpecs[PROP_STYLE_CLASS] = g_param_spec_string(
      "style-class", "Style Class", "a CSS style class",
      "", G_PARAM_READWRITE);

    auto gObjectClass = G_OBJECT_CLASS(g_class);
    gObjectClass->set_property = &set_property;
    gObjectClass->get_property = &get_property;
    g_object_class_install_properties(gObjectClass, PROP_COUNT, s_paramSpecs);
  }

  static void
  set_property(GObject* const object, unsigned const property_id,
               const GValue* const value, GParamSpec* const pspec)
  {
    switch (property_id) {
      case PROP_STYLE_CLASS: {
        auto const str = g_value_get_string(value);
        set_style_class(GTK_WIDGET(object), str);
        break;
      }

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
    }
  }

  static void
  set_style_class(GtkWidget* const widget, char const* const str)
  {
    if (!str) {
      return;
    }

    auto const styleContext = gtk_widget_get_style_context(widget);
    gtk_style_context_add_class (styleContext, str);
  }

  static void
  get_property(GObject* const object, unsigned const property_id,
               GValue* const value, GParamSpec* const pspec)
  {
    switch (property_id) {
      case PROP_STYLE_CLASS:
        g_value_set_string(value, "oops not implemented yet");
        break;

      default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
    }
  }
};
```

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
for posterity, it looks like someone else has had the same questions but didn't get much further:


>>>
First is that before I load the glade file I have to call the constructor by
creating an object of my problematic custom widget. I don't like it and I
can't find a way around. I marked that place in the source with ///!!! in
main_form.cpp

Second is that my properties are loaded in the glade file but they are not set to the default value and when I change them from glade I can't get the new values. I marked the place where I try connecting a signal again with ///!!! in area.cpp
>>>
Basically, it seems that we can't do what I hoped with glibmm/gtkmm, except by writing a pile of C to glue it properly onto the core GOBject functionality - at which point it's probably best either to go full C or to use a binding that has better integration for creating custom GObjects (and that's before we start thinking about GtkBuilder templates!)

I still want to keep writing C++, but I guess most of the programmatic widget-packing noise will have to remain... I'm not sure that hoisting out only the bits that don't require derived widgets or new properties would be worth doing, given that I'd end up with a mixture of paradigms; it's probably best to stick to one only, even if that's the worst one! (at least I can keep telling myself that'll avoid the overhead of parsing XML...)


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
> for posterity, it looks like someone else has had the same questions but didn't get much further:

That user was also using wrap_register() to have code called when glibmm wraps the C instances. I know next to nothing about that - but I wonder if it's at all possible that it could be used with builder->get_objects() in order to ensure everything gets wrapped, and custom code in the registered wrap_new() function to apply the derived properties? However, it seems that user didn't get that to work, if I understood correctly.


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
On 6/13/19 2:41 PM, Daniel Boles via gtkmm-list wrote:

> > for posterity, it looks like someone else has had the same questions
> but didn't get much further:
>
> That user was also using wrap_register() to have code called when
> glibmm wraps the C instances. I know next to nothing about that - but
> I wonder if it's at all possible that it could be used with
> builder->get_objects() in order to ensure everything gets wrapped, and
> custom code in the registered wrap_new() function to apply the derived
> properties? However, it seems that user didn't get that to work, if I
> understood correctly.
>
I've made a similar test. It failed.

I started with gtkmm-documentation/examples/book/builder/derived and
replaced the GtkButton with
   class MyButton : public Gtk::Button
with a few custom properties. I used Glib::Property for those
properties. In the .ui file:
   <object class="gtkmm__CustomObject_MyButton" id="quit_button">

MyButton has
   MyButton(GtkButton* castitem);
   static Glib::ObjectBase* wrap_new(GObject* object);
and I call
Glib::wrap_register(g_type_from_name("gtkmm__CustomObject_MyButton"),
&MyButton::wrap_new);
after having created a dummy MyButton instance to have the class init
function called.

The only problem is that the custom property values, specified in the
.ui file, are not stored anywhere. Glib::Property stores custom property
values in the C++ code, but GtkBuilder creates a C instance of MyButton
and tries to store the property values before Gtk::Builder::get_widget()
has created the C++ wrapper. Glib::custom_set_property_callback() in
glibmm/property.cc discards the values.

Conclusion: It's not possible to combine Glib::Property with Gtk::Builder.

_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list
Reply | Threaded
Open this post in threaded view
|

Re: Setting custom Glib::Property values in Gtk::Builder .ui XML

Gtkmm mailing list
Thanks for testing it anyway!

I wondered also if one could register properties in ExtraClassInit, but delay applying until them wrapping with wrap_new()*, i.e. have C++ actually reflect whatever values were set... but that seems like it might be too much work, even if possible.

( * if I understood its purpose right, which I might not have )


_______________________________________________
gtkmm-list mailing list
[hidden email]
https://mail.gnome.org/mailman/listinfo/gtkmm-list