Skip to content

Commit 68f92b8

Browse files
committed
chore: some bouncing demo changes
1 parent 95579fd commit 68f92b8

1 file changed

Lines changed: 122 additions & 35 deletions

File tree

examples/src/main/java/org/codejive/twinkle/demos/BouncingTwinkleDemo.java

Lines changed: 122 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,36 @@
1212
import java.io.IOException;
1313
import java.io.Reader;
1414
import java.util.concurrent.ThreadLocalRandom;
15+
import java.util.function.Supplier;
1516
import org.codejive.twinkle.ansi.Ansi;
1617
import org.codejive.twinkle.ansi.Color;
1718
import org.codejive.twinkle.ansi.Style;
18-
import org.codejive.twinkle.ansi.util.AnsiTricks;
19+
import org.codejive.twinkle.ansi.util.Printable;
1920
import org.codejive.twinkle.fluent.Fluent;
2021
import org.codejive.twinkle.screen.Buffer;
2122
import org.codejive.twinkle.screen.BufferStack;
2223
import org.codejive.twinkle.screen.io.PrintBufferWriter;
2324
import org.codejive.twinkle.screen.util.FrameCounter;
25+
import org.codejive.twinkle.screen.util.Sized;
2426
import org.codejive.twinkle.shapes.Borders;
2527
import org.codejive.twinkle.terminal.Terminal;
28+
import org.codejive.twinkle.text.Position;
2629
import org.codejive.twinkle.text.Size;
2730
import org.codejive.twinkle.text.Sizer;
31+
import org.jspecify.annotations.NonNull;
2832

