Skip to content

Commit e2bdf82

Browse files
committed
WIP Calculator
1 parent 242845c commit e2bdf82

File tree

10 files changed

+467
-34
lines changed

10 files changed

+467
-34
lines changed

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/basewidgets/TextFieldWidget.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -384,13 +384,13 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) {
384384
}
385385

386386
if (this.focused && hovered && button == 0) {
387-
int int_2 = Mth.floor(mouseX) - this.bounds.x;
387+
int xOffset = Mth.floor(mouseX) - this.bounds.x;
388388
if (this.hasBorder) {
389-
int_2 -= 4;
389+
xOffset -= 4;
390390
}
391391

392392
String string_1 = this.font.plainSubstrByWidth(this.text.substring(this.firstCharacterIndex), this.getWidth());
393-
this.moveCursorTo(this.font.plainSubstrByWidth(string_1, int_2).length() + this.firstCharacterIndex);
393+
this.moveCursorTo(this.font.plainSubstrByWidth(string_1, xOffset).length() + this.firstCharacterIndex);
394394
return true;
395395
} else {
396396
return false;

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/entrylist/EntryListSearchManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private static List<HNEntryStackWrapper> getAllEntriesContextually(SearchFilter
7777
public void update(String searchTerm, boolean ignoreLastSearch, Consumer<List</*EntryStack<?> | CollapsedStack*/ Object>> update) {
7878
Stopwatch stopwatch = Stopwatch.createStarted();
7979
if (ignoreLastSearch) searchManager.markDirty();
80-
searchManager.updateFilter(searchTerm);
80+
searchManager.updateFilter(!searchTerm.isEmpty() && searchTerm.charAt(0) == '=' ? "" : searchTerm);
8181
if (searchManager.isDirty()) {
8282
searchManager.getAsync((list, filter) -> {
8383
if (!filter.getFilter().equals(searchTerm)) return;

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/FavoritesListWidget.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
import me.shedaniel.rei.impl.common.util.RectangleUtils;
6565
import net.minecraft.client.gui.GuiGraphics;
6666
import net.minecraft.client.gui.screens.Screen;
67+
import net.minecraft.network.chat.Component;
6768
import org.apache.commons.lang3.mutable.MutableLong;
6869
import org.apache.commons.lang3.tuple.Triple;
6970
import org.jetbrains.annotations.ApiStatus;
@@ -87,8 +88,17 @@ public class FavoritesListWidget extends WidgetWithBounds implements DraggableCo
8788
public final FavoritesPanel favoritePanel = new FavoritesPanel(this);
8889
public final TrashWidget trash = new TrashWidget(this);
8990
public final DisplayHistoryWidget displayHistory = new DisplayHistoryWidget(this);
90-
public final FavoritesTogglePanelButton togglePanelButton = new FavoritesTogglePanelButton(this);
91-
private List<Widget> children = ImmutableList.of(favoritePanel, togglePanelButton, systemRegion, region);
91+
public final FavoritesTogglePanelButton togglePanelButton = new FavoritesTogglePanelButton(this, 0, Component.translatable("text.rei.add_favorite_widget"),
92+
favoritePanel.expendState, () -> {
93+
favoritePanel.expendState.setTo(!favoritePanel.expendState.target(), ConfigObject.getInstance().isReducedMotion() ? 0 : 1500);
94+
favoritePanel.resetRows();
95+
});
96+
public final FavoritesTogglePanelButton calculatorPanelButton = new FavoritesTogglePanelButton(this, 1, Component.translatable("text.rei.calculator_widget"),
97+
favoritePanel.expendState, () -> {
98+
favoritePanel.expendState.setTo(!favoritePanel.expendState.target(), ConfigObject.getInstance().isReducedMotion() ? 0 : 1500);
99+
favoritePanel.resetRows();
100+
});
101+
private final List<Widget> children = ImmutableList.of(favoritePanel, togglePanelButton, calculatorPanelButton, systemRegion, region);
92102

93103
@Override
94104
public boolean mouseScrolled(double mouseX, double mouseY, double amountX, double amountY) {
@@ -260,6 +270,7 @@ public void setSystemRegionEntries(@Nullable RealRegionEntry<FavoriteEntry> remo
260270
private void renderAddFavorite(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
261271
this.favoritePanel.render(graphics, mouseX, mouseY, delta);
262272
this.togglePanelButton.render(graphics, mouseX, mouseY, delta);
273+
this.calculatorPanelButton.render(graphics, mouseX, mouseY, delta);
263274
}
264275

265276
@Override

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/panel/FadingFavoritesPanelButton.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,7 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
6767
int buttonColor = 0xFFFFFF | (Math.round(0x74 * alpha.floatValue()) << 24);
6868
graphics.fillGradient(bounds.x, bounds.y, bounds.getMaxX(), bounds.getMaxY(), buttonColor, buttonColor);
6969
if (isVisible()) {
70-
graphics.drawSpecial(source -> {
71-
renderButtonText(graphics, source);
72-
});
73-
graphics.flush();
70+
renderContents(graphics);
7471
}
7572
if (hovered) {
7673
queueTooltip();
@@ -79,7 +76,7 @@ public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
7976

8077
protected abstract boolean isAvailable(int mouseX, int mouseY);
8178

82-
protected abstract void renderButtonText(GuiGraphics graphics, MultiBufferSource bufferSource);
79+
protected abstract void renderContents(GuiGraphics graphics);
8380

8481
@Override
8582
public Rectangle getBounds() {

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/panel/FavoritesPanel.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@
4545
import java.util.ArrayList;
4646
import java.util.List;
4747

48-
@SuppressWarnings("UnstableApiUsage")
4948
public class FavoritesPanel extends WidgetWithBounds {
5049
private final FavoritesListWidget parent;
5150
public final ProgressValueAnimator<Boolean> expendState = ValueAnimator.ofBoolean(0.1, false);

runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/favorites/panel/FavoritesTogglePanelButton.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323

2424
package me.shedaniel.rei.impl.client.gui.widget.favorites.panel;
2525

26+
import me.shedaniel.clothconfig2.api.animator.ProgressValueAnimator;
2627
import me.shedaniel.math.Point;
2728
import me.shedaniel.math.Rectangle;
2829
import me.shedaniel.rei.api.client.REIRuntime;
29-
import me.shedaniel.rei.api.client.config.ConfigObject;
3030
import me.shedaniel.rei.api.client.gui.widgets.Tooltip;
3131
import me.shedaniel.rei.impl.client.gui.widget.favorites.FavoritesListWidget;
3232
import net.minecraft.client.gui.Font;
@@ -36,42 +36,53 @@
3636

3737
@SuppressWarnings("UnstableApiUsage")
3838
public class FavoritesTogglePanelButton extends FadingFavoritesPanelButton {
39-
public FavoritesTogglePanelButton(FavoritesListWidget parent) {
39+
private final int i;
40+
private final Component tooltip;
41+
private final ProgressValueAnimator<Boolean> progress;
42+
private final Runnable onClick;
43+
44+
public FavoritesTogglePanelButton(FavoritesListWidget parent, int i, Component tooltip, ProgressValueAnimator<Boolean> progress, Runnable onClick) {
4045
super(parent);
46+
this.i = i;
47+
this.tooltip = tooltip;
48+
this.progress = progress;
49+
this.onClick = onClick;
4150
}
4251

4352
@Override
4453
protected void onClick() {
45-
parent.favoritePanel.expendState.setTo(!parent.favoritePanel.expendState.target(), ConfigObject.getInstance().isReducedMotion() ? 0 : 1500);
46-
parent.favoritePanel.resetRows();
54+
this.onClick.run();
4755
}
4856

4957
@Override
5058
protected void queueTooltip() {
51-
Tooltip.create(Component.translatable("text.rei.add_favorite_widget")).queue();
59+
Tooltip.create(this.tooltip).queue();
5260
}
5361

5462
@Override
5563
protected Rectangle updateArea(Rectangle fullArea) {
56-
return new Rectangle(fullArea.x + 4, fullArea.getMaxY() - 16 - 4, 16, 16);
64+
return new Rectangle(fullArea.x + 4 + i * 20, fullArea.getMaxY() - 16 - 4, 16, 16);
5765
}
5866

5967
@Override
6068
protected boolean isAvailable(int mouseX, int mouseY) {
61-
boolean expended = parent.favoritePanel.expendState.value();
69+
boolean expended = this.progress.value();
6270
return parent.fullBounds.contains(mouseX, mouseY) || REIRuntime.getInstance().getOverlay().orElseThrow().getEntryList().containsMouse(new Point(mouseX, mouseY)) || expended;
6371
}
6472

6573
@Override
66-
protected void renderButtonText(GuiGraphics graphics, MultiBufferSource bufferSource) {
67-
float expendProgress = (float) parent.favoritePanel.expendState.progress();
68-
if (expendProgress < .9f) {
69-
int textColor = 0xFFFFFF | (Math.round(0xFF * alpha.floatValue() * (1 - expendProgress)) << 24);
70-
font.drawInBatch("+", bounds.getCenterX() - 2.5f, bounds.getCenterY() - 3, textColor, false, graphics.pose().last().pose(), bufferSource, Font.DisplayMode.NORMAL, 0, 15728880);
71-
}
72-
if (expendProgress > .1f) {
73-
int textColor = 0xFFFFFF | (Math.round(0xFF * alpha.floatValue() * expendProgress) << 24);
74-
font.drawInBatch("+", bounds.getCenterX() - 2.5f, bounds.getCenterY() - 3, textColor, false, graphics.pose().last().pose(), bufferSource, Font.DisplayMode.NORMAL, 0, 15728880);
75-
}
74+
protected void renderContents(GuiGraphics graphics) {
75+
graphics.drawSpecial(source -> {
76+
float expendProgress = (float) this.progress.progress();
77+
if (expendProgress < .9f) {
78+
int textColor = 0xFFFFFF | (Math.round(0xFF * alpha.floatValue() * (1 - expendProgress)) << 24);
79+
font.drawInBatch("+", bounds.getCenterX() - 2.5f, bounds.getCenterY() - 3, textColor, false, graphics.pose().last().pose(), source, Font.DisplayMode.NORMAL, 0, 15728880);
80+
}
81+
if (expendProgress > .1f) {
82+
int textColor = 0xFFFFFF | (Math.round(0xFF * alpha.floatValue() * expendProgress) << 24);
83+
font.drawInBatch("+", bounds.getCenterX() - 2.5f, bounds.getCenterY() - 3, textColor, false, graphics.pose().last().pose(), source, Font.DisplayMode.NORMAL, 0, 15728880);
84+
}
85+
});
86+
graphics.flush();
7687
}
7788
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/*
2+
* This file is licensed under the MIT License, part of Roughly Enough Items.
3+
* Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in all
13+
* copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
* SOFTWARE.
22+
*/
23+
24+
package me.shedaniel.rei.impl.client.gui.widget.search;
25+
26+
import me.shedaniel.clothconfig2.api.animator.NumberAnimator;
27+
import me.shedaniel.clothconfig2.api.animator.ValueAnimator;
28+
import me.shedaniel.math.Rectangle;
29+
import me.shedaniel.rei.api.client.config.ConfigObject;
30+
import me.shedaniel.rei.api.client.gui.widgets.Tooltip;
31+
import net.minecraft.client.Minecraft;
32+
import net.minecraft.client.gui.GuiGraphics;
33+
import net.minecraft.network.chat.Component;
34+
import net.minecraft.util.FormattedCharSequence;
35+
36+
import java.math.RoundingMode;
37+
import java.text.DecimalFormat;
38+
import java.text.DecimalFormatSymbols;
39+
import java.util.Locale;
40+
import java.util.function.Consumer;
41+
42+
public class CalculatorDisplay implements Consumer<String> {
43+
private static final int MAX_LEN = 7;
44+
private static final DecimalFormat DEC;
45+
private static final DecimalFormat SCI;
46+
static {
47+
DecimalFormatSymbols sym = DecimalFormatSymbols.getInstance(Locale.ROOT);
48+
DEC = new DecimalFormat("0.##########", sym);
49+
DEC.setRoundingMode(RoundingMode.HALF_UP);
50+
DEC.setGroupingUsed(false);
51+
SCI = new DecimalFormat("0.##########E0", sym);
52+
SCI.setRoundingMode(RoundingMode.HALF_UP);
53+
SCI.setGroupingUsed(false);
54+
}
55+
56+
private final OverlaySearchField searchField;
57+
private final NumberAnimator<Integer> width = ValueAnimator.ofDouble().asInt();
58+
private Component fullText = Component.empty();
59+
private FormattedCharSequence text = FormattedCharSequence.EMPTY;
60+
61+
public CalculatorDisplay(OverlaySearchField searchField) {
62+
this.searchField = searchField;
63+
}
64+
65+
public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) {
66+
int prevWidth = width.value();
67+
this.width.update(delta);
68+
if (prevWidth != width.value()) {
69+
// Keep the search field bounded
70+
this.searchField.addText("");
71+
}
72+
73+
if (width.value() <= 0) return;
74+
Rectangle searchFieldBounds = searchField.getBounds();
75+
boolean contains = searchFieldBounds.contains(mouseX, mouseY) || searchField.isFocused();
76+
graphics.fill(searchFieldBounds.getMaxX() - width.value(), searchFieldBounds.y + 1, searchFieldBounds.getMaxX() - 1, searchFieldBounds.getMaxY() - 1, contains ? 0xBBFFFFFF : 0x80FFFFFF);
77+
graphics.enableScissor(searchFieldBounds.getMaxX() - width.value() + 3, searchFieldBounds.y, searchFieldBounds.getMaxX() - 3, searchFieldBounds.getMaxY());
78+
graphics.drawString(Minecraft.getInstance().font, text, searchFieldBounds.getMaxX() - width.value() + 3, searchFieldBounds.getCenterY() - 4, 0xFF000000, false);
79+
graphics.disableScissor();
80+
81+
if (!this.fullText.getString().isEmpty() && contains && new Rectangle(searchFieldBounds.getMaxX() - width.value() + 3, searchFieldBounds.y, width.value() - 6, searchFieldBounds.getHeight()).contains(mouseX, mouseY)) {
82+
Tooltip.create(this.fullText).queue();
83+
}
84+
}
85+
86+
@Override
87+
public void accept(String text) {
88+
if (!text.startsWith("=")) {
89+
this.width.setTo(0, ConfigObject.getInstance().isReducedMotion() ? 0 : 400);
90+
this.text = Component.literal("NaN").getVisualOrderText();
91+
this.fullText = Component.empty();
92+
return;
93+
}
94+
95+
if (TextCalculator.isValid(text)) {
96+
double eval = new TextCalculator(text).eval();
97+
this.text = Component.literal(fmt(eval)).getVisualOrderText();
98+
this.fullText = Component.literal(fmtAccurate(eval));
99+
}
100+
101+
this.width.setTo(Minecraft.getInstance().font.width(this.text) + 6, ConfigObject.getInstance().isReducedMotion() ? 0 : 400);
102+
}
103+
104+
public int width() {
105+
return width.value();
106+
}
107+
108+
public static String fmt(double x) {
109+
if (Double.isNaN(x) || Double.isInfinite(x))
110+
return String.valueOf(x);
111+
112+
boolean neg = x < 0;
113+
double a = Math.abs(x);
114+
115+
// 1) SMALL <1: decimals to fill WIDTH, else sci
116+
if (a > 0 && a < 1) {
117+
int used = neg ? 1 : 0;
118+
int avail = MAX_LEN - used; // total chars left
119+
// "0" + "." → 2 chars, rest decimals
120+
int dec = avail - 2;
121+
if (dec > 0) {
122+
double minShow = Math.pow(10, -dec);
123+
if (a >= minShow) {
124+
String fmt = "%." + dec + "f";
125+
String s = String.format(Locale.ROOT, fmt, a)
126+
.replaceFirst("0+$", "") // drop trailing zeros
127+
.replaceFirst("\\.$", ""); // drop trailing dot
128+
// if we got something like ".123", prepend "0"
129+
if (s.startsWith(".")) s = "0" + s;
130+
return neg ? "-" + s : s;
131+
}
132+
}
133+
// too small → scientific
134+
return sciFmt(a, neg);
135+
}
136+
137+
// 2) exact under threshold
138+
double thresh = neg ? 1_000_000 : 10_000_000;
139+
if (a < thresh) {
140+
String small = (a == Math.rint(a))
141+
? String.valueOf((long) a)
142+
: String.format(Locale.ROOT, "%.2f", a)
143+
.replaceFirst("\\.?0+$", "");
144+
if (small.length() + (neg ? 1 : 0) <= MAX_LEN)
145+
return neg ? "-" + small : small;
146+
}
147+
148+
// 3) suffix m/b/t
149+
char suf;
150+
double v;
151+
if (a >= 1e12 && a < 1e15) {
152+
suf = 't';
153+
v = a / 1e12;
154+
} else if (a >= 1e9) {
155+
suf = 'b';
156+
v = a / 1e9;
157+
} else if (a >= 1e6) {
158+
suf = 'm';
159+
v = a / 1e6;
160+
} else {
161+
// small ≥1 but <1e6 (or neg ≥1e6)
162+
return sciFmt(a, neg);
163+
}
164+
{
165+
int used = (neg ? 1 : 0) + 1; // sign + suffix
166+
int avail = MAX_LEN - used;
167+
String intP = String.valueOf((long) v);
168+
int ip = intP.length();
169+
int dec = Math.max(0, avail - ip - 1); // -1 for dot
170+
for (; dec >= 0; dec--) {
171+
String fmt = dec > 0 ? "%." + dec + "f" : "%.0f";
172+
String man = String.format(Locale.ROOT, fmt, v);
173+
if (man.length() <= avail)
174+
return (neg ? "-" : "") + man + suf;
175+
}
176+
}
177+
178+
// 4) fallback sci
179+
return sciFmt(a, neg);
180+
}
181+
182+
private static String sciFmt(double a, boolean neg) {
183+
int exp = (int) Math.floor(Math.log10(a));
184+
double man = a / Math.pow(10, exp);
185+
String expS = String.valueOf(exp);
186+
int used = (neg ? 1 : 0) + 1 + expS.length(); // sign + 'e'+exp
187+
int avail = MAX_LEN - used;
188+
String intP = String.valueOf((long) man);
189+
int ip = intP.length();
190+
int dec = Math.max(0, avail - ip - 1);
191+
for (; dec >= 0; dec--) {
192+
String fmt = dec > 0 ? "%." + dec + "f" : "%.0f";
193+
String mS = String.format(Locale.ROOT, fmt, man);
194+
if (mS.length() <= avail)
195+
return (neg ? "-" : "") + mS + "e" + expS;
196+
}
197+
// worst‐case: truncate integer mantissa
198+
String mS = intP;
199+
if (mS.length() > avail) mS = mS.substring(0, avail);
200+
return (neg ? "-" : "") + mS + "e" + expS;
201+
}
202+
203+
public static String fmtAccurate(double x) {
204+
if (Double.isNaN(x) || Double.isInfinite(x))
205+
return String.valueOf(x);
206+
207+
double a = Math.abs(x);
208+
if (a != 0 && (a < 1e-30 || a >= 1e30)) {
209+
// scientific
210+
return SCI.format(x).replace("E", "e");
211+
} else {
212+
return DEC.format(x);
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)