ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
61.33k stars 10.33k forks source link

Docking branch available for testing #2109

Open ocornut opened 6 years ago

ocornut commented 6 years ago

I have pushed an experimental 'docking' branch: https://github.com/ocornut/imgui/tree/docking Effectively providing a long awaited official solution to the features discussed in #351 and #261.

TL;DR; You can benefit from good portion of docking features with no extra work or API call. Windows can be merged with each others, with or without partitioning the space.

Prefer creating separate New Issues for specific questions/issues, so they can more easily be closed when solved and not clutter this thread (which is already too big)

Please do not report issues without reading the 'Feedback' section of the next post and without reading the 'Contributing' document listed in there. The number of ambiguous, ill-formed questions and incomplete bug reports posted on this entire github is difficult to deal with. Thank you.

image

GIF (this is actually a gif from an older version which has some glitches, but you get the gist!) 20180809_docking

Quick demo

You may checkout docking and build one the example e.g. example_win32_dx11/, example_glfw_opengl3, example_sdl_vulkan to test this. Head to Demo>Configuration to see the global configuration flags. Note that multi-viewports are enabled by default in those demo but only well tested under Windows. If you have issues with them you can disable them via the Demo>Configuration pane or by commenting out the line that sets ImGuiConfigFlags_ViewportsEnable in the main.cpp of your example. Docking is functional without multi-viewports enabled.

ocornut commented 6 years ago

@damqui

for some usage, I needed to programmatically be able to make a tabbed window selected. I didn't find something in the API to do this.

As discussed off-line: if you want to select the tab and make the window focused, you can use SetWindowFocus(const char* name). Docked windows are trying to behave the same as normal windows so this should work just as well.

If you are trying to select the tab in its current tab bar (to make it visible) but without taking focus, your function above SetTabbedWindowSelected() will be useful and we could introduce the feature eventually.

damqui commented 6 years ago

Hi, another tab/docking related question :

In our editor app, we have various ui areas (corresponding to docknodes) (ex : one on the left for file broswer or other game module uis, one on the bottom for log, etc).

In our previous (non-imgui) app, we had hotkeys to hide/show those areas (ex : to switch between "fullscreen view" and "windowed view") : not closing the windows, but just hiding/showing the areas. How to implement this behaviour ?

I know I can close all windows on the "fullscreen view" command, and it will make docknode disappear (hide), but if I want to open those windows again on the "Editor view" command, I have to keep track of what was closed "in order to hide the area" and what was closed "because the user wanted".

To summarize : is there a way of "hiding" a docknode without closing all its docked windows ?

Thanks !

ocornut commented 6 years ago

I know I can close all windows on the "fullscreen view" command, and it will make docknode disappear (hide), but if I want to open those windows again on the "Editor view" command, I have to keep track of what was closed "in order to hide the area" and what was closed "because the user wanted". To summarize : is there a way of "hiding" a docknode without closing all its docked windows ?

Not really. There is a ImGuiDockNodeFlags_KeepAliveOnly flag to Dockspace() that doesn't display the dockspace without detaching any window docked into it (so they'll be hidden if submitted) however that wouldn't solve your issue if any of your windows are NOT docked into this dockspace (e.g. if they are loose floating windows).

In fact, once you consider the possibility that you have any floating window, your problem is not a docking-specific problem at all.

I have to keep track of what was closed "in order to hide the area" and what was closed "because the user wanted".

That should be trivial, e.g. have a global bool show_tools flag and test this flag before testing the per-window visibility flag..

if (show_tools && show_browser_tool) ImGui::Begin("Browser", &show_browser_tool) ...

The global hotkey will toggle show_tools and closing a window manually will close the window specific flag.. This should be non-problem AFAIK..

damqui commented 6 years ago

well yes, that's so simple.. ! thanks ! (and sorry)

ocornut commented 6 years ago

One problem you will run into with this approach is that you will lose the relative z-order of floating windows compared to each others. If this is important to you please open a separate issue. When using Docking it is less of an issue because most of your windows will be docked anyway.

eclbtownsend commented 6 years ago

Hi Omar, really enjoying the docking feature!

