#71 new & opt.

- ssh page virt key long press
- custom ssh virt keys
This commit is contained in:
lollipopkit
2023-06-27 00:41:56 +08:00
parent dc63d902e5
commit 0022294ea4
30 changed files with 550 additions and 94 deletions

View File

@@ -300,6 +300,12 @@ abstract class S {
/// **'Delete'** /// **'Delete'**
String get delete; String get delete;
/// No description provided for @disabled.
///
/// In en, this message translates to:
/// **'Disabled'**
String get disabled;
/// No description provided for @disconnected. /// No description provided for @disconnected.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -372,6 +378,12 @@ abstract class S {
/// **'Edit'** /// **'Edit'**
String get edit; String get edit;
/// No description provided for @editVirtKeys.
///
/// In en, this message translates to:
/// **'Edit virtual keys'**
String get editVirtKeys;
/// No description provided for @editor. /// No description provided for @editor.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -106,6 +106,9 @@ class SDe extends S {
@override @override
String get delete => 'Löschen'; String get delete => 'Löschen';
@override
String get disabled => 'Disabled';
@override @override
String get disconnected => 'Disconnected'; String get disconnected => 'Disconnected';
@@ -152,6 +155,9 @@ class SDe extends S {
@override @override
String get edit => 'Bearbeiten'; String get edit => 'Bearbeiten';
@override
String get editVirtKeys => 'Edit virtual keys';
@override @override
String get editor => 'Editor'; String get editor => 'Editor';

View File

@@ -106,6 +106,9 @@ class SEn extends S {
@override @override
String get delete => 'Delete'; String get delete => 'Delete';
@override
String get disabled => 'Disabled';
@override @override
String get disconnected => 'Disconnected'; String get disconnected => 'Disconnected';
@@ -152,6 +155,9 @@ class SEn extends S {
@override @override
String get edit => 'Edit'; String get edit => 'Edit';
@override
String get editVirtKeys => 'Edit virtual keys';
@override @override
String get editor => 'Editor'; String get editor => 'Editor';

View File

@@ -106,6 +106,9 @@ class SZh extends S {
@override @override
String get delete => '删除'; String get delete => '删除';
@override
String get disabled => '已禁用';
@override @override
String get disconnected => '连接断开'; String get disconnected => '连接断开';
@@ -152,6 +155,9 @@ class SZh extends S {
@override @override
String get edit => '编辑'; String get edit => '编辑';
@override
String get editVirtKeys => '编辑虚拟按键';
@override @override
String get editor => '编辑器'; String get editor => '编辑器';
@@ -743,6 +749,9 @@ class SZhTw extends SZh {
@override @override
String get delete => '刪除'; String get delete => '刪除';
@override
String get disabled => '已禁用';
@override @override
String get disconnected => '連接斷開'; String get disconnected => '連接斷開';
@@ -789,6 +798,9 @@ class SZhTw extends SZh {
@override @override
String get edit => '編輯'; String get edit => '編輯';
@override
String get editVirtKeys => '編輯虛擬按鍵';
@override @override
String get editor => '編輯器'; String get editor => '編輯器';

View File

@@ -6,6 +6,8 @@ PODS:
- Flutter (1.0.0) - Flutter (1.0.0)
- flutter_native_splash (0.0.1): - flutter_native_splash (0.0.1):
- Flutter - Flutter
- flutter_volume_controller (0.0.1):
- Flutter
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
- FlutterMacOS - FlutterMacOS
@@ -23,6 +25,7 @@ DEPENDENCIES:
- file_picker (from `.symlinks/plugins/file_picker/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`)
- Flutter (from `Flutter`) - Flutter (from `Flutter`)
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- plain_notification_token (from `.symlinks/plugins/plain_notification_token/ios`) - plain_notification_token (from `.symlinks/plugins/plain_notification_token/ios`)
- r_upgrade (from `.symlinks/plugins/r_upgrade/ios`) - r_upgrade (from `.symlinks/plugins/r_upgrade/ios`)
@@ -38,6 +41,8 @@ EXTERNAL SOURCES:
:path: Flutter :path: Flutter
flutter_native_splash: flutter_native_splash:
:path: ".symlinks/plugins/flutter_native_splash/ios" :path: ".symlinks/plugins/flutter_native_splash/ios"
flutter_volume_controller:
:path: ".symlinks/plugins/flutter_volume_controller/ios"
path_provider_foundation: path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin" :path: ".symlinks/plugins/path_provider_foundation/darwin"
plain_notification_token: plain_notification_token:
@@ -54,6 +59,7 @@ SPEC CHECKSUMS:
file_picker: 1d63c4949e05e386da864365f8c13e1e64787675 file_picker: 1d63c4949e05e386da864365f8c13e1e64787675
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1 plain_notification_token: b36467dc91939a7b6754267c701bbaca14996ee1
r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114 r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114
@@ -62,4 +68,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 7fb15c416f8685fca4966867a8da218ec592ec2e PODFILE CHECKSUM: 7fb15c416f8685fca4966867a8da218ec592ec2e
COCOAPODS: 1.11.1 COCOAPODS: 1.12.1

View File

@@ -1,9 +1,9 @@
import 'package:toolbox/core/persistant_store.dart'; import 'package:toolbox/core/persistant_store.dart';
typedef StringOrder = List<String>; typedef Order<T> = List<T>;
extension StringOrderX on StringOrder { extension OrderX<T> on Order<T> {
void move(int oldIndex, int newIndex, StoreProperty property) { void move(int oldIndex, int newIndex, StoreProperty<List<T>> property) {
if (oldIndex == newIndex) return; if (oldIndex == newIndex) return;
if (oldIndex < newIndex) { if (oldIndex < newIndex) {
newIndex -= 1; newIndex -= 1;
@@ -14,17 +14,17 @@ extension StringOrderX on StringOrder {
property.put(this); property.put(this);
} }
void update(String id, String newId) { void update(T id, T newId) {
final index = indexOf(id); final index = indexOf(id);
if (index == -1) return; if (index == -1) return;
this[index] = newId; this[index] = newId;
} }
int index(String id) { int index(T id) {
return indexOf(id); return indexOf(id);
} }
void moveById(String oid, String nid, StoreProperty property) { void moveById(T oid, T nid, StoreProperty<List<T>> property) {
final index = indexOf(oid); final index = indexOf(oid);
if (index == -1) return; if (index == -1) return;
final newIndex = indexOf(nid); final newIndex = indexOf(nid);

View File

@@ -1,20 +1,151 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:xterm/core.dart'; import 'package:xterm/core.dart';
class VirtualKey { part 'virtual_key.g.dart';
final String text;
final bool toggleable;
final TerminalKey? key;
final IconData? icon;
final VirtualKeyFunc? func;
VirtualKey( @HiveType(typeId: 4)
this.text, { enum VirtKey {
this.key, @HiveField(0)
this.toggleable = false, esc,
this.icon, @HiveField(1)
this.func, alt,
}); @HiveField(2)
home,
@HiveField(3)
up,
@HiveField(4)
end,
@HiveField(5)
file,
@HiveField(6)
snippet,
@HiveField(7)
tab,
@HiveField(8)
ctrl,
@HiveField(9)
left,
@HiveField(10)
down,
@HiveField(11)
right,
@HiveField(12)
paste,
@HiveField(13)
ime,
@HiveField(14)
pgup,
@HiveField(15)
pgdn;
String get text {
switch (this) {
case VirtKey.pgdn:
return 'PgDn';
case VirtKey.pgup:
return 'PgUp';
default:
if (name.length > 1) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}
return name;
}
}
TerminalKey? get key {
switch (this) {
case VirtKey.esc:
return TerminalKey.escape;
case VirtKey.alt:
return TerminalKey.alt;
case VirtKey.home:
return TerminalKey.home;
case VirtKey.up:
return TerminalKey.arrowUp;
case VirtKey.end:
return TerminalKey.end;
case VirtKey.tab:
return TerminalKey.tab;
case VirtKey.ctrl:
return TerminalKey.control;
case VirtKey.left:
return TerminalKey.arrowLeft;
case VirtKey.down:
return TerminalKey.arrowDown;
case VirtKey.right:
return TerminalKey.arrowRight;
case VirtKey.pgup:
return TerminalKey.pageUp;
case VirtKey.pgdn:
return TerminalKey.pageDown;
default:
return null;
}
}
IconData? get icon {
switch (this) {
case VirtKey.up:
return Icons.arrow_upward;
case VirtKey.left:
return Icons.arrow_back;
case VirtKey.down:
return Icons.arrow_downward;
case VirtKey.right:
return Icons.arrow_forward;
case VirtKey.file:
return Icons.file_open;
case VirtKey.snippet:
return Icons.code;
case VirtKey.paste:
return Icons.paste;
case VirtKey.ime:
return Icons.keyboard_hide;
default:
return null;
}
}
// Use [VirtualKeyFunc] instead of [VirtKey]
// This can help linter to enum all [VirtualKeyFunc]
// and make sure all [VirtualKeyFunc] are handled
VirtualKeyFunc? get func {
switch (this) {
case VirtKey.file:
return VirtualKeyFunc.file;
case VirtKey.snippet:
return VirtualKeyFunc.snippet;
case VirtKey.paste:
return VirtualKeyFunc.paste;
case VirtKey.ime:
return VirtualKeyFunc.toggleIME;
default:
return null;
}
}
bool get toggleable {
switch (this) {
case VirtKey.alt:
case VirtKey.ctrl:
return true;
default:
return false;
}
}
bool get canLongPress {
switch (this) {
case VirtKey.up:
case VirtKey.left:
case VirtKey.down:
case VirtKey.right:
return true;
default:
return false;
}
}
} }
enum VirtualKeyFunc { toggleIME, backspace, copy, paste, snippet, file } enum VirtualKeyFunc { toggleIME, backspace, copy, paste, snippet, file }

View File

@@ -0,0 +1,116 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'virtual_key.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class VirtKeyAdapter extends TypeAdapter<VirtKey> {
@override
final int typeId = 4;
@override
VirtKey read(BinaryReader reader) {
switch (reader.readByte()) {
case 0:
return VirtKey.esc;
case 1:
return VirtKey.alt;
case 2:
return VirtKey.home;
case 3:
return VirtKey.up;
case 4:
return VirtKey.end;
case 5:
return VirtKey.file;
case 6:
return VirtKey.snippet;
case 7:
return VirtKey.tab;
case 8:
return VirtKey.ctrl;
case 9:
return VirtKey.left;
case 10:
return VirtKey.down;
case 11:
return VirtKey.right;
case 12:
return VirtKey.paste;
case 13:
return VirtKey.ime;
case 14:
return VirtKey.pgup;
case 15:
return VirtKey.pgdn;
default:
return VirtKey.esc;
}
}
@override
void write(BinaryWriter writer, VirtKey obj) {
switch (obj) {
case VirtKey.esc:
writer.writeByte(0);
break;
case VirtKey.alt:
writer.writeByte(1);
break;
case VirtKey.home:
writer.writeByte(2);
break;
case VirtKey.up:
writer.writeByte(3);
break;
case VirtKey.end:
writer.writeByte(4);
break;
case VirtKey.file:
writer.writeByte(5);
break;
case VirtKey.snippet:
writer.writeByte(6);
break;
case VirtKey.tab:
writer.writeByte(7);
break;
case VirtKey.ctrl:
writer.writeByte(8);
break;
case VirtKey.left:
writer.writeByte(9);
break;
case VirtKey.down:
writer.writeByte(10);
break;
case VirtKey.right:
writer.writeByte(11);
break;
case VirtKey.paste:
writer.writeByte(12);
break;
case VirtKey.ime:
writer.writeByte(13);
break;
case VirtKey.pgup:
writer.writeByte(14);
break;
case VirtKey.pgdn:
writer.writeByte(15);
break;
}
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is VirtKeyAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}

View File

@@ -23,8 +23,8 @@ typedef ServersMap = Map<String, Server>;
class ServerProvider extends BusyProvider { class ServerProvider extends BusyProvider {
final ServersMap _servers = {}; final ServersMap _servers = {};
ServersMap get servers => _servers; ServersMap get servers => _servers;
final StringOrder _serverOrder = []; final Order<String> _serverOrder = [];
StringOrder get serverOrder => _serverOrder; Order get serverOrder => _serverOrder;
final List<String> _tags = []; final List<String> _tags = [];
List<String> get tags => _tags; List<String> get tags => _tags;

View File

@@ -1,5 +1,7 @@
import 'dart:ui'; import 'dart:ui';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
// default server details page cards order // default server details page cards order
const defaultDetailCardOrder = [ const defaultDetailCardOrder = [
'uptime', 'uptime',
@@ -20,6 +22,23 @@ const defaultDiskIgnorePath = [
'none', 'none',
]; ];
const defaultSSHVirtKeys = [
VirtKey.esc,
VirtKey.alt,
VirtKey.home,
VirtKey.up,
VirtKey.end,
VirtKey.file,
VirtKey.snippet,
VirtKey.tab,
VirtKey.ctrl,
VirtKey.left,
VirtKey.down,
VirtKey.right,
VirtKey.paste,
VirtKey.ime,
];
const defaultPrimaryColor = Color.fromARGB(255, 145, 58, 31); const defaultPrimaryColor = Color.fromARGB(255, 145, 58, 31);
const defaultLaunchPageIdx = 0; const defaultLaunchPageIdx = 0;

View File

@@ -1,29 +0,0 @@
import 'package:flutter/material.dart';
import 'package:xterm/core.dart';
import '../model/ssh/virtual_key.dart';
final virtualKeys = [
VirtualKey('Esc', key: TerminalKey.escape),
VirtualKey('Alt', key: TerminalKey.alt, toggleable: true),
VirtualKey('Home', key: TerminalKey.home),
VirtualKey('Up', key: TerminalKey.arrowUp, icon: Icons.arrow_upward),
VirtualKey('End', key: TerminalKey.end),
VirtualKey(
'File',
func: VirtualKeyFunc.file,
icon: Icons.file_open,
),
VirtualKey('Snippet', func: VirtualKeyFunc.snippet, icon: Icons.code),
VirtualKey('Tab', key: TerminalKey.tab),
VirtualKey('Ctrl', key: TerminalKey.control, toggleable: true),
VirtualKey('Left', key: TerminalKey.arrowLeft, icon: Icons.arrow_back),
VirtualKey('Down', key: TerminalKey.arrowDown, icon: Icons.arrow_downward),
VirtualKey('Right', key: TerminalKey.arrowRight, icon: Icons.arrow_forward),
VirtualKey('Paste', func: VirtualKeyFunc.paste, icon: Icons.paste),
VirtualKey(
'IME',
func: VirtualKeyFunc.toggleIME,
icon: Icons.keyboard_hide,
),
];

View File

@@ -1,11 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:toolbox/core/persistant_store.dart'; import 'package:toolbox/core/persistant_store.dart';
import 'package:toolbox/core/utils/platform.dart'; import 'package:toolbox/core/utils/platform.dart';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
import '../res/default.dart'; import '../res/default.dart';
class SettingStore extends PersistentStore { class SettingStore extends PersistentStore {
StoreProperty<int> get primaryColor => property( StoreProperty<int> get primaryColor => property<int>(
'primaryColor', 'primaryColor',
defaultValue: 4287106639, defaultValue: 4287106639,
); );
@@ -76,4 +77,7 @@ class SettingStore extends PersistentStore {
StoreProperty<int> get keyboardType => StoreProperty<int> get keyboardType =>
property('keyboardType', defaultValue: TextInputType.text.index); property('keyboardType', defaultValue: TextInputType.text.index);
StoreProperty<List<VirtKey>> get sshVirtKeys =>
property('sshVirtKeys', defaultValue: defaultSSHVirtKeys);
} }

View File

@@ -34,6 +34,7 @@
"debug": "Debug", "debug": "Debug",
"decode": "Decode", "decode": "Decode",
"delete": "Delete", "delete": "Delete",
"disabled": "Disabled",
"disconnected": "Disconnected", "disconnected": "Disconnected",
"diskIgnorePath": "Ignore path for disk", "diskIgnorePath": "Ignore path for disk",
"dl2Local": "Download {fileName} to local?", "dl2Local": "Download {fileName} to local?",
@@ -46,6 +47,7 @@
"download": "Download", "download": "Download",
"downloadStatus": "{percent}% of {size}", "downloadStatus": "{percent}% of {size}",
"edit": "Edit", "edit": "Edit",
"editVirtKeys": "Edit virtual keys",
"editor": "Editor", "editor": "Editor",
"encode": "Encode", "encode": "Encode",
"error": "Error", "error": "Error",

View File

@@ -34,6 +34,7 @@
"debug": "调试", "debug": "调试",
"decode": "解码", "decode": "解码",
"delete": "删除", "delete": "删除",
"disabled": "已禁用",
"disconnected": "连接断开", "disconnected": "连接断开",
"diskIgnorePath": "忽略的磁盘路径", "diskIgnorePath": "忽略的磁盘路径",
"dl2Local": "下载 {fileName} 到本地?", "dl2Local": "下载 {fileName} 到本地?",
@@ -46,6 +47,7 @@
"download": "下载", "download": "下载",
"downloadStatus": "{size} 的 {percent}%", "downloadStatus": "{size} 的 {percent}%",
"edit": "编辑", "edit": "编辑",
"editVirtKeys": "编辑虚拟按键",
"editor": "编辑器", "editor": "编辑器",
"encode": "编码", "encode": "编码",
"error": "错误", "error": "错误",

View File

@@ -34,6 +34,7 @@
"debug": "調試", "debug": "調試",
"decode": "解碼", "decode": "解碼",
"delete": "刪除", "delete": "刪除",
"disabled": "已禁用",
"disconnected": "連接斷開", "disconnected": "連接斷開",
"diskIgnorePath": "忽略的磁盤路徑", "diskIgnorePath": "忽略的磁盤路徑",
"dl2Local": "下載 {fileName} 到本地?", "dl2Local": "下載 {fileName} 到本地?",
@@ -46,6 +47,7 @@
"download": "下載", "download": "下載",
"downloadStatus": "{size} 的 {percent}%", "downloadStatus": "{size} 的 {percent}%",
"edit": "編輯", "edit": "編輯",
"editVirtKeys": "編輯虛擬按鍵",
"editor": "編輯器", "editor": "編輯器",
"encode": "編碼", "encode": "編碼",
"error": "錯誤", "error": "錯誤",

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
import 'app.dart'; import 'app.dart';
import 'core/analysis.dart'; import 'core/analysis.dart';
@@ -47,6 +48,7 @@ Future<void> initApp() async {
Future<void> initHive() async { Future<void> initHive() async {
await Hive.initFlutter(); await Hive.initFlutter();
Hive.registerAdapter(SnippetAdapter()); Hive.registerAdapter(SnippetAdapter());
Hive.registerAdapter(VirtKeyAdapter());
Hive.registerAdapter(PrivateKeyInfoAdapter()); Hive.registerAdapter(PrivateKeyInfoAdapter());
Hive.registerAdapter(ServerPrivateInfoAdapter()); Hive.registerAdapter(ServerPrivateInfoAdapter());
} }

View File

@@ -3,7 +3,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/route.dart'; import 'package:toolbox/core/route.dart';
import 'package:toolbox/view/page/ssh.dart'; import 'package:toolbox/view/page/ssh/term.dart';
import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/input_field.dart';
import '../../core/utils/ui.dart'; import '../../core/utils/ui.dart';

View File

@@ -32,7 +32,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
late MediaQueryData _media; late MediaQueryData _media;
late S _s; late S _s;
final StringOrder _cardsOrder = []; final Order _cardsOrder = [];
final _setting = locator<SettingStore>(); final _setting = locator<SettingStore>();
@override @override

View File

@@ -25,7 +25,7 @@ import '../../widget/round_rect_card.dart';
import '../docker.dart'; import '../docker.dart';
import '../pkg.dart'; import '../pkg.dart';
import '../sftp/remote.dart'; import '../sftp/remote.dart';
import '../ssh.dart'; import '../ssh/term.dart';
import 'detail.dart'; import 'detail.dart';
import 'edit.dart'; import 'edit.dart';

View File

@@ -8,7 +8,9 @@ import 'package:flutter_material_color_picker/flutter_material_color_picker.dart
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/locale.dart'; import 'package:toolbox/core/extension/locale.dart';
import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/route.dart';
import 'package:toolbox/data/model/app/tab.dart'; import 'package:toolbox/data/model/app/tab.dart';
import 'package:toolbox/view/page/ssh/virt_key_setting.dart';
import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/input_field.dart';
import 'package:toolbox/view/widget/value_notifier.dart'; import 'package:toolbox/view/widget/value_notifier.dart';
@@ -165,6 +167,7 @@ class _SettingPageState extends State<SettingPage> {
_buildTermFontSize(), _buildTermFontSize(),
_buildSSHVirtualKeyAutoOff(), _buildSSHVirtualKeyAutoOff(),
_buildKeyboardType(), _buildKeyboardType(),
_buildSSHVirtKeys(),
].map((e) => RoundRectCard(e)).toList(), ].map((e) => RoundRectCard(e)).toList(),
); );
} }
@@ -739,4 +742,15 @@ class _SettingPageState extends State<SettingPage> {
}, },
); );
} }
Widget _buildSSHVirtKeys() {
return ListTile(
title: Text(_s.editVirtKeys),
trailing: const Icon(Icons.arrow_forward_ios, size: 13),
onTap: () => AppRoute(
const SSHVirtKeySettingPage(),
'ssh virt key edit',
).go(context),
);
}
} }

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:dartssh2/dartssh2.dart'; import 'package:dartssh2/dartssh2.dart';
@@ -10,20 +11,19 @@ import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/data/res/server_cmd.dart'; import 'package:toolbox/data/res/server_cmd.dart';
import 'package:xterm/xterm.dart'; import 'package:xterm/xterm.dart';
import '../../core/route.dart'; import '../../../core/route.dart';
import '../../core/utils/platform.dart'; import '../../../core/utils/platform.dart';
import '../../core/utils/misc.dart'; import '../../../core/utils/misc.dart';
import '../../core/utils/ui.dart'; import '../../../core/utils/ui.dart';
import '../../core/utils/server.dart'; import '../../../core/utils/server.dart';
import '../../data/model/server/server_private_info.dart'; import '../../../data/model/server/server_private_info.dart';
import '../../data/model/ssh/virtual_key.dart'; import '../../../data/model/ssh/virtual_key.dart';
import '../../data/provider/virtual_keyboard.dart'; import '../../../data/provider/virtual_keyboard.dart';
import '../../data/res/color.dart'; import '../../../data/res/color.dart';
import '../../data/res/terminal.dart'; import '../../../data/res/terminal.dart';
import '../../data/res/virtual_key.dart'; import '../../../data/store/setting.dart';
import '../../data/store/setting.dart'; import '../../../locator.dart';
import '../../locator.dart'; import '../sftp/remote.dart';
import 'sftp/remote.dart';
class SSHPage extends StatefulWidget { class SSHPage extends StatefulWidget {
final ServerPrivateInfo spi; final ServerPrivateInfo spi;
@@ -35,22 +35,26 @@ class SSHPage extends StatefulWidget {
} }
class _SSHPageState extends State<SSHPage> { class _SSHPageState extends State<SSHPage> {
late final _terminal = Terminal(inputHandler: _keyboard);
SSHClient? _client;
late SSHSession _session;
final _keyboard = locator<VirtualKeyboard>(); final _keyboard = locator<VirtualKeyboard>();
final _setting = locator<SettingStore>(); final _setting = locator<SettingStore>();
late MediaQueryData _media; late final _terminal = Terminal(inputHandler: _keyboard);
final _virtualKeyboardHeight = 57.0;
final TerminalController _terminalController = TerminalController(); final TerminalController _terminalController = TerminalController();
final ContextMenuController _menuController = ContextMenuController(); final ContextMenuController _menuController = ContextMenuController();
final List<List<VirtKey>> _virtKeysList = [];
late MediaQueryData _media;
late TextStyle _menuTextStyle; late TextStyle _menuTextStyle;
late S _s; late S _s;
late TerminalStyle _terminalStyle; late TerminalStyle _terminalStyle;
late TerminalTheme _terminalTheme; late TerminalTheme _terminalTheme;
late TextInputType _keyboardType; late TextInputType _keyboardType;
late SSHSession _session;
late double _virtKeyWidth;
late double _virtKeysHeight;
var _isDark = false; bool _isDark = false;
Timer? _virtKeyLongPressTimer;
SSHClient? _client;
@override @override
void initState() { void initState() {
@@ -62,7 +66,8 @@ class _SSHPageState extends State<SSHPage> {
); );
_terminalStyle = TerminalStyle.fromTextStyle(textStyle); _terminalStyle = TerminalStyle.fromTextStyle(textStyle);
_keyboardType = TextInputType.values[_setting.keyboardType.fetch()!]; _keyboardType = TextInputType.values[_setting.keyboardType.fetch()!];
initTerminal(); _initTerminal();
_initVirtKeys();
} }
@override @override
@@ -73,6 +78,9 @@ class _SSHPageState extends State<SSHPage> {
_menuTextStyle = TextStyle(color: contentColor.resolve(context)); _menuTextStyle = TextStyle(color: contentColor.resolve(context));
_s = S.of(context)!; _s = S.of(context)!;
_terminalTheme = _isDark ? termDarkTheme : termLightTheme; _terminalTheme = _isDark ? termDarkTheme : termLightTheme;
// Calculate virtkey width / height
_virtKeyWidth = _media.size.width / 7;
_virtKeysHeight = _media.size.height * 0.047 * _virtKeysList.length;
} }
@override @override
@@ -100,7 +108,7 @@ class _SSHPageState extends State<SSHPage> {
Widget _buildBody() { Widget _buildBody() {
return SizedBox( return SizedBox(
height: _media.size.height - height: _media.size.height -
_virtualKeyboardHeight - _virtKeysHeight -
_media.padding.bottom - _media.padding.bottom -
_media.padding.top, _media.padding.top,
child: TerminalView( child: TerminalView(
@@ -125,7 +133,7 @@ class _SSHPageState extends State<SSHPage> {
curve: Curves.fastOutSlowIn, curve: Curves.fastOutSlowIn,
child: Container( child: Container(
color: _terminalTheme.background, color: _terminalTheme.background,
height: _virtualKeyboardHeight, height: _virtKeysHeight,
child: Consumer<VirtualKeyboard>( child: Consumer<VirtualKeyboard>(
builder: (_, __, ___) => _buildVirtualKey(), builder: (_, __, ___) => _buildVirtualKey(),
), ),
@@ -135,23 +143,18 @@ class _SSHPageState extends State<SSHPage> {
} }
Widget _buildVirtualKey() { Widget _buildVirtualKey() {
final half = virtualKeys.length ~/ 2; final rows = _virtKeysList
final top = virtualKeys.sublist(0, half); .map((e) => Row(
final bottom = virtualKeys.sublist(half); children: e.map((ee) => _buildVirtualKeyItem(ee)).toList(),
))
.toList();
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: rows,
Row(
children: top.map((e) => _buildVirtualKeyItem(e)).toList(),
),
Row(
children: bottom.map((e) => _buildVirtualKeyItem(e)).toList(),
)
],
); );
} }
Widget _buildVirtualKeyItem(VirtualKey item) { Widget _buildVirtualKeyItem(VirtKey item) {
var selected = false; var selected = false;
switch (item.key) { switch (item.key) {
case TerminalKey.control: case TerminalKey.control:
@@ -176,9 +179,19 @@ class _SSHPageState extends State<SSHPage> {
return InkWell( return InkWell(
onTap: () => _doVirtualKey(item), onTap: () => _doVirtualKey(item),
onTapDown: (details) {
if (item.canLongPress) {
_virtKeyLongPressTimer = Timer.periodic(
const Duration(milliseconds: 137),
(_) => _doVirtualKey(item),
);
}
},
onTapCancel: () => _virtKeyLongPressTimer?.cancel(),
onTapUp: (_) => _virtKeyLongPressTimer?.cancel(),
child: SizedBox( child: SizedBox(
width: _media.size.width / (virtualKeys.length / 2), width: _virtKeyWidth,
height: _virtualKeyboardHeight / 2, height: _virtKeysHeight / _virtKeysList.length,
child: Center( child: Center(
child: child, child: child,
), ),
@@ -186,7 +199,7 @@ class _SSHPageState extends State<SSHPage> {
); );
} }
void _doVirtualKey(VirtualKey item) { void _doVirtualKey(VirtKey item) {
if (item.func != null) { if (item.func != null) {
_doVirtualKeyFunc(item.func!); _doVirtualKeyFunc(item.func!);
return; return;
@@ -326,7 +339,19 @@ class _SSHPageState extends State<SSHPage> {
_terminal.write('$p0\r\n'); _terminal.write('$p0\r\n');
} }
Future<void> initTerminal() async { void _initVirtKeys() {
final virtKeys = _setting.sshVirtKeys.fetch()!;
for (int len = 0; len < virtKeys.length; len += 7) {
if (len + 7 > virtKeys.length) {
_virtKeysList.add(virtKeys.sublist(len));
} else {
_virtKeysList.add(virtKeys.sublist(len, len + 7));
}
}
}
Future<void> _initTerminal() async {
_write('Connecting...\r\n'); _write('Connecting...\r\n');
_client = await genClient( _client = await genClient(

View File

@@ -0,0 +1,100 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:toolbox/core/extension/order.dart';
import 'package:toolbox/core/utils/ui.dart';
import 'package:toolbox/data/model/ssh/virtual_key.dart';
import 'package:toolbox/data/store/setting.dart';
import 'package:toolbox/locator.dart';
class SSHVirtKeySettingPage extends StatefulWidget {
const SSHVirtKeySettingPage({Key? key}) : super(key: key);
@override
_SSHVirtKeySettingPageState createState() => _SSHVirtKeySettingPageState();
}
class _SSHVirtKeySettingPageState extends State<SSHVirtKeySettingPage> {
final _setting = locator<SettingStore>();
late S _s;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_s = S.of(context)!;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_s.editVirtKeys),
),
body: _buildBody(),
);
}
Widget _buildBody() {
final keys = List<VirtKey>.from(_setting.sshVirtKeys.fetch()!);
final disabled = VirtKey.values.where((e) => !keys.contains(e)).toList();
final allKeys = [...keys, ...disabled];
return ReorderableListView.builder(
padding: const EdgeInsets.fromLTRB(11, 3, 0, 3),
itemBuilder: (_, idx) {
final key = allKeys[idx];
return ListTile(
key: ValueKey(idx),
title: _buildTitle(key),
leading: const Icon(Icons.drag_handle, color: Colors.grey),
trailing: _buildCheckBox(keys, key, idx, idx < keys.length),
);
},
itemCount: allKeys.length,
onReorder: (o, n) {
if (o >= keys.length || n >= keys.length) {
showSnackBar(context, Text(_s.disabled));
return;
}
keys.moveById(keys[o], keys[n], _setting.sshVirtKeys);
setState(() {
});
},
);
}
Widget _buildTitle(VirtKey key) {
return key.icon == null
? Text(
key.text,
textAlign: TextAlign.center,
)
: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(key.text),
const SizedBox(width: 10),
Icon(key.icon),
],
);
}
Widget _buildCheckBox(List<VirtKey> keys, VirtKey key, int idx, bool value) {
return Checkbox(
value: value,
onChanged: (val) {
if (val == null) return;
if (val) {
if (idx >= keys.length) {
keys.add(key);
} else {
keys.insert(idx - 1, key);
}
} else {
keys.remove(key);
}
_setting.sshVirtKeys.put(keys);
setState(() {});
},
);
}
}

View File

@@ -6,9 +6,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <flutter_volume_controller/flutter_volume_controller_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h> #include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin");
flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
flutter_volume_controller
url_launcher_linux url_launcher_linux
) )

View File

@@ -5,11 +5,13 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import flutter_volume_controller
import path_provider_foundation import path_provider_foundation
import share_plus import share_plus
import url_launcher_macos import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))

View File

@@ -1,4 +1,6 @@
PODS: PODS:
- flutter_volume_controller (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0) - FlutterMacOS (1.0.0)
- path_provider_foundation (0.0.1): - path_provider_foundation (0.0.1):
- Flutter - Flutter
@@ -9,12 +11,15 @@ PODS:
- FlutterMacOS - FlutterMacOS
DEPENDENCIES: DEPENDENCIES:
- flutter_volume_controller (from `Flutter/ephemeral/.symlinks/plugins/flutter_volume_controller/macos`)
- FlutterMacOS (from `Flutter/ephemeral`) - FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
- share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
EXTERNAL SOURCES: EXTERNAL SOURCES:
flutter_volume_controller:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_volume_controller/macos
FlutterMacOS: FlutterMacOS:
:path: Flutter/ephemeral :path: Flutter/ephemeral
path_provider_foundation: path_provider_foundation:
@@ -25,6 +30,7 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
SPEC CHECKSUMS: SPEC CHECKSUMS:
flutter_volume_controller: 25d09126b0d695560f11c80b1311d5063fed882f
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7 share_plus: 76dd39142738f7a68dd57b05093b5e8193f220f7

View File

@@ -377,6 +377,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_volume_controller:
dependency: "direct main"
description:
name: flutter_volume_controller
sha256: "5e7d1b63d051c881450a98155024219079a1cde0cf66ef895eb96dc9d8a7f670"
url: "https://pub.dev"
source: hosted
version: "1.2.6"
flutter_web_plugins: flutter_web_plugins:
dependency: transitive dependency: transitive
description: flutter description: flutter

View File

@@ -66,6 +66,7 @@ dependencies:
highlight: ^0.7.0 highlight: ^0.7.0
flutter_highlight: ^0.7.0 flutter_highlight: ^0.7.0
code_text_field: ^1.1.0 code_text_field: ^1.1.0
flutter_volume_controller: ^1.2.6
dev_dependencies: dev_dependencies:
flutter_native_splash: ^2.1.6 flutter_native_splash: ^2.1.6

View File

@@ -6,10 +6,13 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <flutter_volume_controller/flutter_volume_controller_plugin_c_api.h>
#include <share_plus/share_plus_windows_plugin_c_api.h> #include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h> #include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) { void RegisterPlugins(flutter::PluginRegistry* registry) {
FlutterVolumeControllerPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterVolumeControllerPluginCApi"));
SharePlusWindowsPluginCApiRegisterWithRegistrar( SharePlusWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi"));
UrlLauncherWindowsRegisterWithRegistrar( UrlLauncherWindowsRegisterWithRegistrar(

View File

@@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
flutter_volume_controller
share_plus share_plus
url_launcher_windows url_launcher_windows
) )