Skip to content

Commit

Permalink
Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_Has…
Browse files Browse the repository at this point in the history
…MouseHoveredViewport. Backends: SDL: Added support for simplified HasMouseHoveredViewport. (#1542, #4665)
  • Loading branch information
ocornut committed Jan 18, 2022
1 parent 007a427 commit 1338eb3
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 25 deletions.
16 changes: 10 additions & 6 deletions backends/imgui_impl_glfw.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,12 +578,14 @@ static void ImGui_ImplGlfw_UpdateMouseData()
}

// (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering.
// Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because
// - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows).
// - This is _regardless_ of whether another viewport is focused or being dragged from.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, imgui will ignore this field and infer the information by relying on the
// rectangles and last focused time of every viewports it knows about. It will be unaware of other windows that may be sitting between or over your windows.
// [GLFW] FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
// - [X] GLFW >= 3.3 backend ON WINDOWS ONLY does correctly ignore viewports with the _NoInputs flag.
// - [!] GLFW <= 3.2 backend CANNOT correctly ignore viewports with the _NoInputs flag, and CANNOT reported Hovered Viewport because of mouse capture.
// Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
// for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
// by the backend, and use its flawed heuristic to guess the viewport behind.
// - [X] GLFW backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
// FIXME: This is currently only correct on Win32. See what we do below with the WM_NCHITTEST, missing an equivalent for other systems.
// See https://github.com/glfw/glfw/issues/1236 if you want to help in making this a GLFW feature.
#if GLFW_HAS_MOUSE_PASSTHROUGH || (GLFW_HAS_WINDOW_HOVERED && defined(_WIN32))
const bool window_no_input = (viewport->Flags & ImGuiViewportFlags_NoInputs) != 0;
Expand All @@ -592,6 +594,8 @@ static void ImGui_ImplGlfw_UpdateMouseData()
#endif
if (glfwGetWindowAttrib(window, GLFW_HOVERED) && !window_no_input)
io.MouseHoveredViewport = viewport->ID;
#else
// We cannot use bd->MouseWindow maintained from CursorEnter/Leave callbacks, because it is locked to the window capturing mouse.
#endif
}
}
Expand Down
2 changes: 1 addition & 1 deletion backends/imgui_impl_osx.mm
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ bool ImGui_ImplOSX_Init(NSView* view)
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
//io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
//io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
//io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
//io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional)
io.BackendPlatformName = "imgui_impl_osx";

// Load cursors. Some of them are undocumented.
Expand Down
22 changes: 21 additions & 1 deletion backends/imgui_impl_sdl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ struct ImGui_ImplSDL2_Data
{
SDL_Window* Window;
Uint64 Time;
Uint32 MouseWindowID;
int MouseButtonsDown;
SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
char* ClipboardTextData;
Expand Down Expand Up @@ -312,9 +313,16 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
}
case SDL_WINDOWEVENT:
{
// When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
// However we won't get a correct LEAVE event for a captured window.
Uint8 window_event = event->window.event;
if (window_event == SDL_WINDOWEVENT_ENTER)
bd->MouseWindowID = event->window.windowID;
if (window_event == SDL_WINDOWEVENT_LEAVE)
{
bd->MouseWindowID = 0;
io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
}
if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)
io.AddFocusEvent(true);
else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST)
Expand Down Expand Up @@ -359,7 +367,10 @@ static bool ImGui_ImplSDL2_Init(SDL_Window* window, void* sdl_gl_context)
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
if (mouse_can_use_global_state)
{
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;//We can set io.MouseHoveredViewport correctly (optional)
}

bd->Window = window;
bd->MouseCanUseGlobalState = mouse_can_use_global_state;
Expand Down Expand Up @@ -510,8 +521,17 @@ static void ImGui_ImplSDL2_UpdateMouseData()
}
}

// We don't support ImGuiBackendFlags_HasMouseHoveredViewport
// (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
// - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
// Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
// for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
// by the backend, and use its flawed heuristic to guess the viewport behind.
// - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
io.MouseHoveredViewport = 0;
if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID))
if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window))
io.MouseHoveredViewport = mouse_viewport->ID;
}

static void ImGui_ImplSDL2_UpdateMouseCursor()
Expand Down
16 changes: 8 additions & 8 deletions backends/imgui_impl_win32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ bool ImGui_ImplWin32_Init(void* hwnd)
io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional, not easy)
io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport; // We can set io.MouseHoveredViewport correctly (optional)

bd->hWnd = (HWND)hwnd;
bd->WantUpdateHasGamepad = true;
Expand Down Expand Up @@ -300,17 +300,17 @@ static void ImGui_ImplWin32_UpdateMouseData()
}

// (Optional) When using multiple viewports: set io.MouseHoveredViewport to the viewport the OS mouse cursor is hovering.
// Important: this information is not easy to provide and many high-level windowing library won't be able to provide it correctly, because
// - This is _ignoring_ viewports with the ImGuiViewportFlags_NoInputs flag (pass-through windows).
// - This is _regardless_ of whether another viewport is focused or being dragged from.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, imgui will ignore this field and infer the information by relying on the
// rectangles and last focused time of every viewports it knows about. It will be unaware of foreign windows that may be sitting between or over your windows.
// If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
// - [X] Win32 backend correctly ignore viewports with the _NoInputs flag (here using ::WindowFromPoint with WM_NCHITTEST + HTTRANSPARENT in WndProc does that)
// Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
// for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
// by the backend, and use its flawed heuristic to guess the viewport behind.
// - [X] Win32 backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
io.MouseHoveredViewport = 0;
if (has_mouse_screen_pos)
if (HWND hovered_hwnd = ::WindowFromPoint(mouse_screen_pos))
if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)hovered_hwnd))
if ((viewport->Flags & ImGuiViewportFlags_NoInputs) == 0) // FIXME: We still get our NoInputs window with WM_NCHITTEST/HTTRANSPARENT code when decorated?
io.MouseHoveredViewport = viewport->ID;
io.MouseHoveredViewport = viewport->ID;
}

