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:
21pages 2024-07-08 20:08:05 +08:00 committed by GitHub
parent eb5ab4d7d9
commit af66d2a73b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 162 additions and 135 deletions

View file

@ -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),
)),
]), ]),
)); ));
} }

View file

@ -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,
), ),
); );
} }

View file

@ -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,

View file

@ -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();
}
},
),
], ],
), ),
), ),