Skip to content

Commit 68d2799

Browse files
authored
Tooltip & Hover Fix (#57)
* fix tooltip ignoring transformations * fix parent widgets not being hovered when they get background from theme
1 parent c69cec4 commit 68d2799

13 files changed

Lines changed: 92 additions & 72 deletions

File tree

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import com.cleanroommc.modularui.api.drawable.IKey;
55
import com.cleanroommc.modularui.screen.Tooltip;
66
import com.cleanroommc.modularui.utils.Alignment;
7-
import com.cleanroommc.modularui.widget.sizer.Area;
87

98
import org.jetbrains.annotations.NotNull;
109
import org.jetbrains.annotations.Nullable;
@@ -69,17 +68,6 @@ default W tooltipBuilder(Consumer<Tooltip> tooltipBuilder) {
6968
return getThis();
7069
}
7170

72-
/**
73-
* Sets an excluded area. The tooltip will always try to avoid the excluded area.
74-
*
75-
* @param area area to exclude
76-
* @return this
77-
*/
78-
default W excludeTooltipArea(Area area) {
79-
tooltip().excludeArea(area);
80-
return getThis();
81-
}
82-
8371
/**
8472
* Sets a general tooltip position. The true position is calculated every frame.
8573
*

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

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.cleanroommc.modularui.api.drawable.IDrawable;
55
import com.cleanroommc.modularui.api.drawable.IIcon;
66
import com.cleanroommc.modularui.api.drawable.IKey;
7+
import com.cleanroommc.modularui.api.widget.IWidget;
78
import com.cleanroommc.modularui.drawable.*;
89
import com.cleanroommc.modularui.screen.viewport.GuiContext;
910
import com.cleanroommc.modularui.utils.Alignment;
@@ -27,9 +28,9 @@
2728

2829
public class Tooltip {
2930

31+
private final IWidget parent;
3032
private final List<IDrawable> lines = new ArrayList<>();
3133
private List<IDrawable> additionalLines = new ArrayList<>();
32-
private Area excludeArea;
3334
private Pos pos = ModularUIConfig.tooltipPos;
3435
private boolean customPos = false;
3536
private Consumer<Tooltip> tooltipBuilder;
@@ -47,6 +48,10 @@ public class Tooltip {
4748

4849
private boolean dirty = true;
4950

51+
public Tooltip(IWidget parent) {
52+
this.parent = parent;
53+
}
54+
5055
public void buildTooltip() {
5156
this.dirty = false;
5257
this.lines.clear();
@@ -136,24 +141,24 @@ public Rectangle determineTooltipArea(GuiContext context, List<IDrawable> lines,
136141
}
137142

138143
if (this.pos == Pos.NEXT_TO_MOUSE) {
139-
final int PADDING = 8;
144+
final int padding = 8;
140145
// magic number to place tooltip nicer. Look at GuiScreen#L237
141-
final int MOUSE_OFFSET = 12;
142-
int x = mouseX + MOUSE_OFFSET, y = mouseY - MOUSE_OFFSET;
143-
if (x < PADDING) {
144-
x = PADDING;
145-
} else if (x + width + PADDING > screenWidth) {
146-
x -= MOUSE_OFFSET * 2 + width; // flip side of cursor
147-
if (x < PADDING) {
148-
x = PADDING;
146+
final int mouseOffset = 12;
147+
int x = mouseX + mouseOffset, y = mouseY - mouseOffset;
148+
if (x < padding) {
149+
x = padding;
150+
} else if (x + width + padding > screenWidth) {
151+
x -= mouseOffset * 2 + width; // flip side of cursor
152+
if (x < padding) {
153+
x = padding;
149154
}
150155
}
151-
y = MathHelper.clamp(y, PADDING, screenHeight - PADDING - height);
156+
y = MathHelper.clamp(y, padding, screenHeight - padding - height);
152157
return new Rectangle(x, y, width, height);
153158
}
154159

155-
if (this.excludeArea == null) {
156-
throw new IllegalStateException();
160+
if (this.parent == null) {
161+
throw new IllegalStateException("Tooltip pos is " + this.pos.name() + ", but no widget parent is set!");
157162
}
158163

159164
int minWidth = 0;
@@ -168,13 +173,16 @@ public Rectangle determineTooltipArea(GuiContext context, List<IDrawable> lines,
168173
int shiftAmount = 10;
169174
int padding = 7;
170175

176+
Area area = Area.SHARED;
177+
area.set(this.parent.getArea());
178+
area.setPos(0, 0); // context is transformed to this widget
179+
area.transformAndRectanglerize(context);
171180
int x = 0, y = 0;
172181
if (this.pos.vertical) {
173-
int xArea = this.excludeArea.x;
174-
if (width < this.excludeArea.width) {
175-
x = xArea + shiftAmount;
182+
if (width < area.width) {
183+
x = area.x + shiftAmount;
176184
} else {
177-
x = xArea - shiftAmount;
185+
x = area.x - shiftAmount;
178186
if (x < padding) {
179187
x = padding;
180188
} else if (x + width > screenWidth - padding) {
@@ -188,33 +196,32 @@ public Rectangle determineTooltipArea(GuiContext context, List<IDrawable> lines,
188196

189197
Pos pos = this.pos;
190198
if (this.pos == Pos.VERTICAL) {
191-
int bottomSpace = screenHeight - this.excludeArea.ey();
192-
pos = bottomSpace < height + padding && bottomSpace < this.excludeArea.y ? Pos.ABOVE : Pos.BELOW;
199+
int bottomSpace = screenHeight - area.ey();
200+
pos = bottomSpace < height + padding && bottomSpace < area.y ? Pos.ABOVE : Pos.BELOW;
193201
}
194202

195203
if (pos == Pos.BELOW) {
196-
y = this.excludeArea.y + this.excludeArea.height + padding;
204+
y = area.ey() + padding;
197205
} else if (pos == Pos.ABOVE) {
198-
y = this.excludeArea.y - height - padding;
206+
y = area.y - height - padding;
199207
}
200208
} else if (this.pos.horizontal) {
201209
boolean usedMoreSpaceSide = false;
202210
Pos pos = this.pos;
203211
if (this.pos == Pos.HORIZONTAL) {
204-
if (this.excludeArea.x > screenWidth - this.excludeArea.x - this.excludeArea.width) {
212+
if (area.x > screenWidth - area.ex()) {
205213
pos = Pos.LEFT;
206214
x = 0;
207215
} else {
208216
pos = Pos.RIGHT;
209-
x = screenWidth - this.excludeArea.x - this.excludeArea.width + padding;
217+
x = screenWidth - area.ex() + padding;
210218
}
211219
}
212220

213-
int yArea = this.excludeArea.y;
214-
if (height < this.excludeArea.height) {
215-
y = yArea + shiftAmount;
221+
if (height < area.height) {
222+
y = area.y + shiftAmount;
216223
} else {
217-
y = yArea - shiftAmount;
224+
y = area.y - shiftAmount;
218225
if (y < padding) {
219226
y = padding;
220227
}
@@ -223,9 +230,9 @@ public Rectangle determineTooltipArea(GuiContext context, List<IDrawable> lines,
223230
if (x + width > screenWidth - padding) {
224231
int maxWidth;
225232
if (pos == Pos.LEFT) {
226-
maxWidth = Math.max(minWidth, this.excludeArea.x - padding * 2);
233+
maxWidth = Math.max(minWidth, area.x - padding * 2);
227234
} else {
228-
maxWidth = Math.max(minWidth, screenWidth - this.excludeArea.x - this.excludeArea.width - padding * 2);
235+
maxWidth = Math.max(minWidth, screenWidth - area.ex() - padding * 2);
229236
}
230237
usedMoreSpaceSide = true;
231238
renderer.setAlignment(this.alignment, maxWidth);
@@ -235,14 +242,14 @@ public Rectangle determineTooltipArea(GuiContext context, List<IDrawable> lines,
235242
}
236243

237244
if (this.pos == Pos.HORIZONTAL && !usedMoreSpaceSide) {
238-
int rightSpace = screenWidth - this.excludeArea.x - this.excludeArea.width;
239-
pos = rightSpace < width + padding && rightSpace < this.excludeArea.x ? Pos.LEFT : Pos.RIGHT;
245+
int rightSpace = screenWidth - area.ex();
246+
pos = rightSpace < width + padding && rightSpace < area.x ? Pos.LEFT : Pos.RIGHT;
240247
}
241248

242249
if (pos == Pos.RIGHT) {
243-
x = this.excludeArea.x + this.excludeArea.width + padding;
250+
x = area.ex() + padding;
244251
} else if (pos == Pos.LEFT) {
245-
x = this.excludeArea.x - width - padding;
252+
x = area.x - width - padding;
246253
}
247254
}
248255
return new Rectangle(x, y, width, height);
@@ -259,10 +266,6 @@ public void markDirty() {
259266
this.dirty = true;
260267
}
261268

262-
public Area getExcludeArea() {
263-
return this.excludeArea;
264-
}
265-
266269
public int getShowUpTimer() {
267270
return this.showUpTimer;
268271
}
@@ -280,11 +283,6 @@ public boolean hasTitleMargin() {
280283
return this.hasTitleMargin;
281284
}
282285

283-
public Tooltip excludeArea(Area area) {
284-
this.excludeArea = area;
285-
return this;
286-
}
287-
288286
public Tooltip pos(Pos pos) {
289287
this.customPos = true;
290288
this.pos = pos;

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,7 @@ public void rotate(float angle, float x, float y, float z) {
150150
}
151151

152152
public void rotateZ(float angle) {
153-
checkViewport();
154-
this.top.getMatrix().rotate(angle, vec(0f, 0f, 1f));
155-
this.top.markDirty();
153+
rotate(angle, 0f, 0f, 1f);
156154
}
157155

158156
public void scale(float x, float y) {

src/main/java/com/cleanroommc/modularui/test/EventHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public static void onItemUse(PlayerInteractEvent.RightClickItem event) {
1616
.inFrontOf(Minecraft.getMinecraft().player, 5, false)
1717
.screenScale(0.5f)
1818
.open(new TestGui());*/
19-
ClientGUI.open(new TestGui());
19+
ClientGUI.open(new ResizerTest());
2020
}
2121
}
2222
}

src/main/java/com/cleanroommc/modularui/test/ResizerTest.java

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package com.cleanroommc.modularui.test;
22

3+
import com.cleanroommc.modularui.api.drawable.IKey;
4+
import com.cleanroommc.modularui.api.layout.IViewportStack;
35
import com.cleanroommc.modularui.drawable.GuiTextures;
46
import com.cleanroommc.modularui.screen.CustomModularScreen;
57
import com.cleanroommc.modularui.screen.ModularPanel;
68
import com.cleanroommc.modularui.screen.viewport.GuiContext;
7-
import com.cleanroommc.modularui.utils.Alignment;
8-
import com.cleanroommc.modularui.widgets.ButtonWidget;
9-
import com.cleanroommc.modularui.widgets.layout.Column;
10-
import com.cleanroommc.modularui.widgets.layout.Row;
9+
import com.cleanroommc.modularui.widget.Widget;
10+
11+
import net.minecraft.client.Minecraft;
1112

1213
import org.jetbrains.annotations.NotNull;
1314

@@ -25,14 +26,32 @@ public class ResizerTest extends CustomModularScreen {
2526
.align(Alignment.Center));*/
2627
return ModularPanel.defaultPanel("main")
2728
.size(150)
28-
.child(new Column()
29+
.child(new SpinningWidget()
30+
.size(80, 20)
31+
.center()
32+
.background(GuiTextures.MC_BUTTON)
33+
.overlay(IKey.str("Text"))
34+
.addTooltipLine("Long Tooltip Line"));
35+
/*.child(new Column()
2936
.alignX(0.5f)
3037
.heightRel(1f)
3138
.margin(0, 7)
3239
.coverChildrenWidth()
3340
.mainAxisAlignment(Alignment.MainAxis.SPACE_BETWEEN)
3441
.child(new ButtonWidget<>().width(40))
3542
.child(new Row().height(30).widthRel(1f).background(GuiTextures.CHECKBOARD).debugName("row"))
36-
.child(new ButtonWidget<>()));
43+
.child(new ButtonWidget<>()));*/
44+
}
45+
46+
private static class SpinningWidget extends Widget<SpinningWidget> {
47+
48+
@Override
49+
public void transform(IViewportStack stack) {
50+
super.transform(stack);
51+
stack.translate(getArea().width / 2f, getArea().height / 2f);
52+
float p = Minecraft.getSystemTime() % 4000 / 4000f;
53+
stack.rotateZ((float) (p * Math.PI * 2));
54+
stack.translate(-getArea().width / 2f, -getArea().height / 2f);
55+
}
3756
}
3857
}

src/main/java/com/cleanroommc/modularui/test/TestGui.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public void onClose() {
5050
List<List<AvailableElement>> availableMatrix = Grid.mapToMatrix(2, this.lines, (index, value) -> {
5151
AvailableElement availableElement = new AvailableElement().overlay(IKey.str(value))
5252
.size(60, 14)
53+
.addTooltipLine(value)
5354
.onMousePressed(mouseButton1 -> {
5455
if (this.availableElements.get(value).available) {
5556
ref.get().add(value, -1);
@@ -85,6 +86,7 @@ public void onClose() {
8586
.pos(10, 10).right(10).bottom(10))*/
8687
SortableListWidget<String, SortableListWidget.Item<String>> sortableListWidget = SortableListWidget.sortableBuilder(this.lines, this.configuredOptions,
8788
s -> new SortableListWidget.Item<>(s, new Widget<>()
89+
.addTooltipLine(s)
8890
.background(GuiTextures.BUTTON_CLEAN)
8991
.overlay(IKey.str(s))
9092
.left(0).right(10))

src/main/java/com/cleanroommc/modularui/widget/ParentWidget.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.cleanroommc.modularui.api.widget.IWidget;
44
import com.cleanroommc.modularui.screen.ModularPanel;
55

6+
import com.cleanroommc.modularui.theme.WidgetTheme;
7+
68
import org.jetbrains.annotations.NotNull;
79

810
import java.util.ArrayList;
@@ -25,7 +27,13 @@ public boolean canHover() {
2527
return getBackground() != null ||
2628
getHoverBackground() != null ||
2729
getHoverOverlay() != null ||
28-
getTooltip() != null;
30+
getTooltip() != null ||
31+
hasThemeBackground();
32+
}
33+
34+
protected boolean hasThemeBackground() {
35+
WidgetTheme widgetTheme = getWidgetTheme(getContext().getTheme());
36+
return widgetTheme.getBackground() != null || widgetTheme.getHoverBackground() != null;
2937
}
3038

3139
public boolean addChild(IWidget child, int index) {

src/main/java/com/cleanroommc/modularui/widget/Widget.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ public Tooltip getTooltip() {
210210
@Override
211211
public @NotNull Tooltip tooltip() {
212212
if (this.tooltip == null) {
213-
this.tooltip = new Tooltip().excludeArea(getArea());
213+
this.tooltip = new Tooltip(this);
214214
}
215215
return this.tooltip;
216216
}

src/main/java/com/cleanroommc/modularui/widget/WidgetTree.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,24 @@ public static void drawTree(IWidget parent, GuiContext context, boolean ignoreEn
202202
}
203203

204204
public static void drawTreeForeground(IWidget parent, GuiContext context) {
205+
IViewport viewport = parent instanceof IViewport viewport1 ? viewport1 : null;
206+
context.pushMatrix();
207+
parent.transform(context);
208+
205209
GlStateManager.color(1, 1, 1, 1);
206210
GlStateManager.enableBlend();
207211
parent.drawForeground(context);
208212

209213
List<IWidget> children = parent.getChildren();
210214
if (!children.isEmpty()) {
215+
if (viewport != null) {
216+
context.pushViewport(viewport, parent.getArea());
217+
viewport.transformChildren(context);
218+
}
211219
children.forEach(widget -> drawTreeForeground(widget, context));
220+
if (viewport != null) context.popViewport(viewport);
212221
}
222+
context.popMatrix();
213223
}
214224

215225
@ApiStatus.Internal

src/main/java/com/cleanroommc/modularui/widgets/CycleButtonWidget.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,9 @@ public CycleButtonWidget addTooltip(int state, String tooltip) {
182182

183183
public CycleButtonWidget length(int length) {
184184
this.length = length;
185+
// adjust tooltip buffer size
185186
while (this.stateTooltip.size() < this.length) {
186-
Tooltip tooltip = new Tooltip().excludeArea(getArea());
187-
this.stateTooltip.add(tooltip);
187+
this.stateTooltip.add(new Tooltip(this));
188188
}
189189
while (this.stateTooltip.size() > this.length) {
190190
this.stateTooltip.remove(this.stateTooltip.size() - 1);

0 commit comments

Comments
 (0)