910e62b5创建于 1月15日历史提交
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/bubble/bubble_border_arrow_utils.h"

namespace views {

gfx::Point GetArrowAnchorPointFromAnchorRect(BubbleBorder::Arrow arrow,
                                             const gfx::Rect& anchor_rect) {
  switch (arrow) {
    case BubbleBorder::TOP_LEFT:
      return anchor_rect.bottom_left();

    case BubbleBorder::TOP_RIGHT:
      return anchor_rect.bottom_right();

    case BubbleBorder::BOTTOM_LEFT:
      return anchor_rect.origin();

    case BubbleBorder::BOTTOM_RIGHT:
      return anchor_rect.top_right();

    case BubbleBorder::LEFT_TOP:
      return anchor_rect.top_right();

    case BubbleBorder::RIGHT_TOP:
      return anchor_rect.origin();

    case BubbleBorder::LEFT_BOTTOM:
      return anchor_rect.bottom_right();

    case BubbleBorder::RIGHT_BOTTOM:
      return anchor_rect.bottom_left();

    case BubbleBorder::TOP_CENTER:
      return anchor_rect.bottom_center();

    case BubbleBorder::BOTTOM_CENTER:
      return anchor_rect.top_center();

    case BubbleBorder::LEFT_CENTER:
      return anchor_rect.right_center();

    case BubbleBorder::RIGHT_CENTER:
      return anchor_rect.left_center();

    default:
      NOTREACHED();
  }
}

gfx::Vector2d GetContentBoundsOffsetToArrowAnchorPoint(
    const gfx::Rect& contents_bounds,
    BubbleBorder::Arrow arrow,
    const gfx::Point& anchor_point) {
  switch (arrow) {
    case BubbleBorder::TOP_LEFT:
      return anchor_point - contents_bounds.origin();

    case BubbleBorder::TOP_RIGHT:
      return anchor_point - contents_bounds.top_right();

    case BubbleBorder::BOTTOM_LEFT:
      return anchor_point - contents_bounds.bottom_left();

    case BubbleBorder::BOTTOM_RIGHT:
      return anchor_point - contents_bounds.bottom_right();

    case BubbleBorder::LEFT_TOP:
      return anchor_point - contents_bounds.origin();

    case BubbleBorder::RIGHT_TOP:
      return anchor_point - contents_bounds.top_right();

    case BubbleBorder::LEFT_BOTTOM:
      return anchor_point - contents_bounds.bottom_left();

    case BubbleBorder::RIGHT_BOTTOM:
      return anchor_point - contents_bounds.bottom_right();

    case BubbleBorder::TOP_CENTER:
      return anchor_point - contents_bounds.top_center();

    case BubbleBorder::BOTTOM_CENTER:
      return anchor_point - contents_bounds.bottom_center();

    case BubbleBorder::LEFT_CENTER:
      return anchor_point - contents_bounds.left_center();

    case BubbleBorder::RIGHT_CENTER:
      return anchor_point - contents_bounds.right_center();

    default:
      NOTREACHED();
  }
}

BubbleArrowSide GetBubbleArrowSide(BubbleBorder::Arrow arrow) {
  // Note: VERTICAL arrows are on the sides of the bubble, while !VERTICAL are
  // on the top or bottom.
  if (int{arrow} & BubbleBorder::VERTICAL) {
    return (int{arrow} & BubbleBorder::RIGHT) ? BubbleArrowSide::kRight
                                              : BubbleArrowSide::kLeft;
  }
  return (int{arrow} & BubbleBorder::BOTTOM) ? BubbleArrowSide::kBottom
                                             : BubbleArrowSide::kTop;
}

gfx::Vector2d GetContentsBoundsOffsetToPlaceVisibleArrow(
    BubbleBorder::Arrow arrow,
    bool include_gap) {
  if (arrow == BubbleBorder::NONE || arrow == BubbleBorder::FLOAT) {
    return gfx::Vector2d();
  }
  const gfx::Insets visible_arrow_insets =
      GetVisibleArrowInsets(arrow, include_gap);
  return gfx::Vector2d(
      visible_arrow_insets.left() - visible_arrow_insets.right(),
      visible_arrow_insets.top() - visible_arrow_insets.bottom());
}

gfx::Insets GetVisibleArrowInsets(BubbleBorder::Arrow arrow, bool include_gap) {
  DCHECK(BubbleBorder::has_arrow(arrow));
  const int arrow_size = include_gap ? BubbleBorder::kVisibleArrowGap +
                                           BubbleBorder::kVisibleArrowLength
                                     : BubbleBorder::kVisibleArrowLength;
  gfx::Insets result;
  switch (GetBubbleArrowSide(arrow)) {
    case BubbleArrowSide::kRight:
      result.set_right(arrow_size);
      break;
    case BubbleArrowSide::kLeft:
      result.set_left(arrow_size);
      break;
    case BubbleArrowSide::kTop:
      result.set_top(arrow_size);
      break;
    case BubbleArrowSide::kBottom:
      result.set_bottom(arrow_size);
      break;
  }
  return result;
}

bool IsVerticalArrow(BubbleBorder::Arrow arrow) {
  const BubbleArrowSide side = GetBubbleArrowSide(arrow);
  return side == BubbleArrowSide::kTop || side == BubbleArrowSide::kBottom;
}

gfx::Size GetVisibleArrowSize(BubbleBorder::Arrow arrow) {
  int kVisibleArrowDiameter = 2 * BubbleBorder::kVisibleArrowRadius;

  return IsVerticalArrow(arrow) ? gfx::Size(kVisibleArrowDiameter,
                                            BubbleBorder::kVisibleArrowLength)
                                : gfx::Size(BubbleBorder::kVisibleArrowLength,
                                            kVisibleArrowDiameter);
}

}  // namespace views