Skip to content

Commit

Permalink
IsWindowHovered: Added support for ImGuiHoveredFlags_Stationary.
Browse files Browse the repository at this point in the history
  • Loading branch information
ocornut committed Jun 20, 2023
1 parent b60acfa commit e7a4327
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 14 deletions.
1 change: 1 addition & 0 deletions docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Other changes:
isn't covered as much. (Match offset for drag and drop tooltips)
- IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40,
Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485)
- IsWindowHovered: Added support for ImGuiHoveredFlags_Stationary.
- Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either
ScrollX or ScrollY flags from being impossible to resize. (#6503)
- Clipper: Rework inner logic to allow functioning with a zero-clear constructor.
Expand Down
32 changes: 25 additions & 7 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3933,6 +3933,16 @@ bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flag
return true;
}

static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags)
{
ImGuiContext& g = *GImGui;
if (flags & ImGuiHoveredFlags_DelayShort)
return g.Style.HoverDelayShort;
if (flags & ImGuiHoveredFlags_DelayNormal)
return g.Style.HoverDelayNormal;
return 0.0f;
}

// 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()
// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId
Expand Down Expand Up @@ -3995,13 +4005,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)

// Handle hover delay
// (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
float delay;
if (flags & ImGuiHoveredFlags_DelayShort)
delay = g.Style.HoverDelayShort;
else if (flags & ImGuiHoveredFlags_DelayNormal)
delay = g.Style.HoverDelayNormal;
else
delay = 0.0f;
const float delay = CalcDelayFromHoveredFlags(flags);
if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
{
ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect);
Expand Down Expand Up @@ -4567,6 +4571,10 @@ void ImGui::NewFrame()
g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
else if (g.HoverItemDelayId == 0)
g.HoverItemUnlockedStationaryId = 0;
if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID;
else if (g.HoveredWindow == NULL)
g.HoverWindowUnlockedStationaryId = 0;

// Update hover delay for IsItemHovered() with delays and tooltips
g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
Expand Down Expand Up @@ -7274,6 +7282,16 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId)
return false;

// When changing hovered window we requires a bit of stationary delay before activating hover timer.
// FIXME: We don't support delay other than stationary one for now, other delay would need a way
// to fullfill the possibility that multiple IsWindowHovered() with varying flag could return true
// for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache.
// We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow.
if (flags & ImGuiHoveredFlags_ForTooltip)
flags |= g.Style.HoverFlagsForTooltipMouse;
if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID)
return false;

return true;
}

Expand Down
4 changes: 2 additions & 2 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -1297,13 +1297,13 @@ enum ImGuiHoveredFlags_
// - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior.
// e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'.
// - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often.
// - for items which main purpose is to be hovered, or items with low affordance, or less consistent app, prefer no delay or shorter delay.
// - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay.
ImGuiHoveredFlags_ForTooltip = 1 << 11, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence.

// (Advanced) Mouse Hovering delays.
// - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags.
// - use those if you need specific overrides.
ImGuiHoveredFlags_Stationary = 1 << 12, // IsItemHovered() only: Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item.
ImGuiHoveredFlags_Stationary = 1 << 12, // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay.
ImGuiHoveredFlags_DelayNone = 1 << 13, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this.
ImGuiHoveredFlags_DelayShort = 1 << 14, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
ImGuiHoveredFlags_DelayNormal = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item).
Expand Down
6 changes: 4 additions & 2 deletions imgui_demo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2523,7 +2523,8 @@ static void ShowDemoWindowWidgets()
"IsWindowHovered(_RootWindow) = %d\n"
"IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n"
"IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n"
"IsWindowHovered(_AnyWindow) = %d\n",
"IsWindowHovered(_AnyWindow) = %d\n"
"IsWindowHovered(_Stationary) = %d\n",
ImGui::IsWindowHovered(),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem),
Expand All @@ -2534,7 +2535,8 @@ static void ShowDemoWindowWidgets()
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow),
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy),
ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup),
ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow));
ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow),
ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary));

ImGui::BeginChild("child", ImVec2(0, 50), true);
ImGui::Text("This is another child window for testing the _ChildWindows flag.");
Expand Down
7 changes: 4 additions & 3 deletions imgui_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1963,12 +1963,13 @@ struct ImGuiContext
ImGuiID HoverItemDelayIdPreviousFrame;
float HoverItemDelayTimer; // Currently used by IsItemHovered()
float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared.
ImGuiID HoverItemUnlockedStationaryId;
ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item.
ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window.

// Mouse state
ImGuiMouseCursor MouseCursor;
int MouseMovingFrames;
float MouseStationaryTimer;
float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic)
ImVec2 MouseLastValidPos;

// Widget state
Expand Down Expand Up @@ -2167,7 +2168,7 @@ struct ImGuiContext
TablesTempDataStacked = 0;
CurrentTabBar = NULL;

HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = 0;
HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0;
HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;

MouseCursor = ImGuiMouseCursor_Arrow;
Expand Down

0 comments on commit e7a4327

Please sign in to comment.