From efc4b086d106573d4ff594d82d472276c177db55 Mon Sep 17 00:00:00 2001 From: SashegDev Date: Mon, 4 May 2026 20:39:29 +0000 Subject: [PATCH] =?UTF-8?q?fix(TUI):=20proper=20arrow=20key=20handling=20?= =?UTF-8?q?=E2=80=94=20parse=20ESC=20sequences=20instead=20of=20treating?= =?UTF-8?q?=20as=20Esc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +-- launcher/pom.xml | 5 ++++ .../zernmc/launcher/menu/LoginMenu.java | 12 ++++++-- .../zernmc/launcher/ui/ArrowMenu.java | 29 ++++++++++++++----- 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f6b4c4c..6eca6a7 100644 --- a/README.md +++ b/README.md @@ -52,12 +52,10 @@ Лаунчер использует **текстовый интерфейс (TUI)**: -- `W` / `S` (или `Ц` / `Ы`) — перемещение по меню +- `W` / `S` (или `Ц` / `Ы`) или `↑` / `↓` — перемещение по меню - `ENTER` — выбор пункта - `ESC` или пункт «Назад» — возврат назад -> **Важно:** Стрелки ↑/↓ могут вызывать баги и краши. Используйте только `W`/`S`. - Если вы случайно кликнули мышкой в окне лаунчера и он «заморозился» — просто нажмите **любую клавишу** на клавиатуре. ### Расположение сборок diff --git a/launcher/pom.xml b/launcher/pom.xml index 032c307..7551767 100644 --- a/launcher/pom.xml +++ b/launcher/pom.xml @@ -45,6 +45,11 @@ jansi 2.4.1 + + org.jline + jline + 3.24.1 + me.tongfei progressbar diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LoginMenu.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LoginMenu.java index ebdff19..6a106a6 100644 --- a/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LoginMenu.java +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/menu/LoginMenu.java @@ -149,7 +149,6 @@ public class LoginMenu { * если недоступно (IDE/терминал без TTY) — читаем обычным способом. */ private String readPassword(String prompt) throws IOException { - // Создаём временный терминал для ввода пароля org.jline.terminal.Terminal passTerminal = org.jline.terminal.TerminalBuilder.builder() .system(true) .jna(true) @@ -165,6 +164,15 @@ public class LoginMenu { while (true) { int key = passTerminal.reader().read(); + if (key == 27) { + // Escape sequence — consume remaining bytes (arrow keys, etc.) + int next = passTerminal.reader().read(50); + if (next == 91) { // '[' — arrow key sequence + passTerminal.reader().read(50); // consume 'A'/'B'/'C'/'D' + } + continue; + } + if (key == 13 || key == 10) { // Enter passTerminal.writer().println(); break; @@ -177,7 +185,7 @@ public class LoginMenu { } else if (key == 3) { // Ctrl+C passTerminal.writer().println(); System.exit(0); - } else if (key >= 32 && key < 127) { // Печатные символы + } else if (key >= 32 && key < 127) { // Printable characters password.append((char) key); passTerminal.writer().print('*'); passTerminal.writer().flush(); diff --git a/launcher/src/main/java/me/sashegdev/zernmc/launcher/ui/ArrowMenu.java b/launcher/src/main/java/me/sashegdev/zernmc/launcher/ui/ArrowMenu.java index 2d948b0..1a1720c 100644 --- a/launcher/src/main/java/me/sashegdev/zernmc/launcher/ui/ArrowMenu.java +++ b/launcher/src/main/java/me/sashegdev/zernmc/launcher/ui/ArrowMenu.java @@ -36,17 +36,30 @@ public class ArrowMenu { printPagedMenu(); int key = terminal.reader().read(); - if (key == 'w' || key == 'W' || key == 'ц' || key == 'Ц') { // Up + if (key == 'w' || key == 'W' || key == 'ц' || key == 'Ц' + || key == 'k' || key == 'K' || key == 'л' || key == 'Л') { // Up / Arrow Up selected = (selected - 1 + options.size()) % options.size(); - } - else if (key == 's' || key == 'S' || key == 'ы' || key == 'Ы') { // Down + } + else if (key == 's' || key == 'S' || key == 'ы' || key == 'Ы' + || key == 'j' || key == 'J' || key == 'о' || key == 'О') { // Down / Arrow Down selected = (selected + 1) % options.size(); - } + } else if (key == 13 || key == 10) { // Enter return selected; - } - else if (key == 27) { // Esc - return -1; + } + else if (key == 27) { // Esc or arrow escape seq + int next = terminal.reader().read(50); + if (next == 91) { // '[' — start of arrow escape sequence + int arrow = terminal.reader().read(50); + if (arrow == 65) { // 'A' — Up arrow + selected = (selected - 1 + options.size()) % options.size(); + } else if (arrow == 66) { // 'B' — Down arrow + selected = (selected + 1) % options.size(); + } + // else — unknown escape seq, ignore + } else { + return -1; // genuine Esc + } } } } finally { @@ -83,7 +96,7 @@ public class ArrowMenu { // Подсказка внизу (фиксированная) sb.append("\n") - .append(ZAnsi.white("W/S (Ц/Ы) - перемещение | Enter - выбрать | Esc - назад")); + .append(ZAnsi.white("W/S (Ц/Ы) или ↑/↓ - перемещение | Enter - выбрать | Esc - назад")); System.out.print(sb); }