refactor(ui): Better API for UI texts

This commit is contained in:
Jakub Žitník 2025-03-31 11:58:30 +02:00
parent 6ece6d3096
commit 6028b54d10
Signed by: jzitnik
GPG Key ID: C577A802A6AF4EF3
4 changed files with 185 additions and 16 deletions

View File

@ -9,6 +9,8 @@ import cz.jzitnik.game.handlers.tooluse.ToolUseProvider;
import cz.jzitnik.game.mobs.EntityHurtAnimation;
import cz.jzitnik.game.mobs.EntityKill;
import cz.jzitnik.game.smelting.Smelting;
import cz.jzitnik.game.sprites.ui.Font;
import cz.jzitnik.game.ui.Escape;
public class Dependencies {
public PlaceHandler placeHandler = new PlaceHandler();
@ -20,4 +22,6 @@ public class Dependencies {
public Smelting smelting = new Smelting();
public ToolUseProvider toolUseProvider = new ToolUseProvider();
public Sound sound = new Sound();
public Font font = new Font();
public Escape escape = new Escape();
}

View File

@ -1,11 +1,34 @@
package cz.jzitnik.game.sprites.ui;
import cz.jzitnik.tui.ResourceLoader;
import java.util.function.Function;
import org.jline.terminal.Terminal;
import cz.jzitnik.tui.ResourceLoader;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Font {
private final String[] lines = ResourceLoader.loadResource("ui/font.ans").split("\n");
private final int HEIGHT = 7;
@AllArgsConstructor
@Getter
public class FontDTO {
private String data;
private int width;
private int height;
}
@Getter
@AllArgsConstructor
private class CharDTO {
private String character;
private int width;
}
private String get(int startY, int startX, int height, int width, String color) {
StringBuilder stringBuilder = new StringBuilder();
@ -18,11 +41,10 @@ public class Font {
stringBuilder.append("\033[0m\n");
}
return stringBuilder.toString();
}
private String getDigit(char digit, String color) {
private CharDTO getDigit(char digit, String color) {
int num = Integer.valueOf(digit);
int width = 12;
int startY = HEIGHT;
@ -33,12 +55,12 @@ public class Font {
startX = (26 + 9) * width;
}
return get(startY, startX, HEIGHT, width, color);
return new CharDTO(get(startY, startX, HEIGHT, width, color), width);
}
public String getChar(char character, String color) {
public CharDTO getChar(char character, String color) {
if (character == ' ') {
return ("\033[49m ".repeat(12) + "\n").repeat(HEIGHT);
return new CharDTO(("\033[49m ".repeat(12) + "\n").repeat(HEIGHT), 12);
}
if (Character.isDigit(character)) {
@ -50,16 +72,19 @@ public class Font {
int startY = (upper ? 0 : 1) * HEIGHT;
int startX = (upper ? character - 'A' : character - 'a') * width;
return get(startY, startX, HEIGHT, width, color);
return new CharDTO(get(startY, startX, HEIGHT, width, color), width);
}
public String getLine(String text) {
public FontDTO getLine(String text) {
char[] chars = text.toCharArray();
String[][] letters = new String[chars.length][];
int width = 0;
for (int i = 0; i < chars.length; i++) {
letters[i] = getChar(chars[i], "[47m").split("\n");
var charDto = getChar(chars[i], "[47m");
letters[i] = charDto.getCharacter().split("\n");
width += charDto.getWidth();
}
StringBuilder stringBuilder = new StringBuilder();
@ -72,6 +97,137 @@ public class Font {
stringBuilder.append("\n");
}
return stringBuilder.toString();
return new FontDTO(stringBuilder.toString(), width, HEIGHT);
}
public FontDTO scale(FontDTO font, int times) {
StringBuilder stringBuilder = new StringBuilder();
var line = font.getData();
var lines = line.split("\n");
for (int lineNum = 0; lineNum < lines.length; lineNum++) {
for (int i = 0; i < times; i++) {
stringBuilder.append(scaleLine(lines[lineNum], times));
stringBuilder.append("\n");
}
}
return new FontDTO(stringBuilder.toString(), font.getWidth() * times, font.getHeight() * times);
}
private StringBuilder scaleLine(String line, int times) {
StringBuilder result = new StringBuilder();
for (char c : line.toCharArray()) {
if (c == ' ') {
result.append(" ".repeat(times));
} else {
result.append(c);
}
}
return result;
}
public FontDTO getScaledLine(String text, Size size, int termWidth) {
return scale(getLine(text), size.getFunc().apply(termWidth));
}
// This will probably need little more work
public enum Size {
SMALL((x) -> {
if (x < 800) {
return 1;
}
return 2;
}),
MEDIUM((x) -> {
if (x < 800) {
return 2;
}
return 3;
}),
LARGE((x) -> {
if (x < 800) {
return 3;
}
return 4;
});
@Getter
private Function<Integer, Integer> func;
Size(Function<Integer, Integer> func) {
this.func = func;
}
}
public enum Align {
LEFT,
RIGHT,
CENTER
}
private FontDTO applyAlignment(FontDTO data, Align alignment, int termWidth) {
return switch (alignment) {
case LEFT -> data;
case CENTER -> {
var lines = data.getData().split("\n");
int nowWidth = data.getWidth();
StringBuilder buffer = new StringBuilder();
var leftPad = (termWidth - nowWidth) / 2;
for (int i = 0; i < lines.length; i++) {
buffer.append(" ".repeat(leftPad)).append(lines[i]);
buffer.append("\n");
}
yield new FontDTO(buffer.toString(), termWidth, data.getHeight());
}
case RIGHT -> {
var lines = data.getData().split("\n");
int nowWidth = data.getWidth();
StringBuilder buffer = new StringBuilder();
var leftPad = termWidth - nowWidth;
for (int i = 0; i < lines.length; i++) {
buffer.append(" ".repeat(leftPad)).append(lines[i]);
buffer.append("\n");
}
yield new FontDTO(buffer.toString(), termWidth, data.getHeight());
}
};
}
public FontDTO line(Terminal terminal, String text, Object... attributes) {
int termWidth = terminal.getWidth();
//int termHeight = terminal.getHeight();
Size fontSize = Size.MEDIUM;
Align alignment = Align.LEFT;
for (Object attribute : attributes) {
if (Size.class.isAssignableFrom(attribute.getClass())) {
fontSize = (Size) attribute;
continue;
}
if (Align.class.isAssignableFrom(attribute.getClass())) {
alignment = (Align) attribute;
continue;
}
log.error("Invalid attribute class {}. Skipping", attribute.getClass().getSimpleName());
}
return applyAlignment(getScaledLine(text, fontSize, termWidth), alignment, termWidth);
}
}

View File

@ -2,13 +2,21 @@ package cz.jzitnik.game.ui;
import org.jline.terminal.Terminal;
import cz.jzitnik.game.sprites.ui.Font;
import cz.jzitnik.game.Game;
import cz.jzitnik.game.sprites.ui.Font.*;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Escape {
public static void render(StringBuilder buffer, Terminal terminal) {
Font font = new Font();
String character = font.getLine("Never gonna give you up never gonna let you down");
public void render(StringBuilder buffer, Terminal terminal, Game game) {
var font = game.getGameStates().dependencies.font;
var width = terminal.getWidth();
var height = terminal.getHeight();
buffer.append(character);
log.debug("Terminal width: {}", width);
log.debug("Terminal height: {}", height);
var twodcraft = font.line(terminal, "2DCraft", Size.LARGE, Align.LEFT);
buffer.append(twodcraft.getData());
}
}

View File

@ -72,6 +72,7 @@ public class ScreenRenderer {
main.append("\033[H\033[2J");
switch (game.getWindow()) {
// Different screens: Probably will need a rewrite
case INVENTORY -> game.getInventory().renderFull(main, terminal, spriteList, true, Optional.empty());
case CRAFTING_TABLE -> game.getGameStates().craftingTable.render(main, terminal, spriteList);
case CHEST -> ((Chest) game.getWorld()[game.getGameStates().clickY][game.getGameStates().clickX].stream()
@ -80,7 +81,7 @@ public class ScreenRenderer {
case FURNACE -> ((Furnace) game.getWorld()[game.getGameStates().clickY][game.getGameStates().clickX]
.stream().filter(i -> i.getBlockId().equals("furnace")).toList().getFirst().getData()).render(game,
main, terminal, spriteList);
case ESC -> Escape.render(main, terminal);
case ESC -> game.getGameStates().dependencies.escape.render(main, terminal, game);
case WORLD -> {
// World