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

Enhanced ImGuiListClipper for drag&drop operations #3841

Conversation

GamingMinds-DanielC
Copy link
Contributor

Current limitation

When using the list clipper, only the visible subset of elements is actually calculated, which saves a lot of processing time in larger lists. However, if a drag&drop source is scrolled out and gets clipped, the tooltip no longer gets generated and jumps back to "...". If that is not good enough, the current workaround would be to detect if an item was displayed and fall back to an alternative with additional code which can complicate things.

Solution

I enhanced the list clipper with a new method "ForceDisplay", with that the user can specify a single element that will be displayed even if it isn't visible. This allows the user to generate drag&drop tooltips (or possibly for other reasons) as if the element were visible. The forced element is optional (no change in functionality if it isn't used). It introduces another instance of Step() returning true only if the element is not handled by another step. The order of elements is also maintained, the additional step happens before or after the main step depending on element indices.

Example test code

When running this example code, drag an element and scroll it out of the visible set with the mouse wheel. With the "Use ForceDisplay" checkbox checked, the tooltip gets continually updated, with it unchecked the tooltip reverts to "...". If you comment out the single call to ForceDisplay, the code works with the current ImGui version.

void TestForcedElement()
{
	const int  NumItems       = 100;
	static int draggedItem    = -1;
	int        itemsSubmitted = 0;

	static bool useForceDisplay = true;

	if (ImGui::GetDragDropPayload() == nullptr)
		draggedItem = -1;

	// instructions and test settings
	ImGui::SetNextWindowSize(ImVec2(320, 240), ImGuiCond_FirstUseEver);
	if (ImGui::Begin("Forced Element Settings"))
	{
		ImGui::PushTextWrapPos();
		ImGui::TextUnformatted("Drag any item while scrolling the list so that the dragged item is no longer visible.");
		ImGui::PopTextWrapPos();

		ImGui::Checkbox("Use ForceDisplay", &useForceDisplay);
	}
	ImGui::End();

	// test window, use drag&drop on items
	ImGui::SetNextWindowSize(ImVec2(320, 480), ImGuiCond_FirstUseEver );
	if (ImGui::Begin("Forced Element Test"))
	{
		ImGuiListClipper clipper;

		clipper.Begin(100);

#if 1	// deactivate this for unenhanced list clipper
		if (useForceDisplay)
			clipper.ForceDisplay(draggedItem);	// if >= 0, this item will be displayed regardless of visibility
#endif

		while (clipper.Step())
		{
			for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i)
			{
				char buffer[32];
				sprintf_s(buffer, "item #%d", i);

				ImGui::Selectable(buffer);

				if (ImGui::BeginDragDropSource())
				{
					int data = i;

					ImGui::SetDragDropPayload("Test Payload", &data, sizeof(data));
					ImGui::TextUnformatted(buffer);
					ImGui::EndDragDropSource();

					draggedItem = i;
				}

				itemsSubmitted++;
			}
		}
	}
	ImGui::End();

	// append metrics to test settings window
	if (ImGui::Begin("Forced Element Settings"))
	{
		if (ImGui::BeginTable("Metrics", 2))
		{
			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Number of Items:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", NumItems);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Items submitted:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", itemsSubmitted);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Dragged item index:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", draggedItem);

			ImGui::EndTable();
		}
	}
	ImGui::End();
}

@ocornut ocornut added the drag drop drag and drop label Mar 4, 2021
@ocornut
Copy link
Owner

ocornut commented Mar 10, 2021

Thank you for the PR.
I agree this has been a needed improvement to the clipper.

I think we should aim to reformulate this

  • A) with a function call that takes a region of space (y min .. y max).
  • B) providing the "index" version you added is going to be useful too

Note how underlying CalcListClipping() already have specific handling for g.NavMoveRequest and g.NavJustMovedToId cases and both of those are extending the initial rect, so it is a really unoptimal version of (A). If we can provide (A) then that code can use it more optimally.

