Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support (Gtk) tree-store:* #85

Open
daym opened this issue Sep 23, 2020 · 30 comments
Open

Support (Gtk) tree-store:* #85

daym opened this issue Sep 23, 2020 · 30 comments

Comments

@daym
Copy link

daym commented Sep 23, 2020

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>
@daym daym changed the title Support gtk-tree-store:new Support (Gtk) tree-store:new Sep 23, 2020
@spk121
Copy link
Owner

spk121 commented Sep 23, 2020

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

@daym
Copy link
Author

daym commented Sep 23, 2020

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
Copy link
Author

daym commented Sep 23, 2020

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 daym changed the title Support (Gtk) tree-store:new Support (Gtk) tree-store:* Sep 23, 2020
@daym
Copy link
Author

daym commented Sep 23, 2020

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
Copy link
Owner

spk121 commented Sep 24, 2020

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

@spk121
Copy link
Owner

spk121 commented Sep 24, 2020

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
Copy link
Owner

spk121 commented Sep 24, 2020

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
Copy link
Author

daym commented Sep 24, 2020

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
Copy link
Owner

spk121 commented Sep 25, 2020

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

@spk121
Copy link
Owner

spk121 commented Sep 25, 2020

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

@spk121
Copy link
Owner

spk121 commented Sep 25, 2020

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

@daym
Copy link
Author

daym commented Sep 25, 2020

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
Copy link
Collaborator

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

guile-gi/src/gig_type.c

Lines 823 to 832 in c8908fd

gig_type_associate(G_TYPE_STRING, scm_c_public_ref("oop goops", "<string>"));
SCM _scm_real = scm_c_public_ref("oop goops", "<real>");
SCM _scm_integer = scm_c_public_ref("oop goops", "<integer>");
SCM _scm_hashtable = scm_c_public_ref("oop goops", "<hashtable>");
gig_type_register(G_TYPE_INT, _scm_integer);
gig_type_register(G_TYPE_UINT, _scm_integer);
gig_type_register(G_TYPE_LONG, _scm_integer);
gig_type_register(G_TYPE_ULONG, _scm_integer);
gig_type_register(G_TYPE_INT64, _scm_integer);
gig_type_register(G_TYPE_UINT64, _scm_integer);

@daym
Copy link
Author

daym commented Oct 25, 2020

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
Copy link
Collaborator

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
Copy link
Author

daym commented Oct 25, 2020

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
Copy link
Collaborator

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

guile-gi/test/marshall.scm

Lines 751 to 767 in 89babf6

(test-equal "gvalue-return-class"
<GValue>
(class-of (gvalue-return)))
;; TODO: Inside `guix environment`, this test appears to fail.
(unless (test-passed?)
;; skip value/closure tests -- they are broken
(test-skip 16)
;; These tests weirdly pass:
;; - gvalue-flat-array
;; - gvalue-flat-array-round-trip
;; - gvalue-multi-array-key-value-in
;; - gvalue-return->in
;; These tests error:
;; return-gvalue-flat-array:
;; GuileGI-ERROR **: 13:55:16.003: unhandled argument type 'pointer to GArray of type GBoxed' src/gig_argument.c:1542
)

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
Copy link
Collaborator

LordYuuma commented Oct 25, 2020

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
Copy link
Author

daym commented Nov 3, 2020

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
Copy link
Collaborator

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
Copy link
Author

daym commented Nov 5, 2020

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
Copy link
Collaborator

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
Copy link
Author

daym commented Nov 5, 2020

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
Copy link
Collaborator

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
Copy link
Owner

spk121 commented Nov 7, 2020

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
Copy link

ZelphirKaltstahl commented Jan 21, 2022

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
Copy link
Owner

spk121 commented Jan 21, 2022

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
Copy link

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

@ZelphirKaltstahl
Copy link

@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
Copy link
Owner

spk121 commented Jan 21, 2022

@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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants