Listview_clocks.c from gtk4-demo standalone and in other programming languages

Recently we got a request asking how the listview_clocks.c demo would look in Nim languages.

Obviously none C implementations following closely the C code are not that easy, as the example uses the low level stuff like G_DEFINE_TYPE_WITH_CODE() macro to create a real new widget class, which in theory can become part of the GTK library and can be used from other programs them.

Generally when we write apps in high level languages we have not really the need to create real new widget classes, we just use and compose existing widgets.

Well, Vala, Rust, JavaScript and Python at least seems to support the creation of new classes with its own properties.

First question related to gtk4-demo: What is the suggested method to split all the demos like listview_clocks.c into single, stand alone apps which we can use and test then separately? listview_clocks.c has no main() function, and it is not that obvious what else is missing to generate a single executable.

The other question is, if it really makes sense to support all the low level stuff to create true new widgets for high level bindings? For Nim it is generally possible, we can try to expand macros like G_DEFINE_TYPE_WITH_CODE() in single library function calls and then use as well. But I have the feeling that it makes not much sense. In the gintro Nim bindings we use proxy objects, we we can use, extend and subclass easily. Or we can use composition instead of inheritance, which is what Golang generally does. From a short look at the listview_clocks.c example the main reason to create a new gobject class is to be able to define new properties, which are then monitored for changes to drive the clocks. But of course there are other, simpler solutions available in high level languages. The gintro Nim bindings come with some simple, timer driven animation examples already, like a animated sine curve drawing or buttons that are counting down automatically.

I donā€™t know much about Nim but I can try to answer some of the issues.

Usually at the bottom there is a non-static function that creates the window, in that case there is do_listview_clocks. To make that run by itself, you could run that function in your GApplication::activate handler.

Likely one wouldnā€™t call that macro, you would have the bindings set up the class somehow and then call g_type_register_static, g_type_add_interface_static, etc.

The main benefit to me of using GObject properties is the notify signal which enables use of easy data binding as in g_object_bind_property and gtk_expression_bind. If Nim doesnā€™t have a similar system with signals and property notifications, then it wonā€™t be equivalent. The other thing done with inheritance in that example is to implement the GdkPaintable interface, so the clock can be attached directly to a GtkPicture. This probably isnā€™t necessary but it does make some things easier. Iā€™m not sure how you could handle that in a way that is idiomatic to Nim.

This is somewhat unrelated, but generally it is not a good idea to use timeouts and counters to drive animations. You would want to use gdk_frame_clock_get_frame_time in the draw handler to get the time interval, and gtk_widget_add_tick_callback to queue the draw. For example the animation here may not perform as expected in some circumstances: gintro/examples/gtk3/cairo_anim.nim at master Ā· StefanSalewski/gintro Ā· GitHub

1 Like

Yes, I got that working. Reusing the C code from https://gtk.org/ I just appended these two functions to the listview_clocks.c code:

static void on_activate (GtkApplication *app) {
  GtkWidget *window = gtk_application_window_new (app);
  GtkWidget *button = do_listview_clocks(window);
  gtk_window_present (GTK_WINDOW (button));
}

int main (int argc, char *argv[]) {
  GtkApplication *app = gtk_application_new ("com.example.GtkApplication",
                                             G_APPLICATION_FLAGS_NONE);
  g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);
  return g_application_run (G_APPLICATION (app), argc, argv);
}

// gcc listview_clocks.c -o listview_clocks `pkg-config --cflags --libs gtk4`

Compiles with warnings, and clicking the X does not terminate the program, but at least it runs, so I can see how far I can translate it to Nim.

warning: ā€˜g_time_zone_newā€™ is deprecated: Use 'g_time_zone_new_identifier' instead

To fix that, you could either use gtk_application_add_window, or gtk_window_set_application.

1 Like

Well, that example is really hard for bindings. So two more questions:

In the example gtk-constant-expression-new()

GtkExpression: GTK 4 Reference Manual

is used, which is a varargs function and so is not provided by gobject-introspection. But it is used with only two arguments, and I guess two arguments is the general use case. Often as a replacement for functions with varargs arguments Gtk provides simplified substitutes which are provided by gobject-introspection for language bindings. Is there something available, or do we have to add this varags function manually?

