1010import net .minecraftforge .fml .relauncher .Side ;
1111import net .minecraftforge .fml .relauncher .SideOnly ;
1212
13+ import org .jetbrains .annotations .ApiStatus ;
14+ import org .jetbrains .annotations .NotNull ;
15+
1316import java .awt .*;
1417import java .awt .geom .Point2D ;
18+ import java .text .DecimalFormat ;
1519import java .util .Collections ;
1620import java .util .List ;
21+ import java .util .stream .Collectors ;
1722
1823public class TextFieldRenderer extends TextRenderer {
24+ private static final DecimalFormat INTEGER_FIELD_FORMAT = new DecimalFormat ("#" );
25+ private static final char groupingSeparator = INTEGER_FIELD_FORMAT .getDecimalFormatSymbols ().getGroupingSeparator ();
26+
27+ static {
28+ INTEGER_FIELD_FORMAT .setGroupingUsed (true );
29+ INTEGER_FIELD_FORMAT .setGroupingSize (3 );
30+ }
1931
2032 protected final TextFieldHandler handler ;
2133 protected int markedColor = 0x2F72A8 ;
2234 protected int cursorColor = 0xFFFFFFFF ;
2335 protected boolean renderCursor = false ;
36+ private boolean formatAsInteger = false ;
2437
2538 public TextFieldRenderer (TextFieldHandler handler ) {
2639 this .handler = handler ;
@@ -42,6 +55,30 @@ public void setCursorColor(int cursorColor) {
4255 this .cursorColor = cursorColor ;
4356 }
4457
58+ @ ApiStatus .Experimental
59+ public void setFormatAsInteger (boolean formatAsInteger ) {
60+ this .formatAsInteger = formatAsInteger ;
61+ }
62+
63+ @ Override
64+ public void draw (List <String > lines ) {
65+ if (formatAsInteger ) lines = decorateLines (lines );
66+ super .draw (lines );
67+ }
68+
69+ private static @ NotNull List <String > decorateLines (List <String > lines ) {
70+ return lines .stream ().map (TextFieldRenderer ::tryFormatString )
71+ .collect (Collectors .toList ());
72+ }
73+
74+ private static @ NotNull String tryFormatString (String str ) {
75+ try {
76+ return INTEGER_FIELD_FORMAT .format (Long .parseLong (str ));
77+ } catch (NumberFormatException e ) {
78+ return str ;
79+ }
80+ }
81+
4582 @ Override
4683 protected void drawMeasuredLines (List <Line > measuredLines ) {
4784 drawMarked (measuredLines );
@@ -96,39 +133,79 @@ public Point getCursorPos(List<String> lines, int x, int y) {
96133 if (lines .isEmpty ()) {
97134 return new Point ();
98135 }
136+ if (formatAsInteger ) lines = decorateLines (lines );
99137 List <Line > measuredLines = measureLines (lines );
100138 y -= getStartY (measuredLines .size ());
101139 int index = (int ) (y / (getFontHeight ()));
102140 if (index < 0 ) return new Point ();
103- if (index >= measuredLines .size ())
104- return new Point (measuredLines .get (measuredLines .size () - 1 ).getText ().length (), measuredLines .size () - 1 );
141+ if (index >= measuredLines .size ()) {
142+ return new Point (getRealLength (measuredLines .get (measuredLines .size () - 1 ).getText ()), measuredLines .size () - 1 );
143+ }
105144 Line line = measuredLines .get (index );
106145 x -= getStartX (line .getWidth ());
107146 if (line .getWidth () <= 0 ) return new Point (0 , index );
108- if (line .getWidth () < x ) return new Point (line .getText ().length (), index );
147+ if (line .getWidth () < x ) {
148+ return new Point (getRealLength (line .getText ()), index );
149+ }
109150 float currentX = 0 ;
151+ int ignoredChars = 0 ;
110152 for (int i = 0 ; i < line .getText ().length (); i ++) {
111153 char c = line .getText ().charAt (i );
112154 float charWidth = getFontRenderer ().getCharWidth (c ) * this .scale ;
113155 currentX += charWidth ;
156+ if (isIgnoredChar (c )) ignoredChars ++;
114157 if (currentX >= x ) {
115158 // dist with current letter < dist without current letter -> next letter pos
116159 if (Math .abs (currentX - x ) < Math .abs (currentX - charWidth - x )) i ++;
117- return new Point (i , index );
160+ return new Point (i - ignoredChars , index );
118161 }
119162 }
120163 return new Point ();
121164 }
122165
166+ /**
167+ * Whether the given character should be ignored for cursor positioning purposes
168+ */
169+ @ ApiStatus .Experimental
170+ protected boolean isIgnoredChar (int c ) {
171+ return formatAsInteger && c == groupingSeparator ;
172+ }
173+
174+ private int getRealLength (String text ) {
175+ int length = text .length ();
176+ if (formatAsInteger ) length -= (int ) text .chars ().filter (this ::isIgnoredChar ).count ();
177+ return length ;
178+ }
179+
123180 public Point2D .Float getPosOf (List <Line > measuredLines , Point cursorPos ) {
124181 if (measuredLines .isEmpty ()) {
125182 return new Point2D .Float (getStartX (0 ), getStartYOfLines (1 ));
126183 }
127184 Line line = measuredLines .get (cursorPos .y );
128- String sub = line . getText (). substring ( 0 , Math . min ( line . getText (). length () , cursorPos . x ) );
185+ String sub = getStringBeforeCursor ( line , cursorPos );
129186 return new Point2D .Float (getStartX (line .getWidth ()) + getFontRenderer ().getStringWidth (sub ) * this .scale , getStartYOfLines (measuredLines .size ()) + cursorPos .y * getFontHeight ());
130187 }
131188
189+ private @ NotNull String getStringBeforeCursor (Line line , Point cursorPos ) {
190+ String text = line .getText ();
191+ String sub = text .substring (0 , Math .min (text .length (), cursorPos .x ));
192+ if (formatAsInteger ) {
193+ int i = 0 ;
194+ int ignoredChars = 0 ;
195+ while (i < sub .length () && i + ignoredChars < text .length ()) {
196+ if (isIgnoredChar (text .charAt (i + ignoredChars ))) {
197+ ignoredChars ++;
198+ } else {
199+ i ++;
200+ }
201+ }
202+ if (ignoredChars > 0 ) {
203+ sub = sub + text .substring (sub .length (), Math .min (text .length (), cursorPos .x + ignoredChars ));
204+ }
205+ }
206+ return sub ;
207+ }
208+
132209 @ SideOnly (Side .CLIENT )
133210 public void drawMarked (float y0 , float x0 , float x1 ) {
134211 y0 -= 1 ;
0 commit comments