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

ImGuiTableFlags_ScrollY cancels ImGuiChildFlags_NavFlattened #8280

Open
Brambor opened this issue Jan 1, 2025 · 2 comments
Open

ImGuiTableFlags_ScrollY cancels ImGuiChildFlags_NavFlattened #8280

Brambor opened this issue Jan 1, 2025 · 2 comments

Comments

@Brambor
Copy link

Brambor commented Jan 1, 2025

Version/Branch of Dear ImGui:

Version 1.95.5, Branch: XXX (master/docking/etc.); using CleverRaven/Cataclysm-DDA#78144

Back-ends:

n/a

Compiler, OS:

Windows 10 + MSVC 2019

Full config/build information:

Dear ImGui 1.91.5 (19150)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1929
define: _MSVC_LANG=201703
--------------------------------
io.BackendPlatformName: imgui_impl_sdl2
io.BackendRendererName: imgui_impl_sdlrenderer2
io.ConfigFlags: 0x00000003
 NavEnableKeyboard
 NavEnableGamepad
io.ConfigNavCaptureKeyboard
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x0000000E
 HasMouseCursors
 HasSetMousePos
 RendererHasVtxOffset
--------------------------------
io.Fonts: 2 fonts, Flags: 0x00000000, TexSize: 512,128
io.DisplaySize: 1536.00,841.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

Details:

I want a menu with:

  • buttons selectable with the mouse
  • table with a TableSetupScrollFreeze( 0, /*rows*/1 ) and with ImGuiTableFlags_ScrollY
  • entries of the table
  • table fills the rest of the screen
  • the user to (when they open the menu) use arrow keys to select entries of the menu, not the buttons, not the whole table

I tried making the buttons unselectable. When opening the menu, the user still has to first select the table and only then use up/down arrows to select entries:
image

Then I tried to put the table inside of BeginChild( "table", ImVec2( 0, 0 ), ImGuiChildFlags_NavFlattened ); to expose the entries allowing up/down arrows right away without the need to press enter to select the table first.

I worked out that this solution worked only as long, as BeginTable didn't have ImGuiTableFlags_ScrollY.

  • when it doesn't have the flag, on opening the menu and pressing the down arrow, an entry is selected
  • when it does have the flag, on opening the menu, the user has to select the table first - THIS FEELS LIKE A BUG

I need ImGuiTableFlags_ScrollY in order to have the header frozen. When I don't use it, the header can hide as the child window scrolls:
image

Important bits of my code I would expect to work (with ImGuiTableFlags_ScrollY), but it does not:

    ImGui::PushItemFlag( ImGuiItemFlags_NoNav, true );
    // draw buttons
    ImGui::PopItemFlag();  // ImGuiItemFlags_NoNav

    ImGui::BeginChild( "table", ImVec2( 0, 0 ), ImGuiChildFlags_NavFlattened );
    if( ! ImGui::BeginTable( "PATH_MANAGER", 6, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY ) ) {
        ImGui::EndChild();
        return;
    }
    ImGui::TableSetupScrollFreeze( 0, 1 );
    // draw table
    ImGui::EndTable();
    ImGui::EndChild();
Much more of my code
bool cataimgui::window::action_button( const std::string &action, const std::string &text )
{
    if( ImGui::Button( text.c_str() ) ) {
        button_action = action;
        return true;
    }
    return false;
}

void path_manager_ui::enabled_active_button( const std::string action, bool enabled )
{
    ImGui::BeginDisabled( !enabled );
    action_button( action, ctxt.get_button_text( action ) );
    ImGui::EndDisabled();
}

void path_manager_ui::draw_controls()
{
    // Put it all into a child window to use NavFlattened to select the first entry in the table
    // instead of the table itself.
    // make buttons unselectable with arrows
    ImGui::PushItemFlag( ImGuiItemFlags_NoNav, true );
    // walk buttons
    cataimgui::draw_colored_text( _( "Walk:" ) );
    ImGui::SameLine();
    enabled_active_button( "WALK_PATH", pimpl->avatar_at_what_start_or_end() != -1 );
    ImGui::SameLine();
    enabled_active_button( "WALK_PATH_FROM_MIDDLE", !pimpl->avatar_at_what_paths().empty() );

    // recording buttons
    cataimgui::draw_colored_text( _( "Recording:" ) );
    ImGui::SameLine();
    enabled_active_button( "START_RECORDING", !pimpl->is_recording_path() );
    ImGui::SameLine();
    enabled_active_button( "CONTINUE_RECORDING", !pimpl->is_recording_path()
                           && pimpl->avatar_at_what_start_or_end() != -1 );
    ImGui::SameLine();
    enabled_active_button( "STOP_RECORDING", pimpl->is_recording_path() );

    // manage buttons
    cataimgui::draw_colored_text( _( "Manage:" ) );
    ImGui::SameLine();
    enabled_active_button( "DELETE_PATH", pimpl->selected_id != -1 );
    ImGui::SameLine();
    enabled_active_button( "MOVE_PATH_UP", pimpl->selected_id > 0 );
    ImGui::SameLine();
    enabled_active_button( "MOVE_PATH_DOWN", pimpl->selected_id != -1 &&
                           pimpl->selected_id + 1 < static_cast<int>( pimpl->paths.size() ) );

    // name buttons
    cataimgui::draw_colored_text( _( "Rename:" ) );
    ImGui::SameLine();
    enabled_active_button( "RENAME_START", pimpl->selected_id != -1 );
    ImGui::SameLine();
    enabled_active_button( "RENAME_END", pimpl->selected_id != -1 );
    ImGui::SameLine();
    enabled_active_button( "SWAP_START_END", pimpl->selected_id != -1 );
    ImGui::PopItemFlag();  // ImGuiItemFlags_NoNav

    ImGui::BeginChild( "table", ImVec2( 0, 0 ), ImGuiChildFlags_NavFlattened );
    if( ! ImGui::BeginTable( "PATH_MANAGER", 6, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY ) ) {
        ImGui::EndChild();
        return;
    }
    // TODO invlet
    ImGui::TableSetupScrollFreeze( 0, 1 );
    ImGui::TableSetupColumn( _( "start name" ) );
    ImGui::TableSetupColumn( _( "start distance" ) );
    ImGui::TableSetupColumn( _( "end name" ) );
    ImGui::TableSetupColumn( _( "end distance" ) );
    ImGui::TableSetupColumn( _( "closest tile" ) );
    ImGui::TableSetupColumn( _( "total length" ) );
    ImGui::PushItemFlag( ImGuiItemFlags_NoNav, true );
    ImGui::TableHeadersRow();
    ImGui::PopItemFlag();  // ImGuiItemFlags_NoNav

    ImGuiListClipper clipper;
    clipper.Begin( pimpl->paths.size() );
    while( clipper.Step() ) {
        for( int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++ ) {
            const path &curr_path = pimpl->paths[i];
            ImGui::TableNextColumn();
            if( ImGui::Selectable( ( "##" + std::to_string( i ) ).c_str(), pimpl->selected_id == i,
                                   ImGuiSelectableFlags_SpanAllColumns )
              ) {
                pimpl->selected_id = i;
            }
            ImGui::SameLine();
            cataimgui::draw_colored_text( curr_path.name_start );

            ImGui::TableNextColumn();
            cataimgui::draw_colored_text( avatar_distance_from_tile( curr_path.recorded_path.front() ) );

            ImGui::TableNextColumn();
            cataimgui::draw_colored_text( curr_path.name_end );

            ImGui::TableNextColumn();
            cataimgui::draw_colored_text( avatar_distance_from_tile( curr_path.recorded_path.back() ) );

            ImGui::TableNextColumn();
            cataimgui::draw_colored_text( avatar_distance_from_tile(
                                              curr_path.recorded_path[curr_path.avatar_closest_i_approximate()] ) );

            ImGui::TableNextColumn();
            ImGui::Text( "%zu", curr_path.recorded_path.size() );
        }
    }
    ImGui::EndTable();
    ImGui::EndChild();
}