2933
class BouncingTwinkleDemo {
3034

3135
private static final BufferStack buffers = BufferStack.create();
3236
private static final Buffer helpBuffer = Buffer.of(30, 10);
3337
private static volatile Size size;
34-
private static volatile Size textSize;
35-
private static volatile int minX, minY, maxX, maxY, textX, textY, dx, dy;
38+
private static volatile Position textPos;
39+
private static volatile int dx, dy;
3640
private static volatile long currentSleep = 50;
3741
private static volatile Borders.LineStyle lineStyle = Borders.LineStyle.ASCII;
3842
private static volatile Borders.CornerStyle cornerStyle = Borders.CornerStyle.ASCII;
3943
private static final FrameCounter fps = new FrameCounter();
44+
private static final FigletShape text = new FigletShape("TWINKLE");
4045

4146
private static final String URL = "https://github.com/codejive/twinkle";
4247

@@ -70,21 +75,13 @@ public static void main(String[] args) throws Exception {
7075
try (Terminal terminal = Terminal.getDefault()) {
7176
size = terminal.size();
7277

73-
String text = AnsiTricks.blockify(Sizer.trim(FigletFont.convertOneLine("TWINKLE")));
74-
textSize = Sizer.measure(text);
75-
7678
terminal.onResize(BouncingTwinkleDemo::handleResize);
7779

7880
try {
7981
// Hide cursor and clear screen
8082
Fluent.of(terminal.writer()).screen().alternate().hide().screen().clear().done();
8183

82-
minX = 1;
83-
minY = 1;
84-
maxX = Math.max(minX, size.width() - textSize.width() - 1);
85-
maxY = Math.max(minY, size.height() - textSize.height() - 1);
86-
textX = Math.max(minX, Math.min((size.width() - textSize.width()) / 2, maxX));
87-
textY = Math.max(minY, Math.min((size.height() - textSize.height()) / 2, maxY));
84+
textPos = size.center(text.size());
8885
dx = 1;
8986
dy = 1;
9087

@@ -113,7 +110,7 @@ public static void main(String[] args) throws Exception {
113110
.markup("{green}[ {blue}{ul}{$1}Twinkle{/}{/ul}{green} ]", URL);
114111
f.at(size.width() - 12, 0)
115112
.markup("{green}[ {+}{white}fps %s{-} ]", Math.round(fps.average()));
116-
f.at(textX, textY).color(textColor).text(text).done();
113+
f.at(textPos).color(textColor).text(text).done();
117114

118115
// Write entire frame buffer to connection in one call
119116
terminal.writer().write(Ansi.cursorHome() + buffers.toAnsi());
@@ -170,11 +167,7 @@ private static void toggleHelp() {
170167
BufferStack.BufferElement helpElement = buffers.contains(helpBuffer);
171168
if (helpElement == null) {
172169
helpElement =
173-
buffers.add(
174-
helpBuffer,
175-
size.width() / 2 - helpBuffer.size().width() / 2,
176-
size.height() / 2 - helpBuffer.size().height() / 2,
177-
Integer.MAX_VALUE);
170+
buffers.add(helpBuffer, size.center(helpBuffer.size()), Integer.MAX_VALUE);
178171
helpElement.transparancy = "";
179172
} else {
180173
helpElement.visible = !helpElement.visible;
@@ -204,30 +197,31 @@ private static void drawHelp() {
204197
}
205198

206199
private static void handleResize(Size newSize) {
207-
maxX = Math.max(minX, newSize.width() - textSize.width() - 1);
208-
maxY = Math.max(minY, newSize.height() - textSize.height() - 1);
209200
size = newSize;
201+
BufferStack.BufferElement helpElement = buffers.contains(helpBuffer);
202+
if (helpElement != null) {
203+
helpElement.pos = size.center(helpBuffer.size());
204+
}
210205
}
211206

212207
private static void bounce() {
213-
textX = Math.max(minX, Math.min(textX, maxX));
214-
textY = Math.max(minY, Math.min(textY, maxY));
215-
216-
if (maxX > minX) {
217-
textX += dx;
218-
if (textX <= minX || textX >= maxX) {
219-
textX = Math.max(minX, Math.min(textX, maxX));
220-
dx = -dx;
221-
}
208+
Size bounceSize = size.shrink(text.size()).shrink(1, 1);
209+
int textX = textPos.x();
210+
int textY = textPos.y();
211+
212+
textX += dx;
213+
if (textX <= 1 || textX >= bounceSize.width()) {
214+
textX = Math.max(1, Math.min(textX, bounceSize.width()));
215+
dx = -dx;
222216
}
223217

224-
if (maxY > minY) {
225-
textY += dy;
226-
if (textY <= minY || textY >= maxY) {
227-
textY = Math.max(minY, Math.min(textY, maxY));
228-
dy = -dy;
229-
}
218+
textY += dy;
219+
if (textY <= 1 || textY >= bounceSize.height()) {
220+
textY = Math.max(1, Math.min(textY, bounceSize.height()));
221+
dy = -dy;
230222
}
223+
224+
textPos = Position.of(textX, textY);
231225
}
232226

233227
private static void colorize() {
@@ -236,4 +230,97 @@ private static void colorize() {
236230
textColor = textPalette[idx];
237231
}
238232
}
233+
234+
static class FigletShape extends CachingSupplierShape {
235+
private String text;
236+
237+
public FigletShape(String initialText) {
238+
super(null);
239+
text(initialText);
240+
}
241+
242+
public void text(String text) {
243+
this.text = text;
244+
update();
245+
}
246+
247+
protected String getUpdated() {
248+
try {
249+
return Sizer.trim(FigletFont.convertOneLine(text));
250+
} catch (Exception e) {
251+
return "???";
252+
}
253+
}
254+
}
255+
256+
static class CachingSupplierShape extends SupplierShape {
257+
protected volatile String contents;
258+
protected volatile Size size;
259+
260+
public CachingSupplierShape(Supplier<String> supplier) {
261+
super(supplier);
262+
this.contents = null;
263+
this.size = null;
264+
}
265+
266+
public String get() {
267+
if (contents == null) {
268+
contents = getUpdated();
269+
size = null;
270+
}
271+
return contents;
272+
}
273+
274+
protected void update() {
275+
contents = null;
276+
size = null;
277+
}
278+
279+
@Override
280+
public @NonNull Size size() {
281+
if (size == null) {
282+
size = Sizer.measure(get());
283+
}
284+
return size;
285+
}
286+
}
287+
288+
abstract static class SupplierShape implements Supplier<String>, Printable, Shape {
289+
protected final Supplier<String> supplier;
290+
291+
public SupplierShape(Supplier<String> supplier) {
292+
this.supplier = supplier;
293+
}
294+
295+
public String get() {
296+
if (supplier == null) {
297+
throw new IllegalStateException(
298+
"Internal error: either provide a Supplier or override get()");
299+
}
300+
return getUpdated();
301+
}
302+
303+
protected String getUpdated() {
304+
return supplier.get();
305+
}
306+
307+
@Override
308+
public @NonNull Appendable toAnsi(
309+
@NonNull Appendable appendable, @NonNull Style currentStyle) {
310+
try {
311+
return appendable.append(get());
312+
} catch (IOException e) {
313+
throw new RuntimeException(e);
314+
}
315+
}
316+
}
317+
318+
interface Shape extends Sized {
319+
320+
default @NonNull Size size() {
321+
return Size.MAX;
322+
}
323+
324+
default void resize(@NonNull Size availableSize) {}
325+
}
239326
}

0 commit comments

Comments
 (0)