|
@@ -405,6 +405,7 @@ CODE
|
|
|
- likewise io.MousePos and GetMousePos() will use OS coordinates.
|
|
|
If you query mouse positions to interact with non-imgui coordinates you will need to offset them, e.g. subtract GetWindowViewport()->Pos.
|
|
|
|
|
|
+ - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage.
|
|
|
- 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3.
|
|
|
- 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago:
|
|
|
- ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference)
|
|
@@ -1001,6 +1002,9 @@ static const float WINDOWS_HOVER_PADDING = 4.0f; // Exten
|
|
|
static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time.
|
|
|
static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved.
|
|
|
|
|
|
+// Tooltip offset
|
|
|
+static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale
|
|
|
+
|
|
|
// Docking
|
|
|
static const float DOCKING_TRANSPARENT_PAYLOAD_ALPHA = 0.50f; // For use with io.ConfigDockingTransparentPayload. Apply to Viewport _or_ WindowBg in host viewport.
|
|
|
static const float DOCKING_SPLITTER_SIZE = 2.0f;
|
|
@@ -1183,6 +1187,13 @@ ImGuiStyle::ImGuiStyle()
|
|
|
CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality.
|
|
|
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). "
|
|
|
+ HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse.
|
|
|
+ HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad.
|
|
|
+
|
|
|
// Default theme
|
|
|
ImGui::StyleColorsDark(this);
|
|
|
}
|
|
@@ -1231,16 +1242,10 @@ ImGuiIO::ImGuiIO()
|
|
|
IniSavingRate = 5.0f;
|
|
|
IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables).
|
|
|
LogFilename = "imgui_log.txt";
|
|
|
- MouseDoubleClickTime = 0.30f;
|
|
|
- MouseDoubleClickMaxDist = 6.0f;
|
|
|
#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO
|
|
|
for (int i = 0; i < ImGuiKey_COUNT; i++)
|
|
|
KeyMap[i] = -1;
|
|
|
#endif
|
|
|
- KeyRepeatDelay = 0.275f;
|
|
|
- KeyRepeatRate = 0.050f;
|
|
|
- HoverDelayNormal = 0.30f;
|
|
|
- HoverDelayShort = 0.10f;
|
|
|
UserData = NULL;
|
|
|
|
|
|
Fonts = NULL;
|
|
@@ -1278,6 +1283,13 @@ ImGuiIO::ImGuiIO()
|
|
|
ConfigDebugBeginReturnValueOnce = false;
|
|
|
ConfigDebugBeginReturnValueLoop = false;
|
|
|
|
|
|
+ // Inputs Behaviors
|
|
|
+ MouseDoubleClickTime = 0.30f;
|
|
|
+ MouseDoubleClickMaxDist = 6.0f;
|
|
|
+ MouseDragThreshold = 6.0f;
|
|
|
+ KeyRepeatDelay = 0.275f;
|
|
|
+ KeyRepeatRate = 0.050f;
|
|
|
+
|
|
|
// Platform Functions
|
|
|
// Note: Initialize() will setup default clipboard/ime handlers.
|
|
|
BackendPlatformName = BackendRendererName = NULL;
|
|
@@ -1287,7 +1299,6 @@ ImGuiIO::ImGuiIO()
|
|
|
MousePos = ImVec2(-FLT_MAX, -FLT_MAX);
|
|
|
MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX);
|
|
|
MouseSource = ImGuiMouseSource_Mouse;
|
|
|
- MouseDragThreshold = 6.0f;
|
|
|
for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f;
|
|
|
for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; }
|
|
|
AppAcceptingEvents = true;
|
|
@@ -2807,9 +2818,6 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it
|
|
|
ImGuiListClipper::ImGuiListClipper()
|
|
|
{
|
|
|
memset(this, 0, sizeof(*this));
|
|
|
- Ctx = ImGui::GetCurrentContext();
|
|
|
- IM_ASSERT(Ctx != NULL);
|
|
|
- ItemsCount = -1;
|
|
|
}
|
|
|
|
|
|
ImGuiListClipper::~ImGuiListClipper()
|
|
@@ -2819,6 +2827,9 @@ ImGuiListClipper::~ImGuiListClipper()
|
|
|
|
|
|
void ImGuiListClipper::Begin(int items_count, float items_height)
|
|
|
{
|
|
|
+ if (Ctx == NULL)
|
|
|
+ Ctx = ImGui::GetCurrentContext();
|
|
|
+
|
|
|
ImGuiContext& g = *Ctx;
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name);
|
|
@@ -2844,10 +2855,10 @@ void ImGuiListClipper::Begin(int items_count, float items_height)
|
|
|
|
|
|
void ImGuiListClipper::End()
|
|
|
{
|
|
|
- ImGuiContext& g = *Ctx;
|
|
|
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
|
|
|
{
|
|
|
// In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
|
|
|
+ ImGuiContext& g = *Ctx;
|
|
|
IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name);
|
|
|
if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
|
|
|
ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
|
|
@@ -3573,6 +3584,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx)
|
|
|
// IMPORTANT: ###xxx suffixes must be same in ALL languages
|
|
|
static const ImGuiLocEntry GLocalizationEntriesEnUS[] =
|
|
|
{
|
|
|
+ { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" },
|
|
|
{ ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" },
|
|
|
{ ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" },
|
|
|
{ ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" },
|
|
@@ -4013,6 +4025,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
|
|
@@ -4020,12 +4042,17 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* window = g.CurrentWindow;
|
|
|
+ IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!");
|
|
|
+
|
|
|
if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride))
|
|
|
{
|
|
|
if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled))
|
|
|
return false;
|
|
|
if (!IsItemFocused())
|
|
|
return false;
|
|
|
+
|
|
|
+ if (flags & ImGuiHoveredFlags_ForTooltip)
|
|
|
+ flags |= g.Style.HoverFlagsForTooltipNav;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -4033,6 +4060,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
|
|
|
ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags;
|
|
|
if (!(status_flags & ImGuiItemStatusFlags_HoveredRect))
|
|
|
return false;
|
|
|
+
|
|
|
+ if (flags & ImGuiHoveredFlags_ForTooltip)
|
|
|
+ flags |= g.Style.HoverFlagsForTooltipMouse;
|
|
|
+
|
|
|
IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_DockHierarchy)) == 0); // Flags not supported by this function
|
|
|
|
|
|
// Done with rectangle culling so we can perform heavier checks now
|
|
@@ -4069,20 +4100,22 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags)
|
|
|
|
|
|
// Handle hover delay
|
|
|
// (some ideas: https://www.nngroup.com/articles/timing-exposing-content)
|
|
|
- float delay;
|
|
|
- if (flags & ImGuiHoveredFlags_DelayNormal)
|
|
|
- delay = g.IO.HoverDelayNormal;
|
|
|
- else if (flags & ImGuiHoveredFlags_DelayShort)
|
|
|
- delay = g.IO.HoverDelayShort;
|
|
|
- else
|
|
|
- delay = 0.0f;
|
|
|
- if (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);
|
|
|
- if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverDelayIdPreviousFrame != hover_delay_id))
|
|
|
- g.HoverDelayTimer = 0.0f;
|
|
|
- g.HoverDelayId = hover_delay_id;
|
|
|
- return g.HoverDelayTimer >= delay;
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -4716,21 +4749,33 @@ 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;
|
|
|
+ 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.HoverDelayIdPreviousFrame = g.HoverDelayId;
|
|
|
- if (g.HoverDelayId != 0)
|
|
|
+ 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.HoverDelayTimer += g.IO.DeltaTime;
|
|
|
- g.HoverDelayClearTimer = 0.0f;
|
|
|
- g.HoverDelayId = 0;
|
|
|
+ g.HoverItemDelayTimer += g.IO.DeltaTime;
|
|
|
+ g.HoverItemDelayClearTimer = 0.0f;
|
|
|
+ g.HoverItemDelayId = 0;
|
|
|
}
|
|
|
- else if (g.HoverDelayTimer > 0.0f)
|
|
|
+ else if (g.HoverItemDelayTimer > 0.0f)
|
|
|
{
|
|
|
// This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps
|
|
|
- g.HoverDelayClearTimer += g.IO.DeltaTime;
|
|
|
- if (g.HoverDelayClearTimer >= ImMax(0.20f, g.IO.DeltaTime * 2.0f)) // ~6 frames at 30 Hz + allow for low framerate
|
|
|
- g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
|
|
|
+ // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle.
|
|
|
+ g.HoverItemDelayClearTimer += g.IO.DeltaTime;
|
|
|
+ if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate
|
|
|
+ g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer.
|
|
|
}
|
|
|
|
|
|
// Drag and drop
|
|
@@ -6452,12 +6497,13 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags
|
|
|
// When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing)
|
|
|
// should be positioned behind that modal window, unless the window was created inside the modal begin-stack.
|
|
|
// In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent.
|
|
|
-// - Window // FindBlockingModal() returns Modal1
|
|
|
-// - Window // .. returns Modal1
|
|
|
+// - WindowA // FindBlockingModal() returns Modal1
|
|
|
+// - WindowB // .. returns Modal1
|
|
|
// - Modal1 // .. returns Modal2
|
|
|
-// - Window // .. returns Modal2
|
|
|
-// - Window // .. returns Modal2
|
|
|
+// - WindowC // .. returns Modal2
|
|
|
+// - WindowD // .. returns Modal2
|
|
|
// - Modal2 // .. returns Modal2
|
|
|
+// - WindowE // .. returns NULL
|
|
|
// Notes:
|
|
|
// - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL.
|
|
|
// Only difference is here we check for ->Active/WasActive but it may be unecessary.
|
|
@@ -6468,7 +6514,7 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
|
|
|
return NULL;
|
|
|
|
|
|
// Find a modal that has common parent with specified window. Specified window should be positioned behind that modal.
|
|
|
- for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--)
|
|
|
+ for (int i = 0; i < g.OpenPopupStack.Size; i++)
|
|
|
{
|
|
|
ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window;
|
|
|
if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal))
|
|
@@ -6477,11 +6523,9 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window)
|
|
|
continue;
|
|
|
if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click.
|
|
|
return popup_window;
|
|
|
- if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed.
|
|
|
- break;
|
|
|
- for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow)
|
|
|
- if (IsWindowWithinBeginStackOf(window, parent))
|
|
|
- return popup_window; // Place window above its begin stack parent.
|
|
|
+ if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal
|
|
|
+ continue;
|
|
|
+ return popup_window; // Place window right below first block modal
|
|
|
}
|
|
|
return NULL;
|
|
|
}
|
|
@@ -7760,7 +7804,8 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b
|
|
|
|
|
|
bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
|
|
|
{
|
|
|
- IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function
|
|
|
+ IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!");
|
|
|
+
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
ImGuiWindow* ref_window = g.HoveredWindow;
|
|
|
ImGuiWindow* cur_window = g.CurrentWindow;
|
|
@@ -7789,6 +7834,17 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags)
|
|
|
if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem))
|
|
|
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;
|
|
|
}
|
|
|
|
|
@@ -9127,6 +9183,15 @@ 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.
|
|
|
if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)
|
|
|
g.NavDisableMouseHover = false;
|
|
@@ -10695,26 +10760,35 @@ bool ImGui::BeginTooltip()
|
|
|
return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
|
|
|
}
|
|
|
|
|
|
+bool ImGui::BeginItemTooltip()
|
|
|
+{
|
|
|
+ if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip))
|
|
|
+ return false;
|
|
|
+ return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None);
|
|
|
+}
|
|
|
+
|
|
|
bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags)
|
|
|
{
|
|
|
ImGuiContext& g = *GImGui;
|
|
|
|
|
|
if (g.DragDropWithinSource || g.DragDropWithinTarget)
|
|
|
{
|
|
|
- // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor)
|
|
|
- // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor.
|
|
|
- // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do.
|
|
|
+ // Drag and Drop tooltips are positioning differently than other tooltips:
|
|
|
+ // - offset visibility to increase visibility around mouse.
|
|
|
+ // - never clamp within outer viewport boundary.
|
|
|
+ // We call SetNextWindowPos() to enforce position and disable clamping.
|
|
|
+ // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones).
|
|
|
//ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding;
|
|
|
- ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale);
|
|
|
+ ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale;
|
|
|
SetNextWindowPos(tooltip_pos);
|
|
|
SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f);
|
|
|
//PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :(
|
|
|
- tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip;
|
|
|
+ tooltip_flags |= ImGuiTooltipFlags_OverridePrevious;
|
|
|
}
|
|
|
|
|
|
char window_name[16];
|
|
|
ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount);
|
|
|
- if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip)
|
|
|
+ if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious)
|
|
|
if (ImGuiWindow* window = FindWindowByName(window_name))
|
|
|
if (window->Active)
|
|
|
{
|
|
@@ -10738,22 +10812,40 @@ void ImGui::EndTooltip()
|
|
|
End();
|
|
|
}
|
|
|
|
|
|
+void ImGui::SetTooltip(const char* fmt, ...)
|
|
|
+{
|
|
|
+ va_list args;
|
|
|
+ va_start(args, fmt);
|
|
|
+ SetTooltipV(fmt, args);
|
|
|
+ va_end(args);
|
|
|
+}
|
|
|
+
|
|
|
void ImGui::SetTooltipV(const char* fmt, va_list args)
|
|
|
{
|
|
|
- if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None))
|
|
|
+ if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None))
|
|
|
return;
|
|
|
TextV(fmt, args);
|
|
|
EndTooltip();
|
|
|
}
|
|
|
|
|
|
-void ImGui::SetTooltip(const char* fmt, ...)
|
|
|
+// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'.
|
|
|
+// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse.
|
|
|
+void ImGui::SetItemTooltip(const char* fmt, ...)
|
|
|
{
|
|
|
va_list args;
|
|
|
va_start(args, fmt);
|
|
|
- SetTooltipV(fmt, args);
|
|
|
+ if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
|
|
|
+ SetTooltipV(fmt, args);
|
|
|
va_end(args);
|
|
|
}
|
|
|
|
|
|
+void ImGui::SetItemTooltipV(const char* fmt, va_list args)
|
|
|
+{
|
|
|
+ if (IsItemHovered(ImGuiHoveredFlags_ForTooltip))
|
|
|
+ SetTooltipV(fmt, args);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
//-----------------------------------------------------------------------------
|
|
|
// [SECTION] POPUPS
|
|
|
//-----------------------------------------------------------------------------
|
|
@@ -11287,15 +11379,20 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window)
|
|
|
}
|
|
|
if (window->Flags & ImGuiWindowFlags_Tooltip)
|
|
|
{
|
|
|
- // Position tooltip (always follows mouse)
|
|
|
- float sc = g.Style.MouseCursorScale;
|
|
|
- ImVec2 ref_pos = NavCalcPreferredRefPos();
|
|
|
+ // Position tooltip (always follows mouse + clamp within outer boundaries)
|
|
|
+ // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position.
|
|
|
+ // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin()
|
|
|
+ IM_ASSERT(g.CurrentWindow == window);
|
|
|
+ const float scale = g.Style.MouseCursorScale;
|
|
|
+ const ImVec2 ref_pos = NavCalcPreferredRefPos();
|
|
|
+ const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale;
|
|
|
ImRect r_avoid;
|
|
|
if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos))
|
|
|
r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8);
|
|
|
else
|
|
|
- r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
|
|
|
- return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
|
|
|
+ r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important.
|
|
|
+ //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255));
|
|
|
+ return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip);
|
|
|
}
|
|
|
IM_ASSERT(0);
|
|
|
return window->Pos;
|
|
@@ -19040,7 +19137,7 @@ void ImGui::DebugTextEncoding(const char* str)
|
|
|
static void MetricsHelpMarker(const char* desc)
|
|
|
{
|
|
|
ImGui::TextDisabled("(?)");
|
|
|
- if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip())
|
|
|
+ if (ImGui::BeginItemTooltip())
|
|
|
{
|
|
|
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
|
|
|
ImGui::TextUnformatted(desc);
|
|
@@ -19135,7 +19232,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
|
|
else if (rect_type == WRT_InnerRect) { return window->InnerRect; }
|
|
|
else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; }
|
|
|
else if (rect_type == WRT_WorkRect) { return window->WorkRect; }
|
|
|
- else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
|
|
|
+ else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); }
|
|
|
else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); }
|
|
|
else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; }
|
|
|
IM_ASSERT(0);
|
|
@@ -19512,6 +19609,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();
|
|
@@ -19589,7 +19687,7 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
|
|
Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL");
|
|
|
Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask);
|
|
|
Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame
|
|
|
- Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer);
|
|
|
+ Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer);
|
|
|
Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize);
|
|
|
DebugLocateItemOnHover(g.DragDropPayload.SourceId);
|
|
|
Unindent();
|