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

Tracing back to the original pushed element ID #7457

Open
WickedP666 opened this issue Mar 31, 2024 · 5 comments
Open

Tracing back to the original pushed element ID #7457

WickedP666 opened this issue Mar 31, 2024 · 5 comments
Labels
label/id and id stack implicit identifiers, pushid(), id stack

Comments

@WickedP666
Copy link

Version/Branch of Dear ImGui:

Version 1.90.2 WIP

Back-ends:

imgui_internal + imgui_stdlib + imgui_single_file + imgui_impl_glfw + imgui_impl_opengl3

Compiler, OS:

Windows 10, VS2022

Full config/build information:

No response

Details:

Hi folks,

What I'm trying to achieve: I'd like to get back to the original element ID of the main element that's being edited. I'm trying to get this, so that I can send an internal message to another system, that can't interact with the ImGui data values.

For example, a colour field has a 3-float container, with other inputs that can also edit the underlying float data. Each bit of the interface seems to have its own ID on the stack. That's fine, and I seem to be able to get the individual IDs. But I can't seem to find a way back to the original element ID.

I've looked at various things, including context IDs etc., and have ended up with a variant of the following pseudo code:

... Add colour picker etc.
const char *str_id = String(...);
int hashid = ImGui::GetID(str_id);	// <-- trying to get back to this id
ImGui::PushID(hashid);
ImGui::ColorPicker3(label,floatValues);
ImGui::PopID();
IDS.SetData(passedID,hashid);	// <-- custom storage for passed and hashed ids, to help get back to the original project data containers
...

.. Now, while changing the colour field, try to find an id match back to the original pushed element ID
ImGuiContext &g = *GImGui;
ImGuiIDStackTool *stackTool = &g.DebugIDStackTool;

for(int i = 0; i < stackTool->Results.Size;)
{
	ImGuiStackLevelInfo *info = &stackTool->Results[i];

	if(info != nullptr)
	{
		int hashID = stackTool->Results[i].ID;
		Print("Stack level i..: " + intStr(hashID) + etc...);

		int originalID = [iterate IDs here for originalmatch];	// See if there's a hashID match to any of the original element IDs here, e.g., from the custom storage above
		
		if(hashID == originalID)
		{
			Print("Yay!");
		}
	}
				
	++i;
}
...

I've tried other things, like printing other IDs etc. from the running context. None (so far) seem to match.

The issue I'm getting above, is that the id prints from the stack tool are not showing the original first id I set before adding the colour picker (see accompanying screen shot). But, it is listed in the debug id stack tool's window. For some reason, mine are different from the stack tools window. And I just can't see how to get the path to what that has.

Probably showing my ignorance of the stack system, but would like to learn. How can I trace back to the original pushed element ID here?

Just to explain briefly, as I realise this design approach is counter to the expected ImGui way. My main project data sits in other container objects managed by another SDK, which don't allow access the underlying data values. So you can't create references or pointers to them, which is why I can't use them directly. I have to keep two copies. So, I'm trying to make a searcher, to find an original element ID match, so I can send a message back to the other SDK with the original element ID (which I can tie to the project data container ids) so as to action changes elsewhere.

Hopefully I've posted in the right place this time!

Regards,

WP.

Screenshots/Video:

Description Interface ID Issue

Minimal, Complete and Verifiable Example code:

No response

@PathogenDavid
Copy link
Contributor

This feels extremely like an XY problem. What you're describing sounds excessively convoluted.

The best documentation for understanding the ID stack is this FAQ entry. It feels like you have some incorrect preconceived notions of how it works that are muddying things here.

int hashid = ImGui::GetID(str_id);	// <-- trying to get back to this id
ImGui::PushID(hashid);

This is not how GetID/PushID are meant to be used. The PushID(int) overload is meant for pushing something like a loop index, the passed int_id will be hashed so you're hashing str_id and then hashing that hashed value again.

My main project data sits in other container objects managed by another SDK, which don't allow access the underlying data values. So you can't create references or pointers to them, which is why I can't use them directly.

It would help to know the API shape of this SDK.

Assuming you just have a pair of getter/setter functions, all you need to do is use a local temporary:

ImVec4 c = GetColor();
if (ImGui::ColorEdit4("My color", &c.x))
    SetColor(c);

I have to keep two copies.

One of Dear ImGui's defining principles is that you shouldn't have redundant state. Needing to do so is usually a sign you're doing something incorrectly.

@WickedP666
Copy link
Author

Thanks PathogenDavid,

I'll try keep my response to an acceptable length. I'll just preface the below by saying that my program is indeed convoluted, and partially by design. But also because it has to be. The ImGui layer is put on top of another system that is much bigger, and that is well and truly the commanding one here. And it just doesn't gel well with others.

The best documentation for understanding the ID stack is this FAQ entry.

Have read this several times. That said, I've just had a realisation of something I may not have understood before. So, this might still be part of my problem.

This is not how GetID/PushID are meant to be used.

I picked up on this as well. I had other variants. I'll follow this up with a question further below.

It would help to know the API shape of this SDK.

Just quickly, they built a custom container system that takes an id and can seat any data type. It can house objects equivalent to long, float, vector, matrix, strings and etc. It can even house a container of it's own, inside itself. So you can go deep with it if you want!! With exceptions to one or two, we just don't have access to the objects directly, only via getter/setter functions as you mentioned.

Assuming you just have a pair of getter/setter functions, all you need to do is use a local temporary:

ImVec4 c = GetColor();
if (ImGui::ColorEdit4("My color", &c.x))
    SetColor(c);

First question: What happens if local temporary objects go out of scope, before the call to render? Will that matter to ImGui?

One of Dear ImGui's defining principles is that you shouldn't have redundant state. Needing to do so is usually a sign you're doing something incorrectly.

Yep, I've read this elsewhere. And it bothers me a little, because of the consequence of having to manage things twice. But there's no way to access the other data objects directly. Simple. Though, some clarification on my first question might help inform design.

As an example for instance: the ImGui::ColorPicker3 seems to have a bunch of additional input fields each with their own IDs. But the overarching element is a colour picker with a single float array for RGB. Irrespective of how ImGui handles the additional fields, eventually all the events must come back to edit the single float array, right? If I had, for instance, the item's top level hash ID, I could try to traverse backup the debug ID stack to see if there's a match with it, and if there is, hey presto, I have my matching id to message back to the other system with (which is what I'm attempting to do at present).

Second question: Following the example above, how would I catch a top level hash ID of an element? Is this possible? If you could, use a ImGui::ColorPicker3 as an example as it has a bunch of child ids to deal with. Can I immediately grab the top level color picker ID after the call to ImGui::ColorPicker3?

If the stack id system here is still my problem, I'm happy to be schooled! :)

WP.

@ocornut
Copy link
Owner

ocornut commented Mar 31, 2024

First question: What happens if local temporary objects go out of scope, before the call to render? Will that matter to ImGui?

We never hold on your data past the function call, so that’s fine, you can edit a local variable.

I’m only skimming through this but it is hard to comprehend your use case, it may be better if you explicitly explain what you need to achieve with your other subsystem, rather than circle around what you perceive are possible solutions.

@PathogenDavid
Copy link
Contributor

What happens if local temporary objects go out of scope, before the call to render? Will that matter to ImGui?

As Omar noted, that reference isn't stored or used outside of the Dear ImGui function call.

If you read the implementations you'll see that Dear ImGui's widget functions all boil down to creating a list of triangles to be later rendered on the GPU which visually represent the widget. By that point your actual data isn't needed anymore.

(If you want to dig around in the code to solidify your understanding here, ImGui::Checkbox is a good candidate. It's a lot simpler than the color-related widgets.)

As an example for instance: the ImGui::ColorPicker3 seems to have a bunch of additional input fields each with their own IDs.

While this is true, this is purely an implementation detail and not something you should be having to think about.

Following the example above, how would I catch a top level hash ID of an element? Is this possible? If you could, use a ImGui::ColorPicker3 as an example as it has a bunch of child ids to deal with. Can I immediately grab the top level color picker ID after the call to ImGui::ColorPicker3?

You should not need to be doing this, you should consider this to be information which has disappeared into the ether.

If the stack id system here is still my problem, I'm happy to be schooled! :)

I think a key element you might be missing is that IDs are really just self-contained 32-bit values under the hood. (The result column in the ID stack tool.)

The ID of a child is created by using the ID of its parent (again, just a 32-bit value -- the seed column of the stack tool) and combining it with the child ID data to create another 32-bit ID value.

(It's an implementation detail, but IDs are distilled into 32-bit values by hashing them using CRC32.)

You should consider the intermediate values that make up this 32 bit value to be information lost to time. (Normally it really is lost to time but there's an internal debug hook that retains it purely for debugging purposes when the stack tool is open.)

@ocornut ocornut added the label/id and id stack implicit identifiers, pushid(), id stack label Apr 2, 2024
@WickedP666
Copy link
Author

OK, I believe I've managed to build a working prototype that aides in this.

How I've done it (briefly): I added my own header file with a macro and a global struct and some functions to add/store/clear/find description ids that sit alongside DImGui hash IDs. The macro is for enabling/disabling the id tree system. I also injected some one-liner code into DImGui's GetID() methods, and created some custom methods in other areas of my code (GetID() equivalents) with some minor changes specific for the tree.

I know I'll have to be carefulupdating DImGui files in future. Noted.

The system allows me to traverse back through an id stack to search, locate and match original custom ids for data located in the other system structures. The system also seems to work with items like the colour picker, that might have multiple hash ids associated with them.

I'm still having to store two copies of the data and have had to create a wrapper to manage this. There are reasons. I won't elaborate - I think I'll just make my attempt at explaining look poor!

Other than that, what an exercise :) Thus far, it seems to work. Thanks for the thoughts above - they helped.

While I think of it - I'm not sure of the performance hit, but the tree structure does get rebuilt every frame. I have some ideas to counter that, just didn't trouble with it as part of the prototype. Though I'm not really concerned with performance for now, I'm treating the program more as a data thing at the moment, and not so much a real time application.

The green viewport gradient in the attached image is proof of concept.

WP.

Description Data Feedback - Working

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
label/id and id stack implicit identifiers, pushid(), id stack
Projects
None yet
Development

No branches or pull requests

3 participants