Skip to content

Commit

Permalink
Clipper: added SeekCursorForItem() function, for use when using ImGui…
Browse files Browse the repository at this point in the history
…ListClipper::Begin(INT_MAX). (#1311)

Tagging #3609 just in case we made a mistake introducing a regression (but tests are passing and have been extended).
  • Loading branch information
ocornut committed Jul 17, 2024
1 parent 74a1854 commit 8bab3ea
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 14 deletions.
4 changes: 4 additions & 0 deletions docs/CHANGELOG.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Other changes:
Disabling this was previously possible for Selectable() via a direct flag but not for MenuItem().
(#1379, #1468, #2200, #4936, #5216, #7302, #7573)
- This was mostly all previously in imgui_internal.h.
- Clipper: added SeekCursorForItem() function. When using ImGuiListClipper::Begin(INT_MAX) you can
can use the clipper without knowing the amount of items beforehand. (#1311)
In this situation, call ImGuiListClipper::SeekCursorForItem(items_count) as the end of your iteration
loop to position the layout cursor correctly. This is done automatically if provided a count to Begin().
- Style: close button and collapse/window-menu button hover highlight made rectangular instead of round.
- Debug Tools: Added IMGUI_DEBUG_LOG(), ImGui::DebugLog() in public API. (#5855)
Debug log entries add a imgui frame counter prefix + are redirected to ShowDebugLogWindow() and
Expand Down
30 changes: 18 additions & 12 deletions imgui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2911,15 +2911,6 @@ static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_
}
}

static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n)
{
// StartPosY starts from ItemsFrozen hence the subtraction
// Perform the add and multiply with double to allow seeking through larger ranges
ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
float pos_y = (float)((double)clipper->StartPosY + data->LossynessOffset + (double)(item_n - data->ItemsFrozen) * clipper->ItemsHeight);
ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight);
}

ImGuiListClipper::ImGuiListClipper()
{
memset(this, 0, sizeof(*this));
Expand Down Expand Up @@ -2956,6 +2947,7 @@ void ImGuiListClipper::Begin(int items_count, float items_height)
data->Reset(this);
data->LossynessOffset = window->DC.CursorStartPosLossyness.y;
TempData = data;
StartSeekOffsetY = data->LossynessOffset;
}

void ImGuiListClipper::End()
Expand All @@ -2966,7 +2958,7 @@ void ImGuiListClipper::End()
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);
SeekCursorForItem(ItemsCount);

// Restore temporary buffer and fix back pointers which may be invalidated when nesting
IM_ASSERT(data->ListClipper == this);
Expand All @@ -2990,6 +2982,17 @@ void ImGuiListClipper::IncludeItemsByIndex(int item_begin, int item_end)
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_begin, item_end));
}

// This is already called while stepping.
// The ONLY reason you may want to call this is if you passed INT_MAX to ImGuiListClipper::Begin() because you couldn't step item count beforehand.
void ImGuiListClipper::SeekCursorForItem(int item_n)
{
// - Perform the add and multiply with double to allow seeking through larger ranges.
// - StartPosY starts from ItemsFrozen, by adding SeekOffsetY we generally cancel that out (SeekOffsetY == LossynessOffset - ItemsFrozen * ItemsHeight).
// - The reason we store SeekOffsetY instead of inferring it, is because we want to allow user to perform Seek after the last step, where ImGuiListClipperData is already done.
float pos_y = (float)((double)StartPosY + StartSeekOffsetY + (double)item_n * ItemsHeight);
ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, ItemsHeight);
}

static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
{
ImGuiContext& g = *clipper->Ctx;
Expand Down Expand Up @@ -3053,6 +3056,9 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
const int already_submitted = clipper->DisplayEnd;
if (calc_clipping)
{
// Record seek offset, this is so ImGuiListClipper::Seek() can be called after ImGuiListClipperData is done
clipper->StartSeekOffsetY = (double)data->LossynessOffset - data->ItemsFrozen * (double)clipper->ItemsHeight;

if (g.LogEnabled)
{
// If logging is active, do not perform any clipping
Expand Down Expand Up @@ -3100,7 +3106,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount);
if (clipper->DisplayStart > already_submitted) //-V1051
ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart);
clipper->SeekCursorForItem(clipper->DisplayStart);
data->StepNo++;
if (clipper->DisplayStart == clipper->DisplayEnd && data->StepNo < data->Ranges.Size)
continue;
Expand All @@ -3110,7 +3116,7 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper)
// After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd),
// Advance the cursor to the end of the list and then returns 'false' to end the loop.
if (clipper->ItemsCount < INT_MAX)
ImGuiListClipper_SeekCursorForItem(clipper, clipper->ItemsCount);
clipper->SeekCursorForItem(clipper->ItemsCount);

return false;
}
Expand Down
10 changes: 8 additions & 2 deletions imgui.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
// Library Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345')
#define IMGUI_VERSION "1.91.0 WIP"
#define IMGUI_VERSION_NUM 19094
#define IMGUI_VERSION_NUM 19095
#define IMGUI_HAS_TABLE

/*
Expand Down Expand Up @@ -2601,9 +2601,10 @@ struct ImGuiListClipper
int ItemsCount; // [Internal] Number of items
float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it
float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed
double StartSeekOffsetY; // [Internal] Account for frozen rows in a table and initial loss of precision in very large windows.
void* TempData; // [Internal] Internal data

// items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step)
// items_count: Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step, and you can call SeekCursorForItem() manually if you need)
// items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().
IMGUI_API ImGuiListClipper();
IMGUI_API ~ImGuiListClipper();
Expand All @@ -2616,6 +2617,11 @@ struct ImGuiListClipper
inline void IncludeItemByIndex(int item_index) { IncludeItemsByIndex(item_index, item_index + 1); }
IMGUI_API void IncludeItemsByIndex(int item_begin, int item_end); // item_end is exclusive e.g. use (42, 42+1) to make item 42 never clipped.

// Seek cursor toward given item. This is automatically called while stepping.
// - The only reason to call this is: you can use ImGuiListClipper::Begin(INT_MAX) if you don't know item count ahead of time.
// - In this case, after all steps are done, you'll want to call SeekCursorForItem(item_count).
IMGUI_API void SeekCursorForItem(int item_index);

#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline void IncludeRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.9]
inline void ForceDisplayRangeByIndices(int item_begin, int item_end) { IncludeItemsByIndex(item_begin, item_end); } // [renamed in 1.89.6]
Expand Down

0 comments on commit 8bab3ea

Please sign in to comment.