Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show tooltip when mouse inside a cell instead of an item #6347

Closed
SuperWangKai opened this issue Apr 18, 2023 · 7 comments
Closed

Show tooltip when mouse inside a cell instead of an item #6347

SuperWangKai opened this issue Apr 18, 2023 · 7 comments

Comments

@SuperWangKai
Copy link

SuperWangKai commented Apr 18, 2023

Hi Omar,

Thanks again for the amazing UI solution!
I'm using Table APIs and trying to to show a tooltip for each of the cells, however, if using ImGui::IsItemHovered() as the condition, the hovering rect is the actual item rect and if the item is very small, e.g. one character, it's very hard to have the tooltip showed.
Is there a way to trigger the tooltip in the range of a cell size?

Screen record
table_tooltip

Version/Branch of Dear ImGui:

Dear ImGui 1.89.4 WIP (18931)
--------------------------------

sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1935
define: _MSVC_LANG=201703
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------

io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx12
io.ConfigFlags: 0x00000443
 NavEnableKeyboard
 NavEnableGamepad
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------

io.Fonts: 3 fonts, Flags: 0x00000000, TexSize: 2048,2048
io.DisplaySize: 700.00,900.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------

style.WindowPadding: 12.00,12.00
style.WindowBorderSize: 1.00
style.FramePadding: 6.00,4.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 12.00,6.00
style.ItemInnerSpacing: 6.00,6.00

Code to reproduce the issue

constexpr ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders;
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();

// Test tooltip.
if (ImGui::TreeNodeEx("Properties", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_DefaultOpen))
{
    if (ImGui::BeginTable("prop", 2, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 10)))
    {
        ImGui::TableSetupScrollFreeze(0, 1);
        ImGui::TableSetupColumn("Property");
        ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
        // ImGui::TableHeadersRow();

        for (uint32_t i = 0; i < 10; ++i)
        {
            // Prop 1
            ImGui::TableNextRow();
            ImGui::TableNextColumn();
            ImGui::Text("Name_%u", i);

            // Tooltip for Prop 1
            if (ImGui::IsItemHovered())
            {
                ImGui::BeginTooltip();
                ImGui::Text("Tooltip on name %u", i);
                ImGui::EndTooltip();
            }
            // Value 1
            ImGui::TableNextColumn();
            ImGui::Text("Value_%u", i);

            // Tooltip for Value 1
            if (ImGui::IsItemHovered())
            {
                ImGui::BeginTooltip();
                ImGui::Text("Tooltip on value %u", i);
                ImGui::EndTooltip();
            }
        }

        ImGui::EndTable();
    }

    ImGui::TreePop();
}

Best regards,
Kai

@ocornut ocornut changed the title [Table] Show tooltip when mouse inside a cell instead of an item Show tooltip when mouse inside a cell instead of an item Apr 18, 2023
@ocornut
Copy link
Owner

ocornut commented Apr 18, 2023

This seems like the same as #6250 (comment)

TL;DR; There's no "one liner" way of telling is a cell is hovered in every situation because we can't always reliably get line height ahead of time. Essentially we are missing a TableGetHoveredRow() function. But if you call TableNextRow() with a height and don't step beyond this height, then you can tell the Y1/Y2 of your row.

Use TableGetHoveredColumn() + look at the code from TableGetCellBgRect() to find Y1/Y2 and you can then tell if a cell is hovered if you pass your row height ahead of time in TableNextRow()

@SuperWangKai
Copy link
Author

Hi Omar,

Thank you so much for the help.
I guess due to the fact that Text is used instead of a hoverable item? TableGetHoveredColumn always returns -1 here.

I'm now using column->ClipRect.Min to tell the column. Seems working here.

constexpr ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders;
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();

// Test tooltip.
if (ImGui::TreeNodeEx("Properties", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_DefaultOpen))
{
    if (ImGui::BeginTable("prop", 2, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 10)))
    {
        ImGui::TableSetupScrollFreeze(0, 1);
        ImGui::TableSetupColumn("Property");
        ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
        // ImGui::TableHeadersRow();

        auto mouse_pos = ImGui::GetMousePos();

        for (uint32_t i = 0; i < 10; ++i)
        {
            // Prop 1
            ImGui::TableNextRow(0, TEXT_BASE_HEIGHT); // WA for knowing the height of the row.
            ImGui::TableNextColumn();
            ImGui::Text("Name_%u", i);

            auto table = ImGui::GetCurrentTable();
            assert(table);

            //auto hovered_col = ImGui::TableGetHoveredColumn(); // hovered_col always equals -1

            ImGuiTableColumn* column = &table->Columns[0];

            // Tooltip for Prop1
            if ((mouse_pos.x > column->ClipRect.Min.x)
                && (mouse_pos.x < column->ClipRect.Max.x)
                && (mouse_pos.y > table->RowPosY1)
                && (mouse_pos.y < table->RowPosY2))
            {
                ImGui::BeginTooltip();
                ImGui::Text("Tooltip on name %u", i);
                ImGui::EndTooltip();
            }
            // Value 1
            ImGui::TableNextColumn();
            ImGui::Text("Value_%u", i);

            column = &table->Columns[1];

            // Tooltip for Value 1
            if ((mouse_pos.x > column->ClipRect.Min.x)
                && (mouse_pos.x < column->ClipRect.Max.x)
                && (mouse_pos.y > table->RowPosY1)
                && (mouse_pos.y < table->RowPosY2))
            {
                ImGui::BeginTooltip();
                ImGui::Text("Tooltip on value %u", i);
                ImGui::EndTooltip();
            }
        }

        ImGui::EndTable();
    }

    ImGui::TreePop();
}

