spk121 / guile-gi

Bindings for GObject Introspection and libgirepository for Guile
GNU General Public License v3.0
58 stars 7 forks source link

Support (Gtk) tree-store:* #85

Open daym opened 4 years ago

daym commented 4 years ago

It would be nice if GtkTreeStore worked.

Current situation:

(use-modules (system foreign))
(load-by-name "Gtk" "TreeStore")
(tree-store:new 1 long)
(process:32652): GuileGI-ERROR **: 10:35:18.655: unhandled argument type 'pointer to GArray of type GBoxed' src/gig_argument.c:555

Gtk+ version is gtk+ 3.24.20.

gir is:

    <class name="TreeStore"
           c:symbol-prefix="tree_store"
           c:type="GtkTreeStore"
           parent="GObject.Object"
           glib:type-name="GtkTreeStore"
           glib:get-type="gtk_tree_store_get_type"
           glib:type-struct="TreeStoreClass">
      <source-position filename="gtktreestore.h" line="61"/>
      <implements name="Buildable"/>
      <implements name="TreeDragDest"/>
      <implements name="TreeDragSource"/>
      <implements name="TreeModel"/>
      <implements name="TreeSortable"/>
      <constructor name="new"
                   c:identifier="gtk_tree_store_new"
                   shadowed-by="newv"
                   introspectable="0">
...
      </constructor>
      <constructor name="newv"
                   c:identifier="gtk_tree_store_newv"
                   shadows="new">
        <source-position filename="gtktreestore.h" line="70"/>
        <return-value transfer-ownership="full">
          <doc xml:space="preserve"
               filename="gtktreestore.c"
               line="355">a new #GtkTreeStore</doc>
          <type name="TreeStore" c:type="GtkTreeStore*"/>
        </return-value>
        <parameters>
          <parameter name="n_columns" transfer-ownership="none">
            <doc xml:space="preserve"
                 filename="gtktreestore.c"
                 line="350">number of columns in the tree store</doc>
            <type name="gint" c:type="gint"/>
          </parameter>
          <parameter name="types" transfer-ownership="none">
            <doc xml:space="preserve"
                 filename="gtktreestore.c"
                 line="351">an array of #GType types for the columns, from first to last</doc>
            <array length="0" zero-terminated="0" c:type="GType*">
              <type name="GType" c:type="GType"/>
            </array>
          </parameter>
        </parameters>
      </constructor>
      <method name="append" c:identifier="gtk_tree_store_append">
        <source-position filename="gtktreestore.h" line="135"/>
        <return-value transfer-ownership="none">
          <type name="none" c:type="void"/>
        </return-value>
        <parameters>
          <instance-parameter name="tree_store" transfer-ownership="none">
            <doc xml:space="preserve"
                 filename="gtktreestore.c"
                 line="1718">A #GtkTreeStore</doc>
            <type name="TreeStore" c:type="GtkTreeStore*"/>
          </instance-parameter>
          <parameter name="iter"
                     direction="out"
                     caller-allocates="1"
                     transfer-ownership="none">
            <doc xml:space="preserve"
                 filename="gtktreestore.c"
                 line="1719">An unset #GtkTreeIter to set to the appended row</doc>
            <type name="TreeIter" c:type="GtkTreeIter*"/>
          </parameter>
          <parameter name="parent"
                     transfer-ownership="none"
                     nullable="1"
                     allow-none="1">
            <doc xml:space="preserve"
                 filename="gtktreestore.c"
                 line="1720">A valid #GtkTreeIter, or %NULL</doc>
            <type name="TreeIter" c:type="GtkTreeIter*"/>
          </parameter>
        </parameters>
      </method>
[...]
</class>
spk121 commented 4 years ago

Thanks for the report. I'll see what I can do.

daym commented 4 years ago

Added pull request.

In order to test:

(tree-store:new (vector G_TYPE_LONG))