I haven't extensively tested it but gut reaction since the PR is that it adds too much duplicate code, and although the Clipper is currently a little of a tricky mess we should strive to untangle it rather than not. If anything, supporting (A) which is more generic than (B) may force refactoring the code in a neater way. I feel like instead of using sequential numbered steps the clipper could for each step "figure what's coming next", return that, repeat there's no more.

If you have ideas of how to improve this feel free to toy around, otherwise I'll probably investigate it at some point, with your PR as a starting point.

Thank you!

@GamingMinds-DanielC
Copy link
Contributor Author

I didn't think of ranges, but that might come in handy. Which of these variants would you prefer?

// count variant (single item is default, ranges must be converted to start + count)
IMGUI_API void ForceDisplay(int item_start, int item_count = 1);

// range variant (one overload for a single item, start + explicit count must be converted to range)
IMGUI_API void ForceDisplay(int item_start, int item_end);
inline void ForceDisplay(int item_index) { ForceDisplay(item_index, item_index + 1); }

// both combined (single item, start + explicit count and range are all handled, but needs two separate function names)
IMGUI_API void ForceDisplayRange(int item_start, int item_end);
inline void ForceDisplay(int item_start, int item_count = 1) { ForceDisplayRange(item_start, item_start + item_count); }

On first glance, I would favor the combined variant since all use cases are there without the user having to convert anything.

Regarding code duplication, a better way would be to have a fixed list (2 entries should already be enough) of ranges. These can then be sorted (just a conditional swap) and fused (if continuous or overlapping). That should reduce the code needed quite a bit, I will look into it as soon as I have some time. I don't know the internal navigation code so far, but I will take a look if it can be incorporated without complications.

@ocornut
Copy link
Owner

ocornut commented Mar 10, 2021

A) with a function call that takes a region of space (y min .. y max).

I meant provided as Y coordinates, not necessarily item count.

@GamingMinds-DanielC
Copy link
Contributor Author

Oh, OK. Internally for the navigation only or for the user too? I'll take a look at that as well.

@ocornut
Copy link
Owner

ocornut commented Mar 10, 2021

Oh, OK. Internally for the navigation only or for the user too? I'll take a look at that as well.

Those navigation things come to mind now but I sense it wouldn't hurt to provide the api to the user.

I'm not sure about the ForceDisplay() name but not too fussed about it now, if we can get the logic in the names we can decide later so don't worry about that.

@ocornut
Copy link
Owner

ocornut commented Mar 10, 2021

On that specific example you gave:

cpp
#if 1	// deactivate this for unenhanced list clipper
		if (useForceDisplay)
			clipper.ForceDisplay(draggedItem);	// if >= 0, this item will be displayed regardless of visibility
#endif
[...]
				if (ImGui::BeginDragDropSource())
[...]

I think it would be perfectly adequate to store the bounding box of the ActiveId and automatically have clipper takes that into account. So that would be a case where the clipping could automatically add a y-min/y-max range and then none of the extra API would be required to solve your issue here. (But the extra API can be useful in many other situations).

@GamingMinds-DanielC
Copy link
Contributor Author

I didn't get around to the y position range yet, but I made huge improvements with the ranges so far. Now the user can force an item, a number of items from a starting index or a range of items. The generated ranges get fused together to reduce the number of calls to Step(). Step() itself is a lot simpler, down from 145 lines in my previous commit to now 109. The new version no longer has to rely (as much) on fixed StepNo values.

Here is some new test code for it:

void TestForcedElement()
{
	const int  NumItems       = 100;
	static int draggedItem    = -1;
	int        stepsProcessed = 0;
	int        itemsSubmitted = 0;

	static bool explicitHeight   = false;
	static int  forceDisplayMode = 1;
	static int  forceRangeStart  = 90;
	static int  forceRangeEnd    = 95;

	if (ImGui::GetDragDropPayload() == nullptr)
		draggedItem = -1;

	// instructions and test settings
	ImGui::SetNextWindowSize(ImVec2(320, 240), ImGuiCond_FirstUseEver);
	if (ImGui::Begin("Forced Element Settings"))
	{
		ImGui::PushTextWrapPos();
		ImGui::TextUnformatted("Drag any item while scrolling the list so that the dragged item is no longer visible.");
		ImGui::PopTextWrapPos();

		ImGui::Checkbox("Set explicit Item Height", &explicitHeight);

		ImGui::RadioButton("No forced Display", &forceDisplayMode, 0);
		ImGui::RadioButton("Force Display of dragged Item", &forceDisplayMode, 1);
		ImGui::RadioButton("Force Display of Range", &forceDisplayMode, 2);

		ImGui::SliderInt("Forced Range Start", &forceRangeStart, 0, NumItems);
		ImGui::SliderInt("Forced Range End", &forceRangeEnd, 0, NumItems);
	}
	ImGui::End();

	// test window, use drag&drop on items
	ImGui::SetNextWindowSize(ImVec2(320, 480), ImGuiCond_FirstUseEver );
	if (ImGui::Begin("Forced Element Test"))
	{
		ImGuiListClipper clipper;

		clipper.Begin(100, explicitHeight ? 17.0f : -1.0f);

#if 1	// deactivate this for unenhanced list clipper
		if ((forceDisplayMode == 1) && (draggedItem >= 0))
			clipper.ForceDisplay(draggedItem);		// this item will be displayed regardless of visibility
		if ((forceDisplayMode == 2) && (forceRangeStart < forceRangeEnd))
			clipper.ForceDisplayRange(forceRangeStart, forceRangeEnd);
#endif

		while (clipper.Step())
		{
			stepsProcessed++;

			for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i)
			{
				char buffer[32];
				sprintf_s(buffer, "item #%d", i);

				ImGui::Selectable(buffer);

				if (ImGui::BeginDragDropSource())
				{
					int data = i;

					ImGui::SetDragDropPayload("Test Payload", &data, sizeof(data));
					ImGui::TextUnformatted(buffer);
					ImGui::EndDragDropSource();

					draggedItem = i;
				}

				itemsSubmitted++;
			}
		}
	}
	ImGui::End();

	// append metrics to test settings window
	if (ImGui::Begin("Forced Element Settings"))
	{
		if (ImGui::BeginTable("Metrics", 2))
		{
			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Number of Items:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", NumItems);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Steps processed:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", stepsProcessed);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Items submitted:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", itemsSubmitted);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Dragged item index:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", draggedItem);

			ImGui::EndTable();
		}
	}
	ImGui::End();
}

A few things to look out for in the tracked stats:

  • If item 0 gets tested for height and item 1 gets dragged, the 1st step will fuse both together.
  • Item height now gets calculated as the average height of all items displayed in the initial range.
  • If the initial range encompasses the entire visible set, no extra step gets added.

@GamingMinds-DanielC
Copy link
Contributor Author

I added the range based on Y coordinates. I didn't modify ImGui::CalcListClipping() since it is part of the public interface and I don't want to break it for anyone, but I based the new forced Y range on it.

Here is also a new test function with improved metrics to show the steps in more detail:

void TestForcedDisplay()
{
	const int  NumItems       = 100;
	float      cursorStartY   = 0.0f;
	float      itemHeight     = ImGui::GetTextLineHeight() + ImGui::GetStyle().ItemSpacing.y;
	static int draggedItem    = -1;
	int        itemsSubmitted = 0;
	int        stepsProcessed = 0;
	int        displayStart[8];
	int        displayEnd[8];

	static bool  explicitHeight   = false;
	static int   forceDisplayMode = 1;
	static int   forceRangeStart  = 90;
	static int   forceRangeEnd    = 95;
	static float forceYRangeMin   = 0.0f;
	static float forceYRangeMax   = 0.0f;

	if (ImGui::GetDragDropPayload() == nullptr)
		draggedItem = -1;

	// instructions and test settings
	ImGui::SetNextWindowPos(ImVec2(80, 80), ImGuiCond_FirstUseEver);
	ImGui::SetNextWindowSize(ImVec2(400, 480), ImGuiCond_FirstUseEver);
	if (ImGui::Begin("Forced Display Settings"))
	{
		ImGui::PushTextWrapPos();
		ImGui::TextUnformatted("Drag any item while scrolling the list so that the dragged item is no longer visible.");
		ImGui::PopTextWrapPos();

		ImGui::Checkbox("Set explicit Item Height", &explicitHeight);

		ImGui::RadioButton("No forced Display", &forceDisplayMode, 0);
		ImGui::RadioButton("Force Display of dragged Item", &forceDisplayMode, 1);
		ImGui::RadioButton("Force Display of Range", &forceDisplayMode, 2);
		ImGui::RadioButton("Force Display of Y Range", &forceDisplayMode, 3);

		ImGui::SliderInt("Forced Range Start", &forceRangeStart, 0, NumItems);
		ImGui::SliderInt("Forced Range End", &forceRangeEnd, 0, NumItems);

		ImGui::DragFloat("Forced Y Range Min", &forceYRangeMin);
		ImGui::DragFloat("Forced Y Range Max", &forceYRangeMax);
	}
	ImGui::End();

	// test window, use drag&drop on items
	ImGui::SetNextWindowPos(ImVec2(500, 80), ImGuiCond_FirstUseEver);
	ImGui::SetNextWindowSize(ImVec2(320, 480), ImGuiCond_FirstUseEver);
	if (ImGui::Begin("Forced Display Test"))
	{
		cursorStartY = ImGui::GetCursorScreenPos().y;

		ImGuiListClipper clipper;

		clipper.Begin(100, explicitHeight ? itemHeight : -1.0f);

#if 1	// deactivate this for unenhanced list clipper
		if ((forceDisplayMode == 1) && (draggedItem >= 0))
			clipper.ForceDisplay(draggedItem);		// this item will be displayed regardless of visibility
		if ((forceDisplayMode == 2) && (forceRangeStart < forceRangeEnd))
			clipper.ForceDisplayRange(forceRangeStart, forceRangeEnd);
		if ((forceDisplayMode == 3) && (forceYRangeMin <= forceYRangeMax))
			clipper.ForceDisplayYRange(forceYRangeMin, forceYRangeMax);
#endif

		while (clipper.Step())
		{
			displayStart[stepsProcessed] = clipper.DisplayStart;
			displayEnd[stepsProcessed]   = clipper.DisplayEnd;
			stepsProcessed++;

			for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; ++i)
			{
				char buffer[32];
				sprintf_s(buffer, "item #%d", i);

				ImGui::Selectable(buffer);

				if (ImGui::BeginDragDropSource())
				{
					int data = i;

					ImGui::SetDragDropPayload("Test Payload", &data, sizeof(data));
					ImGui::TextUnformatted(buffer);
					ImGui::EndDragDropSource();

					draggedItem = i;
				}

				itemsSubmitted++;
			}
		}

		itemHeight = clipper.ItemsHeight;
	}
	ImGui::End();

	// append metrics to test settings window
	if (ImGui::Begin("Forced Display Settings"))
	{
		if (ImGui::BeginTable("Metrics", 2))
		{
			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Number of Items:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", NumItems);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Screen Cursor Start Y:");
			if (ImGui::TableNextColumn()) ImGui::Text("%.3f", cursorStartY);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Item Height:");
			if (ImGui::TableNextColumn()) ImGui::Text("%.3f", itemHeight);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Dragged item index:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", draggedItem);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Items submitted:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", itemsSubmitted);

			if (ImGui::TableNextColumn()) ImGui::TextUnformatted("Steps processed:");
			if (ImGui::TableNextColumn()) ImGui::Text("%d", stepsProcessed);

			for (int i = 0; i < stepsProcessed; ++i)
			{
				if (ImGui::TableNextColumn()) ImGui::BulletText("Step %d:", i + 1);
				if (ImGui::TableNextColumn()) ImGui::Text("%d - %d", displayStart[i], displayEnd[i]);
			}

			ImGui::EndTable();
		}
	}
	ImGui::End();
}

Btw, I'm also not heavily invested in the names or internal workings of the new functions, you can of course change them however you see fit. As long as a way to force the display of a single item will be included, I'm happy.

@ocornut ocornut added the clipper label Apr 7, 2021
@ocornut ocornut force-pushed the master branch 2 times, most recently from 0c1e5bd to bb6a60b Compare August 27, 2021 19:10
@ocornut
Copy link
Owner

ocornut commented Oct 11, 2021

I will shortly be looking at this since I need a similar thing to finish solving a problem I'm on.

Note that once we can manage multiple ranges we can automatically add the bounding box of the currently focused item (g.NavId) which would likely solve your specific case without calling any API, since clicking the drag source would make it the focused item.

I didn't modify ImGui::CalcListClipping() since it is part of the public interface and I don't want to break it for anyone, but I based the new forced Y range on it.

Similarly, the two unclipped_rect.Add() in CalcListClipping() should be part of iterative ranges.

I don't think it is possible to keep the CalcListClipping() api with those features so I am possibly going to discourage using it, and internally have a CalcListClippingEx() proxy with flags and the clipper can use a version that doesn't enlarge the bounding box but add ranges instead.

ocornut added a commit that referenced this pull request Oct 27, 2021
…the reference point from window->Pos to window->DC.CursorStartPos. This commit should have no effect.

