Widgets.java
package com.guinetik.hexafun.examples.tui;
import static com.guinetik.hexafun.examples.tui.Ansi.*;
import java.util.List;
import java.util.function.Function;
/**
* Reusable TUI widget builders.
*
* <p>All methods return Strings (pure functions), keeping side effects
* at the edges. Compose these with {@link View} for full screens.
*
* <p>Example:
* <pre class="language-java">{@code
* String header = Widgets.header("My App", 80, CYAN);
* String progress = Widgets.progressBar(75, 60, GREEN);
* String box = Widgets.box("Content here", 40, YELLOW);
* }</pre>
*/
public final class Widgets {
private Widgets() {}
// ═══════════════════════════════════════════════════════════════════
// HEADERS & TITLES
// ═══════════════════════════════════════════════════════════════════
/**
* Render a boxed header with title and optional subtitle.
*
* @param title Main title text
* @param subtitle Optional subtitle (can be null)
* @param width Total width including borders
* @param borderColor ANSI color for the border
* @return Rendered header string
*/
public static String header(
String title,
String subtitle,
int width,
String borderColor
) {
int innerWidth = width - 4;
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb
.append(
color(
" " +
DBOX_TOP_LEFT +
repeat(DBOX_HORIZONTAL, innerWidth) +
DBOX_TOP_RIGHT,
borderColor
)
)
.append("\n");
sb
.append(color(" " + DBOX_VERTICAL, borderColor))
.append(color(center(title, innerWidth), BOLD, BRIGHT_WHITE))
.append(color(DBOX_VERTICAL, borderColor))
.append("\n");
if (subtitle != null && !subtitle.isEmpty()) {
sb
.append(color(" " + DBOX_VERTICAL, borderColor))
.append(color(center(subtitle, innerWidth), DIM))
.append(color(DBOX_VERTICAL, borderColor))
.append("\n");
}
sb
.append(
color(
" " +
DBOX_BOTTOM_LEFT +
repeat(DBOX_HORIZONTAL, innerWidth) +
DBOX_BOTTOM_RIGHT,
borderColor
)
)
.append("\n");
sb.append("\n");
return sb.toString();
}
/**
* Simple header without subtitle.
*/
public static String header(String title, int width, String borderColor) {
return header(title, null, width, borderColor);
}
/**
* Section divider with label.
*
* @param label Section label
* @param width Total width
* @return Rendered divider
*/
public static String section(String label, int width) {
int labelWidth = label.length() + 2;
int lineWidth = width - 4 - labelWidth;
return (
color(
" " +
BOX_HORIZONTAL +
" " +
label +
" " +
repeat(BOX_HORIZONTAL, lineWidth),
DIM
) +
"\n\n"
);
}
// ═══════════════════════════════════════════════════════════════════
// PROGRESS & STATS
// ═══════════════════════════════════════════════════════════════════
/**
* Render a progress bar.
*
* @param percent Completion percentage (0-100)
* @param width Bar width in characters
* @param fillColor Color for filled portion
* @return Rendered progress bar
*/
public static String progressBar(int percent, int width, String fillColor) {
int clamped = Math.max(0, Math.min(100, percent));
int filled = (width * clamped) / 100;
int empty = width - filled;
return (
" " +
color(repeat(BLOCK_FULL, filled), fillColor) +
color(repeat(BLOCK_LIGHT, empty), BRIGHT_BLACK) +
color(String.format(" %3d%%", clamped), DIM) +
"\n"
);
}
/**
* Powerline-style segment for stats bars.
*
* @param text Segment text
* @param bgColor Background color
* @param fgColor Foreground color
* @return Rendered segment (without separator)
*/
public static String segment(
String text,
int width,
String bgColor,
String fgColor
) {
return color(center(text, width), bgColor, fgColor, BOLD);
}
/**
* Powerline separator arrow.
*
* @param fromColor Color of previous segment
* @param toColor Color of next segment (or null for end)
* @return Rendered separator
*/
public static String separator(String fromColor, String toColor) {
if (toColor == null) {
return color(PL_LEFT, fromColor);
}
return color(
PL_LEFT,
fromColor,
toColor.replace("\u001B[3", "\u001B[4")
); // Convert fg to bg
}
// ═══════════════════════════════════════════════════════════════════
// BOXES & CONTAINERS
// ═══════════════════════════════════════════════════════════════════
/**
* Render content in a single-line box.
*
* @param content Content to box
* @param width Box width
* @param borderColor Border color
* @return Boxed content
*/
public static String box(String content, int width, String borderColor) {
int innerWidth = width - 2;
StringBuilder sb = new StringBuilder();
sb
.append(
color(
BOX_TOP_LEFT +
repeat(BOX_HORIZONTAL, innerWidth) +
BOX_TOP_RIGHT,
borderColor
)
)
.append("\n");
sb
.append(color(BOX_VERTICAL, borderColor))
.append(center(content, innerWidth))
.append(color(BOX_VERTICAL, borderColor))
.append("\n");
sb
.append(
color(
BOX_BOTTOM_LEFT +
repeat(BOX_HORIZONTAL, innerWidth) +
BOX_BOTTOM_RIGHT,
borderColor
)
)
.append("\n");
return sb.toString();
}
/**
* Render multiple lines in a box.
*
* @param lines Content lines
* @param width Box width
* @param borderColor Border color
* @return Boxed content
*/
public static String box(
List<String> lines,
int width,
String borderColor
) {
int innerWidth = width - 2;
StringBuilder sb = new StringBuilder();
sb
.append(
color(
BOX_TOP_LEFT +
repeat(BOX_HORIZONTAL, innerWidth) +
BOX_TOP_RIGHT,
borderColor
)
)
.append("\n");
for (String line : lines) {
String padded = line.length() > innerWidth
? line.substring(0, innerWidth)
: pad(line, innerWidth);
sb
.append(color(BOX_VERTICAL, borderColor))
.append(padded)
.append(color(BOX_VERTICAL, borderColor))
.append("\n");
}
sb
.append(
color(
BOX_BOTTOM_LEFT +
repeat(BOX_HORIZONTAL, innerWidth) +
BOX_BOTTOM_RIGHT,
borderColor
)
)
.append("\n");
return sb.toString();
}
/**
* Horizontal rule/divider.
*
* @param width Total width
* @return Rendered divider
*/
public static String hr(int width) {
return color(" " + repeat(BOX_HORIZONTAL, width - 4), DIM) + "\n";
}
// ═══════════════════════════════════════════════════════════════════
// LISTS & ITEMS
// ═══════════════════════════════════════════════════════════════════
/**
* Render a numbered list item.
*
* @param index Item number
* @param icon Icon to display
* @param iconColor Icon color
* @param text Item text
* @param textColor Text color
* @return Rendered list item
*/
public static String listItem(
int index,
String icon,
String iconColor,
String text,
String textColor
) {
return (
" " +
color(index + " ", BRIGHT_BLACK) +
color(icon + " ", iconColor) +
color(text, textColor) +
"\n"
);
}
/**
* Render a simple bullet item.
*/
public static String bulletItem(String text) {
return " " + color(BULLET + " ", DIM) + text + "\n";
}
/**
* Render a numbered selection item (for menus).
*/
public static String selectionItem(int index, String text) {
return color(" [" + index + "] ", BRIGHT_BLACK) + text + "\n";
}
// ═══════════════════════════════════════════════════════════════════
// MENUS & PROMPTS
// ═══════════════════════════════════════════════════════════════════
/**
* Menu item with key shortcut.
*
* @param key Shortcut key
* @param icon Icon
* @param label Menu label
* @param keyColor Color for the key
* @return Rendered menu item
*/
public static String menuItem(
String key,
String icon,
String label,
String keyColor
) {
return (
color("[" + key + "]", keyColor, BOLD) +
color(" " + icon + " " + label + " ", keyColor)
);
}
/**
* Input prompt.
*
* @param symbol Prompt symbol
* @param promptColor Prompt color
* @return Rendered prompt
*/
public static String prompt(String symbol, String promptColor) {
return color(" " + symbol + " ", promptColor, BOLD);
}
/**
* Default prompt with ">".
*/
public static String prompt(String promptColor) {
return prompt(">", promptColor);
}
// ═══════════════════════════════════════════════════════════════════
// STATUS & MESSAGES
// ═══════════════════════════════════════════════════════════════════
/**
* Status message with arrow indicator.
*
* @param message Status message
* @param messageColor Message color
* @return Rendered status
*/
public static String status(String message, String messageColor) {
if (message == null || message.isEmpty()) return "\n";
return color(" " + ARROW_RIGHT + " " + message, messageColor) + "\n\n";
}
/**
* Success message.
*/
public static String success(String message) {
return status(CHECK + " " + message, GREEN);
}
/**
* Error message.
*/
public static String error(String message) {
return status(CROSS + " " + message, RED);
}
/**
* Warning message.
*/
public static String warning(String message) {
return status(BULLET + " " + message, YELLOW);
}
/**
* Info message.
*/
public static String info(String message) {
return status(ICON_INFO + " " + message, BLUE);
}
}