opt emoji tooltip

Signed-off-by: dom <githubaccount56556@proton.me>
This commit is contained in:
dom
2026-01-19 18:10:41 +08:00
parent 036dbcaf21
commit 5a61dbe30c

View File

@@ -3,6 +3,11 @@ import 'dart:ui' show clampDouble;
import 'package:PiliPlus/utils/platform_utils.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart'
show
ContainerRenderObjectMixin,
RenderBoxContainerDefaultsMixin,
MultiChildLayoutParentData;
import 'package:flutter/widgets.dart';
enum TooltipType { top, right }
@@ -74,8 +79,10 @@ class _CustomTooltipState extends State<CustomTooltip> {
@protected
@override
void dispose() {
_longPressRecognizer?.onLongPressCancel = null;
_longPressRecognizer?.dispose();
_longPressRecognizer
?..onLongPressCancel = null
..dispose();
_longPressRecognizer = null;
super.dispose();
}
@@ -128,38 +135,31 @@ class _CustomTooltipOverlay extends StatelessWidget {
@override
Widget build(BuildContext context) {
Widget child = CustomMultiChildLayout(
delegate: _CustomMultiTooltipPositionDelegate(
type: type,
target: target,
verticalOffset: verticalOffset,
horizontalOffset: horizontalOffset,
preferBelow: false,
),
return _ToolTip(
type: type,
target: target,
verticalOffset: verticalOffset,
horizontalOffset: horizontalOffset,
preferBelow: false,
onTap: PlatformUtils.isMobile ? onDismiss : null,
children: [
LayoutId(
id: _ChildType.overlay,
child: overlayWidget(),
),
LayoutId(
id: _ChildType.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 {
_CustomMultiTooltipPositionDelegate({
class _ToolTip extends MultiChildRenderObjectWidget {
const _ToolTip({
super.children,
this.onTap,
required this.type,
required this.target,
required this.verticalOffset,
@@ -167,98 +167,188 @@ class _CustomMultiTooltipPositionDelegate extends MultiChildLayoutDelegate {
required this.preferBelow,
});
final VoidCallback? onTap;
final TooltipType type;
final Offset target;
final double verticalOffset;
final double horizontalOffset;
final bool preferBelow;
@override
void performLayout(Size size) {
switch (type) {
case TooltipType.top:
Size? indicatorSize;
if (hasChild(_ChildType.indicator)) {
indicatorSize = layoutChild(
_ChildType.indicator,
BoxConstraints.loose(size),
);
}
RenderObject createRenderObject(BuildContext context) {
return _RenderToolTip(
onTap: onTap,
type: type,
target: target,
verticalOffset: verticalOffset,
horizontalOffset: horizontalOffset,
preferBelow: preferBelow,
);
}
if (hasChild(_ChildType.overlay)) {
final overlaySize = layoutChild(
_ChildType.overlay,
BoxConstraints.loose(size),
);
Offset offset = positionDependentBox(
type: type,
size: size,
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),
);
}
@override
void updateRenderObject(BuildContext context, _RenderToolTip renderObject) {
renderObject
..onTap = onTap
..target = target
..verticalOffset = verticalOffset
..horizontalOffset = horizontalOffset
..preferBelow = preferBelow;
}
}
if (hasChild(_ChildType.overlay)) {
final overlaySize = layoutChild(
_ChildType.overlay,
BoxConstraints.loose(size),
);
Offset offset = positionDependentBox(
type: type,
size: size,
childSize: overlaySize,
target: target,
verticalOffset: verticalOffset,
horizontalOffset: horizontalOffset,
preferBelow: preferBelow,
);
if (indicatorSize != null) {
offset = Offset(offset.dx + indicatorSize.height - 1, offset.dy);
positionChild(
_ChildType.indicator,
Offset(
offset.dx - indicatorSize.width + 1,
target.dy - indicatorSize.height / 2,
),
);
}
positionChild(_ChildType.overlay, offset);
}
class _RenderToolTip extends RenderBox
with
ContainerRenderObjectMixin<RenderBox, MultiChildLayoutParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, MultiChildLayoutParentData> {
_RenderToolTip({
VoidCallback? onTap,
required TooltipType type,
required Offset target,
required double verticalOffset,
required double horizontalOffset,
required bool preferBelow,
}) : _type = type,
_target = target,
_verticalOffset = verticalOffset,
_horizontalOffset = horizontalOffset,
_preferBelow = preferBelow,
_hitTestSelf = onTap != null {
if (onTap != null) {
_tapGestureRecognizer = TapGestureRecognizer()..onTap = onTap;
}
}
TapGestureRecognizer? _tapGestureRecognizer;
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
bool shouldRelayout(_CustomMultiTooltipPositionDelegate oldDelegate) {
return target != oldDelegate.target ||
verticalOffset != oldDelegate.verticalOffset ||
preferBelow != oldDelegate.preferBelow;
void performLayout() {
size = constraints.constrain(constraints.biggest);
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 {
@@ -354,7 +444,7 @@ class RenderTriangle extends RenderBox {
bool get isRepaintBoundary => true;
}
Offset positionDependentBox({
Offset _positionDependentBox({
required TooltipType type,
required Size size,
required Size childSize,