Skip to content

Commit

Permalink
Added ImGuiFocusedFlags_NoPopupHierarchy and ImGuiHoveredFlags_NoPopu…
Browse files Browse the repository at this point in the history
…pHierarchy (followup #4527)

IsWindowFocused: fix flag usage (amend 6b1e094) was technically harmless because of light typing.
  • Loading branch information
ocornut committed Sep 24, 2021
1 parent ca097c2 commit 65ad63d
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 35 deletions.
4 changes: 4 additions & 0 deletions docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ Breaking Changes:
Other Changes:

- Windows: Fixed background order of overlapping childs submitted sequentially. (#4493)
- IsWindowFocused: Added ImGuiFocusedFlags_NoPopupHierarchy flag allowing to exclude child popups
from the tested windows when combined with _ChildWindows.
- IsWindowHovered: Added ImGuiHoveredFlags_NoPopupHierarchy flag allowing to exclude child popups
from the tested windows when combined with _ChildWindows.
- InputTextMultiline: Fixed label size not being included into window contents rect unless
the whole widget is clipped.
- InputText: Allow cancelling/validating input with gamepad nav events to facilitate undoing
Expand Down
40 changes: 26 additions & 14 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4031,7 +4031,7 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags()

// Modal windows prevents mouse from hovering behind them.
ImGuiWindow* modal_window = GetTopMostPopupModal();
if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true))
if (modal_window && g.HoveredWindow && !IsWindowChildOf(g.HoveredWindow->RootWindowDockTree, modal_window, true, true))
clear_hovered_windows = true;

// Disabled mouse?
Expand Down Expand Up @@ -6137,13 +6137,15 @@ void ImGui::RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& titl
void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window)
{
window->ParentWindow = parent_window;
window->RootWindow = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
window->RootWindow = window->RootWindowPopupTree = window->RootWindowDockTree = window->RootWindowForTitleBarHighlight = window->RootWindowForNav = window;
if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip))
{
window->RootWindowDockTree = parent_window->RootWindowDockTree;
if (!window->DockIsActive && !(parent_window->Flags & ImGuiWindowFlags_DockNodeHost))
window->RootWindow = parent_window->RootWindow;
}
if (parent_window && (flags & ImGuiWindowFlags_Popup))
window->RootWindowPopupTree = parent_window->RootWindowPopupTree;
if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)))
window->RootWindowForTitleBarHighlight = parent_window->RootWindowForTitleBarHighlight;
while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened)
Expand Down Expand Up @@ -7261,19 +7263,27 @@ void ImGui::PopTextWrapPos()
window->DC.TextWrapPosStack.pop_back();
}

bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy)
static ImGuiWindow* GetCombinedRootWindow(ImGuiWindow* window, bool popup_hierarchy, bool dock_hierarchy)
{
if ((dock_hierarchy ? window->RootWindowDockTree : window->RootWindow) == potential_parent)
window = window->RootWindow;
if (popup_hierarchy)
window = window->RootWindowPopupTree;
if (dock_hierarchy)
window = window->RootWindowDockTree;
return window;
}

bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy)
{
ImGuiWindow* window_root = GetCombinedRootWindow(window, popup_hierarchy, dock_hierarchy);
if (window_root == potential_parent)
return true;
while (window != NULL)
{
if (window == potential_parent)
return true;
// 2021-09-11: we broke the unexpressed contract that this function (prior to 6b1e094, #4527)
// would follow through popup parents as well. Restoring this for now. May want to add a ImGuiFocusedFlags_PopupHierarchy flag later.
if ((window->Flags & ImGuiWindowFlags_Popup) == 0)
if (window == (dock_hierarchy ? window->RootWindowDockTree : window->RootWindow))
return false;
if (window == window_root) // end of chain
return false;
window = window->ParentWindow;
}
return false;
Expand Down Expand Up @@ -7305,14 +7315,15 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
if ((flags & ImGuiHoveredFlags_AnyWindow) == 0)
{
IM_ASSERT(cur_window); // Not inside a Begin()/End()
const bool popup_hierarchy = (flags & ImGuiHoveredFlags_NoPopupHierarchy) == 0;
const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;

if (flags & ImGuiHoveredFlags_RootWindow)
cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow;
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);

bool result;
if (flags & ImGuiHoveredFlags_ChildWindows)
result = IsWindowChildOf(ref_window, cur_window, dock_hierarchy);
result = IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
else
result = (ref_window == cur_window);
if (!result)
Expand All @@ -7339,13 +7350,14 @@ bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags)
return true;

IM_ASSERT(cur_window); // Not inside a Begin()/End()
const bool dock_hierarchy = (flags & ImGuiHoveredFlags_DockHierarchy) != 0;
const bool popup_hierarchy = (flags & ImGuiFocusedFlags_NoPopupHierarchy) == 0;
const bool dock_hierarchy = (flags & ImGuiFocusedFlags_DockHierarchy) != 0;

if (flags & ImGuiHoveredFlags_RootWindow)
cur_window = dock_hierarchy ? cur_window->RootWindowDockTree : cur_window->RootWindow;
cur_window = GetCombinedRootWindow(cur_window, popup_hierarchy, dock_hierarchy);

if (flags & ImGuiHoveredFlags_ChildWindows)
return IsWindowChildOf(ref_window, cur_window, dock_hierarchy);
return IsWindowChildOf(ref_window, cur_window, popup_hierarchy, dock_hierarchy);
else
return (ref_window == cur_window);
}
Expand Down
16 changes: 9 additions & 7 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -1312,7 +1312,8 @@ enum ImGuiFocusedFlags_
ImGuiFocusedFlags_ChildWindows = 1 << 0, // Return true if any children of the window is focused
ImGuiFocusedFlags_RootWindow = 1 << 1, // Test from root window (top most parent of the current hierarchy)
ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ!
ImGuiFocusedFlags_DockHierarchy = 1 << 3, // Consider docking hierarchy (treat dockspace host as parent of docked window)
ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows
};

Expand All @@ -1325,12 +1326,13 @@ enum ImGuiHoveredFlags_
ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered
ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy)
ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered
ImGuiHoveredFlags_DockHierarchy = 1 << 3, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window)
ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 4, // Return true even if a popup window is normally blocking access to this item/window
//ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is obstructed or overlapped by another window
ImGuiHoveredFlags_AllowWhenDisabled = 1 << 7, // Return true even if the item is disabled
ImGuiHoveredFlags_NoPopupHierarchy = 1 << 3, // IsWindowHovered() only: Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow)
ImGuiHoveredFlags_DockHierarchy = 1 << 4, // IsWindowHovered() only: Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow)
ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 5, // Return true even if a popup window is normally blocking access to this item/window
//ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 6, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns.
ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // Return true even if the position is obstructed or overlapped by another window
ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // Return true even if the item is disabled
ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows
};
Expand Down
43 changes: 32 additions & 11 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2238,7 +2238,7 @@ static void ShowDemoWindowWidgets()
ImGui::TreePop();
}

if (ImGui::TreeNode("Querying Status (Edited/Active/Hovered etc.)"))
if (ImGui::TreeNode("Querying Item Status (Edited/Active/Hovered etc.)"))
{
// Select an item type
const char* item_names[] =
Expand Down Expand Up @@ -2324,51 +2324,75 @@ static void ShowDemoWindowWidgets()
if (item_disabled)
ImGui::EndDisabled();

char buf[1] = "";
ImGui::InputText("unused", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
HelpMarker("This widget is only here to be able to tab-out of the widgets above and see e.g. Deactivated() status.");

ImGui::TreePop();
}

if (ImGui::TreeNode("Querying Window Status (Focused/Hovered etc.)"))
{
static bool embed_all_inside_a_child_window = false;
ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window);
ImGui::Checkbox("Embed everything inside a child window for testing _RootWindow flag.", &embed_all_inside_a_child_window);
if (embed_all_inside_a_child_window)
ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20.0f), true);

