فهرست منبع

IsItemHovered, Tooltips: Added ImGuiHoveredFlags_ForTooltip, ImGuiHoveredFlags_Stationary. (#1485)

Update demo accordingly.
ocornut 2 سال پیش
والد
کامیت
b3b8cbd001
5فایلهای تغییر یافته به همراه75 افزوده شده و 17 حذف شده
  1. 7 2
      docs/CHANGELOG.txt
  2. 28 4
      imgui.cpp
  3. 9 4
      imgui.h
  4. 25 6
      imgui_demo.cpp
  5. 6 1
      imgui_internal.h

+ 7 - 2
docs/CHANGELOG.txt

@@ -50,7 +50,12 @@ Other changes:
   where user may not be callinga constructor manually. (#5856)
 - Modals: In the case of nested modal, made sure that focused or appearing windows are
   moved below the lowest blocking modal (rather than the highest one). (#4317)
-- IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40,
+- IsItemHovered: Added ImGuiHoveredFlags_ForTooltip as a shortcut for using _Stationary
+  and _DelayNormal flags. (#1485)
+- IsItemHovered: Added ImGuiHoveredFlags_Stationary to add a stationary test on
+  hovering a new item. Added style.HoverStationaryDelay (default 0.15 sec). Once the mouse
+  has been stationary once the state is preserved. (#1485)
+- IsItemHovered: Tweaked default value style.HoverDelayNormal from 0.30 to 0.40,
   Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485)
 - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items
   isn't covered as much. (Match offset for drag and drop tooltips)
@@ -562,7 +567,7 @@ Other Changes:
 - ColorEdit3: fixed id collision leading to an assertion. (#5707)
 - IsItemHovered: Added ImGuiHoveredFlags_DelayNormal and ImGuiHoveredFlags_DelayShort flags,
   allowing to introduce a shared delay for tooltip idioms. The delays are respectively
-  io.HoverDelayNormal (default to 0.30f) and io.HoverDelayFast (default to 0.10f). (#1485)
+  io.HoverDelayNormal (default to 0.30f) and io.HoverDelayShort (default to 0.10f). (#1485)
 - IsItemHovered: Added ImGuiHoveredFlags_NoSharedDelay to disable sharing delays between items,
   so moving from one item to a nearby one will requires delay to elapse again. (#1485)
 - Tables: activating an ID (e.g. clicking button inside) column doesn't prevent columns

+ 28 - 4
imgui.cpp

@@ -1164,6 +1164,7 @@ ImGuiStyle::ImGuiStyle()
     CircleTessellationMaxError = 0.30f;         // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry.
 
     // Behaviors
+    HoverStationaryDelay    = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
     HoverDelayShort         = 0.15f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
     HoverDelayNormal        = 0.40f;            // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
 
@@ -3992,12 +3993,19 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
         delay = g.Style.HoverDelayNormal;
     else
         delay = 0.0f;
-    if (delay > 0.0f)
+    if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary))
     {
         ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect);
         if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id))
             g.HoverItemDelayTimer = 0.0f;
         g.HoverItemDelayId = hover_delay_id;
+
+        // When changing hovered item we requires a bit of stationary delay before activating hover timer,
+        // but once unlocked on a given item we also moving.
+        //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); }
+        if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id)
+            return false;
+
         if (g.HoverItemDelayTimer < delay)
             return false;
     }
@@ -4543,11 +4551,18 @@ void ImGui::NewFrame()
     }
 #endif
 
+    // Record when we have been stationary as this state is preserved while over same item.
+    // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values.
+    // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function.
+    if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay)
+        g.HoverItemUnlockedStationaryId = g.HoverItemDelayId;
+    else if (g.HoverItemDelayId == 0)
+        g.HoverItemUnlockedStationaryId = 0;
+
     // Update hover delay for IsItemHovered() with delays and tooltips
     g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId;
     if (g.HoverItemDelayId != 0)
     {
-        //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags
         g.HoverItemDelayTimer += g.IO.DeltaTime;
         g.HoverItemDelayClearTimer = 0.0f;
         g.HoverItemDelayId = 0;
@@ -8535,9 +8550,17 @@ static void ImGui::UpdateMouseInputs()
     else
         io.MouseDelta = ImVec2(0.0f, 0.0f);
 
+    // Update stationary timer. Only reset on 2 successive moving frames.
+    // FIXME: May need to expose threshold or treat touch inputs differently.
+    const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework.
+    g.MouseMovingFrames = (ImLengthSqr(io.MouseDelta) >= mouse_stationary_threshold * mouse_stationary_threshold) ? (g.MouseMovingFrames + 1) : 0;
+    if (g.MouseMovingFrames == 0)
+        g.MouseStationaryTimer += io.DeltaTime;
+    else if (g.MouseMovingFrames > 1)
+        g.MouseStationaryTimer = 0.0f;
+
     // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true.
-    const bool is_stationary = (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f);
-    if (!is_stationary)
+    if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
         g.NavDisableMouseHover = false;
 
     io.MousePosPrev = io.MousePos;
@@ -13901,6 +13924,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
             Text("Mouse clicked:");  for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); }
             Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); }
             Text("Mouse wheel: %.1f", io.MouseWheel);
+            Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer);
             Text("Mouse source: %s", GetMouseSourceName(io.MouseSource));
             Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused
             Unindent();

+ 9 - 4
imgui.h

