iup-go
iup-go copied to clipboard
What type does the iup.Tree USERDATAid attribute expect?
In the C docs, it seems to expect a pointer to a custom struct, but I cannot figure out how to store userdata in the Go port. I've tried int and string, and it returns garbage after I've set the attribute. I've also tried a pointer to a Go struct, as well as an instance of iup.User without success. (The hope was that I could use int value corresponding to the iup.Ihandle as a unique key.)
How do you use this attribute in the Go port?
If it's not possible, is there any other way to get a unique value (of any kind) for each visible node in the tree and obtain it from the current ID of a node as it is e.g. delivered in a callback? The problem is that LASTADDNODE does not return such a value when I build the tree because I'm adding items recursively in reverse order. Later in callbacks the ID is completely different because the order of items has changed.
The bindings have support for these types https://github.com/gen2brain/iup-go/blob/main/iup/bind_attributes.go#L19, so basically, besides standard types, it can also set iup.Ihandle and uintptr (both are actually uintptr). So, iup.User should work. Also note that if you want something from Go passed to C, you should make sure that it is not garbage collected, one way that is used in bindings and in examples is to use cgo.Handle which is created for that.
Edit: That is, iup.User should work if USERDATAid allows the IUP handle to be passed, if it expects something else, there is no support for that something else.
It allows the data to be passed but returns 0 later. Maybe I really don't understand how to use this attribute. I set the data as follows after adding a new node:
last := d.tree.GetAttribute("LASTADDNODE")
n, err := strconv.Atoi(last)
if err != nil {
log.Println(err)
}
user := iup.User()
iup.SetAttribute(d.tree, fmt.Sprintf("USERDATA%v", n), user)
user is not 0 and I can convert it to int to store my own data. No error occurs. I'm adding data like this for every node in the tree during construction. Using LASTADDNODE like this works fine for images. However, when I later attempt to retrieve the userdata for a node by given ID, it doesn't work:
user := iup.GetAttributeHandle(d.tree, fmt.Sprintf("USERDATA%v", id))
log.Printf("This is always 0: %v\n", user)
As the text says, this is always 0 aka NULL. The node positions change during construction of the tree but the given id in the above code is valid. For example, I use the id provided by SELECTION_CB. It's as if nothing is stored.
I just checked, looks like it is working like this:
user := iup.User()
user.SetAttribute("my", 10)
iup.TreeSetAttributeHandle(tree, "USERDATA", n, user)
...
// then later
user := iup.GetAttributeHandle(iup.GetHandle("tree"), fmt.Sprintf("USERDATA%v", n))
fmt.Printf("%v %d\n", user, user.GetInt("my"))
I also noticed that extra functions like IupTreeSetUserId/IupTreeGetUserId/IupTreeGetId are not implemented, which seems to be useful for these cases. When I find the time I can add them.
Thanks, that reply was very helpful. Since user is 0 in my tree, this confirms that there is no problem with user attribute but that I cannot use LASTADDNODE in the way I'm populating the tree. I'm doing this from bottom up, using ADLEAF with the level as ID and adding items in reverse order. That is, I always use ADDBRANCHLASTADDNODE returns numbers like 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2,1.
What's puzzling is that using LASTADDNODE for IMAGEid in this way works fine, all entries have the right image. But it appears that USERDATA requires the real index of the node. Unfortunately, there does not seem any way to obtain the real index from the tree, and calculating it is complicated - in fact, I still don't really get how IUP's own example of "method 1" for populating a tree in the docs works for more complex nested trees. Both methods are the most obscure ways of specifying a tree I've ever encountered. :(
int IupTreeGetId(Ihandle *ih, void *userid);
ih: Identifier of the interface element.
userid: Pointer or Lua table associated to the node.
Returns the id of the node that has the userid on success or -1 (nil) if not found. SetUserId must have been called with the same userid.
This looks like it is what you need. I skipped those functions because they pass a user pointer. Maybe cgo.Handle can be used here, so the user can wrap Go struct etc. like it is now, iup.Ihandle already works, and iup.User is designed for that, not sure what to do here. Adding support for iup.User would be easy, can be used for string, int, float, etc.
That seems to be what I need. Adding support for this would be helpful. I'm not 100% sure though because my problem seems to be knowing the right ID when adding the userdata. The problem is that branches are counted differently from leaves, which makes keeping track of it difficult when adding data recursively according to "method 2" (bottom up). It's kind of annoying that IUP doesn't just provide a unique node ID out of the box, no matter how the node is being moved. Ironically, IMAGEid would suffice for my use case but it is write only.
I've re-opened the issue as a reminder - feel free to close it if you don't have time to add more of these helper functions.
I've found a crazy workaround for my problem, just in case someone has the same problem. When adding nodes, I set the title to an arbitrary key counter as string, add this key with my custom data to an internal store, a simple map[key]datum. After everything has been added, a traverse the nodes directly by counting i from 0...COUNT-1, get the title, lookup my custom data, correct the title, and set the custom data in my internal store with key i:
// correctTitles corrects the titles for the asset tree nodes based on the numbers stored, after all nodes
// have been added, and adds the asset to the id store of the tree. This is a bit of a hack.
// CHECK Performance.
func (d *AssetTreeDisplay) correctTitles(ctx context.Context, prj *projects.Projects, p projects.Project,
store map[int]projects.Asset) {
total := d.tree.GetInt("COUNT")
for i := 0; i < total; i++ {
n := d.tree.GetInt(fmt.Sprintf("TITLE%v", i))
asset, ok := store[n]
if !ok {
log.Printf("WARN: Missing title for asset tree node %v.\n", i)
continue
}
if asset.IsNull() {
d.tree.SetAttribute(fmt.Sprintf("TITLE%v", i), fmt.Sprintf(tr.T("dlg.asset-list-project-name"), p.Name(prj)))
} else {
d.tree.SetAttribute(fmt.Sprintf("TITLE%v", i), asset.Name(prj, p))
}
d.ids.Set(i, asset)
}
}
i is the real index I was looking for. It's quite a hack but works like a charm. The changing of the title is not visible.