@ocornut
Copy link
Owner

ocornut commented Apr 19, 2023

TableGetHoveredColumn always returns -1 here.

That's a problem which should be investigated.
If i look at the code it looks correct.
In TableUpdateLayout():

        // Detect hovered column
        if (is_hovering_table && g.IO.MousePos.x >= column->ClipRect.Min.x && g.IO.MousePos.x < column->ClipRect.Max.x)
            table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;

Can you check the value in Metrics?
image

            ImGuiTableColumn* column = &table->Columns[0];

            // Tooltip for Prop1
            if ((mouse_pos.x > column->ClipRect.Min.x)
                && (mouse_pos.x < column->ClipRect.Max.x)
                && (mouse_pos.y > table->RowPosY1)
                && (mouse_pos.y < table->RowPosY2))
            {

At minimum you should wrap this in a function, but I suspect you might as well simply use TableGetCellBgRect(g.CurrentTable, g.CurrentTable->CurrentColumn).Contains(mouse_pos);

My suggestion to use TableGetHoveredColumn() was to call a simplified version and only on column is hovered.

@SuperWangKai
Copy link
Author

SuperWangKai commented Apr 19, 2023

You are right. It's must too late yesterday :)

Both TableGetHoveredColumn and TableGetCellBgRect(g.CurrentTable, g.CurrentTable->CurrentColumn).Contains(mouse_pos); work.

So here is the code:

constexpr ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_Borders;
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();

// Test tooltip.
if (ImGui::TreeNodeEx("Properties", ImGuiTreeNodeFlags_SpanFullWidth | ImGuiTreeNodeFlags_DefaultOpen))
{
    if (ImGui::BeginTable("prop", 2, flags, ImVec2(0.0f, TEXT_BASE_HEIGHT * 10)))
    {
        ImGui::TableSetupScrollFreeze(0, 1);
        ImGui::TableSetupColumn("Property");
        ImGui::TableSetupColumn("Value", ImGuiTableColumnFlags_WidthStretch);
        // ImGui::TableHeadersRow();

        // Function returns if current cell is hovered.
        auto cell_hovered = []()
        {
            auto mouse_pos = ImGui::GetMousePos();
            auto table = ImGui::GetCurrentTable();

            return ImGui::TableGetCellBgRect(table, table->CurrentColumn).Contains(mouse_pos);
        };

        for (uint32_t i = 0; i < 10; ++i)
        {
            // Name
            ImGui::TableNextRow(0, TEXT_BASE_HEIGHT);  // WA for knowing the height of the row.
            ImGui::TableNextColumn();
            ImGui::Text("Name_%u", i);

            // Tooltip for name
            if (cell_hovered())
            {
                ImGui::BeginTooltip();
                ImGui::Text("Tooltip on Name %u", i);
                ImGui::EndTooltip();
            }
            // Value
            ImGui::TableNextColumn();
            ImGui::Text("Value_%u", i);

            // Tooltip for value
            if (cell_hovered())
            {
                ImGui::BeginTooltip();
                ImGui::Text("Tooltip on Value %u", i);
                ImGui::EndTooltip();
            }
        }

        ImGui::EndTable();
    }

    ImGui::TreePop();
}

@SuperWangKai
Copy link
Author

So here comes the next question... how to handle the delay for the tooltip.
Here #1485 we have GImGui->HoveredIdTimer > MY_TIME_THRESHOLD works great, I wonder how to simulate one in our cell case?

@ocornut
Copy link
Owner

ocornut commented Apr 19, 2023

Closing this.
My suggestion to use TableGetHoveredColumn() was only to make code a little bit faster:

if (column_hovered == 0 && row_hovered(0))

Where row_hovered can only do the Y1/Y2 check, but your solution seems to work.

I'll post here and #6250 if I decide to add a TableGetHoveredRow() or TableIsHoveredCell() etc.

@ocornut ocornut closed this as completed Apr 19, 2023
@ocornut
Copy link
Owner

ocornut commented Apr 19, 2023

So here comes the next question... how to handle the delay for the tooltip.

You will need to maintain the timer yourself.

As per #1485 (comment) while we added delay support in IsItemHovered() there is still a vague plan to add a similar feature in BeginTooltip() in the future.

ocornut added a commit that referenced this issue Jul 13, 2023
…#6588, #3740)

Works with one-frame delay inconsistent with other functions, may be too bug-prone.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants