mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-05-27 11:38:40 +00:00
@@ -3,6 +3,11 @@ import 'dart:ui' show clampDouble;
|
|||||||
|
|
||||||
import 'package:PiliPlus/utils/platform_utils.dart';
|
import 'package:PiliPlus/utils/platform_utils.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter/rendering.dart'
|
||||||
|
show
|
||||||
|
ContainerRenderObjectMixin,
|
||||||
|
RenderBoxContainerDefaultsMixin,
|
||||||
|
MultiChildLayoutParentData;
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
enum TooltipType { top, right }
|
enum TooltipType { top, right }
|
||||||
@@ -74,8 +79,10 @@ class _CustomTooltipState extends State<CustomTooltip> {
|
|||||||
@protected
|
@protected
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_longPressRecognizer?.onLongPressCancel = null;
|
_longPressRecognizer
|
||||||
_longPressRecognizer?.dispose();
|
?..onLongPressCancel = null
|
||||||
|
..dispose();
|
||||||
|
_longPressRecognizer = null;
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,38 +135,31 @@ class _CustomTooltipOverlay extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = CustomMultiChildLayout(
|
return _ToolTip(
|
||||||
delegate: _CustomMultiTooltipPositionDelegate(
|
type: type,
|
||||||
type: type,
|
target: target,
|
||||||
target: target,
|
verticalOffset: verticalOffset,
|
||||||
verticalOffset: verticalOffset,
|
horizontalOffset: horizontalOffset,
|
||||||
horizontalOffset: horizontalOffset,
|
preferBelow: false,
|
||||||
preferBelow: false,
|
onTap: PlatformUtils.isMobile ? onDismiss : null,
|
||||||
),
|
|
||||||
children: [
|
children: [
|
||||||
LayoutId(
|
|
||||||
id: _ChildType.overlay,
|
|
||||||
child: overlayWidget(),
|
|
||||||
),
|
|
||||||
LayoutId(
|
LayoutId(
|
||||||
id: _ChildType.indicator,
|
id: _ChildType.indicator,
|
||||||
child: indicator(),
|
child: indicator(),
|
||||||
),
|
),
|
||||||
|
LayoutId(
|
||||||
|
id: _ChildType.overlay,
|
||||||
|
child: overlayWidget(),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (PlatformUtils.isMobile) {
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: onDismiss,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return child;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CustomMultiTooltipPositionDelegate extends MultiChildLayoutDelegate {
|
class _ToolTip extends MultiChildRenderObjectWidget {
|
||||||
_CustomMultiTooltipPositionDelegate({
|
const _ToolTip({
|
||||||
|
super.children,
|
||||||
|
this.onTap,
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.target,
|
required this.target,
|
||||||
required this.verticalOffset,
|
required this.verticalOffset,
|
||||||
@@ -167,98 +167,188 @@ class _CustomMultiTooltipPositionDelegate extends MultiChildLayoutDelegate {
|
|||||||
required this.preferBelow,
|
required this.preferBelow,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final VoidCallback? onTap;
|
||||||
final TooltipType type;
|
final TooltipType type;
|
||||||
|
|
||||||
final Offset target;
|
final Offset target;
|
||||||
|
|
||||||
final double verticalOffset;
|
final double verticalOffset;
|
||||||
|
|
||||||
final double horizontalOffset;
|
final double horizontalOffset;
|
||||||
|
|
||||||
final bool preferBelow;
|
final bool preferBelow;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void performLayout(Size size) {
|
RenderObject createRenderObject(BuildContext context) {
|
||||||
switch (type) {
|
return _RenderToolTip(
|
||||||
case TooltipType.top:
|
onTap: onTap,
|
||||||
Size? indicatorSize;
|
type: type,
|
||||||
if (hasChild(_ChildType.indicator)) {
|
target: target,
|
||||||
indicatorSize = layoutChild(
|
verticalOffset: verticalOffset,
|
||||||
_ChildType.indicator,
|
horizontalOffset: horizontalOffset,
|
||||||
BoxConstraints.loose(size),
|
preferBelow: preferBelow,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasChild(_ChildType.overlay)) {
|
@override
|
||||||
final overlaySize = layoutChild(
|
void updateRenderObject(BuildContext context, _RenderToolTip renderObject) {
|
||||||
_ChildType.overlay,
|
renderObject
|
||||||
BoxConstraints.loose(size),
|
..onTap = onTap
|
||||||
);
|
..target = target
|
||||||
Offset offset = positionDependentBox(
|
..verticalOffset = verticalOffset
|
||||||
type: type,
|
..horizontalOffset = horizontalOffset
|
||||||
size: size,
|
..preferBelow = preferBelow;
|
||||||
childSize: overlaySize,
|
}
|
||||||
target: target,
|
}
|
||||||
verticalOffset: verticalOffset,
|
|
||||||
horizontalOffset: horizontalOffset,
|
|
||||||
preferBelow: preferBelow,
|
|
||||||
);
|
|
||||||
if (indicatorSize != null) {
|
|
||||||
offset = Offset(offset.dx, offset.dy - indicatorSize.height + 1);
|
|
||||||
positionChild(
|
|
||||||
_ChildType.indicator,
|
|
||||||
Offset(
|
|
||||||
target.dx - indicatorSize.width / 2,
|
|
||||||
offset.dy + overlaySize.height - 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
positionChild(_ChildType.overlay, offset);
|
|
||||||
}
|
|
||||||
case TooltipType.right:
|
|
||||||
Size? indicatorSize;
|
|
||||||
if (hasChild(_ChildType.indicator)) {
|
|
||||||
indicatorSize = layoutChild(
|
|
||||||
_ChildType.indicator,
|
|
||||||
BoxConstraints.loose(size),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasChild(_ChildType.overlay)) {
|
class _RenderToolTip extends RenderBox
|
||||||
final overlaySize = layoutChild(
|
with
|
||||||
_ChildType.overlay,
|
ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
|
||||||
BoxConstraints.loose(size),
|
RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
|
||||||
);
|
_RenderToolTip({
|
||||||
Offset offset = positionDependentBox(
|
VoidCallback? onTap,
|
||||||
type: type,
|
required TooltipType type,
|
||||||
size: size,
|
required Offset target,
|
||||||
childSize: overlaySize,
|
required double verticalOffset,
|
||||||
target: target,
|
required double horizontalOffset,
|
||||||
verticalOffset: verticalOffset,
|
required bool preferBelow,
|
||||||
horizontalOffset: horizontalOffset,
|
}) : _type = type,
|
||||||
preferBelow: preferBelow,
|
_target = target,
|
||||||
);
|
_verticalOffset = verticalOffset,
|
||||||
if (indicatorSize != null) {
|
_horizontalOffset = horizontalOffset,
|
||||||
offset = Offset(offset.dx + indicatorSize.height - 1, offset.dy);
|
_preferBelow = preferBelow,
|
||||||
positionChild(
|
_hitTestSelf = onTap != null {
|
||||||
_ChildType.indicator,
|
if (onTap != null) {
|
||||||
Offset(
|
_tapGestureRecognizer = TapGestureRecognizer()..onTap = onTap;
|
||||||
offset.dx - indicatorSize.width + 1,
|
}
|
||||||
target.dy - indicatorSize.height / 2,
|
}
|
||||||
),
|
|
||||||
);
|
TapGestureRecognizer? _tapGestureRecognizer;
|
||||||
}
|
|
||||||
positionChild(_ChildType.overlay, offset);
|
set onTap(VoidCallback? value) {
|
||||||
}
|
_tapGestureRecognizer?.onTap = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_tapGestureRecognizer
|
||||||
|
?..onTap = null
|
||||||
|
..dispose();
|
||||||
|
_tapGestureRecognizer = null;
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
final bool _hitTestSelf;
|
||||||
|
@override
|
||||||
|
bool hitTestSelf(Offset position) => _hitTestSelf;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void handleEvent(PointerEvent event, HitTestEntry<HitTestTarget> entry) {
|
||||||
|
if (event is PointerDownEvent) {
|
||||||
|
_tapGestureRecognizer?.addPointer(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final TooltipType _type;
|
||||||
|
|
||||||
|
Offset _target;
|
||||||
|
Offset get target => _target;
|
||||||
|
set target(Offset value) {
|
||||||
|
if (_target == value) return;
|
||||||
|
_target = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
double _verticalOffset;
|
||||||
|
double get verticalOffset => _verticalOffset;
|
||||||
|
set verticalOffset(double value) {
|
||||||
|
if (_verticalOffset == value) return;
|
||||||
|
_verticalOffset = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
double _horizontalOffset;
|
||||||
|
double get horizontalOffset => _horizontalOffset;
|
||||||
|
set horizontalOffset(double value) {
|
||||||
|
if (_horizontalOffset == value) return;
|
||||||
|
_horizontalOffset = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _preferBelow;
|
||||||
|
bool get preferBelow => _preferBelow;
|
||||||
|
set preferBelow(bool value) {
|
||||||
|
if (_preferBelow == value) return;
|
||||||
|
_preferBelow = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setupParentData(RenderBox child) {
|
||||||
|
if (child.parentData is! MultiChildLayoutParentData) {
|
||||||
|
child.parentData = MultiChildLayoutParentData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool shouldRelayout(_CustomMultiTooltipPositionDelegate oldDelegate) {
|
void performLayout() {
|
||||||
return target != oldDelegate.target ||
|
size = constraints.constrain(constraints.biggest);
|
||||||
verticalOffset != oldDelegate.verticalOffset ||
|
|
||||||
preferBelow != oldDelegate.preferBelow;
|
final c = BoxConstraints.loose(size);
|
||||||
|
RenderBox indicator = firstChild!..layout(c, parentUsesSize: true);
|
||||||
|
RenderBox overlay = lastChild!..layout(c, parentUsesSize: true);
|
||||||
|
|
||||||
|
final indicatorSize = indicator.size;
|
||||||
|
final overlaySize = overlay.size;
|
||||||
|
|
||||||
|
final indicatorParentData =
|
||||||
|
indicator.parentData as MultiChildLayoutParentData;
|
||||||
|
final overlayParentData = overlay.parentData as MultiChildLayoutParentData;
|
||||||
|
|
||||||
|
switch (_type) {
|
||||||
|
case TooltipType.top:
|
||||||
|
Offset offset = _positionDependentBox(
|
||||||
|
type: _type,
|
||||||
|
size: size,
|
||||||
|
childSize: overlaySize,
|
||||||
|
target: target,
|
||||||
|
verticalOffset: verticalOffset,
|
||||||
|
horizontalOffset: horizontalOffset,
|
||||||
|
preferBelow: preferBelow,
|
||||||
|
);
|
||||||
|
offset = Offset(offset.dx, offset.dy - indicatorSize.height + 1);
|
||||||
|
overlayParentData.offset = offset;
|
||||||
|
indicatorParentData.offset = Offset(
|
||||||
|
target.dx - indicatorSize.width / 2,
|
||||||
|
offset.dy + overlaySize.height - 1,
|
||||||
|
);
|
||||||
|
case TooltipType.right:
|
||||||
|
Offset offset = _positionDependentBox(
|
||||||
|
type: _type,
|
||||||
|
size: size,
|
||||||
|
childSize: overlaySize,
|
||||||
|
target: target,
|
||||||
|
verticalOffset: verticalOffset,
|
||||||
|
horizontalOffset: horizontalOffset,
|
||||||
|
preferBelow: preferBelow,
|
||||||
|
);
|
||||||
|
offset = Offset(offset.dx + indicatorSize.height - 1, offset.dy);
|
||||||
|
overlayParentData.offset = offset;
|
||||||
|
Offset(
|
||||||
|
offset.dx - indicatorSize.width + 1,
|
||||||
|
target.dy - indicatorSize.height / 2,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final childParentData = child.parentData as MultiChildLayoutParentData;
|
||||||
|
context.paintChild(child, childParentData.offset + offset);
|
||||||
|
child = childParentData.nextSibling;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isRepaintBoundary => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Triangle extends LeafRenderObjectWidget {
|
class Triangle extends LeafRenderObjectWidget {
|
||||||
@@ -354,7 +444,7 @@ class RenderTriangle extends RenderBox {
|
|||||||
bool get isRepaintBoundary => true;
|
bool get isRepaintBoundary => true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Offset positionDependentBox({
|
Offset _positionDependentBox({
|
||||||
required TooltipType type,
|
required TooltipType type,
|
||||||
required Size size,
|
required Size size,
|
||||||
required Size childSize,
|
required Size childSize,
|
||||||
|
|||||||
Reference in New Issue
Block a user