I ran into a problem with using the animated title bars (###) which we use for translating window titles. Changing the display text part appears to have randomish behavior:

title translation

To be clear: Using stock imgui, calling Begin() with "Object Properties###somedeterministicid" one frame and "Translated###somedeterministicid" the next

It appears to only update the title bar if a window is docked into the same space as it

ocornut commented 6 years ago

@eclbtownsend I pushed a fix for it! Next time please provide a helper repro to save me time. Here's a repro:

static bool toggle = false;
if (ImGui::IsMouseClicked(2))
    toggle = !toggle;

ImGui::Begin(toggle ? "Object Properties###somedeterministicid" : "Translated###somedeterministicid");
ImGui::Text(toggle ? "Object Properties###somedeterministicid" : "Translated###somedeterministicid");
ImGui::End();

The bug would happen on leaf node with a single window (there's a small side-effect/bug in DockNodeUpdate which resets window->DockIsActive, I've added a comment about it too).

eclbtownsend commented 6 years ago

Thank you! WIll provide repros going forward ^^

r-lyeh commented 6 years ago

Quick bug report: modal windows should have docking disabled (IMO). At this point the controls were unresponsive and had to restart the app :D

image

PS: sorry if it has been posted elsewhere already

ocornut commented 6 years ago

@r-lyeh

Quick bug report: modal windows should have docking disabled (IMO).

Correct, nice catch! Fixed in 33994bbf.

@rokups I forgot to reply to the (older) code you posted in https://github.com/ocornut/imgui/issues/2109#issuecomment-426539279. The reason it is wrong is that you are trying to dock into the root node which has already been split. That's illegal.

SSBMTonberry commented 6 years ago

Awesome! The docking works very well, but I'm having issues wrapping my head around how to correctly define a docking layout by code. I've picked up some stuff from this thread and tried to figure something out, but I've failed :(

Would be nice if I could get a tip or two :) So, this my function doing all the stuff:

void pms::ProgramManager::drawTestDock()
{
    bool open = true;

    if (ImGui::DockBuilderGetNode(ImGui::GetID("MyDockspace")) == NULL)
    {
        ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
        ImGuiViewport* viewport = ImGui::GetMainViewport();
        ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout
        ImGui::DockBuilderAddNode(dockspace_id, viewport->Size); // Add empty node

        ImGuiID dock_main_id = dockspace_id; // This variable will track the document node, however we are not using it here as we aren't docking anything into it.
        ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.20f, NULL, &dock_main_id);
        ImGuiID dock_id_top = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Up, 0.80f, NULL, &dock_main_id);
        ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.20f, NULL, &dock_main_id);
        ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.20f, NULL, &dock_main_id);

        ImGui::DockBuilderDockWindow("James_1", dock_id_left);
        ImGui::DockBuilderDockWindow("James_2", dock_id_top);
        ImGui::DockBuilderDockWindow("James_3", dock_id_right);
        ImGui::DockBuilderDockWindow("James_4", dock_id_bottom);
        ImGui::DockBuilderFinish(dockspace_id);
    }

    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
    ImGuiViewport* viewport = ImGui::GetMainViewport();
    ImGui::SetNextWindowPos(viewport->Pos);
    ImGui::SetNextWindowSize(viewport->Size);
    ImGui::SetNextWindowViewport(viewport->ID);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
    window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;

    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
    ImGui::Begin("DockSpace Demo", &open, window_flags);
    ImGui::PopStyleVar();

    ImGui::PopStyleVar(2);

    ImGui::PushStyleColor(ImGuiCol_DockingEmptyBg, sf::Color::Red);
    ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
    ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), 0);
    ImGui::PopStyleColor();
    ImGui::End();

    ImGui::Begin("James_1", &open, 0);
    ImGui::Text("Text 1");
    ImGui::End();

    ImGui::Begin("James_2", &open, 0);
    ImGui::Text("Text 2");
    ImGui::End();

    ImGui::Begin("James_3", &open, 0);
    ImGui::Text("Text 3");
    ImGui::End();

    ImGui::Begin("James_4", &open, 0);
    ImGui::Text("Text 4");
    ImGui::End();
}

Of course there is something off with the code above, I just cannot figure out what :(

This is what I get (All the "forms" are in the upper left corner): image

This is what I want to achieve: image

Any help would be greatly appreciated :)

pinam45 commented 6 years ago

When you configure the dockspace layout, you still haven't created your window "DockSpace Demo", so you configure a dockspace "MyDockspace" out of the window, not your dockspace "MyDockspace" inside the window.

This should work:

void pms::ProgramManager::drawTestDock()
{
    bool open = true;

    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
    ImGuiViewport* viewport = ImGui::GetMainViewport();
    ImGui::SetNextWindowPos(viewport->Pos);
    ImGui::SetNextWindowSize(viewport->Size);
    ImGui::SetNextWindowViewport(viewport->ID);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
    window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;

    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
    ImGui::Begin("DockSpace Demo", &open, window_flags);
    ImGui::PopStyleVar();

    ImGui::PopStyleVar(2);

    if (ImGui::DockBuilderGetNode(ImGui::GetID("MyDockspace")) == NULL)
    {
        ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
        ImGuiViewport* viewport = ImGui::GetMainViewport();
        ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout
        ImGui::DockBuilderAddNode(dockspace_id, viewport->Size); // Add empty node

        ImGuiID dock_main_id = dockspace_id; // This variable will track the document node, however we are not using it here as we aren't docking anything into it.
        ImGuiID dock_id_left = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.20f, NULL, &dock_main_id);
        ImGuiID dock_id_right = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.20f, NULL, &dock_main_id);
        ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.20f, NULL, &dock_main_id);

        ImGui::DockBuilderDockWindow("James_1", dock_id_left);
        ImGui::DockBuilderDockWindow("James_2", dock_main_id);
        ImGui::DockBuilderDockWindow("James_3", dock_id_right);
        ImGui::DockBuilderDockWindow("James_4", dock_id_bottom);
        ImGui::DockBuilderFinish(dockspace_id);
    }

    ImGui::PushStyleColor(ImGuiCol_DockingEmptyBg, sf::Color::Red);
    ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
    ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), 0);
    ImGui::PopStyleColor();
    ImGui::End();

    ImGui::Begin("James_1", &open, 0);
    ImGui::Text("Text 1");
    ImGui::End();

    ImGui::Begin("James_2", &open, 0);
    ImGui::Text("Text 2");
    ImGui::End();

    ImGui::Begin("James_3", &open, 0);
    ImGui::Text("Text 3");
    ImGui::End();

    ImGui::Begin("James_4", &open, 0);
    ImGui::Text("Text 4");
    ImGui::End();
}

Moved the conditional dockspace configuration after the window begin and also slightly modified the dockspace config block so there is no empty docking space as on your picture.

SSBMTonberry commented 6 years ago

@pinam45 : Thank you very much! Worked like a charm! :smile:

r-lyeh commented 6 years ago

I was giving imgui some stress test (sorry! :) with most of widgets in the Wiki, and found that the plot widgets from the main imgui demo have lost their avail width after some heaving docking. The plot widgets seem to stick to a very thin width for some reason (check region at top-right-center image):

image

Ideas?

PS: Other than that the docking branch seems good to go! Congrats :D

codecat commented 6 years ago

After upgrading to the docking branch, window settings don't seem to save to the ini file anymore. Any ideas?

The file only seems to contain one line:

[Docking][Data]
heroboy commented 6 years ago

How to use this to make a spliter view? That is disable user to change the docking layout, and disable showing the top tab bar. And I think ImGuiWindowFlags_NoDocking should split to ImGuiWindowFlags_NoDockingToAnother and ImGuiWindowFlags_NoBeingDocked

r-lyeh commented 6 years ago

Two bugs:

image

image

image

edit: highlighted overlap conflicts.

ocornut commented 6 years ago

@codecat

After upgrading to the docking branch, window settings don't seem to save to the ini file anymore. Any ideas?

Do some debugger stepping in SaveIniSettingsToMemory() to find what's wrong. I suspect you might be doing something fishy either with the context either with settings.

@r-lyeh

The plot widgets seem to stick to a very thin width for some reason (check region at top-right-center image):

How are the other widgets? That doesn't seem to be a plot widget issue but we cannot see the other widgets in your screenshot. The imgui_demo.cpp code does: ImGui::PushItemWidth(ImGui::GetFontSize() * -12)

Which depending on your font may remove more or less space (we should probably have some sort of font helper to retrieve the width of "M" or average width of characters?).

image

Tweak a color widget on the right side of the screen, so the popup appears like a native/floating window. Then close the app (while the popup is being displayed). Next time you open the app it will crash.

I can't seem to repro this. Do you have a callstack and can you try to make a repro?

