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

Multiple instances of GUIs and viewports #269

Closed
yngccc opened this issue Jul 9, 2015 · 14 comments
Closed

Multiple instances of GUIs and viewports #269

yngccc opened this issue Jul 9, 2015 · 14 comments

Comments

@yngccc
Copy link

yngccc commented Jul 9, 2015

Hello, does ImGui support having multiple instance of GUIs, potentially display on multiple viewports with different dimensions, with only one viewport activated and accepting input at a time?

I would like to add use ImGui on my project here. But worry it might not be easy to make it work the way I wanted.

@paultech
Copy link

paultech commented Jul 9, 2015

I've had success with using ImGui::GetInternalStateSize(), ImGui::SetInternalState()/ImGui::GetInternalState() for handling for multiple independent instances. You do need to take it into account in your render function.

@ocornut
Copy link
Owner

ocornut commented Jul 9, 2015

It should work. What you have to do is switch the global context pointer using ImGui::GetInternalState(), ImGui::SetInternalState(). All the ImGui functions access this global context.

It may be not well supported or tested so I'm sure you'll run into problems here and there but overall it's possible.

@Pagghiu
Copy link
Contributor

Pagghiu commented Jul 9, 2015

I did it with GetInternalState/SetInternalState and it works.
I just have some problems with tooltips kinda "flickering" by printing lines of tooltip text, but it could very well be my fault on how I handle the gui update/rendering phases (or something else completely unrelated).

@yngccc
Copy link
Author

yngccc commented Jul 10, 2015

I got it working without running into any problem so far.
Image
Awesome library. 👍

@yngccc yngccc closed this as completed Jul 10, 2015
@Cthutu
Copy link

Cthutu commented Mar 8, 2016

I am not sure how this works. I have multiple HWNDs each with a HGLRC, but only a callback to a single render function. Do I initialise the render callback, then make a copy of the buffer with GetInternalState/GetInternalStateSize, then everytime I want to a do a NewFrame...Render, I have to start with a SetInternalState()?

@ocornut
Copy link
Owner

ocornut commented Mar 8, 2016

Your render callback will be called by Render() as a convenience. So at the time you call Render() you should know which of your ImGui state is active if you have more than one. You may pass arbitrary data via the io.UserData storage if that's any helpful. You can also disable the render callback and call ImGui::GetDrawData() after Render() but it'll get you the exact same thing as the parameter to your render function.

then make a copy of the buffer with GetInternalState/GetInternalStateSize

This isn't a structure you can copy yourself. The rendering data is in ImDrawData and only valid after Render() and until you call NewFrame() for the context. So actual render directly about you call ImGui::Render() is preferable. If you really need to make a copy (for later frames) you may make a deep copy of the ImDrawData structure and all vectors there, but you shouldn't have to do that.

@ocornut
Copy link
Owner

ocornut commented Mar 8, 2016

If you want to create additional state, allocate GetInternalStateSize() bytes and call SetInternalState() once with contruct=true. Your new state is constructed.

If you want to retrive the default state, call GetInternalState() once on startup before changing the pointer.

You can switch state at any time by calling SetInternalState().

EDIT I realize it is strange that there's no helper function to create a new state.
EDIT2 The reason it is setup this way is that when creating a new state you don't want to rely on io.MemAlloc from the current state. So we let you allocate the initial block of memory.

@Cthutu
Copy link

Cthutu commented Mar 8, 2016

So my workflow should be:

For each openGL context/HWND:

  1. Rewrite to the ImGuiIO structure (including display size)
  2. Call NewFrame
  3. Call ImGui functions
  4. Call Render
  5. Call GetDrawData() and do what have done in the render callback

Am I correct?

On Tue, 8 Mar 2016 at 15:14 omar notifications@github.com wrote:

If you want to create additional state, allocate GetInternalStateSize()
bytes and call SetInternalState() once with contruct=true. Your new state
is constructed.

If you want to retrive the default state, call GetInternalState() once on
startup before changing the pointer.

You can switch state at any time by calling SetInternalState().


Reply to this email directly or view it on GitHub
#269 (comment).

@ocornut
Copy link
Owner

ocornut commented Mar 8, 2016

Missing the most important part

  1. Call SetInternalState()

But you can totally keep changing the internal state during the frame as well, you don't need to do NewFrame/misc calls/Render completely, you can interleave access to the different context as long as you call SetInternalState() to select the active context.

@Cthutu
Copy link

Cthutu commented Mar 8, 2016

