forked from Taurenkey/OtterGui
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathImGuiClip.cs
149 lines (124 loc) · 5.68 KB
/
ImGuiClip.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
using ImGuiNET;
using OtterGui.Raii;
namespace OtterGui;
public static class ImGuiClip
{
// Get the number of skipped items of a given height necessary for the current scroll bar,
// and apply the dummy of the appropriate height, removing one item spacing.
// The height has to contain the spacing.
public static int GetNecessarySkips(float height)
{
var curY = ImGui.GetScrollY();
var skips = (int)(curY / height);
if (skips > 0)
ImGui.Dummy(new Vector2(1, skips * height - ImGui.GetStyle().ItemSpacing.Y));
return skips;
}
// Draw the dummy for the remaining items computed by ClippedDraw,
// removing one item spacing.
public static void DrawEndDummy(int remainder, float height)
{
if (remainder > 0)
ImGui.Dummy(new Vector2(1, remainder * height - ImGui.GetStyle().ItemSpacing.Y));
}
// Draw a clipped random-access collection of consistent height lineHeight.
// Uses ImGuiListClipper and thus handles start- and end-dummies itself.
public static void ClippedDraw<T>(IReadOnlyList<T> data, Action<T> draw, float lineHeight)
{
ImGuiListClipperPtr clipper;
unsafe
{
clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper());
}
clipper.Begin(data.Count, lineHeight);
while (clipper.Step())
{
for (var actualRow = clipper.DisplayStart; actualRow < clipper.DisplayEnd; actualRow++)
{
if (actualRow >= data.Count)
return;
if (actualRow < 0)
continue;
draw(data[actualRow]);
}
}
clipper.End();
clipper.Destroy();
}
// Draw a clipped random-access collection of consistent height lineHeight.
// Uses ImGuiListClipper and thus handles start- and end-dummies itself, but acts on type and index.
public static void ClippedDraw<T>(IReadOnlyList<T> data, Action<T, int> draw, float lineHeight)
{
ImGuiListClipperPtr clipper;
unsafe
{
clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper());
}
clipper.Begin(data.Count, lineHeight);
while (clipper.Step())
{
for (var actualRow = clipper.DisplayStart; actualRow < clipper.DisplayEnd; actualRow++)
{
if (actualRow >= data.Count)
return;
if (actualRow < 0)
continue;
draw(data[actualRow], actualRow);
}
}
clipper.End();
clipper.Destroy();
}
// Draw non-random-access data without storing state.
// Use GetNecessarySkips first and use its return value for skips.
// startIndex can be set if using multiple separate chunks of data with different filter or draw functions (of the same height).
// Returns either the non-negative remaining objects in data that could not be drawn due to being out of the visible area,
// if count was given this will be subtracted instead of counted,
// or the bitwise-inverse of the next startIndex for subsequent collections, if there is still room for more visible objects.
public static int ClippedDraw<T>(IEnumerable<T> data, int skips, Action<T> draw, int? count = null, int startIndex = 0)
{
if (count != null && count.Value + startIndex <= skips)
return ~(count.Value + startIndex);
using var it = data.GetEnumerator();
var visible = false;
var idx = startIndex;
while (it.MoveNext())
{
if (idx >= skips)
{
using (var group = ImRaii.Group())
{
using var id = ImRaii.PushId(idx);
draw(it.Current);
}
// Just checking IsItemVisible caused some issues when not the entire width of the window was visible.
if (!ImGui.IsRectVisible(ImGui.GetItemRectMin(), ImGui.GetItemRectMin() with { Y = ImGui.GetItemRectMax().Y }))
{
if (visible)
{
if (count != null)
return Math.Max(0, count.Value - idx + startIndex - 1);
var remainder = 0;
while (it.MoveNext())
++remainder;
return remainder;
}
}
else
{
visible = true;
}
}
++idx;
}
return ~idx;
}
// Draw non-random-access data that gets filtered without storing state.
// Use GetNecessarySkips first and use its return value for skips.
// checkFilter should return true for items that should be displayed and false for those that should be skipped.
// startIndex can be set if using multiple separate chunks of data with different filter or draw functions (of the same height).
// Returns either the non-negative remaining objects in data that could not be drawn due to being out of the visible area,
// or the bitwise-inverse of the next startIndex for subsequent collections, if there is still room for more visible objects.
public static int FilteredClippedDraw<T>(IEnumerable<T> data, int skips, Func<T, bool> checkFilter, Action<T> draw, int startIndex = 0)
=> ClippedDraw(data.Where(checkFilter), skips, draw, null, startIndex);
}