mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat: SSH page background (#775)
This commit is contained in:
@@ -230,6 +230,9 @@ class SettingStore extends HiveStore {
|
|||||||
|
|
||||||
/// ssh page
|
/// ssh page
|
||||||
late final sshWakeLock = propertyDefault('sshWakeLock', true);
|
late final sshWakeLock = propertyDefault('sshWakeLock', true);
|
||||||
|
late final sshBgImage = propertyDefault('sshBgImage', '');
|
||||||
|
late final sshBgOpacity = propertyDefault('sshBgOpacity', 0.3);
|
||||||
|
late final sshBlurRadius = propertyDefault('sshBlurRadius', 0.0);
|
||||||
|
|
||||||
/// fmt: https://example.com/{DIST}-{BRIGHT}.png
|
/// fmt: https://example.com/{DIST}-{BRIGHT}.png
|
||||||
late final serverLogoUrl = propertyDefault('serverLogoUrl', '');
|
late final serverLogoUrl = propertyDefault('serverLogoUrl', '');
|
||||||
|
|||||||
@@ -78,10 +78,7 @@ extension _App on _AppSettingsPageState {
|
|||||||
},
|
},
|
||||||
trailing: ValBuilder(
|
trailing: ValBuilder(
|
||||||
listenable: _setting.serverStatusUpdateInterval.listenable(),
|
listenable: _setting.serverStatusUpdateInterval.listenable(),
|
||||||
builder: (val) => Text(
|
builder: (val) => Text('$val ${l10n.second}', style: UIs.text15),
|
||||||
'$val ${l10n.second}',
|
|
||||||
style: UIs.text15,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -90,17 +87,16 @@ extension _App on _AppSettingsPageState {
|
|||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.colorize),
|
leading: const Icon(Icons.colorize),
|
||||||
title: Text(libL10n.primaryColorSeed),
|
title: Text(libL10n.primaryColorSeed),
|
||||||
trailing: _setting.colorSeed.listenable().listenVal(
|
trailing: _setting.colorSeed.listenable().listenVal((val) {
|
||||||
(val) {
|
|
||||||
final c = Color(val);
|
final c = Color(val);
|
||||||
return ClipOval(child: Container(color: c, height: 27, width: 27));
|
return ClipOval(child: Container(color: c, height: 27, width: 27));
|
||||||
},
|
}),
|
||||||
),
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final ctrl = TextEditingController(text: UIs.primaryColor.toHex);
|
final ctrl = TextEditingController(text: UIs.primaryColor.toHex);
|
||||||
await context.showRoundDialog(
|
await context.showRoundDialog(
|
||||||
title: libL10n.primaryColorSeed,
|
title: libL10n.primaryColorSeed,
|
||||||
child: StatefulBuilder(builder: (context, setState) {
|
child: StatefulBuilder(
|
||||||
|
builder: (context, setState) {
|
||||||
final children = <Widget>[
|
final children = <Widget>[
|
||||||
/// Plugin [dynamic_color] is not supported on iOS
|
/// Plugin [dynamic_color] is not supported on iOS
|
||||||
if (!isIOS)
|
if (!isIOS)
|
||||||
@@ -110,21 +106,22 @@ extension _App on _AppSettingsPageState {
|
|||||||
prop: _setting.useSystemPrimaryColor,
|
prop: _setting.useSystemPrimaryColor,
|
||||||
callback: (_) => setState(() {}),
|
callback: (_) => setState(() {}),
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
];
|
];
|
||||||
if (!_setting.useSystemPrimaryColor.fetch()) {
|
if (!_setting.useSystemPrimaryColor.fetch()) {
|
||||||
children.add(ColorPicker(
|
children.add(
|
||||||
|
ColorPicker(
|
||||||
color: Color(_setting.colorSeed.fetch()),
|
color: Color(_setting.colorSeed.fetch()),
|
||||||
onColorChanged: (c) => ctrl.text = c.toHex,
|
onColorChanged: (c) => ctrl.text = c.toHex,
|
||||||
));
|
),
|
||||||
}
|
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: children,
|
|
||||||
);
|
);
|
||||||
}),
|
}
|
||||||
|
return Column(mainAxisSize: MainAxisSize.min, children: children);
|
||||||
|
},
|
||||||
|
),
|
||||||
actions: Btn.ok(onTap: () => _onSaveColor(ctrl.text)).toList,
|
actions: Btn.ok(onTap: () => _onSaveColor(ctrl.text)).toList,
|
||||||
);
|
);
|
||||||
|
ctrl.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -157,10 +154,7 @@ extension _App on _AppSettingsPageState {
|
|||||||
_setting.maxRetryCount.put(selected);
|
_setting.maxRetryCount.put(selected);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
trailing: Text(
|
trailing: Text('$val ${l10n.times}', style: UIs.text15),
|
||||||
'$val ${l10n.times}',
|
|
||||||
style: UIs.text15,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -185,10 +179,7 @@ extension _App on _AppSettingsPageState {
|
|||||||
},
|
},
|
||||||
trailing: ValBuilder(
|
trailing: ValBuilder(
|
||||||
listenable: _setting.themeMode.listenable(),
|
listenable: _setting.themeMode.listenable(),
|
||||||
builder: (val) => Text(
|
builder: (val) => Text(_buildThemeModeStr(val), style: UIs.text15),
|
||||||
_buildThemeModeStr(val),
|
|
||||||
style: UIs.text15,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -216,10 +207,7 @@ extension _App on _AppSettingsPageState {
|
|||||||
title: TipText(l10n.fontSize, l10n.termFontSizeTip),
|
title: TipText(l10n.fontSize, l10n.termFontSizeTip),
|
||||||
trailing: ValBuilder(
|
trailing: ValBuilder(
|
||||||
listenable: _setting.termFontSize.listenable(),
|
listenable: _setting.termFontSize.listenable(),
|
||||||
builder: (val) => Text(
|
builder: (val) => Text(val.toString(), style: UIs.text15),
|
||||||
val.toString(),
|
|
||||||
style: UIs.text15,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
onTap: () => _showFontSizeDialog(_setting.termFontSize),
|
onTap: () => _showFontSizeDialog(_setting.termFontSize),
|
||||||
);
|
);
|
||||||
@@ -244,10 +232,7 @@ extension _App on _AppSettingsPageState {
|
|||||||
},
|
},
|
||||||
trailing: ListenBuilder(
|
trailing: ListenBuilder(
|
||||||
listenable: _setting.locale.listenable(),
|
listenable: _setting.locale.listenable(),
|
||||||
builder: () => Text(
|
builder: () => Text(context.localeNativeName, style: UIs.text15),
|
||||||
context.localeNativeName,
|
|
||||||
style: UIs.text15,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,14 +100,13 @@ extension _Editor on _AppSettingsPageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _showFontSizeDialog(HiveProp<double> property) {
|
void _showFontSizeDialog(HiveProp<double> property) {
|
||||||
final ctrller = TextEditingController(text: property.get().toString());
|
|
||||||
void onSave() {
|
void onSave() {
|
||||||
context.pop();
|
context.pop();
|
||||||
final fontSize = double.tryParse(ctrller.text);
|
final fontSize = double.tryParse(_editorTextSizeCtrl.text);
|
||||||
if (fontSize == null) {
|
if (fontSize == null) {
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: libL10n.fail,
|
title: libL10n.fail,
|
||||||
child: Text('Parsed failed: ${ctrller.text}'),
|
child: Text('Parsed failed: ${_editorTextSizeCtrl.text}'),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -117,7 +116,7 @@ extension _Editor on _AppSettingsPageState {
|
|||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.fontSize,
|
title: l10n.fontSize,
|
||||||
child: Input(
|
child: Input(
|
||||||
controller: ctrller,
|
controller: _editorTextSizeCtrl,
|
||||||
autoFocus: true,
|
autoFocus: true,
|
||||||
type: TextInputType.number,
|
type: TextInputType.number,
|
||||||
icon: Icons.font_download,
|
icon: Icons.font_download,
|
||||||
|
|||||||
@@ -72,7 +72,6 @@ extension _Server on _AppSettingsPageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildTextScaler() {
|
Widget _buildTextScaler() {
|
||||||
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),
|
||||||
@@ -88,11 +87,11 @@ extension _Server on _AppSettingsPageState {
|
|||||||
type: TextInputType.number,
|
type: TextInputType.number,
|
||||||
hint: '1.0',
|
hint: '1.0',
|
||||||
icon: Icons.format_size,
|
icon: Icons.format_size,
|
||||||
controller: ctrl,
|
controller: _textScalerCtrl,
|
||||||
onSubmitted: _onSaveTextScaler,
|
onSubmitted: _onSaveTextScaler,
|
||||||
suggestion: false,
|
suggestion: false,
|
||||||
),
|
),
|
||||||
actions: Btn.ok(onTap: () => _onSaveTextScaler(ctrl.text)).toList,
|
actions: Btn.ok(onTap: () => _onSaveTextScaler(_textScalerCtrl.text)).toList,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -273,14 +272,13 @@ extension _Server on _AppSettingsPageState {
|
|||||||
title: const Text('Logo URL'),
|
title: const Text('Logo URL'),
|
||||||
trailing: const Icon(Icons.keyboard_arrow_right),
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
final ctrl = TextEditingController(text: _setting.serverLogoUrl.fetch());
|
|
||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: 'Logo URL',
|
title: 'Logo URL',
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Input(
|
Input(
|
||||||
controller: ctrl,
|
controller: _serverLogoCtrl,
|
||||||
autoFocus: true,
|
autoFocus: true,
|
||||||
hint: 'https://example.com/logo.png',
|
hint: 'https://example.com/logo.png',
|
||||||
icon: Icons.link,
|
icon: Icons.link,
|
||||||
@@ -295,7 +293,7 @@ extension _Server on _AppSettingsPageState {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
actions: Btn.ok(onTap: () => onSave(ctrl.text)).toList,
|
actions: Btn.ok(onTap: () => onSave(_serverLogoCtrl.text)).toList,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,15 +39,11 @@ extension _SFTP on _AppSettingsPageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSftpEditor() {
|
Widget _buildSftpEditor() {
|
||||||
return _setting.sftpEditor.listenable().listenVal(
|
return _setting.sftpEditor.listenable().listenVal((val) {
|
||||||
(val) {
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(MingCute.edit_fill),
|
leading: const Icon(MingCute.edit_fill),
|
||||||
title: TipText(l10n.editor, l10n.sftpEditorTip),
|
title: TipText(l10n.editor, l10n.sftpEditorTip),
|
||||||
trailing: Text(
|
trailing: Text(val.isEmpty ? l10n.inner : val, style: UIs.text15),
|
||||||
val.isEmpty ? l10n.inner : val,
|
|
||||||
style: UIs.text15,
|
|
||||||
),
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final ctrl = TextEditingController(text: val);
|
final ctrl = TextEditingController(text: val);
|
||||||
void onSave() {
|
void onSave() {
|
||||||
@@ -69,9 +65,9 @@ extension _SFTP on _AppSettingsPageState {
|
|||||||
),
|
),
|
||||||
actions: Btn.ok(onTap: onSave).toList,
|
actions: Btn.ok(onTap: onSave).toList,
|
||||||
);
|
);
|
||||||
|
ctrl.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ extension _SSH on _AppSettingsPageState {
|
|||||||
_buildTermTheme(),
|
_buildTermTheme(),
|
||||||
_buildFont(),
|
_buildFont(),
|
||||||
_buildTermFontSize(),
|
_buildTermFontSize(),
|
||||||
|
_buildSshBg(),
|
||||||
if (isDesktop) _buildDesktopTerminal(),
|
if (isDesktop) _buildDesktopTerminal(),
|
||||||
_buildSSHVirtualKeyAutoOff(),
|
_buildSSHVirtualKeyAutoOff(),
|
||||||
if (isMobile) _buildSSHVirtKeys(),
|
if (isMobile) _buildSSHVirtKeys(),
|
||||||
@@ -46,10 +47,7 @@ extension _SSH on _AppSettingsPageState {
|
|||||||
context.showRoundDialog(
|
context.showRoundDialog(
|
||||||
title: l10n.font,
|
title: l10n.font,
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(onPressed: () async => await _pickFontFile(), child: Text(libL10n.file)),
|
||||||
onPressed: () async => await _pickFontFile(),
|
|
||||||
child: Text(libL10n.file),
|
|
||||||
),
|
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_setting.fontPath.delete();
|
_setting.fontPath.delete();
|
||||||
@@ -81,17 +79,31 @@ extension _SSH on _AppSettingsPageState {
|
|||||||
RNodes.app.notify();
|
RNodes.app.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _pickBgImage() async {
|
||||||
|
final path = await Pfs.pickFilePath();
|
||||||
|
if (path == null) return;
|
||||||
|
|
||||||
|
final file = File(path);
|
||||||
|
final extIndex = path.lastIndexOf('.');
|
||||||
|
final ext = extIndex != -1 ? path.substring(extIndex) : '';
|
||||||
|
final newPath = Paths.img.joinPath('ssh_bg$ext');
|
||||||
|
final destFile = File(newPath);
|
||||||
|
if (await destFile.exists()) {
|
||||||
|
await destFile.delete();
|
||||||
|
}
|
||||||
|
await file.copy(newPath);
|
||||||
|
_setting.sshBgImage.put(newPath);
|
||||||
|
|
||||||
|
context.pop();
|
||||||
|
RNodes.app.notify();
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildDesktopTerminal() {
|
Widget _buildDesktopTerminal() {
|
||||||
return _setting.desktopTerminal.listenable().listenVal((val) {
|
return _setting.desktopTerminal.listenable().listenVal((val) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: const Icon(Icons.terminal),
|
leading: const Icon(Icons.terminal),
|
||||||
title: TipText(l10n.terminal, l10n.desktopTerminalTip),
|
title: TipText(l10n.terminal, l10n.desktopTerminalTip),
|
||||||
trailing: Text(
|
trailing: Text(val, style: UIs.text15, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||||
val,
|
|
||||||
style: UIs.text15,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
final ctrl = TextEditingController(text: val);
|
final ctrl = TextEditingController(text: val);
|
||||||
void onSave() {
|
void onSave() {
|
||||||
@@ -99,7 +111,7 @@ extension _SSH on _AppSettingsPageState {
|
|||||||
context.pop();
|
context.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
context.showRoundDialog<bool>(
|
await context.showRoundDialog<bool>(
|
||||||
title: libL10n.select,
|
title: libL10n.select,
|
||||||
child: Input(
|
child: Input(
|
||||||
controller: ctrl,
|
controller: ctrl,
|
||||||
@@ -112,6 +124,7 @@ extension _SSH on _AppSettingsPageState {
|
|||||||
),
|
),
|
||||||
actions: Btn.ok(onTap: onSave).toList,
|
actions: Btn.ok(onTap: onSave).toList,
|
||||||
);
|
);
|
||||||
|
ctrl.dispose();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -168,11 +181,114 @@ extension _SSH on _AppSettingsPageState {
|
|||||||
// '${l10n.letterCacheTip}\n${l10n.needRestart}',
|
// '${l10n.letterCacheTip}\n${l10n.needRestart}',
|
||||||
// style: UIs.textGrey,
|
// style: UIs.textGrey,
|
||||||
// ),
|
// ),
|
||||||
title: TipText(
|
title: TipText(l10n.letterCache, '${l10n.letterCacheTip}\n${l10n.needRestart}'),
|
||||||
l10n.letterCache,
|
|
||||||
'${l10n.letterCacheTip}\n${l10n.needRestart}',
|
|
||||||
),
|
|
||||||
trailing: StoreSwitch(prop: _setting.letterCache),
|
trailing: StoreSwitch(prop: _setting.letterCache),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildSshBg() {
|
||||||
|
return ExpandTile(
|
||||||
|
leading: const Icon(MingCute.background_fill),
|
||||||
|
title: Text(libL10n.background),
|
||||||
|
children: [_buildSshBgImage(), _buildSshBgOpacity(), _buildSshBlurRadius()],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSshBgImage() {
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(Icons.image),
|
||||||
|
title: Text(libL10n.image),
|
||||||
|
trailing: _setting.sshBgImage.listenable().listenVal((val) {
|
||||||
|
final name = val.getFileName();
|
||||||
|
return Text(name ?? libL10n.empty, style: UIs.text15);
|
||||||
|
}),
|
||||||
|
onTap: () {
|
||||||
|
context.showRoundDialog(
|
||||||
|
title: libL10n.image,
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () async => await _pickBgImage(), child: Text(libL10n.file)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_setting.sshBgImage.delete();
|
||||||
|
context.pop();
|
||||||
|
RNodes.app.notify();
|
||||||
|
},
|
||||||
|
child: Text(libL10n.clear),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSshBgOpacity() {
|
||||||
|
void onSave(String s) {
|
||||||
|
final val = double.tryParse(s);
|
||||||
|
if (val == null) {
|
||||||
|
context.showSnackBar(libL10n.fail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_setting.sshBgOpacity.put(val.clamp(0.0, 1.0));
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(Icons.opacity),
|
||||||
|
title: Text(libL10n.opacity),
|
||||||
|
trailing: ValBuilder(
|
||||||
|
listenable: _setting.sshBgOpacity.listenable(),
|
||||||
|
builder: (val) => Text(val.toString(), style: UIs.text15),
|
||||||
|
),
|
||||||
|
onTap: () => context.showRoundDialog(
|
||||||
|
title: libL10n.opacity,
|
||||||
|
child: Input(
|
||||||
|
controller: _sshOpacityCtrl,
|
||||||
|
autoFocus: true,
|
||||||
|
type: TextInputType.number,
|
||||||
|
hint: '0.3',
|
||||||
|
icon: Icons.opacity,
|
||||||
|
suggestion: false,
|
||||||
|
onSubmitted: onSave,
|
||||||
|
),
|
||||||
|
actions: Btn.ok(onTap: () => onSave(_sshOpacityCtrl.text)).toList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildSshBlurRadius() {
|
||||||
|
void onSave(String s) {
|
||||||
|
final val = double.tryParse(s);
|
||||||
|
if (val == null) {
|
||||||
|
context.showSnackBar(libL10n.fail);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const minRadius = 0.0;
|
||||||
|
const maxBlur = 50.0;
|
||||||
|
final clampedVal = val.clamp(minRadius, maxBlur);
|
||||||
|
_setting.sshBlurRadius.put(clampedVal);
|
||||||
|
context.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(Icons.blur_on),
|
||||||
|
title: Text(libL10n.blurRadius),
|
||||||
|
trailing: ValBuilder(
|
||||||
|
listenable: _setting.sshBlurRadius.listenable(),
|
||||||
|
builder: (val) => Text(val.toString(), style: UIs.text15),
|
||||||
|
),
|
||||||
|
onTap: () => context.showRoundDialog(
|
||||||
|
title: libL10n.blurRadius,
|
||||||
|
child: Input(
|
||||||
|
controller: _sshBlurCtrl,
|
||||||
|
autoFocus: true,
|
||||||
|
type: TextInputType.number,
|
||||||
|
hint: '0',
|
||||||
|
icon: Icons.blur_on,
|
||||||
|
suggestion: false,
|
||||||
|
onSubmitted: onSave,
|
||||||
|
),
|
||||||
|
actions: Btn.ok(onTap: () => onSave(_sshBlurCtrl.text)).toList,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,10 +37,7 @@ const _kIconSize = 23.0;
|
|||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({super.key});
|
||||||
|
|
||||||
static const route = AppRouteNoArg(
|
static const route = AppRouteNoArg(page: SettingsPage.new, path: '/settings');
|
||||||
page: SettingsPage.new,
|
|
||||||
path: '/settings',
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SettingsPage> createState() => _SettingsPageState();
|
State<SettingsPage> createState() => _SettingsPageState();
|
||||||
@@ -70,19 +67,15 @@ class _SettingsPageState extends State<SettingsPage> with SingleTickerProviderSt
|
|||||||
actions: [
|
actions: [
|
||||||
Btn.text(
|
Btn.text(
|
||||||
text: 'Logs',
|
text: 'Logs',
|
||||||
onTap: () => DebugPage.route.go(
|
onTap: () =>
|
||||||
context,
|
DebugPage.route.go(context, args: const DebugPageArgs(title: 'Logs(${BuildData.build})')),
|
||||||
args: const DebugPageArgs(title: 'Logs(${BuildData.build})'),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Btn.icon(
|
Btn.icon(
|
||||||
icon: const Icon(Icons.delete),
|
icon: const Icon(Icons.delete),
|
||||||
onTap: () => context.showRoundDialog(
|
onTap: () => context.showRoundDialog(
|
||||||
title: libL10n.attention,
|
title: libL10n.attention,
|
||||||
child: SimpleMarkdown(
|
child: SimpleMarkdown(
|
||||||
data: libL10n.askContinue(
|
data: libL10n.askContinue('${libL10n.delete} **${libL10n.all}** ${libL10n.setting}'),
|
||||||
'${libL10n.delete} **${libL10n.all}** ${libL10n.setting}',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
CountDownBtn(
|
CountDownBtn(
|
||||||
@@ -93,7 +86,7 @@ class _SettingsPageState extends State<SettingsPage> with SingleTickerProviderSt
|
|||||||
context.showSnackBar(libL10n.success);
|
context.showSnackBar(libL10n.success);
|
||||||
},
|
},
|
||||||
afterColor: Colors.red,
|
afterColor: Colors.red,
|
||||||
)
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -114,6 +107,13 @@ final class AppSettingsPage extends StatefulWidget {
|
|||||||
final class _AppSettingsPageState extends State<AppSettingsPage> {
|
final class _AppSettingsPageState extends State<AppSettingsPage> {
|
||||||
final _setting = Stores.setting;
|
final _setting = Stores.setting;
|
||||||
|
|
||||||
|
late final _sshOpacityCtrl = TextEditingController(text: _setting.sshBgOpacity.fetch().toString());
|
||||||
|
late final _sshBlurCtrl = TextEditingController(text: _setting.sshBlurRadius.fetch().toString());
|
||||||
|
late final _textScalerCtrl = TextEditingController(text: _setting.textFactor.toString());
|
||||||
|
|
||||||
|
late final _editorTextSizeCtrl = TextEditingController(text: _setting.editorFontSize.get().toString());
|
||||||
|
late final _serverLogoCtrl = TextEditingController(text: _setting.serverLogoUrl.fetch());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiList(
|
return MultiList(
|
||||||
@@ -121,12 +121,7 @@ final class _AppSettingsPageState extends State<AppSettingsPage> {
|
|||||||
[const CenterGreyTitle('App'), _buildApp()],
|
[const CenterGreyTitle('App'), _buildApp()],
|
||||||
[CenterGreyTitle(l10n.server), _buildServer()],
|
[CenterGreyTitle(l10n.server), _buildServer()],
|
||||||
[const CenterGreyTitle('SSH'), _buildSSH(), const CenterGreyTitle('SFTP'), _buildSFTP()],
|
[const CenterGreyTitle('SSH'), _buildSSH(), const CenterGreyTitle('SFTP'), _buildSFTP()],
|
||||||
[
|
[CenterGreyTitle(l10n.container), _buildContainer(), CenterGreyTitle(l10n.editor), _buildEditor()],
|
||||||
CenterGreyTitle(l10n.container),
|
|
||||||
_buildContainer(),
|
|
||||||
CenterGreyTitle(l10n.editor),
|
|
||||||
_buildEditor(),
|
|
||||||
],
|
|
||||||
|
|
||||||
/// Fullscreen Mode is designed for old mobile phone which can be
|
/// Fullscreen Mode is designed for old mobile phone which can be
|
||||||
/// used as a status screen.
|
/// used as a status screen.
|
||||||
@@ -140,8 +135,7 @@ enum SettingsTabs {
|
|||||||
app,
|
app,
|
||||||
privateKey,
|
privateKey,
|
||||||
backup,
|
backup,
|
||||||
about,
|
about;
|
||||||
;
|
|
||||||
|
|
||||||
String get i18n => switch (this) {
|
String get i18n => switch (this) {
|
||||||
SettingsTabs.app => libL10n.app,
|
SettingsTabs.app => libL10n.app,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
@@ -144,6 +146,8 @@ class SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin, Af
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context);
|
super.build(context);
|
||||||
|
final bgImage = Stores.setting.sshBgImage.fetch();
|
||||||
|
final hasBg = bgImage.isNotEmpty;
|
||||||
Widget child = PopScope(
|
Widget child = PopScope(
|
||||||
canPop: false,
|
canPop: false,
|
||||||
onPopInvokedWithResult: (didPop, _) {
|
onPopInvokedWithResult: (didPop, _) {
|
||||||
@@ -156,7 +160,7 @@ class SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin, Af
|
|||||||
actions: [_buildCopyBtn, _buildKillBtn],
|
actions: [_buildCopyBtn, _buildKillBtn],
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
),
|
),
|
||||||
backgroundColor: _terminalTheme.background,
|
backgroundColor: hasBg ? Colors.transparent : _terminalTheme.background,
|
||||||
body: _buildBody(),
|
body: _buildBody(),
|
||||||
bottomNavigationBar: isDesktop ? null : _buildBottom(),
|
bottomNavigationBar: isDesktop ? null : _buildBottom(),
|
||||||
),
|
),
|
||||||
@@ -173,14 +177,38 @@ class SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin, Af
|
|||||||
|
|
||||||
Widget _buildBody() {
|
Widget _buildBody() {
|
||||||
final letterCache = Stores.setting.letterCache.fetch();
|
final letterCache = Stores.setting.letterCache.fetch();
|
||||||
return SizedBox(
|
final bgImage = Stores.setting.sshBgImage.fetch();
|
||||||
height: _media.size.height - _virtKeysHeight - _media.padding.bottom - _media.padding.top,
|
final opacity = Stores.setting.sshBgOpacity.fetch();
|
||||||
child: Padding(
|
final blur = Stores.setting.sshBlurRadius.fetch();
|
||||||
padding: EdgeInsets.only(
|
final file = File(bgImage);
|
||||||
// top: widget.args.notFromTab ? CustomAppBar.sysStatusBarHeight : 0,
|
final hasBg = bgImage.isNotEmpty && file.existsSync();
|
||||||
left: _horizonPadding,
|
final theme = hasBg ? _terminalTheme.copyWith(background: Colors.transparent) : _terminalTheme;
|
||||||
right: _horizonPadding,
|
final children = <Widget>[];
|
||||||
|
if (hasBg) {
|
||||||
|
children.add(
|
||||||
|
Positioned.fill(
|
||||||
|
child: Image.file(file, fit: BoxFit.cover, errorBuilder: (_, __, ___) => const SizedBox()),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
if (blur > 0) {
|
||||||
|
children.add(
|
||||||
|
Positioned.fill(
|
||||||
|
child: BackdropFilter(
|
||||||
|
filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur),
|
||||||
|
child: const SizedBox(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
children.add(
|
||||||
|
Positioned.fill(
|
||||||
|
child: ColoredBox(color: _terminalTheme.background.withValues(alpha: opacity)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
children.add(
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: _horizonPadding, right: _horizonPadding),
|
||||||
child: TerminalView(
|
child: TerminalView(
|
||||||
_terminal,
|
_terminal,
|
||||||
key: _termKey,
|
key: _termKey,
|
||||||
@@ -188,7 +216,8 @@ class SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin, Af
|
|||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
enableSuggestions: letterCache,
|
enableSuggestions: letterCache,
|
||||||
textStyle: _terminalStyle,
|
textStyle: _terminalStyle,
|
||||||
theme: _terminalTheme,
|
backgroundOpacity: 0,
|
||||||
|
theme: theme,
|
||||||
deleteDetection: isMobile,
|
deleteDetection: isMobile,
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
keyboardAppearance: _isDark ? Brightness.dark : Brightness.light,
|
keyboardAppearance: _isDark ? Brightness.dark : Brightness.light,
|
||||||
@@ -199,17 +228,21 @@ class SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin, Af
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return SizedBox(
|
||||||
|
height: _media.size.height - _virtKeysHeight - _media.padding.bottom - _media.padding.top,
|
||||||
|
child: Stack(children: children),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBottom() {
|
Widget _buildBottom() {
|
||||||
return SafeArea(
|
return AnimatedPadding(
|
||||||
child: AnimatedPadding(
|
|
||||||
padding: _media.viewInsets,
|
padding: _media.viewInsets,
|
||||||
duration: const Duration(milliseconds: 23),
|
duration: const Duration(milliseconds: 23),
|
||||||
curve: Curves.fastOutSlowIn,
|
curve: Curves.fastOutSlowIn,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: _terminalTheme.background,
|
color: _terminalTheme.background,
|
||||||
height: _virtKeysHeight,
|
height: _virtKeysHeight + _media.padding.bottom,
|
||||||
child: ChangeNotifierProvider(
|
child: ChangeNotifierProvider(
|
||||||
create: (_) => _keyboard,
|
create: (_) => _keyboard,
|
||||||
builder: (_, __) => Consumer<VirtKeyProvider>(
|
builder: (_, __) => Consumer<VirtKeyProvider>(
|
||||||
@@ -219,7 +252,6 @@ class SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin, Af
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -497,8 +497,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "v1.0.315"
|
ref: "v1.0.316"
|
||||||
resolved-ref: "8593608e8f78ea53f2b1fdad5d5395392870c0d3"
|
resolved-ref: "93da3ff5e042f9f0d29ace7b1ffeff5716fe2c81"
|
||||||
url: "https://github.com/lppcg/fl_lib"
|
url: "https://github.com/lppcg/fl_lib"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
version: "0.0.1"
|
||||||
@@ -1774,8 +1774,8 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: "v1.0.586"
|
ref: "v1.0.587"
|
||||||
resolved-ref: b8c73bec722055f24c5724cd2f2297859e95b6af
|
resolved-ref: "0f5027286380e3829ad2696d69d5a1ecf68eb875"
|
||||||
url: "https://github.com/lollipopkit/xterm.dart"
|
url: "https://github.com/lollipopkit/xterm.dart"
|
||||||
source: git
|
source: git
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ dependencies:
|
|||||||
xterm:
|
xterm:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/lollipopkit/xterm.dart
|
url: https://github.com/lollipopkit/xterm.dart
|
||||||
ref: v1.0.586
|
ref: v1.0.587
|
||||||
computer:
|
computer:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/lollipopkit/dart_computer
|
url: https://github.com/lollipopkit/dart_computer
|
||||||
@@ -62,7 +62,7 @@ dependencies:
|
|||||||
fl_lib:
|
fl_lib:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/lppcg/fl_lib
|
url: https://github.com/lppcg/fl_lib
|
||||||
ref: v1.0.315
|
ref: v1.0.316
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
# webdav_client_plus:
|
# webdav_client_plus:
|
||||||
|
|||||||
Reference in New Issue
Block a user