diff --git a/example/lib/main.dart b/example/lib/main.dart index 65965c6..ceb1841 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -58,8 +58,6 @@ class _TargetWidgetState extends State { return true; } - TooltipDirection _tooltipDirection = TooltipDirection.left; - @override Widget build(BuildContext context) { return PopScope( @@ -69,10 +67,7 @@ class _TargetWidgetState extends State { children: [ SuperTooltip( controller: _controller, - popupDirection: TooltipDirection.left, - popupDirectionBuilder: () { - return _tooltipDirection; - }, + popupDirection: TooltipDirection.auto, backgroundColor: Color(0xff2f2d2f), showCloseButton: true, left: 30, @@ -113,29 +108,6 @@ class _TargetWidgetState extends State { ), ), ), - Center( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - IconButton( - onPressed: () { - _tooltipDirection = TooltipDirection.left; - }, - icon: Icon( - Icons.arrow_left, - ), - ), - IconButton( - onPressed: () { - _tooltipDirection = TooltipDirection.right; - }, - icon: Icon( - Icons.arrow_right, - ), - ), - ], - ), - ) ], ), ); diff --git a/lib/src/enums.dart b/lib/src/enums.dart index 2bd9683..7fa7b53 100644 --- a/lib/src/enums.dart +++ b/lib/src/enums.dart @@ -1,4 +1,4 @@ -enum TooltipDirection { up, down, left, right } +enum TooltipDirection { up, down, left, right, auto } enum CloseButtonType { inside, outside } diff --git a/lib/src/super_tooltip.dart b/lib/src/super_tooltip.dart index 514a0fd..5008b30 100644 --- a/lib/src/super_tooltip.dart +++ b/lib/src/super_tooltip.dart @@ -440,6 +440,10 @@ class SuperTooltip extends StatefulWidget { this.mouseCursor, }) : assert(showDropBoxFilter ? showBarrier ?? false : true, 'showDropBoxFilter or showBarrier can\'t be false | null'), + assert( + popupDirectionBuilder == null || + popupDirection != TooltipDirection.auto, + 'popupDirectionBuilder cannot be used with TooltipDirection.auto'), super(key: key); /// Key used to identify the inside close button. @@ -480,6 +484,8 @@ class _SuperTooltipState extends State late Offset shadowOffset; late bool showBlur; + late TooltipDirection _resolvedDirection; + Timer? _showTimer; Timer? _hideTimer; Timer? _showDurationTimer; @@ -667,6 +673,47 @@ class _SuperTooltipState extends State var right = widget.right; var top = widget.top; var bottom = widget.bottom; + _resolvedDirection = preferredDirection; + // When [TooltipDirection.auto] is specified, the tooltip direction is + // dynamically resolved based on available space around the target widget. + if (preferredDirection == TooltipDirection.auto && overlay != null) { + final estimatedTooltipSize = Size( + constraints.maxWidth.isFinite + ? constraints.maxWidth + : overlay.size.width * 0.8, + constraints.maxHeight.isFinite + ? constraints.maxHeight + : overlay.size.height * 0.4, + ); + + final screen = overlay.size; + + final spaceAbove = target.dy - widget.minimumOutsideMargin; + final spaceBelow = + screen.height - target.dy - widget.minimumOutsideMargin; + final spaceLeft = target.dx - widget.minimumOutsideMargin; + final spaceRight = screen.width - target.dx - widget.minimumOutsideMargin; + + if (spaceBelow >= estimatedTooltipSize.height) { + preferredDirection = TooltipDirection.down; + } else if (spaceAbove >= estimatedTooltipSize.height) { + preferredDirection = TooltipDirection.up; + } else if (spaceRight >= estimatedTooltipSize.width) { + preferredDirection = TooltipDirection.right; + } else if (spaceLeft >= estimatedTooltipSize.width) { + preferredDirection = TooltipDirection.left; + } else { + final candidates = { + TooltipDirection.down: spaceBelow, + TooltipDirection.up: spaceAbove, + TooltipDirection.right: spaceRight, + TooltipDirection.left: spaceLeft, + }; + + preferredDirection = + candidates.entries.reduce((a, b) => a.value > b.value ? a : b).key; + } + } if (widget.snapsFarAwayVertically) { constraints = constraints.copyWith(maxHeight: null); @@ -704,6 +751,7 @@ class _SuperTooltipState extends State left = 0.0; } } + _resolvedDirection = preferredDirection; _barrierEntry = showBarrier ? OverlayEntry( @@ -793,7 +841,7 @@ class _SuperTooltipState extends State arrowLength: widget.arrowLength, arrowTipDistance: widget.arrowTipDistance, closeButtonSize: closeButtonSize, - preferredDirection: preferredDirection, + preferredDirection: _resolvedDirection, closeButtonType: closeButtonType, showCloseButton: showCloseButton, ), @@ -959,7 +1007,7 @@ class _SuperTooltipState extends State double right; double top; - switch (widget.popupDirectionBuilder?.call() ?? widget.popupDirection) { + switch (_resolvedDirection) { // // LEFT: ------------------------------------- case TooltipDirection.left: diff --git a/lib/src/super_tooltip_position_delegate.dart b/lib/src/super_tooltip_position_delegate.dart index 21f5c50..4297c0c 100644 --- a/lib/src/super_tooltip_position_delegate.dart +++ b/lib/src/super_tooltip_position_delegate.dart @@ -65,6 +65,18 @@ class SuperToolTipPositionDelegate extends SingleChildLayoutDelegate { right: right, ); break; + case TooltipDirection.auto: + availableConstraints = SuperUtils.verticalConstraints( + constraints: availableConstraints, + margin: margin, + bottom: bottom, + isUp: false, + target: target, + top: top, + left: left, + right: right, + ); + break; } // Now we merge the calculated "Available Space" with the User's "Desired Constraints". diff --git a/lib/src/utils.dart b/lib/src/utils.dart index aae2dee..80a6e6d 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -232,4 +232,38 @@ class SuperUtils { maxWidth: maxWidth, ); } + + /// This method determines the most suitable direction for displaying + /// the tooltip relative to the target widget. + static TooltipDirection resolve({ + required RenderBox overlay, + required Offset target, + required Size estimatedTooltipSize, + required double margin, + }) { + final screen = overlay.size; + + final spaceAbove = target.dy - margin; + final spaceBelow = screen.height - target.dy - margin; + final spaceLeft = target.dx - margin; + final spaceRight = screen.width - target.dx - margin; + + final requiredHeight = estimatedTooltipSize.height; + final requiredWidth = estimatedTooltipSize.width; + + if (spaceBelow >= requiredHeight) return TooltipDirection.down; + if (spaceAbove >= requiredHeight) return TooltipDirection.up; + + if (spaceRight >= requiredWidth) return TooltipDirection.right; + if (spaceLeft >= requiredWidth) return TooltipDirection.left; + + final candidates = { + TooltipDirection.down: spaceBelow, + TooltipDirection.up: spaceAbove, + TooltipDirection.right: spaceRight, + TooltipDirection.left: spaceLeft, + }; + + return candidates.entries.reduce((a, b) => a.value > b.value ? a : b).key; + } } diff --git a/makedoc.bat b/makedoc.bat deleted file mode 100644 index 053587e..0000000 --- a/makedoc.bat +++ /dev/null @@ -1 +0,0 @@ -dartdoc --exclude 'dart:async,dart:collection,dart:convert,dart:core,dart:developer,dart:ffi,dart:html,dart:io,dart:isolate,dart:js,dart:js_util,dart:math,dart:typed_data,dart:ui' \ No newline at end of file