mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-06-07 09:52:49 +08:00
flutter_desktop: connection type, mid commit
Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
parent
256149ecdf
commit
01e96a1134
@ -743,39 +743,3 @@ Future<List<Peer>>? matchPeers(String searchText, List<Peer> peers) async {
|
||||
}
|
||||
return filteredList;
|
||||
}
|
||||
|
||||
class PrivacyModeState {
|
||||
static String tag(String id) => 'privacy_mode_' + id;
|
||||
|
||||
static void init(String id) {
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
||||
}
|
||||
|
||||
class BlockInputState {
|
||||
static String tag(String id) => 'block_input_' + id;
|
||||
|
||||
static void init(String id) {
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
||||
}
|
||||
|
||||
class CurrentDisplayState {
|
||||
static String tag(String id) => 'current_display_' + id;
|
||||
|
||||
static void init(String id) {
|
||||
final RxInt state = RxInt(0);
|
||||
Get.put(state, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static RxInt find(String id) => Get.find<RxInt>(tag: tag(id));
|
||||
}
|
||||
|
73
flutter/lib/common/shared_state.dart
Normal file
73
flutter/lib/common/shared_state.dart
Normal file
@ -0,0 +1,73 @@
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../consts.dart';
|
||||
|
||||
class PrivacyModeState {
|
||||
static String tag(String id) => 'privacy_mode_$id';
|
||||
|
||||
static void init(String id) {
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
||||
}
|
||||
|
||||
class BlockInputState {
|
||||
static String tag(String id) => 'block_input_$id';
|
||||
|
||||
static void init(String id) {
|
||||
final RxBool state = false.obs;
|
||||
Get.put(state, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static RxBool find(String id) => Get.find<RxBool>(tag: tag(id));
|
||||
}
|
||||
|
||||
class CurrentDisplayState {
|
||||
static String tag(String id) => 'current_display_$id';
|
||||
|
||||
static void init(String id) {
|
||||
final RxInt state = RxInt(0);
|
||||
Get.put(state, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static RxInt find(String id) => Get.find<RxInt>(tag: tag(id));
|
||||
}
|
||||
|
||||
class ConnectionType {
|
||||
final Rx<String> _secure = kInvalidValueStr.obs;
|
||||
final Rx<String> _direct = kInvalidValueStr.obs;
|
||||
|
||||
Rx<String> get secure => _secure;
|
||||
Rx<String> get direct => _direct;
|
||||
|
||||
void setSecure(bool v) {
|
||||
_secure.value = v ? 'secure' : 'insecure';
|
||||
}
|
||||
|
||||
void setDirect(bool v) {
|
||||
_direct.value = v ? '' : '_relay';
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return _secure.value != kInvalidValueStr &&
|
||||
_direct.value != kInvalidValueStr;
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionTypeState {
|
||||
static String tag(String id) => 'connection_type_$id';
|
||||
|
||||
static void init(String id) {
|
||||
final ConnectionType collectionType = ConnectionType();
|
||||
Get.put(collectionType, tag: tag(id));
|
||||
}
|
||||
|
||||
static void delete(String id) => Get.delete(tag: tag(id));
|
||||
static ConnectionType find(String id) =>
|
||||
Get.find<ConnectionType>(tag: tag(id));
|
||||
}
|
@ -10,3 +10,5 @@ const String kTabLabelSettingPage = "Settings";
|
||||
|
||||
const int kDefaultDisplayWidth = 1280;
|
||||
const int kDefaultDisplayHeight = 720;
|
||||
|
||||
const kInvalidValueStr = "InvalidValueStr";
|
||||
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hbb/common.dart';
|
||||
import 'package:flutter_hbb/common/shared_state.dart';
|
||||
import 'package:flutter_hbb/consts.dart';
|
||||
import 'package:flutter_hbb/desktop/pages/remote_page.dart';
|
||||
import 'package:flutter_hbb/desktop/widgets/tabbar_widget.dart';
|
||||
@ -20,8 +21,8 @@ class ConnectionTabPage extends StatefulWidget {
|
||||
|
||||
class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
final tabController = Get.put(DesktopTabController());
|
||||
static final IconData selectedIcon = Icons.desktop_windows_sharp;
|
||||
static final IconData unselectedIcon = Icons.desktop_windows_outlined;
|
||||
static const IconData selectedIcon = Icons.desktop_windows_sharp;
|
||||
static const IconData unselectedIcon = Icons.desktop_windows_outlined;
|
||||
|
||||
var connectionMap = RxList<Widget>.empty(growable: true);
|
||||
|
||||
@ -34,7 +35,7 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
selectedIcon: selectedIcon,
|
||||
unselectedIcon: unselectedIcon,
|
||||
page: Obx(() => RemotePage(
|
||||
key: ValueKey(params['id']),
|
||||
key: ValueKey(params['id']),
|
||||
id: params['id'],
|
||||
tabBarHeight:
|
||||
fullscreen.isTrue ? 0 : kDesktopRemoteTabBarHeight,
|
||||
@ -88,10 +89,10 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
child: Scaffold(
|
||||
backgroundColor: MyTheme.color(context).bg,
|
||||
body: Obx(() => DesktopTab(
|
||||
controller: tabController,
|
||||
theme: theme,
|
||||
isMainWindow: false,
|
||||
showTabBar: fullscreen.isFalse,
|
||||
controller: tabController,
|
||||
theme: theme,
|
||||
isMainWindow: false,
|
||||
showTabBar: fullscreen.isFalse,
|
||||
onClose: () {
|
||||
tabController.clear();
|
||||
},
|
||||
@ -103,7 +104,36 @@ class _ConnectionTabPageState extends State<ConnectionTabPage> {
|
||||
.setFullscreen(fullscreen.isTrue);
|
||||
return pageView;
|
||||
},
|
||||
))),
|
||||
tabBuilder: (key, icon, label, themeConf) {
|
||||
final connectionType = ConnectionTypeState.find(key);
|
||||
if (!connectionType.isValid()) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
icon,
|
||||
label,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
final iconName =
|
||||
'${connectionType.secure.value}${connectionType.direct.value}';
|
||||
final connectionIcon = Image.asset(
|
||||
'assets/$iconName.png',
|
||||
width: themeConf.iconSize,
|
||||
height: themeConf.iconSize,
|
||||
color: theme.selectedtabIconColor,
|
||||
);
|
||||
//.paddingOnly(right: 5);
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
icon,
|
||||
connectionIcon,
|
||||
label,
|
||||
],
|
||||
);
|
||||
}
|
||||
}))),
|
||||
),
|
||||
));
|
||||
}
|
||||
|
@ -5,11 +5,9 @@ import 'dart:ui' as ui;
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hbb/models/chat_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
// import 'package:window_manager/window_manager.dart';
|
||||
|
||||
@ -19,6 +17,8 @@ import '../../mobile/widgets/dialog.dart';
|
||||
import '../../mobile/widgets/overlay.dart';
|
||||
import '../../models/model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../models/chat_model.dart';
|
||||
import '../../common/shared_state.dart';
|
||||
|
||||
final initText = '\1' * 1024;
|
||||
|
||||
@ -41,7 +41,7 @@ class _RemotePageState extends State<RemotePage>
|
||||
Timer? _timer;
|
||||
bool _showBar = !isWebDesktop;
|
||||
String _value = '';
|
||||
var _cursorOverImage = false.obs;
|
||||
final _cursorOverImage = false.obs;
|
||||
|
||||
final FocusNode _mobileFocusNode = FocusNode();
|
||||
final FocusNode _physicalFocusNode = FocusNode();
|
||||
@ -54,6 +54,20 @@ class _RemotePageState extends State<RemotePage>
|
||||
_ffi.canvasModel.tabBarHeight = widget.tabBarHeight;
|
||||
}
|
||||
|
||||
void _initStates(String id) {
|
||||
PrivacyModeState.init(id);
|
||||
BlockInputState.init(id);
|
||||
CurrentDisplayState.init(id);
|
||||
ConnectionTypeState.init(id);
|
||||
}
|
||||
|
||||
void _removeStates(String id) {
|
||||
PrivacyModeState.delete(id);
|
||||
BlockInputState.delete(id);
|
||||
CurrentDisplayState.delete(id);
|
||||
ConnectionTypeState.delete(id);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -74,14 +88,12 @@ class _RemotePageState extends State<RemotePage>
|
||||
_ffi.listenToMouse(true);
|
||||
_ffi.qualityMonitorModel.checkShowQualityMonitor(widget.id);
|
||||
// WindowManager.instance.addListener(this);
|
||||
PrivacyModeState.init(widget.id);
|
||||
BlockInputState.init(widget.id);
|
||||
CurrentDisplayState.init(widget.id);
|
||||
_initStates(widget.id);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
print("REMOTE PAGE dispose ${widget.id}");
|
||||
debugPrint("REMOTE PAGE dispose ${widget.id}");
|
||||
hideMobileActionsOverlay();
|
||||
_ffi.listenToMouse(false);
|
||||
_mobileFocusNode.dispose();
|
||||
@ -97,9 +109,7 @@ class _RemotePageState extends State<RemotePage>
|
||||
// WindowManager.instance.removeListener(this);
|
||||
Get.delete<FFI>(tag: widget.id);
|
||||
super.dispose();
|
||||
PrivacyModeState.delete(widget.id);
|
||||
BlockInputState.delete(widget.id);
|
||||
CurrentDisplayState.delete(widget.id);
|
||||
_removeStates(widget.id);
|
||||
}
|
||||
|
||||
void resetTool() {
|
||||
|
@ -4,12 +4,10 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import './material_mod_popup_menu.dart' as modMenu;
|
||||
|
||||
const kInvalidValueStr = "InvalidValueStr";
|
||||
import './material_mod_popup_menu.dart' as mod_menu;
|
||||
|
||||
// https://stackoverflow.com/questions/68318314/flutter-popup-menu-inside-popup-menu
|
||||
class PopupMenuChildrenItem<T> extends modMenu.PopupMenuEntry<T> {
|
||||
class PopupMenuChildrenItem<T> extends mod_menu.PopupMenuEntry<T> {
|
||||
const PopupMenuChildrenItem({
|
||||
key,
|
||||
this.height = kMinInteractiveDimension,
|
||||
@ -17,19 +15,19 @@ class PopupMenuChildrenItem<T> extends modMenu.PopupMenuEntry<T> {
|
||||
this.enable = true,
|
||||
this.textStyle,
|
||||
this.onTap,
|
||||
this.position = modMenu.PopupMenuPosition.overSide,
|
||||
this.position = mod_menu.PopupMenuPosition.overSide,
|
||||
this.offset = Offset.zero,
|
||||
required this.itemBuilder,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
final modMenu.PopupMenuPosition position;
|
||||
final mod_menu.PopupMenuPosition position;
|
||||
final Offset offset;
|
||||
final TextStyle? textStyle;
|
||||
final EdgeInsets? padding;
|
||||
final bool enable;
|
||||
final void Function()? onTap;
|
||||
final List<modMenu.PopupMenuEntry<T>> Function(BuildContext) itemBuilder;
|
||||
final List<mod_menu.PopupMenuEntry<T>> Function(BuildContext) itemBuilder;
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
@ -59,7 +57,7 @@ class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>>
|
||||
popupMenuTheme.textStyle ??
|
||||
theme.textTheme.subtitle1!;
|
||||
|
||||
return modMenu.PopupMenuButton<T>(
|
||||
return mod_menu.PopupMenuButton<T>(
|
||||
enabled: widget.enable,
|
||||
position: widget.position,
|
||||
offset: widget.offset,
|
||||
@ -88,22 +86,26 @@ class MenuConfig {
|
||||
static const iconWidth = 12.0;
|
||||
static const iconHeight = 12.0;
|
||||
|
||||
final double secondMenuHeight;
|
||||
final double height;
|
||||
final double dividerHeight;
|
||||
final Color commonColor;
|
||||
|
||||
const MenuConfig(
|
||||
{required this.commonColor,
|
||||
this.secondMenuHeight = kMinInteractiveDimension});
|
||||
this.height = kMinInteractiveDimension,
|
||||
this.dividerHeight = 16.0});
|
||||
}
|
||||
|
||||
abstract class MenuEntryBase<T> {
|
||||
modMenu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf);
|
||||
mod_menu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf);
|
||||
}
|
||||
|
||||
class MenuEntryDivider<T> extends MenuEntryBase<T> {
|
||||
@override
|
||||
modMenu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return const modMenu.PopupMenuDivider();
|
||||
mod_menu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return mod_menu.PopupMenuDivider(
|
||||
height: conf.dividerHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,14 +140,15 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
}
|
||||
}
|
||||
|
||||
modMenu.PopupMenuEntry<T> _buildSecondMenu(
|
||||
mod_menu.PopupMenuEntry<T> _buildSecondMenu(
|
||||
BuildContext context, MenuConfig conf, Tuple2<String, String> opt) {
|
||||
return modMenu.PopupMenuItem(
|
||||
return mod_menu.PopupMenuItem(
|
||||
padding: EdgeInsets.zero,
|
||||
height: conf.height,
|
||||
child: TextButton(
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: conf.secondMenuHeight),
|
||||
constraints: BoxConstraints(minHeight: conf.height),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
@ -156,7 +159,7 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
Icons.check,
|
||||
color: conf.commonColor,
|
||||
)
|
||||
: SizedBox.shrink())),
|
||||
: const SizedBox.shrink())),
|
||||
const SizedBox(width: MenuConfig.midPadding),
|
||||
Text(
|
||||
opt.item1,
|
||||
@ -178,10 +181,10 @@ class MenuEntrySubRadios<T> extends MenuEntryBase<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
modMenu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
mod_menu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return PopupMenuChildrenItem(
|
||||
height: conf.secondMenuHeight,
|
||||
padding: EdgeInsets.zero,
|
||||
height: conf.height,
|
||||
itemBuilder: (BuildContext context) =>
|
||||
options.map((opt) => _buildSecondMenu(context, conf, opt)).toList(),
|
||||
child: Row(children: [
|
||||
@ -218,9 +221,10 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||
Future<void> setOption(bool option);
|
||||
|
||||
@override
|
||||
modMenu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return modMenu.PopupMenuItem(
|
||||
mod_menu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return mod_menu.PopupMenuItem(
|
||||
padding: EdgeInsets.zero,
|
||||
height: conf.height,
|
||||
child: Obx(
|
||||
() => SwitchListTile(
|
||||
value: curOption.value,
|
||||
@ -229,7 +233,7 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||
},
|
||||
title: Container(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: conf.secondMenuHeight),
|
||||
constraints: BoxConstraints(minHeight: conf.height),
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
@ -242,7 +246,7 @@ abstract class MenuEntrySwitchBase<T> extends MenuEntryBase<T> {
|
||||
horizontal: VisualDensity.minimumDensity,
|
||||
vertical: VisualDensity.minimumDensity,
|
||||
),
|
||||
contentPadding: EdgeInsets.only(left: 8.0),
|
||||
contentPadding: const EdgeInsets.only(left: 8.0),
|
||||
),
|
||||
),
|
||||
);
|
||||
@ -303,11 +307,11 @@ class MenuEntrySubMenu<T> extends MenuEntryBase<T> {
|
||||
});
|
||||
|
||||
@override
|
||||
modMenu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
mod_menu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return PopupMenuChildrenItem(
|
||||
height: conf.secondMenuHeight,
|
||||
height: conf.height,
|
||||
padding: EdgeInsets.zero,
|
||||
position: modMenu.PopupMenuPosition.overSide,
|
||||
position: mod_menu.PopupMenuPosition.overSide,
|
||||
itemBuilder: (BuildContext context) =>
|
||||
entries.map((entry) => entry.build(context, conf)).toList(),
|
||||
child: Row(children: [
|
||||
@ -342,13 +346,14 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
||||
});
|
||||
|
||||
@override
|
||||
modMenu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return modMenu.PopupMenuItem(
|
||||
mod_menu.PopupMenuEntry<T> build(BuildContext context, MenuConfig conf) {
|
||||
return mod_menu.PopupMenuItem(
|
||||
padding: EdgeInsets.zero,
|
||||
height: conf.height,
|
||||
child: TextButton(
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
constraints: BoxConstraints(minHeight: conf.secondMenuHeight),
|
||||
constraints: BoxConstraints(minHeight: conf.height),
|
||||
child: childBuilder(
|
||||
const TextStyle(
|
||||
color: Colors.black,
|
||||
@ -362,14 +367,3 @@ class MenuEntryButton<T> extends MenuEntryBase<T> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomMenu<T> {
|
||||
final List<MenuEntryBase<T>> entries;
|
||||
final MenuConfig conf;
|
||||
|
||||
const CustomMenu({required this.entries, required this.conf});
|
||||
|
||||
List<modMenu.PopupMenuEntry<T>> build(BuildContext context) {
|
||||
return entries.map((entry) => entry.build(context, conf)).toList();
|
||||
}
|
||||
}
|
||||
|
@ -9,12 +9,15 @@ import '../../mobile/widgets/dialog.dart';
|
||||
import '../../mobile/widgets/overlay.dart';
|
||||
import '../../models/model.dart';
|
||||
import '../../models/platform_model.dart';
|
||||
import '../../common/shared_state.dart';
|
||||
import './popup_menu.dart';
|
||||
import './material_mod_popup_menu.dart' as mod_menu;
|
||||
|
||||
class _MenubarTheme {
|
||||
static const Color commonColor = MyTheme.accent;
|
||||
static const double height = kMinInteractiveDimension;
|
||||
// kMinInteractiveDimension
|
||||
static const double height = 24.0;
|
||||
static const double dividerHeight = 12.0;
|
||||
}
|
||||
|
||||
class RemoteMenubar extends StatefulWidget {
|
||||
@ -168,11 +171,9 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
),
|
||||
itemBuilder: (BuildContext context) {
|
||||
final List<Widget> rowChildren = [];
|
||||
const double selectorScale = 1.3;
|
||||
for (int i = 0; i < pi.displays.length; i++) {
|
||||
rowChildren.add(Transform.scale(
|
||||
scale: selectorScale,
|
||||
child: Stack(
|
||||
rowChildren.add(
|
||||
Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
@ -203,7 +204,7 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
)
|
||||
],
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
return <mod_menu.PopupMenuEntry<String>>[
|
||||
mod_menu.PopupMenuItem<String>(
|
||||
@ -232,7 +233,8 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
context,
|
||||
const MenuConfig(
|
||||
commonColor: _MenubarTheme.commonColor,
|
||||
secondMenuHeight: _MenubarTheme.height,
|
||||
height: _MenubarTheme.height,
|
||||
dividerHeight: _MenubarTheme.dividerHeight,
|
||||
)))
|
||||
.toList(),
|
||||
);
|
||||
@ -253,7 +255,8 @@ class _RemoteMenubarState extends State<RemoteMenubar> {
|
||||
context,
|
||||
const MenuConfig(
|
||||
commonColor: _MenubarTheme.commonColor,
|
||||
secondMenuHeight: _MenubarTheme.height,
|
||||
height: _MenubarTheme.height,
|
||||
dividerHeight: _MenubarTheme.dividerHeight,
|
||||
)))
|
||||
.toList(),
|
||||
);
|
||||
|
@ -121,6 +121,16 @@ class DesktopTabController {
|
||||
}
|
||||
}
|
||||
|
||||
class TabThemeConf {
|
||||
double iconSize;
|
||||
TarBarTheme theme;
|
||||
TabThemeConf({required this.iconSize, required this.theme});
|
||||
}
|
||||
|
||||
typedef TabBuilder = Widget Function(
|
||||
String key, Widget icon, Widget label, TabThemeConf themeConf);
|
||||
typedef LabelGetter = Rx<String> Function(String key);
|
||||
|
||||
class DesktopTab extends StatelessWidget {
|
||||
final Function(String)? onTabClose;
|
||||
final TarBarTheme theme;
|
||||
@ -134,24 +144,29 @@ class DesktopTab extends StatelessWidget {
|
||||
final Widget Function(Widget pageView)? pageViewBuilder;
|
||||
final Widget? tail;
|
||||
final VoidCallback? onClose;
|
||||
final TabBuilder? tabBuilder;
|
||||
final LabelGetter? labelGetter;
|
||||
|
||||
final DesktopTabController controller;
|
||||
Rx<DesktopTabState> get state => controller.state;
|
||||
|
||||
const DesktopTab(
|
||||
{required this.controller,
|
||||
required this.isMainWindow,
|
||||
this.theme = const TarBarTheme.light(),
|
||||
this.onTabClose,
|
||||
this.showTabBar = true,
|
||||
this.showLogo = true,
|
||||
this.showTitle = true,
|
||||
this.showMinimize = true,
|
||||
this.showMaximize = true,
|
||||
this.showClose = true,
|
||||
this.pageViewBuilder,
|
||||
this.tail,
|
||||
this.onClose});
|
||||
const DesktopTab({
|
||||
required this.controller,
|
||||
required this.isMainWindow,
|
||||
this.theme = const TarBarTheme.light(),
|
||||
this.onTabClose,
|
||||
this.showTabBar = true,
|
||||
this.showLogo = true,
|
||||
this.showTitle = true,
|
||||
this.showMinimize = true,
|
||||
this.showMaximize = true,
|
||||
this.showClose = true,
|
||||
this.pageViewBuilder,
|
||||
this.tail,
|
||||
this.onClose,
|
||||
this.tabBuilder,
|
||||
this.labelGetter,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -194,8 +209,10 @@ class DesktopTab extends StatelessWidget {
|
||||
child: Row(
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: !Platform.isMacOS,
|
||||
child: const SizedBox(width: 78,)),
|
||||
offstage: !Platform.isMacOS,
|
||||
child: const SizedBox(
|
||||
width: 78,
|
||||
)),
|
||||
Row(children: [
|
||||
Offstage(
|
||||
offstage: !showLogo,
|
||||
@ -228,6 +245,8 @@ class DesktopTab extends StatelessWidget {
|
||||
controller: controller,
|
||||
onTabClose: onTabClose,
|
||||
theme: theme,
|
||||
tabBuilder: tabBuilder,
|
||||
labelGetter: labelGetter,
|
||||
)),
|
||||
),
|
||||
],
|
||||
@ -356,10 +375,18 @@ class _ListView extends StatelessWidget {
|
||||
final DesktopTabController controller;
|
||||
final Function(String key)? onTabClose;
|
||||
final TarBarTheme theme;
|
||||
|
||||
final TabBuilder? tabBuilder;
|
||||
final LabelGetter? labelGetter;
|
||||
|
||||
Rx<DesktopTabState> get state => controller.state;
|
||||
|
||||
const _ListView(
|
||||
{required this.controller, required this.onTabClose, required this.theme});
|
||||
_ListView(
|
||||
{required this.controller,
|
||||
required this.onTabClose,
|
||||
required this.theme,
|
||||
this.tabBuilder,
|
||||
this.labelGetter});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -373,7 +400,9 @@ class _ListView extends StatelessWidget {
|
||||
final tab = e.value;
|
||||
return _Tab(
|
||||
index: index,
|
||||
label: tab.label,
|
||||
label: labelGetter == null
|
||||
? Rx<String>(tab.label)
|
||||
: labelGetter!(tab.label),
|
||||
selectedIcon: tab.selectedIcon,
|
||||
unselectedIcon: tab.unselectedIcon,
|
||||
closable: tab.closable,
|
||||
@ -381,6 +410,16 @@ class _ListView extends StatelessWidget {
|
||||
onClose: () => controller.remove(index),
|
||||
onSelected: () => controller.jumpTo(index),
|
||||
theme: theme,
|
||||
tabBuilder: tabBuilder == null
|
||||
? null
|
||||
: (Widget icon, Widget labelWidget, TabThemeConf themeConf) {
|
||||
return tabBuilder!(
|
||||
tab.label,
|
||||
icon,
|
||||
labelWidget,
|
||||
themeConf,
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList()));
|
||||
}
|
||||
@ -388,7 +427,7 @@ class _ListView extends StatelessWidget {
|
||||
|
||||
class _Tab extends StatelessWidget {
|
||||
late final int index;
|
||||
late final String label;
|
||||
late final Rx<String> label;
|
||||
late final IconData? selectedIcon;
|
||||
late final IconData? unselectedIcon;
|
||||
late final bool closable;
|
||||
@ -397,6 +436,8 @@ class _Tab extends StatelessWidget {
|
||||
late final Function() onSelected;
|
||||
final RxBool _hover = false.obs;
|
||||
late final TarBarTheme theme;
|
||||
final Widget Function(Widget icon, Widget label, TabThemeConf themeConf)?
|
||||
tabBuilder;
|
||||
|
||||
_Tab(
|
||||
{Key? key,
|
||||
@ -404,6 +445,7 @@ class _Tab extends StatelessWidget {
|
||||
required this.label,
|
||||
this.selectedIcon,
|
||||
this.unselectedIcon,
|
||||
this.tabBuilder,
|
||||
required this.closable,
|
||||
required this.selected,
|
||||
required this.onClose,
|
||||
@ -411,11 +453,49 @@ class _Tab extends StatelessWidget {
|
||||
required this.theme})
|
||||
: super(key: key);
|
||||
|
||||
Widget _buildTabContent() {
|
||||
bool showIcon = selectedIcon != null && unselectedIcon != null;
|
||||
bool isSelected = index == selected;
|
||||
|
||||
final icon = Offstage(
|
||||
offstage: !showIcon,
|
||||
child: Icon(
|
||||
isSelected ? selectedIcon : unselectedIcon,
|
||||
size: _kIconSize,
|
||||
color: isSelected
|
||||
? theme.selectedtabIconColor
|
||||
: theme.unSelectedtabIconColor,
|
||||
).paddingOnly(right: 5));
|
||||
final labelWidget = Obx(() {
|
||||
return Text(
|
||||
translate(label.value),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: isSelected
|
||||
? theme.selectedTextColor
|
||||
: theme.unSelectedTextColor),
|
||||
);
|
||||
});
|
||||
|
||||
if (tabBuilder == null) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
icon,
|
||||
labelWidget,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return tabBuilder!(
|
||||
icon, labelWidget, TabThemeConf(iconSize: _kIconSize, theme: theme));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool show_icon = selectedIcon != null && unselectedIcon != null;
|
||||
bool is_selected = index == selected;
|
||||
bool show_divider = index != selected - 1 && index != selected;
|
||||
bool showIcon = selectedIcon != null && unselectedIcon != null;
|
||||
bool isSelected = index == selected;
|
||||
bool showDivider = index != selected - 1 && index != selected;
|
||||
return Ink(
|
||||
child: InkWell(
|
||||
onHover: (hover) => _hover.value = hover,
|
||||
@ -427,40 +507,19 @@ class _Tab extends StatelessWidget {
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Offstage(
|
||||
offstage: !show_icon,
|
||||
child: Icon(
|
||||
is_selected ? selectedIcon : unselectedIcon,
|
||||
size: _kIconSize,
|
||||
color: is_selected
|
||||
? theme.selectedtabIconColor
|
||||
: theme.unSelectedtabIconColor,
|
||||
).paddingOnly(right: 5)),
|
||||
Text(
|
||||
translate(label),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: is_selected
|
||||
? theme.selectedTextColor
|
||||
: theme.unSelectedTextColor),
|
||||
),
|
||||
],
|
||||
),
|
||||
_buildTabContent(),
|
||||
Offstage(
|
||||
offstage: !closable,
|
||||
child: Obx((() => _CloseButton(
|
||||
visiable: _hover.value,
|
||||
tabSelected: is_selected,
|
||||
tabSelected: isSelected,
|
||||
onClose: () => onClose(),
|
||||
theme: theme,
|
||||
))),
|
||||
)
|
||||
])).paddingSymmetric(horizontal: 10),
|
||||
Offstage(
|
||||
offstage: !show_divider,
|
||||
offstage: !showDivider,
|
||||
child: VerticalDivider(
|
||||
width: 1,
|
||||
indent: _kDividerIndent,
|
||||
|
@ -202,7 +202,7 @@ class App extends StatelessWidget {
|
||||
title: 'RustDesk',
|
||||
theme: getCurrentTheme(),
|
||||
home: isDesktop
|
||||
? DesktopTabPage()
|
||||
? const DesktopTabPage()
|
||||
: !isAndroid
|
||||
? WebHomePage()
|
||||
: HomePage(),
|
||||
|
@ -17,6 +17,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
import '../common.dart';
|
||||
import '../common/shared_state.dart';
|
||||
import '../mobile/widgets/dialog.dart';
|
||||
import '../mobile/widgets/overlay.dart';
|
||||
import 'peer_model.dart';
|
||||
@ -96,25 +97,26 @@ class FfiModel with ChangeNotifier {
|
||||
clearPermissions();
|
||||
}
|
||||
|
||||
void setConnectionType(bool secure, bool direct) {
|
||||
void setConnectionType(String peerId, bool secure, bool direct) {
|
||||
_secure = secure;
|
||||
_direct = direct;
|
||||
try {
|
||||
var connectionType = ConnectionTypeState.find(peerId);
|
||||
connectionType.setSecure(secure);
|
||||
connectionType.setDirect(direct);
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
Image? getConnectionImage() {
|
||||
String? icon;
|
||||
if (secure == true && direct == true) {
|
||||
icon = 'secure';
|
||||
} else if (secure == false && direct == true) {
|
||||
icon = 'insecure';
|
||||
} else if (secure == false && direct == false) {
|
||||
icon = 'insecure_relay';
|
||||
} else if (secure == true && direct == false) {
|
||||
icon = 'secure_relay';
|
||||
if (secure == null || direct == null) {
|
||||
return null;
|
||||
} else {
|
||||
final icon =
|
||||
'${secure == true ? "secure" : "insecure"}${direct == true ? "" : "_relay"}';
|
||||
return Image.asset('assets/$icon.png', width: 48, height: 48);
|
||||
}
|
||||
return icon == null
|
||||
? null
|
||||
: Image.asset('assets/$icon.png', width: 48, height: 48);
|
||||
}
|
||||
|
||||
void clearPermissions() {
|
||||
@ -130,7 +132,8 @@ class FfiModel with ChangeNotifier {
|
||||
} else if (name == 'peer_info') {
|
||||
handlePeerInfo(evt, peerId);
|
||||
} else if (name == 'connection_ready') {
|
||||
setConnectionType(evt['secure'] == 'true', evt['direct'] == 'true');
|
||||
setConnectionType(
|
||||
peerId, evt['secure'] == 'true', evt['direct'] == 'true');
|
||||
} else if (name == 'switch_display') {
|
||||
handleSwitchDisplay(evt);
|
||||
} else if (name == 'cursor_data') {
|
||||
@ -189,7 +192,7 @@ class FfiModel with ChangeNotifier {
|
||||
handlePeerInfo(evt, peerId);
|
||||
} else if (name == 'connection_ready') {
|
||||
parent.target?.ffiModel.setConnectionType(
|
||||
evt['secure'] == 'true', evt['direct'] == 'true');
|
||||
peerId, evt['secure'] == 'true', evt['direct'] == 'true');
|
||||
} else if (name == 'switch_display') {
|
||||
handleSwitchDisplay(evt);
|
||||
} else if (name == 'cursor_data') {
|
||||
|
Loading…
Reference in New Issue
Block a user