Skip to content

Commit c987110

Browse files
committed
fix nested viewport hover issue
1 parent 4751817 commit c987110

File tree

14 files changed

+165
-100
lines changed

14 files changed

+165
-100
lines changed

src/main/java/com/cleanroommc/modularui/api/layout/IViewport.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
/**
1010
* A gui element which can transform its children f.e. a scrollable list.
1111
*/
12-
public interface IViewport {
12+
public interface IViewport extends IWidget {
1313

1414
/**
1515
* Apply shifts of this viewport.
@@ -26,7 +26,11 @@ default void transformChildren(IViewportStack stack) {}
2626
* @param x x position
2727
* @param y y position
2828
*/
29-
void getWidgetsAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y);
29+
default void getWidgetsAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {
30+
if (hasChildren()) {
31+
getChildrenAt(this, stack, widgets, x, y);
32+
}
33+
}
3034

3135
/**
3236
* Gathers all children at a position. Transformations from this viewport are not applied.
@@ -37,7 +41,11 @@ default void transformChildren(IViewportStack stack) {}
3741
* @param x x position
3842
* @param y y position
3943
*/
40-
default void getSelfAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {}
44+
default void getSelfAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {
45+
if (isInside(stack, x, y)) {
46+
widgets.add(this, stack, getAdditionalHoverInfo(stack, x, y));
47+
}
48+
}
4149

4250
/**
4351
* Called during drawing twice (before children are drawn). Once with transformation of this viewport and once without
@@ -71,7 +79,7 @@ static void getChildrenAt(IWidget parent, IViewportStack stack, HoveredWidgetLis
7179
stack.pushMatrix();
7280
child.transform(stack);
7381
if (child.isInside(stack, x, y)) {
74-
widgetList.add(child, stack.peek(), child.getAdditionalHoverInfo(stack, x, y));
82+
widgetList.add(child, stack, child.getAdditionalHoverInfo(stack, x, y));
7583
}
7684
if (child.hasChildren()) {
7785
getChildrenAt(child, stack, widgetList, x, y);
@@ -116,6 +124,4 @@ static boolean foreachChild(IViewportStack stack, IWidget parent, Predicate<IWid
116124
}
117125
return true;
118126
}
119-
120-
IViewport EMPTY = (viewports, widgets, x, y) -> {};
121-
}
127+
}

src/main/java/com/cleanroommc/modularui/api/layout/IViewportStack.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
import com.cleanroommc.modularui.utils.Vector3f;
66
import com.cleanroommc.modularui.widget.sizer.Area;
77

8+
import org.jetbrains.annotations.ApiStatus;
89
import org.jetbrains.annotations.Nullable;
910

1011
/**
1112
* This handles all viewports in a GUI. Also keeps track of a matrix stack used for rendering and
1213
* user interaction.
1314
*/
15+
@ApiStatus.NonExtendable
1416
public interface IViewportStack {
1517

1618
/**
@@ -206,4 +208,4 @@ default Vector3f unTransform(Vector3f vec) {
206208
*/
207209
@Nullable
208210
TransformationMatrix peek();
209-
}
211+
}

src/main/java/com/cleanroommc/modularui/api/widget/IDraggable.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.cleanroommc.modularui.api.widget;
22

3-
import com.cleanroommc.modularui.api.layout.IViewport;
43
import com.cleanroommc.modularui.api.layout.IViewportStack;
54
import com.cleanroommc.modularui.screen.viewport.ModularGuiContext;
6-
import com.cleanroommc.modularui.utils.HoveredWidgetList;
75
import com.cleanroommc.modularui.widget.sizer.Area;
86

