From 2ddc29f45e49be923516caf061bc499828b8db93 Mon Sep 17 00:00:00 2001 From: lollipopkit Date: Fri, 8 Mar 2024 02:03:11 -0600 Subject: [PATCH] new: setting of `termCursor` type --- lib/core/extension/enum.dart | 12 + lib/data/store/setting.dart | 5 + lib/l10n/app_de.arb | 1 + lib/l10n/app_en.arb | 1 + lib/l10n/app_es.arb | 1 + lib/l10n/app_fr.arb | 1 + lib/l10n/app_id.arb | 1 + lib/l10n/app_ja.arb | 1 + lib/l10n/app_pt.arb | 1 + lib/l10n/app_ru.arb | 1 + lib/l10n/app_zh.arb | 1 + lib/l10n/app_zh_tw.arb | 1 + lib/view/page/setting/entry.dart | 456 ++++++++++++------------------- lib/view/page/ssh/page.dart | 24 +- 14 files changed, 209 insertions(+), 298 deletions(-) create mode 100644 lib/core/extension/enum.dart diff --git a/lib/core/extension/enum.dart b/lib/core/extension/enum.dart new file mode 100644 index 00000000..10119f63 --- /dev/null +++ b/lib/core/extension/enum.dart @@ -0,0 +1,12 @@ +extension EnumListX on List { + T fromIndex(int index, [T? defaultValue]) { + try { + return this[index]; + } catch (e) { + if (defaultValue != null) { + return defaultValue; + } + throw Exception('Invalid index: $index'); + } + } +} diff --git a/lib/data/store/setting.dart b/lib/data/store/setting.dart index 656efdba..4c4a99d0 100644 --- a/lib/data/store/setting.dart +++ b/lib/data/store/setting.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:toolbox/core/persistant_store.dart'; import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/data/model/app/menu/server_func.dart'; +import 'package:xterm/ui.dart'; import '../model/app/net_view.dart'; import '../res/default.dart'; @@ -255,6 +256,10 @@ class SettingStore extends PersistentStore { /// 0: unset, 1: use, 2: not use late final useCdn = property('useCdn', 0); + /// Index of terminal cursor type + late final termCursor = + property('termCursor', TerminalCursorType.block.index); + // Never show these settings for users // // ------BEGIN------ diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 24696183..6370d880 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -48,6 +48,7 @@ "copyPath": "Pfad kopieren", "createFile": "Datei erstellen", "createFolder": "Ordner erstellen", + "cursorType": "Cursor-Typ", "dark": "Dunkel", "debug": "Debug", "decode": "Decode", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 5216ae16..e79c0312 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -48,6 +48,7 @@ "copyPath": "Copy path", "createFile": "Create file", "createFolder": "Create folder", + "cursorType": "Cursor type", "dark": "Dark", "debug": "Debug", "decode": "Decode", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 3320bc18..5a577b21 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -48,6 +48,7 @@ "copyPath": "Copiar ruta", "createFile": "Crear archivo", "createFolder": "Crear carpeta", + "cursorType": "Tipo de cursor", "dark": "Oscuro", "debug": "Depurar", "decode": "Decodificar", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index 2f814c9f..e97e9c20 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -48,6 +48,7 @@ "copyPath": "Copier le chemin", "createFile": "Créer un fichier", "createFolder": "Créer un dossier", + "cursorType": "Type de curseur", "dark": "Sombre", "debug": "Déboguer", "decode": "Décoder", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 44b8df13..cc48614b 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -48,6 +48,7 @@ "copyPath": "Path Copy", "createFile": "Buat file", "createFolder": "Membuat folder", + "cursorType": "Jenis kursor", "dark": "Gelap", "debug": "Debug", "decode": "Membaca sandi", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index 37de97fd..87fa24cd 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -48,6 +48,7 @@ "copyPath": "パスをコピー", "createFile": "ファイルを作成", "createFolder": "フォルダーを作成", + "cursorType": "カーソルタイプ", "dark": "ダーク", "debug": "デバッグ", "decode": "デコード", diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 24b2774c..eeb305f8 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -48,6 +48,7 @@ "copyPath": "Copiar caminho", "createFile": "Criar arquivo", "createFolder": "Criar pasta", + "cursorType": "Tipo de cursor", "dark": "Escuro", "debug": "Debugar", "decode": "Decodificar", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index 354bfb8f..41339f8b 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -48,6 +48,7 @@ "copyPath": "копировать путь", "createFile": "создать файл", "createFolder": "создать папку", + "cursorType": "Тип курсора", "dark": "темная", "debug": "отладка", "decode": "декодировать", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 89d71e88..5a005145 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -48,6 +48,7 @@ "copyPath": "复制路径", "createFile": "创建文件", "createFolder": "创建文件夹", + "cursorType": "光标类型", "dark": "暗", "debug": "调试", "decode": "解码", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index 80af5451..0952eb5e 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -48,6 +48,7 @@ "copyPath": "複製路徑", "createFile": "創建文件", "createFolder": "創建文件夾", + "cursorType": "光標類型", "dark": "暗", "debug": "調試", "decode": "解碼", diff --git a/lib/view/page/setting/entry.dart b/lib/view/page/setting/entry.dart index 67dbe8b7..834405ac 100644 --- a/lib/view/page/setting/entry.dart +++ b/lib/view/page/setting/entry.dart @@ -8,6 +8,7 @@ import 'package:toolbox/core/extension/colorx.dart'; import 'package:toolbox/core/extension/context/common.dart'; import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/extension/context/snackbar.dart'; +import 'package:toolbox/core/extension/enum.dart'; import 'package:toolbox/core/extension/locale.dart'; import 'package:toolbox/core/extension/context/dialog.dart'; import 'package:toolbox/core/extension/stringx.dart'; @@ -17,6 +18,7 @@ import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/rebuild.dart'; import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/view/widget/expand_tile.dart'; +import 'package:xterm/ui.dart'; import '../../../core/persistant_store.dart'; import '../../../core/route.dart'; @@ -42,44 +44,8 @@ class SettingPage extends StatefulWidget { } class _SettingPageState extends State { - final _themeKey = GlobalKey>(); - final _updateIntervalKey = GlobalKey>(); - final _maxRetryKey = GlobalKey>(); - final _localeKey = GlobalKey>(); - final _editorThemeKey = GlobalKey>(); - final _editorDarkThemeKey = GlobalKey>(); - final _keyboardTypeKey = GlobalKey>(); - //final _rotateQuarterKey = GlobalKey>(); - final _netViewTypeKey = GlobalKey>(); final _setting = Stores.setting; - late final _selectedColorValue = ValueNotifier(_setting.primaryColor.fetch()); - late final _nightMode = ValueNotifier(_setting.themeMode.fetch()); - late final _maxRetryCount = ValueNotifier(_setting.maxRetryCount.fetch()); - late final _updateInterval = - ValueNotifier(_setting.serverStatusUpdateInterval.fetch()); - late final _termFontSize = ValueNotifier(_setting.termFontSize.fetch()); - late final _editorFontSize = ValueNotifier(_setting.editorFontSize.fetch()); - late final _localeCode = ValueNotifier(''); - late final _editorTheme = ValueNotifier(_setting.editorTheme.fetch()); - late final _editorDarkTheme = ValueNotifier(_setting.editorDarkTheme.fetch()); - late final _keyboardType = ValueNotifier(_setting.keyboardType.fetch()); - // late final _rotateQuarter = - // ValueNotifier(_setting.fullScreenRotateQuarter.fetch()); - late final _netViewType = ValueNotifier(_setting.netViewType.fetch()); - late final _textScaler = ValueNotifier(_setting.textFactor.fetch()); - - @override - void didChangeDependencies() { - super.didChangeDependencies(); - final localeSettingVal = _setting.locale.fetch(); - if (localeSettingVal.isEmpty) { - _localeCode.value = l10n.localeName; - } else { - _localeCode.value = localeSettingVal; - } - } - @override Widget build(BuildContext context) { return Scaffold( @@ -206,6 +172,7 @@ class _SettingPageState extends State { children: [ _buildFont(), _buildTermFontSize(), + _buildTermCursor(), _buildSSHVirtualKeyAutoOff(), // Use hardware keyboard on desktop, so there is no need to set it if (isMobile) _buildKeyboardType(), @@ -249,17 +216,6 @@ class _SettingPageState extends State { } Widget _buildUpdateInterval() { - final items = List.generate( - 10, - (index) => PopupMenuItem( - value: index, - child: Text(index == 0 ? l10n.manual : '$index ${l10n.second}'), - ), - growable: false, - ).toList(); - // 1 second is too fast, so remove it - items.removeAt(1); - return ListTile( title: Text( l10n.updateServerStatusInterval, @@ -268,27 +224,21 @@ class _SettingPageState extends State { l10n.willTakEeffectImmediately, style: UIs.textGrey, ), - onTap: () { - _updateIntervalKey.currentState?.showButtonMenu(); + onTap: () async { + final val = await context.showPickSingleDialog( + items: List.generate(10, (idx) => idx == 1 ? null : idx), + initial: _setting.serverStatusUpdateInterval.fetch(), + name: (p0) => p0 == 0 ? l10n.manual : '$p0 ${l10n.second}', + ); + if (val != null) { + _setting.serverStatusUpdateInterval.put(val); + } }, - trailing: ListenableBuilder( - listenable: _updateInterval, - builder: (_, __) => PopupMenuButton( - key: _updateIntervalKey, - itemBuilder: (_) => items, - initialValue: _updateInterval.value, - onSelected: (int val) { - _updateInterval.value = val; - _setting.serverStatusUpdateInterval.put(val); - Pros.server.startAutoRefresh(); - if (val == 0) { - context.showSnackBar(l10n.updateIntervalEqual0); - } - }, - child: Text( - '${_updateInterval.value} ${l10n.second}', - style: UIs.text15, - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.serverStatusUpdateInterval.listenable(), + builder: (_, val, __) => Text( + '$val ${l10n.second}', + style: UIs.text15, ), ), ); @@ -297,10 +247,10 @@ class _SettingPageState extends State { Widget _buildAppColor() { return ListTile( trailing: ClipOval( - child: ListenableBuilder( - listenable: _selectedColorValue, - builder: (_, __) => Container( - color: primaryColor, + child: ValueListenableBuilder( + valueListenable: _setting.primaryColor.listenable(), + builder: (_, val, __) => Container( + color: Color(val), height: 27, width: 27, ), @@ -362,8 +312,7 @@ class _SettingPageState extends State { // Change [primaryColor] first, then change [_selectedColorValue], // So the [ValueBuilder] will be triggered with the new value primaryColor = color; - _selectedColorValue.value = color.value; - _setting.primaryColor.put(_selectedColorValue.value); + _setting.primaryColor.put(color.value); context.pop(); RebuildNodes.app.rebuild(); } @@ -409,89 +358,57 @@ class _SettingPageState extends State { // } Widget _buildMaxRetry() { - final items = List.generate( - 10, - (index) => PopupMenuItem( - value: index, - child: Text('$index ${l10n.times}'), - ), - growable: false, - ).toList(); - final help = _maxRetryCount.value == 0 - ? l10n.maxRetryCountEqual0 - : l10n.canPullRefresh; - - return ListTile( - title: Text( - l10n.maxRetryCount, - textAlign: TextAlign.start, - ), - subtitle: Text(help, style: UIs.textGrey), - onTap: () { - _maxRetryKey.currentState?.showButtonMenu(); - }, - trailing: ListenableBuilder( - builder: (_, __) => PopupMenuButton( - key: _maxRetryKey, - itemBuilder: (BuildContext context) => items, - initialValue: _maxRetryCount.value, - onSelected: (int val) { - _maxRetryCount.value = val; - _setting.maxRetryCount.put(_maxRetryCount.value); - }, - child: Text( - '${_maxRetryCount.value} ${l10n.times}', - style: UIs.text15, - ), + return ValueListenableBuilder( + valueListenable: _setting.maxRetryCount.listenable(), + builder: (_, val, __) => ListTile( + title: Text( + l10n.maxRetryCount, + textAlign: TextAlign.start, + ), + subtitle: Text( + val == 0 ? l10n.maxRetryCountEqual0 : l10n.canPullRefresh, + style: UIs.textGrey), + onTap: () async { + final selected = await context.showPickSingleDialog( + items: List.generate(10, (index) => index), + name: (p0) => '$p0 ${l10n.times}', + initial: val, + ); + if (selected != null) { + _setting.maxRetryCount.put(selected); + } + }, + trailing: Text( + '$val ${l10n.times}', + style: UIs.text15, ), - listenable: _maxRetryCount, ), ); } Widget _buildThemeMode() { - final items = ThemeMode.values - .map( - (e) => PopupMenuItem( - value: e.index, - child: Text(_buildThemeModeStr(e.index)), - ), - ) - .toList(); // Issue #57 final len = ThemeMode.values.length; - - /// Add AMOLED theme - items.add(PopupMenuItem(value: len, child: Text(_buildThemeModeStr(len)))); - - /// Add AUTO-AMOLED theme - items.add( - PopupMenuItem(value: len + 1, child: Text(_buildThemeModeStr(len + 1))), - ); - return ListTile( title: Text( l10n.themeMode, ), - onTap: () { - _themeKey.currentState?.showButtonMenu(); + onTap: () async { + final selected = await context.showPickSingleDialog( + items: List.generate(len + 2, (index) => index), + name: (p0) => _buildThemeModeStr(p0), + initial: _setting.themeMode.fetch(), + ); + if (selected != null) { + _setting.themeMode.put(selected); + RebuildNodes.app.rebuild(); + } }, - trailing: ListenableBuilder( - listenable: _nightMode, - builder: (_, __) => PopupMenuButton( - key: _themeKey, - itemBuilder: (BuildContext context) => items, - initialValue: _nightMode.value, - onSelected: (int idx) { - _nightMode.value = idx; - _setting.themeMode.put(_nightMode.value); - - RebuildNodes.app.rebuild(); - }, - child: Text( - _buildThemeModeStr(_nightMode.value), - style: UIs.text15, - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.themeMode.listenable(), + builder: (_, val, __) => Text( + _buildThemeModeStr(val), + style: UIs.text15, ), ), ); @@ -573,7 +490,7 @@ class _SettingPageState extends State { style: UIs.text15, ), ), - onTap: () => _showFontSizeDialog(_termFontSize, _setting.termFontSize), + onTap: () => _showFontSizeDialog(_setting.termFontSize), ); } @@ -615,35 +532,25 @@ class _SettingPageState extends State { // } Widget _buildLocale() { - final items = S.supportedLocales - .map( - (e) => PopupMenuItem( - value: e.code, - child: Text(e.code), - ), - ) - .toList(); return ListTile( title: Text(l10n.language), - onTap: () { - _localeKey.currentState?.showButtonMenu(); + onTap: () async { + final selected = await context.showPickSingleDialog( + items: S.supportedLocales, + name: (p0) => p0.code, + initial: _setting.locale.fetch().toLocale, + ); + if (selected != null) { + _setting.locale.put(selected.code); + context.pop(); + RebuildNodes.app.rebuild(); + } }, - trailing: ListenableBuilder( - listenable: _localeCode, - builder: (_, __) => PopupMenuButton( - key: _localeKey, - itemBuilder: (BuildContext context) => items, - initialValue: _localeCode.value, - onSelected: (String idx) { - _localeCode.value = idx; - _setting.locale.put(idx); - RebuildNodes.app.rebuild(); - context.pop(); - }, - child: Text( - l10n.languageName, - style: UIs.text15, - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.locale.listenable(), + builder: (_, ___, __) => Text( + l10n.languageName, + style: UIs.text15, ), ), ); @@ -658,67 +565,41 @@ class _SettingPageState extends State { } Widget _buildEditorTheme() { - final items = themeMap.keys.map( - (key) { - return PopupMenuItem( - value: key, - child: Text(key), - ); - }, - ).toList(); return ListTile( title: Text('${l10n.light} ${l10n.theme.toLowerCase()}'), - trailing: ListenableBuilder( - listenable: _editorTheme, - builder: (_, __) => PopupMenuButton( - key: _editorThemeKey, - itemBuilder: (BuildContext context) => items, - initialValue: _editorTheme.value, - onSelected: (String idx) { - _editorTheme.value = idx; - _setting.editorTheme.put(idx); - }, - child: Text( - _editorTheme.value, - style: UIs.text15, - ), - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.editorTheme.listenable(), + builder: (_, val, __) => Text(val, style: UIs.text15), ), - onTap: () { - _editorThemeKey.currentState?.showButtonMenu(); + onTap: () async { + final selected = await context.showPickSingleDialog( + items: themeMap.keys.toList(), + name: (p0) => p0, + initial: _setting.editorTheme.fetch(), + ); + if (selected != null) { + _setting.editorTheme.put(selected); + } }, ); } Widget _buildEditorDarkTheme() { - final items = themeMap.keys.map( - (key) { - return PopupMenuItem( - value: key, - child: Text(key), - ); - }, - ).toList(); return ListTile( title: Text('${l10n.dark} ${l10n.theme.toLowerCase()}'), - trailing: ListenableBuilder( - listenable: _editorDarkTheme, - builder: (_, __) => PopupMenuButton( - key: _editorDarkThemeKey, - itemBuilder: (BuildContext context) => items, - initialValue: _editorDarkTheme.value, - onSelected: (String idx) { - _editorDarkTheme.value = idx; - _setting.editorDarkTheme.put(idx); - }, - child: Text( - _editorDarkTheme.value, - style: UIs.text15, - ), - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.editorDarkTheme.listenable(), + builder: (_, val, __) => Text(val, style: UIs.text15), ), - onTap: () { - _editorDarkThemeKey.currentState?.showButtonMenu(); + onTap: () async { + final selected = await context.showPickSingleDialog( + items: themeMap.keys.toList(), + name: (p0) => p0, + initial: _setting.editorDarkTheme.fetch(), + ); + if (selected != null) { + _setting.editorDarkTheme.put(selected); + } }, ); } @@ -788,39 +669,28 @@ class _SettingPageState extends State { 'address', 'none', ]; - if (names.length != TextInputType.values.length) { - // This notify me to update the code - throw Exception('names.length != TextInputType.values.length'); - } - final items = TextInputType.values.map( - (key) { - return PopupMenuItem( - value: key.index, - child: Text(names[key.index]), - ); - }, - ).toList(); return ListTile( title: Text(l10n.keyboardType), subtitle: Text(l10n.keyboardCompatibility, style: UIs.textGrey), - trailing: ListenableBuilder( - listenable: _keyboardType, - builder: (_, __) => PopupMenuButton( - key: _keyboardTypeKey, - itemBuilder: (BuildContext context) => items, - initialValue: _keyboardType.value, - onSelected: (idx) { - _keyboardType.value = idx; - _setting.keyboardType.put(idx); - }, - child: Text( - names[_keyboardType.value], - style: UIs.text15, - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.keyboardType.listenable(), + builder: (_, val, __) => Text( + names[val], + style: UIs.text15, ), ), - onTap: () { - _keyboardTypeKey.currentState?.showButtonMenu(); + onTap: () async { + if (names.length != TextInputType.values.length) { + // This notify me to update the code + context.showSnackBar('names.length != TextInputType.values.length'); + } + final selected = await context.showPickSingleDialog( + items: names, + initial: names.fromIndex(_setting.keyboardType.fetch()), + ); + if (selected != null) { + _setting.keyboardType.put(names.indexOf(selected)); + } }, ); } @@ -859,32 +729,24 @@ class _SettingPageState extends State { } Widget _buildNetViewType() { - final items = NetViewType.values - .map((e) => PopupMenuItem( - value: e, - child: Text(e.toStr), - )) - .toList(); return ListTile( title: Text(l10n.netViewType), - trailing: ListenableBuilder( - listenable: _netViewType, - builder: (_, __) => PopupMenuButton( - key: _netViewTypeKey, - itemBuilder: (BuildContext context) => items, - initialValue: _netViewType.value, - onSelected: (idx) { - _netViewType.value = idx; - _setting.netViewType.put(idx); - }, - child: Text( - _netViewType.value.toStr, - style: UIs.text15, - ), + trailing: ValueListenableBuilder( + valueListenable: _setting.netViewType.listenable(), + builder: (_, val, __) => Text( + val.toStr, + style: UIs.text15, ), ), - onTap: () { - _netViewTypeKey.currentState?.showButtonMenu(); + onTap: () async { + final selected = await context.showPickSingleDialog( + items: NetViewType.values, + name: (p0) => p0.toStr, + initial: _setting.netViewType.fetch(), + ); + if (selected != null) { + _setting.netViewType.put(selected); + } }, ); } @@ -933,14 +795,14 @@ class _SettingPageState extends State { } Widget _buildTextScaler() { - final ctrl = TextEditingController(text: _textScaler.value.toString()); + final ctrl = TextEditingController(text: _setting.textFactor.toString()); return ListTile( title: Text(l10n.textScaler), subtitle: Text(l10n.textScalerTip, style: UIs.textGrey), - trailing: ListenableBuilder( - listenable: _textScaler, - builder: (_, __) => Text( - _textScaler.value.toString(), + trailing: ValueListenableBuilder( + valueListenable: _setting.textFactor.listenable(), + builder: (_, val, __) => Text( + val.toString(), style: UIs.text15, ), ), @@ -970,7 +832,6 @@ class _SettingPageState extends State { context.showSnackBar(l10n.failed); return; } - _textScaler.value = val; _setting.textFactor.put(val); RebuildNodes.app.rebuild(); context.pop(); @@ -1019,25 +880,23 @@ class _SettingPageState extends State { } Widget _buildEditorFontSize() { - return ListenableBuilder( - listenable: _editorFontSize, - builder: (_, __) => ListTile( - title: Text(l10n.fontSize), - trailing: Text( - _editorFontSize.value.toString(), + return ListTile( + title: Text(l10n.fontSize), + trailing: ValueListenableBuilder( + valueListenable: _setting.editorFontSize.listenable(), + builder: (_, val, __) => Text( + val.toString(), style: UIs.text15, ), - onTap: () => - _showFontSizeDialog(_editorFontSize, _setting.editorFontSize), ), + onTap: () => _showFontSizeDialog(_setting.editorFontSize), ); } void _showFontSizeDialog( - ValueNotifier notifier, StorePropertyBase property, ) { - final ctrller = TextEditingController(text: notifier.value.toString()); + final ctrller = TextEditingController(text: property.fetch().toString()); void onSave() { context.pop(); final fontSize = double.tryParse(ctrller.text); @@ -1048,7 +907,6 @@ class _SettingPageState extends State { ); return; } - notifier.value = fontSize; property.put(fontSize); } @@ -1199,4 +1057,28 @@ class _SettingPageState extends State { ], ); } + + Widget _buildTermCursor() { + return ListTile( + title: Text(l10n.cursorType), + trailing: ValueListenableBuilder( + valueListenable: _setting.termCursor.listenable(), + builder: (_, val, __) => Text( + TerminalCursorType.values.fromIndex(val).name, + style: UIs.text15, + ), + ), + onTap: () async { + final selected = await context.showPickSingleDialog( + items: TerminalCursorType.values, + name: (p0) => p0.name, + initial: + TerminalCursorType.values.fromIndex(_setting.termCursor.fetch()), + ); + if (selected != null) { + _setting.termCursor.put(selected.index); + } + }, + ); + } } diff --git a/lib/view/page/ssh/page.dart b/lib/view/page/ssh/page.dart index 0874fb58..a6c86c5e 100644 --- a/lib/view/page/ssh/page.dart +++ b/lib/view/page/ssh/page.dart @@ -13,7 +13,6 @@ import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/utils/platform/base.dart'; import 'package:toolbox/core/utils/server.dart'; import 'package:toolbox/core/utils/share.dart'; -import 'package:toolbox/data/model/server/server.dart'; import 'package:toolbox/data/model/server/snippet.dart'; import 'package:toolbox/data/provider/virtual_keyboard.dart'; import 'package:toolbox/data/res/provider.dart'; @@ -61,11 +60,11 @@ class _SSHPageState extends State with AutomaticKeepAliveClientMixin { late double _originTextSize; double _virtKeyWidth = 0; double _virtKeysHeight = 0; + late final TerminalCursorType _termCursor; bool _isDark = false; Timer? _virtKeyLongPressTimer; - late final Server? _server = widget.spi.server; - late SSHClient? _client = _server?.client; + late SSHClient? _client = widget.spi.server?.client; Timer? _discontinuityTimer; @override @@ -84,13 +83,6 @@ class _SSHPageState extends State with AutomaticKeepAliveClientMixin { super.dispose(); _virtKeyLongPressTimer?.cancel(); _terminalController.dispose(); - - /// Use the same [SSHClient], so don't close it - // if (_client?.isClosed == false) { - // try { - // _client?.close(); - // } catch (_) {} - // } _discontinuityTimer?.cancel(); } @@ -157,6 +149,7 @@ class _SSHPageState extends State with AutomaticKeepAliveClientMixin { deleteDetection: isMobile, autofocus: true, keyboardAppearance: _isDark ? Brightness.dark : Brightness.light, + cursorType: _termCursor, //hideScrollBar: false, ), ), @@ -377,7 +370,7 @@ class _SSHPageState extends State with AutomaticKeepAliveClientMixin { //_setupDiscontinuityTimer(); if (session == null) { - _writeLn(_server?.status.err ?? 'Null session'); + _writeLn('Null session'); return; } @@ -494,6 +487,15 @@ class _SSHPageState extends State with AutomaticKeepAliveClientMixin { _terminalStyle = TerminalStyle.fromTextStyle(textStyle); _keyboardType = TextInputType.values[Stores.setting.keyboardType.fetch()]; + + final termCursor = Stores.setting.termCursor.fetch(); + _termCursor = () { + try { + return TerminalCursorType.values[termCursor]; + } catch (_) { + return TerminalCursorType.block; + } + }(); } }