IsItemHovered(), IsWindowHovered(): added flags to enable various and more specific behavior. Will enable improvements for popups/context menus and drag'n drop. (relate ~#439, #1013, #143, #925)

The legacy confusing IsItemRectHovered(), IsWindowRectHovered() can be completely removed now.
Changed IsWindowHovered() behavior with default parameter: it now return false is the window is blocked by a popup.
Demo: Added tests for those two functions.
This commit is contained in:
omar 2017-10-20 17:52:22 +02:00
parent d29a6a5a5a
commit 564ff2dfd3
3 changed files with 69 additions and 33 deletions

View File

@ -216,11 +216,12 @@
Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
Also read releases logs https://github.com/ocornut/imgui/releases for more details. Also read releases logs https://github.com/ocornut/imgui/releases for more details.
- 2017/10/20 (1.52) - removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51, the new flags available for IsItemHovered() and IsWindowHovered() are providing much more details/options, and those legacy entry points were confusing/misleading.
- 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead!
- 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete).
- 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete).
- 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)".
- 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)!
- renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete).
- renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete).
- 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency.
@ -1944,15 +1945,22 @@ void ImGui::KeepAliveID(ImGuiID id)
g.ActiveIdIsAlive = true; g.ActiveIdIsAlive = true;
} }
static inline bool IsWindowContentHoverable(ImGuiWindow* window) static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags)
{ {
// An active popup disable hovering on other windows (apart from its own children) // An active popup disable hovering on other windows (apart from its own children)
// FIXME-OPT: This could be cached/stored within the window. // FIXME-OPT: This could be cached/stored within the window.
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
if (g.NavWindow) if (g.NavWindow)
if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow)
if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow) if (focused_root_window->WasActive && focused_root_window != window->RootWindow)
{
// For the purpose of those flags we differentiate "standard popup" from "modal popup"
// NB: The order of those two tests is important because Modal windows are also Popups.
if (focused_root_window->Flags & ImGuiWindowFlags_Modal)
return false; return false;
if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup))
return false;
}
return true; return true;
} }
@ -2012,7 +2020,7 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id)
// This is roughly matching the behavior of internal-facing ItemHoverable() // This is roughly matching the behavior of internal-facing ItemHoverable()
// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered()) // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered())
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
bool ImGui::IsItemHovered() bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -2023,21 +2031,16 @@ bool ImGui::IsItemHovered()
// Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while. // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while.
//if (g.HoveredWindow != window) //if (g.HoveredWindow != window)
// return false; // return false;
if (g.HoveredRootWindow != window->RootWindow) if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped))
return false; return false;
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId)
return false; return false;
if (!IsWindowContentHoverable(window)) if (!IsWindowContentHoverable(window, flags))
return false; return false;
return true; return true;
} }
bool ImGui::IsItemRectHovered()
{
ImGuiWindow* window = GetCurrentWindowRead();
return window->DC.LastItemRectHoveredRect;
}
// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). // Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered().
bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
{ {
@ -2052,7 +2055,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id)
return false; return false;
if (!IsMouseHoveringRect(bb.Min, bb.Max)) if (!IsMouseHoveringRect(bb.Min, bb.Max))
return false; return false;
if (!IsWindowContentHoverable(window)) if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_Default))
return false; return false;
SetHoveredID(id); SetHoveredID(id);
@ -5086,16 +5089,11 @@ const char* ImGui::GetStyleColorName(ImGuiCol idx)
return "Unknown"; return "Unknown";
} }
bool ImGui::IsWindowHovered() bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
{ {
IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow); return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow, flags);
}
bool ImGui::IsWindowRectHovered()
{
ImGuiContext& g = *GImGui;
return g.HoveredWindow == g.CurrentWindow;
} }
bool ImGui::IsWindowFocused() bool ImGui::IsWindowFocused()
@ -5116,10 +5114,11 @@ bool ImGui::IsRootWindowOrAnyChildFocused()
return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow;
} }
bool ImGui::IsRootWindowOrAnyChildHovered() bool ImGui::IsRootWindowOrAnyChildHovered(ImGuiHoveredFlags flags)
{ {
IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) == 0); // Flags not supported by this function
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow); return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow, flags);
} }
float ImGui::GetWindowWidth() float ImGui::GetWindowWidth()

23
imgui.h
View File

@ -80,6 +80,7 @@ typedef int ImGuiColumnsFlags; // flags: for *Columns*()
typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_
typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_
typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(), Collapsing*() // enum ImGuiTreeNodeFlags_ typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(), Collapsing*() // enum ImGuiTreeNodeFlags_
typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() // enum ImGuiHoveredFlags_
typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data);
typedef void (*ImGuiSizeConstraintCallback)(ImGuiSizeConstraintCallbackData* data); typedef void (*ImGuiSizeConstraintCallback)(ImGuiSizeConstraintCallbackData* data);
#ifdef _MSC_VER #ifdef _MSC_VER
@ -414,8 +415,7 @@ namespace ImGui
IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL);
// Utilities // Utilities
IMGUI_API bool IsItemHovered(); // is the last item hovered by mouse (and usable)? IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered by mouse (and usable)?
IMGUI_API bool IsItemRectHovered(); // is the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this
IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false)
IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on)
IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.) IMGUI_API bool IsItemVisible(); // is the last item visible? (aka not out of sight due to clipping/scrolling.)
@ -426,11 +426,10 @@ namespace ImGui
IMGUI_API ImVec2 GetItemRectSize(); // " IMGUI_API ImVec2 GetItemRectSize(); // "
IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area.
IMGUI_API bool IsWindowFocused(); // is current window focused IMGUI_API bool IsWindowFocused(); // is current window focused
IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags = 0); // is current window hovered (and typically: not blocked by a popup/modal)
IMGUI_API bool IsWindowRectHovered(); // is current window rectangle hovered, disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup)
IMGUI_API bool IsRootWindowFocused(); // is current root window focused (root = top-most parent of a child, otherwise self) IMGUI_API bool IsRootWindowFocused(); // is current root window focused (root = top-most parent of a child, otherwise self)
IMGUI_API bool IsRootWindowOrAnyChildFocused(); // is current root window or any of its child (including current window) focused IMGUI_API bool IsRootWindowOrAnyChildFocused(); // is current root window or any of its child (including current window) focused
IMGUI_API bool IsRootWindowOrAnyChildHovered(); // is current root window or any of its child (including current window) hovered and hoverable (not blocked by a popup) IMGUI_API bool IsRootWindowOrAnyChildHovered(ImGuiHoveredFlags flags = 0); // is current root window or any of its child (including current window) hovered and hoverable (not blocked by a popup)
IMGUI_API bool IsAnyWindowHovered(); // is mouse hovering any visible window IMGUI_API bool IsAnyWindowHovered(); // is mouse hovering any visible window
IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped.
IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side.
@ -569,6 +568,16 @@ enum ImGuiSelectableFlags_
ImGuiSelectableFlags_AllowDoubleClick = 1 << 2 // Generate press events on double clicks too ImGuiSelectableFlags_AllowDoubleClick = 1 << 2 // Generate press events on double clicks too
}; };
enum ImGuiHoveredFlags_
{
ImGuiHoveredFlags_Default = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them.
ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 0, // Return true even if a popup window is normally blocking access to this item/window
//ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 1, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet.
ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 2, // Return true even if an active item is blocking access to this item/window
ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 3, // Return true even if the position is overlapped by another window
ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped
};
// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array // User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array
enum ImGuiKey_ enum ImGuiKey_
{ {
@ -881,10 +890,10 @@ namespace ImGui
bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // OBSOLETE 1.52+. use SetNextWindowSize() instead if you want to set a window size. bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // OBSOLETE 1.52+. use SetNextWindowSize() instead if you want to set a window size.
static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } // OBSOLETE 1.52+ static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } // OBSOLETE 1.52+
static inline void SetNextWindowPosCenter(ImGuiCond cond = 0) { SetNextWindowPos(ImVec2(GetIO().DisplaySize.x * 0.5f, GetIO().DisplaySize.y * 0.5f), cond, ImVec2(0.5f, 0.5f)); } // OBSOLETE 1.52+ static inline void SetNextWindowPosCenter(ImGuiCond cond = 0) { SetNextWindowPos(ImVec2(GetIO().DisplaySize.x * 0.5f, GetIO().DisplaySize.y * 0.5f), cond, ImVec2(0.5f, 0.5f)); } // OBSOLETE 1.52+
static inline bool IsItemHoveredRect() { return IsItemRectHovered(); } // OBSOLETE 1.51+ static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } // OBSOLETE 1.51+
static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // OBSOLETE 1.51+. This was partly broken. You probably wanted to use ImGui::GetIO().WantCaptureMouse instead. static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // OBSOLETE 1.51+. This was partly broken. You probably wanted to use ImGui::GetIO().WantCaptureMouse instead.
static inline bool IsMouseHoveringAnyWindow() { return IsAnyWindowHovered(); } // OBSOLETE 1.51+ static inline bool IsMouseHoveringAnyWindow() { return IsAnyWindowHovered(); } // OBSOLETE 1.51+
static inline bool IsMouseHoveringWindow() { return IsWindowRectHovered(); } // OBSOLETE 1.51+ static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } // OBSOLETE 1.51+
static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1 << 5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } // OBSOLETE 1.49+ static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1 << 5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } // OBSOLETE 1.49+
static inline ImFont* GetWindowFont() { return GetFont(); } // OBSOLETE 1.48+ static inline ImFont* GetWindowFont() { return GetFont(); } // OBSOLETE 1.48+
static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETE 1.48+ static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETE 1.48+

View File

@ -1763,6 +1763,34 @@ void ImGui::ShowTestWindow(bool* p_open)
ImGui::TreePop(); ImGui::TreePop();
} }
if (ImGui::TreeNode("Hovering"))
{
// Testing IsWindowHovered() function
ImGui::BulletText(
"IsWindowHovered() = %d\n"
"IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n",
//"IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n",
ImGui::IsWindowHovered(),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup));
//ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem));
// Testing IsItemHovered() function (because BulletText is an item itself and that would affect the output of IsItemHovered, we pass all lines in a single items to shorten the code)
ImGui::Button("ITEM");
ImGui::BulletText(
"IsItemHovered() = %d\n"
"IsItemHovered(_AllowWhenBlockedByPopup) = %d\n"
"IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n"
"IsItemHovered(_AllowWhenOverlapped) = %d\n"
"IsItemhovered(_RectOnly) = %d\n",
ImGui::IsItemHovered(),
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped),
ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly));
ImGui::TreePop();
}
if (ImGui::TreeNode("Dragging")) if (ImGui::TreeNode("Dragging"))
{ {
ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget.");