new: setting of termCursor type

This commit is contained in:
lollipopkit
2024-03-08 02:03:11 -06:00
parent b9aa4ba124
commit 2ddc29f45e
14 changed files with 209 additions and 298 deletions

View File

@@ -0,0 +1,12 @@
extension EnumListX<T> on List<T> {
T fromIndex(int index, [T? defaultValue]) {
try {
return this[index];
} catch (e) {
if (defaultValue != null) {
return defaultValue;
}
throw Exception('Invalid index: $index');
}
}
}

View File

@@ -2,6 +2,7 @@ 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/base.dart'; import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/data/model/app/menu/server_func.dart'; import 'package:toolbox/data/model/app/menu/server_func.dart';
import 'package:xterm/ui.dart';
import '../model/app/net_view.dart'; import '../model/app/net_view.dart';
import '../res/default.dart'; import '../res/default.dart';
@@ -255,6 +256,10 @@ class SettingStore extends PersistentStore {
/// 0: unset, 1: use, 2: not use /// 0: unset, 1: use, 2: not use
late final useCdn = property('useCdn', 0); 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 // Never show these settings for users
// //
// ------BEGIN------ // ------BEGIN------

View File

@@ -48,6 +48,7 @@
"copyPath": "Pfad kopieren", "copyPath": "Pfad kopieren",
"createFile": "Datei erstellen", "createFile": "Datei erstellen",
"createFolder": "Ordner erstellen", "createFolder": "Ordner erstellen",
"cursorType": "Cursor-Typ",
"dark": "Dunkel", "dark": "Dunkel",
"debug": "Debug", "debug": "Debug",
"decode": "Decode", "decode": "Decode",

View File

@@ -48,6 +48,7 @@
"copyPath": "Copy path", "copyPath": "Copy path",
"createFile": "Create file", "createFile": "Create file",
"createFolder": "Create folder", "createFolder": "Create folder",
"cursorType": "Cursor type",
"dark": "Dark", "dark": "Dark",
"debug": "Debug", "debug": "Debug",
"decode": "Decode", "decode": "Decode",

View File

@@ -48,6 +48,7 @@
"copyPath": "Copiar ruta", "copyPath": "Copiar ruta",
"createFile": "Crear archivo", "createFile": "Crear archivo",
"createFolder": "Crear carpeta", "createFolder": "Crear carpeta",
"cursorType": "Tipo de cursor",
"dark": "Oscuro", "dark": "Oscuro",
"debug": "Depurar", "debug": "Depurar",
"decode": "Decodificar", "decode": "Decodificar",

View File

@@ -48,6 +48,7 @@
"copyPath": "Copier le chemin", "copyPath": "Copier le chemin",
"createFile": "Créer un fichier", "createFile": "Créer un fichier",
"createFolder": "Créer un dossier", "createFolder": "Créer un dossier",
"cursorType": "Type de curseur",
"dark": "Sombre", "dark": "Sombre",
"debug": "Déboguer", "debug": "Déboguer",
"decode": "Décoder", "decode": "Décoder",

View File

@@ -48,6 +48,7 @@
"copyPath": "Path Copy", "copyPath": "Path Copy",
"createFile": "Buat file", "createFile": "Buat file",
"createFolder": "Membuat folder", "createFolder": "Membuat folder",
"cursorType": "Jenis kursor",
"dark": "Gelap", "dark": "Gelap",
"debug": "Debug", "debug": "Debug",
"decode": "Membaca sandi", "decode": "Membaca sandi",

View File

@@ -48,6 +48,7 @@
"copyPath": "パスをコピー", "copyPath": "パスをコピー",
"createFile": "ファイルを作成", "createFile": "ファイルを作成",
"createFolder": "フォルダーを作成", "createFolder": "フォルダーを作成",
"cursorType": "カーソルタイプ",
"dark": "ダーク", "dark": "ダーク",
"debug": "デバッグ", "debug": "デバッグ",
"decode": "デコード", "decode": "デコード",

View File

@@ -48,6 +48,7 @@
"copyPath": "Copiar caminho", "copyPath": "Copiar caminho",
"createFile": "Criar arquivo", "createFile": "Criar arquivo",
"createFolder": "Criar pasta", "createFolder": "Criar pasta",
"cursorType": "Tipo de cursor",
"dark": "Escuro", "dark": "Escuro",
"debug": "Debugar", "debug": "Debugar",
"decode": "Decodificar", "decode": "Decodificar",

View File

@@ -48,6 +48,7 @@
"copyPath": "копировать путь", "copyPath": "копировать путь",
"createFile": "создать файл", "createFile": "создать файл",
"createFolder": "создать папку", "createFolder": "создать папку",
"cursorType": "Тип курсора",
"dark": "темная", "dark": "темная",
"debug": "отладка", "debug": "отладка",
"decode": "декодировать", "decode": "декодировать",

View File

@@ -48,6 +48,7 @@
"copyPath": "复制路径", "copyPath": "复制路径",
"createFile": "创建文件", "createFile": "创建文件",
"createFolder": "创建文件夹", "createFolder": "创建文件夹",
"cursorType": "光标类型",
"dark": "暗", "dark": "暗",
"debug": "调试", "debug": "调试",
"decode": "解码", "decode": "解码",

View File

@@ -48,6 +48,7 @@
"copyPath": "複製路徑", "copyPath": "複製路徑",
"createFile": "創建文件", "createFile": "創建文件",
"createFolder": "創建文件夾", "createFolder": "創建文件夾",
"cursorType": "光標類型",
"dark": "暗", "dark": "暗",
"debug": "調試", "debug": "調試",
"decode": "解碼", "decode": "解碼",

View File

@@ -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/common.dart';
import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/context/snackbar.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/locale.dart';
import 'package:toolbox/core/extension/context/dialog.dart'; import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/stringx.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/rebuild.dart';
import 'package:toolbox/data/res/store.dart'; import 'package:toolbox/data/res/store.dart';
import 'package:toolbox/view/widget/expand_tile.dart'; import 'package:toolbox/view/widget/expand_tile.dart';
import 'package:xterm/ui.dart';
import '../../../core/persistant_store.dart'; import '../../../core/persistant_store.dart';
import '../../../core/route.dart'; import '../../../core/route.dart';
@@ -42,44 +44,8 @@ class SettingPage extends StatefulWidget {
} }
class _SettingPageState extends State<SettingPage> { class _SettingPageState extends State<SettingPage> {
final _themeKey = GlobalKey<PopupMenuButtonState<int>>();
final _updateIntervalKey = GlobalKey<PopupMenuButtonState<int>>();
final _maxRetryKey = GlobalKey<PopupMenuButtonState<int>>();
final _localeKey = GlobalKey<PopupMenuButtonState<String>>();
final _editorThemeKey = GlobalKey<PopupMenuButtonState<String>>();
final _editorDarkThemeKey = GlobalKey<PopupMenuButtonState<String>>();
final _keyboardTypeKey = GlobalKey<PopupMenuButtonState<int>>();
//final _rotateQuarterKey = GlobalKey<PopupMenuButtonState<int>>();
final _netViewTypeKey = GlobalKey<PopupMenuButtonState<NetViewType>>();
final _setting = Stores.setting; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@@ -206,6 +172,7 @@ class _SettingPageState extends State<SettingPage> {
children: [ children: [
_buildFont(), _buildFont(),
_buildTermFontSize(), _buildTermFontSize(),
_buildTermCursor(),
_buildSSHVirtualKeyAutoOff(), _buildSSHVirtualKeyAutoOff(),
// Use hardware keyboard on desktop, so there is no need to set it // Use hardware keyboard on desktop, so there is no need to set it
if (isMobile) _buildKeyboardType(), if (isMobile) _buildKeyboardType(),
@@ -249,17 +216,6 @@ class _SettingPageState extends State<SettingPage> {
} }
Widget _buildUpdateInterval() { 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( return ListTile(
title: Text( title: Text(
l10n.updateServerStatusInterval, l10n.updateServerStatusInterval,
@@ -268,39 +224,33 @@ class _SettingPageState extends State<SettingPage> {
l10n.willTakEeffectImmediately, l10n.willTakEeffectImmediately,
style: UIs.textGrey, style: UIs.textGrey,
), ),
onTap: () { onTap: () async {
_updateIntervalKey.currentState?.showButtonMenu(); final val = await context.showPickSingleDialog(
}, items: List.generate(10, (idx) => idx == 1 ? null : idx),
trailing: ListenableBuilder( initial: _setting.serverStatusUpdateInterval.fetch(),
listenable: _updateInterval, name: (p0) => p0 == 0 ? l10n.manual : '$p0 ${l10n.second}',
builder: (_, __) => PopupMenuButton( );
key: _updateIntervalKey, if (val != null) {
itemBuilder: (_) => items,
initialValue: _updateInterval.value,
onSelected: (int val) {
_updateInterval.value = val;
_setting.serverStatusUpdateInterval.put(val); _setting.serverStatusUpdateInterval.put(val);
Pros.server.startAutoRefresh();
if (val == 0) {
context.showSnackBar(l10n.updateIntervalEqual0);
} }
}, },
child: Text( trailing: ValueListenableBuilder(
'${_updateInterval.value} ${l10n.second}', valueListenable: _setting.serverStatusUpdateInterval.listenable(),
builder: (_, val, __) => Text(
'$val ${l10n.second}',
style: UIs.text15, style: UIs.text15,
), ),
), ),
),
); );
} }
Widget _buildAppColor() { Widget _buildAppColor() {
return ListTile( return ListTile(
trailing: ClipOval( trailing: ClipOval(
child: ListenableBuilder( child: ValueListenableBuilder(
listenable: _selectedColorValue, valueListenable: _setting.primaryColor.listenable(),
builder: (_, __) => Container( builder: (_, val, __) => Container(
color: primaryColor, color: Color(val),
height: 27, height: 27,
width: 27, width: 27,
), ),
@@ -362,8 +312,7 @@ class _SettingPageState extends State<SettingPage> {
// Change [primaryColor] first, then change [_selectedColorValue], // Change [primaryColor] first, then change [_selectedColorValue],
// So the [ValueBuilder] will be triggered with the new value // So the [ValueBuilder] will be triggered with the new value
primaryColor = color; primaryColor = color;
_selectedColorValue.value = color.value; _setting.primaryColor.put(color.value);
_setting.primaryColor.put(_selectedColorValue.value);
context.pop(); context.pop();
RebuildNodes.app.rebuild(); RebuildNodes.app.rebuild();
} }
@@ -409,91 +358,59 @@ class _SettingPageState extends State<SettingPage> {
// } // }
Widget _buildMaxRetry() { Widget _buildMaxRetry() {
final items = List.generate( return ValueListenableBuilder(
10, valueListenable: _setting.maxRetryCount.listenable(),
(index) => PopupMenuItem( builder: (_, val, __) => ListTile(
value: index,
child: Text('$index ${l10n.times}'),
),
growable: false,
).toList();
final help = _maxRetryCount.value == 0
? l10n.maxRetryCountEqual0
: l10n.canPullRefresh;
return ListTile(
title: Text( title: Text(
l10n.maxRetryCount, l10n.maxRetryCount,
textAlign: TextAlign.start, textAlign: TextAlign.start,
), ),
subtitle: Text(help, style: UIs.textGrey), subtitle: Text(
onTap: () { val == 0 ? l10n.maxRetryCountEqual0 : l10n.canPullRefresh,
_maxRetryKey.currentState?.showButtonMenu(); 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: ListenableBuilder( trailing: Text(
builder: (_, __) => PopupMenuButton( '$val ${l10n.times}',
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, style: UIs.text15,
), ),
), ),
listenable: _maxRetryCount,
),
); );
} }
Widget _buildThemeMode() { Widget _buildThemeMode() {
final items = ThemeMode.values
.map(
(e) => PopupMenuItem(
value: e.index,
child: Text(_buildThemeModeStr(e.index)),
),
)
.toList();
// Issue #57 // Issue #57
final len = ThemeMode.values.length; 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( return ListTile(
title: Text( title: Text(
l10n.themeMode, l10n.themeMode,
), ),
onTap: () { onTap: () async {
_themeKey.currentState?.showButtonMenu(); final selected = await context.showPickSingleDialog(
}, items: List.generate(len + 2, (index) => index),
trailing: ListenableBuilder( name: (p0) => _buildThemeModeStr(p0),
listenable: _nightMode, initial: _setting.themeMode.fetch(),
builder: (_, __) => PopupMenuButton( );
key: _themeKey, if (selected != null) {
itemBuilder: (BuildContext context) => items, _setting.themeMode.put(selected);
initialValue: _nightMode.value,
onSelected: (int idx) {
_nightMode.value = idx;
_setting.themeMode.put(_nightMode.value);
RebuildNodes.app.rebuild(); RebuildNodes.app.rebuild();
}
}, },
child: Text( trailing: ValueListenableBuilder(
_buildThemeModeStr(_nightMode.value), valueListenable: _setting.themeMode.listenable(),
builder: (_, val, __) => Text(
_buildThemeModeStr(val),
style: UIs.text15, style: UIs.text15,
), ),
), ),
),
); );
} }
@@ -573,7 +490,7 @@ class _SettingPageState extends State<SettingPage> {
style: UIs.text15, style: UIs.text15,
), ),
), ),
onTap: () => _showFontSizeDialog(_termFontSize, _setting.termFontSize), onTap: () => _showFontSizeDialog(_setting.termFontSize),
); );
} }
@@ -615,37 +532,27 @@ class _SettingPageState extends State<SettingPage> {
// } // }
Widget _buildLocale() { Widget _buildLocale() {
final items = S.supportedLocales
.map(
(e) => PopupMenuItem<String>(
value: e.code,
child: Text(e.code),
),
)
.toList();
return ListTile( return ListTile(
title: Text(l10n.language), title: Text(l10n.language),
onTap: () { onTap: () async {
_localeKey.currentState?.showButtonMenu(); final selected = await context.showPickSingleDialog(
}, items: S.supportedLocales,
trailing: ListenableBuilder( name: (p0) => p0.code,
listenable: _localeCode, initial: _setting.locale.fetch().toLocale,
builder: (_, __) => PopupMenuButton( );
key: _localeKey, if (selected != null) {
itemBuilder: (BuildContext context) => items, _setting.locale.put(selected.code);
initialValue: _localeCode.value,
onSelected: (String idx) {
_localeCode.value = idx;
_setting.locale.put(idx);
RebuildNodes.app.rebuild();
context.pop(); context.pop();
RebuildNodes.app.rebuild();
}
}, },
child: Text( trailing: ValueListenableBuilder(
valueListenable: _setting.locale.listenable(),
builder: (_, ___, __) => Text(
l10n.languageName, l10n.languageName,
style: UIs.text15, style: UIs.text15,
), ),
), ),
),
); );
} }
@@ -658,67 +565,41 @@ class _SettingPageState extends State<SettingPage> {
} }
Widget _buildEditorTheme() { Widget _buildEditorTheme() {
final items = themeMap.keys.map(
(key) {
return PopupMenuItem<String>(
value: key,
child: Text(key),
);
},
).toList();
return ListTile( return ListTile(
title: Text('${l10n.light} ${l10n.theme.toLowerCase()}'), title: Text('${l10n.light} ${l10n.theme.toLowerCase()}'),
trailing: ListenableBuilder( trailing: ValueListenableBuilder(
listenable: _editorTheme, valueListenable: _setting.editorTheme.listenable(),
builder: (_, __) => PopupMenuButton( builder: (_, val, __) => Text(val, style: UIs.text15),
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,
), ),
), onTap: () async {
), final selected = await context.showPickSingleDialog(
onTap: () { items: themeMap.keys.toList(),
_editorThemeKey.currentState?.showButtonMenu(); name: (p0) => p0,
initial: _setting.editorTheme.fetch(),
);
if (selected != null) {
_setting.editorTheme.put(selected);
}
}, },
); );
} }
Widget _buildEditorDarkTheme() { Widget _buildEditorDarkTheme() {
final items = themeMap.keys.map(
(key) {
return PopupMenuItem<String>(
value: key,
child: Text(key),
);
},
).toList();
return ListTile( return ListTile(
title: Text('${l10n.dark} ${l10n.theme.toLowerCase()}'), title: Text('${l10n.dark} ${l10n.theme.toLowerCase()}'),
trailing: ListenableBuilder( trailing: ValueListenableBuilder(
listenable: _editorDarkTheme, valueListenable: _setting.editorDarkTheme.listenable(),
builder: (_, __) => PopupMenuButton( builder: (_, val, __) => Text(val, style: UIs.text15),
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,
), ),
), onTap: () async {
), final selected = await context.showPickSingleDialog(
onTap: () { items: themeMap.keys.toList(),
_editorDarkThemeKey.currentState?.showButtonMenu(); name: (p0) => p0,
initial: _setting.editorDarkTheme.fetch(),
);
if (selected != null) {
_setting.editorDarkTheme.put(selected);
}
}, },
); );
} }
@@ -788,39 +669,28 @@ class _SettingPageState extends State<SettingPage> {
'address', 'address',
'none', '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<int>(
value: key.index,
child: Text(names[key.index]),
);
},
).toList();
return ListTile( return ListTile(
title: Text(l10n.keyboardType), title: Text(l10n.keyboardType),
subtitle: Text(l10n.keyboardCompatibility, style: UIs.textGrey), subtitle: Text(l10n.keyboardCompatibility, style: UIs.textGrey),
trailing: ListenableBuilder( trailing: ValueListenableBuilder(
listenable: _keyboardType, valueListenable: _setting.keyboardType.listenable(),
builder: (_, __) => PopupMenuButton<int>( builder: (_, val, __) => Text(
key: _keyboardTypeKey, names[val],
itemBuilder: (BuildContext context) => items,
initialValue: _keyboardType.value,
onSelected: (idx) {
_keyboardType.value = idx;
_setting.keyboardType.put(idx);
},
child: Text(
names[_keyboardType.value],
style: UIs.text15, style: UIs.text15,
), ),
), ),
), onTap: () async {
onTap: () { if (names.length != TextInputType.values.length) {
_keyboardTypeKey.currentState?.showButtonMenu(); // 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<SettingPage> {
} }
Widget _buildNetViewType() { Widget _buildNetViewType() {
final items = NetViewType.values
.map((e) => PopupMenuItem(
value: e,
child: Text(e.toStr),
))
.toList();
return ListTile( return ListTile(
title: Text(l10n.netViewType), title: Text(l10n.netViewType),
trailing: ListenableBuilder( trailing: ValueListenableBuilder(
listenable: _netViewType, valueListenable: _setting.netViewType.listenable(),
builder: (_, __) => PopupMenuButton<NetViewType>( builder: (_, val, __) => Text(
key: _netViewTypeKey, val.toStr,
itemBuilder: (BuildContext context) => items,
initialValue: _netViewType.value,
onSelected: (idx) {
_netViewType.value = idx;
_setting.netViewType.put(idx);
},
child: Text(
_netViewType.value.toStr,
style: UIs.text15, style: UIs.text15,
), ),
), ),
), onTap: () async {
onTap: () { final selected = await context.showPickSingleDialog(
_netViewTypeKey.currentState?.showButtonMenu(); 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<SettingPage> {
} }
Widget _buildTextScaler() { Widget _buildTextScaler() {
final ctrl = TextEditingController(text: _textScaler.value.toString()); final ctrl = TextEditingController(text: _setting.textFactor.toString());
return ListTile( return ListTile(
title: Text(l10n.textScaler), title: Text(l10n.textScaler),
subtitle: Text(l10n.textScalerTip, style: UIs.textGrey), subtitle: Text(l10n.textScalerTip, style: UIs.textGrey),
trailing: ListenableBuilder( trailing: ValueListenableBuilder(
listenable: _textScaler, valueListenable: _setting.textFactor.listenable(),
builder: (_, __) => Text( builder: (_, val, __) => Text(
_textScaler.value.toString(), val.toString(),
style: UIs.text15, style: UIs.text15,
), ),
), ),
@@ -970,7 +832,6 @@ class _SettingPageState extends State<SettingPage> {
context.showSnackBar(l10n.failed); context.showSnackBar(l10n.failed);
return; return;
} }
_textScaler.value = val;
_setting.textFactor.put(val); _setting.textFactor.put(val);
RebuildNodes.app.rebuild(); RebuildNodes.app.rebuild();
context.pop(); context.pop();
@@ -1019,25 +880,23 @@ class _SettingPageState extends State<SettingPage> {
} }
Widget _buildEditorFontSize() { Widget _buildEditorFontSize() {
return ListenableBuilder( return ListTile(
listenable: _editorFontSize,
builder: (_, __) => ListTile(
title: Text(l10n.fontSize), title: Text(l10n.fontSize),
trailing: Text( trailing: ValueListenableBuilder(
_editorFontSize.value.toString(), valueListenable: _setting.editorFontSize.listenable(),
builder: (_, val, __) => Text(
val.toString(),
style: UIs.text15, style: UIs.text15,
), ),
onTap: () =>
_showFontSizeDialog(_editorFontSize, _setting.editorFontSize),
), ),
onTap: () => _showFontSizeDialog(_setting.editorFontSize),
); );
} }
void _showFontSizeDialog( void _showFontSizeDialog(
ValueNotifier<double> notifier,
StorePropertyBase<double> property, StorePropertyBase<double> property,
) { ) {
final ctrller = TextEditingController(text: notifier.value.toString()); final ctrller = TextEditingController(text: property.fetch().toString());
void onSave() { void onSave() {
context.pop(); context.pop();
final fontSize = double.tryParse(ctrller.text); final fontSize = double.tryParse(ctrller.text);
@@ -1048,7 +907,6 @@ class _SettingPageState extends State<SettingPage> {
); );
return; return;
} }
notifier.value = fontSize;
property.put(fontSize); property.put(fontSize);
} }
@@ -1199,4 +1057,28 @@ class _SettingPageState extends State<SettingPage> {
], ],
); );
} }
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);
}
},
);
}
} }

View File

@@ -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/platform/base.dart';
import 'package:toolbox/core/utils/server.dart'; import 'package:toolbox/core/utils/server.dart';
import 'package:toolbox/core/utils/share.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/model/server/snippet.dart';
import 'package:toolbox/data/provider/virtual_keyboard.dart'; import 'package:toolbox/data/provider/virtual_keyboard.dart';
import 'package:toolbox/data/res/provider.dart'; import 'package:toolbox/data/res/provider.dart';
@@ -61,11 +60,11 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
late double _originTextSize; late double _originTextSize;
double _virtKeyWidth = 0; double _virtKeyWidth = 0;
double _virtKeysHeight = 0; double _virtKeysHeight = 0;
late final TerminalCursorType _termCursor;
bool _isDark = false; bool _isDark = false;
Timer? _virtKeyLongPressTimer; Timer? _virtKeyLongPressTimer;
late final Server? _server = widget.spi.server; late SSHClient? _client = widget.spi.server?.client;
late SSHClient? _client = _server?.client;
Timer? _discontinuityTimer; Timer? _discontinuityTimer;
@override @override
@@ -84,13 +83,6 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
super.dispose(); super.dispose();
_virtKeyLongPressTimer?.cancel(); _virtKeyLongPressTimer?.cancel();
_terminalController.dispose(); _terminalController.dispose();
/// Use the same [SSHClient], so don't close it
// if (_client?.isClosed == false) {
// try {
// _client?.close();
// } catch (_) {}
// }
_discontinuityTimer?.cancel(); _discontinuityTimer?.cancel();
} }
@@ -157,6 +149,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
deleteDetection: isMobile, deleteDetection: isMobile,
autofocus: true, autofocus: true,
keyboardAppearance: _isDark ? Brightness.dark : Brightness.light, keyboardAppearance: _isDark ? Brightness.dark : Brightness.light,
cursorType: _termCursor,
//hideScrollBar: false, //hideScrollBar: false,
), ),
), ),
@@ -377,7 +370,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
//_setupDiscontinuityTimer(); //_setupDiscontinuityTimer();
if (session == null) { if (session == null) {
_writeLn(_server?.status.err ?? 'Null session'); _writeLn('Null session');
return; return;
} }
@@ -494,6 +487,15 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
_terminalStyle = TerminalStyle.fromTextStyle(textStyle); _terminalStyle = TerminalStyle.fromTextStyle(textStyle);
_keyboardType = TextInputType.values[Stores.setting.keyboardType.fetch()]; _keyboardType = TextInputType.values[Stores.setting.keyboardType.fetch()];
final termCursor = Stores.setting.termCursor.fetch();
_termCursor = () {
try {
return TerminalCursorType.values[termCursor];
} catch (_) {
return TerminalCursorType.block;
}
}();
} }
} }