mirror of
https://github.com/Drezil/imgui.git
synced 2024-11-22 11:57:00 +00:00
ImGuiListClipper: internal rework and tidying up to facilitate supporting frozen rows in tables + stop promoting using constructors parameters.
This commit is contained in:
parent
324e0310ad
commit
52c0b1a340
130
imgui.cpp
130
imgui.cpp
@ -2192,35 +2192,44 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
|
|||||||
columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
|
columns->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1
|
ImGuiListClipper::ImGuiListClipper(int items_count, float items_height)
|
||||||
|
{
|
||||||
|
DisplayStart = DisplayEnd = 0;
|
||||||
|
ItemsCount = -1;
|
||||||
|
StepNo = 0;
|
||||||
|
ItemsHeight = StartPosY = 0.0f;
|
||||||
|
Begin(items_count, items_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiListClipper::~ImGuiListClipper()
|
||||||
|
{
|
||||||
|
IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
|
||||||
// Use case B: Begin() called from constructor with items_height>0
|
// Use case B: Begin() called from constructor with items_height>0
|
||||||
// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
|
// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style.
|
||||||
void ImGuiListClipper::Begin(int count, float items_height)
|
void ImGuiListClipper::Begin(int items_count, float items_height)
|
||||||
{
|
{
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
ImGuiWindow* window = g.CurrentWindow;
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
|
|
||||||
StartPosY = window->DC.CursorPos.y;
|
StartPosY = window->DC.CursorPos.y;
|
||||||
ItemsHeight = items_height;
|
ItemsHeight = items_height;
|
||||||
ItemsCount = count;
|
ItemsCount = items_count;
|
||||||
StepNo = 0;
|
StepNo = 0;
|
||||||
DisplayEnd = DisplayStart = -1;
|
DisplayStart = -1;
|
||||||
if (ItemsHeight > 0.0f)
|
DisplayEnd = 0;
|
||||||
{
|
|
||||||
ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display
|
|
||||||
if (DisplayStart > 0)
|
|
||||||
SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor
|
|
||||||
StepNo = 2;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiListClipper::End()
|
void ImGuiListClipper::End()
|
||||||
{
|
{
|
||||||
if (ItemsCount < 0)
|
if (ItemsCount < 0) // Already ended
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
|
// In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user.
|
||||||
if (ItemsCount < INT_MAX)
|
if (ItemsCount < INT_MAX && DisplayStart >= 0)
|
||||||
SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
|
SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight);
|
||||||
ItemsCount = -1;
|
ItemsCount = -1;
|
||||||
StepNo = 3;
|
StepNo = 3;
|
||||||
}
|
}
|
||||||
@ -2230,38 +2239,70 @@ bool ImGuiListClipper::Step()
|
|||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
ImGuiWindow* window = g.CurrentWindow;
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
|
|
||||||
if (ItemsCount == 0 || window->SkipItems)
|
// Reached end of list
|
||||||
|
if (DisplayEnd >= ItemsCount || window->SkipItems)
|
||||||
{
|
{
|
||||||
|
End();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
|
||||||
|
if (StepNo == 0)
|
||||||
|
{
|
||||||
|
StartPosY = window->DC.CursorPos.y;
|
||||||
|
if (ItemsHeight <= 0.0f)
|
||||||
|
{
|
||||||
|
// Submit the first item so we can measure its height (generally it is 0..1)
|
||||||
|
DisplayStart = 0;
|
||||||
|
DisplayEnd = 1;
|
||||||
|
StepNo = 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already has item height (given by user in Begin): skip to calculating step
|
||||||
|
DisplayStart = DisplayEnd;
|
||||||
|
StepNo = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 1: the clipper infer height from first element
|
||||||
|
if (StepNo == 1)
|
||||||
|
{
|
||||||
|
IM_ASSERT(ItemsHeight <= 0.0f);
|
||||||
|
ItemsHeight = window->DC.CursorPos.y - StartPosY;
|
||||||
|
IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
|
||||||
|
StepNo = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: calculate the actual range of elements to display, and position the cursor before the first element
|
||||||
|
if (StepNo == 2)
|
||||||
|
{
|
||||||
|
IM_ASSERT(ItemsHeight > 0.0f);
|
||||||
|
|
||||||
|
int already_submitted = DisplayEnd;
|
||||||
|
ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd);
|
||||||
|
DisplayStart += already_submitted;
|
||||||
|
DisplayEnd += already_submitted;
|
||||||
|
|
||||||
|
// Seek cursor
|
||||||
|
if (DisplayStart > already_submitted)
|
||||||
|
SetCursorPosYAndSetupForPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight);
|
||||||
|
|
||||||
|
StepNo = 3;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: 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 (StepNo == 3)
|
||||||
|
{
|
||||||
|
// Seek cursor
|
||||||
|
if (ItemsCount < INT_MAX)
|
||||||
|
SetCursorPosYAndSetupForPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor
|
||||||
ItemsCount = -1;
|
ItemsCount = -1;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height.
|
|
||||||
{
|
IM_ASSERT(0);
|
||||||
DisplayStart = 0;
|
|
||||||
DisplayEnd = 1;
|
|
||||||
StartPosY = window->DC.CursorPos.y;
|
|
||||||
StepNo = 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element.
|
|
||||||
{
|
|
||||||
if (ItemsCount == 1) { ItemsCount = -1; return false; }
|
|
||||||
float items_height = window->DC.CursorPos.y - StartPosY;
|
|
||||||
IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically
|
|
||||||
Begin(ItemsCount - 1, items_height);
|
|
||||||
DisplayStart++;
|
|
||||||
DisplayEnd++;
|
|
||||||
StepNo = 3;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (StepNo == 2) // Step 2: empty step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3.
|
|
||||||
{
|
|
||||||
IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0);
|
|
||||||
StepNo = 3;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (StepNo == 3) // Step 3: 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.
|
|
||||||
End();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8062,7 +8103,7 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s
|
|||||||
|
|
||||||
// For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
|
// For tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible.
|
||||||
if (policy == ImGuiPopupPositionPolicy_Tooltip)
|
if (policy == ImGuiPopupPositionPolicy_Tooltip)
|
||||||
return ref_pos + ImVec2(2, 2);
|
return ref_pos + ImVec2(2, 2);
|
||||||
|
|
||||||
// Otherwise try to keep within display
|
// Otherwise try to keep within display
|
||||||
ImVec2 pos = ref_pos;
|
ImVec2 pos = ref_pos;
|
||||||
@ -10439,7 +10480,8 @@ void ImGui::ShowMetricsWindow(bool* p_open)
|
|||||||
NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false);
|
NodeDrawCmdShowMeshAndBoundingBox(window, draw_list, pcmd, elem_offset, true, false);
|
||||||
|
|
||||||
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
|
// Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted.
|
||||||
ImGuiListClipper clipper(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
|
ImGuiListClipper clipper;
|
||||||
|
clipper.Begin(pcmd->ElemCount / 3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible.
|
||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
|
for (int prim = clipper.DisplayStart, idx_i = elem_offset + clipper.DisplayStart * 3; prim < clipper.DisplayEnd; prim++)
|
||||||
{
|
{
|
||||||
|
9
imgui.h
9
imgui.h
@ -1900,7 +1900,8 @@ struct ImGuiStorage
|
|||||||
// - Step 3: 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.
|
// - Step 3: 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.
|
||||||
struct ImGuiListClipper
|
struct ImGuiListClipper
|
||||||
{
|
{
|
||||||
int DisplayStart, DisplayEnd;
|
int DisplayStart;
|
||||||
|
int DisplayEnd;
|
||||||
int ItemsCount;
|
int ItemsCount;
|
||||||
|
|
||||||
// [Internal]
|
// [Internal]
|
||||||
@ -1911,12 +1912,12 @@ struct ImGuiListClipper
|
|||||||
// items_count: Use -1 to ignore (you can call Begin later). 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 -1 to ignore (you can call Begin later). 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_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().
|
// items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing().
|
||||||
// If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step().
|
// If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step().
|
||||||
ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want).
|
ImGuiListClipper(int items_count = -1, float items_height = -1.0f);
|
||||||
~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false.
|
~ImGuiListClipper();
|
||||||
|
|
||||||
IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items.
|
|
||||||
IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1.
|
IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1.
|
||||||
IMGUI_API void End(); // Automatically called on the last call of Step() that returns false.
|
IMGUI_API void End(); // Automatically called on the last call of Step() that returns false.
|
||||||
|
IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helpers macros to generate 32-bit encoded colors
|
// Helpers macros to generate 32-bit encoded colors
|
||||||
|
@ -3340,7 +3340,8 @@ static void ShowDemoWindowColumns()
|
|||||||
ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar);
|
ImGui::BeginChild("##ScrollingRegion", child_size, false, ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
ImGui::Columns(10);
|
ImGui::Columns(10);
|
||||||
int ITEMS_COUNT = 2000;
|
int ITEMS_COUNT = 2000;
|
||||||
ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list
|
ImGuiListClipper clipper; // Also demonstrate using the clipper for large list
|
||||||
|
clipper.Begin(ITEMS_COUNT);
|
||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
{
|
{
|
||||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
||||||
@ -4330,7 +4331,8 @@ struct ExampleAppConsole
|
|||||||
// To use the clipper we can replace your standard loop:
|
// To use the clipper we can replace your standard loop:
|
||||||
// for (int i = 0; i < Items.Size; i++)
|
// for (int i = 0; i < Items.Size; i++)
|
||||||
// With:
|
// With:
|
||||||
// ImGuiListClipper clipper(Items.Size);
|
// ImGuiListClipper clipper;
|
||||||
|
// clipper.Begin(Items.Size);
|
||||||
// while (clipper.Step())
|
// while (clipper.Step())
|
||||||
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
||||||
// - That your items are evenly spaced (same height)
|
// - That your items are evenly spaced (same height)
|
||||||
@ -4897,7 +4899,8 @@ static void ShowExampleAppLongText(bool* p_open)
|
|||||||
{
|
{
|
||||||
// Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
|
// Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper.
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||||
ImGuiListClipper clipper(lines);
|
ImGuiListClipper clipper;
|
||||||
|
clipper.Begin(lines);
|
||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
||||||
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
|
ImGui::Text("%i The quick brown fox jumps over the lazy dog", i);
|
||||||
|
@ -6154,7 +6154,8 @@ bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(v
|
|||||||
// Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
|
// Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper.
|
||||||
ImGuiContext& g = *GImGui;
|
ImGuiContext& g = *GImGui;
|
||||||
bool value_changed = false;
|
bool value_changed = false;
|
||||||
ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
|
ImGuiListClipper clipper;
|
||||||
|
clipper.Begin(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to.
|
||||||
while (clipper.Step())
|
while (clipper.Step())
|
||||||
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user