ocornut/imgui

Controlling z order

inolen opened this issue · 8 comments

I've been doing some custom UI rendering with a mixture of widgets / raw draw list commands.

All was well, until I split one of my windows up with a child window through BeginChild / EndChild:
clouds

In this image, the clouds and white rectangle are added through AddRectFilled, and each disc is an Image widget added inside of a child window. Finally, a fullscreen black quad is again added with AddRectFilled when "fading out" the UI.

After converting over to using BeginChild / EndChild, the black quad stopped covering the disc images. Is there a way I can control the z-order in order to ensure the black quad renders last?

Childs are always drawn after parents, in the same order as BeginChild() was called.
If you need a black quad covering everything you might create yet another child.

However - I don't know exactly what you are using here but it looks like you could ignore most of ImGui constructs and just work within a single ImDrawList for that sort of high-level very custom ui.

Also linking to #983

PS: Please always use the github attachment system for pictures and not imgur. The later will delete images in the future and render this issues harder to grasp for other users encountering a similar issue.

Thanks a lot for the response! Another child with BeginChild sounds interesting - I'll give that a shot tonight.

I broke away from the drawlist primarily to handle the scrolling of the discs there.

Re #983, using another window for the black quad also seemed interesting, but ImGuiWindowFlags_NoBringToFrontOnFocus and ImGuiWindowFlags_NoFocusOnAppearing didn't quite offer what I needed. When not using them, if I rendered the black quad window last it would cover the screen, but if I clicked in that area it would end up coming to the front on top of the main menu bar which created an odd experience. What I really wanted was a window flag to bring it to front on appearing (not necessarily focus on appearing, as I wanted to enable ImGuiWindowFlags_NoBringToFrontOnFocus such that it wouldn't come to the front when clicked on).

P.S. edited the issue and attached the image through github, will keep that in mind.

I broke away from the drawlist primarily to handle the scrolling of the discs there.

But the scrolling is essentially just offsetting stuff, it may be easier to just handle all of that yourself with ImDrawList as a simple drawing api (with basic clipping for your covers if necessary). Right now there's nothing in your app that looks remotely like a window or the typical use of ImGui.

(I did the same my game The Dragon's Trap essentially piped all its rendering through ImDrawList, without relying on ImGui stuff for the game itself).

Re #983 yeah I realize it doesn't answer "Controlling Z order", just linking the threads because the initial request is the same. If we expose absolute and arbitrary Z values it means the program needs to allocate/know its Z values for the whole application, which might occasionally hinder code sharing or reuse. But I suppose there's no way out of that, might bite the bullet as well.

I found a simple and not too disgusting way to add Z ordering to Imgui.

imgui_user.h: (needs -DIMGUI_INCLUDE_IMGUI_USER_H)

enum WindowZOrder {
        WindowZOrder_Chat,
        WindowZOrder_Scoreboard,
        WindowZOrder_Menu,
        WindowZOrder_Console,
};

namespace ImGui {
        void Begin( const char * name, WindowZOrder z_order, ImGuiWindowFlags flags );
};

Somewhere else:

namespace ImGui {
        void Begin( const char * name, WindowZOrder z_order, ImGuiWindowFlags flags ) {
                ImGui::Begin( name, NULL, flags );
                ImGui::GetCurrentWindow()->BeginOrderWithinContext = z_order;
        }
}

and before you call ImGui::Render():

ImGuiContext * ctx = ImGui::GetCurrentContext();
std::stable_sort( ctx->Windows.begin(), ctx->Windows.end(),
        []( const ImGuiWindow * a, const ImGuiWindow * b ) {
                return a->BeginOrderWithinContext < b->BeginOrderWithinContext;
        }
);

ImGui::Render();

BeginOrderWithinContext isn't used for anything except debug prints so it's ok to clobber it.

ImGui::GetCurrentWindow() isn't working anymore in the @mikejsavage 's solution

Is there any way to just make Begin() to move vertices at the end of draw buffer?

I mean there is ImGuiWindowFlags_NoBringToFrontOnFocus which moves it
I need inverse behaviour

I'm trying to use https://github.com/patrickcjk/imgui-notify
But it seems abandoned
It doesn't render notification on top in docking branch (even when I call render at the end of client code)

This function tries to render on top https://github.com/patrickcjk/imgui-notify/blob/v2/example/src/imgui_notify.h#L237

Can BringWindowToDisplayFront help?

Oh wow it magically solved everything
Why just nobody said about it?

flags can be ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoFocusOnAppearing
(in my case this is toast notifications)

ImGui::Begin(window_name, NULL, flags);
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindow()); // needs imgui_internal.h
// something
ImGui::End()

imgui-notify specific
I've added BringWindowToDisplayFront right after Begin inside ImGui::RenderNotifications()

zbuffer
;