fix(TUI): proper arrow key handling — parse ESC sequences instead of treating as Esc

This commit is contained in:
SashegDev
2026-05-04 20:39:29 +00:00
parent 2cdc438411
commit efc4b086d1
4 changed files with 37 additions and 13 deletions
+1 -3
View File
@@ -52,12 +52,10 @@
Лаунчер использует **текстовый интерфейс (TUI)**: Лаунчер использует **текстовый интерфейс (TUI)**:
- `W` / `S` (или `Ц` / `Ы`) — перемещение по меню - `W` / `S` (или `Ц` / `Ы`) или `↑` / `↓` — перемещение по меню
- `ENTER` — выбор пункта - `ENTER` — выбор пункта
- `ESC` или пункт «Назад» — возврат назад - `ESC` или пункт «Назад» — возврат назад
> **Важно:** Стрелки ↑/↓ могут вызывать баги и краши. Используйте только `W`/`S`.
Если вы случайно кликнули мышкой в окне лаунчера и он «заморозился» — просто нажмите **любую клавишу** на клавиатуре. Если вы случайно кликнули мышкой в окне лаунчера и он «заморозился» — просто нажмите **любую клавишу** на клавиатуре.
### Расположение сборок ### Расположение сборок
+5
View File
@@ -45,6 +45,11 @@
<artifactId>jansi</artifactId> <artifactId>jansi</artifactId>
<version>2.4.1</version> <version>2.4.1</version>
</dependency> </dependency>
<dependency>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>3.24.1</version>
</dependency>
<dependency> <dependency>
<groupId>me.tongfei</groupId> <groupId>me.tongfei</groupId>
<artifactId>progressbar</artifactId> <artifactId>progressbar</artifactId>
@@ -149,7 +149,6 @@ public class LoginMenu {
* если недоступно (IDE/терминал без TTY) — читаем обычным способом. * если недоступно (IDE/терминал без TTY) — читаем обычным способом.
*/ */
private String readPassword(String prompt) throws IOException { private String readPassword(String prompt) throws IOException {
// Создаём временный терминал для ввода пароля
org.jline.terminal.Terminal passTerminal = org.jline.terminal.TerminalBuilder.builder() org.jline.terminal.Terminal passTerminal = org.jline.terminal.TerminalBuilder.builder()
.system(true) .system(true)
.jna(true) .jna(true)
@@ -165,6 +164,15 @@ public class LoginMenu {
while (true) { while (true) {
int key = passTerminal.reader().read(); 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 if (key == 13 || key == 10) { // Enter
passTerminal.writer().println(); passTerminal.writer().println();
break; break;
@@ -177,7 +185,7 @@ public class LoginMenu {
} else if (key == 3) { // Ctrl+C } else if (key == 3) { // Ctrl+C
passTerminal.writer().println(); passTerminal.writer().println();
System.exit(0); System.exit(0);
} else if (key >= 32 && key < 127) { // Печатные символы } else if (key >= 32 && key < 127) { // Printable characters
password.append((char) key); password.append((char) key);
passTerminal.writer().print('*'); passTerminal.writer().print('*');
passTerminal.writer().flush(); passTerminal.writer().flush();
@@ -36,17 +36,30 @@ public class ArrowMenu {
printPagedMenu(); printPagedMenu();
int key = terminal.reader().read(); 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(); 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(); selected = (selected + 1) % options.size();
} }
else if (key == 13 || key == 10) { // Enter else if (key == 13 || key == 10) { // Enter
return selected; return selected;
} }
else if (key == 27) { // Esc else if (key == 27) { // Esc or arrow escape seq
return -1; 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 { } finally {
@@ -83,7 +96,7 @@ public class ArrowMenu {
// Подсказка внизу (фиксированная) // Подсказка внизу (фиксированная)
sb.append("\n") sb.append("\n")
.append(ZAnsi.white("W/S (Ц/Ы) - перемещение | Enter - выбрать | Esc - назад")); .append(ZAnsi.white("W/S (Ц/Ы) или ↑/↓ - перемещение | Enter - выбрать | Esc - назад"));
System.out.print(sb); System.out.print(sb);
} }