@@ -1285,10 +1285,14 @@ enum ImGuiHoveredFlags_
     ImGuiHoveredFlags_RectOnly                      = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped,
     ImGuiHoveredFlags_RootAndChildWindows           = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows,
 
-    // Mouse Hovering delays (for tooltips)
-    ImGuiHoveredFlags_DelayShort                    = 1 << 11,  // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec)
-    ImGuiHoveredFlags_DelayNormal                   = 1 << 12,  // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec)
-    ImGuiHoveredFlags_NoSharedDelay                 = 1 << 13,  // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)
+    // Mouse Hovering delays (e.g. for tooltips)
+    // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + normal delay) so the tooltip doesn't show too often.
+    // - for items which main purpose is to be hovered for a tooltip, or items with low affordance, prefer no delay or shorter delay.
+    ImGuiHoveredFlags_Stationary                    = 1 << 11,  // 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_DelayShort                    = 1 << 13,  // 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 << 14,  // 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).
+    ImGuiHoveredFlags_NoSharedDelay                 = 1 << 15,  // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays)
+    ImGuiHoveredFlags_ForTooltip                    = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayNormal,
 };
 
 // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload()
@@ -1891,6 +1895,7 @@ struct ImGuiStyle
     ImVec4      Colors[ImGuiCol_COUNT];
 
     // Behaviors
+    float             HoverStationaryDelay;     // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary.
     float             HoverDelayShort;          // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay.
     float             HoverDelayNormal;         // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). "
 

+ 25 - 6
imgui_demo.cpp

@@ -629,16 +629,16 @@ static void ShowDemoWindowWidgets()
         {
             // Tooltips
             IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips");
-            //ImGui::AlignTextToFramePadding();
+            ImGui::AlignTextToFramePadding();
             ImGui::Text("Tooltips:");
 
             ImGui::SameLine();
-            ImGui::SmallButton("Basic");
+            ImGui::Button("Basic");
             if (ImGui::IsItemHovered())
                 ImGui::SetTooltip("I am a tooltip");
 
             ImGui::SameLine();
-            ImGui::SmallButton("Fancy");
+            ImGui::Button("Fancy");
             if (ImGui::IsItemHovered() && ImGui::BeginTooltip())
             {
                 ImGui::Text("I am a fancy tooltip");
@@ -648,11 +648,22 @@ static void ShowDemoWindowWidgets()
                 ImGui::EndTooltip();
             }
 
+            // Showcase use of ImGuiHoveredFlags_ForTooltip which is an alias for ImGuiHoveredFlags_DelayNormal + ImGuiHoveredFlags_Stationary.
+            // - ImGuiHoveredFlags_DelayNormal requires an hovering delay (default to 0.40 sec)
+            // - ImGuiHoveredFlags_Stationary requires mouse to be stationary (default to 0.15 sec) at least once on a new item.
+            // We show two items to showcase how the main delay is by default shared between items,
+            // so once in "tooltip mode" moving to another tooltip only requires the stationary delay.
+
             ImGui::SameLine();
-            ImGui::SmallButton("Delayed");
-            if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) // With a delay
+            ImGui::Button("Delayed1");
+            if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
                 ImGui::SetTooltip("I am a tooltip with a delay.");
 
+            ImGui::SameLine();
+            ImGui::Button("Delayed2");
+            if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip))
+                ImGui::SetTooltip("I am another tooltip with a delay.");
+
             ImGui::SameLine();
             HelpMarker(
                 "Tooltip are created by using the IsItemHovered() function over any kind of item.");
@@ -2377,8 +2388,10 @@ static void ShowDemoWindowWidgets()
         if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", &current, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); }
 
         bool hovered_delay_none = ImGui::IsItemHovered();
+        bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary);
         bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort);
         bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal);
+        bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary
 
         // Display the values of IsItemHovered() and other common item state functions.
         // Note that the ImGuiHoveredFlags_XXX flags can be combined.
@@ -2425,7 +2438,13 @@ static void ShowDemoWindowWidgets()
             ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y
         );
         ImGui::BulletText(
-            "w/ Hovering Delay: None = %d, Fast %d, Normal = %d", hovered_delay_none, hovered_delay_short, hovered_delay_normal);
+            "with Hovering Delay or Stationary test:\n"
+            "IsItemHovered() = = %d\n"
+            "IsItemHovered(_Stationary) = %d\n"
+            "IsItemHovered(_DelayShort) = %d\n"
+            "IsItemHovered(_DelayNormal) = %d\n"
+            "IsItemHovered(_Tooltip) = %d",
+            hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip);
 
         if (item_disabled)
             ImGui::EndDisabled();

+ 6 - 1
imgui_internal.h

@@ -1963,9 +1963,12 @@ 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;
 
     // Mouse state
     ImGuiMouseCursor        MouseCursor;
+    int                     MouseMovingFrames;
+    float                   MouseStationaryTimer;
     ImVec2                  MouseLastValidPos;
 
     // Widget state
@@ -2164,10 +2167,12 @@ struct ImGuiContext
         TablesTempDataStacked = 0;
         CurrentTabBar = NULL;
 
-        HoverItemDelayId = HoverItemDelayIdPreviousFrame = 0;
+        HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = 0;
         HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f;
 
         MouseCursor = ImGuiMouseCursor_Arrow;
+        MouseMovingFrames = 0;
+        MouseStationaryTimer = 0.0f;
 
         TempInputId = 0;
         ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_;