Window contents are overlapping docked tab regions sometimes (see 1st and 2nd images). It is not a problem until the overlapping area contains a menu which causes logic hovering to malfunction (see 2nd image). Note: no rendering/logic problems when viewed as regular windows.

Would also need a repro (probably relate to your style setting).

codecat commented 6 years ago

Ok, looks like it's only saving if the docking flag is set: (I suppose that's intentional for now?)

if (!(g.IO.ConfigFlags & ImGuiConfigFlags_DockingEnable))
    return;
r-lyeh commented 6 years ago

@ocornut ah good catches!

  1. Plots: It is definitely the spacing * -12. It behaves then as intended due to my large font. Btw, I find usually better to measure 'W', whose width may be equal to 'M' in best case (if both left-right sides of W are parallel) or larger in worst case (when both left/right sides are diagonal).
  2. Menu overlap: It is due to font face or font size definitely. It does not happen with embedded font.
  3. Palette crash: I will try to repro a case with stack tomorrow; as it is not crashing at the moment in my 2nd pc. (*)

PS: (*) I will edit this post when i recap some more info.

Edit: 2. The problem is: tab caption font face does not match content font face actually, but regular window captions do. Ie, hexeditor & texteditor use the monospaced font, all the other windows use a variable width font. See attached pic. Tab caption is variable width and tab content is monospaced, as opposed to regular windows which use same font uniformly and got aligned. Increasing FramePadding.Y fixes the issue for now.

image

// repro: configure a very large font at slot #0 then:
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
    static MemoryEditor mem_edit_1;
    char *mem_block = "oh lala"; size_t mem_block_size = strlen(mem_block) + 1;
    mem_edit_1.DrawWindow("Memory Editor", mem_block, mem_block_size, 0x0000);
ImGui::PopFont();
meshula commented 6 years ago

@r-lyeh FWIW, "M" is a typographical standard for layout calculations - https://en.wikipedia.org/wiki/Em_(typography)

ocornut commented 6 years ago

@codecat

Ok, looks like it's only saving if the docking flag is set: (I suppose that's intentional for now?)

That's totally a bug, this test was added in the wrong function. Fixed now.

@r-lyeh It is a little tricky but at the moment Docking doesn't allow altering per-window title bar style. The docking nodes are processed in NewFrame() or DockSpace(). This specific bug is a little curious but it won't work either way: what would happen if you dock two windows with different title font with each others? It's an open problem.

@heroboy

How to use this to make a spliter view? That is disable user to change the docking layout, and disable showing the top tab bar. And I think ImGuiWindowFlags_NoDocking should split to ImGuiWindowFlags_NoDockingToAnother and ImGuiWindowFlags_NoBeingDocked

It's not possible at the moment but I would like to allow it. It wouldn't use the ImGuiWindowFlags_NoDocking flag, rather then dockspace itself would have something eg. ImGuiDockFlags_Locked so you can't change it's layout, and the window would be docked programmatically at the spot you want.

gwjames commented 6 years ago

It's not possible at the moment but I would like to allow it. It wouldn't use the ImGuiWindowFlags_NoDocking flag, rather then dockspace itself would have something eg. ImGuiDockFlags_Locked so you can't change it's layout, and the window would be docked programmatically at the spot you want.

This sounds great! Any idea on the timeframe or priority of this feature compared to everything else?

hilbertdu commented 6 years ago

Awesome, very easy to use. but it sames modal popup window is not top most when ImGuiConfigFlags_ViewportsNoMerge flag has been set.

r-lyeh commented 6 years ago

More feedback,

I am trying to create a dockable toolbar (by creating a window with NoTitlebar flag), however, the toolbar titlebar is always visible no matter what flags I set up. I understand this is a design decision, since the titlebar is needed to drag the docked tabs, but... if we can move regular windows by dragging their contents, why dont we allow to move tabs by dragging contents as well !? :)

image

PS: maybe to keep things compatible as they currently are we might: 1) If NoTitlebar is set, allow user to move tabs by dragging content only. 2) If NoTitlebar is unset, allow user to move tabs by dragging titlebars only.

JackMcCallum commented 6 years ago

It would be a good idea to add a button to hide the tab bar when there is only 1 tab in it, like in the unreal engine editor

On Tue, Nov 6, 2018 at 11:52 PM r-lyeh notifications@github.com wrote:

More feedback,

I am trying to create a dockable toolbar (by creating a window with NoTitlebar flag), however, the toolbar titlebar is always visible no matter what flags I set up. I understand this is a design decision, since the titlebar is needed to move the docked tabs, but... if we can move windows by dragging their contents, why dont we allow to drag contents to move tabs as well !? :)

[image: image] https://user-images.githubusercontent.com/35402248/48065303-9a1c7600-e1ca-11e8-8f61-8ce763cf46f0.png

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ocornut/imgui/issues/2109#issuecomment-436241650, or mute the thread https://github.com/notifications/unsubscribe-auth/AGQa7Qsx7qN0uHH5_t5elnY2-qYCNhniks5usYXvgaJpZM4XCbmM .

r-lyeh commented 6 years ago

For reference, this is what UE4 does:

image

(upper: the yellow triangle is clickable and expands to bottom image; bottom: the titlebar is right-clickable and then you can click on "hide tab" to show upper image)

JackMcCallum commented 6 years ago

Thanks

On Wed, Nov 7, 2018 at 10:25 PM r-lyeh notifications@github.com wrote:

For reference, this is what UE4 does:

[image: image] https://user-images.githubusercontent.com/35402248/48128809-ea0d4280-e287-11e8-9ce8-0f274d048ada.png

(upper: the yellow triangle is clickable and expands to bottom image; bottom: the titlebar is right-clickable and then you can click on "hide tab" to show upper image)

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ocornut/imgui/issues/2109#issuecomment-436592778, or mute the thread https://github.com/notifications/unsubscribe-auth/AGQa7UaIlxYBxJkOuH0WHc_ICU9_hJDpks5ussMlgaJpZM4XCbmM .

ImBored commented 6 years ago

I've just implemented this in an older project and I love it! I have hit a small issue and hopefully there's something basic I'm missing?

I'm trying to save various preset layouts to disk as .ini files and load them back at run time to change the dock layout dynamically. To do this, I'm just saving the imgui.ini file as normal and copying it/creating duplicates. All good - at first run, when the default ini is loaded, dock layouts are loaded correctly and as expected.

However calling ImGui::LoadIniSettingsFromDisk(/ini file/) will place my windows correctly but undock every window. Is there anything extra that needs to be called at this time? I threw together a bare bones test as I wanted to remove any exotic things I may have been doing otherwise to sanity check.

    ImGuiViewport* viewport = ImGui::GetMainViewport();
    ImGui::SetNextWindowPos(viewport->Pos);
    ImGui::SetNextWindowSize(viewport->Size);
    ImGui::SetNextWindowViewport(viewport->ID);
    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
    window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
        ImGui::Begin("DockSpace Demo", &open, window_flags);
    ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
    ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), 0);
    ImGui::End();
    ImGui::Begin("Win_1", &open, 0);
    ImGui::Text("Text 1");
    if (ImGui::Button("Load")) {
        ImGui::LoadIniSettingsFromDisk(/*valid file path to a copy of a known working imgui.ini*/);
    }
    ImGui::End();

    ImGui::Begin("Win_2", &open, 0);
    ImGui::Text("Text 2");
    ImGui::End();

    ImGui::Begin("Win_3", &open, 0);
    ImGui::Text("Text 3");
    ImGui::End();

    ImGui::Begin("Win_4", &open, 0);
    ImGui::Text("Text 4");
    ImGui::End();
ejulien commented 6 years ago

From the LoadIniSettingsFrom* documentation: call after CreateContext() and before the first call to NewFrame() seems to imply that what you are trying to achieve cannot be done atm.

ImBored commented 6 years ago

ah, my bad. I missed that when looking through. Thanks for the reply

moonheart08 commented 6 years ago

Just a question, but is it possible to "restrict" which docks windows can be attached to? Dumb example: Say I want to restrict a few different windows to only being dock-able on the top or the bottom, not the sides.

ocornut commented 6 years ago

@moonheart08

Just a question, but is it possible to "restrict" which docks windows can be attached to? Dumb example: Say I want to restrict a few different windows to only being dock-able on the top or the bottom, not the sides.

Sorry this is not supported. Finding it a little odd to be honest, but it wouldn't be impossible to eventually support it through the ImGuiDockFamily mechanism. I personally won't likely work on that.

meshula commented 6 years ago

From my perspective, it's not a question of restricting by region (top, bottom), but of restricting by utility: Toolbar windows can only dock in the toolbar region; viewport windows in the viewport region, property editors in the property region. In my pre-dock-branch code, I managed this in my docking manager by allowing docks to reject panels. i.e. dragging a viewport into the toolbar was impossible because there was a bool accept_panel(Panel*) callback on the toolbar that would report "nope, I can't accommodate you".

ocornut commented 6 years ago

@meshula

restricting by utility:

This is what the SetNextWindowDockFamily/Dockspace/ImGuiDockFamily is trying to provide, but the feature-set and API probably need some further design and work.

Toolbar windows can only dock in the toolbar region; viewport windows in the viewport region, property editors in the property region.

This is a little ambiguous. If there is such thing as a "toolbar region", "viewport region" does it means that you are not allowing the user to create their own overall layout at all?

You can currently use the DockFamily system in two ways:

If what you stated below is actually what you need, you could imagine creating a "locked" dockspace (given a yet missing hypotherical ImGuiDockNodeFlags_LockLayout flag) and in each node create another dockspace means to receive only a certain type of window.

I think the constraints you stated are not actually exactly what you need, but it would be worth exploring this and see how/if the design needs to evolve further.

Similarly I think @moonheart08 request as stated might be a little misleading but if there is a useful user story behind it I am happy to dig into the idea further, just please provide more detailed use cases.


FYI all : there seems to a regression in 760c1d95b984f2b4bc2db6b7487a7e67674f1af5 which affects the Docking branch. The regression is a visible 1 frame flicker when dragging a docking node outside of the main viewport into a new viewport. However, while I could repro it yesterday I can't seem to repro it today, so there is maybe something at the framework/driver/os level that effects the repro. If that happens to you let me know. You may also checkout the previous commit 510f0e505c8014f67335643d3ca929be0e865bbe, if the bug affects you.

ocornut commented 6 years ago

It would be a good idea to add a button to hide the tab bar when there is only 1 tab in it, like in the unreal engine editor … [...] For reference, this is what UE4 does:

This has been in my list for a while, added it now: image

image

This is persisting value. I also experimented with the possibility of automatically enabling tab-bar collapse mode (with or without an explicit flag) but it raises more issues and confusion so I decided against it for now.

ocornut commented 5 years ago

@ImBored

I'm trying to save various preset layouts to disk as .ini files and load them back at run time to change the dock layout dynamically. To do this, I'm just saving the imgui.ini file as normal and copying it/creating duplicates. All good - at first run, when the default ini is loaded, dock layouts are loaded correctly and as expected. However calling ImGui::LoadIniSettingsFromDisk(/ini file/) will place my windows correctly but undock every window. Is there anything extra that needs to be called at this time? I threw together a bare bones test as I wanted to remove any exotic things I may have been doing otherwise to sanity check.

To be honest I am surprised you are not hitting a crash or assertion.. Just looking at e.g. LoadIniSettingsFromMemory() it has stuff like: IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); Which should assert early on.

This is not supported yet.. There is a very simple workaround which is to kill the imgui context and recreate it (basically call ShutdownContext,CreateContext and maybe some other glue/init stuff). You will lose temporary state such as the open/closed state of tree nodes but it may be good enough for your purpose.

There are DockBuilder* api for manipulating the docking state but I realize there is use for a simple "save layout" / "load layout" system. The problem is that the .ini state is not guaranteed to only contains layout data but I'll probably look into fixing the code to allow mid-reload. Either way you will probably need to perform reload before the NewFrame() call and not in the middle of the frame.

meshula commented 5 years ago

This is a little ambiguous. If there is such thing as a "toolbar region", "viewport region" does it means that you are not allowing the user to create their own overall layout at all?

Yes, that's right. I'm making little end user applications that have regions that are expected to be the same no matter whose desk you're running at, and "workspace" areas, where they can arrange panels to their liking to support their personal workflows. Whereas I could allow them to drag a color picker panel to fill the main viewport, that's not part of the intended design.

some features to help illustrate -

As near as I can tell, this is achievable already? I'm mostly trying to clarify the use case.

manuliner commented 5 years ago

@ImBored i managed to achieve dynamic layout switching.

see the following example:

you can build different layouts with the following functions and variables

ImGui::DockBuilderSplitNode()

ImGui::DockBuilderDockWindow()

bind changeLayout to a key to switch

this version should work but i couldn't test it. But this is how i achieve it


bool changeLayout_m = false;
bool  currentLayout_m = true;
ImGuiID dockSpaceId_m;  

void drawTestDock(){

    bool open = true;
    const char* dockspaceName;
    if (currentLayout_m)
    {
        dockspaceName = "layout1";
    }
    else
    {
        dockspaceName = "layout2";
    }

    dockSpaceId_m = ImGui::GetID(dockspaceName);

    ImGuiViewport* viewport = ImGui::GetMainViewport();
    ImGui::SetNextWindowPos(viewport->Pos);
    ImGui::SetNextWindowSize(viewport->Size);
    ImGui::SetNextWindowViewport(viewport->ID);

    ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar;
    window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove;
    window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;

    ImGui::Begin("Dock", &open, window_flags);

    if (currentLayout_m && changeLayout_m)
    {
        ImGui::DockBuilderRemoveNode(dockSpaceId_m);
        ImGui::DockBuilderAddNode(dockSpaceId_m, viewport->Size);
        ImGuiID dock_main_id = dockSpaceId_m;

        const ImGuiID james1 = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.35f, NULL, &dock_main_id);
        const ImGuiID james3 = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.40f, NULL, &dock_main_id);

        ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.65f, NULL, &dock_main_id);

        ImGui::DockBuilderDockWindow("james2", dock_main_id);
        ImGui::DockBuilderDockWindow("james1", james1);
        ImGui::DockBuilderDockWindow("james3", james3);

        ImGui::DockBuilderFinish(dockSpaceId_m);
    }
    else
    {
        ImGui::DockBuilderRemoveNode(dockSpaceId_m); // Clear out existing layout
        ImGui::DockBuilderAddNode(dockSpaceId_m, viewport->Size); // Add empty node

        ImGuiID dock_main_id = dockSpaceId_m;
        ImGuiID james5 = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Right, 0.4f, NULL, &dock_main_id);
        ImGuiID james2 = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 0.6f, NULL, &dock_main_id);
        ImGuiID james6 = ImGui::DockBuilderSplitNode(james5, ImGuiDir_Right, 0.5f, NULL, &james5);

        const ImGuiID james3 = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Down, 0.40f, NULL, &dock_main_id);
        const ImGuiID james4 = ImGui::DockBuilderSplitNode(james5, ImGuiDir_Down, 0.5f, NULL, &james5);
        const ImGuiID james1 = ImGui::DockBuilderSplitNode(james6, ImGuiDir_Down, 0.5f, NULL, &james6);

        ImGui::DockBuilderDockWindow("james2", dock_main_id);
        ImGui::DockBuilderDockWindow("james5", james5);
        ImGui::DockBuilderDockWindow("james4", james4);
        ImGui::DockBuilderDockWindow("james6", james6);
        ImGui::DockBuilderDockWindow("james1", james1);
        ImGui::DockBuilderDockWindow("james3", james3);
        ImGui::DockBuilderFinish(dockSpaceId_m);
    }

    ImGui::DockSpace(dockSpaceId_m, ImVec2(0.0f, 0.0f), 0);
    ImGui::End();

    if (currentLayout_m)
    {
        ImGui::Begin("james1");
        ImGui::Text("james1");
        ImGui::End();

        ImGui::Begin("james2");
        ImGui::Text("james2");
        ImGui::End();

        ImGui::Begin("james3");
        ImGui::Text("james3");
        ImGui::End();
    }
    else
    {
        ImGui::Begin("james1");
        ImGui::Text("james1");
        ImGui::End();

        ImGui::Begin("james2");
        ImGui::Text("james2");
        ImGui::End();

        ImGui::Begin("james3");
        ImGui::Text("james3");
        ImGui::End();

        ImGui::Begin("james4");
        ImGui::Text("james4");
        ImGui::End();

        ImGui::Begin("james5");
        ImGui::Text("james5");
        ImGui::End();

        ImGui::Begin("james6");
        ImGui::Text("james6");
        ImGui::End();
    }
 }
manuliner commented 5 years ago

@ocornut by the way docking is working fine on windows and in combination with openFrameworks ( multi viewport not tested)

moonheart08 commented 5 years ago

