diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d98b0ac13ca8..62e00aa64107 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -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. diff --git a/imgui.cpp b/imgui.cpp index 9f0005205351..9ab15b0f8cc3 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -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 @@ -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); @@ -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; @@ -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; } diff --git a/imgui.h b/imgui.h index c955a6842ed9..e254e16b316e 100644 --- a/imgui.h +++ b/imgui.h @@ -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). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8bdfd6ec7a7b..58302b09098d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -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), @@ -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."); diff --git a/imgui_internal.h b/imgui_internal.h index ba834124c4ee..2e19ed01f3f1 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -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 @@ -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;