mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge pull request #1445 from 21pages/optimize
id format && dialog key and focus handling
This commit is contained in:
commit
f385127f3b
@ -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';
|
||||||
@ -154,7 +155,7 @@ class MyTheme {
|
|||||||
brightness: Brightness.light,
|
brightness: Brightness.light,
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
tabBarTheme: TabBarTheme(
|
tabBarTheme: const TabBarTheme(
|
||||||
labelColor: Colors.black87,
|
labelColor: Colors.black87,
|
||||||
),
|
),
|
||||||
splashColor: Colors.transparent,
|
splashColor: Colors.transparent,
|
||||||
@ -162,13 +163,14 @@ class MyTheme {
|
|||||||
).copyWith(
|
).copyWith(
|
||||||
extensions: <ThemeExtension<dynamic>>[
|
extensions: <ThemeExtension<dynamic>>[
|
||||||
ColorThemeExtension.light,
|
ColorThemeExtension.light,
|
||||||
|
TabbarTheme.light,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
static ThemeData darkTheme = ThemeData(
|
static ThemeData darkTheme = ThemeData(
|
||||||
brightness: Brightness.dark,
|
brightness: Brightness.dark,
|
||||||
primarySwatch: Colors.blue,
|
primarySwatch: Colors.blue,
|
||||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||||
tabBarTheme: TabBarTheme(
|
tabBarTheme: const TabBarTheme(
|
||||||
labelColor: Colors.white70,
|
labelColor: Colors.white70,
|
||||||
),
|
),
|
||||||
splashColor: Colors.transparent,
|
splashColor: Colors.transparent,
|
||||||
@ -176,12 +178,17 @@ class MyTheme {
|
|||||||
).copyWith(
|
).copyWith(
|
||||||
extensions: <ThemeExtension<dynamic>>[
|
extensions: <ThemeExtension<dynamic>>[
|
||||||
ColorThemeExtension.dark,
|
ColorThemeExtension.dark,
|
||||||
|
TabbarTheme.dark,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
static ColorThemeExtension color(BuildContext context) {
|
static ColorThemeExtension color(BuildContext context) {
|
||||||
return Theme.of(context).extension<ColorThemeExtension>()!;
|
return Theme.of(context).extension<ColorThemeExtension>()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TabbarTheme tabbar(BuildContext context) {
|
||||||
|
return Theme.of(context).extension<TabbarTheme>()!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isDarkTheme() {
|
bool isDarkTheme() {
|
||||||
@ -340,34 +347,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 +391,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 +417,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 +474,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 +506,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 +529,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]) {
|
||||||
|
|||||||
@ -1,4 +1,52 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
/// TODO: Divide every 3 number to display ID
|
class IDTextEditingController extends TextEditingController {
|
||||||
class IdFormController extends TextEditingController {}
|
IDTextEditingController({String? text}) : super(text: text);
|
||||||
|
|
||||||
|
String get id => trimID(value.text);
|
||||||
|
|
||||||
|
set id(String newID) => text = formatID(newID);
|
||||||
|
}
|
||||||
|
|
||||||
|
class IDTextInputFormatter extends TextInputFormatter {
|
||||||
|
@override
|
||||||
|
TextEditingValue formatEditUpdate(
|
||||||
|
TextEditingValue oldValue, TextEditingValue newValue) {
|
||||||
|
if (newValue.text.isEmpty) {
|
||||||
|
return newValue.copyWith(text: '');
|
||||||
|
} else if (newValue.text.compareTo(oldValue.text) == 0) {
|
||||||
|
return newValue;
|
||||||
|
} else {
|
||||||
|
int selectionIndexFromTheRight =
|
||||||
|
newValue.text.length - newValue.selection.extentOffset;
|
||||||
|
String newID = formatID(newValue.text);
|
||||||
|
return TextEditingValue(
|
||||||
|
text: newID,
|
||||||
|
selection: TextSelection.collapsed(
|
||||||
|
offset: newID.length - selectionIndexFromTheRight,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatID(String id) {
|
||||||
|
String id2 = id.replaceAll(' ', '');
|
||||||
|
String newID = '';
|
||||||
|
if (id2.length <= 3) {
|
||||||
|
newID = id2;
|
||||||
|
} else {
|
||||||
|
var n = id2.length;
|
||||||
|
var a = n % 3 != 0 ? n % 3 : 3;
|
||||||
|
newID = id2.substring(0, a);
|
||||||
|
for (var i = a; i < n; i += 3) {
|
||||||
|
newID += " ${id2.substring(i, i + 3)}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newID;
|
||||||
|
}
|
||||||
|
|
||||||
|
String trimID(String id) {
|
||||||
|
return id.replaceAll(' ', '');
|
||||||
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
|
import '../../common/formatter/id_formatter.dart';
|
||||||
import '../../mobile/pages/scan_page.dart';
|
import '../../mobile/pages/scan_page.dart';
|
||||||
import '../../mobile/pages/settings_page.dart';
|
import '../../mobile/pages/settings_page.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
@ -30,7 +31,7 @@ class ConnectionPage extends StatefulWidget {
|
|||||||
/// State for the connection page.
|
/// State for the connection page.
|
||||||
class _ConnectionPageState extends State<ConnectionPage> {
|
class _ConnectionPageState extends State<ConnectionPage> {
|
||||||
/// Controller for the id input bar.
|
/// Controller for the id input bar.
|
||||||
final _idController = TextEditingController();
|
final _idController = IDTextEditingController();
|
||||||
|
|
||||||
/// Update url. If it's not null, means an update is available.
|
/// Update url. If it's not null, means an update is available.
|
||||||
final _updateUrl = '';
|
final _updateUrl = '';
|
||||||
@ -43,9 +44,9 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
if (_idController.text.isEmpty) {
|
if (_idController.text.isEmpty) {
|
||||||
() async {
|
() async {
|
||||||
final lastRemoteId = await bind.mainGetLastRemoteId();
|
final lastRemoteId = await bind.mainGetLastRemoteId();
|
||||||
if (lastRemoteId != _idController.text) {
|
if (lastRemoteId != _idController.id) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_idController.text = lastRemoteId;
|
_idController.id = lastRemoteId;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
@ -110,7 +111,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
/// Callback for the connect button.
|
/// Callback for the connect button.
|
||||||
/// Connects to the selected peer.
|
/// Connects to the selected peer.
|
||||||
void onConnect({bool isFileTransfer = false}) {
|
void onConnect({bool isFileTransfer = false}) {
|
||||||
final id = _idController.text.trim();
|
final id = _idController.id;
|
||||||
connect(id, isFileTransfer: isFileTransfer);
|
connect(id, isFileTransfer: isFileTransfer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,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)),
|
||||||
@ -178,42 +179,54 @@ 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),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextField(
|
child: Obx(
|
||||||
autocorrect: false,
|
() => TextField(
|
||||||
enableSuggestions: false,
|
autocorrect: false,
|
||||||
keyboardType: TextInputType.visiblePassword,
|
enableSuggestions: false,
|
||||||
style: TextStyle(
|
keyboardType: TextInputType.visiblePassword,
|
||||||
fontFamily: 'WorkSans',
|
focusNode: focusNode,
|
||||||
fontSize: 22,
|
style: const TextStyle(
|
||||||
height: 1,
|
fontFamily: 'WorkSans',
|
||||||
),
|
fontSize: 22,
|
||||||
decoration: InputDecoration(
|
height: 1,
|
||||||
hintText: translate('Enter Remote ID'),
|
),
|
||||||
hintStyle: TextStyle(
|
maxLines: 1,
|
||||||
color: MyTheme.color(context).placeholder),
|
cursorColor: MyTheme.color(context).text!,
|
||||||
border: OutlineInputBorder(
|
decoration: InputDecoration(
|
||||||
|
hintText: inputFocused.value
|
||||||
|
? null
|
||||||
|
: translate('Enter Remote ID'),
|
||||||
|
hintStyle: TextStyle(
|
||||||
|
color: MyTheme.color(context).placeholder),
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.zero,
|
||||||
|
borderSide: BorderSide(
|
||||||
|
color: MyTheme.color(context).border!)),
|
||||||
|
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.color(context).placeholder!)),
|
BorderSide(color: MyTheme.button, width: 3),
|
||||||
focusedBorder: OutlineInputBorder(
|
),
|
||||||
borderRadius: BorderRadius.zero,
|
isDense: true,
|
||||||
borderSide:
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
BorderSide(color: MyTheme.button, width: 3),
|
horizontal: 10, vertical: 12)),
|
||||||
),
|
controller: _idController,
|
||||||
isDense: true,
|
inputFormatters: [IDTextInputFormatter()],
|
||||||
contentPadding:
|
onSubmitted: (s) {
|
||||||
EdgeInsets.symmetric(horizontal: 10, vertical: 12)),
|
onConnect();
|
||||||
controller: _idController,
|
},
|
||||||
onSubmitted: (s) {
|
),
|
||||||
onConnect();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -259,7 +272,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
).marginSymmetric(horizontal: 12),
|
).marginSymmetric(horizontal: 12),
|
||||||
),
|
),
|
||||||
)),
|
)),
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
width: 17,
|
width: 17,
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
@ -304,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
|
||||||
@ -654,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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -727,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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -799,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: () {
|
||||||
@ -818,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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,7 +83,6 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light();
|
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
||||||
return Obx(() => SubWindowDragToResizeArea(
|
return Obx(() => SubWindowDragToResizeArea(
|
||||||
resizeEdgeSize: fullscreen.value ? 1.0 : 8.0,
|
resizeEdgeSize: fullscreen.value ? 1.0 : 8.0,
|
||||||
@ -95,14 +94,11 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
backgroundColor: MyTheme.color(context).bg,
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
body: Obx(() => DesktopTab(
|
body: Obx(() => DesktopTab(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
theme: theme,
|
|
||||||
showTabBar: fullscreen.isFalse,
|
showTabBar: fullscreen.isFalse,
|
||||||
onClose: () {
|
onClose: () {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
},
|
},
|
||||||
tail: AddButton(
|
tail: AddButton().paddingOnly(left: 10),
|
||||||
theme: theme,
|
|
||||||
).paddingOnly(left: 10),
|
|
||||||
pageViewBuilder: (pageView) {
|
pageViewBuilder: (pageView) {
|
||||||
WindowController.fromWindowId(windowId())
|
WindowController.fromWindowId(windowId())
|
||||||
.setFullscreen(fullscreen.isTrue);
|
.setFullscreen(fullscreen.isTrue);
|
||||||
|
|||||||
@ -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,23 +93,23 @@ 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: 11),
|
||||||
height: 52,
|
height: 57,
|
||||||
child: Row(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(
|
||||||
width: 2,
|
width: 2,
|
||||||
decoration: BoxDecoration(color: MyTheme.accent),
|
decoration: const BoxDecoration(color: MyTheme.accent),
|
||||||
),
|
).marginOnly(top: 5),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 7),
|
||||||
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,
|
||||||
@ -120,7 +120,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
color: MyTheme.color(context).lightText),
|
color: MyTheme.color(context).lightText),
|
||||||
),
|
).marginOnly(top: 5),
|
||||||
buildPopupMenu(context)
|
buildPopupMenu(context)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -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: 20),
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -244,7 +244,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
},
|
},
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => CircleAvatar(
|
() => CircleAvatar(
|
||||||
radius: 12,
|
radius: 15,
|
||||||
backgroundColor: hover.value
|
backgroundColor: hover.value
|
||||||
? MyTheme.color(context).grayBg!
|
? MyTheme.color(context).grayBg!
|
||||||
: MyTheme.color(context).bg!,
|
: MyTheme.color(context).bg!,
|
||||||
@ -277,7 +277,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 8.0),
|
padding: const EdgeInsets.only(left: 7),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -303,7 +303,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
readOnly: true,
|
readOnly: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: InputBorder.none,
|
border: InputBorder.none,
|
||||||
contentPadding: EdgeInsets.only(bottom: 8),
|
contentPadding: EdgeInsets.only(bottom: 2),
|
||||||
),
|
),
|
||||||
style: TextStyle(fontSize: 15),
|
style: TextStyle(fontSize: 15),
|
||||||
),
|
),
|
||||||
@ -317,7 +317,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
? MyTheme.color(context).text
|
? MyTheme.color(context).text
|
||||||
: Color(0xFFDDDDDD),
|
: Color(0xFFDDDDDD),
|
||||||
size: 22,
|
size: 22,
|
||||||
).marginOnly(right: 10, bottom: 8),
|
).marginOnly(right: 8, bottom: 2),
|
||||||
),
|
),
|
||||||
onTap: () => bind.mainUpdateTemporaryPassword(),
|
onTap: () => bind.mainUpdateTemporaryPassword(),
|
||||||
onHover: (value) => refreshHover.value = value,
|
onHover: (value) => refreshHover.value = value,
|
||||||
@ -425,13 +425,13 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
color: editHover.value
|
color: editHover.value
|
||||||
? MyTheme.color(context).text
|
? MyTheme.color(context).text
|
||||||
: Color(0xFFDDDDDD))
|
: Color(0xFFDDDDDD))
|
||||||
.marginOnly(bottom: 8)));
|
.marginOnly(bottom: 2)));
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTip(BuildContext context) {
|
buildTip(BuildContext context) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(left: 20.0, right: 16, top: 16.0, bottom: 14),
|
const EdgeInsets.only(left: 20.0, right: 16, top: 16.0, bottom: 5),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -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 © 2022 Purslane Ltd.\n$license",
|
"Copyright © 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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,7 @@ class _DesktopSettingPageState extends State<DesktopSettingPage>
|
|||||||
'Display', Icons.desktop_windows_outlined, Icons.desktop_windows_sharp),
|
'Display', Icons.desktop_windows_outlined, Icons.desktop_windows_sharp),
|
||||||
_TabInfo('Audio', Icons.volume_up_outlined, Icons.volume_up_sharp),
|
_TabInfo('Audio', Icons.volume_up_outlined, Icons.volume_up_sharp),
|
||||||
_TabInfo('Connection', Icons.link_outlined, Icons.link_sharp),
|
_TabInfo('Connection', Icons.link_outlined, Icons.link_sharp),
|
||||||
_TabInfo('About RustDesk', Icons.info_outline, Icons.info_sharp)
|
_TabInfo('About', Icons.info_outline, Icons.info_sharp)
|
||||||
];
|
];
|
||||||
|
|
||||||
late PageController controller;
|
late PageController controller;
|
||||||
@ -714,7 +714,7 @@ class _AboutState extends State<_About> {
|
|||||||
],
|
],
|
||||||
).marginOnly(left: _kContentHMargin)
|
).marginOnly(left: _kContentHMargin)
|
||||||
]),
|
]),
|
||||||
]).marginOnly(left: _kCardLeftMargin);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,25 +37,27 @@ 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,
|
tail: ActionIcon(
|
||||||
theme: dark ? TarBarTheme.dark() : TarBarTheme.light(),
|
message: 'Settings',
|
||||||
onTap: onAddSetting,
|
icon: IconFont.menu,
|
||||||
is_close: false,
|
onTap: onAddSetting,
|
||||||
),
|
isClose: false,
|
||||||
)),
|
),
|
||||||
),
|
));
|
||||||
));
|
})
|
||||||
|
]),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAddSetting() {
|
void onAddSetting() {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -62,8 +62,6 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme =
|
|
||||||
isDarkTheme() ? const TarBarTheme.dark() : const TarBarTheme.light();
|
|
||||||
return SubWindowDragToResizeArea(
|
return SubWindowDragToResizeArea(
|
||||||
windowId: windowId(),
|
windowId: windowId(),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -73,13 +71,10 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
backgroundColor: MyTheme.color(context).bg,
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
body: DesktopTab(
|
body: DesktopTab(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
theme: theme,
|
|
||||||
onClose: () {
|
onClose: () {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
},
|
},
|
||||||
tail: AddButton(
|
tail: AddButton().paddingOnly(left: 10),
|
||||||
theme: theme,
|
|
||||||
).paddingOnly(left: 10),
|
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -69,7 +69,6 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light();
|
|
||||||
return SubWindowDragToResizeArea(
|
return SubWindowDragToResizeArea(
|
||||||
windowId: windowId(),
|
windowId: windowId(),
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -79,13 +78,10 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
backgroundColor: MyTheme.color(context).bg,
|
backgroundColor: MyTheme.color(context).bg,
|
||||||
body: DesktopTab(
|
body: DesktopTab(
|
||||||
controller: tabController,
|
controller: tabController,
|
||||||
theme: theme,
|
|
||||||
onClose: () {
|
onClose: () {
|
||||||
tabController.clear();
|
tabController.clear();
|
||||||
},
|
},
|
||||||
tail: AddButton(
|
tail: AddButton().paddingOnly(left: 10),
|
||||||
theme: theme,
|
|
||||||
).paddingOnly(left: 10),
|
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -118,7 +118,6 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
: DesktopTab(
|
: DesktopTab(
|
||||||
theme: isDarkTheme() ? TarBarTheme.dark() : TarBarTheme.light(),
|
|
||||||
showTitle: false,
|
showTitle: false,
|
||||||
showMaximize: false,
|
showMaximize: false,
|
||||||
showMinimize: true,
|
showMinimize: true,
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
|
import '../../common/formatter/id_formatter.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/peer_model.dart';
|
import '../../models/peer_model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
@ -47,7 +48,7 @@ class _PeerCard extends StatefulWidget {
|
|||||||
class _PeerCardState extends State<_PeerCard>
|
class _PeerCardState extends State<_PeerCard>
|
||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
var _menuPos = RelativeRect.fill;
|
var _menuPos = RelativeRect.fill;
|
||||||
final double _cardRadis = 20;
|
final double _cardRadis = 16;
|
||||||
final double _borderWidth = 2;
|
final double _borderWidth = 2;
|
||||||
final RxBool _iconMoreHover = false.obs;
|
final RxBool _iconMoreHover = false.obs;
|
||||||
|
|
||||||
@ -119,7 +120,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
? Colors.green
|
? Colors.green
|
||||||
: Colors.yellow)),
|
: Colors.yellow)),
|
||||||
Text(
|
Text(
|
||||||
'${peer.id}',
|
formatID('${peer.id}'),
|
||||||
style: TextStyle(fontWeight: FontWeight.w400),
|
style: TextStyle(fontWeight: FontWeight.w400),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
@ -240,7 +241,7 @@ class _PeerCardState extends State<_PeerCard>
|
|||||||
backgroundColor: peer.online
|
backgroundColor: peer.online
|
||||||
? Colors.green
|
? Colors.green
|
||||||
: Colors.yellow)),
|
: Colors.yellow)),
|
||||||
Text(peer.id)
|
Text(formatID(peer.id))
|
||||||
]).paddingSymmetric(vertical: 8),
|
]).paddingSymmetric(vertical: 8),
|
||||||
_actionMore(peer),
|
_actionMore(peer),
|
||||||
],
|
],
|
||||||
@ -562,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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -749,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: () {
|
||||||
@ -768,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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -870,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(
|
||||||
@ -897,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
|
||||||
@ -957,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,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
import './material_mod_popup_menu.dart' as mod_menu;
|
import './material_mod_popup_menu.dart' as mod_menu;
|
||||||
@ -174,8 +175,8 @@ class MenuEntryRadios<T> extends MenuEntryBase<T> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
opt.text,
|
opt.text,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: MyTheme.color(context).text,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
),
|
),
|
||||||
@ -256,8 +257,8 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
opt.text,
|
opt.text,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: MyTheme.color(context).text,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
),
|
),
|
||||||
@ -300,8 +301,8 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
|||||||
const SizedBox(width: MenuConfig.midPadding),
|
const SizedBox(width: MenuConfig.midPadding),
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: MyTheme.color(context).text,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
),
|
),
|
||||||
@ -346,8 +347,8 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
|||||||
// const SizedBox(width: MenuConfig.midPadding),
|
// const SizedBox(width: MenuConfig.midPadding),
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: MyTheme.color(context).text,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
),
|
),
|
||||||
@ -450,8 +451,8 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
|||||||
const SizedBox(width: MenuConfig.midPadding),
|
const SizedBox(width: MenuConfig.midPadding),
|
||||||
Text(
|
Text(
|
||||||
text,
|
text,
|
||||||
style: const TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.black,
|
color: MyTheme.color(context).text,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
),
|
),
|
||||||
@ -491,8 +492,8 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
|||||||
alignment: AlignmentDirectional.centerStart,
|
alignment: AlignmentDirectional.centerStart,
|
||||||
constraints: BoxConstraints(minHeight: conf.height),
|
constraints: BoxConstraints(minHeight: conf.height),
|
||||||
child: childBuilder(
|
child: childBuilder(
|
||||||
const TextStyle(
|
TextStyle(
|
||||||
color: Colors.black,
|
color: MyTheme.color(context).text,
|
||||||
fontSize: MenuConfig.fontSize,
|
fontSize: MenuConfig.fontSize,
|
||||||
fontWeight: FontWeight.normal),
|
fontWeight: FontWeight.normal),
|
||||||
)),
|
)),
|
||||||
|
|||||||
@ -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')),
|
);
|
||||||
),
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import 'dart:async';
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart' hide TabBarTheme;
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/main.dart';
|
import 'package:flutter_hbb/main.dart';
|
||||||
@ -158,8 +158,7 @@ class DesktopTabController {
|
|||||||
|
|
||||||
class TabThemeConf {
|
class TabThemeConf {
|
||||||
double iconSize;
|
double iconSize;
|
||||||
TarBarTheme theme;
|
TabThemeConf({required this.iconSize});
|
||||||
TabThemeConf({required this.iconSize, required this.theme});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef TabBuilder = Widget Function(
|
typedef TabBuilder = Widget Function(
|
||||||
@ -168,7 +167,6 @@ typedef LabelGetter = Rx<String> Function(String key);
|
|||||||
|
|
||||||
class DesktopTab extends StatelessWidget {
|
class DesktopTab extends StatelessWidget {
|
||||||
final Function(String)? onTabClose;
|
final Function(String)? onTabClose;
|
||||||
final TarBarTheme theme;
|
|
||||||
final bool showTabBar;
|
final bool showTabBar;
|
||||||
final bool showLogo;
|
final bool showLogo;
|
||||||
final bool showTitle;
|
final bool showTitle;
|
||||||
@ -189,7 +187,6 @@ class DesktopTab extends StatelessWidget {
|
|||||||
DesktopTab({
|
DesktopTab({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
this.theme = const TarBarTheme.light(),
|
|
||||||
this.onTabClose,
|
this.onTabClose,
|
||||||
this.showTabBar = true,
|
this.showTabBar = true,
|
||||||
this.showLogo = true,
|
this.showLogo = true,
|
||||||
@ -213,15 +210,15 @@ class DesktopTab extends StatelessWidget {
|
|||||||
return Column(children: [
|
return Column(children: [
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: !showTabBar,
|
offstage: !showTabBar,
|
||||||
child: Container(
|
child: SizedBox(
|
||||||
height: _kTabBarHeight,
|
height: _kTabBarHeight,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Container(
|
SizedBox(
|
||||||
height: _kTabBarHeight - 1,
|
height: _kTabBarHeight - 1,
|
||||||
child: _buildBar(),
|
child: _buildBar(),
|
||||||
),
|
),
|
||||||
Divider(
|
const Divider(
|
||||||
height: 1,
|
height: 1,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
),
|
),
|
||||||
@ -300,7 +297,7 @@ class DesktopTab extends StatelessWidget {
|
|||||||
)),
|
)),
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: !showTitle,
|
offstage: !showTitle,
|
||||||
child: Text(
|
child: const Text(
|
||||||
"RustDesk",
|
"RustDesk",
|
||||||
style: TextStyle(fontSize: 13),
|
style: TextStyle(fontSize: 13),
|
||||||
).marginOnly(left: 2))
|
).marginOnly(left: 2))
|
||||||
@ -321,7 +318,6 @@ class DesktopTab extends StatelessWidget {
|
|||||||
child: _ListView(
|
child: _ListView(
|
||||||
controller: controller,
|
controller: controller,
|
||||||
onTabClose: onTabClose,
|
onTabClose: onTabClose,
|
||||||
theme: theme,
|
|
||||||
tabBuilder: tabBuilder,
|
tabBuilder: tabBuilder,
|
||||||
labelGetter: labelGetter,
|
labelGetter: labelGetter,
|
||||||
)),
|
)),
|
||||||
@ -334,7 +330,6 @@ class DesktopTab extends StatelessWidget {
|
|||||||
mainTab: isMainWindow,
|
mainTab: isMainWindow,
|
||||||
tabType: tabType,
|
tabType: tabType,
|
||||||
state: state,
|
state: state,
|
||||||
theme: theme,
|
|
||||||
showMinimize: showMinimize,
|
showMinimize: showMinimize,
|
||||||
showMaximize: showMaximize,
|
showMaximize: showMaximize,
|
||||||
showClose: showClose,
|
showClose: showClose,
|
||||||
@ -349,7 +344,6 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
final bool mainTab;
|
final bool mainTab;
|
||||||
final DesktopTabType tabType;
|
final DesktopTabType tabType;
|
||||||
final Rx<DesktopTabState> state;
|
final Rx<DesktopTabState> state;
|
||||||
final TarBarTheme theme;
|
|
||||||
|
|
||||||
final bool showMinimize;
|
final bool showMinimize;
|
||||||
final bool showMaximize;
|
final bool showMaximize;
|
||||||
@ -361,7 +355,6 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
required this.mainTab,
|
required this.mainTab,
|
||||||
required this.tabType,
|
required this.tabType,
|
||||||
required this.state,
|
required this.state,
|
||||||
required this.theme,
|
|
||||||
this.showMinimize = true,
|
this.showMinimize = true,
|
||||||
this.showMaximize = true,
|
this.showMaximize = true,
|
||||||
this.showClose = true,
|
this.showClose = true,
|
||||||
@ -377,7 +370,6 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
child: ActionIcon(
|
child: ActionIcon(
|
||||||
message: 'Minimize',
|
message: 'Minimize',
|
||||||
icon: IconFont.min,
|
icon: IconFont.min,
|
||||||
theme: theme,
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
windowManager.minimize();
|
windowManager.minimize();
|
||||||
@ -385,31 +377,30 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
WindowController.fromWindowId(windowId!).minimize();
|
WindowController.fromWindowId(windowId!).minimize();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
is_close: false,
|
isClose: false,
|
||||||
)),
|
)),
|
||||||
// TODO: drag makes window restore
|
// TODO: drag makes window restore
|
||||||
Offstage(
|
Offstage(
|
||||||
offstage: !showMaximize,
|
offstage: !showMaximize,
|
||||||
child: FutureBuilder(builder: (context, snapshot) {
|
child: FutureBuilder(builder: (context, snapshot) {
|
||||||
RxBool is_maximized = false.obs;
|
RxBool isMaximized = false.obs;
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
windowManager.isMaximized().then((maximized) {
|
windowManager.isMaximized().then((maximized) {
|
||||||
is_maximized.value = maximized;
|
isMaximized.value = maximized;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
final wc = WindowController.fromWindowId(windowId!);
|
final wc = WindowController.fromWindowId(windowId!);
|
||||||
wc.isMaximized().then((maximized) {
|
wc.isMaximized().then((maximized) {
|
||||||
is_maximized.value = maximized;
|
isMaximized.value = maximized;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Obx(
|
return Obx(
|
||||||
() => ActionIcon(
|
() => ActionIcon(
|
||||||
message: is_maximized.value ? "Restore" : "Maximize",
|
message: isMaximized.value ? "Restore" : "Maximize",
|
||||||
icon: is_maximized.value ? IconFont.restore : IconFont.max,
|
icon: isMaximized.value ? IconFont.restore : IconFont.max,
|
||||||
theme: theme,
|
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
if (is_maximized.value) {
|
if (isMaximized.value) {
|
||||||
windowManager.unmaximize();
|
windowManager.unmaximize();
|
||||||
} else {
|
} else {
|
||||||
windowManager.maximize();
|
windowManager.maximize();
|
||||||
@ -417,15 +408,15 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
} else {
|
} else {
|
||||||
// TODO: subwindow is maximized but first query result is not maximized.
|
// TODO: subwindow is maximized but first query result is not maximized.
|
||||||
final wc = WindowController.fromWindowId(windowId!);
|
final wc = WindowController.fromWindowId(windowId!);
|
||||||
if (is_maximized.value) {
|
if (isMaximized.value) {
|
||||||
wc.unmaximize();
|
wc.unmaximize();
|
||||||
} else {
|
} else {
|
||||||
wc.maximize();
|
wc.maximize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is_maximized.value = !is_maximized.value;
|
isMaximized.value = !isMaximized.value;
|
||||||
},
|
},
|
||||||
is_close: false,
|
isClose: false,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
})),
|
})),
|
||||||
@ -434,7 +425,6 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
child: ActionIcon(
|
child: ActionIcon(
|
||||||
message: 'Close',
|
message: 'Close',
|
||||||
icon: IconFont.close,
|
icon: IconFont.close,
|
||||||
theme: theme,
|
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
action() {
|
action() {
|
||||||
if (mainTab) {
|
if (mainTab) {
|
||||||
@ -455,29 +445,31 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
is_close: true,
|
isClose: true,
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
@ -488,17 +480,15 @@ class WindowActionPanel extends StatelessWidget {
|
|||||||
class _ListView extends StatelessWidget {
|
class _ListView extends StatelessWidget {
|
||||||
final DesktopTabController controller;
|
final DesktopTabController controller;
|
||||||
final Function(String key)? onTabClose;
|
final Function(String key)? onTabClose;
|
||||||
final TarBarTheme theme;
|
|
||||||
|
|
||||||
final TabBuilder? tabBuilder;
|
final TabBuilder? tabBuilder;
|
||||||
final LabelGetter? labelGetter;
|
final LabelGetter? labelGetter;
|
||||||
|
|
||||||
Rx<DesktopTabState> get state => controller.state;
|
Rx<DesktopTabState> get state => controller.state;
|
||||||
|
|
||||||
_ListView(
|
const _ListView(
|
||||||
{required this.controller,
|
{required this.controller,
|
||||||
required this.onTabClose,
|
required this.onTabClose,
|
||||||
required this.theme,
|
|
||||||
this.tabBuilder,
|
this.tabBuilder,
|
||||||
this.labelGetter});
|
this.labelGetter});
|
||||||
|
|
||||||
@ -508,7 +498,7 @@ class _ListView extends StatelessWidget {
|
|||||||
controller: state.value.scrollController,
|
controller: state.value.scrollController,
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
physics: BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(),
|
||||||
children: state.value.tabs.asMap().entries.map((e) {
|
children: state.value.tabs.asMap().entries.map((e) {
|
||||||
final index = e.key;
|
final index = e.key;
|
||||||
final tab = e.value;
|
final tab = e.value;
|
||||||
@ -523,7 +513,6 @@ class _ListView extends StatelessWidget {
|
|||||||
selected: state.value.selected,
|
selected: state.value.selected,
|
||||||
onClose: () => controller.remove(index),
|
onClose: () => controller.remove(index),
|
||||||
onSelected: () => controller.jumpTo(index),
|
onSelected: () => controller.jumpTo(index),
|
||||||
theme: theme,
|
|
||||||
tabBuilder: tabBuilder == null
|
tabBuilder: tabBuilder == null
|
||||||
? null
|
? null
|
||||||
: (Widget icon, Widget labelWidget, TabThemeConf themeConf) {
|
: (Widget icon, Widget labelWidget, TabThemeConf themeConf) {
|
||||||
@ -540,31 +529,29 @@ class _ListView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _Tab extends StatefulWidget {
|
class _Tab extends StatefulWidget {
|
||||||
late final int index;
|
final int index;
|
||||||
late final Rx<String> label;
|
final Rx<String> label;
|
||||||
late final IconData? selectedIcon;
|
final IconData? selectedIcon;
|
||||||
late final IconData? unselectedIcon;
|
final IconData? unselectedIcon;
|
||||||
late final bool closable;
|
final bool closable;
|
||||||
late final int selected;
|
final int selected;
|
||||||
late final Function() onClose;
|
final Function() onClose;
|
||||||
late final Function() onSelected;
|
final Function() onSelected;
|
||||||
late final TarBarTheme theme;
|
|
||||||
final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)?
|
final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)?
|
||||||
tabBuilder;
|
tabBuilder;
|
||||||
|
|
||||||
_Tab(
|
const _Tab({
|
||||||
{Key? key,
|
Key? key,
|
||||||
required this.index,
|
required this.index,
|
||||||
required this.label,
|
required this.label,
|
||||||
this.selectedIcon,
|
this.selectedIcon,
|
||||||
this.unselectedIcon,
|
this.unselectedIcon,
|
||||||
this.tabBuilder,
|
this.tabBuilder,
|
||||||
required this.closable,
|
required this.closable,
|
||||||
required this.selected,
|
required this.selected,
|
||||||
required this.onClose,
|
required this.onClose,
|
||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
required this.theme})
|
}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<_Tab> createState() => _TabState();
|
State<_Tab> createState() => _TabState();
|
||||||
@ -584,8 +571,8 @@ class _TabState extends State<_Tab> with RestorationMixin {
|
|||||||
isSelected ? widget.selectedIcon : widget.unselectedIcon,
|
isSelected ? widget.selectedIcon : widget.unselectedIcon,
|
||||||
size: _kIconSize,
|
size: _kIconSize,
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? widget.theme.selectedtabIconColor
|
? MyTheme.tabbar(context).selectedTabIconColor
|
||||||
: widget.theme.unSelectedtabIconColor,
|
: MyTheme.tabbar(context).unSelectedTabIconColor,
|
||||||
).paddingOnly(right: 5));
|
).paddingOnly(right: 5));
|
||||||
final labelWidget = Obx(() {
|
final labelWidget = Obx(() {
|
||||||
return Text(
|
return Text(
|
||||||
@ -593,8 +580,8 @@ class _TabState extends State<_Tab> with RestorationMixin {
|
|||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: isSelected
|
color: isSelected
|
||||||
? widget.theme.selectedTextColor
|
? MyTheme.tabbar(context).selectedTextColor
|
||||||
: widget.theme.unSelectedTextColor),
|
: MyTheme.tabbar(context).unSelectedTextColor),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -607,8 +594,8 @@ class _TabState extends State<_Tab> with RestorationMixin {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return widget.tabBuilder!(icon, labelWidget,
|
return widget.tabBuilder!(
|
||||||
TabThemeConf(iconSize: _kIconSize, theme: widget.theme));
|
icon, labelWidget, TabThemeConf(iconSize: _kIconSize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,7 +624,6 @@ class _TabState extends State<_Tab> with RestorationMixin {
|
|||||||
visiable: hover.value && widget.closable,
|
visiable: hover.value && widget.closable,
|
||||||
tabSelected: isSelected,
|
tabSelected: isSelected,
|
||||||
onClose: () => widget.onClose(),
|
onClose: () => widget.onClose(),
|
||||||
theme: widget.theme,
|
|
||||||
)))
|
)))
|
||||||
])).paddingSymmetric(horizontal: 10),
|
])).paddingSymmetric(horizontal: 10),
|
||||||
Offstage(
|
Offstage(
|
||||||
@ -646,7 +632,7 @@ class _TabState extends State<_Tab> with RestorationMixin {
|
|||||||
width: 1,
|
width: 1,
|
||||||
indent: _kDividerIndent,
|
indent: _kDividerIndent,
|
||||||
endIndent: _kDividerIndent,
|
endIndent: _kDividerIndent,
|
||||||
color: widget.theme.dividerColor,
|
color: MyTheme.tabbar(context).dividerColor,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -669,14 +655,12 @@ class _CloseButton extends StatelessWidget {
|
|||||||
final bool visiable;
|
final bool visiable;
|
||||||
final bool tabSelected;
|
final bool tabSelected;
|
||||||
final Function onClose;
|
final Function onClose;
|
||||||
late final TarBarTheme theme;
|
|
||||||
|
|
||||||
_CloseButton({
|
const _CloseButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.visiable,
|
required this.visiable,
|
||||||
required this.tabSelected,
|
required this.tabSelected,
|
||||||
required this.onClose,
|
required this.onClose,
|
||||||
required this.theme,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -692,8 +676,8 @@ class _CloseButton extends StatelessWidget {
|
|||||||
Icons.close,
|
Icons.close,
|
||||||
size: _kIconSize,
|
size: _kIconSize,
|
||||||
color: tabSelected
|
color: tabSelected
|
||||||
? theme.selectedIconColor
|
? MyTheme.tabbar(context).selectedIconColor
|
||||||
: theme.unSelectedIconColor,
|
: MyTheme.tabbar(context).unSelectedIconColor,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)).paddingOnly(left: 5);
|
)).paddingOnly(left: 5);
|
||||||
@ -703,16 +687,14 @@ class _CloseButton extends StatelessWidget {
|
|||||||
class ActionIcon extends StatelessWidget {
|
class ActionIcon extends StatelessWidget {
|
||||||
final String message;
|
final String message;
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final TarBarTheme theme;
|
|
||||||
final Function() onTap;
|
final Function() onTap;
|
||||||
final bool is_close;
|
final bool isClose;
|
||||||
const ActionIcon({
|
const ActionIcon({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.message,
|
required this.message,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.theme,
|
|
||||||
required this.onTap,
|
required this.onTap,
|
||||||
required this.is_close,
|
required this.isClose,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -720,34 +702,32 @@ class ActionIcon extends StatelessWidget {
|
|||||||
RxBool hover = false.obs;
|
RxBool hover = false.obs;
|
||||||
return Obx(() => Tooltip(
|
return Obx(() => Tooltip(
|
||||||
message: translate(message),
|
message: translate(message),
|
||||||
waitDuration: Duration(seconds: 1),
|
waitDuration: const Duration(seconds: 1),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
hoverColor:
|
hoverColor: isClose
|
||||||
is_close ? Color.fromARGB(255, 196, 43, 28) : theme.hoverColor,
|
? const Color.fromARGB(255, 196, 43, 28)
|
||||||
|
: MyTheme.tabbar(context).hoverColor,
|
||||||
onHover: (value) => hover.value = value,
|
onHover: (value) => hover.value = value,
|
||||||
child: Container(
|
onTap: onTap,
|
||||||
|
child: SizedBox(
|
||||||
height: _kTabBarHeight - 1,
|
height: _kTabBarHeight - 1,
|
||||||
width: _kTabBarHeight - 1,
|
width: _kTabBarHeight - 1,
|
||||||
child: Icon(
|
child: Icon(
|
||||||
icon,
|
icon,
|
||||||
color: hover.value && is_close
|
color: hover.value && isClose
|
||||||
? Colors.white
|
? Colors.white
|
||||||
: theme.unSelectedIconColor,
|
: MyTheme.tabbar(context).unSelectedIconColor,
|
||||||
size: _kActionIconSize,
|
size: _kActionIconSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onTap: onTap,
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddButton extends StatelessWidget {
|
class AddButton extends StatelessWidget {
|
||||||
late final TarBarTheme theme;
|
const AddButton({
|
||||||
|
|
||||||
AddButton({
|
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.theme,
|
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -755,41 +735,101 @@ class AddButton extends StatelessWidget {
|
|||||||
return ActionIcon(
|
return ActionIcon(
|
||||||
message: 'New Connection',
|
message: 'New Connection',
|
||||||
icon: IconFont.add,
|
icon: IconFont.add,
|
||||||
theme: theme,
|
|
||||||
onTap: () =>
|
onTap: () =>
|
||||||
rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""),
|
rustDeskWinManager.call(WindowType.Main, "main_window_on_top", ""),
|
||||||
is_close: false);
|
isClose: false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TarBarTheme {
|
class TabbarTheme extends ThemeExtension<TabbarTheme> {
|
||||||
final Color unSelectedtabIconColor;
|
final Color? selectedTabIconColor;
|
||||||
final Color selectedtabIconColor;
|
final Color? unSelectedTabIconColor;
|
||||||
final Color selectedTextColor;
|
final Color? selectedTextColor;
|
||||||
final Color unSelectedTextColor;
|
final Color? unSelectedTextColor;
|
||||||
final Color selectedIconColor;
|
final Color? selectedIconColor;
|
||||||
final Color unSelectedIconColor;
|
final Color? unSelectedIconColor;
|
||||||
final Color dividerColor;
|
final Color? dividerColor;
|
||||||
final Color hoverColor;
|
final Color? hoverColor;
|
||||||
|
|
||||||
const TarBarTheme.light()
|
const TabbarTheme(
|
||||||
: unSelectedtabIconColor = const Color.fromARGB(255, 162, 203, 241),
|
{required this.selectedTabIconColor,
|
||||||
selectedtabIconColor = MyTheme.accent,
|
required this.unSelectedTabIconColor,
|
||||||
selectedTextColor = const Color.fromARGB(255, 26, 26, 26),
|
required this.selectedTextColor,
|
||||||
unSelectedTextColor = const Color.fromARGB(255, 96, 96, 96),
|
required this.unSelectedTextColor,
|
||||||
selectedIconColor = const Color.fromARGB(255, 26, 26, 26),
|
required this.selectedIconColor,
|
||||||
unSelectedIconColor = const Color.fromARGB(255, 96, 96, 96),
|
required this.unSelectedIconColor,
|
||||||
dividerColor = const Color.fromARGB(255, 238, 238, 238),
|
required this.dividerColor,
|
||||||
hoverColor = const Color.fromARGB(
|
required this.hoverColor});
|
||||||
51, 158, 158, 158); // Colors.grey; //0xFF9E9E9E
|
|
||||||
|
|
||||||
const TarBarTheme.dark()
|
static const light = TabbarTheme(
|
||||||
: unSelectedtabIconColor = const Color.fromARGB(255, 30, 65, 98),
|
selectedTabIconColor: MyTheme.accent,
|
||||||
selectedtabIconColor = MyTheme.accent,
|
unSelectedTabIconColor: Color.fromARGB(255, 162, 203, 241),
|
||||||
selectedTextColor = const Color.fromARGB(255, 255, 255, 255),
|
selectedTextColor: Color.fromARGB(255, 26, 26, 26),
|
||||||
unSelectedTextColor = const Color.fromARGB(255, 207, 207, 207),
|
unSelectedTextColor: Color.fromARGB(255, 96, 96, 96),
|
||||||
selectedIconColor = const Color.fromARGB(255, 215, 215, 215),
|
selectedIconColor: Color.fromARGB(255, 26, 26, 26),
|
||||||
unSelectedIconColor = const Color.fromARGB(255, 255, 255, 255),
|
unSelectedIconColor: Color.fromARGB(255, 96, 96, 96),
|
||||||
dividerColor = const Color.fromARGB(255, 64, 64, 64),
|
dividerColor: Color.fromARGB(255, 238, 238, 238),
|
||||||
hoverColor = Colors.black26;
|
hoverColor: Color.fromARGB(51, 158, 158, 158));
|
||||||
|
|
||||||
|
static const dark = TabbarTheme(
|
||||||
|
selectedTabIconColor: MyTheme.accent,
|
||||||
|
unSelectedTabIconColor: Color.fromARGB(255, 30, 65, 98),
|
||||||
|
selectedTextColor: Color.fromARGB(255, 255, 255, 255),
|
||||||
|
unSelectedTextColor: Color.fromARGB(255, 207, 207, 207),
|
||||||
|
selectedIconColor: Color.fromARGB(255, 215, 215, 215),
|
||||||
|
unSelectedIconColor: Color.fromARGB(255, 255, 255, 255),
|
||||||
|
dividerColor: Color.fromARGB(255, 64, 64, 64),
|
||||||
|
hoverColor: Colors.black26);
|
||||||
|
|
||||||
|
@override
|
||||||
|
ThemeExtension<TabbarTheme> copyWith({
|
||||||
|
Color? selectedTabIconColor,
|
||||||
|
Color? unSelectedTabIconColor,
|
||||||
|
Color? selectedTextColor,
|
||||||
|
Color? unSelectedTextColor,
|
||||||
|
Color? selectedIconColor,
|
||||||
|
Color? unSelectedIconColor,
|
||||||
|
Color? dividerColor,
|
||||||
|
Color? hoverColor,
|
||||||
|
}) {
|
||||||
|
return TabbarTheme(
|
||||||
|
selectedTabIconColor: selectedTabIconColor ?? this.selectedTabIconColor,
|
||||||
|
unSelectedTabIconColor:
|
||||||
|
unSelectedTabIconColor ?? this.unSelectedTabIconColor,
|
||||||
|
selectedTextColor: selectedTextColor ?? this.selectedTextColor,
|
||||||
|
unSelectedTextColor: unSelectedTextColor ?? this.unSelectedTextColor,
|
||||||
|
selectedIconColor: selectedIconColor ?? this.selectedIconColor,
|
||||||
|
unSelectedIconColor: unSelectedIconColor ?? this.unSelectedIconColor,
|
||||||
|
dividerColor: dividerColor ?? this.dividerColor,
|
||||||
|
hoverColor: hoverColor ?? this.hoverColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ThemeExtension<TabbarTheme> lerp(
|
||||||
|
ThemeExtension<TabbarTheme>? other, double t) {
|
||||||
|
if (other is! TabbarTheme) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return TabbarTheme(
|
||||||
|
selectedTabIconColor:
|
||||||
|
Color.lerp(selectedTabIconColor, other.selectedTabIconColor, t),
|
||||||
|
unSelectedTabIconColor:
|
||||||
|
Color.lerp(unSelectedTabIconColor, other.unSelectedTabIconColor, t),
|
||||||
|
selectedTextColor:
|
||||||
|
Color.lerp(selectedTextColor, other.selectedTextColor, t),
|
||||||
|
unSelectedTextColor:
|
||||||
|
Color.lerp(unSelectedTextColor, other.unSelectedTextColor, t),
|
||||||
|
selectedIconColor:
|
||||||
|
Color.lerp(selectedIconColor, other.selectedIconColor, t),
|
||||||
|
unSelectedIconColor:
|
||||||
|
Color.lerp(unSelectedIconColor, other.unSelectedIconColor, t),
|
||||||
|
dividerColor: Color.lerp(dividerColor, other.dividerColor, t),
|
||||||
|
hoverColor: Color.lerp(hoverColor, other.hoverColor, t),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static color(BuildContext context) {
|
||||||
|
return Theme.of(context).extension<ColorThemeExtension>()!;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: chatModel,
|
value: chatModel,
|
||||||
child: Container(
|
child: Container(
|
||||||
color: MyTheme.grayBg,
|
color: MyTheme.color(context).grayBg,
|
||||||
child: Consumer<ChatModel>(builder: (context, chatModel, child) {
|
child: Consumer<ChatModel>(builder: (context, chatModel, child) {
|
||||||
final currentUser = chatModel.currentUser;
|
final currentUser = chatModel.currentUser;
|
||||||
return Stack(
|
return Stack(
|
||||||
@ -59,7 +59,14 @@ class ChatPage extends StatelessWidget implements PageShape {
|
|||||||
messages: chatModel
|
messages: chatModel
|
||||||
.messages[chatModel.currentID]?.chatMessages ??
|
.messages[chatModel.currentID]?.chatMessages ??
|
||||||
[],
|
[],
|
||||||
inputOptions: const InputOptions(sendOnEnter: true),
|
inputOptions: InputOptions(
|
||||||
|
sendOnEnter: true,
|
||||||
|
inputDecoration: defaultInputDecoration(
|
||||||
|
fillColor: MyTheme.color(context).bg),
|
||||||
|
sendButtonBuilder: defaultSendButton(
|
||||||
|
color: MyTheme.color(context).text!),
|
||||||
|
inputTextStyle:
|
||||||
|
TextStyle(color: MyTheme.color(context).text)),
|
||||||
messageOptions: MessageOptions(
|
messageOptions: MessageOptions(
|
||||||
showOtherUsersAvatar: false,
|
showOtherUsersAvatar: false,
|
||||||
showTime: true,
|
showTime: true,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import 'package:flutter_hbb/models/platform_model.dart';
|
|||||||
import 'package:wakelock/wakelock.dart';
|
import 'package:wakelock/wakelock.dart';
|
||||||
|
|
||||||
import '../common.dart';
|
import '../common.dart';
|
||||||
|
import '../common/formatter/id_formatter.dart';
|
||||||
import '../desktop/pages/server_page.dart' as Desktop;
|
import '../desktop/pages/server_page.dart' as Desktop;
|
||||||
import '../desktop/widgets/tabbar_widget.dart';
|
import '../desktop/widgets/tabbar_widget.dart';
|
||||||
import '../mobile/pages/server_page.dart';
|
import '../mobile/pages/server_page.dart';
|
||||||
@ -29,7 +30,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
String _temporaryPasswordLength = "";
|
String _temporaryPasswordLength = "";
|
||||||
|
|
||||||
late String _emptyIdShow;
|
late String _emptyIdShow;
|
||||||
late final TextEditingController _serverId;
|
late final IDTextEditingController _serverId;
|
||||||
final _serverPasswd = TextEditingController(text: "");
|
final _serverPasswd = TextEditingController(text: "");
|
||||||
|
|
||||||
final tabController = DesktopTabController(tabType: DesktopTabType.cm);
|
final tabController = DesktopTabController(tabType: DesktopTabType.cm);
|
||||||
@ -88,7 +89,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
|
|
||||||
ServerModel(this.parent) {
|
ServerModel(this.parent) {
|
||||||
_emptyIdShow = translate("Generating ...");
|
_emptyIdShow = translate("Generating ...");
|
||||||
_serverId = TextEditingController(text: this._emptyIdShow);
|
_serverId = IDTextEditingController(text: _emptyIdShow);
|
||||||
|
|
||||||
Timer.periodic(Duration(seconds: 1), (timer) async {
|
Timer.periodic(Duration(seconds: 1), (timer) async {
|
||||||
var status = await bind.mainGetOnlineStatue();
|
var status = await bind.mainGetOnlineStatue();
|
||||||
@ -208,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();
|
||||||
}
|
}
|
||||||
@ -300,7 +303,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fetchID() async {
|
_fetchID() async {
|
||||||
final old = _serverId.text;
|
final old = _serverId.id;
|
||||||
var count = 0;
|
var count = 0;
|
||||||
const maxCount = 10;
|
const maxCount = 10;
|
||||||
while (count < maxCount) {
|
while (count < maxCount) {
|
||||||
@ -309,12 +312,12 @@ class ServerModel with ChangeNotifier {
|
|||||||
if (id.isEmpty) {
|
if (id.isEmpty) {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
_serverId.text = id;
|
_serverId.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugPrint("fetch id again at $count:id:${_serverId.text}");
|
debugPrint("fetch id again at $count:id:${_serverId.id}");
|
||||||
count++;
|
count++;
|
||||||
if (_serverId.text != old) {
|
if (_serverId.id != old) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -387,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() {
|
||||||
@ -562,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,
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user