Current point makes rectangle invalid right after a scroll, for interactive actions it's rarely a problem but e.g. clipper will want to use g.NavID rect rel while scrolling. (#3841)
ocornut added a commit that referenced this pull request Oct 27, 2021
…artPos to be independent of scrolling. Will facilitate rework of clipper (#3841)

+ Extracted code into NavUpdateCreateWrappingRequest()
+ Fix for PVS in NavUpdate() assigning g.NavMousePosDirty twice.
ocornut pushed a commit that referenced this pull request Nov 4, 2021
ocornut added a commit that referenced this pull request Nov 4, 2021
- Focused/NavId now always included in display range.
- Any number of steps (while preserving zero-alloc policy).
- Non contiguous ranges for nav processing
- Moved new fields internally (+ moved StepNo away from sight so it doesn't get missused).
- Generally tweaks/refactors.
ocornut added a commit that referenced this pull request Nov 4, 2021
…ns functions until we find a need for them, since #3841 is now solved automatically.
ocornut added a commit that referenced this pull request Nov 4, 2021
@ocornut
Copy link
Owner

ocornut commented Nov 4, 2021

Finally got around to finish the work on this... it eventually became quite an hairy amount of yak-shaving in order to have navigation system fully work, spent several days on this.

  • Relative rectangle stored by the navigation system over frames are now stored in a scrolling independent way (otherwise using nav rectangle on the same frame as a scrolling would rely on incorrect data, this was an issue with using e.g. PageUp/PageDown 8361ed1 a67f7dc)
  • Your commit cd1b5f7
  • Various rework of the clipper 93cccd2, including:
    • Focused/NavId now always included in display range. This specifically solve your main issue WITHOUT having to call in new API.
    • Any number of steps (while preserving zero-alloc policy).
    • Non contiguous ranges for nav processing (Page Up/Page Down was tricky).
    • Moved new fields internally (+ moved StepNo away from sight so it doesn't get misused).
    • General tweaks/refactors.
    • Wrote a bunch of new tests.
  • I ACTUALLY REMOVED the two main functions that were intended to be added by this PR (ForceDisplayRangeByIndices(), ForceDisplayRangeByPositions() before my next-to-last commit). Made the removal in a separate commit (6a7e2c7). The reasoning is both of you and Losing drag and drop when clipped or source item becomes not visible / or not submitted anymore #1725 came in with a specific problem and the fact that the clipper now adds the NavId to visible range actually solves this problem by default. I think those two functions might be useful but I don't want to clutter public API (and carry the responsibility) without a known problem to solve. If we need them later it'll be only a matter of reverting 6a7e2c7.
  • Obsoleted the CalcListClipping() function (64daedd) its api doesn't make it possible to work with multiple non-contiguous ranges (compared to clipper), and I don't really think people were using this. This was initially designed as a lower-level version of the clipper only but I don't really see a good purpose for it now.

Thanks for this and also thanks a lot for the nice test-bed!

@ocornut
Copy link
Owner

ocornut commented Nov 8, 2021

  • I ACTUALLY REMOVED the two main functions that were intended to be added by this PR (ForceDisplayRangeByIndices(), ForceDisplayRangeByPositions() before my next-to-last commit). Made the removal in a separate commit (6a7e2c7). The reasoning is both of you and Losing drag and drop when clipped or source item becomes not visible / or not submitted anymore #1725 came in with a specific problem and the fact that the clipper now adds the NavId to visible range actually solves this problem by default. I think those two functions might be useful but I don't want to clutter public API (and carry the responsibility) without a known problem to solve. If we need them later it'll be only a matter of reverting 6a7e2c7.

Based on #3578 I have now added ForceDisplayRangeByIndices() to the public API (bce1ba4)

ButtonHeck pushed a commit to ButtonHeck/imgui that referenced this pull request Nov 10, 2021
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
…the reference point from window->Pos to window->DC.CursorStartPos. This commit should have no effect.

Current point makes rectangle invalid right after a scroll, for interactive actions it's rarely a problem but e.g. clipper will want to use g.NavID rect rel while scrolling. (ocornut#3841)
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
…artPos to be independent of scrolling. Will facilitate rework of clipper (ocornut#3841)

+ Extracted code into NavUpdateCreateWrappingRequest()
+ Fix for PVS in NavUpdate() assigning g.NavMousePosDirty twice.
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
- Focused/NavId now always included in display range.
- Any number of steps (while preserving zero-alloc policy).
- Non contiguous ranges for nav processing
- Moved new fields internally (+ moved StepNo away from sight so it doesn't get missused).
- Generally tweaks/refactors.
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
…ns functions until we find a need for them, since ocornut#3841 is now solved automatically.
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
actondev pushed a commit to actondev/imgui that referenced this pull request Nov 26, 2021
ocornut added a commit that referenced this pull request May 15, 2023
…s(). (#6424, #3841) + commented out obsolete ImGuiListClipper() constructor.
@ocornut
Copy link
Owner

ocornut commented May 15, 2023

Based on #3578 I have now added ForceDisplayRangeByIndices() to the public API (bce1ba4)

📣 FYI the function ForceDisplayRangeByIndices() was now renamed to IncludeRangeByIndices() in 1.89.6 WIP (commit ecb0aaa).

ocornut pushed a commit that referenced this pull request May 15, 2023
ocornut added a commit that referenced this pull request Aug 25, 2023
… to IncludeItemsByIndex(). (#6424, #3841)

Single item version added in prevous commit (2000537) renamed to IncludeItemByIndex() too.
ocornut added a commit that referenced this pull request Aug 25, 2023
… to IncludeItemsByIndex(). (#6424, #3841)

Single item version added in prevous commit (2000537) renamed to IncludeItemByIndex() too.
@ocornut
Copy link
Owner

ocornut commented Aug 25, 2023

FYI I have made two changes to those API now:

  • Added single item version IncludeItemByIndex(int item_index). This is common with some things I'm working on and I didn't want to burden people with the range version.
  • Renamed IncludeRangeByIndices() again.. to IncludeItemsByIndex(). Felt more consistent next to each others. Sorry!

ocornut added a commit that referenced this pull request Aug 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants