mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-05-11 10:26:19 +02:00
main window add block mask, cm add keyboard block (#8640)
* block window body only so user can still click minisize button. * cm doesn't show mask * Remove focusable Offstage in tabbar_widget.dart Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
parent
eb5ab4d7d9
commit
af66d2a73b
4 changed files with 162 additions and 135 deletions
|
@ -2715,20 +2715,26 @@ Future<void> shouldBeBlocked(RxBool block, WhetherUseRemoteBlock? use) async {
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef WhetherUseRemoteBlock = Future<bool> Function();
|
typedef WhetherUseRemoteBlock = Future<bool> Function();
|
||||||
Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) {
|
Widget buildRemoteBlock(
|
||||||
var block = false.obs;
|
{required Widget child,
|
||||||
|
required RxBool block,
|
||||||
|
required bool mask,
|
||||||
|
WhetherUseRemoteBlock? use}) {
|
||||||
return Obx(() => MouseRegion(
|
return Obx(() => MouseRegion(
|
||||||
onEnter: (_) async {
|
onEnter: (_) async {
|
||||||
await shouldBeBlocked(block, use);
|
await shouldBeBlocked(block, use);
|
||||||
},
|
},
|
||||||
onExit: (event) => block.value = false,
|
onExit: (event) => block.value = false,
|
||||||
child: Stack(children: [
|
child: Stack(children: [
|
||||||
child,
|
// scope block tab
|
||||||
Offstage(
|
FocusScope(child: child, canRequestFocus: !block.value),
|
||||||
offstage: !block.value,
|
// mask block click, cm not block click and still use check_click_time to avoid block local click
|
||||||
child: Container(
|
if (mask)
|
||||||
color: Colors.black.withOpacity(0.5),
|
Offstage(
|
||||||
)),
|
offstage: !block.value,
|
||||||
|
child: Container(
|
||||||
|
color: Colors.black.withOpacity(0.5),
|
||||||
|
)),
|
||||||
]),
|
]),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,22 +116,14 @@ class _DesktopTabPageState extends State<DesktopTabPage>
|
||||||
isClose: false,
|
isClose: false,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
blockTab: _block,
|
||||||
)));
|
)));
|
||||||
widget() => MouseRegion(
|
|
||||||
onEnter: (_) async {
|
|
||||||
// mouseIn = true;
|
|
||||||
await shouldBeBlocked(_block, canBeBlocked);
|
|
||||||
},
|
|
||||||
onExit: (_) {
|
|
||||||
// mouseIn = false;
|
|
||||||
},
|
|
||||||
child: FocusScope(child: tabWidget, canRequestFocus: !_block.value));
|
|
||||||
return isMacOS || kUseCompatibleUiMode
|
return isMacOS || kUseCompatibleUiMode
|
||||||
? Obx(() => widget())
|
? Obx(() => tabWidget)
|
||||||
: Obx(
|
: Obx(
|
||||||
() => DragToResizeArea(
|
() => DragToResizeArea(
|
||||||
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
|
||||||
child: widget(),
|
child: tabWidget,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,18 @@ class ConnectionManager extends StatefulWidget {
|
||||||
State<StatefulWidget> createState() => ConnectionManagerState();
|
State<StatefulWidget> createState() => ConnectionManagerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConnectionManagerState extends State<ConnectionManager> {
|
class ConnectionManagerState extends State<ConnectionManager>
|
||||||
|
with WidgetsBindingObserver {
|
||||||
|
final RxBool _block = false.obs;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
super.didChangeAppLifecycleState(state);
|
||||||
|
if (state == AppLifecycleState.resumed) {
|
||||||
|
shouldBeBlocked(_block, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
gFFI.serverModel.updateClientState();
|
gFFI.serverModel.updateClientState();
|
||||||
|
@ -127,9 +138,16 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
gFFI.chatModel.isConnManager = true;
|
gFFI.chatModel.isConnManager = true;
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final serverModel = Provider.of<ServerModel>(context);
|
final serverModel = Provider.of<ServerModel>(context);
|
||||||
|
@ -165,6 +183,7 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||||
selectedBorderColor: MyTheme.accent,
|
selectedBorderColor: MyTheme.accent,
|
||||||
maxLabelWidth: 100,
|
maxLabelWidth: 100,
|
||||||
tail: null, //buildScrollJumper(),
|
tail: null, //buildScrollJumper(),
|
||||||
|
blockTab: _block,
|
||||||
selectedTabBackgroundColor:
|
selectedTabBackgroundColor:
|
||||||
Theme.of(context).hintColor.withOpacity(0),
|
Theme.of(context).hintColor.withOpacity(0),
|
||||||
tabBuilder: (key, icon, label, themeConf) {
|
tabBuilder: (key, icon, label, themeConf) {
|
||||||
|
@ -208,14 +227,9 @@ class ConnectionManagerState extends State<ConnectionManager> {
|
||||||
builder: (_, model, child) => SizedBox(
|
builder: (_, model, child) => SizedBox(
|
||||||
width: realChatPageWidth,
|
width: realChatPageWidth,
|
||||||
child: buildRemoteBlock(
|
child: buildRemoteBlock(
|
||||||
child: Container(
|
child: buildSidePage(),
|
||||||
decoration: BoxDecoration(
|
block: _block,
|
||||||
border: Border(
|
mask: true),
|
||||||
right: BorderSide(
|
|
||||||
color: Theme.of(context)
|
|
||||||
.dividerColor))),
|
|
||||||
child: buildSidePage()),
|
|
||||||
),
|
|
||||||
)),
|
)),
|
||||||
SizedBox(
|
SizedBox(
|
||||||
width: realClosedWidth,
|
width: realClosedWidth,
|
||||||
|
|
|
@ -248,6 +248,7 @@ class DesktopTab extends StatelessWidget {
|
||||||
final Color? selectedTabBackgroundColor;
|
final Color? selectedTabBackgroundColor;
|
||||||
final Color? unSelectedTabBackgroundColor;
|
final Color? unSelectedTabBackgroundColor;
|
||||||
final Color? selectedBorderColor;
|
final Color? selectedBorderColor;
|
||||||
|
final RxBool? blockTab;
|
||||||
|
|
||||||
final DesktopTabController controller;
|
final DesktopTabController controller;
|
||||||
|
|
||||||
|
@ -277,6 +278,7 @@ class DesktopTab extends StatelessWidget {
|
||||||
this.selectedTabBackgroundColor,
|
this.selectedTabBackgroundColor,
|
||||||
this.unSelectedTabBackgroundColor,
|
this.unSelectedTabBackgroundColor,
|
||||||
this.selectedBorderColor,
|
this.selectedBorderColor,
|
||||||
|
this.blockTab,
|
||||||
}) : super(key: key) {
|
}) : super(key: key) {
|
||||||
tabType = controller.tabType;
|
tabType = controller.tabType;
|
||||||
isMainWindow = tabType == DesktopTabType.main ||
|
isMainWindow = tabType == DesktopTabType.main ||
|
||||||
|
@ -292,10 +294,10 @@ class DesktopTab extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(children: [
|
return Column(children: [
|
||||||
Obx(() => Offstage(
|
Obx(() {
|
||||||
offstage: !stateGlobal.showTabBar.isTrue ||
|
if (stateGlobal.showTabBar.isTrue &&
|
||||||
(kUseCompatibleUiMode && isHideSingleItem()),
|
!(kUseCompatibleUiMode && isHideSingleItem())) {
|
||||||
child: SizedBox(
|
return SizedBox(
|
||||||
height: _kTabBarHeight,
|
height: _kTabBarHeight,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
|
@ -308,7 +310,11 @@ class DesktopTab extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
))),
|
);
|
||||||
|
} else {
|
||||||
|
return Offstage();
|
||||||
|
}
|
||||||
|
}),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: pageViewBuilder != null
|
child: pageViewBuilder != null
|
||||||
? pageViewBuilder!(_buildPageView())
|
? pageViewBuilder!(_buildPageView())
|
||||||
|
@ -317,10 +323,15 @@ class DesktopTab extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildBlock({required Widget child}) {
|
Widget _buildBlock({required Widget child}) {
|
||||||
if (tabType != DesktopTabType.main) {
|
if (blockTab != null) {
|
||||||
|
return buildRemoteBlock(
|
||||||
|
child: child,
|
||||||
|
block: blockTab!,
|
||||||
|
use: canBeBlocked,
|
||||||
|
mask: tabType == DesktopTabType.main);
|
||||||
|
} else {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
return buildRemoteBlock(child: child, use: canBeBlocked);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Widget> _tabWidgets = [];
|
List<Widget> _tabWidgets = [];
|
||||||
|
@ -703,72 +714,69 @@ class WindowActionPanelState extends State<WindowActionPanel>
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Obx(() => Offstage(
|
Obx(() {
|
||||||
offstage:
|
if (showTabDowndown() && existingInvisibleTab().isNotEmpty) {
|
||||||
!(showTabDowndown() && existingInvisibleTab().isNotEmpty),
|
return _TabDropDownButton(
|
||||||
child: _TabDropDownButton(
|
controller: widget.tabController,
|
||||||
controller: widget.tabController,
|
labelGetter: widget.labelGetter,
|
||||||
labelGetter: widget.labelGetter,
|
tabkeys: existingInvisibleTab());
|
||||||
tabkeys: existingInvisibleTab()),
|
} else {
|
||||||
)),
|
return Offstage();
|
||||||
Offstage(offstage: widget.tail == null, child: widget.tail),
|
}
|
||||||
Offstage(
|
}),
|
||||||
offstage: kUseCompatibleUiMode,
|
if (widget.tail != null) widget.tail!,
|
||||||
child: Row(
|
if (!kUseCompatibleUiMode)
|
||||||
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Offstage(
|
if (widget.showMinimize && !isMacOS)
|
||||||
offstage: !widget.showMinimize || isMacOS,
|
ActionIcon(
|
||||||
child: ActionIcon(
|
message: 'Minimize',
|
||||||
message: 'Minimize',
|
icon: IconFont.min,
|
||||||
icon: IconFont.min,
|
onTap: () {
|
||||||
onTap: () {
|
if (widget.isMainWindow) {
|
||||||
if (widget.isMainWindow) {
|
windowManager.minimize();
|
||||||
windowManager.minimize();
|
} else {
|
||||||
} else {
|
WindowController.fromWindowId(kWindowId!).minimize();
|
||||||
WindowController.fromWindowId(kWindowId!).minimize();
|
}
|
||||||
}
|
},
|
||||||
},
|
isClose: false,
|
||||||
isClose: false,
|
),
|
||||||
)),
|
if (widget.showMaximize && !isMacOS)
|
||||||
Offstage(
|
Obx(() => ActionIcon(
|
||||||
offstage: !widget.showMaximize || isMacOS,
|
message: stateGlobal.isMaximized.isTrue
|
||||||
child: Obx(() => ActionIcon(
|
? 'Restore'
|
||||||
message: stateGlobal.isMaximized.isTrue
|
: 'Maximize',
|
||||||
? 'Restore'
|
icon: stateGlobal.isMaximized.isTrue
|
||||||
: 'Maximize',
|
? IconFont.restore
|
||||||
icon: stateGlobal.isMaximized.isTrue
|
: IconFont.max,
|
||||||
? IconFont.restore
|
onTap: bind.isIncomingOnly() && isInHomePage()
|
||||||
: IconFont.max,
|
? null
|
||||||
onTap: bind.isIncomingOnly() && isInHomePage()
|
: _toggleMaximize,
|
||||||
? null
|
isClose: false,
|
||||||
: _toggleMaximize,
|
)),
|
||||||
isClose: false,
|
if (widget.showClose && !isMacOS)
|
||||||
))),
|
ActionIcon(
|
||||||
Offstage(
|
message: 'Close',
|
||||||
offstage: !widget.showClose || isMacOS,
|
icon: IconFont.close,
|
||||||
child: ActionIcon(
|
onTap: () async {
|
||||||
message: 'Close',
|
final res = await widget.onClose?.call() ?? true;
|
||||||
icon: IconFont.close,
|
if (res) {
|
||||||
onTap: () async {
|
// hide for all window
|
||||||
final res = await widget.onClose?.call() ?? true;
|
// note: the main window can be restored by tray icon
|
||||||
if (res) {
|
Future.delayed(Duration.zero, () async {
|
||||||
// hide for all window
|
if (widget.isMainWindow) {
|
||||||
// note: the main window can be restored by tray icon
|
await windowManager.close();
|
||||||
Future.delayed(Duration.zero, () async {
|
} else {
|
||||||
if (widget.isMainWindow) {
|
await WindowController.fromWindowId(kWindowId!)
|
||||||
await windowManager.close();
|
.close();
|
||||||
} else {
|
}
|
||||||
await WindowController.fromWindowId(kWindowId!)
|
});
|
||||||
.close();
|
}
|
||||||
}
|
},
|
||||||
});
|
isClose: true,
|
||||||
}
|
)
|
||||||
},
|
|
||||||
isClose: true,
|
|
||||||
))
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1172,22 +1180,26 @@ class _CloseButton extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: _kIconSize,
|
width: _kIconSize,
|
||||||
child: Offstage(
|
child: () {
|
||||||
offstage: !visible,
|
if (visible) {
|
||||||
child: InkWell(
|
return InkWell(
|
||||||
hoverColor: MyTheme.tabbar(context).closeHoverColor,
|
hoverColor: MyTheme.tabbar(context).closeHoverColor,
|
||||||
customBorder: const CircleBorder(),
|
customBorder: const CircleBorder(),
|
||||||
onTap: () => onClose(),
|
onTap: () => onClose(),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.close,
|
Icons.close,
|
||||||
size: _kIconSize,
|
size: _kIconSize,
|
||||||
color: tabSelected
|
color: tabSelected
|
||||||
? MyTheme.tabbar(context).selectedIconColor
|
? MyTheme.tabbar(context).selectedIconColor
|
||||||
: MyTheme.tabbar(context).unSelectedIconColor,
|
: MyTheme.tabbar(context).unSelectedIconColor,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
)).paddingOnly(left: 10);
|
} else {
|
||||||
|
return Offstage();
|
||||||
|
}
|
||||||
|
}())
|
||||||
|
.paddingOnly(left: 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1341,27 +1353,30 @@ class _TabDropDownButtonState extends State<_TabDropDownButton> {
|
||||||
child: InkWell(child: Text(label)),
|
child: InkWell(child: Text(label)),
|
||||||
),
|
),
|
||||||
Obx(
|
Obx(
|
||||||
() => Offstage(
|
() {
|
||||||
offstage: !(tabInfo?.onTabCloseButton != null &&
|
if (tabInfo?.onTabCloseButton != null &&
|
||||||
menuHover.value),
|
menuHover.value) {
|
||||||
child: InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
tabInfo?.onTabCloseButton?.call();
|
tabInfo?.onTabCloseButton?.call();
|
||||||
if (Navigator.of(context).canPop()) {
|
if (Navigator.of(context).canPop()) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: MouseRegion(
|
child: MouseRegion(
|
||||||
cursor: SystemMouseCursors.click,
|
cursor: SystemMouseCursors.click,
|
||||||
onHover: (event) =>
|
onHover: (event) =>
|
||||||
setState(() => btnHover.value = true),
|
setState(() => btnHover.value = true),
|
||||||
onExit: (event) =>
|
onExit: (event) =>
|
||||||
setState(() => btnHover.value = false),
|
setState(() => btnHover.value = false),
|
||||||
child: Icon(Icons.close,
|
child: Icon(Icons.close,
|
||||||
color:
|
color:
|
||||||
btnHover.value ? Colors.red : null))),
|
btnHover.value ? Colors.red : null)));
|
||||||
),
|
} else {
|
||||||
)
|
return Offstage();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue