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 = [
|
List<Widget> children = [
|
||||||
Popover(
|
Popover(
|
||||||
mutex: _popoverMutex,
|
mutex: _popoverMutex,
|
||||||
|
asBarrier: true,
|
||||||
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
|
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
popupBuilder: (BuildContext context) {
|
popupBuilder: (BuildContext context) {
|
||||||
@ -357,6 +358,7 @@ class _CalDateTimeSettingState extends State<_CalDateTimeSetting> {
|
|||||||
),
|
),
|
||||||
Popover(
|
Popover(
|
||||||
mutex: _popoverMutex,
|
mutex: _popoverMutex,
|
||||||
|
asBarrier: true,
|
||||||
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
|
triggerActions: PopoverTriggerFlags.hover | PopoverTriggerFlags.click,
|
||||||
offset: const Offset(20, 0),
|
offset: const Offset(20, 0),
|
||||||
popupBuilder: (BuildContext context) {
|
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/material.dart';
|
||||||
import 'package:flutter/services.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() onTap;
|
||||||
final void Function()? onExit;
|
final void Function()? onExit;
|
||||||
final Decoration? decoration;
|
final Decoration? decoration;
|
||||||
|
|
||||||
const _PopoverMask(
|
const PopoverMask(
|
||||||
{Key? key,
|
{Key? key, required this.onTap, this.onExit, this.decoration})
|
||||||
required this.onTap,
|
|
||||||
this.onExit,
|
|
||||||
this.decoration =
|
|
||||||
const BoxDecoration(color: Color.fromARGB(0, 244, 67, 54))})
|
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _PopoverMaskState();
|
State<StatefulWidget> createState() => _PopoverMaskState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _PopoverMaskState extends State<_PopoverMask> {
|
class _PopoverMaskState extends State<PopoverMask> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
|
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
|
||||||
@ -30,11 +92,11 @@ class _PopoverMaskState extends State<_PopoverMask> {
|
|||||||
if (widget.onExit != null) {
|
if (widget.onExit != null) {
|
||||||
widget.onExit!();
|
widget.onExit!();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void deactivate() {
|
void deactivate() {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'package:appflowy_popover/src/layout.dart';
|
import 'package:appflowy_popover/src/layout.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'mask.dart';
|
import 'mask.dart';
|
||||||
import 'mutex.dart';
|
import 'mutex.dart';
|
||||||
|
|
||||||
@ -68,6 +67,8 @@ class Popover extends StatefulWidget {
|
|||||||
|
|
||||||
final void Function()? onClose;
|
final void Function()? onClose;
|
||||||
|
|
||||||
|
final bool asBarrier;
|
||||||
|
|
||||||
/// The content area of the popover.
|
/// The content area of the popover.
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@ -77,11 +78,14 @@ class Popover extends StatefulWidget {
|
|||||||
required this.popupBuilder,
|
required this.popupBuilder,
|
||||||
this.controller,
|
this.controller,
|
||||||
this.offset,
|
this.offset,
|
||||||
this.maskDecoration,
|
this.maskDecoration = const BoxDecoration(
|
||||||
|
color: Color.fromARGB(0, 244, 67, 54),
|
||||||
|
),
|
||||||
this.triggerActions = 0,
|
this.triggerActions = 0,
|
||||||
this.direction = PopoverDirection.rightWithTopAligned,
|
this.direction = PopoverDirection.rightWithTopAligned,
|
||||||
this.mutex,
|
this.mutex,
|
||||||
this.onClose,
|
this.onClose,
|
||||||
|
this.asBarrier = false,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -89,11 +93,9 @@ class Popover extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class PopoverState extends State<Popover> {
|
class PopoverState extends State<Popover> {
|
||||||
|
static final RootOverlayEntry _rootEntry = RootOverlayEntry();
|
||||||
final PopoverLink popoverLink = PopoverLink();
|
final PopoverLink popoverLink = PopoverLink();
|
||||||
OverlayEntry? _overlayEntry;
|
Timer? _debounceEnterRegionAction;
|
||||||
bool hasMask = true;
|
|
||||||
|
|
||||||
static PopoverState? _popoverWithMask;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -107,64 +109,51 @@ class PopoverState extends State<Popover> {
|
|||||||
if (widget.mutex != null) {
|
if (widget.mutex != null) {
|
||||||
widget.mutex?.state = this;
|
widget.mutex?.state = this;
|
||||||
}
|
}
|
||||||
|
final shouldAddMask = _rootEntry.isEmpty;
|
||||||
if (_popoverWithMask == null) {
|
|
||||||
_popoverWithMask = this;
|
|
||||||
} else {
|
|
||||||
// hasMask = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final newEntry = OverlayEntry(builder: (context) {
|
final newEntry = OverlayEntry(builder: (context) {
|
||||||
final children = <Widget>[];
|
final children = <Widget>[];
|
||||||
|
if (shouldAddMask) {
|
||||||
if (hasMask) {
|
children.add(
|
||||||
children.add(PopoverMask(
|
PopoverMask(
|
||||||
decoration: widget.maskDecoration,
|
decoration: widget.maskDecoration,
|
||||||
onTap: () => close(),
|
onTap: () => _removeRootOverlay(),
|
||||||
onExit: () => close(),
|
onExit: () => _removeRootOverlay(),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
children.add(PopoverContainer(
|
children.add(
|
||||||
|
PopoverContainer(
|
||||||
direction: widget.direction,
|
direction: widget.direction,
|
||||||
popoverLink: popoverLink,
|
popoverLink: popoverLink,
|
||||||
offset: widget.offset ?? Offset.zero,
|
offset: widget.offset ?? Offset.zero,
|
||||||
popupBuilder: widget.popupBuilder,
|
popupBuilder: widget.popupBuilder,
|
||||||
onClose: () => close(),
|
onClose: () => close(),
|
||||||
onCloseAll: () => closeAll(),
|
onCloseAll: () => _removeRootOverlay(),
|
||||||
));
|
),
|
||||||
|
);
|
||||||
|
|
||||||
return Stack(children: children);
|
return Stack(children: children);
|
||||||
});
|
});
|
||||||
|
|
||||||
_overlayEntry = newEntry;
|
_rootEntry.addEntry(context, this, newEntry, widget.asBarrier);
|
||||||
|
|
||||||
final OverlayState s = Overlay.of(context)!;
|
|
||||||
|
|
||||||
Overlay.of(context)?.insert(newEntry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
if (_overlayEntry != null) {
|
if (_rootEntry.contains(this)) {
|
||||||
_overlayEntry!.remove();
|
_rootEntry.removeEntry(this);
|
||||||
_overlayEntry = null;
|
|
||||||
|
|
||||||
if (_popoverWithMask == this) {
|
|
||||||
_popoverWithMask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
widget.onClose?.call();
|
widget.onClose?.call();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _removeRootOverlay() {
|
||||||
|
_rootEntry.popEntry();
|
||||||
|
|
||||||
if (widget.mutex?.state == this) {
|
if (widget.mutex?.state == this) {
|
||||||
widget.mutex?.removeState();
|
widget.mutex?.removeState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeAll() {
|
|
||||||
_popoverWithMask?.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void deactivate() {
|
void deactivate() {
|
||||||
close();
|
close();
|
||||||
@ -185,10 +174,17 @@ class PopoverState extends State<Popover> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return MouseRegion(
|
return MouseRegion(
|
||||||
onEnter: (PointerEnterEvent event) {
|
onEnter: (event) {
|
||||||
|
_debounceEnterRegionAction =
|
||||||
|
Timer(const Duration(milliseconds: 200), () {
|
||||||
if (widget.triggerActions & PopoverTriggerFlags.hover != 0) {
|
if (widget.triggerActions & PopoverTriggerFlags.hover != 0) {
|
||||||
showOverlay();
|
showOverlay();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onExit: (event) {
|
||||||
|
_debounceEnterRegionAction?.cancel();
|
||||||
|
_debounceEnterRegionAction = null;
|
||||||
},
|
},
|
||||||
child: Listener(
|
child: Listener(
|
||||||
child: widget.child,
|
child: widget.child,
|
||||||
|
Loading…
Reference in New Issue
Block a user