mirror of
				https://github.com/ocornut/imgui.git
				synced 2025-10-26 12:27:30 +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:
		
							
								
								
									
										128
									
								
								imgui.cpp
									
									
									
									
									
								
							
							
						
						
									
										128
									
								
								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; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -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++) | ||||||
|         { |         { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 ocornut
					ocornut