Main point of my suggustion was to prevent, say, a subwindow with it's own docking point having it's windows dragged to the main one. Say for example i have a UI for some random object in my game, and it has docking support. I dont want them to dock the object's UI somewhere else, but I want to allow joining together various objects into a single tabbed window to save space for the user.

ocornut commented 5 years ago

@meshula

As near as I can tell, this is achievable already? I'm mostly trying to clarify the use case.

I think you can do some of that with ImGuiDockFamily and by embedding dockspace within each others, + using a (yet non-existing) ImGuiDockNodeFlags_NoLayoutChanges to lock a dockspace. So your top-most layout would be a programmatically created and dockspace. I would suggest try to toy around with ImGuiDockFamily and see where you get.

@moonheart08 The SetNextWindowDockFamily() and ImGuiDockFamily may help you handle that.

ocornut commented 5 years ago

I also made a change that answers some of the recurrent queries, which is that using SetNextWindowDockID() given a node that is split (e.g. the root node of a dockspace) will now automatically find the central node (or the last focused node for an implicit dockspace).

aCuria commented 5 years ago

Is it possible to do centralization of content in the docked area?

image

ocornut commented 5 years ago

@aCuria

Is it possible to do centralization of content in the docked area?

This is not a docking question, the same would happen with a regular window sized the same. There's no mechanism for centering whole groups of elements. If you only have a single image you can center it yourself given the window bounds. Please open a new topic for similar question not related to docking, thank you.

codz01 commented 5 years ago

just tested the current docking branch but it crashes this way imgui

this is the assertion message imgui

ocornut commented 5 years ago

Thank you @codz01, I added added this assert recently to catch the issue earlier in the pipeline and got sidetracked when I was looking for a repro. You found it :) I have pushed a fix now.

codz01 commented 5 years ago

thanks

izirayd commented 5 years ago

8825e-clip-449kb

lel

ocornut commented 5 years ago

Hello,

FYI i have been steadily pushing additions/fixes over time here.

If you use Docking keep the feedback coming (prefer to open new thread for specific bug/request, to avoid making this one thread growing forever).

(1) One thing I just pushed is I changing some code to make it possible for the (currently internal and wip) DockBuilderAddNode() api to create floating docking node which wasn't possible earlier.

e.g.

static bool display = false;
ImGui::Checkbox("Display", &display);
if (ImGui::Button("Redock"))
{
    ImGuiID dock_id = ImGui::DockBuilderAddNode(0, ImGuiDockNodeFlags_None);
    ImVec2 viewport_pos = ImGui::GetMainViewport()->Pos;
    ImGui::DockBuilderSetNodePos(dock_id, ImVec2(viewport_pos.x + 100, viewport_pos.y + 100));
    ImGui::DockBuilderSetNodeSize(dock_id, ImVec2(200, 200));
    ImGui::DockBuilderDockWindow("AAAA", dock_id);
    ImGui::DockBuilderDockWindow("BBBB", dock_id);
    ImGui::DockBuilderDockWindow("CCCC", dock_id);
    ImGui::DockBuilderFinish(dock_id);
}
if (display)
{
    ImGui::Begin("AAAA");
    ImGui::Text("This is AAAA");
    ImGui::End();
    ImGui::Begin("BBBB");
    ImGui::Text("This is BBBB");
    ImGui::End();
    ImGui::Begin("CCCC");
    ImGui::Text("This is CCCC");
    ImGui::End();
}

If you used DockBuilderAddNode() with dockspace before (unlikely) take note that I have removed the size parameters from it, and you'll need to call it with the ImGuiDockNodeFlags_Dockspace flag. For various reason at the moment a node cannot be split if it doesn't know its size, so if you use DockBuilderAddNode + DockBuilderSplitNode you need to call DockBuilderSetSize in between.


(2) One of my internal test bed has been trying to reproduce the behavior/model of Unreal Editor. I'm posting an experiment patch to example_win31_directx11/main.cpp (main library not affected) for those interested in exploring some advanced functionalities but this is super raw and as-is.

This may be de-facto a good reference for advanced uses of ImGuiWindowClass or the DockBuilderXXX API (which is super in flux at the moment).

Jan 31, 2019: unreal_mdi_45.zip UPDATE 2022/08/22: imgui_unreal_style_mdi_v52.zip Moved to #6487

Here's a screenshot:

image

What it is doing, super roughly: