Merge branch 'master' into docking

# Conflicts:
#	imgui.cpp
This commit is contained in:
ocornut 2021-11-04 15:26:01 +01:00
commit c1d2793580
5 changed files with 224 additions and 97 deletions

View File

@ -115,11 +115,21 @@ Docking+Viewports Branch:
Breaking Changes: Breaking Changes:
- Removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges.
Please open an issue if you think you really need this function. (#3841)
Other Changes: Other Changes:
- Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba] - Added IsMouseTripleClicked() function. Tracking multi-click count in IO structure. (#3229) [@kudaba]
- Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527) - Modals: fixed issue hovering popups inside a child inside a modal. (#4676, #4527)
- Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676) - Fixed IsWindowFocused()/IsWindowHovered() issues with childs inside popups. (#4676)
- Clipper: currently focused item is automatically included in clipper range.
Fixes issue where e.g. drag and dropping an item and scrolling ensure the item source location is
still submitted. (#3841, #1725) [@GamingMinds-DanielC, @ocornut]
- Clipper: rework so gamepad/keyboard navigation doesn't create spikes in number of items requested
by the clipper to display. (#3841)
- Clipper: Fixed content height declaration slightly mismatching the value of when not using a clipper.
(an additional ItemSpacing.y was declared, affecting scrollbar range).
- Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer. - Nav: pressing PageUp/PageDown/Home/End when in Menu layer automatically moves back to Main layer.
- Nav: fixed resizing window from borders setting navigation to Menu layer. - Nav: fixed resizing window from borders setting navigation to Menu layer.
- Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse. - Nav: pressing Esc to exit a child window reactivates the Nav highlight if it was disabled by mouse.

View File

@ -142,7 +142,7 @@ It's mostly a bunch of personal notes, probably incomplete. Feel free to query i
- plot: option/feature: draw unit - plot: option/feature: draw unit
- plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID)
- clipper: ability to force display 1 item in the list would be convenient (for patterns where we need to set active id etc.) - clipper: ability to force display 1 item in the list would be convenient (for patterns where we need to set active id etc.) (#3841) (can resurrect ForceDisplayRangeXXX functions removed)
- clipper: ability to disable the clipping through a simple flag/bool. - clipper: ability to disable the clipping through a simple flag/bool.
- clipper: ability to run without knowing full count in advance. - clipper: ability to run without knowing full count in advance.
- clipper: horizontal clipping support. (#2580) - clipper: horizontal clipping support. (#2580)

231
imgui.cpp
View File

@ -390,6 +390,7 @@ CODE
- 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api. - 2021/XX/XX (1.XX) - Moved IME support functions from io.ImeSetInputScreenPosFn, io.ImeWindowHandle to the PlatformIO api.
- 2021/11/04 (1.86) - removed CalcListClipping() function. Prefer using ImGuiListClipper which can return non-contiguous ranges. Please open an issue if you think you really need this function.
- 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful. - 2021/08/23 (1.85) - removed GetWindowContentRegionWidth() function. keep inline redirection helper. can use 'GetWindowContentRegionMax().x - GetWindowContentRegionMin().x' instead for generally 'GetContentRegionAvail().x' is more useful.
- 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019): - 2021/07/26 (1.84) - commented out redirecting functions/enums names that were marked obsolete in 1.67 and 1.69 (March 2019):
- ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList() - ImGui::GetOverlayDrawList() -> use ImGui::GetForegroundDrawList()
@ -2281,9 +2282,10 @@ static bool GetSkipItemForListClipping()
return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems); return (g.CurrentTable ? g.CurrentTable->HostSkipItems : g.CurrentWindow->SkipItems);
} }
// Helper to calculate coarse clipping of large list of evenly sized items. #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. // Legacy helper to calculate coarse clipping of large list of evenly sized items.
// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX // This legacy API is not ideal because it assume we will return a single contiguous rectangle.
// Prefer using ImGuiListClipper which can returns non-contiguous ranges.
void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
@ -2302,15 +2304,16 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
} }
// We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect // We create the union of the ClipRect and the scoring rect which at worst should be 1 page away from ClipRect
ImRect unclipped_rect = window->ClipRect; // We don't include g.NavId's rectangle in there (unless g.NavJustMovedToId is set) because the rectangle enlargement can get costly.
ImRect rect = window->ClipRect;
if (g.NavMoveScoringItems) if (g.NavMoveScoringItems)
unclipped_rect.Add(g.NavScoringRect); rect.Add(g.NavScoringNoClipRect);
if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId) if (g.NavJustMovedToId && window->NavLastIds[0] == g.NavJustMovedToId)
unclipped_rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel rect.Add(WindowRectRelToAbs(window, window->NavRectRel[0])); // Could store and use NavJustMovedToRectRel
const ImVec2 pos = window->DC.CursorPos; const ImVec2 pos = window->DC.CursorPos;
int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); int start = (int)((rect.Min.y - pos.y) / items_height);
int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); int end = (int)((rect.Max.y - pos.y) / items_height);
// When performing a navigation request, ensure we have one item extra in the direction we are moving to // When performing a navigation request, ensure we have one item extra in the direction we are moving to
if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) if (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up)
@ -2323,17 +2326,42 @@ void ImGui::CalcListClipping(int items_count, float items_height, int* out_items
*out_items_display_start = start; *out_items_display_start = start;
*out_items_display_end = end; *out_items_display_end = end;
} }
#endif
static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height) static void ImGuiListClipper_SortAndFuseRanges(ImVector<ImGuiListClipperRange>& ranges, int offset = 0)
{
if (ranges.Size - offset <= 1)
return;
// Helper to order ranges and fuse them together if possible (bubble sort is fine as we are only sorting 2-3 entries)
for (int sort_end = ranges.Size - offset - 1; sort_end > 0; --sort_end)
for (int i = offset; i < sort_end + offset; ++i)
if (ranges[i].Min > ranges[i + 1].Min)
ImSwap(ranges[i], ranges[i + 1]);
// Now fuse ranges together as much as possible.
for (int i = 1 + offset; i < ranges.Size; i++)
{
IM_ASSERT(!ranges[i].PosToIndexConvert && !ranges[i - 1].PosToIndexConvert);
if (ranges[i - 1].Max < ranges[i].Min)
continue;
ranges[i - 1].Min = ImMin(ranges[i - 1].Min, ranges[i].Min);
ranges[i - 1].Max = ImMax(ranges[i - 1].Max, ranges[i].Max);
ranges.erase(ranges.Data + i);
i--;
}
}
static void ImGuiListClipper_SeekCursorAndSetupPrevLine(float pos_y, float line_height)
{ {
// Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor. // Set cursor position and a few other things so that SetScrollHereY() and Columns() can work when seeking cursor.
// FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue.
// The clipper should probably have a 4th step to display the last item in a regular manner. // The clipper should probably have a final step to display the last item in a regular manner, maybe with an opt-out flag for data sets which may have costly seek?
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
float off_y = pos_y - window->DC.CursorPos.y; float off_y = pos_y - window->DC.CursorPos.y;
window->DC.CursorPos.y = pos_y; window->DC.CursorPos.y = pos_y;
window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y); window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, pos_y - g.Style.ItemSpacing.y);
window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage. window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHereY() can properly function after the end of our clipper usage.
window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. window->DC.PrevLineSize.y = (line_height - g.Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list.
if (ImGuiOldColumns* columns = window->DC.CurrentColumns) if (ImGuiOldColumns* columns = window->DC.CurrentColumns)
@ -2349,6 +2377,14 @@ static void SetCursorPosYAndSetupForPrevLine(float pos_y, float line_height)
} }
} }
static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int item_n)
{
// StartPosY starts from ItemsFrozen hence the subtraction
ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData;
float pos_y = clipper->StartPosY + (item_n - data->ItemsFrozen) * clipper->ItemsHeight;
ImGuiListClipper_SeekCursorAndSetupPrevLine(pos_y, clipper->ItemsHeight);
}
ImGuiListClipper::ImGuiListClipper() ImGuiListClipper::ImGuiListClipper()
{ {
memset(this, 0, sizeof(*this)); memset(this, 0, sizeof(*this));
@ -2357,7 +2393,7 @@ ImGuiListClipper::ImGuiListClipper()
ImGuiListClipper::~ImGuiListClipper() ImGuiListClipper::~ImGuiListClipper()
{ {
IM_ASSERT(ItemsCount == -1 && "Forgot to call End(), or to Step() until false?"); End();
} }
// Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 // Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1
@ -2375,28 +2411,44 @@ void ImGuiListClipper::Begin(int items_count, float items_height)
StartPosY = window->DC.CursorPos.y; StartPosY = window->DC.CursorPos.y;
ItemsHeight = items_height; ItemsHeight = items_height;
ItemsCount = items_count; ItemsCount = items_count;
ItemsFrozen = 0;
StepNo = 0;
DisplayStart = -1; DisplayStart = -1;
DisplayEnd = 0; DisplayEnd = 0;
// Acquire temporary buffer
if (++g.ClipperTempDataStacked > g.ClipperTempData.Size)
g.ClipperTempData.resize(g.ClipperTempDataStacked, ImGuiListClipperData());
ImGuiListClipperData* data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
data->Reset(this);
TempData = data;
} }
void ImGuiListClipper::End() void ImGuiListClipper::End()
{ {
if (ItemsCount < 0) // Already ended // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user.
return; ImGuiContext& g = *GImGui;
if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0)
// 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. ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
if (ItemsCount < INT_MAX && DisplayStart >= 0)
SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight);
ItemsCount = -1; ItemsCount = -1;
StepNo = 3;
// Restore temporary buffer and fix back pointers which may be invalidated when nesting
if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData)
{
IM_ASSERT(data->ListClipper == this);
data->StepNo = data->Ranges.Size;
if (--g.ClipperTempDataStacked > 0)
{
data = &g.ClipperTempData[g.ClipperTempDataStacked - 1];
data->ListClipper->TempData = data;
}
TempData = NULL;
}
} }
bool ImGuiListClipper::Step() bool ImGuiListClipper::Step()
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
ImGuiWindow* window = g.CurrentWindow; ImGuiWindow* window = g.CurrentWindow;
ImGuiListClipperData* data = (ImGuiListClipperData*)TempData;
ImGuiTable* table = g.CurrentTable; ImGuiTable* table = g.CurrentTable;
if (table && table->IsInsideRow) if (table && table->IsInsideRow)
@ -2409,90 +2461,109 @@ bool ImGuiListClipper::Step()
return false; return false;
} }
// Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height) // While we are in frozen row state, keep displaying items one by one, unclipped
if (StepNo == 0) // FIXME: Could be stored as a table-agnostic state.
if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows)
{ {
// While we are in frozen row state, keep displaying items one by one, unclipped DisplayStart = data->ItemsFrozen;
// FIXME: Could be stored as a table-agnostic state. DisplayEnd = data->ItemsFrozen + 1;
if (table != NULL && !table->IsUnfrozenRows) data->ItemsFrozen++;
{ return true;
DisplayStart = ItemsFrozen; }
DisplayEnd = ItemsFrozen + 1;
ItemsFrozen++;
return true;
}
// Step 0: Let you process the first element (regardless of it being visible or not, so we can measure the element height)
bool calc_clipping = false;
if (data->StepNo == 0)
{
StartPosY = window->DC.CursorPos.y; StartPosY = window->DC.CursorPos.y;
if (ItemsHeight <= 0.0f) if (ItemsHeight <= 0.0f)
{ {
// Submit the first item so we can measure its height (generally it is 0..1) // Submit the first item (or range) so we can measure its height (generally the first range is 0..1)
DisplayStart = ItemsFrozen; data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1));
DisplayEnd = ItemsFrozen + 1; DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen);
StepNo = 1; DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount);
data->StepNo = 1;
return true; return true;
} }
calc_clipping = true; // If on the first step with known item height, calculate clipping.
// 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 // Step 1: Let the clipper infer height from first range
if (StepNo == 1) if (ItemsHeight <= 0.0f)
{ {
IM_ASSERT(ItemsHeight <= 0.0f); IM_ASSERT(data->StepNo == 1);
if (table) if (table)
{ {
const float pos_y1 = table->RowPosY1; // Using this instead of StartPosY to handle clipper straddling the frozen row const float pos_y1 = table->RowPosY1; // Using RowPosY1 instead of StartPosY to handle clipper straddling the frozen row
const float pos_y2 = table->RowPosY2; // Using this instead of CursorPos.y to take account of tallest cell. const float pos_y2 = table->RowPosY2; // Using RowPosY2 instead of CursorPos.y to take account of tallest cell.
ItemsHeight = pos_y2 - pos_y1; ItemsHeight = pos_y2 - pos_y1;
window->DC.CursorPos.y = pos_y2; window->DC.CursorPos.y = pos_y2;
} }
else else
{ {
ItemsHeight = window->DC.CursorPos.y - StartPosY; ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart);
} }
IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!");
StepNo = 2; calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards.
} }
// Reached end of list // Step 0 or 1: Calculate the actual ranges of visible elements.
if (DisplayEnd >= ItemsCount) const int already_submitted = DisplayEnd;
if (calc_clipping)
{ {
End(); if (g.LogEnabled)
return false; {
// If logging is active, do not perform any clipping
data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount));
}
else
{
// Add range selected to be included for navigation
if (g.NavMoveScoringItems)
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0));
// Add focused/active item
ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]);
if (g.NavId != 0 && window->NavLastIds[0] == g.NavId)
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(nav_rect_abs.Min.y, nav_rect_abs.Max.y, 0, 0));
// Add visible range
const int off_min = (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Up) ? -1 : 0;
const int off_max = (g.NavMoveScoringItems && g.NavMoveClipDir == ImGuiDir_Down) ? 1 : 0;
data->Ranges.push_back(ImGuiListClipperRange::FromPositions(window->ClipRect.Min.y, window->ClipRect.Max.y, off_min, off_max));
}
// Convert position ranges to item index ranges
// - Very important: when a starting position is after our maximum item, we set Min to (ItemsCount - 1). This allows us to handle most forms of wrapping.
// - Due to how Selectable extra padding they tend to be "unaligned" with exact unit in the item list,
// which with the flooring/ceiling tend to lead to 2 items instead of one being submitted.
for (int i = 0; i < data->Ranges.Size; i++)
if (data->Ranges[i].PosToIndexConvert)
{
data->Ranges[i].Min = ImClamp(already_submitted + (int)ImFloor((data->Ranges[i].Min - window->DC.CursorPos.y) / ItemsHeight) + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1);
data->Ranges[i].Max = ImClamp(already_submitted + (int)ImCeil((data->Ranges[i].Max - window->DC.CursorPos.y) / ItemsHeight) + 0 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount);
data->Ranges[i].PosToIndexConvert = false;
}
ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo);
} }
// Step 2: calculate the actual range of elements to display, and position the cursor before the first element // Step 0+ (if item height is given in advance) or 1+: Display the next range in line.
if (StepNo == 2) if (data->StepNo < data->Ranges.Size)
{ {
IM_ASSERT(ItemsHeight > 0.0f); DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted);
DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount);
int already_submitted = DisplayEnd; if (DisplayStart > already_submitted) //-V1051
ImGui::CalcListClipping(ItemsCount - already_submitted, ItemsHeight, &DisplayStart, &DisplayEnd); ImGuiListClipper_SeekCursorForItem(this, DisplayStart);
DisplayStart += already_submitted; data->StepNo++;
DisplayEnd += already_submitted;
// Seek cursor
if (DisplayStart > already_submitted)
SetCursorPosYAndSetupForPrevLine(StartPosY + (DisplayStart - ItemsFrozen) * ItemsHeight, ItemsHeight);
StepNo = 3;
return true; return true;
} }
// Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // 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. // Advance the cursor to the end of the list and then returns 'false' to end the loop.
if (StepNo == 3) if (ItemsCount < INT_MAX)
{ ImGuiListClipper_SeekCursorForItem(this, ItemsCount);
// Seek cursor ItemsCount = -1;
if (ItemsCount < INT_MAX)
SetCursorPosYAndSetupForPrevLine(StartPosY + (ItemsCount - ItemsFrozen) * ItemsHeight, ItemsHeight); // advance cursor
ItemsCount = -1;
return false;
}
IM_ASSERT(0);
return false; return false;
} }
@ -4426,6 +4497,8 @@ void ImGui::Shutdown(ImGuiContext* context)
g.CurrentTabBarStack.clear(); g.CurrentTabBarStack.clear();
g.ShrinkWidthBuffer.clear(); g.ShrinkWidthBuffer.clear();
g.ClipperTempData.clear_destruct();
g.Tables.Clear(); g.Tables.Clear();
g.TablesTempData.clear_destruct(); g.TablesTempData.clear_destruct();
g.DrawChannelsTempMergeBuffer.clear(); g.DrawChannelsTempMergeBuffer.clear();
@ -10171,6 +10244,7 @@ void ImGui::NavUpdateCreateMoveRequest()
if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; }
} }
g.NavMoveClipDir = g.NavMoveDir; g.NavMoveClipDir = g.NavMoveDir;
g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX);
} }
// Update PageUp/PageDown/Home/End scroll // Update PageUp/PageDown/Home/End scroll
@ -10179,6 +10253,11 @@ void ImGui::NavUpdateCreateMoveRequest()
float scoring_rect_offset_y = 0.0f; float scoring_rect_offset_y = 0.0f;
if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active)
scoring_rect_offset_y = NavUpdatePageUpPageDown(); scoring_rect_offset_y = NavUpdatePageUpPageDown();
if (scoring_rect_offset_y != 0.0f)
{
g.NavScoringNoClipRect = window->InnerRect;
g.NavScoringNoClipRect.TranslateY(scoring_rect_offset_y);
}
// [DEBUG] Always send a request // [DEBUG] Always send a request
#if IMGUI_DEBUG_NAV_SCORING #if IMGUI_DEBUG_NAV_SCORING
@ -10232,8 +10311,10 @@ void ImGui::NavUpdateCreateMoveRequest()
scoring_rect.Max.x = scoring_rect.Min.x; scoring_rect.Max.x = scoring_rect.Min.x;
IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem().
//GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG]
//if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG]
} }
g.NavScoringRect = scoring_rect; g.NavScoringRect = scoring_rect;
g.NavScoringNoClipRect.Add(scoring_rect);
} }
// Apply result from previous frame navigation directional move request. Always called from NavUpdate() // Apply result from previous frame navigation directional move request. Always called from NavUpdate()

42
imgui.h
View File

@ -65,7 +65,7 @@ Index of this file:
// Version // Version
// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) // (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens)
#define IMGUI_VERSION "1.86 WIP" #define IMGUI_VERSION "1.86 WIP"
#define IMGUI_VERSION_NUM 18504 #define IMGUI_VERSION_NUM 18506
#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) #define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx))
#define IMGUI_HAS_TABLE #define IMGUI_HAS_TABLE
#define IMGUI_HAS_VIEWPORT // Viewport WIP branch #define IMGUI_HAS_VIEWPORT // Viewport WIP branch
@ -902,7 +902,6 @@ namespace ImGui
IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.). IMGUI_API const char* GetStyleColorName(ImGuiCol idx); // get a string corresponding to the enum value (for display, saving, etc.).
IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it)
IMGUI_API ImGuiStorage* GetStateStorage(); IMGUI_API ImGuiStorage* GetStateStorage();
IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can.
IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame
IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window)
@ -2278,10 +2277,12 @@ struct ImGuiStorage
}; };
// Helper: Manually clip large list of items. // Helper: Manually clip large list of items.
// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse // If you have lots evenly spaced items and you have a random access to the list, you can perform coarse
// clipping based on visibility to save yourself from processing those items at all. // clipping based on visibility to only submit items that are in view.
// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped.
// (Dear ImGui already clip items based on their bounds but it needs to measure text size to do so, whereas manual coarse clipping before submission makes this cost and your own data fetching/submission cost almost null) // (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally
// fetching/submitting your own data incurs additional cost. Coarse clipping using ImGuiListClipper allows you to easily
// scale using lists with tens of thousands of items without a problem)
// Usage: // Usage:
// ImGuiListClipper clipper; // ImGuiListClipper clipper;
// clipper.Begin(1000); // We have 1000 elements, evenly spaced. // clipper.Begin(1000); // We have 1000 elements, evenly spaced.
@ -2290,30 +2291,27 @@ struct ImGuiStorage
// ImGui::Text("line number %d", i); // ImGui::Text("line number %d", i);
// Generally what happens is: // Generally what happens is:
// - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not. // - Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not.
// - User code submit one element. // - User code submit that one element.
// - Clipper can measure the height of the first element // - Clipper can measure the height of the first element
// - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element. // - Clipper calculate the actual range of elements to display based on the current clipping rectangle, position the cursor before the first visible element.
// - User code submit visible elements. // - User code submit visible elements.
// - The clipper also handles various subtleties related to keyboard/gamepad navigation, wrapping etc.
struct ImGuiListClipper struct ImGuiListClipper
{ {
int DisplayStart; int DisplayStart; // First item to display, updated by each call to Step()
int DisplayEnd; int DisplayEnd; // End of items to display (exclusive)
int ItemsCount; // [Internal] Number of items
// [Internal] float ItemsHeight; // [Internal] Height of item after a first step and item submission can calculate it
int ItemsCount; float StartPosY; // [Internal] Cursor position at the time of Begin() or after table frozen rows are all processed
int StepNo; void* TempData; // [Internal] Internal data
int ItemsFrozen;
float ItemsHeight;
float StartPosY;
IMGUI_API ImGuiListClipper();
IMGUI_API ~ImGuiListClipper();
// 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)
// 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().
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 ImGuiListClipper();
IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. IMGUI_API ~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);
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.
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79] inline ImGuiListClipper(int items_count, float items_height = -1.0f) { memset(this, 0, sizeof(*this)); ItemsCount = -1; Begin(items_count, items_height); } // [removed in 1.79]
@ -3086,6 +3084,8 @@ struct ImGuiPlatformMonitor
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
namespace ImGui namespace ImGui
{ {
// OBSOLETED in 1.86 (from November 2021)
IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper.
// OBSOLETED in 1.85 (from August 2021) // OBSOLETED in 1.85 (from August 2021)
static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; }
// OBSOLETED in 1.81 (from February 2021) // OBSOLETED in 1.81 (from February 2021)

View File

@ -18,6 +18,7 @@ Index of this file:
// [SECTION] Generic helpers // [SECTION] Generic helpers
// [SECTION] ImDrawList support // [SECTION] ImDrawList support
// [SECTION] Widgets support: flags, enums, data structures // [SECTION] Widgets support: flags, enums, data structures
// [SECTION] Clipper support
// [SECTION] Navigation support // [SECTION] Navigation support
// [SECTION] Columns support // [SECTION] Columns support
// [SECTION] Multi-select support // [SECTION] Multi-select support
@ -1182,6 +1183,34 @@ struct ImGuiPtrOrIndex
ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; } ImGuiPtrOrIndex(int index) { Ptr = NULL; Index = index; }
}; };
//-----------------------------------------------------------------------------
// [SECTION] Clipper support
//-----------------------------------------------------------------------------
struct ImGuiListClipperRange
{
int Min;
int Max;
bool PosToIndexConvert; // Begin/End are absolute position (will be converted to indices later)
ImS8 PosToIndexOffsetMin; // Add to Min after converting to indices
ImS8 PosToIndexOffsetMax; // Add to Min after converting to indices
static ImGuiListClipperRange FromIndices(int min, int max) { ImGuiListClipperRange r = { min, max, false, 0, 0 }; return r; }
static ImGuiListClipperRange FromPositions(float y1, float y2, int off_min, int off_max) { ImGuiListClipperRange r = { (int)y1, (int)y2, true, (ImS8)off_min, (ImS8)off_max }; return r; }
};
// Temporary clipper data, buffers shared/reused between instances
struct ImGuiListClipperData
{
ImGuiListClipper* ListClipper;
int StepNo;
int ItemsFrozen;
ImVector<ImGuiListClipperRange> Ranges;
ImGuiListClipperData() { memset(this, 0, sizeof(*this)); }
void Reset(ImGuiListClipper* clipper) { ListClipper = clipper; StepNo = ItemsFrozen = 0; Ranges.resize(0); }
};
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// [SECTION] Navigation support // [SECTION] Navigation support
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -1767,6 +1796,7 @@ struct ImGuiContext
ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveDirForDebug;
ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename?
ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring. ImRect NavScoringRect; // Rectangle used for scoring, in screen space. Based of window->NavRectRel[], modified for directional navigation scoring.
ImRect NavScoringNoClipRect; // Some nav operations (such as PageUp/PageDown) enforce a region which clipper will attempt to always keep submitted
int NavScoringDebugCount; // Metrics for debugging int NavScoringDebugCount; // Metrics for debugging
int NavTabbingInputableRemaining; // >0 when counting items for tabbing int NavTabbingInputableRemaining; // >0 when counting items for tabbing
ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow ImGuiNavItemData NavMoveResultLocal; // Best move request candidate within NavWindow
@ -1811,6 +1841,10 @@ struct ImGuiContext
ImVector<unsigned char> DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size ImVector<unsigned char> DragDropPayloadBufHeap; // We don't expose the ImVector<> directly, ImGuiPayload only holds pointer+size
unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads unsigned char DragDropPayloadBufLocal[16]; // Local buffer for small payloads
// Clipper
int ClipperTempDataStacked;
ImVector<ImGuiListClipperData> ClipperTempData;
// Table // Table
ImGuiTable* CurrentTable; ImGuiTable* CurrentTable;
int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size) int TablesTempDataStacked; // Temporary table data size (because we leave previous instances undestructed, we generally don't use TablesTempData.Size)
@ -2011,6 +2045,8 @@ struct ImGuiContext
DragDropHoldJustPressedId = 0; DragDropHoldJustPressedId = 0;
memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal));
ClipperTempDataStacked = 0;
CurrentTable = NULL; CurrentTable = NULL;
TablesTempDataStacked = 0; TablesTempDataStacked = 0;
CurrentTabBar = NULL; CurrentTabBar = NULL;