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

Docking branch available for testing #2109

Open
ocornut opened this issue Oct 1, 2018 · 125 comments

Comments

Projects
None yet
@ocornut
Copy link
Owner

commented Oct 1, 2018

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

This comment has been minimized.

Copy link
Owner Author

commented Oct 1, 2018

This is my second version of Docking (I developed a V1 during the year which didn't get released, and which I have completely scrapped, then a rebuilt a V2 from stratch. V2 has issues but is already very useful.). The development of this feature has been sponsored by Blizzard Entertainment and supporters on Patreon.

The current branch scheme is docking > viewport > master.
This is because docking is best used with multi-viewport enabled (also a Beta feature) and it is less maintenance for me to follow this arrangement. It is however possible that Docking features may eventually be merged into master before viewport features. Right now I am hoping that both Docking and Viewport would become stable and useful enough to constitute a 1.70 release, but that will depend on your feedback.

The basic design is based on what Visual Studio do. When creating an explicit DockSpace() node, there is a Central Node which is a node that doesn't fold even when empty (when nothing is docked into it). It also uses the remaining/available space.

I'm also working on various features to manipulate dock nodes programmatically allowing to replicate what Unreal Engine is doing (each top-level editor have its own layout, and layout are copied/forked on demand). Some of those features are currently only exposed in imgui_internal.h but will slowly make it into the public API (they currently need a bit of rework).

Preamble

UPDATING

  • This is unrelated to the docking branch, but please note that the contents of imgui.cpp files have been massively shuffled in imgui 1.64+1.65. If you have ANY modifications to imgui.cpp and you are updating from a version OLDER than 1.64, please read the release note for 1.64. Otherwise it should be safe and easy to update.
  • If you use this branch I would appreciate if you tried to update regularly or semi-regularly so you can provide feedback and help detect regression.
  • Some of the API are subject to change, but luckily the majority of stuff here uses no API. If you are not at ease with fixing for minor API changes from time to time, wait until this branch is more stable and come back e.g. a few months.
  • If you end up going back and forth between multiple branches, there are a few #define such as IMGUI_HAS_VIEWPORT and IMGUI_HAS_DOCK which you might use to keep user code compiling in multiple branches.

FEEDBACK

  • If it works for you, let me know! You can also post screenshot/video in #2265 showcasing your use of the docking system.
  • Feedback/issues/bug reports are super welcome. Please be aware that the docking system is (unfortunately) more complex than it looks like and it is easy to convey wrong or misleading information with written words. If you make a report, please be extra careful and thorough with your wording and try to provide detailed information, git hash, pseudo-code, repro, screenshots, copy of .ini file. It's very easy to be unable to understand or reproduce a bug. The order of actions matters. your persistent settings (.ini file) matters, the order of submission of windows and function calls matters, etc. One advice is that if you stumble on an assert/crash, save your callstack and make a copy of your ini file, this will make it easier for you to reproduce and describe your issue.
  • Please try to make a Minimal, Complete and Verifiable Example (MCVE) to demonstrate your problem. An ideal submission is code I can paste in one of the example app to reproduce the issue. It can be tedious but it is also very often the more efficient way to understand and communicate issues. If for some reason you can't provide precise information but you have an issue, feel free to post anyway with what you know. Please read Contributing and try to make an effort to describe things properly and be considerate of my workload.

CONFIGURATION

  • Set the master enable flag io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; to enable docking.
  • Option: io.ConfigDockingWithShift = true; [current default*] → Dock when holding SHIFT, click anywhere in a window and drop anywhere on another window. Less visually noisy because the docking overlay only appears when holding Shift. Tends to be suitable for applications with loose windows and lots of moving.
  • or: io.ConfigDockingWithShift = false; → Dock without holding SHIFT, drag from title bar, to title bar or drop nodes. Tends to be suitable for applications where the majority of windows will be always docked because moving floating window is rare and therefore the overlay is not visually noisy.

* the current default is a little unexpected and may be changed in the future. I would recommend that you try it before toggling the option.

DESIRABLE (NOT STRICTLY DOCKING) FEATURES TO ENABLE ALONG WITH DOCKING

Note: All of this is enabled if you try one of the standard examples e.g. example_win32_directx11/, example_glfw_opengl3/, example_glfw_vulkan/, etc. Those paragraphs are useful if you want to integrate docking in your own application with your own back-end.

  1. Mouse cursors
    Your back-end needs to honor the resizing mouse cursors (ImGui::GetMouseCursor() returning ImGuiMouseCursor_ResizeNS / ImGuiMouseCursor_ResizeEW). Most default back-ends are honoring this already, but if you have implemented your custom back-end, it would be preferable you add support for this. Without those cursors changes you will have less feedback when hovering the small splitter between docking nodes. See the back-ends in examples/ for reference code. Your back-end needs to set io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; to notify imgui that this is supported.

  2. Resizing windows from their edge.
    You'll probably also want to enable io.ConfigResizeWindowsFromEdges = true; to allow resizing windows from their edge, providing a more consistent experience between docked and floating windows. This is not currently enabled by default because of (1). When you hover the edge of a window, the most noticeable feedback is the change of mouse cursor. This will eventually be enabled by default when ImGuiBackendFlags_HasMouseCursors is set by the back-end.

  3. Multi-Viewports
    The Docking feature obviously works more nicely when multi-viewports are enabled (#1542), allowing you to take take anything outside the boundaries of your main OS window. Though it is not required, and Docking works without multi-viewports. You can play with viewports by compiling most of the examples/ app under Windows (with either viewport or docking branch). Depending on the back-end and/or VM, viewports appears to be broken under Linux, your help would be welcome if you are a Linux user.
    For your own application to support multi-viewport, please refer to #1542 + read the comments in imgui.h for ImGuiPlatformIO + read the main.cpp of the examples/ application and the platform and renderer example bindings (e.g. imgui_impl_win32.cpp + imgui_imgui_dx11.cpp).
    Please note that IF you enable viewports io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; the coordinate system changes to follow your OS natural coordinate system, so e.g. (0,0) will always be the top-left of your primary monitor under Windows, etc. If you are using any absolute coordinates when calling e.g. SetNextWindowPos(), you should use GetMainViewport()->Pos as a base position instead.
    If you are interested in enabling multi-viewports, be aware that unlike Docking which requires no changes in the bindings, the integration of Multi-Viewport into your back-end is a little complicated and involved.
    For reference: this is the showing the part that is handled by the multi-viewports feature: taking imgui windows out of your main os window (it is creating new os windows and graphical context on the fly. This is done using a single imgui context, you can drag and drop stuff between windows etc.):
    viewport_201810

(if you have any issue related to viewports please post in #1542)


Docking

The core Docking feature requires no new API. Whenever you are moving a window you can merge them into each others and split nodes. When io.ConfigDockingWithShift = true the docking happens when holding SHIFT (see the "Configuration" blurb above).

Merging windows:
docking_201810_base-01

You can play around with that anywhere in the demo, the merging of windows itself is super useful (even without any of the "splitting" feature usually associated to Docking).

Settings and docking trees are persisting and references are tracked under the hood so when you restore a window it should be where you expect it to be:

docking_201810_base-02

There are various useful subtleties and complications associated to the persistence model which are not conveyed in the GIF. I'm not going to detail everything right now, but basically if you have any feedback or issue, please report them (there WILL be issues).

If you have keyboard navigation enabled, you can use CTRL+Tab to cycle through windows with the keyboard:

ctrl_tab

You can re-order tabs by dragging them horizontal. You can click the button on the upper-left corner to open a menu listing all the tabs by name. You can click and drag this button to undock an entire node when it is docking (so you can redock it elsewhere):

docking_201810_base-03

What should persist:

  • Docking hierarchy / configuration (note that if a dock node doesn't have active windows it is automatically folded)
  • Tab order for each visible dock node
  • Merged windows

What doesn't persist on restart:

  • Selected tab for each visible dock node: TODO
  • Window visibility (this is up to your application to store e.g. the associated bool and execute the code or not).
  • Last focused window (this is currently also missing in the master branch, will be added. It's not a big issue)

API

As pointed out above many operation can be done without any API.

(This is not a complete documentation, merely enough to get you started with basic uses. More advanced programmatic manipulation of docking are currently exposed only in imgui_internal.h as ImGui::DockBuilderXXX and will be reworked into a public-facing API)

There is ImGui::DockSpace() call which allows you to create an explicit docking node within an existing window. If you have a full-on editor this is useful to build your main application around a menu-bar etc. Simpler applications will be using docking functions without ever calling DockSpace(). This function comes with one fundamental constraint: anything you dock into an explicit DockSpace() node needs to be submitted after it, so you’ll have to structure your app around that one limitation. It is also generally expected that your dockspaces, if you have any, persist at all time.

DockSpace() create by default a "Central Node" which is a node that doesn't fold/disappear even if no windows are docked into it. There are a few properties associated to this which I'll detail later.

Using some combination of flag ImGuiDockNodeFlags_PassthruCentralNode (WAS ImGuiDockNodeFlags_PassthruDockspace) you can make the central node invisible and inputs pass-thru. See the Demo>Dockspace options for details and this comment.

image

As with regular window, if you call IsItemHovered() or other "Item" function right after your call to Begin(), the title-bar or tab is used as a reference item. You can use this to create context-menu over title bars or tabs:

image

There is a ImGuiWindowFlags_NoDocking to disable the ability for a window to be involved in docking. Menus, popups etc. are not dockable by default.

The SetNextWindowDockFamily() API, which doesn't have a demo yet, allows you to make some windows dockable into to another set of windows. In particular, if you have multiple dockspace with tools related to a given editor or document, you may want each dockspace to use a unique family so windows from one cannot be docked into the other.

There are 2 new demos:
Demo Window → Examples → Documents
Demo Window → Examples → Dockspace
However as pointed above many of the features do not need those demos.
In fact, the “Dockspace” demo is pretty much only a call to DockSpace() + a bunch of comments.

Picture: This is a dockspace, the upper-right node is the empty document root node

image

// Docking 
// [BETA API] Enable with io.ConfigFlags |= ImGuiConfigFlags_DockingEnable.
// Note: you DO NOT need to call DockSpace() to use most Docking facilities! 
// To dock windows: hold SHIFT anywhere while moving windows (if io.ConfigDockingWithShift == true) or drag windows from their title bar (if io.ConfigDockingWithShift = false)
// Use DockSpace() to create an explicit dock node _within_ an existing window. See Docking demo for details.
void    DockSpace(ImGuiID id, const ImVec2& size = ImVec2(0, 0), ImGuiDockNodeFlags flags = 0, const ImGuiDockFamily* dock_family = NULL);
void    SetNextWindowDockId(ImGuiID dock_id, ImGuiCond cond = 0);           // set next window dock id (FIXME-DOCK)
void    SetNextWindowDockFamily(const ImGuiDockFamily* dock_family);        // set next window user type (docking filters by same user_type)
ImGuiID GetWindowDockId();
bool    IsWindowDocked();                                                   // is current window docked into another window? 
// Flags for ImGui::DockSpace()
enum ImGuiDockNodeFlags_
{
ImGuiDockNodeFlags_None                         = 0,
ImGuiDockNodeFlags_KeepAliveOnly                = 1 << 0,   // Don't display the dockspace node but keep it alive. Windows docked into this dockspace node won't be undocked.
//ImGuiDockNodeFlags_NoCentralNode              = 1 << 1,   // Disable Central Node (the node which can stay empty)
ImGuiDockNodeFlags_NoDockingInCentralNode       = 1 << 2,   // Disable docking inside the Central Node, which will be always kept empty. Note: when turned off, existing docked nodes will be preserved.
ImGuiDockNodeFlags_NoSplit                      = 1 << 3,   // Disable splitting the node into smaller nodes. Useful e.g. when embedding dockspaces into a main root one (the root one may have splitting disabled to reduce confusion). Note: when turned off, existing splits will be preserved.
ImGuiDockNodeFlags_NoResize                     = 1 << 4,   // Disable resizing child nodes using the splitter/separators. Useful with programatically setup dockspaces. 
ImGuiDockNodeFlags_PassthruCentralNode          = 1 << 5,   // Enable passthru dockspace: 1) DockSpace() will render a ImGuiCol_WindowBg background covering everything excepted the Central Node when empty. Meaning the host window should probably use SetNextWindowBgAlpha(0.0f) prior to Begin() when using this. 2) When Central Node is empty: let inputs pass-through + won't display a DockingEmptyBg background. See demo for details.
ImGuiDockNodeFlags_AutoHideTabBar               = 1 << 6    // Tab bar will automatically hide when there is a single window in the dock node.
};

If you have multiple dockspace each behind their own tabs (aka nested tabs), the ImGuiDockNodeFlags_KeepAliveOnly flag is useful to specify that a DockSpace is still alive even though it it is hidden and not being fully submitted. Otherwise, submitted windows that are docked into the hidden dockspace would normally undock themselves (as they lost their parent).

Tab Bars and Tabs

The docking system is in charge of creating tabs, but you can use the tab-bar/tabs system as regular low-level widgets, unrelated to Docking.

if (ImGui::BeginTabBar("blah"))
{
    if (ImGui::BeginTabItem("Video"))
    {
        ImGui::Text("Blah blah");
        ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Audio"))
    {
        ImGui::EndTabItem();
    }
    if (ImGui::BeginTabItem("Controls"))
    {
        ImGui::EndTabItem();
    }
    ImGui::EndTabBar();
}

image

API

bool  BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0);        // create and append into a TabBar
void  EndTabBar();
bool  BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags = 0);// create a Tab. Returns true if the Tab is selected.
void  EndTabItem();                                                       // only call EndTabItem() if BeginTabItem() returns true!
void  SetTabItemClosed(const char* tab_or_docked_window_label);           // notify TabBar or Docking system of a closed tab/window ahead (useful to reduce visual flicker on reorderable tab bars). For tab-bar: call after BeginTabBar() and before Tab submissions. Otherwise call with a window name.
// Flags for ImGui::BeginTabBar()
enum ImGuiTabBarFlags_
{
    ImGuiTabBarFlags_None                           = 0,
    ImGuiTabBarFlags_Reorderable                    = 1 << 0,   // Allow manually dragging tabs to re-order them + New tabs are appended at the end of list
    ImGuiTabBarFlags_AutoSelectNewTabs              = 1 << 1,   // Automatically select new tabs when they appear
    ImGuiTabBarFlags_NoCloseWithMiddleMouseButton   = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
    ImGuiTabBarFlags_NoTabListPopupButton           = 1 << 3,
    ImGuiTabBarFlags_NoTabListScrollingButtons      = 1 << 4,
    ImGuiTabBarFlags_FittingPolicyResizeDown        = 1 << 5,   // Resize tabs when they don't fit
    ImGuiTabBarFlags_FittingPolicyScroll            = 1 << 6,   // Add scroll buttons when tabs don't fit
    ImGuiTabBarFlags_FittingPolicyMask_             = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll,
    ImGuiTabBarFlags_FittingPolicyDefault_          = ImGuiTabBarFlags_FittingPolicyResizeDown
};  

// Flags for ImGui::BeginTabItem()
enum ImGuiTabItemFlags_
{
    ImGuiTabItemFlags_None                          = 0,
    ImGuiTabItemFlags_UnsavedDocument               = 1 << 0,   // Append '*' to title without affecting the ID, as a convenience to avoid using the ### operator. Also: tab is selected on closure and closure is deferred by one frame to allow code to undo it without flicker.
    ImGuiTabItemFlags_SetSelected                   = 1 << 1,   // Trigger flag to programatically make the tab selected when calling BeginTabItem()
    ImGuiTabItemFlags_NoCloseWithMiddleMouseButton  = 1 << 2,   // Disable behavior of closing tabs (that are submitted with p_open != NULL) with middle mouse button. You can still repro this behavior on user's side with if (IsItemHovered() && IsMouseClicked(2)) *p_open = false.
    ImGuiTabItemFlags_NoPushId                      = 1 << 3    // Don't call PushID(tab->ID)/PopID() on BeginTabItem()/EndTabItem()
};

See the demo under Demo->Layout->Tabs

image

And Demo->Examples Menu->Documents

image

image

Standalone tab-bars (not part of the docking system) are not currently saving their order/selected persistently.

TODO

not an exhaustive list, posted for informative pupose

  • A~ Unreal style document system (requires low-level controls of dockspace serialization fork/copy/delete): this is mostly working but the DockBuilderXXX api are not exposed/finished --> proof of concept demo posted.
  • dock: A: it should be possible with dock builder to build a floating node.
  • dock: B: when docking outer, perform size locking on neighbors nodes the same way we do it with splitters, so other nodes are not resized
  • dock: B~ central node resizing behavior incorrect.
  • dock: B~ central node ID retrieval API?
  • dock: B: changing title font/style per-window is not supported as dock nodes are created in NewFrame.
  • dock: B- debug full rebuild loses viewport of floating dock nodes
  • dock: B- dock node inside its own viewports creates 1 temporary viewport per window on startup before ditching them (doesn't affect the user nor request platform windows to be created, but unnecessary)
  • dock: B- resize sibling locking behavior may be less desirable if we merged same-axis sibling in a same node level?
  • dock: B- single visible node part of a hidden split hierarchy (OnlyNodeWithWindows != NULL) should show a normal title bar (not a tab bar)
  • dock: B~ tidy up tab list popup buttons features (available with manual tab-bar, see ImGuiTabBarFlags_NoTabListPopupButton code, not used by docking nodes)
  • dock: B- inconsistent clipping/border 1-pixel issue (trickier to solve that it looks).
  • dock: B- fix/disable auto-resize grip z-order on split host nodes.
  • dock: B- SetNextWindowFocus() doesn't seem to apply if the window is hidden this frame, need repro.
  • dock: B- resizing a dock tree small currently has glitches (overlapping collapse and close button, etc.)
  • dock: B- dpi: look at interaction with the hi-dpi and multi-dpi stuff.
  • dock: B- tab bar: appearing on first frame with a dumb layout would do less harm that not appearing? (when behind dynamic branch) or store titles + render in EndTabBar()
  • dock: B- tab bar: make selected tab always shows its full title?
  • dock: B- tab bar: the order/focus restoring code could be part of TabBar and not DockNode? (#8)
  • dock: B- dockspace: flag to lock the dock tree and/or sizes
  • dock: B- reintroduce collapsing a floating dock node. also collapsing a docked dock node!
  • dock: B- allow dragging a non-floating dock node by clicking on the title-bar-looking section (not just the collapse/menu button)
  • dock: B- option to remember undocked window size? (instead of keeping their docked size) (relate to #2104)
  • dock: C- nav: CTRL+TAB highlighting tabs shows the mismatch between focus-stack and tab-order (not visible in VS because it doesn't highlight the tabs)
  • dock B- nav: being able to dock/undock with gamepad controls?
  • dock: C- after a dock/undock, the Scrollbar Status update in Begin() should use an updated e.g. size_y_for_scrollbars to avoid a 1 frame scrollbar flicker.
  • dock: A- implicit, invisible per-viewport dockspace to dock to.
  • dock: B- DockSpace() border issues

@ocornut ocornut changed the title Docking branch available (Alpha) Docking branch available for testing Oct 1, 2018

@ocornut ocornut referenced this issue Oct 1, 2018

Closed

Tabs #261

@Ylannl

This comment has been minimized.

Copy link

commented Oct 1, 2018

The viewport part does not seem to be working properly on macOS with the glfw_opengl3 example. If I disable ImGuiConfigFlags_ViewportsEnable, the docking works fine. (commit 2cff3f6)

output

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 1, 2018

@Ylannl Thanks! Anything related to viewport please report in the viewport thread #1542 (**edit: for Mac/Linux please report to #2117). By the look of it, it looks like the clipping rectangles are not projected from the global coordinates space to each window’s space. Will have to inspect but last time I tried the glfw+gl demo on Mac OSX it worked for me. Will try again when I have the chance.

@rokups

This comment has been minimized.

Copy link
Contributor

commented Oct 2, 2018

Hey this is great news! Is there a way to set initial position of the docked window? I am looking to have some kind of default placement of newly opened windows.

@bsviglo

This comment has been minimized.

Copy link

commented Oct 2, 2018

Hey this is great news! Is there a way to set initial position of the docked window? I am looking to have some kind of default placement of newly opened windows.

Same question. Having ability to setup default layout either by default setting or programmatically will be really helpful.
Other than that look awesome. Many Thanks.

ocornut added a commit that referenced this issue Oct 2, 2018

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 2, 2018

Hey this is great news! Is there a way to set initial position of the docked window? I am looking to have some kind of default placement of newly opened windows.

All the programmatic stuff are currently hidden in imgui_internal.h so please bear in mind that those API will evolve.

Given dockspace_id (same value passed to DockSpace())
Given dockspace_size (which currently cannot be zero, unlike the one passed to DockSpace()

EDITED Oct 6: Removed ImGuIContext* ctx parameter
EDITED Feb 20: Updated to current internal api

You can create an initial layout, e.g.

ImGui::DockBuilderRemoveNode(dockspace_id); // Clear out existing layout
ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_Dockspace); // Add empty node
ImGui::DockBuilderSetNodeSize(dockspace_id, dockspace_size);

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_prop = ImGui::DockBuilderSplitNode(dock_main_id, ImGuiDir_Left, 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("Log", dock_id_bottom);
ImGui::DockBuilderDockWindow("Properties", dock_id_prop);
ImGui::DockBuilderDockWindow("Mesh", dock_id_prop);
ImGui::DockBuilderDockWindow("Extra", dock_id_prop);
ImGui::DockBuilderFinish(dockspace_id);

In order to only call it once you may use e.g.

if (ImGui::DockBuilderGetNode(dockspace_id) == NULL)
   MyEditor_LayoutPreset();

(Note that I just pushed a fix for DockBuilderSplitNode for a bug I introduced yesterday)

image

@Ylannl

This comment has been minimized.

Copy link

commented Oct 2, 2018

I've just tested the GLFW + OpenGL3 example (branch docking, commit 2cff3f6) on macOS 10.12 and I have not the problem presented by @Ylannl as shown in the following GIF

@Alzathar Interesting! Do you have a retina screen? I do, perhaps that is related? I'm on macOS 10.14 btw, but also noticed this on the viewport branch on macOS 10.13.

@ocornut Thanks for the great work on ImGui. I'm very exited about these new features! Sorry for posting in the wrong thread.

@rokups

This comment has been minimized.

Copy link
Contributor

commented Oct 2, 2018

@ocornut creating initial dock layout is clear, but what about setting initial dock placement of new window in already existing layout? Do we have to rebuild entire layout to dock new window into existing layout?

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 2, 2018

creating initial dock layout is clear, but what about setting initial dock placement of new window in already existing layout? Do we have to rebuild entire layout to dock new window into existing layout?

@rokups You can use DockBuilderDockWindow() or SetNextWindowDockId(). The problem you will have is that you can not easily identify a docking node from an already split state. So the "where" parameter is tricky to define.

If you are building the whole hierarchy you have the ID, otherwise you can use e.g. GetWindowDockId() to get an ID from a window.

Another possibility (which I was using in Docking V1) would be to tag node on creation, and being later able to find a dock node id given the tag, The problem is that node that can merged/split by the user. To be able to track through merge we need node to be able to carry multiple tags.

Take some time to think about the problem and let me know if you have more ideas.

@Alzathar @Ylannl Please move viewport related discussion to the viewport thread.

@rokups

This comment has been minimized.

Copy link
Contributor

commented Oct 2, 2018

Thanks, i got it working easily. Not sure how i missed that. And i hopefully last question: is there a way to set next window dock position to an empty undocked space? I suspect i might need to iterate through ImGuiDockNode* tree but i am not sure how to identify undocked space.
2018-10-02_19-28

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 2, 2018

@rokups Do you actually want "an empty undocked space" (there can be only one) or do you want the DocRoot node (which happens to be the only node that can be empty, but it might have other windows docking). What would do do if there's already a window in there?

@rokups

This comment has been minimized.

Copy link
Contributor

commented Oct 2, 2018

I think i want "empty undocked space". I tried SetNextWindowDockId(dockRoot, ..) but it just does not dock window anywhere so that must be wrong. What i want to do is dock window into empty space if there is any, if not - dock window as a tab to the largest docked tab. That second part is already working great.

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 2, 2018

I tried SetNextWindowDockId(dockRoot, ..) but it just does not dock window anywhere

That's exactly what this function does, dock windows. If you believe you are running into an issue try to build a repro.

I think i want "empty undocked space". [...] What i want to do is dock window into empty space if there is any, if not - dock window as a tab to the largest docked tab.

The DocRoot node (the grey node) is essentially meant to represent that space (If there is an empty node it WILL be the DocRoot node) and it is up to the user to decide where it goes and how big it this. This matches VS behavior, rather than just looking for the "largest docked tab". It also behave differently sizing wise (if you resize your host OS windows you'll find that out).

You should probably either use the DocRoot node (which ID is not possible to query yet, haha) or track the current DockID of the LATEST focused document/tool (up to you how to define document/tool) and use this DockID to dock new windows, I think either would make more sense to the user.

EDIT You should never have to iterate the dock node hierarchy yourself, but we could provide helper functions to find DockID under different criteria: DocRoot node, largest node, last focused node, dock where a given window is hosted, with or without DockFamily filter, etc.

EDIT2: The DocRoot node is now called CentralNode

@codz01

This comment has been minimized.

Copy link

commented Oct 2, 2018

great news :D , it works perfectly on windows
Note : for mingw users , you have to add :
#define WINVER 0x0601
#define _WIN32_WINNT 0x0601
on top of include <windows.h>
this is only to visible the function SetProcessDPIAware

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 2, 2018

Note : for mingw users , you have to add :
#define WINVER 0x0601
#define _WIN32_WINNT 0x0601
on top of include <windows.h>

I think you need to add those defines are program-wide defines if you use Mingw.

@Dingobloo

This comment has been minimized.

Copy link

commented Oct 3, 2018

Took a bit of work going through the examples to update my application because I hadn't updated imgui in a while but docking and viewport seems to work just fine on OSX High Sierra (10.13.6) SDL2 + OpenGL2. The only slightly unintuitive bit is that I cannot interact with the translate gizmo if the window is undocked (it just drags the whole window) but that's likely my doing.

dockingsmall

@rokups

This comment has been minimized.

Copy link
Contributor

commented Oct 3, 2018

@ocornut i created a small testcase of what i attempted. Please take a look.

		static int initialized = 0;
		static int new_window = 0;
		ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar;
		flags |= ImGuiWindowFlags_NoDocking;
		ImGuiViewport* viewport = ImGui::GetMainViewport();
		ImGui::SetNextWindowPos(viewport->Pos);
		ImGui::SetNextWindowSize(viewport->Size);
		ImGui::SetNextWindowViewport(viewport->ID);
		ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
		flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
		flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
		ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
		ImGui::Begin("DockSpace Demo", 0, flags);
		ImGui::PopStyleVar();

		if (ImGui::BeginMenuBar())
		{
			if (initialized == 0)
			{
				if (ImGui::Button("1. Initialize"))
					initialized = 1;
			}
			if (initialized > 0 && new_window == 0)
			{
				if (ImGui::Button("2. New Window"))
					new_window = 1;
			}
			ImGui::EndMenuBar();
		}

		ImGuiIO& io = ImGui::GetIO();
		ImGuiID dockspace_id = ImGui::GetID("MyDockspace");

		if (initialized == 1)
		{
			initialized = 2;
			ImGuiContext* ctx = ImGui::GetCurrentContext();
			ImGui::DockBuilderRemoveNode(ctx, dockspace_id); // Clear out existing layout
			ImGui::DockBuilderAddNode(ctx, dockspace_id, ImGui::GetMainViewport()->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_prop = ImGui::DockBuilderSplitNode(ctx, dock_main_id, ImGuiDir_Left, 0.20f, NULL, &dock_main_id);
			ImGuiID dock_id_bottom = ImGui::DockBuilderSplitNode(ctx, dock_main_id, ImGuiDir_Down, 0.20f, NULL, &dock_main_id);

			ImGui::DockBuilderDockWindow(ctx, "Log", dock_id_bottom);
			ImGui::DockBuilderDockWindow(ctx, "Properties", dock_id_prop);
			ImGui::DockBuilderFinish(ctx, dockspace_id);
		}

		ImGui::DockSpace(dockspace_id);
		if (initialized == 2)
		{
			ImGui::Begin("Properties");
			ImGui::End();

			ImGui::Begin("Log");
			ImGui::End();
		}

		if (new_window == 1)
		{
			// Should dock window to empty space, instead window is not docked anywhere.
			ImGui::SetNextWindowDockId(dockspace_id, ImGuiCond_Once);
			ImGui::Begin("New Window");
			ImGui::End();
		}

		ImGui::End();
		ImGui::PopStyleVar();

Click buttons in the menu bar to 1. initialize default layout and 2. create new window and dock it into empty space. Window is created but not docked anywhere. Do i do it wrong or is this a bug?

And another question: is there a way to detect presence of this free undocked space?

@nem0

This comment has been minimized.

Copy link
Contributor

commented Oct 3, 2018

I want to make a dockspace transparent, while windows docked in it should keep their style. I tried many ways, e.g.

	ImGui::SetNextWindowBgAlpha(0);
	ImGui::Begin("DockSpace Demo", 0, flags);

	ImGuiID dockspace_id = ImGui::GetID("MyDockspace");
	ImGui::DockSpace(dockspace_id);

	ImGui::End();

however, all I managed to do is that everything became transparent. Is there a way to this?

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Oct 3, 2018

I want to make a dockspace transparent, while windows docked in it should keep their style.

I am working on this right now, it should be available during the day.
It also involve passing inputs through.

@codz01

This comment has been minimized.

Copy link

commented Oct 3, 2018

the dockspace's scrollbar is visible , is it normal ?
gif

ocornut added a commit that referenced this issue Oct 3, 2018

Docking: Fixed DockSpace() child window displaying a scrollbar behind…
… the node backgrounds. Rename ImGuiCol_DockingBg to ImGuiCol_DockingEmptyBg. Added ImGuiDockNode::IsLeaftNode(). (#2109)

ocornut added a commit that referenced this issue Apr 19, 2019

Docking: Fixed an issue where DockBuilderSplitNode() wouldn't update …
…the CentralNode shortcut immediately, which was problematic for immediately following DockBuilderDockWindow(). (#2109)
@almic

This comment has been minimized.

Copy link

commented May 18, 2019

I have a question, do you think it would be beneficial to have the WindowClass stored inside the NextWindowData of the imgui context (and the windows themselves) be a pointer instead? Currently I'm writing a system to allow end users to rearrange the windows in a dockspace and save that to the file system, and need to pass a derived class to the SetNextWindowClass() function. However because the context doesn't store it as a pointer, all my extra data is chopped off.

I'm pretty sure this was the intention when adding that, so it would be great if it actually worked that way.

Repository owner deleted a comment from almic May 18, 2019

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented May 18, 2019

@almic Again, best to open a new topic! The quick answer is that we almost never hold on user pointers in dear imgui, but understanding better what you are trying to do may lead to a better answer or solution.

ocornut added a commit that referenced this issue May 18, 2019

ocornut added a commit that referenced this issue May 19, 2019

Docking: Fixed temporarily losing Dockspace flag when merging remaini…
…ng sibling back into a parent node. (#2563, #2109)

Would trigger an assert in the Passthru hole path. Broken by fd5859e.

ocornut added a commit that referenced this issue May 27, 2019

Docking: Clarified terminology of docking/tablist/collapse button int…
…o Window Menu button matching master. Added private ImGuiDockNodeFlags_NoWindowMenuButton, ImGuiDockNodeFlags_NoCloseButton flags. (#2583, #2109)

ocornut added a commit that referenced this issue May 27, 2019

Docking: Saving the NoTabBar, NoWindowMenuButton, NoCloseButton field…
…s of dock node into the .ini file. Added them to the Metrics window. (#2583, #2423, #2109).
@BeachCoder76

This comment has been minimized.

Copy link

commented May 29, 2019

Hi-
I've been playing around with the Docking Branch. Is DockSpace() supposed to be used within any window juste created? I'm asking the question since I tried to have one top window handle "on first call" for other windows opening by docking them directly under itself in its DockSpace. This works until I collapsed my top window (DockSpace owner window) and all docked windows go floating...

imguidockspaceissue

Maybe there's another way to declare a docking region within a window? But if that's a real code issue, I'd be more than pleased to open a new topic about it.

Thanks

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented May 29, 2019

@BeachCoder76 I don't understand all of your message.
I think what is happening here is that you are doing an early-out when the Master Window is collapsed, which is a common pattern. However in the case of a DockSpace you will need to keep it "alive" otherwise its windows will get undocked. Typically you would submit the dockspace even if Begin() returned false.

I will add more commentary in the ShowExampleAppDockSpace() demo to explain this.
Note that you can also call DockSpace() with the ImGuiDockNodeFlags_KeepAliveOnly to keep it alive.

(For further issues, please open a separate issue and tag this one.)

@BeachCoder76

This comment has been minimized.

Copy link

commented May 30, 2019

Sorry about that, I thought I'd keep it as simple as possible without a new topic. I should probably followed my first instinct and submit one instead. I did test your suggestion and it works but it was counter-intuitive to me .. I might still open a Topic on this as I've never read anything about this and it's good info for all coders. Unless you disagree :)

@rokups

This comment has been minimized.

Copy link
Contributor

commented Jun 4, 2019

I noticed that windows are dockable by default. Does it make sense though? Usually in applications that support docking we have a tight selection of dockable widgets and rest of windows (message boxes, file dialogs, options dialogs) are not dockable. Maybe it would make sense to have opt-in dockable windows instead of opt-out?

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Jun 4, 2019

I noticed that windows are dockable by default. Does it make sense though? Usually in applications that support docking we have a tight selection of dockable widgets and rest of windows (message boxes, file dialogs, options dialogs) are not dockable. Maybe it would make sense to have opt-in dockable windows instead of opt-out?

I disagree. I think the docking feature is much more powerful when this is available by default. Making it opt-in would put a severe entry bar to maximimize the use of merging windows.

It's already always off for popups, modals, tooltips, menus.

ocornut added a commit that referenced this issue Jun 13, 2019

Docking: Fixed rendering of outer decoration happening on non-visible…
… docked window (#2623, #2109). Revealed by 0770449. We are actually better than before now, as previously those would get unnecessarily get rendered into a hidden draw list.

ocornut added a commit that referenced this issue Jul 15, 2019

ocornut added a commit that referenced this issue Jul 16, 2019

Docking: Fix so that an appearing window making a dock node reappear …
…won't have a zero-size on its first frame (because dock node ->Size was 0.0 unlike ->SizeRef) (#2109)

Docking: Added ImGuiDockNode to .natvis file.

ocornut added a commit that referenced this issue Jul 17, 2019

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Jul 17, 2019

Work on Docking is still ongoing and I've been regularly pushing fixes and small improvement.
If you have specific bug report/request please prefer opening a new github issue.

I pushed a change to allow undocking a whole node (vs a single window) by dragging from the empty space in tab bar / title bar. This is in line with most people expectation and previously was only possible by draggong from the "collapse button" / "docking menu" button which wasn't well understood.

However if the dock node is part of a moveable, floating hierarchy, dragging from the tab bar will still move the whole hierarchy otherwise we can not move those windows (unless they have OS decoration) enabled. You can still undock whole node that are part of a floating hierarchy by dragging from the "docking menu" menu.

@Aborres

This comment has been minimized.

Copy link

commented Jul 21, 2019

Hey! I'm testing this branch and it's going fine so far; however, I haven't managed to make imgui save the state of the docked windows. Calling SaveIniSettingsToDisk manually seems to make imgui remember the positions of the windows but not how they were docked to each other.

Am I missing something or this is a missing feature? If it's in the TODO list, can I help to implement this?

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Jul 21, 2019

@Aborres Saving should be supported.
A possible issue is that you may be enabling docking after loading the .ini file and that information will be discarded on load. First check your .ini file to rule that out or not.
Otherwise please provide a Minimal, Complete and Reprodudice example (https://stackoverflow.com/help/minimal-reproducible-example) and create a new issue for it. Thank you!.

@Aborres

This comment has been minimized.

Copy link

commented Jul 21, 2019

@ocornut You were right that was the problem, sorry about that.
Apart from that it was quite easy to integrate (awesome). I've found an issue with aligned combo menus and I am having a constant crash when switching to a specific tab but I believe these are my fault.

image

@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Jul 21, 2019

I've found an issue with aligned combo menus and I am having a constant crash when switching to a specific tab but I believe these are my fault.

Please open New Issue for anything you have to report, with a repro. Thank you.

ocornut added a commit that referenced this issue Jul 21, 2019

Perform simple check: error if Docking or Viewport are enabled _exact…
…ly_ on frame 1 (instead of frame 0 or later), which is a common error leading to loss of .ini data. (#2109)
@bilek993

This comment has been minimized.

Copy link

commented Jul 22, 2019

Hi, I would like to achive result like @rokups, but it doesn't seems to work like expected. Probably I'm doing something wrong, but I don't know what.

I'm trying to achive this:
image
I've got this:
image

auto dockingEnabled = true;
const auto windowFlags =	ImGuiWindowFlags_MenuBar | 
							ImGuiWindowFlags_NoDocking | 
							ImGuiWindowFlags_NoTitleBar | 
							ImGuiWindowFlags_NoCollapse | 
							ImGuiWindowFlags_NoResize | 
							ImGuiWindowFlags_NoMove | 
							ImGuiWindowFlags_NoBringToFrontOnFocus | 
							ImGuiWindowFlags_NoNavFocus |
							ImGuiWindowFlags_NoBackground;

const auto dockspaceFlags = ImGuiDockNodeFlags_PassthruCentralNode;

const auto 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);

ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("MainDockspaceArea", &IsDockingEnabled, windowFlags);
ImGui::PopStyleVar(3);

DrawMenuBar();

auto mainDockspaceId = ImGui::GetID("MainDockspaceArea");
ImGui::DockSpace(mainDockspaceId, ImVec2(0.0f, 0.0f), dockspaceFlags);

static auto firstTime = true;
if (firstTime)
{
	firstTime = false;
	ImGui::DockBuilderRemoveNode(mainDockspaceId);
	ImGui::DockBuilderAddNode(mainDockspaceId, dockspaceFlags);
	ImGui::DockBuilderSetNodeSize(mainDockspaceId, viewport->Size);

	auto dockIdProp = ImGui::DockBuilderSplitNode(mainDockspaceId, ImGuiDir_Right, 0.20f, nullptr, &mainDockspaceId);
	auto dockIdBottom = ImGui::DockBuilderSplitNode(mainDockspaceId, ImGuiDir_Down, 0.20f, nullptr, &mainDockspaceId);

	ImGui::DockBuilderDockWindow("Systems browser", dockIdProp);
	ImGui::DockBuilderFinish(mainDockspaceId);
}

ImGui::End();
@ocornut

This comment has been minimized.

Copy link
Owner Author

commented Jul 22, 2019

@bilek993 When calling DockBuilderAddNode() you need to pass the ImGuiDockNodeFlags_DockSpace flag otherwise you won't have a default "central node" which can stay empty.

REMINDER TO EVERYONE USING ImGui::DockBuilderXXX this is a Beta api and it has been changing. If you have issues with it check the posts, git log and comments here.

PLEASE OPEN NEW ISSUES WHEN YOU HAVE AN ISSUE WITH DOCKING. THIS THREAD IS TOO CROWDED.

ocornut added a commit that referenced this issue Jul 22, 2019

Docking: Renamed io.ConfigDockingTabBarOnSingleWindows to io.ConfigDo…
…ckingAlwaysTabBar. (#2109)

Added ImGuiWindowClass::DockingAlwaysTabBar to set on individual windows.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.