StefanSalewski / gintro

High level GObject-Introspection based GTK3/GTK4 bindings for Nim language
MIT License
298 stars 20 forks source link

Provide a GTK 4 example of ListView with custom content #134

Open gavr123456789 opened 3 years ago

gavr123456789 commented 3 years ago

As I understand it from this list guide, there are 2 ways to create a ListView with your own custom content type in GTK 4. The first way is to inherit from the ListModel and implement your own version. So for example done here. The second way is to use a ready-made ListStore, either by filling it with your own GObject objects, or by filling several columns in it with different built-in GType types.

The List API looks like one of the most complex parts of GTK, and it would be cool to make it as easy as possible thanks to the powerful macro system in Nim. For example, create a simple way to create GObject objects with properties of the desired types, I mean without setting GType through strings like setProperty (renderer, "foreground-set", toBoolVal (true)) where toBoolVal is another helper function.

Maybe something that looks like:

type
   MyGObject = ref gobject # or traditionally ref object of GObject
       somebool: bool # or maybe gbool, create this by setProperty, or one call of new_with_properties func
gavr123456789 commented 3 years ago

In general, a macro for simply creating a GObject or GValue, or both.

StefanSalewski commented 3 years ago

For example, create a simple way to create GObject objects with properties of the desired types, I mean without setting GType through strings like setProperty

Yes sure, Python bindings do that. I guess that we do not even need macros for that. But we know only for sure what helper functions we need and how the helper functions should look and behave in detail, when we at the same time use that functions in our own GTK programs. So we have to create some real tools , maybe like Gimp or Inkscape. For me I have to learn more GTK4, continue the GTK4 book then, and continue my drawing app.

gavr123456789 commented 3 years ago

You mentioned Fidget many times, in the context that Araq likes Fidget more and that it is getting more development in the Nim community. I just tried it, and it seems to me that it is ready for 10%, for example, you can check work with text. I don't want to insult the author, just to achieve 20 years of GTK development developed by several people on a salary takes a lot of effort.

StefanSalewski commented 3 years ago

I have not tried fidget at all, and not the 20 other Nim GUI toolkits at all. For GTK it is clear that we can create tools like Inkscape with it. Gintro may have restrictions, but at least in theory we can hire someone from the Rust team and have GTK bindings for Nim as good as for Rust.

For all the other Nim GUI toolkits I really want to see an app compatible in function and look with Inkscape first, and not only toy apps. A toy app can look simple, clean and elegant. For most people that seems to be enough.

The fidget author treeform is smart and works really very hard, and authors of other tools are smart too and work also hard. And as coding in Nim is much faster and easier as coding in C or C++, maybe in a few years we may have really useful pure Nim GUI toolkits. But what is "a few years"? When we get a big baker like google or mozilla maybe only 2 to 3 years, without that maybe more 8 to 12 years?

gavr123456789 commented 3 years ago

hire someone from the Rust team and have GTK bindings for Nim as good as for Rust

To hire someone, there must be a need for some company to do this. I also doubt that among the creators of Rust bindings there are people who know Nim at a sufficient level. I don't think that's going to happen heh.

Also I have a strange idea temporarily use json instead of gobject by storing it in a StringList. Most likely, this will be slower, but much more convenient than the constant get/set property and casting types as text for Value like gtype " gstringarray"
https://github.com/gavr123456789/NimTests/blob/main/jsonAsList/Types.nim
https://github.com/gavr123456789/NimTests/blob/main/jsonAsList/nim.nim#L57

aeldemery commented 3 years ago

Thanks for bringing up this topic.

I was just about to try to code one of the list demos, for example the list_clock, in Nim.

I choose this demo because it's simple enough to start with. Unfortunately I wasn't able to figure out how to subclass objects in Nim, so I really appreciate if you could provide us some tutorial/example on how to do slightly more advanced programs, more than single file, flat functions style. It would be a great companion to your excellent book.

Thank you very much.

gavr123456789 commented 3 years ago

@aeldemery wow, hi, nice to see you also interested in Nim + GTK.
Hope you will help me to provide more GTK 4 examples here https://github.com/StefanSalewski/gintro/pull/122

gavr123456789 commented 3 years ago

@aeldemery

subclass objects in Nim

Just added subclass example int this PR.
The subtype is described in the project's README

aeldemery commented 3 years ago

@gavr123456789 Thanks for the friendly reception :) I wouldn't be too excited as I'm trying to find my way around in Nim. So don't expect something useful, yet ;)

aeldemery commented 3 years ago

@gavr123456789 how to implement certain interfaces like GDK.Paintable or Glib.ListModel. please note that those are interfaces not classes.

gavr123456789 commented 3 years ago

@aeldemery Nim has no concept of interfaces. But it has concepts, ... but they not ready yet( I created issue about support them when they come out. https://github.com/StefanSalewski/gintro/issues/130
As you can see there, it just a kind o sum type now. (Nim also doesn't have sum types, it's just a generic constraint syntax)

I'm not sure, but I can see that the ListModel is present in the gio.nim (generated by gintro in .nimble/pkgs/gintro-#head/gintro) ListModel as a ref object of gobject.Object. Perhaps inheritance from it will be equivalent to the implementation of the interface, it is better to clarify this by @StefanSalewski

StefanSalewski commented 3 years ago

how to implement certain interfaces like GDK.Paintable or Glib.ListModel. please note that those are interfaces not classes.

GTK supports the concept of interfaces, Nim not really, Nim's concepts may be a substitute. For gintro we use Nim's or types to handle the GTK interaces. (Well, interfaces can be also constructed with a struct containing function pointers, Nim's streams module does that)

I have to admit that I do not really understand your question: Interfaces are generally useful when we write a library, but not that much when we write an application program. Gintro is fine for writing applications, but currently not really if you are a GTK core dev and want to extend GTK, maybe working on GTK5 or GTK6 already. If that is the case I would recommend using Rust, as Rust may have the best GTK bindings currently, and Rust is not using proxy objects like gintro. So rust may be the best choice to replace Vala.

For your concrete question, please show us some apps in C or another language that uses interfaces in a way that you desire for Nim.

StefanSalewski commented 3 years ago

Unfortunately I wasn't able to figure out how to subclass objects in Nim,

Currently Nim is using proxy objects, so we do subclassing GTK entities in exactly the same way as we do it for ordinary Nim objects. It is described in

http://ssalewski.de/gintroreadme.html#_extending_or_sub_classing_widgets

Note that you do not need to create your own destructor with Nim versions >= 1.4.

Subclassing this way is mostly adding additional information to widgets, which is generally all we need. But of course it is very different from the way gobject does it. Initially we did it with the oldgtk3 Nim bindings in the gobject way indeed, the old NEd Nim editor is still an example for that. But it was ugly. Rust has better support for that, but it is difficult to provide the gobject way of object handling for high level languages.

Note that the whole OOP programming style with subclassing and inheritance is not that popular any more. Modern languages like GoLang generally use composition instead of inheritance, Rust and Nim go in the same direction, and when you read some of the GTK blog posts you may notice that GTK4 uses more composition and less inheritance now.

You asked for larger example programs: Well at https://github.com/StefanSalewski there is already NEd2, SDT, nim-chess4. All in very early stages, but it should show you the direction. And in the examples/gtk3 directory there is the drawingarea example, which shows how widgets (grid, drawing area, scrollbars) work together.

StefanSalewski commented 3 years ago

I was just about to try to code one of the list demos, for example the list_clock, in Nim.

Oh, there is an official C implementation. So if it really helps I can provide you a Nim version, I may try it this evening.

aeldemery commented 3 years ago

@StefanSalewski thanks for the detailed answer.

As a concrete example, as shown in this demo, an object conforms to the Gdk.Paintable interface, that is, implement snapshot (Gtk.Snapshot, width, height) and other size negotiating functions like get_interinsic_width and so on.

I want to build an object which implements the paintable API so it can be drown by Gtk.

How would you implement that in Nim?

StefanSalewski commented 3 years ago

How would you implement that in Nim?

OK, I will send you the Nim version of the original C code...

StefanSalewski commented 3 years ago

I had just a closer look at the listview_clocks C code example. Actually this is an example for creating a real widget which in principle could be part of the GTK4 lib and could then be used from other applications and other programming language. So large parts of the actual code are not for displaying the clocks, but for all the library interaction including setting and getting properties. I think you have not implemented all that in your Vala code?

I think when one really wants to create new widgets which can then become part of the gtk library, then it is best to do it in plain C, or maybe in Vala or Rust. In principle it is possible in Nim, my old NEd editor was close to that, it used code that simulated the GTK C macros like G_DEFINE_TYPE_WITH_CODE() and such, but then there is no real advantage compared to plain C, the code gets verbose and ugly.

I will see how far I will get by translating the C code to Nim.

aeldemery commented 3 years ago

@StefanSalewski Thanks for your reply.

Actually previously, as I started learning Vala/Gtk, I was trying to translate the C code as much as possible. But as my knowledge about the subject expanded a little bit, I was able to implement the same functionality using vala's Object-Oriented techniques. For example, one of the complexer demos is the listview_color which I translated to vala several times, settling down in my last version here.

For the current simple example of Clock listview, I've done the Clock custom widget which have:

aeldemery commented 3 years ago

Just for info, I've updated the clock list demo to make it a little more interesting, and separated display widget from data source.

StefanSalewski commented 3 years ago

Nice. And nice that you have not already retired -- it is not much fun when people ask for support and vanish some weeks later and get never seen again. (But well I saw your other Vala examples at github, so I had some confidence.)

We are working still on the initial listview_clocks example, see

https://discourse.gnome.org/t/listview-clocks-c-from-gtk4-demo-standalone-and-in-other-programming-languages/6629

The main problem for Nim is that the C example creates indeed a new GObject/Widget with new properties using low level gobject functions and macros. My current idea is to just split the app in a small low level C part and the Nim part. I tested that with a tiny program and it seems to be possible in principle, as explained in

https://nim-lang.org/docs/backends.html#nim-code-calling-the-backend-c-invocation-example

One problem is that we would have to use passL with the actual full glib and gobject library path.

In theory we could provide all the low levels parts in Nim directly, that includes the nested C macros like G_DECLARE_FINAL_TYPE() or low level calls like g_clear_handle_id (&ticking_clock_id, g_source_remove); which are not supported by gobject-introspection.

As you have some experience with GTK, maybe you have an idea what the statement

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

tells us? How much can use of GtkBuilder simplify the low level parts?

I found this

https://stackoverflow.com/questions/54278014/how-to-subclass-gtk-iconview-in-vala-with-glade

as an example for subclassing with new properties. But it is still GTK3.

aeldemery commented 3 years ago

@StefanSalewski Thank you for your response.

  1. I've read your response several times to try to understand your full-of-meanings words. But to tell the truth I wasn't sure what you mean exactly by your first part. As if you imply that you had asked me for assistance with something but I had let you down. I tried hard to remember any occurrences like that but I couldn't. But If that should happen in any way, I sincerely apologize for that. It wasn't by any means intentional. Sorry.
  2. Yes I was away for some time, this could be explained partly because we, humans, have tides of interests in life, sometimes we got excited about something then in a turn of a day we loose that! The other reason is because my professional life is very demanding (I work as a Doctor in cardiac surgery field). My interest in programming is merely a hobby which I got a chance to fulfill only sporadically at weekends. If you could believe it, several weeks pass by without even touching my laptop, let alone do any code related stuff. That influences and explains of course my very limited knowledge about the inner-workings of programming or coding styles.
  3. I've seen your question on Discourse just now, as I don't visit the forum very often. I get an email every two weeks about the most recent topics only.

Coming to the subject at hand:

  1. How Vala achieve the seamless binding with GObject libraries, I must say I don't know exactly! I have compiled a simple vala program like this:
    
    public class Test.TestClass {
    }

int main (string[] args) { return 0; }

The result on the C side was about 400 lines, stripping away some of the code, it's similar to the following:
```c
#define TEST_TYPE_TESTCLASS (test_testclass_get_type ())
#define TEST_TESTCLASS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_TESTCLASS, TestTestClass))
#define TEST_TESTCLASS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TEST_TYPE_TESTCLASS, TestTestClassClass))
#define TEST_IS_TESTCLASS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_TESTCLASS))
#define TEST_IS_TESTCLASS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TEST_TYPE_TESTCLASS))
#define TEST_TESTCLASS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TEST_TYPE_TESTCLASS, TestTestClassClass))

typedef struct _TestTestClass TestTestClass;
typedef struct _TestTestClassClass TestTestClassClass;
typedef struct _TestTestClassPrivate TestTestClassPrivate;
typedef struct _TestParamSpecTestClass TestParamSpecTestClass;

struct _TestTestClass {
    GTypeInstance parent_instance;
    volatile int ref_count;
    TestTestClassPrivate * priv;
};

struct _TestTestClassClass {
    GTypeClass parent_class;
    void (*finalize) (TestTestClass *self);
};

struct _TestParamSpecTestClass {
    GParamSpec parent_instance;
};

static gpointer test_testclass_parent_class = NULL;

VALA_EXTERN gpointer test_testclass_ref (gpointer instance);
VALA_EXTERN void test_testclass_unref (gpointer instance);
VALA_EXTERN GParamSpec* test_param_spec_testclass (const gchar* name,
                                       const gchar* nick,
                                       const gchar* blurb,
                                       GType object_type,
                                       GParamFlags flags);
VALA_EXTERN void test_value_set_testclass (GValue* value, gpointer v_object);
VALA_EXTERN void test_value_take_testclass (GValue* value, gpointer v_object);
VALA_EXTERN gpointer test_value_get_testclass (const GValue* value);
VALA_EXTERN GType test_testclass_get_type (void) G_GNUC_CONST ;
G_DEFINE_AUTOPTR_CLEANUP_FUNC (TestTestClass, test_testclass_unref)
VALA_EXTERN TestTestClass* test_testclass_new (void);
VALA_EXTERN TestTestClass* test_testclass_construct (GType object_type);
static void test_testclass_finalize (TestTestClass * obj);
static GType test_testclass_get_type_once (void);
static gint _vala_main (gchar** args, gint args_length1);

If you add properties to the class, for instance, int counter { get; set; } the following lines are generated in addition to the the previous ones:

VALA_EXTERN gint test_testclass_get_counter (TestTestClass* self);
VALA_EXTERN void test_testclass_set_counter (TestTestClass* self,  gint value);

and inside the private struct

struct _TestTestClassPrivate {
    gint _counter;
};

VALA_EXTERN is merely a linking attribute

#if !defined(VALA_EXTERN)
#if defined(_MSC_VER)
#define VALA_EXTERN __declspec(dllexport) extern
#elif __GNUC__ >= 4
#define VALA_EXTERN __attribute__((visibility("default"))) extern
#else 
#define VALA_EXTERN extern
#endif
#endif
  1. If you some how implement some nim macros to generate those boilerplate code behind the scene It would achieve the goal of a better binding for the gnome platform.
  2. As a side note, the problem with nim it forces you to some programming paradigm, which the language creator thinks it works best universally. Unfortunately this is untrue in all situations. There are thousands of software which were written in OOP manner and very successful, among those GUI application and toolkits.
  3. The excerpt from the List Clock documentation about using Builder files, is discriping how to use GtkExpression inside ui files to communicate objects and data sources with displaying widgets. I've done that in code here. If you want to build ListViews in ui files you could check this python example. The Documention about using GtkExpression inside ui files could be found in Gtk Blog Post.
  4. For the last part about using custom made widgets inside ui files, as I learned in Vala, you have to call GLib.Type.ensure() before using that widget in Gtk.Builder probably early in the program execution before getting to the window which uses that widget.

Again my apologies for not being at level of your expectations. Thanks

StefanSalewski commented 3 years ago

As if you imply that you had asked me for assistance with something but I had let you down.

Sorry. No, the point is that in the past people asked for support of additional packages like libhandy, libadwaita, gstreamer, libnice, webkitgtk and some more. All packages I was not familiar with. I tried to provide that packages with at least one example. And then that people generally vanished very fast. Or in early days people said: I would like to try gintro/gtk but I need more documentation. So I extended the readme and started the GTK4 book. Same for Nim itself: Years ago people asked for beginner docs. So last year I started writing the kids book, which has 200 pages now. But my feeling is that only very few people have read it. So my conclusion is to react not on all user wishes immediately.

programming is merely a hobby

That is surprising for me, as you have made really great Vala examples. My personal feeling is that GTK is just too difficult for hobby programmers. I started with GTK in 2007 with the book of A. Krause for GTK2, using it from Ruby. And still I do know only a tiny part of GTK. I think we have currently only very few people who really know GTK, which is of course Mr. Bassi, and maybe Mr.Larsen and M.Droege and a few more. Compared to learning programming languages like C, Ruby or Nim learning GTK is a very large, never ending effort. Using GTK from C is difficult, and using it from other languages is difficult as well, as there are no examples or tutorials and as bindings may have not best quality.

I will continue with the listview_clocks example in the next days, it provides a lot stuff to learn for me :-)

aeldemery commented 3 years ago

I think gintro would be more useful if it's a generic gobject binding generator. It could be used to produce bindings for other libraries, like for example, the Apache Arrow library.

StefanSalewski commented 3 years ago

I think gintro would be more useful if it's a generic gobject binding generator.

I would say it is exactly that. For C libs that support gobject-introspection it is basically adding one line to the gen.nim generator script to support one more lib. Well basically, libnice and gstreamer was some more work, as both are very low level. libadwaita, libhandy, webkitgtk2 and vte was not much more than adding one line. Well and providing at least one example.

I never heard about Apache Arrow library before. And as I said above I will not add libs that nobody heard of that soon any more.

StefanSalewski commented 3 years ago

I was just about to try to code one of the list demos, for example the list_clock, in Nim.

See

https://github.com/StefanSalewski/gintro/issues/141

for a possible solution.