mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: enable remove one layer when click the empty space
This commit is contained in:
parent
d1737d35fe
commit
723b34a736
@ -341,6 +341,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
|
||||
List<Widget> children = [
|
||||
Popover(
|
||||
mutex: _popoverMutex,
|
||||
asBarrier: true,
|
||||
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
|
||||
offset: const Offset(20, 0),
|
||||
popupBuilder: (BuildContext context) {
|
||||
@ -357,6 +358,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
|
||||
),
|
||||
Popover(
|
||||
mutex: _popoverMutex,
|
||||
asBarrier: true,
|
||||
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
|
||||
offset: const Offset(20, 0),
|
||||
popupBuilder: (BuildContext context) {
|
||||
|
@ -1,24 +1,86 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class _PopoverMask extends StatefulWidget {
|
||||
typedef EntryMap = LinkedHashMap<PopoverState, OverlayEntryContext>;
|
||||
|
||||
class RootOverlayEntry {
|
||||
final EntryMap _entries = EntryMap();
|
||||
RootOverlayEntry();
|
||||
|
||||
void addEntry(
|
||||
BuildContext context,
|
||||
PopoverState newState,
|
||||
OverlayEntry entry,
|
||||
bool asBarrier,
|
||||
) {
|
||||
_entries[newState] = OverlayEntryContext(entry, newState, asBarrier);
|
||||
Overlay.of(context)?.insert(entry);
|
||||
}
|
||||
|
||||
bool contains(PopoverState oldState) {
|
||||
return _entries.containsKey(oldState);
|
||||
}
|
||||
|
||||
void removeEntry(PopoverState oldState) {
|
||||
if (_entries.isEmpty) return;
|
||||
|
||||
final removedEntry = _entries.remove(oldState);
|
||||
removedEntry?.overlayEntry.remove();
|
||||
}
|
||||
|
||||
bool get isEmpty => _entries.isEmpty;
|
||||
|
||||
bool get isNotEmpty => _entries.isNotEmpty;
|
||||
|
||||
bool hasEntry() {
|
||||
return _entries.isNotEmpty;
|
||||
}
|
||||
|
||||
PopoverState? popEntry() {
|
||||
if (_entries.isEmpty) return null;
|
||||
|
||||
final lastEntry = _entries.values.last;
|
||||
_entries.remove(lastEntry.popoverState);
|
||||
lastEntry.overlayEntry.remove();
|
||||
lastEntry.popoverState.widget.onClose?.call();
|
||||
|
||||
if (lastEntry.asBarrier) {
|
||||
return lastEntry.popoverState;
|
||||
} else {
|
||||
return popEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OverlayEntryContext {
|
||||
final bool asBarrier;
|
||||
final PopoverState popoverState;
|
||||
final OverlayEntry overlayEntry;
|
||||
|
||||
OverlayEntryContext(
|
||||
this.overlayEntry,
|
||||
this.popoverState,
|
||||
this.asBarrier,
|
||||
);
|
||||
}
|
||||
|
||||
class PopoverMask extends StatefulWidget {
|
||||
final void Function() onTap;
|
||||
final void Function()? onExit;
|
||||
final Decoration? decoration;
|
||||
|
||||
const _PopoverMask(
|
||||
{Key? key,
|
||||
required this.onTap,
|
||||
this.onExit,
|
||||
this.decoration =
|
||||
const BoxDecoration(color: Color.fromARGB(0, 244, 67, 54))})
|
||||
const PopoverMask(
|
||||
{Key? key, required this.onTap, this.onExit, this.decoration})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _PopoverMaskState();
|
||||
}
|
||||
|
||||
class _PopoverMaskState extends State<_PopoverMask> {
|
||||
class _PopoverMaskState extends State<PopoverMask> {
|
||||
@override
|
||||
void initState() {
|
||||
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
|
||||
@ -30,10 +92,10 @@ class _PopoverMaskState extends State<_PopoverMask> {
|
||||
if (widget.onExit != null) {
|
||||
widget.onExit!();
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'package:appflowy_popover/src/layout.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'mask.dart';
|
||||
import 'mutex.dart';
|
||||
|
||||
@ -68,6 +67,8 @@ class Popover extends StatefulWidget {
|
||||
|
||||
final void Function()? onClose;
|
||||
|
||||
final bool asBarrier;
|
||||
|
||||
/// The content area of the popover.
|
||||
final Widget child;
|
||||
|
||||
@ -77,11 +78,14 @@ class Popover extends StatefulWidget {
|
||||
required this.popupBuilder,
|
||||
this.controller,
|
||||
this.offset,
|
||||
this.maskDecoration,
|
||||
this.maskDecoration = const BoxDecoration(
|
||||
color: Color.fromARGB(0, 244, 67, 54),
|
||||
),
|
||||
this.triggerActions = 0,
|
||||
this.direction = PopoverDirection.rightWithTopAligned,
|
||||
this.mutex,
|
||||
this.onClose,
|
||||
this.asBarrier = false,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -89,11 +93,9 @@ class Popover extends StatefulWidget {
|
||||
}
|
||||
|
||||
class PopoverState extends State<Popover> {
|
||||
static final RootOverlayEntry _rootEntry = RootOverlayEntry();
|
||||
final PopoverLink popoverLink = PopoverLink();
|
||||
OverlayEntry? _overlayEntry;
|
||||
bool hasMask = true;
|
||||
|
||||
static PopoverState? _popoverWithMask;
|
||||
Timer? _debounceEnterRegionAction;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@ -107,64 +109,51 @@ class PopoverState extends State<Popover> {
|
||||
if (widget.mutex != null) {
|
||||
widget.mutex?.state = this;
|
||||
}
|
||||
|
||||
if (_popoverWithMask == null) {
|
||||
_popoverWithMask = this;
|
||||
} else {
|
||||
// hasMask = false;
|
||||
}
|
||||
|
||||
final shouldAddMask = _rootEntry.isEmpty;
|
||||
final newEntry = OverlayEntry(builder: (context) {
|
||||
final children = <Widget>[];
|
||||
|
||||
if (hasMask) {
|
||||
children.add(PopoverMask(
|
||||
decoration: widget.maskDecoration,
|
||||
onTap: () => close(),
|
||||
onExit: () => close(),
|
||||
));
|
||||
if (shouldAddMask) {
|
||||
children.add(
|
||||
PopoverMask(
|
||||
decoration: widget.maskDecoration,
|
||||
onTap: () => _removeRootOverlay(),
|
||||
onExit: () => _removeRootOverlay(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
children.add(PopoverContainer(
|
||||
direction: widget.direction,
|
||||
popoverLink: popoverLink,
|
||||
offset: widget.offset ?? Offset.zero,
|
||||
popupBuilder: widget.popupBuilder,
|
||||
onClose: () => close(),
|
||||
onCloseAll: () => closeAll(),
|
||||
));
|
||||
children.add(
|
||||
PopoverContainer(
|
||||
direction: widget.direction,
|
||||
popoverLink: popoverLink,
|
||||
offset: widget.offset ?? Offset.zero,
|
||||
popupBuilder: widget.popupBuilder,
|
||||
onClose: () => close(),
|
||||
onCloseAll: () => _removeRootOverlay(),
|
||||
),
|
||||
);
|
||||
|
||||
return Stack(children: children);
|
||||
});
|
||||
|
||||
_overlayEntry = newEntry;
|
||||
|
||||
final OverlayState s = Overlay.of(context)!;
|
||||
|
||||
Overlay.of(context)?.insert(newEntry);
|
||||
_rootEntry.addEntry(context, this, newEntry, widget.asBarrier);
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (_overlayEntry != null) {
|
||||
_overlayEntry!.remove();
|
||||
_overlayEntry = null;
|
||||
|
||||
if (_popoverWithMask == this) {
|
||||
_popoverWithMask = null;
|
||||
}
|
||||
|
||||
if (_rootEntry.contains(this)) {
|
||||
_rootEntry.removeEntry(this);
|
||||
widget.onClose?.call();
|
||||
}
|
||||
}
|
||||
|
||||
void _removeRootOverlay() {
|
||||
_rootEntry.popEntry();
|
||||
|
||||
if (widget.mutex?.state == this) {
|
||||
widget.mutex?.removeState();
|
||||
}
|
||||
}
|
||||
|
||||
void closeAll() {
|
||||
_popoverWithMask?.close();
|
||||
}
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
close();
|
||||
@ -185,10 +174,17 @@ class PopoverState extends State<Popover> {
|
||||
}
|
||||
|
||||
return MouseRegion(
|
||||
onEnter: (PointerEnterEvent event) {
|
||||
if (widget.triggerActions & PopoverTriggerFlags.hover != 0) {
|
||||
showOverlay();
|
||||
}
|
||||
onEnter: (event) {
|
||||
_debounceEnterRegionAction =
|
||||
Timer(const Duration(milliseconds: 200), () {
|
||||
if (widget.triggerActions & PopoverTriggerFlags.hover != 0) {
|
||||
showOverlay();
|
||||
}
|
||||
});
|
||||
},
|
||||
onExit: (event) {
|
||||
_debounceEnterRegionAction?.cancel();
|
||||
_debounceEnterRegionAction = null;
|
||||
},
|
||||
child: Listener(
|
||||
child: widget.child,
|
||||
|
Loading…
Reference in New Issue
Block a user