AnsiShopTUI.java
· 12 KiB · Java
Raw
import java.util.Scanner;
/**
* AnsiShopTUI
* ------------------------------------------------------------------
* A VERY simple demonstration of how ANSI terminal escape codes can be
* used to build a text user interface (TUI), without any external
* libraries (no Lanterna, no ncurses, no JLine -- just plain old
* escape sequences printed to System.out).
*
* The idea: your terminal understands special sequences of characters
* that start with the "ESC" character (ASCII 27) followed by "[".
* These sequences tell the terminal to do things like clear the
* screen, move the cursor to a specific row/column, or change text
* color. We define those sequences as simple String constants below,
* then wrap them in tiny helper methods (clearScreen, gotoXY,
* printAt, etc.) so the rest of the program never has to deal with
* raw escape codes directly.
*
* This is a "product lookup" toy app for a fake store that sells
* keyboards, mice, monitors, and power strips. It has several
* screens (Welcome -> Main Menu -> Category List -> Product Detail)
* so you can see how a TUI moves the user between different "pages"
* just by clearing the screen and redrawing.
*
* To run:
* javac AnsiShopTUI.java
* java AnsiShopTUI
*
* NOTE: This works in a real terminal (Terminal.app, iTerm, gnome
* terminal, Windows Terminal, etc). It will NOT render correctly
* inside some IDE "Run" consoles that don't support ANSI codes.
* ------------------------------------------------------------------
*/
public class AnsiShopTUI {
// ================================================================
// 1. RAW ANSI ESCAPE CODES (as constants)
// ================================================================
// Every ANSI escape sequence starts with this "Control Sequence
// Introducer" (CSI): ESC + "[". is the escape character.
static final String ESC = "[";
// --- Screen / cursor control ---
static final String CLEAR_SCREEN = ESC + "2J"; // erase entire screen
static final String CURSOR_HOME = ESC + "H"; // move cursor to row 1, col 1
static final String HIDE_CURSOR = ESC + "?25l"; // hide the blinking cursor
static final String SHOW_CURSOR = ESC + "?25h"; // show the blinking cursor
static final String CLEAR_LINE = ESC + "2K"; // erase the current line
// --- Text style / color ---
static final String RESET = ESC + "0m"; // reset all colors/styles to default
static final String BOLD = ESC + "1m";
static final String REVERSE = ESC + "7m"; // swap foreground/background (highlight)
static final String FG_RED = ESC + "31m";
static final String FG_GREEN = ESC + "32m";
static final String FG_YELLOW = ESC + "33m";
static final String FG_BLUE = ESC + "34m";
static final String FG_MAGENTA = ESC + "35m";
static final String FG_CYAN = ESC + "36m";
static final String FG_WHITE = ESC + "37m";
static final String BG_BLUE = ESC + "44m";
// ================================================================
// 2. SIMPLE WRAPPER METHODS
// ================================================================
// These are the "building blocks" a beginner TUI is made of.
// Everything below just prints raw text -- the escape codes are
// what make the terminal treat that text specially.
/** Clears the whole screen and puts the cursor back at (1,1). */
static void clearScreen() {
System.out.print(CLEAR_SCREEN);
System.out.print(CURSOR_HOME);
System.out.flush();
}
/**
* Moves the cursor to a given row/column. Terminals are 1-indexed,
* with (1,1) being the top-left corner of the screen.
*/
static void gotoXY(int row, int col) {
System.out.print(ESC + row + ";" + col + "H");
}
/** Prints plain text starting at a specific row/column. */
static void printAt(int row, int col, String text) {
gotoXY(row, col);
System.out.print(text);
}
/** Prints colored text starting at a specific row/column. */
static void printAt(int row, int col, String color, String text) {
gotoXY(row, col);
System.out.print(color + text + RESET);
}
/** Draws a simple rectangular box made of +, -, and | characters. */
static void drawBox(int row, int col, int width, int height) {
printAt(row, col, "+" + "-".repeat(width - 2) + "+");
for (int r = 1; r < height - 1; r++) {
printAt(row + r, col, "|");
printAt(row + r, col + width - 1, "|");
}
printAt(row + height - 1, col, "+" + "-".repeat(width - 2) + "+");
}
/** Waits for the user to press Enter before continuing. */
static void pause(Scanner scanner, int row, int col) {
printAt(row, col, FG_WHITE, "Press ENTER to continue...");
scanner.nextLine();
}
// ================================================================
// 3. FAKE PRODUCT DATA
// ================================================================
// A tiny "database" of products, grouped into 4 categories.
static class Product {
String name;
double price;
int stockCount;
Product(String name, double price, int stockCount) {
this.name = name;
this.price = price;
this.stockCount = stockCount;
}
}
static final String[] CATEGORY_NAMES = {
"Keyboards", "Mice", "Monitors", "Power Strips"
};
static final Product[][] CATALOG = {
{ // Keyboards
new Product("ClickyClack Mechanical 87-key", 59.99, 14),
new Product("QuietType Membrane Standard", 19.99, 42),
new Product("WirelessGlide Compact", 34.50, 8),
},
{ // Mice
new Product("PixelPoint Wired Optical", 9.99, 61),
new Product("SmoothGlide Wireless", 24.99, 27),
new Product("ErgoGrip Vertical Mouse", 32.00, 5),
},
{ // Monitors
new Product("ViewMax 24in 1080p", 129.00, 11),
new Product("UltraWide 34in Curved", 379.99, 3),
new Product("BudgetView 21in", 89.50, 19),
},
{ // Power Strips
new Product("SurgeGuard 6-Outlet", 14.99, 33),
new Product("TravelStrip 3-Outlet + USB", 12.50, 20),
new Product("HeavyDuty 8-Outlet Metal", 27.75, 9),
},
};
// ================================================================
// 4. SCREENS ("pages" of the app)
// ================================================================
/** Screen 1: Welcome / splash screen. */
static void showWelcomeScreen(Scanner scanner) {
clearScreen();
drawBox(2, 5, 60, 7);
printAt(4, 10, BOLD + FG_CYAN, "WELCOME TO THE ANSI TERMINAL SHOP");
printAt(6, 10, FG_WHITE, "A tiny demo of ANSI escape codes in Java.");
printAt(7, 10, FG_WHITE, "No libraries. Just escape sequences + println.");
pause(scanner, 11, 5);
}
/** Screen 2: Main menu -- pick a product category. */
static int showMainMenu(Scanner scanner) {
clearScreen();
drawBox(1, 3, 50, CATEGORY_NAMES.length + 6);
printAt(2, 6, BOLD + FG_YELLOW, "MAIN MENU - Product Categories");
printAt(3, 6, FG_WHITE, "------------------------------");
for (int i = 0; i < CATEGORY_NAMES.length; i++) {
printAt(4 + i, 6, FG_GREEN, "[" + (i + 1) + "] " + CATEGORY_NAMES[i]);
}
int quitLine = 4 + CATEGORY_NAMES.length;
printAt(quitLine, 6, FG_RED, "[0] Quit");
printAt(quitLine + 2, 6, FG_WHITE, "Choose an option: ");
String input = scanner.nextLine().trim();
try {
int choice = Integer.parseInt(input);
if (choice >= 0 && choice <= CATEGORY_NAMES.length) {
return choice;
}
} catch (NumberFormatException ignored) {
// fall through to invalid choice handling below
}
printAt(quitLine + 3, 6, FG_RED, "Invalid choice: " + input);
pause(scanner, quitLine + 5, 6);
return -1; // signal "redraw menu"
}
/** Screen 3: Product list for a chosen category. */
static void showCategoryScreen(Scanner scanner, int categoryIndex) {
boolean stayOnScreen = true;
while (stayOnScreen) {
Product[] products = CATALOG[categoryIndex];
clearScreen();
drawBox(1, 3, 66, products.length + 7);
printAt(2, 6, BOLD + FG_CYAN, "CATEGORY: " + CATEGORY_NAMES[categoryIndex]);
printAt(3, 6, FG_WHITE, "----------------------------------------");
printAt(4, 6, REVERSE, " # Product Price Stock ");
for (int i = 0; i < products.length; i++) {
Product p = products[i];
String line = String.format("[%d] %-30s $%-7.2f %3d",
i + 1, p.name, p.price, p.stockCount);
printAt(5 + i, 6, FG_WHITE, line);
}
int backLine = 5 + products.length;
printAt(backLine, 6, FG_YELLOW, "[0] Back to Main Menu");
printAt(backLine + 2, 6, FG_WHITE, "View which product? ");
String input = scanner.nextLine().trim();
if (input.equals("0")) {
stayOnScreen = false;
continue;
}
try {
int choice = Integer.parseInt(input);
if (choice >= 1 && choice <= products.length) {
showProductDetailScreen(scanner, products[choice - 1]);
} else {
printAt(backLine + 3, 6, FG_RED, "No such product number.");
pause(scanner, backLine + 5, 6);
}
} catch (NumberFormatException e) {
printAt(backLine + 3, 6, FG_RED, "Please enter a number.");
pause(scanner, backLine + 5, 6);
}
}
}
/** Screen 4: Detail view for a single product. */
static void showProductDetailScreen(Scanner scanner, Product p) {
clearScreen();
drawBox(3, 8, 50, 9);
printAt(5, 12, BOLD + FG_MAGENTA, "PRODUCT DETAIL");
printAt(6, 12, FG_WHITE, "Name: " + p.name);
printAt(7, 12, FG_WHITE, "Price: $" + String.format("%.2f", p.price));
printAt(8, 12, FG_WHITE, "Stock: " + p.stockCount + " units available");
String status = p.stockCount == 0 ? "OUT OF STOCK"
: p.stockCount < 10 ? "LOW STOCK"
: "IN STOCK";
String statusColor = p.stockCount == 0 ? FG_RED
: p.stockCount < 10 ? FG_YELLOW
: FG_GREEN;
printAt(9, 12, statusColor, "Status: " + status);
pause(scanner, 11, 8);
}
// ================================================================
// 5. MAIN PROGRAM LOOP
// ================================================================
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Hide the blinking cursor while the app runs, restore it on exit.
System.out.print(HIDE_CURSOR);
showWelcomeScreen(scanner);
boolean running = true;
while (running) {
int choice = showMainMenu(scanner);
if (choice == 0) {
running = false;
} else if (choice >= 1 && choice <= CATEGORY_NAMES.length) {
showCategoryScreen(scanner, choice - 1);
}
// choice == -1 means "invalid input" -- just loop and redraw menu
}
clearScreen();
printAt(1, 1, FG_CYAN, "Thanks for visiting the ANSI Terminal Shop. Goodbye!");
System.out.print(RESET);
System.out.print(SHOW_CURSOR);
System.out.println();
scanner.close();
}
}
| 1 | import java.util.Scanner; |
| 2 | |
| 3 | /** |
| 4 | * AnsiShopTUI |
| 5 | * ------------------------------------------------------------------ |
| 6 | * A VERY simple demonstration of how ANSI terminal escape codes can be |
| 7 | * used to build a text user interface (TUI), without any external |
| 8 | * libraries (no Lanterna, no ncurses, no JLine -- just plain old |
| 9 | * escape sequences printed to System.out). |
| 10 | * |
| 11 | * The idea: your terminal understands special sequences of characters |
| 12 | * that start with the "ESC" character (ASCII 27) followed by "[". |
| 13 | * These sequences tell the terminal to do things like clear the |
| 14 | * screen, move the cursor to a specific row/column, or change text |
| 15 | * color. We define those sequences as simple String constants below, |
| 16 | * then wrap them in tiny helper methods (clearScreen, gotoXY, |
| 17 | * printAt, etc.) so the rest of the program never has to deal with |
| 18 | * raw escape codes directly. |
| 19 | * |
| 20 | * This is a "product lookup" toy app for a fake store that sells |
| 21 | * keyboards, mice, monitors, and power strips. It has several |
| 22 | * screens (Welcome -> Main Menu -> Category List -> Product Detail) |
| 23 | * so you can see how a TUI moves the user between different "pages" |
| 24 | * just by clearing the screen and redrawing. |
| 25 | * |
| 26 | * To run: |
| 27 | * javac AnsiShopTUI.java |
| 28 | * java AnsiShopTUI |
| 29 | * |
| 30 | * NOTE: This works in a real terminal (Terminal.app, iTerm, gnome |
| 31 | * terminal, Windows Terminal, etc). It will NOT render correctly |
| 32 | * inside some IDE "Run" consoles that don't support ANSI codes. |
| 33 | * ------------------------------------------------------------------ |
| 34 | */ |
| 35 | public class AnsiShopTUI { |
| 36 | |
| 37 | // ================================================================ |
| 38 | // 1. RAW ANSI ESCAPE CODES (as constants) |
| 39 | // ================================================================ |
| 40 | // Every ANSI escape sequence starts with this "Control Sequence |
| 41 | // Introducer" (CSI): ESC + "[". is the escape character. |
| 42 | static final String ESC = "["; |
| 43 | |
| 44 | // --- Screen / cursor control --- |
| 45 | static final String CLEAR_SCREEN = ESC + "2J"; // erase entire screen |
| 46 | static final String CURSOR_HOME = ESC + "H"; // move cursor to row 1, col 1 |
| 47 | static final String HIDE_CURSOR = ESC + "?25l"; // hide the blinking cursor |
| 48 | static final String SHOW_CURSOR = ESC + "?25h"; // show the blinking cursor |
| 49 | static final String CLEAR_LINE = ESC + "2K"; // erase the current line |
| 50 | |
| 51 | // --- Text style / color --- |
| 52 | static final String RESET = ESC + "0m"; // reset all colors/styles to default |
| 53 | static final String BOLD = ESC + "1m"; |
| 54 | static final String REVERSE = ESC + "7m"; // swap foreground/background (highlight) |
| 55 | |
| 56 | static final String FG_RED = ESC + "31m"; |
| 57 | static final String FG_GREEN = ESC + "32m"; |
| 58 | static final String FG_YELLOW = ESC + "33m"; |
| 59 | static final String FG_BLUE = ESC + "34m"; |
| 60 | static final String FG_MAGENTA = ESC + "35m"; |
| 61 | static final String FG_CYAN = ESC + "36m"; |
| 62 | static final String FG_WHITE = ESC + "37m"; |
| 63 | |
| 64 | static final String BG_BLUE = ESC + "44m"; |
| 65 | |
| 66 | // ================================================================ |
| 67 | // 2. SIMPLE WRAPPER METHODS |
| 68 | // ================================================================ |
| 69 | // These are the "building blocks" a beginner TUI is made of. |
| 70 | // Everything below just prints raw text -- the escape codes are |
| 71 | // what make the terminal treat that text specially. |
| 72 | |
| 73 | /** Clears the whole screen and puts the cursor back at (1,1). */ |
| 74 | static void clearScreen() { |
| 75 | System.out.print(CLEAR_SCREEN); |
| 76 | System.out.print(CURSOR_HOME); |
| 77 | System.out.flush(); |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * Moves the cursor to a given row/column. Terminals are 1-indexed, |
| 82 | * with (1,1) being the top-left corner of the screen. |
| 83 | */ |
| 84 | static void gotoXY(int row, int col) { |
| 85 | System.out.print(ESC + row + ";" + col + "H"); |
| 86 | } |
| 87 | |
| 88 | /** Prints plain text starting at a specific row/column. */ |
| 89 | static void printAt(int row, int col, String text) { |
| 90 | gotoXY(row, col); |
| 91 | System.out.print(text); |
| 92 | } |
| 93 | |
| 94 | /** Prints colored text starting at a specific row/column. */ |
| 95 | static void printAt(int row, int col, String color, String text) { |
| 96 | gotoXY(row, col); |
| 97 | System.out.print(color + text + RESET); |
| 98 | } |
| 99 | |
| 100 | /** Draws a simple rectangular box made of +, -, and | characters. */ |
| 101 | static void drawBox(int row, int col, int width, int height) { |
| 102 | printAt(row, col, "+" + "-".repeat(width - 2) + "+"); |
| 103 | for (int r = 1; r < height - 1; r++) { |
| 104 | printAt(row + r, col, "|"); |
| 105 | printAt(row + r, col + width - 1, "|"); |
| 106 | } |
| 107 | printAt(row + height - 1, col, "+" + "-".repeat(width - 2) + "+"); |
| 108 | } |
| 109 | |
| 110 | /** Waits for the user to press Enter before continuing. */ |
| 111 | static void pause(Scanner scanner, int row, int col) { |
| 112 | printAt(row, col, FG_WHITE, "Press ENTER to continue..."); |
| 113 | scanner.nextLine(); |
| 114 | } |
| 115 | |
| 116 | // ================================================================ |
| 117 | // 3. FAKE PRODUCT DATA |
| 118 | // ================================================================ |
| 119 | // A tiny "database" of products, grouped into 4 categories. |
| 120 | static class Product { |
| 121 | String name; |
| 122 | double price; |
| 123 | int stockCount; |
| 124 | |
| 125 | Product(String name, double price, int stockCount) { |
| 126 | this.name = name; |
| 127 | this.price = price; |
| 128 | this.stockCount = stockCount; |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | static final String[] CATEGORY_NAMES = { |
| 133 | "Keyboards", "Mice", "Monitors", "Power Strips" |
| 134 | }; |
| 135 | |
| 136 | static final Product[][] CATALOG = { |
| 137 | { // Keyboards |
| 138 | new Product("ClickyClack Mechanical 87-key", 59.99, 14), |
| 139 | new Product("QuietType Membrane Standard", 19.99, 42), |
| 140 | new Product("WirelessGlide Compact", 34.50, 8), |
| 141 | }, |
| 142 | { // Mice |
| 143 | new Product("PixelPoint Wired Optical", 9.99, 61), |
| 144 | new Product("SmoothGlide Wireless", 24.99, 27), |
| 145 | new Product("ErgoGrip Vertical Mouse", 32.00, 5), |
| 146 | }, |
| 147 | { // Monitors |
| 148 | new Product("ViewMax 24in 1080p", 129.00, 11), |
| 149 | new Product("UltraWide 34in Curved", 379.99, 3), |
| 150 | new Product("BudgetView 21in", 89.50, 19), |
| 151 | }, |
| 152 | { // Power Strips |
| 153 | new Product("SurgeGuard 6-Outlet", 14.99, 33), |
| 154 | new Product("TravelStrip 3-Outlet + USB", 12.50, 20), |
| 155 | new Product("HeavyDuty 8-Outlet Metal", 27.75, 9), |
| 156 | }, |
| 157 | }; |
| 158 | |
| 159 | // ================================================================ |
| 160 | // 4. SCREENS ("pages" of the app) |
| 161 | // ================================================================ |
| 162 | |
| 163 | /** Screen 1: Welcome / splash screen. */ |
| 164 | static void showWelcomeScreen(Scanner scanner) { |
| 165 | clearScreen(); |
| 166 | drawBox(2, 5, 60, 7); |
| 167 | printAt(4, 10, BOLD + FG_CYAN, "WELCOME TO THE ANSI TERMINAL SHOP"); |
| 168 | printAt(6, 10, FG_WHITE, "A tiny demo of ANSI escape codes in Java."); |
| 169 | printAt(7, 10, FG_WHITE, "No libraries. Just escape sequences + println."); |
| 170 | pause(scanner, 11, 5); |
| 171 | } |
| 172 | |
| 173 | /** Screen 2: Main menu -- pick a product category. */ |
| 174 | static int showMainMenu(Scanner scanner) { |
| 175 | clearScreen(); |
| 176 | drawBox(1, 3, 50, CATEGORY_NAMES.length + 6); |
| 177 | printAt(2, 6, BOLD + FG_YELLOW, "MAIN MENU - Product Categories"); |
| 178 | printAt(3, 6, FG_WHITE, "------------------------------"); |
| 179 | |
| 180 | for (int i = 0; i < CATEGORY_NAMES.length; i++) { |
| 181 | printAt(4 + i, 6, FG_GREEN, "[" + (i + 1) + "] " + CATEGORY_NAMES[i]); |
| 182 | } |
| 183 | int quitLine = 4 + CATEGORY_NAMES.length; |
| 184 | printAt(quitLine, 6, FG_RED, "[0] Quit"); |
| 185 | |
| 186 | printAt(quitLine + 2, 6, FG_WHITE, "Choose an option: "); |
| 187 | String input = scanner.nextLine().trim(); |
| 188 | |
| 189 | try { |
| 190 | int choice = Integer.parseInt(input); |
| 191 | if (choice >= 0 && choice <= CATEGORY_NAMES.length) { |
| 192 | return choice; |
| 193 | } |
| 194 | } catch (NumberFormatException ignored) { |
| 195 | // fall through to invalid choice handling below |
| 196 | } |
| 197 | |
| 198 | printAt(quitLine + 3, 6, FG_RED, "Invalid choice: " + input); |
| 199 | pause(scanner, quitLine + 5, 6); |
| 200 | return -1; // signal "redraw menu" |
| 201 | } |
| 202 | |
| 203 | /** Screen 3: Product list for a chosen category. */ |
| 204 | static void showCategoryScreen(Scanner scanner, int categoryIndex) { |
| 205 | boolean stayOnScreen = true; |
| 206 | while (stayOnScreen) { |
| 207 | Product[] products = CATALOG[categoryIndex]; |
| 208 | clearScreen(); |
| 209 | drawBox(1, 3, 66, products.length + 7); |
| 210 | printAt(2, 6, BOLD + FG_CYAN, "CATEGORY: " + CATEGORY_NAMES[categoryIndex]); |
| 211 | printAt(3, 6, FG_WHITE, "----------------------------------------"); |
| 212 | printAt(4, 6, REVERSE, " # Product Price Stock "); |
| 213 | |
| 214 | for (int i = 0; i < products.length; i++) { |
| 215 | Product p = products[i]; |
| 216 | String line = String.format("[%d] %-30s $%-7.2f %3d", |
| 217 | i + 1, p.name, p.price, p.stockCount); |
| 218 | printAt(5 + i, 6, FG_WHITE, line); |
| 219 | } |
| 220 | |
| 221 | int backLine = 5 + products.length; |
| 222 | printAt(backLine, 6, FG_YELLOW, "[0] Back to Main Menu"); |
| 223 | printAt(backLine + 2, 6, FG_WHITE, "View which product? "); |
| 224 | String input = scanner.nextLine().trim(); |
| 225 | |
| 226 | if (input.equals("0")) { |
| 227 | stayOnScreen = false; |
| 228 | continue; |
| 229 | } |
| 230 | |
| 231 | try { |
| 232 | int choice = Integer.parseInt(input); |
| 233 | if (choice >= 1 && choice <= products.length) { |
| 234 | showProductDetailScreen(scanner, products[choice - 1]); |
| 235 | } else { |
| 236 | printAt(backLine + 3, 6, FG_RED, "No such product number."); |
| 237 | pause(scanner, backLine + 5, 6); |
| 238 | } |
| 239 | } catch (NumberFormatException e) { |
| 240 | printAt(backLine + 3, 6, FG_RED, "Please enter a number."); |
| 241 | pause(scanner, backLine + 5, 6); |
| 242 | } |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | /** Screen 4: Detail view for a single product. */ |
| 247 | static void showProductDetailScreen(Scanner scanner, Product p) { |
| 248 | clearScreen(); |
| 249 | drawBox(3, 8, 50, 9); |
| 250 | printAt(5, 12, BOLD + FG_MAGENTA, "PRODUCT DETAIL"); |
| 251 | printAt(6, 12, FG_WHITE, "Name: " + p.name); |
| 252 | printAt(7, 12, FG_WHITE, "Price: $" + String.format("%.2f", p.price)); |
| 253 | printAt(8, 12, FG_WHITE, "Stock: " + p.stockCount + " units available"); |
| 254 | |
| 255 | String status = p.stockCount == 0 ? "OUT OF STOCK" |
| 256 | : p.stockCount < 10 ? "LOW STOCK" |
| 257 | : "IN STOCK"; |
| 258 | String statusColor = p.stockCount == 0 ? FG_RED |
| 259 | : p.stockCount < 10 ? FG_YELLOW |
| 260 | : FG_GREEN; |
| 261 | printAt(9, 12, statusColor, "Status: " + status); |
| 262 | |
| 263 | pause(scanner, 11, 8); |
| 264 | } |
| 265 | |
| 266 | // ================================================================ |
| 267 | // 5. MAIN PROGRAM LOOP |
| 268 | // ================================================================ |
| 269 | public static void main(String[] args) { |
| 270 | Scanner scanner = new Scanner(System.in); |
| 271 | |
| 272 | // Hide the blinking cursor while the app runs, restore it on exit. |
| 273 | System.out.print(HIDE_CURSOR); |
| 274 | |
| 275 | showWelcomeScreen(scanner); |
| 276 | |
| 277 | boolean running = true; |
| 278 | while (running) { |
| 279 | int choice = showMainMenu(scanner); |
| 280 | |
| 281 | if (choice == 0) { |
| 282 | running = false; |
| 283 | } else if (choice >= 1 && choice <= CATEGORY_NAMES.length) { |
| 284 | showCategoryScreen(scanner, choice - 1); |
| 285 | } |
| 286 | // choice == -1 means "invalid input" -- just loop and redraw menu |
| 287 | } |
| 288 | |
| 289 | clearScreen(); |
| 290 | printAt(1, 1, FG_CYAN, "Thanks for visiting the ANSI Terminal Shop. Goodbye!"); |
| 291 | System.out.print(RESET); |
| 292 | System.out.print(SHOW_CURSOR); |
| 293 | System.out.println(); |
| 294 | scanner.close(); |
| 295 | } |
| 296 | } |
| 297 |