And the other question:

grep Builder gtk/demos/gtk-demo/listview_clocks.c 
 * Typically, this will be done using GtkBuilder .ui files with the

So I wonder how use of GtkBuilder would simplify this example for non C languages. I think there is no GtkBuilder version of this example at all, but are there similar examples which shows how gtkBuilder can be used with subclassing GObjects and adding new properties? I found only one old example:

oop - How to subclass GTK IconView in Vala with Glade - Stack Overflow

If you think that is low-level Iā€™d advise you not to look at classic GObject code/expand the macroā€¦ :slight_smile:

Itā€™s gtk3 but I have a simple window defined in GtkBuilder

But exactly that is the problem for all the non C bindings languages. For C the nested macros are fine and the best solution, but for other compiled languages we have to ā€œemulateā€ these C macros somehow, when we try to offer the same low level stuff that is available for C and Vala programmers in GTK.

I tried these emulation five years ago for the first low level Nim GTK3 bindings, now called oldgtk3. I tried to substitute the C macros with plain library calls and Nimā€™s own macros and templates, but it was hard and maybe not really correct, only one program, a plain text editor called NEd, was using that. Later I learned about the gcc -E option which may help to discover what the macros really do. Now, as a first step I will try to call some C code from Nim just using the C macros. When that works we cater improve it somehow.

Thanks you for the link to your ā€œsimple windowā€ example, I will study that later.

For the gtk-constant-expression-new() call I still wonder why it uses varargs. I used a grep in the gtk source directory for findings its uses, and the few occurrences use it all with two arguments. I think I have to manually create a two arguments version for the Nim bindings.

1 Like

You should be able to use gtk_constant_expression_new_for_value. The varargs constructor is just a wrapper around that: gtk/gtkexpression.c Ā· fca19e72ad079c2728b95b4a26b86312c9c887c7 Ā· GNOME / gtk Ā· GitLab

I donā€™t think there is a single example that shows it all, but the listview_settings, listbox, and constraint-editor demos show how to do various things with builder xmls and property binding.

Vala does not really use the macros that much ā€“ you may want to compile some of the simpler Vala examples and look at the intermediate C output to get an idea of what itā€™s doing. All of that should be accessible to other language bindings in a similar way.

1 Like

Thanks for your detailed instructions.

Seems that we have one more GTK expert now in this forum, additional to Mr ā€œGTKā€ Bassi. :slight_smile:

I have currently no idea where the signature of the callback function used for gtk_cclosure_expression_new() is defined. The signature is very special, as a pointer to char is returned, which then somehow seems to be freed with g_free()?

static char *
convert_time_to_string (GObject   *image,
                        GDateTime *time,
                        gpointer   unused)
{
  return g_date_time_format (time, "%x\n%X");
}
  expression = gtk_cclosure_expression_new (G_TYPE_STRING,
                                            NULL,
                                            1, (GtkExpression *[1]) { expression },
                                            G_CALLBACK (convert_time_to_string),
                                            NULL, NULL);

The signature is dependent on the number of parameters you pass in to the closure. The first argument is the this pointer passed to gtk_expression_evaluate and the rest are corresponding to n_params and params of the closure expression. That callback only has 1 argument for the GDateTime that it gets from the property expression. The callback is able to return a stored string because the return type is G_TYPE_STRING.

From language bindings you would likely not use that function unless you already have your own libffi pieces in place that you want to use, otherwise if you have types for GClosure already then you would use gtk_closure_expression_new.

1 Like

Iā€™ve asked about Expressions before and the same answer I got from @ebassi . Language bindings should use ClosureExpression rather than CClosureExpression.

I can think of one exception: If the language does not have closure types and only re-uses the C ABI with C-compatible function pointers, then it may make sense to use the various cclosure functions. But, most new languages I see do have real closure types which should be wrapped in a GClosure.

I think you are the one who asked for the listview_clocks example some weeks ago for the first time. Well I was able to split the C code example in a Nim and a low level C part, see

Gintro and low level GTK C code like the listview_clocks.c example Ā· Issue #141 Ā· StefanSalewski/gintro Ā· GitHub

This is not too nice, but at least it works. You will need the latest gintro @#head to test it.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.