I don't understand why I would call SetInternalState(). What would I pass
to SetInternalState? I am obviously missing something here. I thought I
wouldn't need to use GetInternalState()? So when would I call
GetInternalState if the results I get back are temporary and deleted on
NewFrame().

Sorry for being blind about this. Would it be possible to jot some
pseudo-code down for posterity about how this would be achieved?

Reading over my original message I am wondering when I said "copy the
buffer" you thought I meant the draw data, which would be understandable.
What I was referring to was the void*/size combination returned by
GetInternalState()/GetInternalStateSize() and NOT the draw data. If this
is the case, that might be causing the confusion I have by misinterpreting
you. Some pseudo-code would clear this up I am sure.

BTW, I think your library is amazing and I've watched the level of support
you give it to other people for many weeks now and I am very impressed. It
was this and the reactive nature of my tool that made me sure that ImGui
was the correct choice for my UI.

On Tue, 8 Mar 2016 at 15:29 omar notifications@github.com wrote:

Missing the most important part

  1. Call SetInternalState()

But you can totally keep changing the internal state during the frame as
well, you don't need to do NewFrame/misc calls/Render completely, you can
interleave access to the different context as long as you call
SetInternalState() to select the active context.


Reply to this email directly or view it on GitHub
#269 (comment).

@ocornut
Copy link
Owner

ocornut commented Mar 8, 2016

If you want to have 2 independant contexts of ImGui running in each of your window (as opposed to a single ImGui running across two windows like a virtual desktop), you need 2 ImGui contexts.

GetInternalState() returns you the pointer to the current context. I may as well rename those functions to use the word "Context" instead of State. This is persitent, we store windows information and state that is carried from frame to frame.

By default only one is created. You can create more:

// Create new ImGui context
void* new_state = malloc(ImGui::GetInternalStateSize());
ImGuI::SetInternalState(new_state, true);

Then change anytime:

// Select current context 1
ImGui::SetInternalState(my_state_1);
// do stuff
// ..
// Select current context 2
ImGui::SetInternalState(my_state_2);
// do stuff

This is like if you have two completely different instances of ImGui running.

@Cthutu
Copy link

Cthutu commented Mar 8, 2016

That makes things very clear to me now. My interpretation of the semantics
of these functions were initially incorrect, which is why I was
struggling. Thank you very much for your time.

On Tue, 8 Mar 2016 at 16:15 omar notifications@github.com wrote:

If you want to have 2 independant contexts of ImGui running in each of
your window (as opposed to a single ImGui running across two windows like a
virtual desktop), you need 2 ImGui contexts.

GetInternalState() returns you the pointer to the current context. I may
as well rename those functions to use the word "Context" instead of State.
This is persitent, we store windows information and state that is carried
from frame to frame.

By default only one is created. You can create more:

// Create new ImGui context
void* new_state = malloc(ImGui::GetInternalStateSize());
ImGuI::SetInternalState(new_state, true);

Then change anytime:

// Select current context 1
ImGui::SetInternalState(my_state_1);
// do stuff
// ..
// Select current context 2
ImGui::SetInternalState(my_state_2);
// do stuff

This is like if you have two completely different instances of ImGui
running.


Reply to this email directly or view it on GitHub
#269 (comment).

@ocornut
Copy link
Owner

ocornut commented Mar 8, 2016

What do you think would have helped? May I comment or reword something?

@Cthutu
Copy link

Cthutu commented Mar 8, 2016

Good question. My initial incorrect assumption was that you'd "save" the
internal state by getting the pointer via GetInternalState(), and the size
via GetInternalStateSize(), and you would memcpy it to your own buffer.
Then you would call SetInternalState() with your copy later and it would
overwrite it's singleton internal state. This assumption was based on my
experience with previous APIs and has nothing to do with your design.

Your use case above doesn't actually require the use of GetInternalState(),
and the GetInternalStateSize() function always returns the same value it
seems.

I see nothing wrong with your wording. The problem was that previous
experience coloured my perception on how it would be used. That said, I
would use the following API if I was forced to redesign it, using your
suggestion of renaming state to context:

size_t GetContextSize();
void CreateContext(void* buffer); // This would be the
SetInternalState with true boolean.
void ActivateContext(void* buffer); // This would be the SetInternalState
without true boolean.
void* GetCurrentContext(); // This would be the
GetInternalState for saving the default initial state.

What do you think?

On Tue, 8 Mar 2016 at 16:23 omar notifications@github.com wrote:

What do you think would have helped? May I comment or reword something?


Reply to this email directly or view it on GitHub
#269 (comment).