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();
Widget buildRemoteBlock({required Widget child, WhetherUseRemoteBlock? use}) {
var block = false.obs;
Widget buildRemoteBlock(
{required Widget child,
required RxBool block,
required bool mask,
WhetherUseRemoteBlock? use}) {
return Obx(() => MouseRegion(
onEnter: (_) async {
await shouldBeBlocked(block, use);
},
onExit: (event) => block.value = false,
child: Stack(children: [
child,
Offstage(
offstage: !block.value,
child: Container(
color: Colors.black.withOpacity(0.5),
)),
// scope block tab
FocusScope(child: child, canRequestFocus: !block.value),
// mask block click, cm not block click and still use check_click_time to avoid block local click
if (mask)
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,
),
),
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
? Obx(() => widget())
? Obx(() => tabWidget)
: Obx(
() => DragToResizeArea(
resizeEdgeSize: stateGlobal.resizeEdgeSize.value,
child: widget(),
child: tabWidget,
),
);
}

View File

@ -104,7 +104,18 @@ class ConnectionManager extends StatefulWidget {
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
void initState() {
gFFI.serverModel.updateClientState();
@ -127,9 +138,16 @@ class ConnectionManagerState extends State<ConnectionManager> {
}
};
gFFI.chatModel.isConnManager = true;
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
final serverModel = Provider.of<ServerModel>(context);
@ -165,6 +183,7 @@ class ConnectionManagerState extends State<ConnectionManager> {
selectedBorderColor: MyTheme.accent,
maxLabelWidth: 100,
tail: null, //buildScrollJumper(),
blockTab: _block,
selectedTabBackgroundColor:
Theme.of(context).hintColor.withOpacity(0),
tabBuilder: (key, icon, label, themeConf) {
@ -208,14 +227,9 @@ class ConnectionManagerState extends State<ConnectionManager> {
builder: (_, model, child) => SizedBox(
width: realChatPageWidth,
child: buildRemoteBlock(
child: Container(
decoration: BoxDecoration(
border: Border(
right: BorderSide(
color: Theme.of(context)
.dividerColor))),
child: buildSidePage()),
),
child: buildSidePage(),
block: _block,
mask: true),
)),
SizedBox(
width: realClosedWidth,

View File

@ -248,6 +248,7 @@ class DesktopTab extends StatelessWidget {
final Color? selectedTabBackgroundColor;
final Color? unSelectedTabBackgroundColor;
final Color? selectedBorderColor;
final RxBool? blockTab;
final DesktopTabController controller;
@ -277,6 +278,7 @@ class DesktopTab extends StatelessWidget {
this.selectedTabBackgroundColor,
this.unSelectedTabBackgroundColor,
this.selectedBorderColor,
this.blockTab,
}) : super(key: key) {
tabType = controller.tabType;
isMainWindow = tabType == DesktopTabType.main ||
@ -292,10 +294,10 @@ class DesktopTab extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(children: [
Obx(() => Offstage(
offstage: !stateGlobal.showTabBar.isTrue ||
(kUseCompatibleUiMode && isHideSingleItem()),
child: SizedBox(
Obx(() {
if (stateGlobal.showTabBar.isTrue &&
!(kUseCompatibleUiMode && isHideSingleItem())) {
return SizedBox(
height: _kTabBarHeight,
child: Column(
children: [
@ -308,7 +310,11 @@ class DesktopTab extends StatelessWidget {
),
],
),
))),
);
} else {
return Offstage();
}
}),
Expanded(
child: pageViewBuilder != null
? pageViewBuilder!(_buildPageView())
@ -317,10 +323,15 @@ class DesktopTab extends StatelessWidget {
}
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 buildRemoteBlock(child: child, use: canBeBlocked);
}
List<Widget> _tabWidgets = [];
@ -703,72 +714,69 @@ class WindowActionPanelState extends State<WindowActionPanel>
return Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Obx(() => Offstage(
offstage:
!(showTabDowndown() && existingInvisibleTab().isNotEmpty),
child: _TabDropDownButton(
controller: widget.tabController,
labelGetter: widget.labelGetter,
tabkeys: existingInvisibleTab()),
)),
Offstage(offstage: widget.tail == null, child: widget.tail),
Offstage(
offstage: kUseCompatibleUiMode,
child: Row(
Obx(() {
if (showTabDowndown() && existingInvisibleTab().isNotEmpty) {
return _TabDropDownButton(
controller: widget.tabController,
labelGetter: widget.labelGetter,
tabkeys: existingInvisibleTab());
} else {
return Offstage();
}
}),
if (widget.tail != null) widget.tail!,
if (!kUseCompatibleUiMode)
Row(
children: [
Offstage(
offstage: !widget.showMinimize || isMacOS,
child: ActionIcon(
message: 'Minimize',
icon: IconFont.min,
onTap: () {
if (widget.isMainWindow) {
windowManager.minimize();
} else {
WindowController.fromWindowId(kWindowId!).minimize();
}
},
isClose: false,
)),
Offstage(
offstage: !widget.showMaximize || isMacOS,
child: Obx(() => ActionIcon(
message: stateGlobal.isMaximized.isTrue
? 'Restore'
: 'Maximize',
icon: stateGlobal.isMaximized.isTrue
? IconFont.restore
: IconFont.max,
onTap: bind.isIncomingOnly() && isInHomePage()
? null
: _toggleMaximize,
isClose: false,
))),
Offstage(
offstage: !widget.showClose || isMacOS,
child: ActionIcon(
message: 'Close',
icon: IconFont.close,
onTap: () async {
final res = await widget.onClose?.call() ?? true;
if (res) {
// hide for all window
// note: the main window can be restored by tray icon
Future.delayed(Duration.zero, () async {
if (widget.isMainWindow) {
await windowManager.close();
} else {
await WindowController.fromWindowId(kWindowId!)
.close();
}
});
}
},
isClose: true,
))
if (widget.showMinimize && !isMacOS)
ActionIcon(
message: 'Minimize',
icon: IconFont.min,
onTap: () {
if (widget.isMainWindow) {
windowManager.minimize();
} else {
WindowController.fromWindowId(kWindowId!).minimize();
}
},
isClose: false,
),
if (widget.showMaximize && !isMacOS)
Obx(() => ActionIcon(
message: stateGlobal.isMaximized.isTrue
? 'Restore'
: 'Maximize',
icon: stateGlobal.isMaximized.isTrue
? IconFont.restore
: IconFont.max,
onTap: bind.isIncomingOnly() && isInHomePage()
? null
: _toggleMaximize,
isClose: false,
)),
if (widget.showClose && !isMacOS)
ActionIcon(
message: 'Close',
icon: IconFont.close,
onTap: () async {
final res = await widget.onClose?.call() ?? true;
if (res) {
// hide for all window
// note: the main window can be restored by tray icon
Future.delayed(Duration.zero, () async {
if (widget.isMainWindow) {
await windowManager.close();
} else {
await WindowController.fromWindowId(kWindowId!)
.close();
}
});
}
},
isClose: true,
)
],
),
),
],
);
}
@ -1172,22 +1180,26 @@ class _CloseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: _kIconSize,
child: Offstage(
offstage: !visible,
child: InkWell(
hoverColor: MyTheme.tabbar(context).closeHoverColor,
customBorder: const CircleBorder(),
onTap: () => onClose(),
child: Icon(
Icons.close,
size: _kIconSize,
color: tabSelected
? MyTheme.tabbar(context).selectedIconColor
: MyTheme.tabbar(context).unSelectedIconColor,
),
),
)).paddingOnly(left: 10);
width: _kIconSize,
child: () {
if (visible) {
return InkWell(
hoverColor: MyTheme.tabbar(context).closeHoverColor,
customBorder: const CircleBorder(),
onTap: () => onClose(),
child: Icon(
Icons.close,
size: _kIconSize,
color: tabSelected
? MyTheme.tabbar(context).selectedIconColor
: MyTheme.tabbar(context).unSelectedIconColor,
),
);
} else {
return Offstage();
}
}())
.paddingOnly(left: 10);
}
}
@ -1341,27 +1353,30 @@ class _TabDropDownButtonState extends State<_TabDropDownButton> {
child: InkWell(child: Text(label)),
),
Obx(
() => Offstage(
offstage: !(tabInfo?.onTabCloseButton != null &&
menuHover.value),
child: InkWell(
onTap: () {
tabInfo?.onTabCloseButton?.call();
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
onHover: (event) =>
setState(() => btnHover.value = true),
onExit: (event) =>
setState(() => btnHover.value = false),
child: Icon(Icons.close,
color:
btnHover.value ? Colors.red : null))),
),
)
() {
if (tabInfo?.onTabCloseButton != null &&
menuHover.value) {
return InkWell(
onTap: () {
tabInfo?.onTabCloseButton?.call();
if (Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
onHover: (event) =>
setState(() => btnHover.value = true),
onExit: (event) =>
setState(() => btnHover.value = false),
child: Icon(Icons.close,
color:
btnHover.value ? Colors.red : null)));
} else {
return Offstage();
}
},
),
],
),
),