// Gamepad navigation mapping
Expand Down
9 changes: 9 additions & 0 deletions docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,18 @@ Other Changes:

Docking+Viewports Branch:

- Viewports: Relaxed specs for backend supporting ImGuiBackendFlags_HasMouseHoveredViewport: it is now _optional_
for the backend to have to ignore viewports with the _NoInputs flag when setting io.MouseHoveredViewport. It is
much better if they can (Win32 and GLFW 3.3+ backends can, SDL and GLFW 3.2 backends cannot, they are lacking data).
A concrete example is: when dragging a viewport for docking, the viewport is marked with _NoInputs to allow us
to pick the target viewports for docking. If the backend reports a viewport with _NoInputs in io.MouseHoveredViewport,
then Dear ImGui will revert to its flawed heuristic to find the viewport under.
By lowering those specs, we allow the SDL and more backend to support this, only relying on the heuristic in a few
drag and drop situations rather that relying on it everywhere.
- Viewports: Fixed a CTRL+TAB crash with viewports enabled when the window list needs to appears in
its own viewport (regression from 1.86). (#4023, #787)
- Viewports: Fixed active InputText() from preventing viewports to merge. (#4212)
- Backends: SDL: Added support for ImGuiBackendFlags_HasMouseHoveredViewport now that its specs have been lowered.
- (Breaking) Removed ImGuiPlatformIO::Platform_SetImeInputPos() in favor of newly standardized
io.SetPlatformImeDataFn() function. Should not affect more than default backends.

Expand Down
12 changes: 5 additions & 7 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12701,17 +12701,14 @@ static void ImGui::UpdateViewportsNewFrame()
{
viewport_hovered = g.IO.MouseHoveredViewport ? (ImGuiViewportP*)FindViewportByID(g.IO.MouseHoveredViewport) : NULL;
if (viewport_hovered && (viewport_hovered->Flags & ImGuiViewportFlags_NoInputs))
{
// Backend failed at honoring its contract if it returned a viewport with the _NoInputs flag.
IM_ASSERT(0);
viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
}
viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos); // Backend failed to handle _NoInputs viewport: revert to our fallback.
}
else
{
// If the backend doesn't know how to honor ImGuiViewportFlags_NoInputs, we do a search ourselves. Note that this search:
// A) won't take account of the possibility that non-imgui windows may be in-between our dragged window and our target window.
// B) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)
// B) won't take account of how the backend apply parent<>child relationship to secondary viewports, which affects their Z order.
// C) uses LastFrameAsRefViewport as a flawed replacement for the last time a window was focused (we could/should fix that by introducing Focus functions in PlatformIO)
viewport_hovered = FindHoveredViewportFromPlatformWindowStack(g.IO.MousePos);
}
if (viewport_hovered != NULL)
Expand Down Expand Up @@ -17663,7 +17660,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
TreePop();
}

if (TreeNode("Inferred order (front-to-back)"))
BulletText("MouseViewport: 0x%08X (UserHovered 0x%08X, LastHovered 0x%08X)", g.MouseViewport ? g.MouseViewport->ID : 0, g.IO.MouseHoveredViewport, g.MouseLastHoveredViewport ? g.MouseLastHoveredViewport->ID : 0);
if (TreeNode("Inferred Z order (front-to-back)"))
{
static ImVector<ImGuiViewportP*> viewports;
viewports.resize(g.Viewports.Size);
Expand Down
4 changes: 2 additions & 2 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -1611,7 +1611,7 @@ enum ImGuiBackendFlags_

// [BETA] Viewports
ImGuiBackendFlags_PlatformHasViewports = 1 << 10, // Backend Platform supports multiple viewports.
ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag and _REGARDLESS_ of whether another viewport is focused and may be capturing the mouse. This information is _NOT EASY_ to provide correctly with most high-level engines! Don't set this without studying _carefully_ how the backends handle ImGuiViewportFlags_NoInputs!
ImGuiBackendFlags_HasMouseHoveredViewport=1 << 11, // Backend Platform supports setting io.MouseHoveredViewport to the viewport directly under the mouse. IF POSSIBLE, ignore viewports with the ImGuiViewportFlags_NoInputs flag (Win32 backend, GLFW 3.30+ backend can do this, SDL backend cannot). If this cannot be done, Dear ImGui needs to use a flawed heuristic to find the viewport under.
ImGuiBackendFlags_RendererHasViewports = 1 << 12 // Backend Renderer supports multiple viewports.
};

Expand Down Expand Up @@ -2076,7 +2076,7 @@ struct ImGuiIO
bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API.
float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text.
float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends.
ImGuiID MouseHoveredViewport; // (Optional) When using multiple viewports: viewport the OS mouse cursor is hovering _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag, and _REGARDLESS_ of whether another viewport is focused. Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows).
ImGuiID MouseHoveredViewport; // (Optional) With multi-viewports: viewport the OS mouse is hovering. If possible _IGNORING_ viewports with the ImGuiViewportFlags_NoInputs flag is much better (few backends can handle that). Set io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport if you can provide this info. If you don't imgui will infer the value using the rectangles and last focused time of the viewports it knows about (ignoring other OS windows).
bool KeyCtrl; // Keyboard modifier down: Control
bool KeyShift; // Keyboard modifier down: Shift
bool KeyAlt; // Keyboard modifier down: Alt
Expand Down

0 comments on commit 1338eb3

Please sign in to comment.