btw: I'm pretty sure that GI said that this function requires two arguments--but it doesn't work if I put two arguments.

Not saying that I want to explicitly specify the number of elements, but it's just something notable.

daym commented 4 years ago

After that, tree-store:append! needs an (possibly) uninitialized allocated GtkTreeIter. How do I get one? I found tree-iter:copy and tree-iter:free (the latter has no exclamation mark O_o), but both don't help me here...

What does exist: (make <GtkTreeIter>), and it does something.

But when I do (tree-view:set-model tree-view tree-store), I get:

(tree1.scm:16667): GuileGI-ERROR **: 19:28:45.219: unhandled argument type 'pointer to GtkTreeModel of type GInterface or NULL' src/gig_argument.c:250

That's because scm_to_c_interface is not implemented yet.

It should do something like arg->v_pointer = g_type_check_instance_cast((GTypeInstance*)gig_type_peek_object(object), meta->gtype).

I added it to my pull request.

daym commented 4 years ago

Even though the following functions should have the following prototypes, actually using them as one would in C does not work (oop/goops.scm:1585:2: No applicable method for #<<generic> tree-store:set (1)> in call (tree-store:set #<<GtkTreeStore> 7ffff2d705f0> #<<GtkTreeIter> 7ffff2d704b0> #(0 1) #(42 24) 2) ).

set-value GtkTreeIter, int32, GValue
        3 parameters! Second one is passed as GBoxed by the library, and is_raw_array is set.
set GtkTreeIter, ARRAY of int32, ARRAY of GValue, INT32
        4 parameters!
        iter
        columns
        values
        n_values

I think that might be because the implicit conversion into GValue is not done? If it was done, it would be done using gig_value_from_scm.

The workaround (define value (make <GValue>)) does not really work. Using (tree-store:set tree-store iter (vector 0 1) (vector value value)) I get: bytevector expected, because scm_to_c_native_immediate_array always expects bytevector, even for int32 vectors.

Using tree-store:set-value, I try (define v (make <GValue>)) (set! (value v) 42) and get In procedure %set: Wrong type argument in position 2: 42.

spk121 commented 4 years ago

For setting values of integer type, perhaps (set! (value G_TYPE_INT) 42) might work better.

spk121 commented 4 years ago

For making typed bytevectors, core Guile provides the family of functions u32vector as in (u32vector 100 100). And also list->u32vector and friends. For vectors from C types that may vary between 32-bit and 64-bit platforms (gi util) provides list->int-vector and friends.

spk121 commented 4 years ago

For tree-store:set, it is a documentation error that there is a 4th argument n_values. It should be just iter, columns, values. The n_values in the C API is computed from the length of the 3rd argument.

daym commented 4 years ago

Ohhh! Thanks!

The following indeed does work:

(define iter (make <GtkTreeIter>))
(tree-store:append! tree-store iter #f)
(define value (make <GValue>))
(set! (value G_TYPE_INT) 42)
(tree-store:set tree-store iter (u32vector 0 1) (vector value value))

The following doesn't work and doesn't give an error:

(define iter (make <GtkTreeIter>))
(define value (make <GValue> #:type G_TYPE_INT #:value 42))
(tree-store:set tree-store iter (u32vector 0 1) (vector value value))

And I can't for the life of me successfully call tree-store:set-value, though. Not that important, but ehhh.

Also, G_TYPE_STRING is missing.

spk121 commented 4 years ago

Added G_TYPE_STRING in 3929243ed52c217a23a473d012300e85f414aceb , but I would imagine that @LordYuuma would say that <string> is more appropriate.

spk121 commented 4 years ago

tree-store:set-value does work; however, in Scheme the API is a bit torturous. See ad02cd82f9ef0677f1365752ea2c97b6a67b5c40

spk121 commented 4 years ago

tree-store:set does also appear to work; however, the Scheme API is also torturous. See c8908fd92e1eaab5959772bfdfd98db299ae0c23 guile-gi definitely needs more flexible handling of GArray . Requiring bytevectors is inflexible.

daym commented 4 years ago

Added G_TYPE_STRING in 3929243 , but I would imagine that @LordYuuma would say that <string> is more appropriate.

Thanks! \<string> does indeed work and is nicer! Is there something else in Guile for G_TYPE_INT, too? I tried (system foreign)'s int, but I guess those are different integers, so that won't work I guess.

LordYuuma commented 4 years ago

There sadly is no scheme equivalent to G_TYPE_INT, you'll have to use the GLib constants. There is no direct equivalence between numeric C types and the Scheme number tower, see https://github.com/spk121/guile-gi/blob/c8908fd92e1eaab5959772bfdfd98db299ae0c23/src/gig_type.c#L823-L832

daym commented 3 years ago

Also doesn't work as it should:

(define (install-tree-view-tooltip-handler! tree-view)
  (set! (has-tooltip tree-view) #t)
  (connect tree-view query-tooltip
   (lambda (tree-view x y keyboard-tip tooltip)
     (let* ((iter (make <GtkTreeIter>))
            (value (make <GValue>)))
       (let-values (((on-row? x y model path iter)
                     (tree-view:get-tooltip-context! tree-view x y keyboard-tip iter)))
         (write (tree-model:get-value! model iter 1 value)) ; error message here
         (tooltip:set-text tooltip "XXX")
         ; (tree-view-set-tooltip-row tree-view tooltip path)
         on-row?)))))

The model used is a GtkTreeModelSort.

Error message (when hovering over a row):

ice-9/boot-9.scm:1669:16: In procedure raise-exception:
No applicable method for #<<generic> tree-model:get-value! (1)> in call (tree-model:get-value! #<<GtkTreeModel> 7fc66bb830c0> #<<GtkTreeIter> 7fc66bb83040> 1 #<<GValue> 7fc66bb87100>)

I suspect that's because the returned GtkTreeModel's actual class is not checked? Or what happens here?

Also, that might be a guile thing, but wouldn't it be possible to, on error messages like "No applicable method", to list all those methods of that generic that are present? I mean it knows those, right? That would help a lot.

LordYuuma commented 3 years ago

You can [roughly] inspect GOOPS methods by doing the following:

(use-modules (ice-9 pretty-print)
             (oop goops)
             (srfi srfi-1))

(let* ((methods (generic-function-methods YOUR-FUNCTION))
       (formals (map method-formals methods))
       (specializers (map method-specializers methods)))
  (pretty-print (map (lambda (a b) (if (pair? a) (zip a b) (list a b))) formals specializers)))

You might encounter a few issues with improper lists, e.g. (a b . rest), but Guile-GI should not produce methods with such specializers anyway.

As for the specific issue with tree-model:get-value!, please try the following and report the result:

(apply peek 'expected (method-specializers (car (generic-function-methods tree-model:get-value!)))
(apply peek 'actual (map class-of (list model iter 1 value)))
daym commented 3 years ago

Thanks!

;;; (expected #<<class> <GtkTreeModel> 7f6874446980> #<<class> <GtkTreeIter> 7f6874446080> #<<class> <integer> 7f687631f200> #<<class> <GValue> 7f687445ee00>)
#<<class> <GValue> 7f687445ee00>

;;; (actual #<<class> <GtkTreeModel> 7f6874446980> #<<class> <GtkTreeIter> 7f6874446080> #<<class> <integer> 7f687631f200> #<<applicable-struct-with-setter-class> <GValue> 7f6874396f80>)
#<<applicable-struct-with-setter-class> <GValue> 7f6874396f80>

So maybe that's because I'm mixing high-level and low-level constructs?

I've been trying to use low-level constructs everywhere--but didn't find the one to make a GtkTreeIter, and neither the one to make a GValue so far.

LordYuuma commented 3 years ago

Nah, you're not doing anything wrong here. The model iter 1 part of your call matches the expectation. The difference lies in the GValue parameter. Guile-GI sets up G_TYPE_VALUE to be an applicable struct with setter, but somehow there exists a separate GValue class, that is created when the introspection is loaded. I have no idea at all what happens here.

For the record, this is a type of behaviour, that for some weird reason I've only been able to observe in Guix environments. I'm hesitant to call it Guix-specific, but something inside Guix seems to exacerbate it. See https://github.com/spk121/guile-gi/blob/89babf64759be7b700477f9b7fb3a84c5c4731d7/test/marshall.scm#L751-L767

This would probably deserve its own issue somewhere, but I have yet to find out, who is at fault here (us, GI, GLib itself, Guix...)

LordYuuma commented 3 years ago

For what it's worth, the tree-store:set tests pass inside guix environment on dev-defer-types, but I have not yet fixed all bugs with that.

daym commented 3 years ago

tree-model:iter-previous! and tree-model:iter-next! seem to be missing, too. Although tree-model:iter-n-children and tree-model:iter-children! are present.

How come?

LordYuuma commented 3 years ago

They're iter-previous? and iter-next?, see #87. You should really just peek into the output from load and load-by-name from time to time. ;)

daym commented 3 years ago

tree-model:get! is missing.

(I'd like to have a method to get the entire tree row at once; get-value! for each cell does work but is slow)

LordYuuma commented 3 years ago

There is sadly little to be done on our front. get! is not introspectable (being varargs as you'd expect), and they don't seem to have get-values!, but that'd be little more than a loop around get-value! anyway.

daym commented 3 years ago

It's fine--but I thought I should ask.

GtkTreeView is badly supported in gobject-introspection, which is worrying because you basically need it any time you show a list. I've fixed so many bugs in upstream gtk+ regarding introspection hints by now. (I wonder whether gtk-rs trusts those hints for lifetime tracking, though O_O)

Still, the usual enterprise distros won't have those fixes for years, because they hardly ever update gtk...

Compared to the other stuff, missing get! is not a bad thing at all :)

Oh well. Thanks for having a look, though!

LordYuuma commented 3 years ago

My personal experience with Gtk/Vala tells me that introspection hints (and vapis) are serious business, so I'd assume some of those bugs make it into Rust. Not that I have too high expectations for Rust anyway.

spk121 commented 3 years ago

From what I gather from the developer blogs, list views and models and associated classes are quite different in Gtk4. Perhaps they will be better supported

ZelphirKaltstahl commented 2 years ago

Sorry to necro up this issue, but I need to ask the state of afairs with this. I want to create an application, which critically relies on GtkTreeView and I think for that I will have to work with a tree-store and all the other things like columns, column renderers, model and whatnot. In my example I need to only display some strings (utf-8 though) and perhaps numbers, but I think not.

Is there a way to make TreeView work and dynamically add and remove things shown in it using guile-gi? To me this issue reads like there are issues with it and that it does not work yet (?).

spk121 commented 2 years ago

Well, I can take another look, but, the core problem is that guile-gi only provides functionality that the introspection library provides, and it doesn't provide any exceptional work-arounds except for Cairo. The introspection that Gtk-3.0 provides for GtkTreeView looks clunky and very verbose in guile-gi. It sort of works, though, depending on what you need.

I made a quick example. See this attachment --> gtk-tree-store.scm.txt

ZelphirKaltstahl commented 2 years ago

Thank you a lot for making an example. I will try and understand and try to use for my use-case.

ZelphirKaltstahl commented 2 years ago

@spk121 Where would be the best place to ask questions about some of the things in that example code? Here in this topic? Or rather a new issue? Or e-mail?

spk121 commented 2 years ago

@ZelphirKaltstahl Email is perfectly fine by me, but if you want to do it on a web site, i enabled the discussions tab in GitHub. I've never used it tho, so I don't know if it is any good