computeListPopupLayoutInfo function

MiuixListPopupLayoutInfo computeListPopupLayoutInfo(
  1. BuildContext context, {
  2. required MiuixPopupAlign alignment,
  3. required MiuixPopupPositionProvider popupPositionProvider,
  4. required Rect parentBounds,
  5. required Size popupContentSize,
})

从窗口安全区、锚点与已测量内容计算 popup host 所需布局信息。

parentBounds 必须是窗口坐标。首次构建可传 Size.zero;此时返回预测 原点,待 MiuixListPopupContent.onPopupContentSizeChange 回报后重新计算。

Implementation

MiuixListPopupLayoutInfo computeListPopupLayoutInfo(
  BuildContext context, {
  required MiuixPopupAlign alignment,
  required MiuixPopupPositionProvider popupPositionProvider,
  required Rect parentBounds,
  required Size popupContentSize,
}) {
  final media = MediaQuery.of(context);
  final direction = Directionality.of(context);
  final containerSize = media.size;
  final viewPadding = media.viewPadding;
  final padding = media.padding;
  final windowBounds = Rect.fromLTRB(
    viewPadding.left,
    padding.top,
    containerSize.width - viewPadding.right,
    containerSize.height - padding.bottom,
  );
  final popupMargin = popupPositionProvider.margins.resolve(direction);
  final resolved = _resolvePopupAlign(alignment, direction);
  final predictedX = switch (resolved) {
    MiuixPopupAlign.end ||
    MiuixPopupAlign.topEnd ||
    MiuixPopupAlign.bottomEnd => parentBounds.right - popupMargin.right,
    _ => parentBounds.left + popupMargin.left,
  };
  final predictedY = switch (resolved) {
    MiuixPopupAlign.bottomStart ||
    MiuixPopupAlign.bottomEnd => parentBounds.top - popupMargin.bottom,
    _ => parentBounds.bottom + popupMargin.bottom,
  };
  final predictedOrigin = safeTransformOrigin(
    predictedX / containerSize.width,
    predictedY / containerSize.height,
  );

  if (popupContentSize == Size.zero) {
    final rightAligned = switch (resolved) {
      MiuixPopupAlign.end ||
      MiuixPopupAlign.topEnd ||
      MiuixPopupAlign.bottomEnd => true,
      _ => false,
    };
    final position = MiuixPopupLayoutPosition(
      showBelow: true,
      showAbove: false,
      isRightAligned: rightAligned,
    );
    return MiuixListPopupLayoutInfo(
      windowBounds: windowBounds,
      popupMargin: popupMargin,
      calculatedOffset: Offset.zero,
      effectiveTransformOrigin: predictedOrigin,
      localTransformOrigin: Offset(rightAligned ? 1 : 0, 0),
      popupLayoutPosition: position,
    );
  }

  final offset = popupPositionProvider.calculatePosition(
    anchorBounds: parentBounds,
    windowBounds: windowBounds,
    textDirection: direction,
    popupContentSize: popupContentSize,
    popupMargin: popupMargin,
    alignment: alignment,
  );
  final popupCenterY = offset.dy + popupContentSize.height / 2;
  final anchorCenterY = parentBounds.top + parentBounds.height / 2;
  final showBelow = popupCenterY > anchorCenterY;
  final showAbove = popupCenterY < anchorCenterY;
  final distanceLeft = (offset.dx - parentBounds.left).abs();
  final distanceRight =
      (offset.dx + popupContentSize.width - parentBounds.right).abs();
  final rightAligned = distanceRight < distanceLeft;
  final position = MiuixPopupLayoutPosition(
    showBelow: showBelow,
    showAbove: showAbove,
    isRightAligned: rightAligned,
  );
  final cornerX = rightAligned ? offset.dx + popupContentSize.width : offset.dx;
  final cornerY = position.showMiddle
      ? offset.dy + popupContentSize.height / 2
      : showBelow
      ? offset.dy
      : offset.dy + popupContentSize.height;
  final localY = position.showMiddle ? 0.5 : (showAbove ? 1.0 : 0.0);
  return MiuixListPopupLayoutInfo(
    windowBounds: windowBounds,
    popupMargin: popupMargin,
    calculatedOffset: offset,
    effectiveTransformOrigin: safeTransformOrigin(
      cornerX / containerSize.width,
      cornerY / containerSize.height,
    ),
    localTransformOrigin: Offset(rightAligned ? 1 : 0, localY),
    popupLayoutPosition: position,
  );
}