From b60acfa87d02065c9aa1fe25438496bab5ba82ef Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 16 Jun 2023 16:42:56 +0200 Subject: [PATCH] Tooltips: Added SetItemTooltip(), BeginItemTooltip(). Improved Demo section. --- docs/CHANGELOG.txt | 13 ++++- imgui.cpp | 26 ++++++++- imgui.h | 9 +++- imgui_demo.cpp | 128 +++++++++++++++++++++++++++------------------ imgui_tables.cpp | 4 +- imgui_widgets.cpp | 5 +- 6 files changed, 124 insertions(+), 61 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3d64639db..d98b0ac13 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,10 +51,18 @@ Other changes: from style.HoverFlagsForTooltipMouse or style.HoverFlagsForTooltipNav. (#1485) - style.HoverFlagsForTooltipMouse defaults to ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort - style.HoverFlagsForTooltipNav defaults to ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal - - 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) + - Tooltips: Added SetItemTooltip() and BeginItemTooltip() functions. + They are shortcuts for the common idiom of using IsItemHovered(). + - SetItemTooltip("Hello") == if (IsItemHovered(ImGuiHoveredFlags_Tooltip)) { SetTooltip("Hello"); } + - BeginItemTooltip() == IsItemHovered(ImGuiHoveredFlags_Tooltip) && BeginTooltip() + The newly added ImGuiHoveredFlags_Tooltip is meant to standardize mouse hovering + delays and rules for a whole application. + The previously common idiom of using 'if (IsItemHovered()) { SetTooltip(...); }' won't + use delay or stationary test. - 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) + - 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) - 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. @@ -64,6 +72,7 @@ Other changes: moved below the lowest blocking modal (rather than the highest one). (#4317) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. +- Demo: Added more developed "Widgets->Tooltips" section. (#1485) - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] - Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely diff --git a/imgui.cpp b/imgui.cpp index 1ec727341..9f0005205 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10088,6 +10088,13 @@ 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; @@ -10149,6 +10156,23 @@ void ImGui::SetTooltipV(const char* fmt, va_list args) EndTooltip(); } +// 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); + 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 @@ -13533,7 +13557,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); diff --git a/imgui.h b/imgui.h index 18fe7b434..c955a6842 100644 --- a/imgui.h +++ b/imgui.h @@ -667,10 +667,17 @@ namespace ImGui // Tooltips // - Tooltips are windows following the mouse. They do not take focus away. IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). - IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip() returns true! + IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true! IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Tooltips: helper for showing a tooltip when hovering an item + // - BeginItemTooltip(), SetItemTooltip() are shortcuts for the 'if (IsItemHovered(ImGuiHoveredFlags_Tooltip)) { BeginTooltip() or SetTooltip() }' idiom. + // - Where 'ImGuiHoveredFlags_Tooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered. + IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceeding item was hovered. override any previous call to SetTooltip(). + IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8a3e3813d..8bdfd6ec7 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -211,7 +211,7 @@ static void ShowDemoWindowInputs(); static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); @@ -626,48 +626,8 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); - { - // Tooltips - IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); - ImGui::AlignTextToFramePadding(); - ImGui::Text("Tooltips:"); - - ImGui::SameLine(); - ImGui::Button("Basic"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - - ImGui::SameLine(); - ImGui::Button("Fancy"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) - { - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); - 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::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."); - } + ImGui::Button("Tooltip"); + ImGui::SetItemTooltip("I am a tooltip"); ImGui::LabelText("label", "Value"); @@ -802,6 +762,73 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tooltips"); + if (ImGui::TreeNode("Tooltips")) + { + // Tooltips are windows following the mouse. They do not take focus away. + ImGui::SeparatorText("General"); + + // Typical use cases: + // - Short-form (text only): SetItemTooltip("Hello"); + // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); } + + // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); } + // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); } + + HelpMarker( + "Tooltip are typically created by using the IsItemHovered() + SetTooltip() functions over any kind of item.\n\n" + "We provide a helper SetItemTooltip() function to perform the two with standards flags."); + + ImVec2 sz = ImVec2(-FLT_MIN, 0.0f); + + ImGui::Button("Basic", sz); + ImGui::SetItemTooltip("I am a tooltip"); + + ImGui::Button("Fancy", sz); + if (ImGui::BeginItemTooltip()) + { + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); + ImGui::EndTooltip(); + } + + ImGui::SeparatorText("Custom"); + + // Showcase NOT relying on a IsItemHovered() to emit a tooltip. + static bool always_on = false; + ImGui::Checkbox("Always On", &always_on); + if (always_on) + ImGui::SetTooltip("I am following you around."); + + // The following examples are passed for documentation purpose but may not be useful to most users. + // Passing ImGuiHoveredFlags_Tooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from + // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used. + // With default settings, ImGuiHoveredFlags_Tooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary. + ImGui::Button("Manual", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ImGui::SetTooltip("I am a manually emitted tooltip"); + + ImGui::Button("DelayNone", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone)) + ImGui::SetTooltip("I am a tooltip with no delay."); + + ImGui::Button("DelayShort", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort); + + ImGui::Button("DelayLong", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec)", ImGui::GetStyle().HoverDelayNormal); + + ImGui::Button("Stationary", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary)) + ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating."); + + ImGui::TreePop(); + } + // Testing ImGuiOnceUponAFrame helper. //static ImGuiOnceUponAFrame once; //for (int i = 0; i < 5; i++) @@ -1067,7 +1094,7 @@ static void ShowDemoWindowWidgets() ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint ImVec4 border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border); ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { float region_sz = 32.0f; float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; @@ -2805,7 +2832,7 @@ static void ShowDemoWindowLayout() ImGui::PushID(i); ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + //ImGui::SetItemTooltip("ListBox %d hovered", i); } ImGui::PopItemWidth(); @@ -2858,8 +2885,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(); ImGui::Button("EEE"); ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); + ImGui::SetItemTooltip("First group hovered"); } // Capture the group size and create widgets using the same size ImVec2 size = ImGui::GetItemRectSize(); @@ -3420,8 +3446,7 @@ static void ShowDemoWindowPopups() ImGui::Separator(); ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); + ImGui::SetItemTooltip("I am a tooltip over a popup"); if (ImGui::Button("Stacked Popup")) ImGui::OpenPopup("another popup"); @@ -3505,8 +3530,7 @@ static void ShowDemoWindowPopups() ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Right-click to open popup"); + ImGui::SetItemTooltip("Right-click to open popup"); } } @@ -3758,7 +3782,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) } ImGui::SameLine(); ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); for (int m = 0; m < IM_ARRAYSIZE(policies); m++) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 4090e5446..c5534a513 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3036,8 +3036,8 @@ void ImGui::TableHeader(const char* label) RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); - if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(label_end - label), label); + if (text_clipped && hovered && g.ActiveId == 0) + SetItemTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsMouseReleased(1) && IsItemHovered()) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 845949484..b85c497aa 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5801,7 +5801,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl } // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; @@ -8474,8 +8474,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button)