blob: fb2084ce6408b5b0f0d44019040f89238c7d9620 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show clampDouble;
import 'basic_types.dart';
/// Position a child box within a container box, either above or below a target
/// point.
///
/// The container's size is described by `size`.
///
/// The target point is specified by `target`, as an offset from the top left of
/// the container.
///
/// The child box's size is given by `childSize`.
///
/// The return value is the suggested distance from the top left of the
/// container box to the top left of the child box.
///
/// The suggested position will be above the target point if `preferBelow` is
/// false, and below the target point if it is true, unless it wouldn't fit on
/// the preferred side but would fit on the other side.
///
/// The suggested position will place the nearest side of the child to the
/// target point `verticalOffset` from the target point (even if it cannot fit
/// given that constraint).
///
/// The suggested position will be at least `margin` away from the edge of the
/// container. If possible, the child will be positioned so that its center is
/// aligned with the target point. If the child cannot fit horizontally within
/// the container given the margin, then the child will be centered in the
/// container.
///
/// Used by [Tooltip] to position a tooltip relative to its parent.
///
/// The arguments must not be null.
Offset positionDependentBox({
required Size size,
required Size childSize,
required Offset target,
required bool preferBelow,
double verticalOffset = 0.0,
double margin = 10.0,
}) {
assert(size != null);
assert(childSize != null);
assert(target != null);
assert(verticalOffset != null);
assert(preferBelow != null);
assert(margin != null);
// VERTICAL DIRECTION
final bool fitsBelow = target.dy + verticalOffset + childSize.height <= size.height - margin;
final bool fitsAbove = target.dy - verticalOffset - childSize.height >= margin;
final bool tooltipBelow = preferBelow ? fitsBelow || !fitsAbove : !(fitsAbove || !fitsBelow);
double y;
if (tooltipBelow) {
y = math.min(target.dy + verticalOffset, size.height - margin);
} else {
y = math.max(target.dy - verticalOffset - childSize.height, margin);
}
// HORIZONTAL DIRECTION
double x;
if (size.width - margin * 2.0 < childSize.width) {
x = (size.width - childSize.width) / 2.0;
} else {
final double normalizedTargetX = clampDouble(target.dx, margin, size.width - margin);
final double edge = margin + childSize.width / 2.0;
if (normalizedTargetX < edge) {
x = margin;
} else if (normalizedTargetX > size.width - edge) {
x = size.width - margin - childSize.width;
} else {
x = normalizedTargetX - childSize.width / 2.0;
}
}
return Offset(x, y);
}