fix(TUI): proper arrow key handling — parse ESC sequences instead of treating as Esc
This commit is contained in:
@@ -52,12 +52,10 @@
|
|||||||
|
|
||||||
Лаунчер использует **текстовый интерфейс (TUI)**:
|
Лаунчер использует **текстовый интерфейс (TUI)**:
|
||||||
|
|
||||||
- `W` / `S` (или `Ц` / `Ы`) — перемещение по меню
|
- `W` / `S` (или `Ц` / `Ы`) или `↑` / `↓` — перемещение по меню
|
||||||
- `ENTER` — выбор пункта
|
- `ENTER` — выбор пункта
|
||||||
- `ESC` или пункт «Назад» — возврат назад
|
- `ESC` или пункт «Назад» — возврат назад
|
||||||
|
|
||||||
> **Важно:** Стрелки ↑/↓ могут вызывать баги и краши. Используйте только `W`/`S`.
|
|
||||||
|
|
||||||
Если вы случайно кликнули мышкой в окне лаунчера и он «заморозился» — просто нажмите **любую клавишу** на клавиатуре.
|
Если вы случайно кликнули мышкой в окне лаунчера и он «заморозился» — просто нажмите **любую клавишу** на клавиатуре.
|
||||||
|
|
||||||
### Расположение сборок
|
### Расположение сборок
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user