dialog focus && deal with Enter/Esc key

Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
21pages
2022-09-03 18:19:50 +08:00
parent 1b56304d9a
commit a553334157
12 changed files with 959 additions and 859 deletions

View File

@@ -7,6 +7,7 @@ import 'package:back_button_interceptor/back_button_interceptor.dart';
import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:desktop_multi_window/desktop_multi_window.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
import 'package:flutter_hbb/models/peer_model.dart'; import 'package:flutter_hbb/models/peer_model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@@ -340,34 +341,41 @@ class OverlayDialogManager {
{bool clickMaskDismiss = false, {bool clickMaskDismiss = false,
bool showCancel = true, bool showCancel = true,
VoidCallback? onCancel}) { VoidCallback? onCancel}) {
show((setState, close) => CustomAlertDialog( show((setState, close) {
cancel() {
dismissAll();
if (onCancel != null) {
onCancel();
}
}
return CustomAlertDialog(
content: Container( content: Container(
constraints: BoxConstraints(maxWidth: 240), constraints: const BoxConstraints(maxWidth: 240),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox(height: 30), const SizedBox(height: 30),
Center(child: CircularProgressIndicator()), const Center(child: CircularProgressIndicator()),
SizedBox(height: 20), const SizedBox(height: 20),
Center( Center(
child: Text(translate(text), child: Text(translate(text),
style: TextStyle(fontSize: 15))), style: const TextStyle(fontSize: 15))),
SizedBox(height: 20), const SizedBox(height: 20),
Offstage( Offstage(
offstage: !showCancel, offstage: !showCancel,
child: Center( child: Center(
child: TextButton( child: TextButton(
style: flatButtonStyle, style: flatButtonStyle,
onPressed: () { onPressed: cancel,
dismissAll();
if (onCancel != null) {
onCancel();
}
},
child: Text(translate('Cancel'), child: Text(translate('Cancel'),
style: TextStyle(color: MyTheme.accent))))) style:
])))); const TextStyle(color: MyTheme.accent)))))
])),
onCancel: showCancel ? cancel : null,
);
});
} }
} }
@@ -377,18 +385,18 @@ void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) {
final entry = OverlayEntry(builder: (_) { final entry = OverlayEntry(builder: (_) {
return IgnorePointer( return IgnorePointer(
child: Align( child: Align(
alignment: Alignment(0.0, 0.8), alignment: const Alignment(0.0, 0.8),
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.black.withOpacity(0.6), color: Colors.black.withOpacity(0.6),
borderRadius: BorderRadius.all( borderRadius: const BorderRadius.all(
Radius.circular(20), Radius.circular(20),
), ),
), ),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 5),
child: Text( child: Text(
text, text,
style: TextStyle( style: const TextStyle(
decoration: TextDecoration.none, decoration: TextDecoration.none,
fontWeight: FontWeight.w300, fontWeight: FontWeight.w300,
fontSize: 18, fontSize: 18,
@@ -403,23 +411,54 @@ void showToast(String text, {Duration timeout = const Duration(seconds: 2)}) {
} }
class CustomAlertDialog extends StatelessWidget { class CustomAlertDialog extends StatelessWidget {
CustomAlertDialog( const CustomAlertDialog(
{this.title, required this.content, this.actions, this.contentPadding}); {Key? key,
this.title,
required this.content,
this.actions,
this.contentPadding,
this.onSubmit,
this.onCancel})
: super(key: key);
final Widget? title; final Widget? title;
final Widget content; final Widget content;
final List<Widget>? actions; final List<Widget>? actions;
final double? contentPadding; final double? contentPadding;
final Function()? onSubmit;
final Function()? onCancel;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertDialog( FocusNode focusNode = FocusNode();
scrollable: true, // request focus if there is no focused FocusNode in the dialog
title: title, Future.delayed(Duration.zero, () {
contentPadding: if (!focusNode.hasFocus) focusNode.requestFocus();
EdgeInsets.symmetric(horizontal: contentPadding ?? 25, vertical: 10), });
content: content, return Focus(
actions: actions, focusNode: focusNode,
autofocus: true,
onKey: (node, key) {
if (key.logicalKey == LogicalKeyboardKey.escape) {
if (key is RawKeyDownEvent) {
onCancel?.call();
}
return KeyEventResult.handled; // avoid TextField exception on escape
} else if (onSubmit != null &&
key.logicalKey == LogicalKeyboardKey.enter) {
if (key is RawKeyDownEvent) onSubmit?.call();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
child: AlertDialog(
scrollable: true,
title: title,
contentPadding: EdgeInsets.symmetric(
horizontal: contentPadding ?? 25, vertical: 10),
content: content,
actions: actions,
),
); );
} }
} }
@@ -429,26 +468,28 @@ void msgBox(
{bool? hasCancel}) { {bool? hasCancel}) {
dialogManager.dismissAll(); dialogManager.dismissAll();
List<Widget> buttons = []; List<Widget> buttons = [];
bool hasOk = false;
submit() {
dialogManager.dismissAll();
// https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263
if (!type.contains("custom")) {
closeConnection();
}
}
cancel() {
dialogManager.dismissAll();
}
if (type != "connecting" && type != "success" && !type.contains("nook")) { if (type != "connecting" && type != "success" && !type.contains("nook")) {
buttons.insert( hasOk = true;
0, buttons.insert(0, msgBoxButton(translate('OK'), submit));
msgBoxButton(translate('OK'), () {
dialogManager.dismissAll();
// https://github.com/fufesou/rustdesk/blob/5e9a31340b899822090a3731769ae79c6bf5f3e5/src/ui/common.tis#L263
if (!type.contains("custom")) {
closeConnection();
}
}));
} }
hasCancel ??= !type.contains("error") && hasCancel ??= !type.contains("error") &&
!type.contains("nocancel") && !type.contains("nocancel") &&
type != "restarting"; type != "restarting";
if (hasCancel) { if (hasCancel) {
buttons.insert( buttons.insert(0, msgBoxButton(translate('Cancel'), cancel));
0,
msgBoxButton(translate('Cancel'), () {
dialogManager.dismissAll();
}));
} }
// TODO: test this button // TODO: test this button
if (type.contains("hasclose")) { if (type.contains("hasclose")) {
@@ -459,9 +500,12 @@ void msgBox(
})); }));
} }
dialogManager.show((setState, close) => CustomAlertDialog( dialogManager.show((setState, close) => CustomAlertDialog(
title: _msgBoxTitle(title), title: _msgBoxTitle(title),
content: Text(translate(text), style: TextStyle(fontSize: 15)), content: Text(translate(text), style: const TextStyle(fontSize: 15)),
actions: buttons)); actions: buttons,
onSubmit: hasOk ? submit : null,
onCancel: hasCancel == true ? cancel : null,
));
} }
Widget msgBoxButton(String text, void Function() onPressed) { Widget msgBoxButton(String text, void Function() onPressed) {
@@ -479,15 +523,19 @@ Widget msgBoxButton(String text, void Function() onPressed) {
Text(translate(text), style: TextStyle(color: MyTheme.accent)))); Text(translate(text), style: TextStyle(color: MyTheme.accent))));
} }
Widget _msgBoxTitle(String title) => Text(translate(title), style: TextStyle(fontSize: 21)); Widget _msgBoxTitle(String title) =>
Text(translate(title), style: TextStyle(fontSize: 21));
void msgBoxCommon(OverlayDialogManager dialogManager, String title, void msgBoxCommon(OverlayDialogManager dialogManager, String title,
Widget content, List<Widget> buttons) { Widget content, List<Widget> buttons,
{bool hasCancel = true}) {
dialogManager.dismissAll(); dialogManager.dismissAll();
dialogManager.show((setState, close) => CustomAlertDialog( dialogManager.show((setState, close) => CustomAlertDialog(
title: _msgBoxTitle(title), title: _msgBoxTitle(title),
content: content, content: content,
actions: buttons)); actions: buttons,
onCancel: hasCancel ? close : null,
));
} }
Color str2color(String str, [alpha = 0xFF]) { Color str2color(String str, [alpha = 0xFF]) {

View File

@@ -167,7 +167,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
}); });
var w = Container( var w = Container(
width: 320 + 20 * 2, width: 320 + 20 * 2,
padding: EdgeInsets.fromLTRB(20, 24, 20, 22), padding: const EdgeInsets.fromLTRB(20, 24, 20, 22),
decoration: BoxDecoration( decoration: BoxDecoration(
color: MyTheme.color(context).bg, color: MyTheme.color(context).bg,
borderRadius: const BorderRadius.all(Radius.circular(13)), borderRadius: const BorderRadius.all(Radius.circular(13)),
@@ -179,7 +179,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
children: [ children: [
Text( Text(
translate('Control Remote Desktop'), translate('Control Remote Desktop'),
style: TextStyle(fontSize: 19, height: 1), style: const TextStyle(fontSize: 19, height: 1),
), ),
], ],
).marginOnly(bottom: 15), ).marginOnly(bottom: 15),
@@ -192,11 +192,13 @@ class _ConnectionPageState extends State<ConnectionPage> {
enableSuggestions: false, enableSuggestions: false,
keyboardType: TextInputType.visiblePassword, keyboardType: TextInputType.visiblePassword,
focusNode: focusNode, focusNode: focusNode,
style: TextStyle( style: const TextStyle(
fontFamily: 'WorkSans', fontFamily: 'WorkSans',
fontSize: 22, fontSize: 22,
height: 1, height: 1,
), ),
maxLines: 1,
cursorColor: MyTheme.color(context).text!,
decoration: InputDecoration( decoration: InputDecoration(
hintText: inputFocused.value hintText: inputFocused.value
? null ? null
@@ -206,14 +208,18 @@ class _ConnectionPageState extends State<ConnectionPage> {
border: OutlineInputBorder( border: OutlineInputBorder(
borderRadius: BorderRadius.zero, borderRadius: BorderRadius.zero,
borderSide: BorderSide( borderSide: BorderSide(
color: MyTheme.color(context).placeholder!)), color: MyTheme.color(context).border!)),
focusedBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.zero,
borderSide: BorderSide(
color: MyTheme.color(context).border!)),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.zero, borderRadius: BorderRadius.zero,
borderSide: borderSide:
BorderSide(color: MyTheme.button, width: 3), BorderSide(color: MyTheme.button, width: 3),
), ),
isDense: true, isDense: true,
contentPadding: EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(
horizontal: 10, vertical: 12)), horizontal: 10, vertical: 12)),
controller: _idController, controller: _idController,
inputFormatters: [IDTextInputFormatter()], inputFormatters: [IDTextInputFormatter()],
@@ -266,7 +272,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
).marginSymmetric(horizontal: 12), ).marginSymmetric(horizontal: 12),
), ),
)), )),
SizedBox( const SizedBox(
width: 17, width: 17,
), ),
Obx( Obx(
@@ -311,7 +317,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
), ),
); );
return Center( return Center(
child: Container(constraints: BoxConstraints(maxWidth: 600), child: w)); child: Container(
constraints: const BoxConstraints(maxWidth: 600), child: w));
} }
@override @override
@@ -661,71 +668,69 @@ class _ConnectionPageState extends State<ConnectionPage> {
var field = ""; var field = "";
var msg = ""; var msg = "";
var isInProgress = false; var isInProgress = false;
TextEditingController controller = TextEditingController(text: field);
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
msg = "";
isInProgress = true;
});
field = controller.text.trim();
if (field.isEmpty) {
// pass
} else {
final ids = field.trim().split(RegExp(r"[\s,;\n]+"));
field = ids.join(',');
for (final newId in ids) {
if (gFFI.abModel.idContainBy(newId)) {
continue;
}
gFFI.abModel.addId(newId);
}
await gFFI.abModel.updateAb();
this.setState(() {});
// final currentPeers
}
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Add ID")), title: Text(translate("Add ID")),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(translate("whitelist_sep")), Text(translate("whitelist_sep")),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: TextField( child: TextField(
onChanged: (s) { maxLines: null,
field = s; decoration: InputDecoration(
}, border: const OutlineInputBorder(),
maxLines: null, errorText: msg.isEmpty ? null : translate(msg),
decoration: InputDecoration( ),
border: OutlineInputBorder(), controller: controller,
errorText: msg.isEmpty ? null : translate(msg), focusNode: FocusNode()..requestFocus()),
),
controller: TextEditingController(text: field),
),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
msg = "";
isInProgress = true;
});
field = field.trim();
if (field.isEmpty) {
// pass
} else {
final ids = field.trim().split(RegExp(r"[\s,;\n]+"));
field = ids.join(',');
for (final newId in ids) {
if (gFFI.abModel.idContainBy(newId)) {
continue;
}
gFFI.abModel.addId(newId);
}
await gFFI.abModel.updateAb();
this.setState(() {});
// final currentPeers
}
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }
@@ -734,67 +739,65 @@ class _ConnectionPageState extends State<ConnectionPage> {
var field = ""; var field = "";
var msg = ""; var msg = "";
var isInProgress = false; var isInProgress = false;
TextEditingController controller = TextEditingController(text: field);
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
msg = "";
isInProgress = true;
});
field = controller.text.trim();
if (field.isEmpty) {
// pass
} else {
final tags = field.trim().split(RegExp(r"[\s,;\n]+"));
field = tags.join(',');
for (final tag in tags) {
gFFI.abModel.addTag(tag);
}
await gFFI.abModel.updateAb();
// final currentPeers
}
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Add Tag")), title: Text(translate("Add Tag")),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(translate("whitelist_sep")), Text(translate("whitelist_sep")),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: TextField( child: TextField(
onChanged: (s) {
field = s;
},
maxLines: null, maxLines: null,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: msg.isEmpty ? null : translate(msg), errorText: msg.isEmpty ? null : translate(msg),
), ),
controller: TextEditingController(text: field), controller: controller,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
msg = "";
isInProgress = true;
});
field = field.trim();
if (field.isEmpty) {
// pass
} else {
final tags = field.trim().split(RegExp(r"[\s,;\n]+"));
field = tags.join(',');
for (final tag in tags) {
gFFI.abModel.addTag(tag);
}
await gFFI.abModel.updateAb();
// final currentPeers
}
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }
@@ -806,13 +809,23 @@ class _ConnectionPageState extends State<ConnectionPage> {
var selectedTag = gFFI.abModel.getPeerTags(id).obs; var selectedTag = gFFI.abModel.getPeerTags(id).obs;
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
isInProgress = true;
});
gFFI.abModel.changeTagForPeer(id, selectedTag);
await gFFI.abModel.updateAb();
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Edit Tag")), title: Text(translate("Edit Tag")),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Wrap( child: Wrap(
children: tags children: tags
.map((e) => buildTag(e, selectedTag, onTap: () { .map((e) => buildTag(e, selectedTag, onTap: () {
@@ -825,26 +838,16 @@ class _ConnectionPageState extends State<ConnectionPage> {
.toList(growable: false), .toList(growable: false),
), ),
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
isInProgress = true;
});
gFFI.abModel.changeTagForPeer(id, selectedTag);
await gFFI.abModel.updateAb();
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }

View File

@@ -55,7 +55,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
return Row( return Row(
children: [ children: [
buildServerInfo(context), buildServerInfo(context),
VerticalDivider( const VerticalDivider(
width: 1, width: 1,
thickness: 1, thickness: 1,
), ),
@@ -93,7 +93,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
buildIDBoard(BuildContext context) { buildIDBoard(BuildContext context) {
final model = gFFI.serverModel; final model = gFFI.serverModel;
return Container( return Container(
margin: EdgeInsets.only(left: 20, right: 16), margin: const EdgeInsets.only(left: 20, right: 16),
height: 52, height: 52,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.baseline, crossAxisAlignment: CrossAxisAlignment.baseline,
@@ -101,7 +101,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
children: [ children: [
Container( Container(
width: 2, width: 2,
decoration: BoxDecoration(color: MyTheme.accent), decoration: const BoxDecoration(color: MyTheme.accent),
), ),
Expanded( Expanded(
child: Padding( child: Padding(
@@ -109,7 +109,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( SizedBox(
height: 25, height: 25,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@@ -135,11 +135,11 @@ class _DesktopHomePageState extends State<DesktopHomePage>
child: TextFormField( child: TextFormField(
controller: model.serverId, controller: model.serverId,
readOnly: true, readOnly: true,
decoration: InputDecoration( decoration: const InputDecoration(
border: InputBorder.none, border: InputBorder.none,
contentPadding: EdgeInsets.only(bottom: 18), contentPadding: EdgeInsets.only(bottom: 18),
), ),
style: TextStyle( style: const TextStyle(
fontSize: 22, fontSize: 22,
), ),
), ),
@@ -642,76 +642,76 @@ class _DesktopHomePageState extends State<DesktopHomePage>
var newId = ""; var newId = "";
var msg = ""; var msg = "";
var isInProgress = false; var isInProgress = false;
TextEditingController controller = TextEditingController();
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
newId = controller.text.trim();
setState(() {
msg = "";
isInProgress = true;
bind.mainChangeId(newId: newId);
});
var status = await bind.mainGetAsyncStatus();
while (status == " ") {
await Future.delayed(const Duration(milliseconds: 100));
status = await bind.mainGetAsyncStatus();
}
if (status.isEmpty) {
// ok
close();
return;
}
setState(() {
isInProgress = false;
msg = translate(status);
});
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Change ID")), title: Text(translate("Change ID")),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(translate("id_change_tip")), Text(translate("id_change_tip")),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
Text("ID:").marginOnly(bottom: 16.0), const Text("ID:").marginOnly(bottom: 16.0),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
onChanged: (s) {
newId = s;
},
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: msg.isEmpty ? null : translate(msg)), errorText: msg.isEmpty ? null : translate(msg)),
inputFormatters: [ inputFormatters: [
LengthLimitingTextInputFormatter(16), LengthLimitingTextInputFormatter(16),
// FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true) // FilteringTextInputFormatter(RegExp(r"[a-zA-z][a-zA-z0-9\_]*"), allow: true)
], ],
maxLength: 16, maxLength: 16,
controller: controller,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
msg = "";
isInProgress = true;
bind.mainChangeId(newId: newId);
});
var status = await bind.mainGetAsyncStatus();
while (status == " ") {
await Future.delayed(Duration(milliseconds: 100));
status = await bind.mainGetAsyncStatus();
}
if (status.isEmpty) {
// ok
close();
return;
}
setState(() {
isInProgress = false;
msg = translate(status);
});
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }
@@ -720,16 +720,16 @@ class _DesktopHomePageState extends State<DesktopHomePage>
final appName = await bind.mainGetAppName(); final appName = await bind.mainGetAppName();
final license = await bind.mainGetLicense(); final license = await bind.mainGetLicense();
final version = await bind.mainGetVersion(); final version = await bind.mainGetVersion();
final linkStyle = TextStyle(decoration: TextDecoration.underline); const linkStyle = TextStyle(decoration: TextDecoration.underline);
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
return CustomAlertDialog( return CustomAlertDialog(
title: Text("About $appName"), title: Text("About $appName"),
content: ConstrainedBox( content: ConstrainedBox(
constraints: BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Text("Version: $version").marginSymmetric(vertical: 4.0), Text("Version: $version").marginSymmetric(vertical: 4.0),
@@ -737,7 +737,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
onTap: () { onTap: () {
launchUrlString("https://rustdesk.com/privacy"); launchUrlString("https://rustdesk.com/privacy");
}, },
child: Text( child: const Text(
"Privacy Statement", "Privacy Statement",
style: linkStyle, style: linkStyle,
).marginSymmetric(vertical: 4.0)), ).marginSymmetric(vertical: 4.0)),
@@ -745,13 +745,14 @@ class _DesktopHomePageState extends State<DesktopHomePage>
onTap: () { onTap: () {
launchUrlString("https://rustdesk.com"); launchUrlString("https://rustdesk.com");
}, },
child: Text( child: const Text(
"Website", "Website",
style: linkStyle, style: linkStyle,
).marginSymmetric(vertical: 4.0)), ).marginSymmetric(vertical: 4.0)),
Container( Container(
decoration: BoxDecoration(color: Color(0xFF2c8cff)), decoration: const BoxDecoration(color: Color(0xFF2c8cff)),
padding: EdgeInsets.symmetric(vertical: 24, horizontal: 8), padding:
const EdgeInsets.symmetric(vertical: 24, horizontal: 8),
child: Row( child: Row(
children: [ children: [
Expanded( Expanded(
@@ -760,9 +761,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
children: [ children: [
Text( Text(
"Copyright &copy; 2022 Purslane Ltd.\n$license", "Copyright &copy; 2022 Purslane Ltd.\n$license",
style: TextStyle(color: Colors.white), style: const TextStyle(color: Colors.white),
), ),
Text( const Text(
"Made with heart in this chaotic world!", "Made with heart in this chaotic world!",
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w800, fontWeight: FontWeight.w800,
@@ -778,12 +779,10 @@ class _DesktopHomePageState extends State<DesktopHomePage>
), ),
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("OK"))),
onPressed: () async {
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: close,
onCancel: close,
); );
}); });
} }
@@ -815,118 +814,124 @@ Future<bool> loginDialog() async {
var isInProgress = false; var isInProgress = false;
var completer = Completer<bool>(); var completer = Completer<bool>();
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
userNameMsg = "";
passMsg = "";
isInProgress = true;
});
cancel() {
setState(() {
isInProgress = false;
});
}
userName = userContontroller.text;
pass = pwdController.text;
if (userName.isEmpty) {
userNameMsg = translate("Username missed");
cancel();
return;
}
if (pass.isEmpty) {
passMsg = translate("Password missed");
cancel();
return;
}
try {
final resp = await gFFI.userModel.login(userName, pass);
if (resp.containsKey('error')) {
passMsg = resp['error'];
cancel();
return;
}
// {access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDFkZjQ2ZjgtZjg3OS00MDE0LTk5Y2QtMGMwYzM2MmViZGJlIiwiZXhwIjoxNjYxNDg2NzYwfQ.GZpe1oI8TfM5yTYNrpcwbI599P4Z_-b2GmnwNl2Lr-w,
// token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}}
debugPrint("$resp");
completer.complete(true);
} catch (err) {
// ignore: avoid_print
print(err.toString());
cancel();
return;
}
close();
}
cancel() {
completer.complete(false);
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Login")), title: Text(translate("Login")),
content: ConstrainedBox( content: ConstrainedBox(
constraints: BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text( child: Text(
"${translate('Username')}:", "${translate('Username')}:",
textAlign: TextAlign.start, textAlign: TextAlign.start,
).marginOnly(bottom: 16.0)), ).marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: userNameMsg.isNotEmpty ? userNameMsg : null), errorText: userNameMsg.isNotEmpty ? userNameMsg : null),
controller: userContontroller, controller: userContontroller,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Password')}:") child: Text("${translate('Password')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
obscureText: true, obscureText: true,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: passMsg.isNotEmpty ? passMsg : null), errorText: passMsg.isNotEmpty ? passMsg : null),
controller: pwdController, controller: pwdController,
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: cancel, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
completer.complete(false);
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
userNameMsg = "";
passMsg = "";
isInProgress = true;
});
final cancel = () {
setState(() {
isInProgress = false;
});
};
userName = userContontroller.text;
pass = pwdController.text;
if (userName.isEmpty) {
userNameMsg = translate("Username missed");
cancel();
return;
}
if (pass.isEmpty) {
passMsg = translate("Password missed");
cancel();
return;
}
try {
final resp = await gFFI.userModel.login(userName, pass);
if (resp.containsKey('error')) {
passMsg = resp['error'];
cancel();
return;
}
// {access_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJndWlkIjoiMDFkZjQ2ZjgtZjg3OS00MDE0LTk5Y2QtMGMwYzM2MmViZGJlIiwiZXhwIjoxNjYxNDg2NzYwfQ.GZpe1oI8TfM5yTYNrpcwbI599P4Z_-b2GmnwNl2Lr-w,
// token_type: Bearer, user: {id: , name: admin, email: null, note: null, status: null, grp: null, is_admin: true}}
debugPrint("$resp");
completer.complete(true);
} catch (err) {
print(err.toString());
cancel();
return;
}
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: cancel,
); );
}); });
return completer.future; return completer.future;
@@ -940,55 +945,78 @@ void setPasswordDialog() async {
var errMsg1 = ""; var errMsg1 = "";
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() {
setState(() {
errMsg0 = "";
errMsg1 = "";
});
final pass = p0.text.trim();
if (pass.length < 6) {
setState(() {
errMsg0 = translate("Too short, at least 6 characters.");
});
return;
}
if (p1.text.trim() != pass) {
setState(() {
errMsg1 = translate("The confirmation is not identical.");
});
return;
}
bind.mainSetPermanentPassword(password: pass);
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Set Password")), title: Text(translate("Set Password")),
content: ConstrainedBox( content: ConstrainedBox(
constraints: BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text( child: Text(
"${translate('Password')}:", "${translate('Password')}:",
textAlign: TextAlign.start, textAlign: TextAlign.start,
).marginOnly(bottom: 16.0)), ).marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
obscureText: true, obscureText: true,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: errMsg0.isNotEmpty ? errMsg0 : null), errorText: errMsg0.isNotEmpty ? errMsg0 : null),
controller: p0, controller: p0,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Confirmation')}:") child: Text("${translate('Confirmation')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
obscureText: true, obscureText: true,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: errMsg1.isNotEmpty ? errMsg1 : null), errorText: errMsg1.isNotEmpty ? errMsg1 : null),
controller: p1, controller: p1,
), ),
@@ -999,35 +1027,11 @@ void setPasswordDialog() async {
), ),
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () {
setState(() {
errMsg0 = "";
errMsg1 = "";
});
final pass = p0.text.trim();
if (pass.length < 6) {
setState(() {
errMsg0 = translate("Too short, at least 6 characters.");
});
return;
}
if (p1.text.trim() != pass) {
setState(() {
errMsg1 = translate("The confirmation is not identical.");
});
return;
}
bind.mainSetPermanentPassword(password: pass);
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }

View File

@@ -1038,52 +1038,117 @@ void changeServer() async {
var keyController = TextEditingController(text: key); var keyController = TextEditingController(text: key);
var isInProgress = false; var isInProgress = false;
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
[idServerMsg, relayServerMsg, apiServerMsg].forEach((element) {
element = "";
});
isInProgress = true;
});
cancel() {
setState(() {
isInProgress = false;
});
}
idServer = idController.text.trim();
relayServer = relayController.text.trim();
apiServer = apiController.text.trim().toLowerCase();
key = keyController.text.trim();
if (idServer.isNotEmpty) {
idServerMsg =
translate(await bind.mainTestIfValidServer(server: idServer));
if (idServerMsg.isEmpty) {
oldOptions['custom-rendezvous-server'] = idServer;
} else {
cancel();
return;
}
} else {
oldOptions['custom-rendezvous-server'] = "";
}
if (relayServer.isNotEmpty) {
relayServerMsg =
translate(await bind.mainTestIfValidServer(server: relayServer));
if (relayServerMsg.isEmpty) {
oldOptions['relay-server'] = relayServer;
} else {
cancel();
return;
}
} else {
oldOptions['relay-server'] = "";
}
if (apiServer.isNotEmpty) {
if (apiServer.startsWith('http://') ||
apiServer.startsWith("https://")) {
oldOptions['api-server'] = apiServer;
return;
} else {
apiServerMsg = translate("invalid_http");
cancel();
return;
}
} else {
oldOptions['api-server'] = "";
}
// ok
oldOptions['key'] = key;
await bind.mainSetOptions(json: jsonEncode(oldOptions));
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("ID/Relay Server")), title: Text(translate("ID/Relay Server")),
content: ConstrainedBox( content: ConstrainedBox(
constraints: BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('ID Server')}:") child: Text("${translate('ID Server')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: idServerMsg.isNotEmpty ? idServerMsg : null), errorText: idServerMsg.isNotEmpty ? idServerMsg : null),
controller: idController, controller: idController,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Relay Server')}:") child: Text("${translate('Relay Server')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: errorText:
relayServerMsg.isNotEmpty ? relayServerMsg : null), relayServerMsg.isNotEmpty ? relayServerMsg : null),
controller: relayController, controller: relayController,
@@ -1091,22 +1156,22 @@ void changeServer() async {
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('API Server')}:") child: Text("${translate('API Server')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: errorText:
apiServerMsg.isNotEmpty ? apiServerMsg : null), apiServerMsg.isNotEmpty ? apiServerMsg : null),
controller: apiController, controller: apiController,
@@ -1114,21 +1179,21 @@ void changeServer() async {
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: child:
Text("${translate('Key')}:").marginOnly(bottom: 16.0)), Text("${translate('Key')}:").marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
controller: keyController, controller: keyController,
@@ -1136,83 +1201,20 @@ void changeServer() async {
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
[idServerMsg, relayServerMsg, apiServerMsg].forEach((element) {
element = "";
});
isInProgress = true;
});
final cancel = () {
setState(() {
isInProgress = false;
});
};
idServer = idController.text.trim();
relayServer = relayController.text.trim();
apiServer = apiController.text.trim().toLowerCase();
key = keyController.text.trim();
if (idServer.isNotEmpty) {
idServerMsg = translate(
await bind.mainTestIfValidServer(server: idServer));
if (idServerMsg.isEmpty) {
oldOptions['custom-rendezvous-server'] = idServer;
} else {
cancel();
return;
}
} else {
oldOptions['custom-rendezvous-server'] = "";
}
if (relayServer.isNotEmpty) {
relayServerMsg = translate(
await bind.mainTestIfValidServer(server: relayServer));
if (relayServerMsg.isEmpty) {
oldOptions['relay-server'] = relayServer;
} else {
cancel();
return;
}
} else {
oldOptions['relay-server'] = "";
}
if (apiServer.isNotEmpty) {
if (apiServer.startsWith('http://') ||
apiServer.startsWith("https://")) {
oldOptions['api-server'] = apiServer;
return;
} else {
apiServerMsg = translate("invalid_http");
cancel();
return;
}
} else {
oldOptions['api-server'] = "";
}
// ok
oldOptions['key'] = key;
await bind.mainSetOptions(json: jsonEncode(oldOptions));
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }
@@ -1231,27 +1233,28 @@ void changeWhiteList() async {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(translate("whitelist_sep")), Text(translate("whitelist_sep")),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: TextField( child: TextField(
maxLines: null, maxLines: null,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: msg.isEmpty ? null : translate(msg), errorText: msg.isEmpty ? null : translate(msg),
), ),
controller: controller, controller: controller,
), focusNode: FocusNode()..requestFocus()),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 4.0, height: 4.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
actions: [ actions: [
@@ -1277,7 +1280,7 @@ void changeWhiteList() async {
final ipMatch = RegExp(r"^\d+\.\d+\.\d+\.\d+$"); final ipMatch = RegExp(r"^\d+\.\d+\.\d+\.\d+$");
for (final ip in ips) { for (final ip in ips) {
if (!ipMatch.hasMatch(ip)) { if (!ipMatch.hasMatch(ip)) {
msg = translate("Invalid IP") + " $ip"; msg = "${translate("Invalid IP")} $ip";
setState(() { setState(() {
isInProgress = false; isInProgress = false;
}); });
@@ -1292,6 +1295,7 @@ void changeWhiteList() async {
}, },
child: Text(translate("OK"))), child: Text(translate("OK"))),
], ],
onCancel: close,
); );
}); });
} }
@@ -1314,50 +1318,80 @@ void changeSocks5Proxy() async {
var isInProgress = false; var isInProgress = false;
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
proxyMsg = "";
isInProgress = true;
});
cancel() {
setState(() {
isInProgress = false;
});
}
proxy = proxyController.text.trim();
username = userController.text.trim();
password = pwdController.text.trim();
if (proxy.isNotEmpty) {
proxyMsg = translate(await bind.mainTestIfValidServer(server: proxy));
if (proxyMsg.isEmpty) {
// ignore
} else {
cancel();
return;
}
}
await bind.mainSetSocks(
proxy: proxy, username: username, password: password);
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Socks5 Proxy")), title: Text(translate("Socks5 Proxy")),
content: ConstrainedBox( content: ConstrainedBox(
constraints: BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Hostname')}:") child: Text("${translate('Hostname')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
errorText: proxyMsg.isNotEmpty ? proxyMsg : null), errorText: proxyMsg.isNotEmpty ? proxyMsg : null),
controller: proxyController, controller: proxyController,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Username')}:") child: Text("${translate('Username')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
controller: userController, controller: userController,
@@ -1365,21 +1399,21 @@ void changeSocks5Proxy() async {
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Password')}:") child: Text("${translate('Password')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
controller: pwdController, controller: pwdController,
@@ -1387,50 +1421,20 @@ void changeSocks5Proxy() async {
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
proxyMsg = "";
isInProgress = true;
});
final cancel = () {
setState(() {
isInProgress = false;
});
};
proxy = proxyController.text.trim();
username = userController.text.trim();
password = pwdController.text.trim();
if (proxy.isNotEmpty) {
proxyMsg =
translate(await bind.mainTestIfValidServer(server: proxy));
if (proxyMsg.isEmpty) {
// ignore
} else {
cancel();
return;
}
}
await bind.mainSetSocks(
proxy: proxy, username: username, password: password);
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }

View File

@@ -37,25 +37,33 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
RxBool fullscreen = false.obs; RxBool fullscreen = false.obs;
Get.put(fullscreen, tag: 'fullscreen'); Get.put(fullscreen, tag: 'fullscreen');
return Obx(() => DragToResizeArea( return Obx(() => DragToResizeArea(
resizeEdgeSize: fullscreen.value ? 1.0 : 8.0, resizeEdgeSize: fullscreen.value ? 1.0 : 8.0,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: MyTheme.color(context).border!)), border: Border.all(color: MyTheme.color(context).border!)),
child: Scaffold( child: Overlay(initialEntries: [
backgroundColor: MyTheme.color(context).bg, OverlayEntry(builder: (context) {
body: DesktopTab( gFFI.dialogManager.setOverlayState(Overlay.of(context));
controller: tabController, return Scaffold(
theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), backgroundColor: MyTheme.color(context).bg,
tail: ActionIcon( body: DesktopTab(
message: 'Settings', controller: tabController,
icon: IconFont.menu, theme: dark
theme: dark ? TarBarTheme.dark() : TarBarTheme.light(), ? const TarBarTheme.dark()
onTap: onAddSetting, : const TarBarTheme.light(),
is_close: false, tail: ActionIcon(
), message: 'Settings',
)), icon: IconFont.menu,
), theme: dark
)); ? const TarBarTheme.dark()
: const TarBarTheme.light(),
onTap: onAddSetting,
is_close: false,
),
));
})
]),
)));
} }
void onAddSetting() { void onAddSetting() {

View File

@@ -642,47 +642,51 @@ class _FileManagerPageState extends State<FileManagerPage>
IconButton( IconButton(
onPressed: () { onPressed: () {
final name = TextEditingController(); final name = TextEditingController();
_ffi.dialogManager _ffi.dialogManager.show((setState, close) {
.show((setState, close) => CustomAlertDialog( submit() {
title: Text(translate("Create Folder")), if (name.value.text.isNotEmpty) {
content: Column( model.createDir(
mainAxisSize: MainAxisSize.min, PathUtil.join(
children: [ model.getCurrentDir(isLocal).path,
TextFormField( name.value.text,
decoration: InputDecoration( model.getCurrentIsWindows(isLocal)),
labelText: translate( isLocal: isLocal);
"Please enter the folder name"), close();
), }
controller: name, }
),
], cancel() => close(false);
), return CustomAlertDialog(
actions: [ title: Text(translate("Create Folder")),
TextButton( content: Column(
style: flatButtonStyle, mainAxisSize: MainAxisSize.min,
onPressed: () => close(false), children: [
child: Text(translate("Cancel"))), TextFormField(
ElevatedButton( decoration: InputDecoration(
style: flatButtonStyle, labelText: translate(
onPressed: () { "Please enter the folder name"),
if (name.value.text.isNotEmpty) { ),
model.createDir( controller: name,
PathUtil.join( focusNode: FocusNode()..requestFocus(),
model ),
.getCurrentDir( ],
isLocal) ),
.path, actions: [
name.value.text, TextButton(
model.getCurrentIsWindows( style: flatButtonStyle,
isLocal)), onPressed: cancel,
isLocal: isLocal); child: Text(translate("Cancel"))),
close(); ElevatedButton(
} style: flatButtonStyle,
}, onPressed: submit,
child: Text(translate("OK"))) child: Text(translate("OK")))
])); ],
onSubmit: submit,
onCancel: cancel,
);
});
}, },
icon: Icon(Icons.create_new_folder_outlined)), icon: const Icon(Icons.create_new_folder_outlined)),
IconButton( IconButton(
onPressed: () async { onPressed: () async {
final items = isLocal final items = isLocal

View File

@@ -70,38 +70,45 @@ class _PortForwardPageState extends State<PortForwardPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
super.build(context); super.build(context);
return Scaffold( return Overlay(initialEntries: [
backgroundColor: MyTheme.color(context).grayBg, OverlayEntry(builder: (context) {
body: FutureBuilder(future: () async { _ffi.dialogManager.setOverlayState(Overlay.of(context));
if (!isRdp) { return Scaffold(
refreshTunnelConfig(); backgroundColor: MyTheme.color(context).grayBg,
} body: FutureBuilder(future: () async {
}(), builder: (context, snapshot) { if (!isRdp) {
if (snapshot.connectionState == ConnectionState.done) { refreshTunnelConfig();
return Container( }
decoration: BoxDecoration( }(), builder: (context, snapshot) {
border: Border.all( if (snapshot.connectionState == ConnectionState.done) {
width: 20, color: MyTheme.color(context).grayBg!)), return Container(
child: Column( decoration: BoxDecoration(
crossAxisAlignment: CrossAxisAlignment.stretch, border: Border.all(
children: [ width: 20, color: MyTheme.color(context).grayBg!)),
buildPrompt(context), child: Column(
Flexible( crossAxisAlignment: CrossAxisAlignment.stretch,
child: Container( children: [
decoration: BoxDecoration( buildPrompt(context),
color: MyTheme.color(context).bg, Flexible(
border: Border.all(width: 1, color: MyTheme.border)), child: Container(
child: decoration: BoxDecoration(
widget.isRDP ? buildRdp(context) : buildTunnel(context), color: MyTheme.color(context).bg,
), border:
Border.all(width: 1, color: MyTheme.border)),
child: widget.isRDP
? buildRdp(context)
: buildTunnel(context),
),
),
],
), ),
], );
), }
); return const Offstage();
} }),
return const Offstage(); );
}), })
); ]);
} }
buildPrompt(BuildContext context) { buildPrompt(BuildContext context) {

View File

@@ -563,47 +563,47 @@ abstract class BasePeerCard extends StatelessWidget {
} }
} }
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
isInProgress.value = true;
name = controller.text;
await bind.mainSetPeerOption(id: id, key: 'alias', value: name);
if (isAddressBook) {
gFFI.abModel.setPeerOption(id, 'alias', name);
await gFFI.abModel.updateAb();
}
alias.value = await bind.mainGetPeerOption(id: peer.id, key: 'alias');
close();
isInProgress.value = false;
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate('Rename')), title: Text(translate('Rename')),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Form( child: Form(
child: TextFormField( child: TextFormField(
controller: controller, controller: controller,
decoration: InputDecoration(border: OutlineInputBorder()), focusNode: FocusNode()..requestFocus(),
decoration:
const InputDecoration(border: OutlineInputBorder()),
), ),
), ),
), ),
Obx(() => Offstage( Obx(() => Offstage(
offstage: isInProgress.isFalse, offstage: isInProgress.isFalse,
child: LinearProgressIndicator())), child: const LinearProgressIndicator())),
], ],
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
isInProgress.value = true;
name = controller.text;
await bind.mainSetPeerOption(id: id, key: 'alias', value: name);
if (isAddressBook) {
gFFI.abModel.setPeerOption(id, 'alias', name);
await gFFI.abModel.updateAb();
}
alias.value =
await bind.mainGetPeerOption(id: peer.id, key: 'alias');
close();
isInProgress.value = false;
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }
@@ -750,13 +750,23 @@ class AddressBookPeerCard extends BasePeerCard {
var selectedTag = gFFI.abModel.getPeerTags(id).obs; var selectedTag = gFFI.abModel.getPeerTags(id).obs;
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
setState(() {
isInProgress = true;
});
gFFI.abModel.changeTagForPeer(id, selectedTag);
await gFFI.abModel.updateAb();
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate("Edit Tag")), title: Text(translate("Edit Tag")),
content: Column( content: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Wrap( child: Wrap(
children: tags children: tags
.map((e) => _buildTag(e, selectedTag, onTap: () { .map((e) => _buildTag(e, selectedTag, onTap: () {
@@ -769,26 +779,16 @@ class AddressBookPeerCard extends BasePeerCard {
.toList(growable: false), .toList(growable: false),
), ),
), ),
Offstage(offstage: !isInProgress, child: LinearProgressIndicator()) Offstage(
offstage: !isInProgress, child: const LinearProgressIndicator())
], ],
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
setState(() {
isInProgress = true;
});
gFFI.abModel.changeTagForPeer(id, selectedTag);
await gFFI.abModel.updateAb();
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }
@@ -871,25 +871,35 @@ void _rdpDialog(String id) async {
RxBool secure = true.obs; RxBool secure = true.obs;
gFFI.dialogManager.show((setState, close) { gFFI.dialogManager.show((setState, close) {
submit() async {
await bind.mainSetPeerOption(
id: id, key: 'rdp_port', value: portController.text.trim());
await bind.mainSetPeerOption(
id: id, key: 'rdp_username', value: userController.text);
await bind.mainSetPeerOption(
id: id, key: 'rdp_password', value: passwordContorller.text);
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text('RDP ' + translate('Settings')), title: Text('RDP ${translate('Settings')}'),
content: ConstrainedBox( content: ConstrainedBox(
constraints: BoxConstraints(minWidth: 500), constraints: const BoxConstraints(minWidth: 500),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text( child: Text(
"${translate('Port')}:", "${translate('Port')}:",
textAlign: TextAlign.start, textAlign: TextAlign.start,
).marginOnly(bottom: 16.0)), ).marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
@@ -898,52 +908,54 @@ void _rdpDialog(String id) async {
FilteringTextInputFormatter.allow(RegExp( FilteringTextInputFormatter.allow(RegExp(
r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$')) r'^([0-9]|[1-9]\d|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$'))
], ],
decoration: InputDecoration( decoration: const InputDecoration(
border: OutlineInputBorder(), hintText: '3389'), border: OutlineInputBorder(), hintText: '3389'),
controller: portController, controller: portController,
focusNode: FocusNode()..requestFocus(),
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text( child: Text(
"${translate('Username')}:", "${translate('Username')}:",
textAlign: TextAlign.start, textAlign: TextAlign.start,
).marginOnly(bottom: 16.0)), ).marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: TextField( child: TextField(
decoration: InputDecoration(border: OutlineInputBorder()), decoration:
const InputDecoration(border: OutlineInputBorder()),
controller: userController, controller: userController,
), ),
), ),
], ],
), ),
SizedBox( const SizedBox(
height: 8.0, height: 8.0,
), ),
Row( Row(
children: [ children: [
ConstrainedBox( ConstrainedBox(
constraints: BoxConstraints(minWidth: 100), constraints: const BoxConstraints(minWidth: 100),
child: Text("${translate('Password')}:") child: Text("${translate('Password')}:")
.marginOnly(bottom: 16.0)), .marginOnly(bottom: 16.0)),
SizedBox( const SizedBox(
width: 24.0, width: 24.0,
), ),
Expanded( Expanded(
child: Obx(() => TextField( child: Obx(() => TextField(
obscureText: secure.value, obscureText: secure.value,
decoration: InputDecoration( decoration: InputDecoration(
border: OutlineInputBorder(), border: const OutlineInputBorder(),
suffixIcon: IconButton( suffixIcon: IconButton(
onPressed: () => secure.value = !secure.value, onPressed: () => secure.value = !secure.value,
icon: Icon(secure.value icon: Icon(secure.value
@@ -958,23 +970,11 @@ void _rdpDialog(String id) async {
), ),
), ),
actions: [ actions: [
TextButton( TextButton(onPressed: close, child: Text(translate("Cancel"))),
onPressed: () { TextButton(onPressed: submit, child: Text(translate("OK"))),
close();
},
child: Text(translate("Cancel"))),
TextButton(
onPressed: () async {
await bind.mainSetPeerOption(
id: id, key: 'rdp_port', value: portController.text.trim());
await bind.mainSetPeerOption(
id: id, key: 'rdp_username', value: userController.text);
await bind.mainSetPeerOption(
id: id, key: 'rdp_password', value: passwordContorller.text);
close();
},
child: Text(translate("OK"))),
], ],
onSubmit: submit,
onCancel: close,
); );
}); });
} }

View File

@@ -596,46 +596,49 @@ void showSetOSPassword(
var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != ""; var autoLogin = await bind.sessionGetOption(id: id, arg: "auto-login") != "";
controller.text = password; controller.text = password;
dialogManager.show((setState, close) { dialogManager.show((setState, close) {
submit() {
var text = controller.text.trim();
bind.sessionPeerOption(id: id, name: "os-password", value: text);
bind.sessionPeerOption(
id: id, name: "auto-login", value: autoLogin ? 'Y' : '');
if (text != "" && login) {
bind.sessionInputOsPassword(id: id, value: text);
}
close();
}
return CustomAlertDialog( return CustomAlertDialog(
title: Text(translate('OS Password')), title: Text(translate('OS Password')),
content: Column(mainAxisSize: MainAxisSize.min, children: [ content: Column(mainAxisSize: MainAxisSize.min, children: [
PasswordWidget(controller: controller), PasswordWidget(controller: controller),
CheckboxListTile( CheckboxListTile(
contentPadding: const EdgeInsets.all(0), contentPadding: const EdgeInsets.all(0),
dense: true, dense: true,
controlAffinity: ListTileControlAffinity.leading, controlAffinity: ListTileControlAffinity.leading,
title: Text( title: Text(
translate('Auto Login'), translate('Auto Login'),
),
value: autoLogin,
onChanged: (v) {
if (v == null) return;
setState(() => autoLogin = v);
},
), ),
]), value: autoLogin,
actions: [ onChanged: (v) {
TextButton( if (v == null) return;
style: flatButtonStyle, setState(() => autoLogin = v);
onPressed: () { },
close(); ),
}, ]),
child: Text(translate('Cancel')), actions: [
), TextButton(
TextButton( style: flatButtonStyle,
style: flatButtonStyle, onPressed: close,
onPressed: () { child: Text(translate('Cancel')),
var text = controller.text.trim(); ),
bind.sessionPeerOption(id: id, name: "os-password", value: text); TextButton(
bind.sessionPeerOption( style: flatButtonStyle,
id: id, name: "auto-login", value: autoLogin ? 'Y' : ''); onPressed: submit,
if (text != "" && login) { child: Text(translate('OK')),
bind.sessionInputOsPassword(id: id, value: text); ),
} ],
close(); onSubmit: submit,
}, onCancel: close,
child: Text(translate('OK')), );
),
]);
}); });
} }

View File

@@ -462,22 +462,24 @@ class WindowActionPanel extends StatelessWidget {
} }
closeConfirmDialog(Function() callback) async { closeConfirmDialog(Function() callback) async {
final res = await gFFI.dialogManager final res = await gFFI.dialogManager.show<bool>((setState, close) {
.show<bool>((setState, close) => CustomAlertDialog( submit() => close(true);
title: Row(children: [ return CustomAlertDialog(
Icon(Icons.warning_amber_sharp, title: Row(children: [
color: Colors.redAccent, size: 28), const Icon(Icons.warning_amber_sharp,
SizedBox(width: 10), color: Colors.redAccent, size: 28),
Text(translate("Warning")), const SizedBox(width: 10),
]), Text(translate("Warning")),
content: Text(translate("Disconnect all devices?")), ]),
actions: [ content: Text(translate("Disconnect all devices?")),
TextButton( actions: [
onPressed: () => close(), child: Text(translate("Cancel"))), TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton( ElevatedButton(onPressed: submit, child: Text(translate("OK"))),
onPressed: () => close(true), child: Text(translate("OK"))), ],
], onSubmit: submit,
)); onCancel: close,
);
});
if (res == true) { if (res == true) {
callback(); callback();
} }

View File

@@ -559,49 +559,55 @@ class FileModel extends ChangeNotifier {
Future<bool?> showRemoveDialog( Future<bool?> showRemoveDialog(
String title, String content, bool showCheckbox) async { String title, String content, bool showCheckbox) async {
return await parent.target?.dialogManager.show<bool>( return await parent.target?.dialogManager.show<bool>(
(setState, Function(bool v) close) => CustomAlertDialog( (setState, Function(bool v) close) {
title: Row( cancel() => close(false);
children: [ submit() => close(true);
Icon(Icons.warning, color: Colors.red), return CustomAlertDialog(
SizedBox(width: 20), title: Row(
Text(title) children: [
], const Icon(Icons.warning, color: Colors.red),
), const SizedBox(width: 20),
content: Column( Text(title)
crossAxisAlignment: CrossAxisAlignment.start, ],
mainAxisSize: MainAxisSize.min, ),
children: [ content: Column(
Text(content), crossAxisAlignment: CrossAxisAlignment.start,
SizedBox(height: 5), mainAxisSize: MainAxisSize.min,
Text(translate("This is irreversible!"), children: [
style: TextStyle(fontWeight: FontWeight.bold)), Text(content),
showCheckbox const SizedBox(height: 5),
? CheckboxListTile( Text(translate("This is irreversible!"),
contentPadding: const EdgeInsets.all(0), style: const TextStyle(fontWeight: FontWeight.bold)),
dense: true, showCheckbox
controlAffinity: ListTileControlAffinity.leading, ? CheckboxListTile(
title: Text( contentPadding: const EdgeInsets.all(0),
translate("Do this for all conflicts"), dense: true,
), controlAffinity: ListTileControlAffinity.leading,
value: removeCheckboxRemember, title: Text(
onChanged: (v) { translate("Do this for all conflicts"),
if (v == null) return; ),
setState(() => removeCheckboxRemember = v); value: removeCheckboxRemember,
}, onChanged: (v) {
) if (v == null) return;
: SizedBox.shrink() setState(() => removeCheckboxRemember = v);
]), },
actions: [ )
TextButton( : const SizedBox.shrink()
style: flatButtonStyle, ]),
onPressed: () => close(false), actions: [
child: Text(translate("Cancel"))), TextButton(
TextButton( style: flatButtonStyle,
style: flatButtonStyle, onPressed: cancel,
onPressed: () => close(true), child: Text(translate("Cancel"))),
child: Text(translate("OK"))), TextButton(
]), style: flatButtonStyle,
useAnimation: false); onPressed: submit,
child: Text(translate("OK"))),
],
onSubmit: submit,
onCancel: cancel,
);
}, useAnimation: false);
} }
bool fileConfirmCheckboxRemember = false; bool fileConfirmCheckboxRemember = false;
@@ -610,55 +616,59 @@ class FileModel extends ChangeNotifier {
String title, String content, bool showCheckbox) async { String title, String content, bool showCheckbox) async {
fileConfirmCheckboxRemember = false; fileConfirmCheckboxRemember = false;
return await parent.target?.dialogManager.show<bool?>( return await parent.target?.dialogManager.show<bool?>(
(setState, Function(bool? v) close) => CustomAlertDialog( (setState, Function(bool? v) close) {
title: Row( cancel() => close(false);
children: [ submit() => close(true);
Icon(Icons.warning, color: Colors.red), return CustomAlertDialog(
SizedBox(width: 20), title: Row(
Text(title) children: [
], const Icon(Icons.warning, color: Colors.red),
), const SizedBox(width: 20),
content: Column( Text(title)
crossAxisAlignment: CrossAxisAlignment.start, ],
mainAxisSize: MainAxisSize.min, ),
children: [ content: Column(
Text( crossAxisAlignment: CrossAxisAlignment.start,
translate( mainAxisSize: MainAxisSize.min,
"This file exists, skip or overwrite this file?"), children: [
style: TextStyle(fontWeight: FontWeight.bold)), Text(translate("This file exists, skip or overwrite this file?"),
SizedBox(height: 5), style: const TextStyle(fontWeight: FontWeight.bold)),
Text(content), const SizedBox(height: 5),
showCheckbox Text(content),
? CheckboxListTile( showCheckbox
contentPadding: const EdgeInsets.all(0), ? CheckboxListTile(
dense: true, contentPadding: const EdgeInsets.all(0),
controlAffinity: ListTileControlAffinity.leading, dense: true,
title: Text( controlAffinity: ListTileControlAffinity.leading,
translate("Do this for all conflicts"), title: Text(
), translate("Do this for all conflicts"),
value: fileConfirmCheckboxRemember, ),
onChanged: (v) { value: fileConfirmCheckboxRemember,
if (v == null) return; onChanged: (v) {
setState(() => fileConfirmCheckboxRemember = v); if (v == null) return;
}, setState(() => fileConfirmCheckboxRemember = v);
) },
: SizedBox.shrink() )
]), : const SizedBox.shrink()
actions: [ ]),
TextButton( actions: [
style: flatButtonStyle, TextButton(
onPressed: () => close(false), style: flatButtonStyle,
child: Text(translate("Cancel"))), onPressed: cancel,
TextButton( child: Text(translate("Cancel"))),
style: flatButtonStyle, TextButton(
onPressed: () => close(null), style: flatButtonStyle,
child: Text(translate("Skip"))), onPressed: () => close(null),
TextButton( child: Text(translate("Skip"))),
style: flatButtonStyle, TextButton(
onPressed: () => close(true), style: flatButtonStyle,
child: Text(translate("OK"))), onPressed: submit,
]), child: Text(translate("OK"))),
useAnimation: false); ],
onSubmit: submit,
onCancel: cancel,
);
}, useAnimation: false);
} }
sendRemoveFile(String path, int fileNum, bool isLocal) { sendRemoveFile(String path, int fileNum, bool isLocal) {

View File

@@ -209,46 +209,48 @@ class ServerModel with ChangeNotifier {
/// Toggle the screen sharing service. /// Toggle the screen sharing service.
toggleService() async { toggleService() async {
if (_isStart) { if (_isStart) {
final res = await parent.target?.dialogManager final res =
.show<bool>((setState, close) => CustomAlertDialog( await parent.target?.dialogManager.show<bool>((setState, close) {
title: Row(children: [ submit() => close(true);
Icon(Icons.warning_amber_sharp, return CustomAlertDialog(
color: Colors.redAccent, size: 28), title: Row(children: [
SizedBox(width: 10), const Icon(Icons.warning_amber_sharp,
Text(translate("Warning")), color: Colors.redAccent, size: 28),
]), const SizedBox(width: 10),
content: Text(translate("android_stop_service_tip")), Text(translate("Warning")),
actions: [ ]),
TextButton( content: Text(translate("android_stop_service_tip")),
onPressed: () => close(), actions: [
child: Text(translate("Cancel"))), TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton( ElevatedButton(onPressed: submit, child: Text(translate("OK"))),
onPressed: () => close(true), ],
child: Text(translate("OK"))), onSubmit: submit,
], onCancel: close,
)); );
});
if (res == true) { if (res == true) {
stopService(); stopService();
} }
} else { } else {
final res = await parent.target?.dialogManager final res =
.show<bool>((setState, close) => CustomAlertDialog( await parent.target?.dialogManager.show<bool>((setState, close) {
title: Row(children: [ submit() => close(true);
Icon(Icons.warning_amber_sharp, return CustomAlertDialog(
color: Colors.redAccent, size: 28), title: Row(children: [
SizedBox(width: 10), const Icon(Icons.warning_amber_sharp,
Text(translate("Warning")), color: Colors.redAccent, size: 28),
]), const SizedBox(width: 10),
content: Text(translate("android_service_will_start_tip")), Text(translate("Warning")),
actions: [ ]),
TextButton( content: Text(translate("android_service_will_start_tip")),
onPressed: () => close(), actions: [
child: Text(translate("Cancel"))), TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton( ElevatedButton(onPressed: submit, child: Text(translate("OK"))),
onPressed: () => close(true), ],
child: Text(translate("OK"))), onSubmit: submit,
], onCancel: close,
)); );
});
if (res == true) { if (res == true) {
startService(); startService();
} }
@@ -388,49 +390,49 @@ class ServerModel with ChangeNotifier {
} }
void showLoginDialog(Client client) { void showLoginDialog(Client client) {
parent.target?.dialogManager.show( parent.target?.dialogManager.show((setState, close) {
(setState, close) => CustomAlertDialog( cancel() {
title: Row( sendLoginResponse(client, false);
mainAxisAlignment: MainAxisAlignment.spaceBetween, close();
children: [ }
Text(translate(client.isFileTransfer
? "File Connection" submit() {
: "Screen Connection")), sendLoginResponse(client, true);
IconButton( close();
onPressed: () { }
close();
}, return CustomAlertDialog(
icon: Icon(Icons.close)) title:
]), Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
content: Column( Text(translate(
mainAxisSize: MainAxisSize.min, client.isFileTransfer ? "File Connection" : "Screen Connection")),
mainAxisAlignment: MainAxisAlignment.center, IconButton(
crossAxisAlignment: CrossAxisAlignment.start, onPressed: () {
children: [ close();
Text(translate("Do you accept?")), },
clientInfo(client), icon: const Icon(Icons.close))
Text( ]),
translate("android_new_connection_tip"), content: Column(
style: TextStyle(color: Colors.black54), mainAxisSize: MainAxisSize.min,
), mainAxisAlignment: MainAxisAlignment.center,
], crossAxisAlignment: CrossAxisAlignment.start,
), children: [
actions: [ Text(translate("Do you accept?")),
TextButton( clientInfo(client),
child: Text(translate("Dismiss")), Text(
onPressed: () { translate("android_new_connection_tip"),
sendLoginResponse(client, false); style: const TextStyle(color: Colors.black54),
close();
}),
ElevatedButton(
child: Text(translate("Accept")),
onPressed: () {
sendLoginResponse(client, true);
close();
}),
],
), ),
tag: getLoginDialogTag(client.id)); ],
),
actions: [
TextButton(onPressed: cancel, child: Text(translate("Dismiss"))),
ElevatedButton(onPressed: submit, child: Text(translate("Accept"))),
],
onSubmit: submit,
onCancel: cancel,
);
}, tag: getLoginDialogTag(client.id));
} }
scrollToBottom() { scrollToBottom() {
@@ -563,24 +565,29 @@ String getLoginDialogTag(int id) {
} }
showInputWarnAlert(FFI ffi) { showInputWarnAlert(FFI ffi) {
ffi.dialogManager.show((setState, close) => CustomAlertDialog( ffi.dialogManager.show((setState, close) {
title: Text(translate("How to get Android input permission?")), submit() {
content: Column( ffi.serverModel.initInput();
mainAxisSize: MainAxisSize.min, close();
children: [ }
Text(translate("android_input_permission_tip1")),
SizedBox(height: 10), return CustomAlertDialog(
Text(translate("android_input_permission_tip2")), title: Text(translate("How to get Android input permission?")),
], content: Column(
), mainAxisSize: MainAxisSize.min,
actions: [ children: [
TextButton(child: Text(translate("Cancel")), onPressed: close), Text(translate("android_input_permission_tip1")),
ElevatedButton( const SizedBox(height: 10),
child: Text(translate("Open System Setting")), Text(translate("android_input_permission_tip2")),
onPressed: () {
ffi.serverModel.initInput();
close();
}),
], ],
)); ),
actions: [
TextButton(onPressed: close, child: Text(translate("Cancel"))),
ElevatedButton(
onPressed: submit, child: Text(translate("Open System Setting"))),
],
onSubmit: submit,
onCancel: close,
);
});
} }