// Testing IsWindowFocused() function with its various flags.
// Note that the ImGuiFocusedFlags_XXX flags can be combined.
ImGui::BulletText(
"IsWindowFocused() = %d\n"
"IsWindowFocused(_ChildWindows) = %d\n"
"IsWindowFocused(_ChildWindows|_NoPopupHierarchy) = %d\n"
"IsWindowFocused(_ChildWindows|_DockHierarchy) = %d\n"
"IsWindowFocused(_ChildWindows|_RootWindow) = %d\n"
"IsWindowFocused(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
"IsWindowFocused(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
"IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n"
"IsWindowFocused(_RootWindow) = %d\n"
"IsWindowFocused(_RootWindow|_NoPopupHierarchy) = %d\n"
"IsWindowFocused(_RootWindow|_DockHierarchy) = %d\n"
"IsWindowFocused(_AnyWindow) = %d\n",
ImGui::IsWindowFocused(),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_NoPopupHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow),
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_DockHierarchy),
ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow));

// Testing IsWindowHovered() function with its various flags.
// Note that the ImGuiHoveredFlags_XXX flags can be combined.
ImGui::BulletText(
"IsWindowHovered() = %d\n"
"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n"
"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n"
"IsWindowHovered(_ChildWindows) = %d\n"
"IsWindowHovered(_ChildWindows|_NoPopupHierarchy) = %d\n"
"IsWindowHovered(_ChildWindows|_DockHierarchy) = %d\n"
"IsWindowHovered(_ChildWindows|_RootWindow) = %d\n"
"IsWindowHovered(_ChildWindows|_RootWindow|_NoPopupHierarchy) = %d\n"
"IsWindowHovered(_ChildWindows|_RootWindow|_DockHierarchy) = %d\n"
"IsWindowHovered(_RootWindow) = %d\n"
"IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
"IsWindowHovered(_RootWindow|_DockHierarchy) = %d\n"
"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
"IsWindowHovered(_RootWindow) = %d\n"
"IsWindowHovered(_AnyWindow) = %d\n",
ImGui::IsWindowHovered(),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_DockHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_DockHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow));

ImGui::BeginChild("child", ImVec2(0, 50), true);
Expand All @@ -2377,9 +2401,6 @@ static void ShowDemoWindowWidgets()
if (embed_all_inside_a_child_window)
ImGui::EndChild();

static char unused_str[] = "This widget is only here to be able to tab-out of the widgets above.";
ImGui::InputText("unused", unused_str, IM_ARRAYSIZE(unused_str), ImGuiInputTextFlags_ReadOnly);

// Calling IsItemHovered() after begin returns the hovered status of the title bar.
// This is useful in particular if you want to create a context menu associated to the title bar of a window.
// This will also work when docked into a Tab (the Tab replace the Title Bar and guarantee the same properties).
Expand Down
7 changes: 4 additions & 3 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -2153,8 +2153,9 @@ struct IMGUI_API ImGuiWindow

ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer)
ImDrawList DrawListInst;
ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL.
ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state.
ImGuiWindow* ParentWindow; // If we are a child _or_ popup _or_ docked window, this is pointing to our parent. Otherwise NULL.
ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. Doesn't cross through popups/dock nodes. We use this so IsWindowFocused() can behave consistently regardless of docking state.
ImGuiWindow* RootWindowPopupTree; // Point to ourself or first ancestor that is not a child window. Cross through popups parent<>child.
ImGuiWindow* RootWindowDockTree; // Point to ourself or first ancestor that is not a child window. Cross through dock nodes.
ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active.
ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag.
Expand Down Expand Up @@ -2559,7 +2560,7 @@ namespace ImGui
IMGUI_API ImGuiWindow* FindWindowByName(const char* name);
IMGUI_API void UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags flags, ImGuiWindow* parent_window);
IMGUI_API ImVec2 CalcWindowNextAutoFitSize(ImGuiWindow* window);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool dock_hierarchy);
IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent, bool popup_hierarchy, bool dock_hierarchy);
IMGUI_API bool IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_below);
IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window);
IMGUI_API void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond = 0);
Expand Down

0 comments on commit 65ad63d

Please sign in to comment.