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

How to set active docked window? #5005

Open
aeris170 opened this issue Feb 10, 2022 · 17 comments
Open

How to set active docked window? #5005

aeris170 opened this issue Feb 10, 2022 · 17 comments

Comments

@aeris170
Copy link

aeris170 commented Feb 10, 2022

I'm building my dock layout like so:

ImGuiID dockspace_id = ImGui::GetID("Dockspace");

if (ImGui::DockBuilderGetNode(dockspace_id) == nullptr) {
	// Clear out existing layout
	ImGui::DockBuilderRemoveNode(dockspace_id);
	// Add empty node
	ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
	// Main node should cover entire window
	ImGui::DockBuilderSetNodeSize(dockspace_id, ImGui::GetWindowSize());
	// Build dock layout
	ImGuiID center = dockspace_id;
	ImGuiID left = ImGui::DockBuilderSplitNode(center, ImGuiDir_Left, 0.25f, nullptr, &center);
	ImGuiID right = ImGui::DockBuilderSplitNode(center, ImGuiDir_Right, 0.25f, nullptr, &center);

	ImGuiID rightDown;
	ImGuiID rightUp = ImGui::DockBuilderSplitNode(right, ImGuiDir_Up, 0.65f, nullptr, &rightDown);

	ImGui::DockBuilderDockWindow(WINDOW1_ID, left);
	ImGui::DockBuilderDockWindow(WINDOW2_ID, rightUp);
	ImGui::DockBuilderDockWindow(WINDOW3_ID, rightDown); 
	ImGui::DockBuilderDockWindow(WINDOW4_ID, rightDown);
	// dock some more windows ...
	
	ImGui::DockBuilderFinish(dockspace_id);
}

ImGui::DockSpace(dockspace_id, { 0, 0 }, dockspace_flags);

I'm docking WINDOW3 and WINDOW4 to the same node, and they are docked but since I docked WINDOW4 after WINDOW3, WINDOW4 comes up as the "selected" dock window. How can I dock WINDOW4 after WINDOW3 but make WINDOW3 come up as the selected dock window programatically?

What happens when I open my program:

image

What I want to happen when I open my program:

image

Here is my build info:

Dear ImGui 1.87 WIP (18605)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1929
define: _MSVC_LANG=202002
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_glfw
io.BackendRendererName: imgui_impl_opengl3
io.ConfigFlags: 0x00000440
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 2 fonts, Flags: 0x00000000, TexSize: 2048,2048
io.DisplaySize: 3440.00,1387.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00
@AlexvZyl
Copy link

Would ImGui::SetWindowFocus(const char* name) not solve your problem?

@aeris170
Copy link
Author

It partially does. I didn't know that function existed up until now, I just tried it and it "solves" my problem.

However, my windows have complicated names. They are in the form of:
SOME_UNICODE_STRING + " " + WINDOW_NAME + "###" + WINDOW_NAME

If we had ImGui::SetWindowFocus(id), then I could just pass "###" + WINDOW_NAME but right now I can't.

Is this easy to implement? If so, maybe I can look into it. I'm asking because I'm not an experienced programmer.

@AlexvZyl
Copy link

AlexvZyl commented Feb 10, 2022

I am not sure what data you have on your side, but would one of these be of help?

FindWindowByID(ImGuiID id)
FindWindowByName(const char* name)

They can be found in imgui_internal.h.

@aeris170
Copy link
Author

aeris170 commented Feb 10, 2022

I don't understand how finding the pointer to the window would help me. I can't find a function in ImGuiWindow class that would focus the window. Maybe I'm missing it, can you help me find it?

