Merge branch 'master' into mobile_feat_update_rebase
This commit is contained in:
commit
9284850dff
@ -1,29 +1,6 @@
|
|||||||
# This file configures the analyzer, which statically analyzes Dart code to
|
include: package:lints/recommended.yaml
|
||||||
# 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
|
|
||||||
|
|
||||||
linter:
|
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:
|
rules:
|
||||||
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
non_constant_identifier_names: false
|
||||||
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
sort_child_properties_last: false
|
||||||
|
|
||||||
# Additional information about this file can be found at
|
|
||||||
# https://dart.dev/guides/language/analysis-options
|
|
||||||
|
@ -244,6 +244,32 @@ final ButtonStyle flatButtonStyle = TextButton.styleFrom(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
List<Locale> 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) {
|
String formatDurationToTime(Duration duration) {
|
||||||
var totalTime = duration.inSeconds;
|
var totalTime = duration.inSeconds;
|
||||||
final secs = totalTime % 60;
|
final secs = totalTime % 60;
|
||||||
@ -734,8 +760,9 @@ class PermissionManager {
|
|||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
}
|
}
|
||||||
if (!permissions.contains(type))
|
if (!permissions.contains(type)) {
|
||||||
return Future.error("Wrong permission!$type");
|
return Future.error("Wrong permission!$type");
|
||||||
|
}
|
||||||
return gFFI.invokeMethod("check_permission", type);
|
return gFFI.invokeMethod("check_permission", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,8 +770,9 @@ class PermissionManager {
|
|||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
}
|
}
|
||||||
if (!permissions.contains(type))
|
if (!permissions.contains(type)) {
|
||||||
return Future.error("Wrong permission!$type");
|
return Future.error("Wrong permission!$type");
|
||||||
|
}
|
||||||
|
|
||||||
gFFI.invokeMethod("request_permission", type);
|
gFFI.invokeMethod("request_permission", type);
|
||||||
if (type == "ignore_battery_optimizations") {
|
if (type == "ignore_battery_optimizations") {
|
||||||
|
@ -33,6 +33,7 @@ class IDTextInputFormatter extends TextInputFormatter {
|
|||||||
|
|
||||||
String formatID(String id) {
|
String formatID(String id) {
|
||||||
String id2 = id.replaceAll(' ', '');
|
String id2 = id.replaceAll(' ', '');
|
||||||
|
if (int.tryParse(id2) == null) return id;
|
||||||
String newID = '';
|
String newID = '';
|
||||||
if (id2.length <= 3) {
|
if (id2.length <= 3) {
|
||||||
newID = id2;
|
newID = id2;
|
||||||
|
@ -2,6 +2,7 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/desktop/widgets/scroll_wrapper.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:visibility_detector/visibility_detector.dart';
|
import 'package:visibility_detector/visibility_detector.dart';
|
||||||
@ -41,6 +42,7 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
static const int _maxQueryCount = 3;
|
static const int _maxQueryCount = 3;
|
||||||
final space = isDesktop ? 12.0 : 8.0;
|
final space = isDesktop ? 12.0 : 8.0;
|
||||||
final _curPeers = <String>{};
|
final _curPeers = <String>{};
|
||||||
|
final _scrollController = ScrollController();
|
||||||
var _lastChangeTime = DateTime.now();
|
var _lastChangeTime = DateTime.now();
|
||||||
var _lastQueryPeers = <String>{};
|
var _lastQueryPeers = <String>{};
|
||||||
var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1));
|
var _lastQueryTime = DateTime.now().subtract(const Duration(hours: 1));
|
||||||
@ -94,8 +96,11 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
? Center(
|
? Center(
|
||||||
child: Text(translate("Empty")),
|
child: Text(translate("Empty")),
|
||||||
)
|
)
|
||||||
: SingleChildScrollView(
|
: DesktopScrollWrapper(
|
||||||
controller: ScrollController(),
|
scrollController: _scrollController,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
physics: NeverScrollableScrollPhysics(),
|
||||||
|
controller: _scrollController,
|
||||||
child: ObxValue<RxString>((searchText) {
|
child: ObxValue<RxString>((searchText) {
|
||||||
return FutureBuilder<List<Peer>>(
|
return FutureBuilder<List<Peer>>(
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
@ -103,10 +108,21 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
final peers = snapshot.data!;
|
final peers = snapshot.data!;
|
||||||
final cards = <Widget>[];
|
final cards = <Widget>[];
|
||||||
for (final peer in peers) {
|
for (final peer in peers) {
|
||||||
final visibilityChild = VisibilityDetector(
|
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: VisibilityDetector(
|
||||||
key: ValueKey(peer.id),
|
key: ValueKey(peer.id),
|
||||||
onVisibilityChanged: (info) {
|
onVisibilityChanged: (info) {
|
||||||
final peerId = (info.key as ValueKey).value;
|
final peerId =
|
||||||
|
(info.key as ValueKey).value;
|
||||||
if (info.visibleFraction > 0.00001) {
|
if (info.visibleFraction > 0.00001) {
|
||||||
_curPeers.add(peerId);
|
_curPeers.add(peerId);
|
||||||
} else {
|
} else {
|
||||||
@ -115,27 +131,14 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
_lastChangeTime = DateTime.now();
|
_lastChangeTime = DateTime.now();
|
||||||
},
|
},
|
||||||
child: widget.peerCardWidgetFunc(peer),
|
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
|
|
||||||
? 140
|
|
||||||
: 42,
|
|
||||||
child: visibilityChild,
|
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
: SizedBox(
|
)));
|
||||||
width: mobileWidth,
|
|
||||||
child: visibilityChild)));
|
|
||||||
}
|
}
|
||||||
return Wrap(
|
return Wrap(
|
||||||
spacing: space, runSpacing: space, children: cards);
|
spacing: space,
|
||||||
|
runSpacing: space,
|
||||||
|
children: cards);
|
||||||
} else {
|
} else {
|
||||||
return const Center(
|
return const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
@ -147,6 +150,7 @@ class _PeerWidgetState extends State<_PeerWidget> with WindowListener {
|
|||||||
}, peerSearchText),
|
}, peerSearchText),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,13 @@ const int kMobileDefaultDisplayHeight = 1280;
|
|||||||
const int kDesktopDefaultDisplayWidth = 1080;
|
const int kDesktopDefaultDisplayWidth = 1080;
|
||||||
const int kDesktopDefaultDisplayHeight = 720;
|
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 kInvalidValueStr = "InvalidValueStr";
|
||||||
|
|
||||||
const kMobilePageConstraints = BoxConstraints(maxWidth: 600);
|
const kMobilePageConstraints = BoxConstraints(maxWidth: 600);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
// main window right pane
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
@ -82,8 +84,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
).marginSymmetric(horizontal: 22),
|
).marginSymmetric(horizontal: 22),
|
||||||
),
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
SizedBox(height: 50, child: Obx(() => buildStatus()))
|
SizedBox(child: Obx(() => buildStatus()))
|
||||||
.paddingSymmetric(horizontal: 12.0)
|
.paddingOnly(bottom: 12, top: 6),
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -187,7 +189,7 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
onConnect(isFileTransfer: true);
|
onConnect(isFileTransfer: true);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 24,
|
height: 27,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: ftPressed.value
|
color: ftPressed.value
|
||||||
@ -224,8 +226,12 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
onTapCancel: () => connPressed.value = false,
|
onTapCancel: () => connPressed.value = false,
|
||||||
onHover: (value) => connHover.value = value,
|
onHover: (value) => connHover.value = value,
|
||||||
onTap: onConnect,
|
onTap: onConnect,
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minWidth: 80.0,
|
||||||
|
),
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 24,
|
height: 27,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: connPressed.value
|
color: connPressed.value
|
||||||
? MyTheme.accent
|
? MyTheme.accent
|
||||||
@ -245,10 +251,11 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
"Connect",
|
"Connect",
|
||||||
),
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 12, color: MyTheme.color(context).bg),
|
fontSize: 12,
|
||||||
|
color: MyTheme.color(context).bg),
|
||||||
),
|
),
|
||||||
).marginSymmetric(horizontal: 12),
|
).marginSymmetric(horizontal: 12),
|
||||||
),
|
)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -275,6 +282,8 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
var svcIsUsingPublicServer = true.obs;
|
var svcIsUsingPublicServer = true.obs;
|
||||||
|
|
||||||
Widget buildStatus() {
|
Widget buildStatus() {
|
||||||
|
final fontSize = 14.0;
|
||||||
|
final textStyle = TextStyle(fontSize: fontSize);
|
||||||
final light = Container(
|
final light = Container(
|
||||||
height: 8,
|
height: 8,
|
||||||
width: 8,
|
width: 8,
|
||||||
@ -282,13 +291,13 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
borderRadius: BorderRadius.circular(20),
|
borderRadius: BorderRadius.circular(20),
|
||||||
color: svcStopped.value ? Colors.redAccent : Colors.green,
|
color: svcStopped.value ? Colors.redAccent : Colors.green,
|
||||||
),
|
),
|
||||||
).paddingSymmetric(horizontal: 10.0);
|
).paddingSymmetric(horizontal: 12.0);
|
||||||
if (svcStopped.value) {
|
if (svcStopped.value) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
light,
|
light,
|
||||||
Text(translate("Service is not running")),
|
Text(translate("Service is not running"), style: textStyle),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
bool checked = await bind.mainCheckSuperUserPermission();
|
bool checked = await bind.mainCheckSuperUserPermission();
|
||||||
@ -296,19 +305,25 @@ class _ConnectionPageState extends State<ConnectionPage> {
|
|||||||
bind.mainSetOption(key: "stop-service", value: "");
|
bind.mainSetOption(key: "stop-service", value: "");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text(translate("Start Service")))
|
child: Text(translate("Start Service"), style: textStyle))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
if (svcStatusCode.value == 0) {
|
if (svcStatusCode.value == 0) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [light, Text(translate("connecting_status"))],
|
children: [
|
||||||
|
light,
|
||||||
|
Text(translate("connecting_status"), style: textStyle)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
} else if (svcStatusCode.value == -1) {
|
} else if (svcStatusCode.value == -1) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
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<ConnectionPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
light,
|
light,
|
||||||
Text(translate('Ready')),
|
Text(translate('Ready'), style: textStyle),
|
||||||
|
Text(', ', style: textStyle),
|
||||||
svcIsUsingPublicServer.value
|
svcIsUsingPublicServer.value
|
||||||
? InkWell(
|
? InkWell(
|
||||||
onTap: onUsePublicServerGuide,
|
onTap: onUsePublicServerGuide,
|
||||||
child: Text(
|
child: Text(
|
||||||
', ${translate('setup_server_tip')}',
|
translate('setup_server_tip'),
|
||||||
style: TextStyle(decoration: TextDecoration.underline),
|
style: TextStyle(
|
||||||
|
decoration: TextDecoration.underline, fontSize: fontSize),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Offstage()
|
: Offstage()
|
||||||
|
@ -5,29 +5,14 @@ import 'package:flutter/material.dart' hide MenuItem;
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/desktop/pages/connection_page.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/platform_model.dart';
|
||||||
import 'package:flutter_hbb/models/server_model.dart';
|
import 'package:flutter_hbb/models/server_model.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:tray_manager/tray_manager.dart';
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
|
||||||
import 'package:window_manager/window_manager.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 {
|
class DesktopHomePage extends StatefulWidget {
|
||||||
const DesktopHomePage({Key? key}) : super(key: key);
|
const DesktopHomePage({Key? key}) : super(key: key);
|
||||||
|
|
||||||
@ -66,19 +51,19 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
return Row(
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
buildServerInfo(context),
|
buildLeftPane(context),
|
||||||
const VerticalDivider(
|
const VerticalDivider(
|
||||||
width: 1,
|
width: 1,
|
||||||
thickness: 1,
|
thickness: 1,
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: buildServerBoard(context),
|
child: buildRightPane(context),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildServerInfo(BuildContext context) {
|
buildLeftPane(BuildContext context) {
|
||||||
return ChangeNotifierProvider.value(
|
return ChangeNotifierProvider.value(
|
||||||
value: gFFI.serverModel,
|
value: gFFI.serverModel,
|
||||||
child: Container(
|
child: Container(
|
||||||
@ -95,7 +80,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildServerBoard(BuildContext context) {
|
buildRightPane(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
color: MyTheme.color(context).grayBg,
|
color: MyTheme.color(context).grayBg,
|
||||||
child: ConnectionPage(),
|
child: ConnectionPage(),
|
||||||
@ -167,93 +152,9 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget buildPopupMenu(BuildContext context) {
|
Widget buildPopupMenu(BuildContext context) {
|
||||||
var position;
|
|
||||||
RxBool hover = false.obs;
|
RxBool hover = false.obs;
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTapDown: (detail) {
|
onTap: () async {},
|
||||||
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 = <PopupMenuEntry>[
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Obx(
|
child: Obx(
|
||||||
() => CircleAvatar(
|
() => CircleAvatar(
|
||||||
radius: 15,
|
radius: 15,
|
||||||
@ -276,6 +177,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
buildPasswordBoard(BuildContext context) {
|
buildPasswordBoard(BuildContext context) {
|
||||||
final model = gFFI.serverModel;
|
final model = gFFI.serverModel;
|
||||||
RxBool refreshHover = false.obs;
|
RxBool refreshHover = false.obs;
|
||||||
|
RxBool editHover = false.obs;
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.only(left: 20.0, right: 16, top: 13, bottom: 13),
|
margin: EdgeInsets.only(left: 20.0, right: 16, top: 13, bottom: 13),
|
||||||
child: Row(
|
child: Row(
|
||||||
@ -334,7 +236,19 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
onTap: () => bind.mainUpdateTemporaryPassword(),
|
onTap: () => bind.mainUpdateTemporaryPassword(),
|
||||||
onHover: (value) => refreshHover.value = value,
|
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<DesktopHomePage>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onTrayMenuItemClick(MenuItem menuItem) {
|
void onTrayMenuItemClick(MenuItem menuItem) {
|
||||||
print('click ${menuItem.key}');
|
debugPrint('click ${menuItem.key}');
|
||||||
switch (menuItem.key) {
|
switch (menuItem.key) {
|
||||||
case "quit":
|
case "quit":
|
||||||
exit(0);
|
exit(0);
|
||||||
@ -394,8 +308,8 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
trayManager.addListener(this);
|
trayManager.addListener(this);
|
||||||
windowManager.addListener(this);
|
windowManager.addListener(this);
|
||||||
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
rustDeskWinManager.setMethodHandler((call, fromWindowId) async {
|
||||||
print(
|
debugPrint(
|
||||||
"call ${call.method} with args ${call.arguments} from window ${fromWindowId}");
|
"call ${call.method} with args ${call.arguments} from window $fromWindowId");
|
||||||
if (call.method == "main_window_on_top") {
|
if (call.method == "main_window_on_top") {
|
||||||
window_on_top(null);
|
window_on_top(null);
|
||||||
}
|
}
|
||||||
@ -408,236 +322,6 @@ class _DesktopHomePageState extends State<DesktopHomePage>
|
|||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void changeTheme(String choice) async {
|
|
||||||
if (choice == "Y") {
|
|
||||||
Get.changeTheme(MyTheme.darkTheme);
|
|
||||||
} else {
|
|
||||||
Get.changeTheme(MyTheme.lightTheme);
|
|
||||||
}
|
|
||||||
Get.find<SharedPreferences>().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<PopupMenuItem<String>> 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<String> genAudioInputPopupMenuItem(
|
|
||||||
bool enableInput, String defaultAudioInput) {
|
|
||||||
final defaultInput = defaultAudioInput.obs;
|
|
||||||
final enabled = enableInput.obs;
|
|
||||||
|
|
||||||
return PopupMenuItem(
|
|
||||||
child: FutureBuilder<List<String>>(
|
|
||||||
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<String>(
|
|
||||||
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
|
/// common login dialog for desktop
|
||||||
@ -689,8 +373,7 @@ Future<bool> loginDialog() async {
|
|||||||
debugPrint("$resp");
|
debugPrint("$resp");
|
||||||
completer.complete(true);
|
completer.complete(true);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// ignore: avoid_print
|
debugPrint(err.toString());
|
||||||
print(err.toString());
|
|
||||||
cancel();
|
cancel();
|
||||||
return;
|
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<MenuEntryBase<String>> _buildMenus() {
|
|
||||||
return <MenuEntryBase<String>>[
|
|
||||||
MenuEntryRadios<String>(
|
|
||||||
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<String>(
|
|
||||||
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<String>(
|
|
||||||
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)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hbb/common.dart';
|
import 'package:flutter_hbb/common.dart';
|
||||||
import 'package:flutter_hbb/consts.dart';
|
import 'package:flutter_hbb/consts.dart';
|
||||||
@ -35,11 +37,7 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
RxBool fullscreen = false.obs;
|
RxBool fullscreen = false.obs;
|
||||||
Get.put(fullscreen, tag: 'fullscreen');
|
Get.put(fullscreen, tag: 'fullscreen');
|
||||||
return Obx(() => DragToResizeArea(
|
final tabWidget = Container(
|
||||||
resizeEdgeSize: fullscreen.value ? 1.0 : 8.0,
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(color: MyTheme.color(context).border!)),
|
|
||||||
child: Overlay(initialEntries: [
|
child: Overlay(initialEntries: [
|
||||||
OverlayEntry(builder: (context) {
|
OverlayEntry(builder: (context) {
|
||||||
gFFI.dialogManager.setOverlayState(Overlay.of(context));
|
gFFI.dialogManager.setOverlayState(Overlay.of(context));
|
||||||
@ -56,7 +54,13 @@ class _DesktopTabPageState extends State<DesktopTabPage> {
|
|||||||
));
|
));
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
)));
|
);
|
||||||
|
return Platform.isMacOS
|
||||||
|
? tabWidget
|
||||||
|
: Obx(() => DragToResizeArea(
|
||||||
|
resizeEdgeSize:
|
||||||
|
fullscreen.value ? kFullScreenEdgeSize : kWindowEdgeSize,
|
||||||
|
child: tabWidget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onAddSetting() {
|
void onAddSetting() {
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
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';
|
||||||
import 'package:flutter_hbb/common.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/pages/file_manager_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
@ -66,9 +68,7 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SubWindowDragToResizeArea(
|
final tabWidget = Container(
|
||||||
windowId: windowId(),
|
|
||||||
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: Scaffold(
|
||||||
@ -78,7 +78,13 @@ class _FileManagerTabPageState extends State<FileManagerTabPage> {
|
|||||||
onWindowCloseButton: handleWindowCloseButton,
|
onWindowCloseButton: handleWindowCloseButton,
|
||||||
tail: const AddButton().paddingOnly(left: 10),
|
tail: const AddButton().paddingOnly(left: 10),
|
||||||
)),
|
)),
|
||||||
),
|
);
|
||||||
|
return Platform.isMacOS
|
||||||
|
? tabWidget
|
||||||
|
: SubWindowDragToResizeArea(
|
||||||
|
resizeEdgeSize: kWindowEdgeSize,
|
||||||
|
windowId: windowId(),
|
||||||
|
child: tabWidget,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
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';
|
||||||
import 'package:flutter_hbb/common.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/pages/port_forward_page.dart';
|
||||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
@ -74,9 +76,7 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SubWindowDragToResizeArea(
|
final tabWidget = Container(
|
||||||
windowId: windowId(),
|
|
||||||
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: Scaffold(
|
||||||
@ -89,7 +89,13 @@ class _PortForwardTabPageState extends State<PortForwardTabPage> {
|
|||||||
},
|
},
|
||||||
tail: AddButton().paddingOnly(left: 10),
|
tail: AddButton().paddingOnly(left: 10),
|
||||||
)),
|
)),
|
||||||
),
|
);
|
||||||
|
return Platform.isMacOS
|
||||||
|
? tabWidget
|
||||||
|
: SubWindowDragToResizeArea(
|
||||||
|
resizeEdgeSize: kWindowEdgeSize,
|
||||||
|
windowId: windowId(),
|
||||||
|
child: tabWidget,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
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';
|
||||||
@ -86,10 +87,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
final RxBool fullscreen = Get.find(tag: 'fullscreen');
|
||||||
return Obx(() => SubWindowDragToResizeArea(
|
final tabWidget = Container(
|
||||||
resizeEdgeSize: fullscreen.value ? 1.0 : 8.0,
|
|
||||||
windowId: windowId(),
|
|
||||||
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: Scaffold(
|
||||||
@ -115,12 +113,12 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final msgDirect = translate(connectionType.direct.value ==
|
final msgDirect = translate(
|
||||||
ConnectionType.strDirect
|
connectionType.direct.value == ConnectionType.strDirect
|
||||||
? 'Direct Connection'
|
? 'Direct Connection'
|
||||||
: 'Relay Connection');
|
: 'Relay Connection');
|
||||||
final msgSecure = translate(connectionType.secure.value ==
|
final msgSecure = translate(
|
||||||
ConnectionType.strSecure
|
connectionType.secure.value == ConnectionType.strSecure
|
||||||
? 'Secure Connection'
|
? 'Secure Connection'
|
||||||
: 'Insecure Connection');
|
: 'Insecure Connection');
|
||||||
return Row(
|
return Row(
|
||||||
@ -141,8 +139,14 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)),
|
)),
|
||||||
),
|
);
|
||||||
));
|
return Platform.isMacOS
|
||||||
|
? tabWidget
|
||||||
|
: Obx(() => SubWindowDragToResizeArea(
|
||||||
|
resizeEdgeSize:
|
||||||
|
fullscreen.value ? kFullScreenEdgeSize : kWindowEdgeSize,
|
||||||
|
windowId: windowId(),
|
||||||
|
child: tabWidget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onRemoveId(String id) {
|
void onRemoveId(String id) {
|
||||||
|
26
flutter/lib/desktop/widgets/scroll_wrapper.dart
Normal file
26
flutter/lib/desktop/widgets/scroll_wrapper.dart
Normal file
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -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_port_forward_screen.dart';
|
||||||
import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart';
|
import 'package:flutter_hbb/desktop/screen/desktop_remote_screen.dart';
|
||||||
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
import 'package:flutter_hbb/utils/multi_window_manager.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@ -123,6 +124,12 @@ void runRemoteScreen(Map<String, dynamic> argument) async {
|
|||||||
home: DesktopRemoteScreen(
|
home: DesktopRemoteScreen(
|
||||||
params: argument,
|
params: argument,
|
||||||
),
|
),
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: supportedLocales,
|
||||||
navigatorObservers: const [
|
navigatorObservers: const [
|
||||||
// FirebaseAnalyticsObserver(analytics: analytics),
|
// FirebaseAnalyticsObserver(analytics: analytics),
|
||||||
],
|
],
|
||||||
@ -141,6 +148,12 @@ void runFileTransferScreen(Map<String, dynamic> argument) async {
|
|||||||
darkTheme: MyTheme.darkTheme,
|
darkTheme: MyTheme.darkTheme,
|
||||||
themeMode: MyTheme.initialThemeMode(),
|
themeMode: MyTheme.initialThemeMode(),
|
||||||
home: DesktopFileTransferScreen(params: argument),
|
home: DesktopFileTransferScreen(params: argument),
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: supportedLocales,
|
||||||
navigatorObservers: const [
|
navigatorObservers: const [
|
||||||
// FirebaseAnalyticsObserver(analytics: analytics),
|
// FirebaseAnalyticsObserver(analytics: analytics),
|
||||||
],
|
],
|
||||||
@ -160,6 +173,12 @@ void runPortForwardScreen(Map<String, dynamic> argument) async {
|
|||||||
darkTheme: MyTheme.darkTheme,
|
darkTheme: MyTheme.darkTheme,
|
||||||
themeMode: MyTheme.initialThemeMode(),
|
themeMode: MyTheme.initialThemeMode(),
|
||||||
home: DesktopPortForwardScreen(params: argument),
|
home: DesktopPortForwardScreen(params: argument),
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: supportedLocales,
|
||||||
navigatorObservers: const [
|
navigatorObservers: const [
|
||||||
// FirebaseAnalyticsObserver(analytics: analytics),
|
// FirebaseAnalyticsObserver(analytics: analytics),
|
||||||
],
|
],
|
||||||
@ -178,6 +197,12 @@ void runConnectionManagerScreen() async {
|
|||||||
theme: MyTheme.lightTheme,
|
theme: MyTheme.lightTheme,
|
||||||
darkTheme: MyTheme.darkTheme,
|
darkTheme: MyTheme.darkTheme,
|
||||||
themeMode: MyTheme.initialThemeMode(),
|
themeMode: MyTheme.initialThemeMode(),
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: supportedLocales,
|
||||||
home: const DesktopServerPage(),
|
home: const DesktopServerPage(),
|
||||||
builder: _keepScaleBuilder()));
|
builder: _keepScaleBuilder()));
|
||||||
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
windowManager.waitUntilReadyToShow(windowOptions, () async {
|
||||||
@ -247,6 +272,12 @@ class _AppState extends State<App> {
|
|||||||
navigatorObservers: const [
|
navigatorObservers: const [
|
||||||
// FirebaseAnalyticsObserver(analytics: analytics),
|
// FirebaseAnalyticsObserver(analytics: analytics),
|
||||||
],
|
],
|
||||||
|
localizationsDelegates: const [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: supportedLocales,
|
||||||
builder: isAndroid
|
builder: isAndroid
|
||||||
? (context, child) => AccessibilityListener(
|
? (context, child) => AccessibilityListener(
|
||||||
child: MediaQuery(
|
child: MediaQuery(
|
||||||
|
@ -45,8 +45,8 @@ class AbModel with ChangeNotifier {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
abError = err.toString();
|
abError = err.toString();
|
||||||
} finally {
|
} finally {
|
||||||
notifyListeners();
|
|
||||||
abLoading = false;
|
abLoading = false;
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -98,12 +98,18 @@ class AbModel with ChangeNotifier {
|
|||||||
final body = jsonEncode({
|
final body = jsonEncode({
|
||||||
"data": jsonEncode({"tags": tags, "peers": peers})
|
"data": jsonEncode({"tags": tags, "peers": peers})
|
||||||
});
|
});
|
||||||
|
try {
|
||||||
final resp =
|
final resp =
|
||||||
await http.post(Uri.parse(api), headers: authHeaders, body: body);
|
await http.post(Uri.parse(api), headers: authHeaders, body: body);
|
||||||
abLoading = false;
|
abError = "";
|
||||||
// await getAb(); // TODO
|
await getAb();
|
||||||
notifyListeners();
|
|
||||||
debugPrint("resp: ${resp.body}");
|
debugPrint("resp: ${resp.body}");
|
||||||
|
} catch (e) {
|
||||||
|
abError = e.toString();
|
||||||
|
} finally {
|
||||||
|
abLoading = false;
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool idContainBy(String id) {
|
bool idContainBy(String id) {
|
||||||
|
@ -4,7 +4,7 @@ project(runner LANGUAGES CXX)
|
|||||||
|
|
||||||
# The name of the executable created for the application. Change this to change
|
# The name of the executable created for the application. Change this to change
|
||||||
# the on-disk name of your application.
|
# 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:
|
# The unique GTK application identifier for this application. See:
|
||||||
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
|
||||||
set(APPLICATION_ID "com.carriez.flutter_hbb")
|
set(APPLICATION_ID "com.carriez.flutter_hbb")
|
||||||
|
@ -51,7 +51,7 @@ static void my_application_activate(GApplication* application) {
|
|||||||
|
|
||||||
// auto bdw = bitsdojo_window_from(window); // <--- add this line
|
// auto bdw = bitsdojo_window_from(window); // <--- add this line
|
||||||
// bdw->setCustomFrame(true); // <-- 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));
|
gtk_widget_show(GTK_WIDGET(window));
|
||||||
|
|
||||||
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
// 'flutter create' template.
|
// 'flutter create' template.
|
||||||
|
|
||||||
// The application's name. By default this is also the title of the Flutter window.
|
// 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
|
// The application's bundle identifier
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb
|
PRODUCT_BUNDLE_IDENTIFIER = com.carriez.flutterHbb
|
||||||
|
@ -140,7 +140,7 @@ packages:
|
|||||||
name: characters
|
name: characters
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.0"
|
||||||
charcode:
|
charcode:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -161,7 +161,7 @@ packages:
|
|||||||
name: clock
|
name: clock
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.1"
|
version: "1.1.0"
|
||||||
code_builder:
|
code_builder:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -367,6 +367,13 @@ packages:
|
|||||||
url: "https://github.com/Kingtous/rustdesk_flutter_custom_cursor"
|
url: "https://github.com/Kingtous/rustdesk_flutter_custom_cursor"
|
||||||
source: git
|
source: git
|
||||||
version: "0.0.1"
|
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:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -374,6 +381,11 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_parsed_text:
|
flutter_parsed_text:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -478,7 +490,7 @@ packages:
|
|||||||
name: icons_launcher
|
name: icons_launcher
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.4"
|
||||||
image:
|
image:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -576,7 +588,7 @@ packages:
|
|||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.1.5"
|
version: "0.1.4"
|
||||||
menu_base:
|
menu_base:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -590,7 +602,7 @@ packages:
|
|||||||
name: meta
|
name: meta
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.0"
|
version: "1.7.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -667,7 +679,7 @@ packages:
|
|||||||
name: path
|
name: path
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.2"
|
version: "1.8.1"
|
||||||
path_provider:
|
path_provider:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -24,6 +24,8 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
@ -78,6 +80,14 @@ dependencies:
|
|||||||
desktop_drop: ^0.3.3
|
desktop_drop: ^0.3.3
|
||||||
scroll_pos: ^0.3.0
|
scroll_pos: ^0.3.0
|
||||||
rxdart: ^0.27.5
|
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:
|
dev_dependencies:
|
||||||
icons_launcher: ^2.0.4
|
icons_launcher: ^2.0.4
|
||||||
|
@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
@ -4,7 +4,7 @@ project(flutter_hbb LANGUAGES CXX)
|
|||||||
|
|
||||||
# The name of the executable created for the application. Change this to change
|
# The name of the executable created for the application. Change this to change
|
||||||
# the on-disk name of your application.
|
# 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
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
|
||||||
# versions of CMake.
|
# versions of CMake.
|
||||||
|
@ -52,7 +52,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
|||||||
|
|
||||||
FlutterWindow window(project);
|
FlutterWindow window(project);
|
||||||
Win32Window::Point origin(10, 10);
|
Win32Window::Point origin(10, 10);
|
||||||
Win32Window::Size size(1280, 720);
|
Win32Window::Size size(800, 600);
|
||||||
if (!window.CreateAndShow(L"flutter_hbb", origin, size))
|
if (!window.CreateAndShow(L"flutter_hbb", origin, size))
|
||||||
{
|
{
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
@ -272,7 +272,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Overwrite", "覆盖"),
|
("Overwrite", "覆盖"),
|
||||||
("This file exists, skip or overwrite this file?", "这个文件/文件夹已存在,跳过/覆盖?"),
|
("This file exists, skip or overwrite this file?", "这个文件/文件夹已存在,跳过/覆盖?"),
|
||||||
("Quit", "退出"),
|
("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", "帮助"),
|
("Help", "帮助"),
|
||||||
("Failed", "失败"),
|
("Failed", "失败"),
|
||||||
("Succeeded", "成功"),
|
("Succeeded", "成功"),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user