feat: SSH page background (#775)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-06-05 08:53:00 +08:00
committed by GitHub
parent 176cb7da03
commit 4701757857
10 changed files with 289 additions and 166 deletions

View File

@@ -9,6 +9,7 @@ extension _SSH on _AppSettingsPageState {
_buildTermTheme(),
_buildFont(),
_buildTermFontSize(),
_buildSshBg(),
if (isDesktop) _buildDesktopTerminal(),
_buildSSHVirtualKeyAutoOff(),
if (isMobile) _buildSSHVirtKeys(),
@@ -46,10 +47,7 @@ extension _SSH on _AppSettingsPageState {
context.showRoundDialog(
title: l10n.font,
actions: [
TextButton(
onPressed: () async => await _pickFontFile(),
child: Text(libL10n.file),
),
TextButton(onPressed: () async => await _pickFontFile(), child: Text(libL10n.file)),
TextButton(
onPressed: () {
_setting.fontPath.delete();
@@ -81,17 +79,31 @@ extension _SSH on _AppSettingsPageState {
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() {
return _setting.desktopTerminal.listenable().listenVal((val) {
return ListTile(
leading: const Icon(Icons.terminal),
title: TipText(l10n.terminal, l10n.desktopTerminalTip),
trailing: Text(
val,
style: UIs.text15,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
trailing: Text(val, style: UIs.text15, maxLines: 1, overflow: TextOverflow.ellipsis),
onTap: () async {
final ctrl = TextEditingController(text: val);
void onSave() {
@@ -99,7 +111,7 @@ extension _SSH on _AppSettingsPageState {
context.pop();
}
context.showRoundDialog<bool>(
await context.showRoundDialog<bool>(
title: libL10n.select,
child: Input(
controller: ctrl,
@@ -112,6 +124,7 @@ extension _SSH on _AppSettingsPageState {
),
actions: Btn.ok(onTap: onSave).toList,
);
ctrl.dispose();
},
);
});
@@ -168,11 +181,114 @@ extension _SSH on _AppSettingsPageState {
// '${l10n.letterCacheTip}\n${l10n.needRestart}',
// style: UIs.textGrey,
// ),
title: TipText(
l10n.letterCache,
'${l10n.letterCacheTip}\n${l10n.needRestart}',
),
title: TipText(l10n.letterCache, '${l10n.letterCacheTip}\n${l10n.needRestart}'),
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,
),
);
}
}