By the way, all these focus related functions only work if the window in question exists (naturally, since it's impossible to find and then focus a non-existent window) but I would like to do this "focusing" (setting selected dock window) while I'm building my dock layout, but the problem is my windows don't exist yet. They ImGui::Begin() after the dock layout is built. Can I somehow tell the dockbuilder to focus the window for me by ID (like how it docks it with ID). Like this:

ImGui::DockBuilderDockWindow(WINDOW1_ID, left);
ImGui::DockBuilderDockWindow(WINDOW2_ID, rightUp);
ImGui::DockBuilderDockWindow(WINDOW3_ID, rightDown); 
ImGui::DockBuilderDockWindow(WINDOW4_ID, rightDown);

ImGui::DockBuilderFocusDockNodeWindow(WINDOW3_ID, rightDown); // sadly, this function doesn't exist yet.

@AlexvZyl
Copy link

AlexvZyl commented Feb 10, 2022

You should not look for functions using the window pointer, but rather the information contained in the struct. It seemed like that would be able help you (I am now aware it will not). Also, I forgot you cannot pass ImGuiCond_Once to the focus function, my bad.

Have you tried changing this:

ImGui::DockBuilderDockWindow(WINDOW3_ID, rightDown); 
ImGui::DockBuilderDockWindow(WINDOW4_ID, rightDown);

to this:

ImGui::DockBuilderDockWindow(WINDOW4_ID, rightDown);
ImGui::DockBuilderDockWindow(WINDOW3_ID, rightDown); 

@aeris170
Copy link
Author

aeris170 commented Feb 10, 2022

If I change the order of the lines, the order of the docked windows change. I want to keep the order in which they are docked, but I want to focus some window other than the last docked one.

@AlexvZyl
Copy link

AlexvZyl commented Feb 10, 2022

A workaround would be to have one frame render with the ImGui focus function at the end of the loop (seperate from your main loop) and after that you start calling your normal main loop. That is what I do for the dock builder. I have a renderInitialFrame() call inside my Application constructor.

@aeris170
Copy link
Author

A workaround would be to have one frame render with the ImGui focus function at the end of the loop (seperate from your main loop) and after that you start calling your normal main loop. That is what I do for the dock builder. I have a renderInitialFrame() call inside my Application constructor.

I think users shouldn't be forced to circumvent this issue. The DockBuilder API should support this feature. What do you think about this @ocornut? I would try to implement this feature and create a PR but I think my abilities are subpar compared to the people who are maintaining this project.

@CobaltXII
Copy link

@aeris170 ImGui::FocusWindow(ImGui::FindWindowByID(id))

@ocornut
Copy link
Owner

ocornut commented Feb 22, 2022

Sorry for not answering been busy and likely won't solve it ASAP but @aeris170 is right that this should be possible before the windows are submitted. That's even the whole reason the DockBuilder API takes strings for window, because it is acting both on persisting settings + runtime window data.

I think using DockBuilderGetNode(id)->SelectedTabId = ImHashStr("#TAB", 0, ImHashStr("window_name", 0, 0)) you can probably workaround it, but due to #2304 (which is quite complex) it might not always restore the way you want depending on the timing of when windows appear. Give it a try @aeris170 and maybe it if works for you we can wrap that in a helper function.

@aeris170
Copy link
Author

aeris170 commented Feb 23, 2022

Sorry for not answering been busy and likely won't solve it ASAP but @aeris170 is right that this should be possible before the windows are submitted. That's even the whole reason the DockBuilder API takes strings for window, because it is acting both on persisting settings + runtime window data.

I think using DockBuilderGetNode(id)->SelectedTabId = ImHashStr("#TAB", 0, ImHashStr("window_name", 0, 0)) you can probably workaround it, but due to #2304 (which is quite complex) it might not always restore the way you want depending on the timing of when windows appear. Give it a try @aeris170 and maybe it if works for you we can wrap that in a helper function.

No worries @ocornut ! I know you are a busy man, and I'm aware you are providing all of this free of charge, so the least I can do would be waiting :)

Coming to your suggestion: I applied it but it doesn't work, as you've predicted. The focusing and order seems to depend on the order of rendering of windows. For example;

ImGui::DockBuilderDockWindow("###A", center);
ImGui::DockBuilderDockWindow("###B", center);

ImGui::DockBuilderGetNode(center)->SelectedTabId = ImHashStr("###A"); // doesn't do anything, I remove this and things don't seem to change

and later,

ImGui::Begin("A###A");
ImGui::End();
ImGui::Begin("B###B");
ImGui::End();

would lead to B getting the focus (selected) instead of A. If I change the order of Begin's then A gets the focus. Changing the order of windows also seems to effect the order of tabs.

If I render B before A, A gets the focus but A is the second tab and B is the first tab.
If I render A before B, B gets the focus but B is the second tab this time.

We should probably desire to seperate the order of windows and order of tabs/default focused window. "But how?" is the question here, and I, sadly, don't have an answer to that.

Here is a screenshot, if I couldn't make myself clear:
image

I understand this one is a tough one to crack (because of #2304 ) so how should we proceed from here?

@aeris170
Copy link
Author

aeris170 commented Feb 23, 2022

As a follow up, I am able to "workaround" this problem with these ugly pieces of code:

// WARNING: BAD HABITS AHEAD!!!
void ExecuteDockBuilderOrderAndFocusWorkAround() {
	static int i = -1; 
	if (i == 0) { // only execute if we are in the second frame of our program
		auto asset = ImGui::FindWindowByName(ASSET_MANAGER_ID);
		ImGui::FocusWindow(asset);

		auto obs = ImGui::FindWindowByName(OBSERVER_ID);
		ImGui::FocusWindow(obs);

		auto viewport = ImGui::FindWindowByName(SCENE_VIEWPORT_ID);
		ImGui::FocusWindow(viewport);
	}
	i++;
}
// ... render stuff in the order I desire...
// ...

ImGui::End();
ExecuteDockBuilderOrderAndFocusWorkAround();

These pieces of code lead to this screenshot:
image

So this issue is pseudo-resolved. We have a working way to achieve the desired result, yet it is inelegant and possibly evil.

@ocornut
Copy link
Owner

ocornut commented Feb 23, 2022 via email

@aeris170
Copy link
Author

aeris170 commented Feb 23, 2022

I'm sorry, I thought if I passed "###ID" for id, everything would be fine. What is meant by "#TAB" and "window_name"?

Lets say my window has the name "Hello there!###hello_window", should I use your code snippet as ImHashStr("#TAB", 0, ImHashStr("Hello There!", 0, 0))? I'm sorry if I'm missing something very obvious.

@ocornut
Copy link
Owner

ocornut commented Feb 24, 2022

First of all we need to know which version are you on, which was omitted from the requested Issue Template.

my window has the name "Hello there!###hello_window"

Then use the window name.. ImHashStr("#TAB", 0, ImHashStr("Hello there!###hello_window", 0, 0))
(technically ImHashStr("#TAB", 0, ImHashStr("###hello_window", 0, 0)) will be identical).

But it'll depend on your version.

@aeris170
Copy link
Author

First of all we need to know which version are you on, which was omitted from the requested Issue Template.

my window has the name "Hello there!###hello_window"

Then use the window name.. ImHashStr("#TAB", 0, ImHashStr("Hello there!###hello_window", 0, 0)) (technically ImHashStr("#TAB", 0, ImHashStr("###hello_window", 0, 0)) will be identical).

But it'll depend on your version.

I'm on 1.87 WIP docking branch, I included config details in my original post. I'll make sure it's included in my future issues.

Here is how I now set up my dockspace:

// declared inside class in .hpp file
static constexpr auto OBSERVER_ID{ "###Observer" };
static constexpr auto CONSOLE_ID{ "###Console" };
static constexpr auto ASSET_MANAGER_ID{ "###Asset Manager" };
static constexpr auto SCENE_VIEWPORT_ID{ "###Scene Viewport" };
static constexpr auto GAME_VIEWPORT_ID{ "###Game Viewport" };
static constexpr auto SCENE_SETTINGS_ID{ "###Scene Stats/Settings" };
// ...
	
// ...
ImGui::DockBuilderDockWindow(SCENE_VIEWPORT_ID, center);
ImGui::DockBuilderDockWindow(GAME_VIEWPORT_ID, center);
			
ImGui::DockBuilderDockWindow(OBSERVER_ID, rightUp);
ImGui::DockBuilderDockWindow(SCENE_SETTINGS_ID, rightUp);

ImGui::DockBuilderDockWindow(ASSET_MANAGER_ID, rightDown);
ImGui::DockBuilderDockWindow(CONSOLE_ID, rightDown);

ImGui::DockBuilderGetNode(center)->SelectedTabId = ImHashStr("#TAB", 0, ImHashStr(SCENE_VIEWPORT_ID, 0, 0));
ImGui::DockBuilderGetNode(rightUp)->SelectedTabId = ImHashStr("#TAB", 0, ImHashStr(OBSERVER_ID, 0, 0));
ImGui::DockBuilderGetNode(rightDown)->SelectedTabId = ImHashStr("#TAB", 0, ImHashStr(ASSET_MANAGER_ID, 0, 0));

ImGui::DockBuilderFinish(dockspace_id);

And here is the result :(
image

Sadly, it didn't work for me.

@Fewnity
Copy link

Fewnity commented Dec 4, 2023

Hello!
I tried

ImGui::DockBuilderDockWindow("Inspector", inspectorNode);
ImGui::DockBuilderDockWindow("Debug", inspectorNode);
ImGui::DockBuilderGetNode(inspectorNode)->SelectedTabId = ImHashStr("#TAB", 0, ImHashStr("Inspector", 0, 0));

and this change nothing, the inspector tab is not selected. Debug is always selected.

And I don't know why, change DockBuilderDockWindow lines order does not change tabs order.

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

No branches or pull requests

5 participants