void cataimgui::window::draw()
{
    button_action.clear();
    if( !is_open ) {
        return;
    }
    bool handled_resize = false;
    if( is_bounds_changed() ) {
        cached_bounds = get_bounds();
        // we want to make sure is_resized is able to be handled for at least a full frame
        handled_resize = true;
    }
    if( cached_bounds.x == -1 || cached_bounds.y == -1 ) {
        ImVec2 center = ImGui::GetMainViewport()->GetCenter();
        if( cached_bounds.x != -1.f ) {
            center.x = cached_bounds.x;
        }
        if( cached_bounds.y != -1.f ) {
            center.y = cached_bounds.y;
        }
        ImGui::SetNextWindowPos( center, ImGuiCond_Always, { cached_bounds.x == -1.f ? 0.5f : 0.f,  cached_bounds.y == -1.f ? 0.5f : 0.f } );
    } else if( cached_bounds.x >= 0 && cached_bounds.y >= 0 ) {
        ImGui::SetNextWindowPos( { cached_bounds.x, cached_bounds.y } );
    }
    if( cached_bounds.h > 1.0 || cached_bounds.w > 1.0 ) {
        ImGui::SetNextWindowSize( { cached_bounds.w, cached_bounds.h } );
    } else if( cached_bounds.h > 0.0 && cached_bounds.w > 0.0 && cached_bounds.h <= 1.0 &&
               cached_bounds.w <= 1.0 ) {
        ImGui::SetNextWindowSize( ImGui::GetMainViewport()->Size * ImVec2 { cached_bounds.w, cached_bounds.h } );
    }
    if( ImGui::Begin( id.c_str(), &is_open, window_flags ) ) {
        draw_controls();
        if( p_impl->window_adaptor->is_on_top && !force_to_back ) {
            ImGui::BringWindowToDisplayFront( ImGui::GetCurrentWindow() );
        }
        if( handled_resize ) {
            point catapos;
            point catasize;
            ImVec2 impos = ImGui::GetWindowPos();
            ImVec2 imsize = ImGui::GetWindowSize();
            imvec2_to_point( &impos, &catapos );
            imvec2_to_point( &imsize, &catasize );
            p_impl->window_adaptor->position_absolute( catapos, catasize );
        }
    }
    ImGui::End();
    if( handled_resize ) {
        p_impl->is_resized = false;
    }
}

cataimgui::window::window( int window_flags )
{
    p_impl = nullptr;

    this->window_flags = window_flags | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
                         ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavFocus |
                         ImGuiWindowFlags_NoBringToFrontOnFocus;
}

path_manager_ui::path_manager_ui( path_manager_impl *pimpl_in )
    : cataimgui::window( _( "Path Manager" ) ), pimpl( pimpl_in ) {}

My question is: Why does ImGuiTableFlags_ScrollY cancel the effect of ImGuiChildFlags_NavFlattened?

Screenshots/Video:

No response

Minimal, Complete and Verifiable Example code:

I made it a part of imgui_demo.cpp.

#include <string>

void ShowExampleAppscrollYandflatnav(bool* p_open)
{
    ImGui::Begin( "scroll Y breaks nav", p_open );

    ImGui::BeginChild( "table", ImVec2( 100, 100 ), ImGuiChildFlags_NavFlattened );
    // Deleting ImGuiTableFlags_ScrollY makes ImGuiChildFlags_NavFlattened work
    ImGui::BeginTable( "PATH_MANAGER", 1, ImGuiTableFlags_ScrollY );
    for( int i = 0; i < 3; ++i ) {
        ImGui::TableNextColumn();
        ImGui::Button( std::to_string( i ).c_str() );
    }
    ImGui::EndTable();
    ImGui::EndChild();

    ImGui::End();
}
  1. Compile, run, open the menu.
  2. Press arrow down
  3. Observe: you get
    • image
    • Note: whole table is selected.
  4. change line ImGui::BeginTable( "PATH_MANAGER", 1, ImGuiTableFlags_ScrollY ); to ImGui::BeginTable( "PATH_MANAGER", 1 );
  5. Compile, run, open the menu.
  6. Press arrow down
  7. Observe: you get
    • image
    • Note: Entry 2 is selected.
@Brambor
Copy link
Author

Brambor commented Jan 1, 2025

I minimized the "Minimal, Complete and Verifiable Example" nicely and provided reproduction steps.

@Brambor
Copy link
Author

Brambor commented Jan 1, 2025

It would be nice if I could do ImGuiChildFlags_NavFlattened for a table and didn't have to do it with the BeginChild.

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