feat(terminal): add two-row floating keyboard buttons for common commands (mobile only) (#13876)

* feat(terminal): add two-row floating keyboard buttons for common commands (mobile only)

* Fix missing newline at end of pl.rs

Add missing newline at the end of the file.
This commit is contained in:
alonginwind
2025-12-28 15:41:25 +08:00
committed by GitHub
parent 969ea28d06
commit 3384eda8b7
51 changed files with 247 additions and 30 deletions

View File

@@ -163,6 +163,7 @@ const String kOptionShowVirtualMouse = "show-virtual-mouse";
const String kOptionVirtualMouseScale = "virtual-mouse-scale";
const String kOptionShowVirtualJoystick = "show-virtual-joystick";
const String kOptionAllowAskForNoteAtEndOfConnection = "allow-ask-for-note";
const String kOptionEnableShowTerminalExtraKeys = "enable-show-terminal-extra-keys";
// network options
const String kOptionAllowWebSocket = "allow-websocket";

View File

@@ -71,6 +71,7 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
var _ignoreBatteryOpt = false;
var _enableStartOnBoot = false;
var _checkUpdateOnStartup = false;
var _showTerminalExtraKeys = false;
var _floatingWindowDisabled = false;
var _keepScreenOn = KeepScreenOn.duringControlled; // relay on floating window
var _enableAbr = false;
@@ -139,6 +140,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
_enableIpv6Punch = mainGetLocalBoolOptionSync(kOptionEnableIpv6Punch);
_allowAskForNoteAtEndOfConnection =
mainGetLocalBoolOptionSync(kOptionAllowAskForNoteAtEndOfConnection);
_showTerminalExtraKeys =
mainGetLocalBoolOptionSync(kOptionEnableShowTerminalExtraKeys);
}
@override
@@ -602,6 +605,23 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
);
}
enhancementsTiles.add(
SettingsTile.switchTile(
initialValue: _showTerminalExtraKeys,
title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Text(translate('Show terminal extra keys')),
]),
onToggle: (bool v) async {
await mainSetLocalBoolOption(kOptionEnableShowTerminalExtraKeys, v);
final newValue =
mainGetLocalBoolOptionSync(kOptionEnableShowTerminalExtraKeys);
setState(() {
_showTerminalExtraKeys = newValue;
});
},
),
);
onFloatingWindowChanged(bool toValue) async {
if (toValue) {
if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {

View File

@@ -9,6 +9,7 @@ import 'package:flutter_hbb/models/terminal_model.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:xterm/xterm.dart';
import '../../desktop/pages/terminal_connection_manager.dart';
import '../../consts.dart';
class TerminalPage extends StatefulWidget {
const TerminalPage({
@@ -37,6 +38,9 @@ class _TerminalPageState extends State<TerminalPage>
double? _cellHeight;
double _sysKeyboardHeight = 0;
Timer? _keyboardDebounce;
final GlobalKey _keyboardKey = GlobalKey();
double _keyboardHeight = 0;
late bool _showTerminalExtraKeys;
// For web only.
// 'monospace' does not work on web, use Google Fonts, `??` is only for null safety.
@@ -75,10 +79,15 @@ class _TerminalPageState extends State<TerminalPage>
// Register this terminal model with FFI for event routing
_ffi.registerTerminalModel(widget.terminalId, _terminalModel);
_showTerminalExtraKeys = mainGetLocalBoolOptionSync(kOptionEnableShowTerminalExtraKeys);
// Initialize terminal connection
WidgetsBinding.instance.addPostFrameCallback((_) {
_ffi.dialogManager
.showLoading(translate('Connecting...'), onCancel: closeConnection);
if (_showTerminalExtraKeys) {
_updateKeyboardHeight();
}
});
_ffi.ffiModel.updateEventListener(_ffi.sessionId, widget.id);
}
@@ -107,15 +116,22 @@ class _TerminalPageState extends State<TerminalPage>
});
}
void _updateKeyboardHeight() {
if (_keyboardKey.currentContext != null) {
final renderBox = _keyboardKey.currentContext!.findRenderObject() as RenderBox;
_keyboardHeight = renderBox.size.height;
}
}
EdgeInsets _calculatePadding(double heightPx) {
if (_cellHeight == null) {
return const EdgeInsets.symmetric(horizontal: 5.0, vertical: 2.0);
}
final realHeight = heightPx - _sysKeyboardHeight;
final realHeight = heightPx - _sysKeyboardHeight - _keyboardHeight;
final rows = (realHeight / _cellHeight!).floor();
final extraSpace = realHeight - rows * _cellHeight!;
final topBottom = max(0.0, extraSpace / 2.0);
return EdgeInsets.only(left: 5.0, right: 5.0, top: topBottom, bottom: topBottom + _sysKeyboardHeight);
return EdgeInsets.only(left: 5.0, right: 5.0, top: topBottom, bottom: topBottom + _sysKeyboardHeight + _keyboardHeight);
}
@override
@@ -134,7 +150,10 @@ class _TerminalPageState extends State<TerminalPage>
return Scaffold(
resizeToAvoidBottomInset: false, // Disable automatic layout adjustment; manually control UI updates to prevent flickering when the keyboard shows/hides
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
body: SafeArea(
body: Stack(
children: [
Positioned.fill(
child: SafeArea(
top: true,
child: LayoutBuilder(
builder: (context, constraints) {
@@ -164,9 +183,135 @@ class _TerminalPageState extends State<TerminalPage>
},
),
),
),
if (_showTerminalExtraKeys) _buildFloatingKeyboard(),
],
),
);
}
Widget _buildFloatingKeyboard() {
return AnimatedPositioned(
duration: const Duration(milliseconds: 200),
left: 0,
right: 0,
bottom: _sysKeyboardHeight,
child: Container(
key: _keyboardKey,
color: Theme.of(context).scaffoldBackgroundColor,
padding: EdgeInsets.zero,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildKeyButton('Esc'),
const SizedBox(width: 2),
_buildKeyButton('/'),
const SizedBox(width: 2),
_buildKeyButton('|'),
const SizedBox(width: 2),
_buildKeyButton('Home'),
const SizedBox(width: 2),
_buildKeyButton(''),
const SizedBox(width: 2),
_buildKeyButton('End'),
const SizedBox(width: 2),
_buildKeyButton('PgUp'),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildKeyButton('Tab'),
const SizedBox(width: 2),
_buildKeyButton('Ctrl+C'),
const SizedBox(width: 2),
_buildKeyButton('~'),
const SizedBox(width: 2),
_buildKeyButton(''),
const SizedBox(width: 2),
_buildKeyButton(''),
const SizedBox(width: 2),
_buildKeyButton(''),
const SizedBox(width: 2),
_buildKeyButton('PgDn'),
],
),
],
),
),
);
}
Widget _buildKeyButton(String label) {
return ElevatedButton(
onPressed: () {
_sendKeyToTerminal(label);
},
child: Text(label),
style: ElevatedButton.styleFrom(
minimumSize: const Size(48, 32),
padding: EdgeInsets.zero,
textStyle: const TextStyle(fontSize: 12),
backgroundColor: Theme.of(context).colorScheme.surfaceVariant,
foregroundColor: Theme.of(context).colorScheme.onSurfaceVariant,
),
);
}
void _sendKeyToTerminal(String key) {
String? send;
switch (key) {
case 'Esc':
send = '\x1B';
break;
case 'Tab':
send = '\t';
break;
case 'Ctrl+C':
send = '\x03';
break;
case '':
send = '\x1B[A';
break;
case '':
send = '\x1B[B';
break;
case '':
send = '\x1B[C';
break;
case '':
send = '\x1B[D';
break;
case 'Home':
send = '\x1B[H';
break;
case 'End':
send = '\x1B[F';
break;
case 'PgUp':
send = '\x1B[5~';
break;
case 'PgDn':
send = '\x1B[6~';
break;
default:
send = key;
break;
}
if (send != null) {
_terminalModel.sendVirtualKey(send);
}
}
// https://github.com/TerminalStudio/xterm.dart/issues/42#issuecomment-877495472
// https://github.com/TerminalStudio/xterm.dart/issues/198#issuecomment-2526548458
TerminalStyle _getTerminalStyle() {

View File

@@ -146,6 +146,10 @@ class TerminalModel with ChangeNotifier {
}
}
Future<void> sendVirtualKey(String data) async {
return _handleInput(data);
}
Future<void> closeTerminal() async {
if (_terminalOpened) {
try {

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "هذه الميزة غير مدعومة من قبل خادمك"),
("input note here", "أدخل الملاحظة هنا"),
("note-at-conn-end-tip", "سيتم عرض هذه الملاحظة عند نهاية الاتصال"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "注意RustDesk 开源服务器 (OSS server) 不包含此功能。"),
("input note here", "输入备注"),
("note-at-conn-end-tip", "在连接结束时请求备注"),
("Show terminal extra keys", "显示终端扩展键"),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "HINWEIS: RustDesk Server OSS enthält diese Funktion nicht."),
("input note here", "Hier eine Notiz eingeben"),
("note-at-conn-end-tip", "Am Ende der Verbindung um eine Notiz bitten."),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "توجه: سرور RustDesk OSS این ویژگی را ندارد."),
("input note here", "یادداشت را اینجا وارد کنید"),
("note-at-conn-end-tip", "در پایان اتصال، یادداشت بخواهید"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "Note : Cette fonctionnalité nest pas disponible sous la version open-source du serveur RustDesk."),
("input note here", "saisir la note ici"),
("note-at-conn-end-tip", "Proposer décrire une note une fois la connexion terminée"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "MEGJEGYZÉS: Az OSS RustDesk kiszolgáló nem támogatja ezt a funkciót."),
("input note here", "Megjegyzés bevitele"),
("note-at-conn-end-tip", "Megjegyzés a kapcsolat végén"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "NOTA: il sistema operativo del server RustDesk non include questa funzionalità."),
("input note here", "Inserisci nota qui"),
("note-at-conn-end-tip", "Visualizza nota alla fine della connessione"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "참고: RustDesk 서버 OSS에는 이 기능이 포함되어 있지 않습니다."),
("input note here", "여기에 노트 입력"),
("note-at-conn-end-tip", "연결이 끝날 때 메모 요청"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "Opmerking: Deze functie is niet beschikbaar in de open-sourceversie van de RustDesk-server."),
("input note here", "voeg hier een opmerking toe"),
("note-at-conn-end-tip", "Vraag om een opmerking aan het einde van de verbinding"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "UWAGA: Serwer OSS RustDesk nie obsługuje tej funkcji."),
("input note here", "Wstaw tutaj notatkę"),
("note-at-conn-end-tip", "Poproś o notatkę po zakończeniu połączenia."),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "ПРИМЕЧАНИЕ: в OSS-сервере RustDesk эта функция отсутствует."),
("input note here", "введите заметку"),
("note-at-conn-end-tip", "Запрашивать заметку в конце соединения"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "NOT: RustDesk sunucu OSS'si bu özelliği içermemektedir."),
("input note here", "Notu buraya girin"),
("note-at-conn-end-tip", "Bağlantı bittiğinde not sorulsun"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", "注意RustDesk 開源伺服器 (OSS server) 不包含此功能。"),
("input note here", "輸入備註"),
("note-at-conn-end-tip", "在連接結束時請求備註"),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}

View File

@@ -729,5 +729,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("server-oss-not-support-tip", ""),
("input note here", ""),
("note-at-conn-end-tip", ""),
("Show terminal extra keys", ""),
].iter().cloned().collect();
}