diff --git a/flutter/analysis_options.yaml b/flutter/analysis_options.yaml index 61b6c4de1..a679f5774 100644 --- a/flutter/analysis_options.yaml +++ b/flutter/analysis_options.yaml @@ -1,29 +1,6 @@ -# This file configures the analyzer, which statically analyzes Dart code to -# check for errors, warnings, and lints. -# -# The issues identified by the analyzer are surfaced in the UI of Dart-enabled -# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be -# invoked from the command line by running `flutter analyze`. - -# The following line activates a set of recommended lints for Flutter apps, -# packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml +include: package:lints/recommended.yaml linter: - # The lint rules applied to this project can be customized in the - # section below to disable rules from the `package:flutter_lints/flutter.yaml` - # included above or to enable additional rules. A list of all available lints - # and their documentation is published at - # https://dart-lang.github.io/linter/lints/index.html. - # - # Instead of disabling a lint rule for the entire project in the - # section below, it can also be suppressed for a single line of code - # or a specific dart file by using the `// ignore: name_of_lint` and - # `// ignore_for_file: name_of_lint` syntax on the line or in the file - # producing the lint. rules: - # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule - -# Additional information about this file can be found at -# https://dart.dev/guides/language/analysis-options + non_constant_identifier_names: false + sort_child_properties_last: false diff --git a/flutter/lib/common.dart b/flutter/lib/common.dart index 70e10a9da..0b56e9f1a 100644 --- a/flutter/lib/common.dart +++ b/flutter/lib/common.dart @@ -244,6 +244,32 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom( ), ); +List supportedLocales = const [ + // specify CN/TW to fix CJK issue in flutter + Locale('zh', 'CN'), + Locale('zh', 'TW'), + Locale('zh', 'SG'), + Locale('fr'), + Locale('de'), + Locale('it'), + Locale('ja'), + Locale('cs'), + Locale('pl'), + Locale('ko'), + Locale('hu'), + Locale('pt'), + Locale('ru'), + Locale('sk'), + Locale('id'), + Locale('da'), + Locale('eo'), + Locale('tr'), + Locale('vi'), + Locale('pl'), + Locale('kz'), + Locale('en', 'US'), +]; + String formatDurationToTime(Duration duration) { var totalTime = duration.inSeconds; final secs = totalTime % 60; @@ -734,8 +760,9 @@ class PermissionManager { if (isDesktop) { return Future.value(true); } - if (!permissions.contains(type)) + if (!permissions.contains(type)) { return Future.error("Wrong permission!$type"); + } return gFFI.invokeMethod("check_permission", type); } @@ -743,8 +770,9 @@ class PermissionManager { if (isDesktop) { return Future.value(true); } - if (!permissions.contains(type)) + if (!permissions.contains(type)) { return Future.error("Wrong permission!$type"); + } gFFI.invokeMethod("request_permission", type); if (type == "ignore_battery_optimizations") { diff --git a/flutter/lib/common/formatter/id_formatter.dart b/flutter/lib/common/formatter/id_formatter.dart index c7ce14da4..a9e4893a6 100644 --- a/flutter/lib/common/formatter/id_formatter.dart +++ b/flutter/lib/common/formatter/id_formatter.dart @@ -33,6 +33,7 @@ class IDTextInputFormatter extends TextInputFormatter { String formatID(String id) { String id2 = id.replaceAll(' ', ''); + if (int.tryParse(id2) == null) return id; String newID = ''; if (id2.length <= 3) { newID = id2; diff --git a/flutter/lib/common/widgets/peer_widget.dart b/flutter/lib/common/widgets/peer_widget.dart index 9ae1a6340..f32b8a2f1 100644 --- a/flutter/lib/common/widgets/peer_widget.dart +++ b/flutter/lib/common/widgets/peer_widget.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:visibility_detector/visibility_detector.dart'; @@ -41,6 +42,7 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener { static const int _maxQueryCount = 3; final space = isDesktop ? 12.0 : 8.0; final _curPeers = {}; + final _scrollController = ScrollController(); var _lastChangeTime = DateTime.now(); var _lastQueryPeers = {}; var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1)); @@ -94,57 +96,59 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener { ? Center( child: Text(translate("Empty")), ) - : SingleChildScrollView( - controller: ScrollController(), - child: ObxValue((searchText) { - return FutureBuilder>( - builder: (context, snapshot) { - if (snapshot.hasData) { - final peers = snapshot.data!; - final cards = []; - for (final peer in peers) { - final visibilityChild = VisibilityDetector( - key: ValueKey(peer.id), - onVisibilityChanged: (info) { - final peerId = (info.key as ValueKey).value; - if (info.visibleFraction > 0.00001) { - _curPeers.add(peerId); - } else { - _curPeers.remove(peerId); - } - _lastChangeTime = DateTime.now(); - }, - child: widget.peerCardWidgetFunc(peer), - ); - cards.add(Offstage( - key: ValueKey("off${peer.id}"), - offstage: widget.offstageFunc(peer), - child: isDesktop - ? Obx( - () => SizedBox( - width: 220, - height: peerCardUiType.value == - PeerUiType.grid + : DesktopScrollWrapper( + scrollController: _scrollController, + child: SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + controller: _scrollController, + child: ObxValue((searchText) { + return FutureBuilder>( + builder: (context, snapshot) { + if (snapshot.hasData) { + final peers = snapshot.data!; + final cards = []; + for (final peer in peers) { + cards.add(Offstage( + key: ValueKey("off${peer.id}"), + offstage: widget.offstageFunc(peer), + child: Obx( + () => SizedBox( + width: 220, + height: + peerCardUiType.value == PeerUiType.grid ? 140 : 42, - child: visibilityChild, - ), - ) - : SizedBox( - width: mobileWidth, - child: visibilityChild))); + child: VisibilityDetector( + key: ValueKey(peer.id), + onVisibilityChanged: (info) { + final peerId = + (info.key as ValueKey).value; + if (info.visibleFraction > 0.00001) { + _curPeers.add(peerId); + } else { + _curPeers.remove(peerId); + } + _lastChangeTime = DateTime.now(); + }, + child: widget.peerCardWidgetFunc(peer), + ), + ), + ))); + } + return Wrap( + spacing: space, + runSpacing: space, + children: cards); + } else { + return const Center( + child: CircularProgressIndicator(), + ); } - return Wrap( - spacing: space, runSpacing: space, children: cards); - } else { - return const Center( - child: CircularProgressIndicator(), - ); - } - }, - future: matchPeers(searchText.value, peers.peers), - ); - }, peerSearchText), + }, + future: matchPeers(searchText.value, peers.peers), + ); + }, peerSearchText), + ), ), ), ); diff --git a/flutter/lib/consts.dart b/flutter/lib/consts.dart index 4f710ea2c..ff15173d3 100644 --- a/flutter/lib/consts.dart +++ b/flutter/lib/consts.dart @@ -17,6 +17,13 @@ const int kMobileDefaultDisplayHeight = 1280; const int kDesktopDefaultDisplayWidth = 1080; const int kDesktopDefaultDisplayHeight = 720; +/// [kDefaultScrollAmountMultiplier] indicates how many rows can be scrolled after a minimum scroll action of mouse +const kDefaultScrollAmountMultiplier = 5.0; +const kDefaultScrollDuration = Duration(milliseconds: 50); +const kDefaultMouseWhellThrottleDuration = Duration(milliseconds: 50); +const kFullScreenEdgeSize = 0.0; +const kWindowEdgeSize = 1.0; + const kInvalidValueStr = "InvalidValueStr"; const kMobilePageConstraints = BoxConstraints(maxWidth: 600); diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart index faf798f0d..6e1eac814 100644 --- a/flutter/lib/desktop/pages/connection_page.dart +++ b/flutter/lib/desktop/pages/connection_page.dart @@ -1,3 +1,5 @@ +// main window right pane + import 'dart:async'; import 'dart:convert'; @@ -82,8 +84,8 @@ class _ConnectionPageState extends State { ).marginSymmetric(horizontal: 22), ), const Divider(), - SizedBox(height: 50, child: Obx(() => buildStatus())) - .paddingSymmetric(horizontal: 12.0) + SizedBox(child: Obx(() => buildStatus())) + .paddingOnly(bottom: 12, top: 6), ]), ); } @@ -187,7 +189,7 @@ class _ConnectionPageState extends State { onConnect(isFileTransfer: true); }, child: Container( - height: 24, + height: 27, alignment: Alignment.center, decoration: BoxDecoration( color: ftPressed.value @@ -224,31 +226,36 @@ class _ConnectionPageState extends State { onTapCancel: () => connPressed.value = false, onHover: (value) => connHover.value = value, onTap: onConnect, - child: Container( - height: 24, - decoration: BoxDecoration( - color: connPressed.value - ? MyTheme.accent - : MyTheme.button, - border: Border.all( - color: connPressed.value - ? MyTheme.accent - : connHover.value - ? MyTheme.hoverBorder - : MyTheme.button, + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: 80.0, ), - borderRadius: BorderRadius.circular(5), - ), - child: Center( - child: Text( - translate( - "Connect", + child: Container( + height: 27, + decoration: BoxDecoration( + color: connPressed.value + ? MyTheme.accent + : MyTheme.button, + border: Border.all( + color: connPressed.value + ? MyTheme.accent + : connHover.value + ? MyTheme.hoverBorder + : MyTheme.button, + ), + borderRadius: BorderRadius.circular(5), ), - style: TextStyle( - fontSize: 12, color: MyTheme.color(context).bg), - ), - ).marginSymmetric(horizontal: 12), - ), + child: Center( + child: Text( + translate( + "Connect", + ), + style: TextStyle( + fontSize: 12, + color: MyTheme.color(context).bg), + ), + ).marginSymmetric(horizontal: 12), + )), ), ), ], @@ -275,6 +282,8 @@ class _ConnectionPageState extends State { var svcIsUsingPublicServer = true.obs; Widget buildStatus() { + final fontSize = 14.0; + final textStyle = TextStyle(fontSize: fontSize); final light = Container( height: 8, width: 8, @@ -282,13 +291,13 @@ class _ConnectionPageState extends State { borderRadius: BorderRadius.circular(20), color: svcStopped.value ? Colors.redAccent : Colors.green, ), - ).paddingSymmetric(horizontal: 10.0); + ).paddingSymmetric(horizontal: 12.0); if (svcStopped.value) { return Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ light, - Text(translate("Service is not running")), + Text(translate("Service is not running"), style: textStyle), TextButton( onPressed: () async { bool checked = await bind.mainCheckSuperUserPermission(); @@ -296,19 +305,25 @@ class _ConnectionPageState extends State { bind.mainSetOption(key: "stop-service", value: ""); } }, - child: Text(translate("Start Service"))) + child: Text(translate("Start Service"), style: textStyle)) ], ); } else { if (svcStatusCode.value == 0) { return Row( crossAxisAlignment: CrossAxisAlignment.center, - children: [light, Text(translate("connecting_status"))], + children: [ + light, + Text(translate("connecting_status"), style: textStyle) + ], ); } else if (svcStatusCode.value == -1) { return Row( crossAxisAlignment: CrossAxisAlignment.center, - children: [light, Text(translate("not_ready_status"))], + children: [ + light, + Text(translate("not_ready_status"), style: textStyle) + ], ); } } @@ -316,13 +331,15 @@ class _ConnectionPageState extends State { crossAxisAlignment: CrossAxisAlignment.center, children: [ light, - Text(translate('Ready')), + Text(translate('Ready'), style: textStyle), + Text(', ', style: textStyle), svcIsUsingPublicServer.value ? InkWell( onTap: onUsePublicServerGuide, child: Text( - ', ${translate('setup_server_tip')}', - style: TextStyle(decoration: TextDecoration.underline), + translate('setup_server_tip'), + style: TextStyle( + decoration: TextDecoration.underline, fontSize: fontSize), ), ) : Offstage() diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart index c8706d5a0..edae7deeb 100644 --- a/flutter/lib/desktop/pages/desktop_home_page.dart +++ b/flutter/lib/desktop/pages/desktop_home_page.dart @@ -5,29 +5,14 @@ import 'package:flutter/material.dart' hide MenuItem; import 'package:flutter/services.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/desktop/pages/connection_page.dart'; -import 'package:flutter_hbb/desktop/pages/desktop_setting_page.dart'; -import 'package:flutter_hbb/desktop/widgets/popup_menu.dart'; -import 'package:flutter_hbb/desktop/widgets/material_mod_popup_menu.dart' - as mod_menu; import 'package:flutter_hbb/models/platform_model.dart'; import 'package:flutter_hbb/models/server_model.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:tray_manager/tray_manager.dart'; -import 'package:url_launcher/url_launcher_string.dart'; import 'package:window_manager/window_manager.dart'; -import '../../common/widgets/dialog.dart'; - -class _MenubarTheme { - static const Color commonColor = MyTheme.accent; - // kMinInteractiveDimension - static const double height = 25.0; - static const double dividerHeight = 12.0; -} - class DesktopHomePage extends StatefulWidget { const DesktopHomePage({Key? key}) : super(key: key); @@ -66,19 +51,19 @@ class _DesktopHomePageState extends State super.build(context); return Row( children: [ - buildServerInfo(context), + buildLeftPane(context), const VerticalDivider( width: 1, thickness: 1, ), Expanded( - child: buildServerBoard(context), + child: buildRightPane(context), ), ], ); } - buildServerInfo(BuildContext context) { + buildLeftPane(BuildContext context) { return ChangeNotifierProvider.value( value: gFFI.serverModel, child: Container( @@ -95,7 +80,7 @@ class _DesktopHomePageState extends State ); } - buildServerBoard(BuildContext context) { + buildRightPane(BuildContext context) { return Container( color: MyTheme.color(context).grayBg, child: ConnectionPage(), @@ -167,93 +152,9 @@ class _DesktopHomePageState extends State } Widget buildPopupMenu(BuildContext context) { - var position; RxBool hover = false.obs; return InkWell( - onTapDown: (detail) { - final x = detail.globalPosition.dx; - final y = detail.globalPosition.dy; - position = RelativeRect.fromLTRB(x, y, x, y); - }, - onTap: () async { - final userName = await gFFI.userModel.getUserName(); - final enabledInput = await bind.mainGetOption(key: 'enable-audio'); - final defaultInput = await gFFI.getDefaultAudioInput(); - var menu = [ - await genEnablePopupMenuItem( - translate("Enable Keyboard/Mouse"), - 'enable-keyboard', - ), - await genEnablePopupMenuItem( - translate("Enable Clipboard"), - 'enable-clipboard', - ), - await genEnablePopupMenuItem( - translate("Enable File Transfer"), - 'enable-file-transfer', - ), - await genEnablePopupMenuItem( - translate("Enable TCP Tunneling"), - 'enable-tunnel', - ), - genAudioInputPopupMenuItem(enabledInput != "N", defaultInput), - PopupMenuDivider(), - PopupMenuItem( - child: Text(translate("ID/Relay Server")), - value: 'custom-server', - ), - PopupMenuItem( - child: Text(translate("IP Whitelisting")), - value: 'whitelist', - ), - PopupMenuItem( - child: Text(translate("Socks5 Proxy")), - value: 'socks5-proxy', - ), - PopupMenuDivider(), - await genEnablePopupMenuItem( - translate("Enable Service"), - 'stop-service', - ), - // TODO: direct server - await genEnablePopupMenuItem( - translate("Always connected via relay"), - 'allow-always-relay', - ), - await genEnablePopupMenuItem( - translate("Start ID/relay service"), - 'stop-rendezvous-service', - ), - PopupMenuDivider(), - userName.isEmpty - ? PopupMenuItem( - child: Text(translate("Login")), - value: 'login', - ) - : PopupMenuItem( - child: Text("${translate("Logout")} $userName"), - value: 'logout', - ), - PopupMenuItem( - child: Text(translate("Change ID")), - value: 'change-id', - ), - PopupMenuDivider(), - await genEnablePopupMenuItem( - translate("Dark Theme"), - 'allow-darktheme', - ), - PopupMenuItem( - child: Text(translate("About")), - value: 'about', - ), - ]; - final v = - await showMenu(context: context, position: position, items: menu); - if (v != null) { - onSelectMenu(v); - } - }, + onTap: () async {}, child: Obx( () => CircleAvatar( radius: 15, @@ -276,6 +177,7 @@ class _DesktopHomePageState extends State buildPasswordBoard(BuildContext context) { final model = gFFI.serverModel; RxBool refreshHover = false.obs; + RxBool editHover = false.obs; return Container( margin: EdgeInsets.only(left: 20.0, right: 16, top: 13, bottom: 13), child: Row( @@ -334,7 +236,19 @@ class _DesktopHomePageState extends State onTap: () => bind.mainUpdateTemporaryPassword(), onHover: (value) => refreshHover.value = value, ), - const _PasswordPopupMenu(), + InkWell( + child: Obx( + () => Icon( + Icons.edit, + color: editHover.value + ? MyTheme.color(context).text + : Color(0xFFDDDDDD), + size: 22, + ).marginOnly(right: 8, bottom: 2), + ), + onTap: () => {}, + onHover: (value) => editHover.value = value, + ), ], ), ], @@ -376,7 +290,7 @@ class _DesktopHomePageState extends State @override void onTrayMenuItemClick(MenuItem menuItem) { - print('click ${menuItem.key}'); + debugPrint('click ${menuItem.key}'); switch (menuItem.key) { case "quit": exit(0); @@ -394,8 +308,8 @@ class _DesktopHomePageState extends State trayManager.addListener(this); windowManager.addListener(this); rustDeskWinManager.setMethodHandler((call, fromWindowId) async { - print( - "call ${call.method} with args ${call.arguments} from window ${fromWindowId}"); + debugPrint( + "call ${call.method} with args ${call.arguments} from window $fromWindowId"); if (call.method == "main_window_on_top") { window_on_top(null); } @@ -408,236 +322,6 @@ class _DesktopHomePageState extends State windowManager.removeListener(this); super.dispose(); } - - void changeTheme(String choice) async { - if (choice == "Y") { - Get.changeTheme(MyTheme.darkTheme); - } else { - Get.changeTheme(MyTheme.lightTheme); - } - Get.find().setString("darkTheme", choice); - Get.forceAppUpdate(); - } - - void onSelectMenu(String key) async { - if (key.startsWith('enable-')) { - final option = await bind.mainGetOption(key: key); - bind.mainSetOption(key: key, value: option == "N" ? "" : "N"); - } else if (key.startsWith('allow-')) { - final option = await bind.mainGetOption(key: key); - final choice = option == "Y" ? "" : "Y"; - bind.mainSetOption(key: key, value: choice); - if (key == "allow-darktheme") changeTheme(choice); - } else if (key == "stop-service") { - final option = await bind.mainGetOption(key: key); - bind.mainSetOption(key: key, value: option == "Y" ? "" : "Y"); - } else if (key == "change-id") { - changeIdDialog(); - } else if (key == "custom-server") { - changeServer(); - } else if (key == "whitelist") { - changeWhiteList(); - } else if (key == "socks5-proxy") { - changeSocks5Proxy(); - } else if (key == "about") { - about(); - } else if (key == "logout") { - logOut(); - } else if (key == "login") { - login(); - } - } - - Future> genEnablePopupMenuItem( - String label, String key) async { - final v = await bind.mainGetOption(key: key); - bool enable; - if (key == "stop-service") { - enable = v != "Y"; - } else if (key.startsWith("allow-")) { - enable = v == "Y"; - } else { - enable = v != "N"; - } - - return PopupMenuItem( - child: Row( - children: [ - Icon(Icons.check, - color: enable ? null : MyTheme.accent.withAlpha(00)), - Text( - label, - style: genTextStyle(enable), - ), - ], - ), - value: key, - ); - } - - TextStyle genTextStyle(bool isPositive) { - return isPositive - ? TextStyle() - : TextStyle( - color: Colors.redAccent, decoration: TextDecoration.lineThrough); - } - - PopupMenuItem genAudioInputPopupMenuItem( - bool enableInput, String defaultAudioInput) { - final defaultInput = defaultAudioInput.obs; - final enabled = enableInput.obs; - - return PopupMenuItem( - child: FutureBuilder>( - future: gFFI.getAudioInputs(), - builder: (context, snapshot) { - if (snapshot.hasData) { - final inputs = snapshot.data!.toList(); - if (Platform.isWindows) { - inputs.insert(0, translate("System Sound")); - } - var inputList = inputs - .map((e) => PopupMenuItem( - child: Row( - children: [ - Obx(() => Offstage( - offstage: defaultInput.value != e, - child: Icon(Icons.check))), - Expanded( - child: Tooltip( - message: e, - child: Text( - "$e", - maxLines: 1, - overflow: TextOverflow.ellipsis, - ))), - ], - ), - value: e, - )) - .toList(); - inputList.insert( - 0, - PopupMenuItem( - child: Row( - children: [ - Obx(() => Offstage( - offstage: enabled.value, child: Icon(Icons.check))), - Expanded(child: Text(translate("Mute"))), - ], - ), - value: "Mute", - )); - return PopupMenuButton( - padding: EdgeInsets.zero, - child: Container( - alignment: Alignment.centerLeft, - child: Text(translate("Audio Input"))), - itemBuilder: (context) => inputList, - onSelected: (dev) async { - if (dev == "Mute") { - await bind.mainSetOption( - key: 'enable-audio', value: enabled.value ? '' : 'N'); - enabled.value = - await bind.mainGetOption(key: 'enable-audio') != 'N'; - } else if (dev != await gFFI.getDefaultAudioInput()) { - gFFI.setDefaultAudioInput(dev); - defaultInput.value = dev; - } - }, - ); - } else { - return Text("..."); - } - }, - ), - value: 'audio-input', - ); - } - - void about() async { - final appName = await bind.mainGetAppName(); - final license = await bind.mainGetLicense(); - final version = await bind.mainGetVersion(); - const linkStyle = TextStyle(decoration: TextDecoration.underline); - gFFI.dialogManager.show((setState, close) { - return CustomAlertDialog( - title: Text("About $appName"), - content: ConstrainedBox( - constraints: const BoxConstraints(minWidth: 500), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox( - height: 8.0, - ), - Text("Version: $version").marginSymmetric(vertical: 4.0), - InkWell( - onTap: () { - launchUrlString("https://rustdesk.com/privacy"); - }, - child: const Text( - "Privacy Statement", - style: linkStyle, - ).marginSymmetric(vertical: 4.0)), - InkWell( - onTap: () { - launchUrlString("https://rustdesk.com"); - }, - child: const Text( - "Website", - style: linkStyle, - ).marginSymmetric(vertical: 4.0)), - Container( - decoration: const BoxDecoration(color: Color(0xFF2c8cff)), - padding: - const EdgeInsets.symmetric(vertical: 24, horizontal: 8), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Copyright © 2022 Purslane Ltd.\n$license", - style: const TextStyle(color: Colors.white), - ), - const Text( - "Made with heart in this chaotic world!", - style: TextStyle( - fontWeight: FontWeight.w800, - color: Colors.white), - ) - ], - ), - ), - ], - ), - ).marginSymmetric(vertical: 4.0) - ], - ), - ), - actions: [ - TextButton(onPressed: close, child: Text(translate("OK"))), - ], - onSubmit: close, - onCancel: close, - ); - }); - } - - void login() { - loginDialog().then((success) { - if (success) { - // refresh frame - setState(() {}); - } - }); - } - - void logOut() { - gFFI.userModel.logOut().then((_) => {setState(() {})}); - } } /// common login dialog for desktop @@ -689,8 +373,7 @@ Future loginDialog() async { debugPrint("$resp"); completer.complete(true); } catch (err) { - // ignore: avoid_print - print(err.toString()); + debugPrint(err.toString()); cancel(); return; } @@ -874,120 +557,3 @@ void setPasswordDialog() async { ); }); } - -class _PasswordPopupMenu extends StatefulWidget { - const _PasswordPopupMenu({Key? key}) : super(key: key); - - @override - State<_PasswordPopupMenu> createState() => _PasswordPopupMenuState(); -} - -class _PasswordPopupMenuState extends State<_PasswordPopupMenu> { - final RxBool _tempEnabled = true.obs; - final RxBool _permEnabled = true.obs; - - List> _buildMenus() { - return >[ - MenuEntryRadios( - text: translate('Password type'), - optionsGetter: () => [ - MenuEntryRadioOption( - text: translate('Use temporary password'), - value: kUseTemporaryPassword), - MenuEntryRadioOption( - text: translate('Use permanent password'), - value: kUsePermanentPassword), - MenuEntryRadioOption( - text: translate('Use both passwords'), - value: kUseBothPasswords), - ], - curOptionGetter: () async { - return gFFI.serverModel.verificationMethod; - }, - optionSetter: (String oldValue, String newValue) async { - await bind.mainSetOption( - key: "verification-method", value: newValue); - await gFFI.serverModel.updatePasswordModel(); - setState(() { - _tempEnabled.value = - gFFI.serverModel.verificationMethod != kUsePermanentPassword; - _permEnabled.value = - gFFI.serverModel.verificationMethod != kUseTemporaryPassword; - }); - }), - MenuEntryDivider(), - MenuEntryButton( - enabled: _permEnabled, - childBuilder: (TextStyle? style) => Text( - translate('Set permanent password'), - style: style, - ), - proc: () { - setPasswordDialog(); - }, - dismissOnClicked: true, - ), - MenuEntrySubMenu( - enabled: _tempEnabled, - text: translate('Set temporary password length'), - entries: [ - MenuEntryRadios( - enabled: _tempEnabled, - text: translate(''), - optionsGetter: () => [ - MenuEntryRadioOption( - text: translate('6'), - value: '6', - enabled: _tempEnabled, - ), - MenuEntryRadioOption( - text: translate('8'), - value: '8', - enabled: _tempEnabled, - ), - MenuEntryRadioOption( - text: translate('10'), - value: '10', - enabled: _tempEnabled, - ), - ], - curOptionGetter: () async { - return gFFI.serverModel.temporaryPasswordLength; - }, - optionSetter: (String oldValue, String newValue) async { - if (oldValue != newValue) { - await gFFI.serverModel.setTemporaryPasswordLength(newValue); - await gFFI.serverModel.updatePasswordModel(); - } - }), - ]) - ]; - } - - @override - Widget build(BuildContext context) { - final editHover = false.obs; - return mod_menu.PopupMenuButton( - padding: EdgeInsets.zero, - onHover: (v) => editHover.value = v, - tooltip: translate(''), - position: mod_menu.PopupMenuPosition.overSide, - itemBuilder: (BuildContext context) => _buildMenus() - .map((entry) => entry.build( - context, - const MenuConfig( - commonColor: _MenubarTheme.commonColor, - height: _MenubarTheme.height, - dividerHeight: _MenubarTheme.dividerHeight, - ))) - .expand((i) => i) - .toList(), - child: Obx(() => Icon(Icons.edit, - size: 22, - color: editHover.value - ? MyTheme.color(context).text - : const Color(0xFFDDDDDD)) - .marginOnly(bottom: 2)), - ); - } -} diff --git a/flutter/lib/desktop/pages/desktop_tab_page.dart b/flutter/lib/desktop/pages/desktop_tab_page.dart index fde543a89..2f24ddbde 100644 --- a/flutter/lib/desktop/pages/desktop_tab_page.dart +++ b/flutter/lib/desktop/pages/desktop_tab_page.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; import 'package:flutter_hbb/consts.dart'; @@ -35,28 +37,30 @@ class _DesktopTabPageState extends State { Widget build(BuildContext context) { RxBool fullscreen = false.obs; Get.put(fullscreen, tag: 'fullscreen'); - return Obx(() => DragToResizeArea( - resizeEdgeSize: fullscreen.value ? 1.0 : 8.0, - child: Container( - decoration: BoxDecoration( - border: Border.all(color: MyTheme.color(context).border!)), - child: Overlay(initialEntries: [ - OverlayEntry(builder: (context) { - gFFI.dialogManager.setOverlayState(Overlay.of(context)); - return Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: DesktopTab( - controller: tabController, - tail: ActionIcon( - message: 'Settings', - icon: IconFont.menu, - onTap: onAddSetting, - isClose: false, - ), - )); - }) - ]), - ))); + final tabWidget = Container( + child: Overlay(initialEntries: [ + OverlayEntry(builder: (context) { + gFFI.dialogManager.setOverlayState(Overlay.of(context)); + return Scaffold( + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabController, + tail: ActionIcon( + message: 'Settings', + icon: IconFont.menu, + onTap: onAddSetting, + isClose: false, + ), + )); + }) + ]), + ); + return Platform.isMacOS + ? tabWidget + : Obx(() => DragToResizeArea( + resizeEdgeSize: + fullscreen.value ? kFullScreenEdgeSize : kWindowEdgeSize, + child: tabWidget)); } void onAddSetting() { diff --git a/flutter/lib/desktop/pages/file_manager_tab_page.dart b/flutter/lib/desktop/pages/file_manager_tab_page.dart index de874b42d..9b8060bb7 100644 --- a/flutter/lib/desktop/pages/file_manager_tab_page.dart +++ b/flutter/lib/desktop/pages/file_manager_tab_page.dart @@ -1,8 +1,10 @@ import 'dart:convert'; +import 'dart:io'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/file_manager_page.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; @@ -66,20 +68,24 @@ class _FileManagerTabPageState extends State { @override Widget build(BuildContext context) { - return SubWindowDragToResizeArea( - windowId: windowId(), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: MyTheme.color(context).border!)), - child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: DesktopTab( - controller: tabController, - onWindowCloseButton: handleWindowCloseButton, - tail: const AddButton().paddingOnly(left: 10), - )), - ), + final tabWidget = Container( + decoration: BoxDecoration( + border: Border.all(color: MyTheme.color(context).border!)), + child: Scaffold( + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabController, + onWindowCloseButton: handleWindowCloseButton, + tail: const AddButton().paddingOnly(left: 10), + )), ); + return Platform.isMacOS + ? tabWidget + : SubWindowDragToResizeArea( + resizeEdgeSize: kWindowEdgeSize, + windowId: windowId(), + child: tabWidget, + ); } void onRemoveId(String id) { diff --git a/flutter/lib/desktop/pages/port_forward_tab_page.dart b/flutter/lib/desktop/pages/port_forward_tab_page.dart index e4aac06fe..d4f17aaef 100644 --- a/flutter/lib/desktop/pages/port_forward_tab_page.dart +++ b/flutter/lib/desktop/pages/port_forward_tab_page.dart @@ -1,8 +1,10 @@ import 'dart:convert'; +import 'dart:io'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hbb/common.dart'; +import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/desktop/pages/port_forward_page.dart'; import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; @@ -74,23 +76,27 @@ class _PortForwardTabPageState extends State { @override Widget build(BuildContext context) { - return SubWindowDragToResizeArea( - windowId: windowId(), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: MyTheme.color(context).border!)), - child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: DesktopTab( - controller: tabController, - onWindowCloseButton: () async { - tabController.clear(); - return true; - }, - tail: AddButton().paddingOnly(left: 10), - )), - ), + final tabWidget = Container( + decoration: BoxDecoration( + border: Border.all(color: MyTheme.color(context).border!)), + child: Scaffold( + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabController, + onWindowCloseButton: () async { + tabController.clear(); + return true; + }, + tail: AddButton().paddingOnly(left: 10), + )), ); + return Platform.isMacOS + ? tabWidget + : SubWindowDragToResizeArea( + resizeEdgeSize: kWindowEdgeSize, + windowId: windowId(), + child: tabWidget, + ); } void onRemoveId(String id) { diff --git a/flutter/lib/desktop/pages/remote_tab_page.dart b/flutter/lib/desktop/pages/remote_tab_page.dart index 4d4c7e6e1..b086a2e35 100644 --- a/flutter/lib/desktop/pages/remote_tab_page.dart +++ b/flutter/lib/desktop/pages/remote_tab_page.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:io'; import 'package:desktop_multi_window/desktop_multi_window.dart'; import 'package:flutter/material.dart'; @@ -86,63 +87,66 @@ class _ConnectionTabPageState extends State { @override Widget build(BuildContext context) { final RxBool fullscreen = Get.find(tag: 'fullscreen'); - return Obx(() => SubWindowDragToResizeArea( - resizeEdgeSize: fullscreen.value ? 1.0 : 8.0, - windowId: windowId(), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: MyTheme.color(context).border!)), - child: Scaffold( - backgroundColor: MyTheme.color(context).bg, - body: DesktopTab( - controller: tabController, - showTabBar: fullscreen.isFalse, - onWindowCloseButton: handleWindowCloseButton, - tail: const AddButton().paddingOnly(left: 10), - pageViewBuilder: (pageView) { - WindowController.fromWindowId(windowId()) - .setFullscreen(fullscreen.isTrue); - return pageView; - }, - tabBuilder: (key, icon, label, themeConf) => Obx(() { - final connectionType = ConnectionTypeState.find(key); - if (!connectionType.isValid()) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - icon, - label, - ], - ); - } else { - final msgDirect = translate(connectionType.direct.value == - ConnectionType.strDirect - ? 'Direct Connection' - : 'Relay Connection'); - final msgSecure = translate(connectionType.secure.value == - ConnectionType.strSecure - ? 'Secure Connection' - : 'Insecure Connection'); - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - icon, - Tooltip( - message: '$msgDirect\n$msgSecure', - child: Image.asset( - 'assets/${connectionType.secure.value}${connectionType.direct.value}.png', - width: themeConf.iconSize, - height: themeConf.iconSize, - ).paddingOnly(right: 5), - ), - label, - ], - ); - } - }), - )), - ), - )); + final tabWidget = Container( + decoration: BoxDecoration( + border: Border.all(color: MyTheme.color(context).border!)), + child: Scaffold( + backgroundColor: MyTheme.color(context).bg, + body: DesktopTab( + controller: tabController, + showTabBar: fullscreen.isFalse, + onWindowCloseButton: handleWindowCloseButton, + tail: const AddButton().paddingOnly(left: 10), + pageViewBuilder: (pageView) { + WindowController.fromWindowId(windowId()) + .setFullscreen(fullscreen.isTrue); + return pageView; + }, + tabBuilder: (key, icon, label, themeConf) => Obx(() { + final connectionType = ConnectionTypeState.find(key); + if (!connectionType.isValid()) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + label, + ], + ); + } else { + final msgDirect = translate( + connectionType.direct.value == ConnectionType.strDirect + ? 'Direct Connection' + : 'Relay Connection'); + final msgSecure = translate( + connectionType.secure.value == ConnectionType.strSecure + ? 'Secure Connection' + : 'Insecure Connection'); + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + icon, + Tooltip( + message: '$msgDirect\n$msgSecure', + child: Image.asset( + 'assets/${connectionType.secure.value}${connectionType.direct.value}.png', + width: themeConf.iconSize, + height: themeConf.iconSize, + ).paddingOnly(right: 5), + ), + label, + ], + ); + } + }), + )), + ); + return Platform.isMacOS + ? tabWidget + : Obx(() => SubWindowDragToResizeArea( + resizeEdgeSize: + fullscreen.value ? kFullScreenEdgeSize : kWindowEdgeSize, + windowId: windowId(), + child: tabWidget)); } void onRemoveId(String id) { diff --git a/flutter/lib/desktop/widgets/scroll_wrapper.dart b/flutter/lib/desktop/widgets/scroll_wrapper.dart new file mode 100644 index 000000000..aaa3aa403 --- /dev/null +++ b/flutter/lib/desktop/widgets/scroll_wrapper.dart @@ -0,0 +1,26 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_hbb/consts.dart'; +import 'package:flutter_improved_scrolling/flutter_improved_scrolling.dart'; + +class DesktopScrollWrapper extends StatelessWidget { + final ScrollController scrollController; + final Widget child; + const DesktopScrollWrapper( + {Key? key, required this.scrollController, required this.child}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return ImprovedScrolling( + scrollController: scrollController, + enableCustomMouseWheelScrolling: true, + customMouseWheelScrollConfig: CustomMouseWheelScrollConfig( + scrollDuration: kDefaultScrollDuration, + scrollCurve: Curves.linearToEaseOut, + mouseWheelTurnsThrottleTimeMs: + kDefaultMouseWhellThrottleDuration.inMilliseconds, + scrollAmountMultiplier: kDefaultScrollAmountMultiplier), + child: child, + ); + } +} diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart index 43069bf79..8f04846e9 100644 --- a/flutter/lib/main.dart +++ b/flutter/lib/main.dart @@ -8,6 +8,7 @@ import 'package:flutter_hbb/desktop/screen/desktop_file_transfer_screen.dart'; import 'package:flutter_hbb/desktop/screen/desktop_port_forward_screen.dart'; import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart'; import 'package:flutter_hbb/utils/multi_window_manager.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -123,6 +124,12 @@ void runRemoteScreen(Map argument) async { home: DesktopRemoteScreen( params: argument, ), + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: supportedLocales, navigatorObservers: const [ // FirebaseAnalyticsObserver(analytics: analytics), ], @@ -141,6 +148,12 @@ void runFileTransferScreen(Map argument) async { darkTheme: MyTheme.darkTheme, themeMode: MyTheme.initialThemeMode(), home: DesktopFileTransferScreen(params: argument), + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: supportedLocales, navigatorObservers: const [ // FirebaseAnalyticsObserver(analytics: analytics), ], @@ -160,6 +173,12 @@ void runPortForwardScreen(Map argument) async { darkTheme: MyTheme.darkTheme, themeMode: MyTheme.initialThemeMode(), home: DesktopPortForwardScreen(params: argument), + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: supportedLocales, navigatorObservers: const [ // FirebaseAnalyticsObserver(analytics: analytics), ], @@ -178,14 +197,20 @@ void runConnectionManagerScreen() async { theme: MyTheme.lightTheme, darkTheme: MyTheme.darkTheme, themeMode: MyTheme.initialThemeMode(), + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: supportedLocales, home: const DesktopServerPage(), builder: _keepScaleBuilder())); windowManager.waitUntilReadyToShow(windowOptions, () async { - await windowManager.setAlignment(Alignment.topRight); - await windowManager.show(); - await windowManager.focus(); - await windowManager.setAlignment(Alignment.topRight); // ensure - }); + await windowManager.setAlignment(Alignment.topRight); + await windowManager.show(); + await windowManager.focus(); + await windowManager.setAlignment(Alignment.topRight); // ensure + }); } WindowOptions getHiddenTitleBarWindowOptions({Size? size}) { @@ -247,6 +272,12 @@ class _AppState extends State { navigatorObservers: const [ // FirebaseAnalyticsObserver(analytics: analytics), ], + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: supportedLocales, builder: isAndroid ? (context, child) => AccessibilityListener( child: MediaQuery( diff --git a/flutter/lib/models/ab_model.dart b/flutter/lib/models/ab_model.dart index e0467565c..161a4d8a5 100644 --- a/flutter/lib/models/ab_model.dart +++ b/flutter/lib/models/ab_model.dart @@ -45,8 +45,8 @@ class AbModel with ChangeNotifier { } catch (err) { abError = err.toString(); } finally { - notifyListeners(); abLoading = false; + notifyListeners(); } return null; } @@ -98,12 +98,18 @@ class AbModel with ChangeNotifier { final body = jsonEncode({ "data": jsonEncode({"tags": tags, "peers": peers}) }); - final resp = - await http.post(Uri.parse(api), headers: authHeaders, body: body); - abLoading = false; - // await getAb(); // TODO + try { + final resp = + await http.post(Uri.parse(api), headers: authHeaders, body: body); + abError = ""; + await getAb(); + debugPrint("resp: ${resp.body}"); + } catch (e) { + abError = e.toString(); + } finally { + abLoading = false; + } notifyListeners(); - debugPrint("resp: ${resp.body}"); } bool idContainBy(String id) { diff --git a/flutter/linux/CMakeLists.txt b/flutter/linux/CMakeLists.txt index 9a4e0527b..9391ed97e 100644 --- a/flutter/linux/CMakeLists.txt +++ b/flutter/linux/CMakeLists.txt @@ -4,7 +4,7 @@ project(runner LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "flutter_hbb") +set(BINARY_NAME "rustdesk") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID set(APPLICATION_ID "com.carriez.flutter_hbb") diff --git a/flutter/linux/my_application.cc b/flutter/linux/my_application.cc index deea3f549..6d101687b 100644 --- a/flutter/linux/my_application.cc +++ b/flutter/linux/my_application.cc @@ -51,7 +51,7 @@ static void my_application_activate(GApplication* application) { // auto bdw = bitsdojo_window_from(window); // <--- add this line // bdw->setCustomFrame(true); // <-- add this line - gtk_window_set_default_size(window, 1280, 720); // <-- comment this line + gtk_window_set_default_size(window, 800, 600); // <-- comment this line gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); diff --git a/flutter/macos/Runner/Configs/AppInfo.xcconfig b/flutter/macos/Runner/Configs/AppInfo.xcconfig index 3c862dee9..bf05a4caa 100644 --- a/flutter/macos/Runner/Configs/AppInfo.xcconfig +++ b/flutter/macos/Runner/Configs/AppInfo.xcconfig @@ -5,7 +5,7 @@ // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = flutter_hbb +PRODUCT_NAME = rustdesk // The application's bundle identifier PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock index bb27d243e..79c25ee5b 100644 --- a/flutter/pubspec.lock +++ b/flutter/pubspec.lock @@ -140,7 +140,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.1" + version: "1.2.0" charcode: dependency: transitive description: @@ -161,7 +161,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.1" + version: "1.1.0" code_builder: dependency: transitive description: @@ -367,6 +367,13 @@ packages: url: "https://github.com/Kingtous/rustdesk_flutter_custom_cursor" source: git version: "0.0.1" + flutter_improved_scrolling: + dependency: "direct main" + description: + name: flutter_improved_scrolling + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.3" flutter_lints: dependency: "direct dev" description: @@ -374,6 +381,11 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_parsed_text: dependency: transitive description: @@ -478,7 +490,7 @@ packages: name: icons_launcher url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.0.4" image: dependency: "direct main" description: @@ -576,7 +588,7 @@ packages: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.5" + version: "0.1.4" menu_base: dependency: transitive description: @@ -590,7 +602,7 @@ packages: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.7.0" mime: dependency: transitive description: @@ -667,7 +679,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.8.1" path_provider: dependency: "direct main" description: diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index 91c3b4164..05c711dcf 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -24,6 +24,8 @@ environment: dependencies: flutter: sdk: flutter + flutter_localizations: + sdk: flutter # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. @@ -78,6 +80,14 @@ dependencies: desktop_drop: ^0.3.3 scroll_pos: ^0.3.0 rxdart: ^0.27.5 + flutter_improved_scrolling: ^0.0.3 + # currently, we use flutter 3.0.5 for windows build, latest for other builds. + # + # for flutter 3.0.5, please use official version(just comment code below). + # if build rustdesk by flutter >=3.3, please use our custom pub below (uncomment code below). + # git: + # url: https://github.com/Kingtous/flutter_improved_scrolling + # ref: 62f09545149f320616467c306c8c5f71714a18e6 dev_dependencies: icons_launcher: ^2.0.4 diff --git a/flutter/test/widget_test.dart b/flutter/test/widget_test.dart deleted file mode 100644 index 3e7534740..000000000 --- a/flutter/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:flutter_hbb/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(App()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/flutter/windows/CMakeLists.txt b/flutter/windows/CMakeLists.txt index 89f0925f3..c6192b584 100644 --- a/flutter/windows/CMakeLists.txt +++ b/flutter/windows/CMakeLists.txt @@ -4,7 +4,7 @@ project(flutter_hbb LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "flutter_hbb") +set(BINARY_NAME "rustdesk") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/flutter/windows/runner/main.cpp b/flutter/windows/runner/main.cpp index f84fc1861..0724ace8a 100644 --- a/flutter/windows/runner/main.cpp +++ b/flutter/windows/runner/main.cpp @@ -52,7 +52,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); + Win32Window::Size size(800, 600); if (!window.CreateAndShow(L"flutter_hbb", origin, size)) { return EXIT_FAILURE; diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 738595aa9..664d6f05b 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -189,7 +189,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("x11 expected", "请切换到 x11"), ("Port", "端口"), ("Settings", "设置"), - ("Username", " 用户名"), + ("Username", "用户名"), ("Invalid port", "无效端口"), ("Closed manually by the peer", "被对方手动关闭"), ("Enable remote configuration modification", "允许远程修改配置"), @@ -272,7 +272,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Overwrite", "覆盖"), ("This file exists, skip or overwrite this file?", "这个文件/文件夹已存在,跳过/覆盖?"), ("Quit", "退出"), - ("doc_mac_permission", "https://rustdesk.com/docs/zh-cn/manual/mac/#启用权限"), + ("doc_mac_permission", "https://rustdesk.com/docs/zh-cn/manual/mac#%E5%90%AF%E7%94%A8%E6%9D%83%E9%99%90"), ("Help", "帮助"), ("Failed", "失败"), ("Succeeded", "成功"),