97
import org.jetbrains.annotations.Nullable;
@@ -14,12 +12,12 @@
1412
*
1513
* @see com.cleanroommc.modularui.widget.DraggableWidget
1614
*/
17-
public interface IDraggable extends IViewport {
15+
public interface IDraggable {
1816

1917
/**
2018
* Gets called every frame after everything else is rendered.
2119
* Is only called when {@link #isMoving()} is true.
22-
* Translate to the mouse pos and draw with {@link com.cleanroommc.modularui.widget.WidgetTree#drawTree(IWidget, ModularGuiContext, boolean)}.
20+
* Translate to the mouse pos and draw with {@link com.cleanroommc.modularui.widget.WidgetTree#drawTree(IWidget, ModularGuiContext, boolean, boolean)}.
2321
*
2422
* @param partialTicks difference from last from
2523
*/
@@ -61,7 +59,4 @@ default boolean canDropHere(int x, int y, @Nullable IGuiElement widget) {
6159
void setMoving(boolean moving);
6260

6361
void transform(IViewportStack viewportStack);
64-
65-
@Override
66-
default void getWidgetsAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {}
6762
}

src/main/java/com/cleanroommc/modularui/screen/ModularPanel.java

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,20 +197,6 @@ public void transform(IViewportStack stack) {
197197
}
198198
}
199199

200-
@Override
201-
public void getWidgetsAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {
202-
if (hasChildren()) {
203-
IViewport.getChildrenAt(this, stack, widgets, x, y);
204-
}
205-
}
206-
207-
@Override
208-
public void getSelfAt(IViewportStack stack, HoveredWidgetList widgets, int x, int y) {
209-
if (isInside(stack, x, y)) {
210-
widgets.add(this, stack.peek(), getAdditionalHoverInfo(stack, x, y));
211-
}
212-
}
213-
214200
private void findHoveredWidgets() {
215201
this.hovering.clear();
216202
this.hovering.trim();

src/main/java/com/cleanroommc/modularui/screen/viewport/GuiViewportStack.java

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class GuiViewportStack implements IViewportStack {
2323

2424
private static final Vector3f sharedVec = new Vector3f();
2525

26+
private final ObjectArrayList<TransformationMatrix> matrixPool = new ObjectArrayList<>(256);
2627
private final ObjectArrayList<TransformationMatrix> viewportStack = new ObjectArrayList<>();
2728
private final List<Area> viewportAreas = new ArrayList<>();
2829
private TransformationMatrix top;
@@ -54,17 +55,29 @@ public void pushViewport(IViewport viewport, Area area) {
5455
}
5556
this.topViewport.getArea().clamp(child);
5657
}
57-
this.viewportStack.push(new TransformationMatrix(viewport, child, parent));
58+
this.viewportStack.push(newMatrix().construct(viewport, child, parent));
5859
updateViewport(false);
5960
this.topViewport = this.viewportStack.top();
6061
}
6162

6263
@Override
6364
public void pushMatrix() {
64-
this.viewportStack.push(new TransformationMatrix(this.top == null ? null : this.top.getMatrix()));
65+
this.viewportStack.push(newMatrix().construct(this.top == null ? null : this.top.getMatrix()));
6566
updateViewport(false);
6667
}
6768

69+
private TransformationMatrix newMatrix() {
70+
return !this.matrixPool.isEmpty() ? this.matrixPool.pop() : new TransformationMatrix();
71+
}
72+
73+
private void pop() {
74+
TransformationMatrix matrix = this.viewportStack.pop();
75+
matrix.dispose();
76+
if (this.matrixPool.size() < 256) {
77+
this.matrixPool.add(matrix);
78+
}
79+
}
80+
6881
private Area getCurrentViewportArea() {
6982
while (this.viewportAreas.size() < this.viewportStack.size() + 1) {
7083
this.viewportAreas.add(new Area());
@@ -75,24 +88,33 @@ private Area getCurrentViewportArea() {
7588
@Override
7689
public void popViewport(IViewport viewport) {
7790
if (this.top == null || !this.top.isViewportMatrix() || this.top.getViewport() != viewport) {
78-
String name = this.top == null ? "null" : this.top.getViewport().toString();
79-
throw new IllegalStateException("Viewports must be popped in reverse order they were pushed. Tried to pop '" + viewport + "', but last pushed is '" + name + "'.");
91+
String name;
92+
if (this.top == null) {
93+
name = "none";
94+
} else {
95+
name = this.top.isViewportMatrix() ? asString(this.top.getViewport()) : "not a viewport";
96+
}
97+
throw new IllegalStateException("Viewports must be popped in reverse order they were pushed. Tried to pop '" + asString(viewport) + "', but last pushed is '" + name + "'.");
8098
}
81-
this.viewportStack.pop();
99+
pop();
82100
updateViewport(true);
83101
}
84102

103+
private static String asString(IViewport viewport) {
104+
return viewport == null ? "screen viewport" : viewport.toString();
105+
}
106+
85107
@Override
86108
public void popMatrix() {
87109
if (this.top.isViewportMatrix()) {
88110
throw new IllegalStateException("Tried to pop viewport matrix, but at the top is a normal matrix.");
89111
}
90-
this.viewportStack.pop();
112+
pop();
91113
updateViewport(false);
92114
}
93115

94116
public void push(TransformationMatrix transformationMatrix) {
95-
this.viewportStack.push(new TransformationMatrix(transformationMatrix, this.top == null ? null : this.top.getMatrix()));
117+
this.viewportStack.push(newMatrix().construct(transformationMatrix, this.top == null ? null : this.top.getMatrix()));
96118
updateViewport(false);
97119
if (this.top.isViewportMatrix()) {
98120
this.topViewport = this.top;
@@ -103,8 +125,9 @@ public void pop(TransformationMatrix transformationMatrix) {
103125
if (this.top.getWrapped() != transformationMatrix) {
104126
throw new IllegalArgumentException();
105127
}
106-
TransformationMatrix tm = this.viewportStack.pop();
107-
updateViewport(tm.isViewportMatrix());
128+
boolean isViewport = this.top.isViewportMatrix();
129+
pop();
130+
updateViewport(isViewport);
108131
}
109132

110133
@Override
@@ -115,7 +138,7 @@ public int getStackSize() {
115138
@Override
116139
public void popUntilIndex(int index) {
117140
for (int i = this.viewportStack.size() - 1; i > index; i--) {
118-
this.viewportStack.pop();
141+
pop();
119142
}
120143
updateViewport(true);
121144
}
@@ -124,7 +147,7 @@ public void popUntilIndex(int index) {
124147
public void popUntilViewport(IViewport viewport) {
125148
int i = this.viewportStack.size();
126149
while (--i >= 0 && this.viewportStack.top().getViewport() != viewport) {
127-
this.viewportStack.pop();
150+
pop();
128151
}
129152
updateViewport(true);
130153
}
@@ -185,16 +208,14 @@ private void checkViewport() {
185208

186209
private void updateViewport(boolean findTopViewport) {
187210
this.top = this.viewportStack.isEmpty() ? null : this.viewportStack.top();
188-
if (!findTopViewport || this.topViewport == null || !this.topViewport.isViewportMatrix()) return;
211+
if (!findTopViewport) return;
189212
// find new top viewport
190213
this.topViewport = null;
191214
if (this.viewportStack.isEmpty()) return;
192-
ListIterator<TransformationMatrix> it = this.viewportStack.listIterator(this.viewportStack.size() - 1);
193-
while (it.hasPrevious()) {
194-
TransformationMatrix transformationMatrix1 = it.previous();
195-
if (transformationMatrix1.isViewportMatrix()) {
196-
this.topViewport = transformationMatrix1;
197-
break;
215+
for (int i = this.viewportStack.size() - 1; i >= 0; i--) {
216+
TransformationMatrix matrix = this.viewportStack.get(i);
217+
if (matrix.isViewportMatrix()) {
218+
this.topViewport = matrix;
198219
}
199220
}
200221
}
@@ -245,4 +266,4 @@ private static Vector3f vec(float x, float y, float z) {
245266
sharedVec.set(x, y, z);
246267
return sharedVec;
247268
}
248-
}
269+
}

src/main/java/com/cleanroommc/modularui/screen/viewport/LocatedElement.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,9 @@ public void applyMatrix(GuiContext context) {
2525
public void unapplyMatrix(GuiContext context) {
2626
context.pop(this.transformationMatrix);
2727
}
28+
29+
@Override
30+
public String toString() {
31+
return "LocatedElement[" + getElement() + "]";
32+
}
2833
}

src/main/java/com/cleanroommc/modularui/screen/viewport/LocatedWidget.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,9 @@ public LocatedWidget(IWidget element, TransformationMatrix transformationMatrix,
5252
public Object getAdditionalHoverInfo() {
5353
return additionalHoverInfo;
5454
}
55+
56+
@Override
57+
public String toString() {
58+
return "LocatedWidget[" + getElement() + " | " + additionalHoverInfo + "]";
59+
}
5560
}

src/main/java/com/cleanroommc/modularui/screen/viewport/TransformationMatrix.java

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,91 @@
55
import com.cleanroommc.modularui.utils.Vector3f;
66
import com.cleanroommc.modularui.widget.sizer.Area;
77

8+
import org.jetbrains.annotations.ApiStatus;
89
import org.jetbrains.annotations.Nullable;
910

1011
/**
1112
* A single matrix in a matrix stack. Also has some other information.
1213
*/
14+
@ApiStatus.Internal
1315
public class TransformationMatrix {
1416

1517
public static final TransformationMatrix EMPTY = new TransformationMatrix(null);
1618

17-
private final TransformationMatrix wrapped;
18-
private final IViewport viewport;
19-
private final Area area;
20-
private final Matrix4f matrix;
19+
private TransformationMatrix wrapped;
20+
private IViewport viewport;
21+
private Area area;
22+
private final Matrix4f matrix = new Matrix4f();
2123
private final Matrix4f invertedMatrix = new Matrix4f();
24+
private boolean inUse = false;
2225

23-
private final boolean viewportMatrix;
24-
private boolean dirty = true;
26+
public TransformationMatrix() {}
27+
28+
public TransformationMatrix(@Nullable Matrix4f parent) {
29+
construct(parent);
30+
}
2531

2632
public TransformationMatrix(TransformationMatrix parent, @Nullable Matrix4f parentMatrix) {
33+
construct(parent, parentMatrix);
34+
}
35+
36+
private boolean viewportMatrix;
37+
private boolean dirty = true;
38+
39+
TransformationMatrix construct(TransformationMatrix parent, @Nullable Matrix4f parentMatrix) {
40+
checkInUse();
2741
this.wrapped = parent;
2842
this.viewport = parent.viewport;
2943
this.area = parent.area;
30-
this.matrix = parentMatrix == null ? new Matrix4f(parent.getMatrix()) : Matrix4f.mul(parentMatrix, parent.getMatrix(), null);
44+
if (parentMatrix == null) {
45+
this.matrix.load(parent.getMatrix());
46+
} else {
47+
Matrix4f.mul(parentMatrix, parent.getMatrix(), this.matrix);
48+
}
3149
this.viewportMatrix = parent.viewportMatrix;
50+
return this;
3251
}
3352

34-
public TransformationMatrix(@Nullable Matrix4f parent) {
35-
this.wrapped = null;
36-
this.viewport = null;
37-
this.area = null;
38-
this.matrix = new Matrix4f();
39-
this.viewportMatrix = false;
40-
if (parent != null) {
41-
this.matrix.load(parent);
42-
}
53+
TransformationMatrix construct(@Nullable Matrix4f parent) {
54+
return construct(null, null, parent, false);
55+
}
56+
57+
TransformationMatrix construct(IViewport viewport, Area area, @Nullable Matrix4f parent) {
58+
return construct(viewport, area, parent, true);
4359
}
4460

45-
public TransformationMatrix(IViewport viewport, Area area, @Nullable Matrix4f parent) {
61+
private TransformationMatrix construct(IViewport viewport, Area area, @Nullable Matrix4f parent, boolean isViewport) {
62+
checkInUse();
4663
this.wrapped = null;
4764
this.viewport = viewport;
4865
this.area = area;
49-
this.matrix = new Matrix4f();
50-
this.viewportMatrix = true;
66+
this.viewportMatrix = isViewport;
5167
if (parent != null) {
5268
this.matrix.load(parent);
69+
} else {
70+
this.matrix.setIdentity();
5371
}
72+
return this;
73+
}
74+
75+
void dispose() {
76+
this.wrapped = null;
77+
this.viewport = null;
78+
this.area = null;
79+
this.inUse = false;
80+
this.viewportMatrix = false;
81+
}
82+
83+
private void checkInUse() {
84+
if (this.inUse) {
85+
throw new IllegalStateException("Transformation matrix is already in use!");
86+
}
87+
this.inUse = true;
88+
this.dirty = true;
89+
}
90+
91+
public boolean isInUse() {
92+
return inUse;
5493
}
5594

5695
public TransformationMatrix getWrapped() {

0 commit comments

Comments
 (0)