chromium/ash/style/close_button.cc

// 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 "ash/style/close_button.h"

#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/style/style_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/rect_based_targeting_utils.h"

namespace ash {

namespace {

constexpr int kSmallButtonSize = 16;
constexpr int kMediumButtonSize = 20;
constexpr int kLargeButtonSize = 32;
constexpr int kSmallIconSize = 8;
constexpr int kMediumIconSize = 16;
constexpr int kLargeIconSize = 24;

int GetCloseButtonSize(CloseButton::Type type) {
  switch (type) {
    case CloseButton::Type::kSmall:
    case CloseButton::Type::kSmallFloating:
      return kSmallButtonSize;
    case CloseButton::Type::kMedium:
    case CloseButton::Type::kMediumFloating:
      return kMediumButtonSize;
    case CloseButton::Type::kLarge:
    case CloseButton::Type::kLargeFloating:
      return kLargeButtonSize;
  }
}

int GetIconSize(CloseButton::Type type) {
  switch (type) {
    case CloseButton::Type::kSmall:
    case CloseButton::Type::kSmallFloating:
      return kSmallIconSize;
    case CloseButton::Type::kMedium:
    case CloseButton::Type::kMediumFloating:
      return kMediumIconSize;
    case CloseButton::Type::kLarge:
    case CloseButton::Type::kLargeFloating:
      return kLargeIconSize;
  }
}

bool IsFloatingCloseButton(CloseButton::Type type) {
  return type == CloseButton::Type::kSmallFloating ||
         type == CloseButton::Type::kMediumFloating ||
         type == CloseButton::Type::kLargeFloating;
}

const gfx::VectorIcon* GetCloseIconForType(CloseButton::Type type) {
  return (type == CloseButton::Type::kSmall ||
          type == CloseButton::Type::kSmallFloating)
             ? &kSmallCloseButtonIcon
             : &kMediumOrLargeCloseButtonIcon;
}

}  // namespace

CloseButton::CloseButton(PressedCallback callback,
                         CloseButton::Type type,
                         const gfx::VectorIcon* icon,
                         ui::ColorId background_color_id,
                         ui::ColorId icon_color_id)
    : ImageButton(std::move(callback)), type_(type) {
  SetImageHorizontalAlignment(views::ImageButton::ALIGN_CENTER);
  SetImageVerticalAlignment(views::ImageButton::ALIGN_MIDDLE);
  SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_ACCNAME_CLOSE));
  StyleUtil::SetUpInkDropForButton(this, gfx::Insets(),
                                   /*highlight_on_hover=*/true,
                                   /*highlight_on_focus=*/false,
                                   /*background_color=*/gfx::kPlaceholderColor);

  // Add a rounded rect background. The rounding will be half the button size so
  // it is a circle.
  if (!IsFloatingCloseButton(type_)) {
    SetBackground(views::CreateThemedRoundedRectBackground(
        background_color_id, GetCloseButtonSize(type_) / 2));
  }

  // Use the default close vector icon base on the given `type_` if the client
  // doesn't explicitly provide one.
  const gfx::VectorIcon* vector_icon = icon ? icon : GetCloseIconForType(type_);
  SetImageModel(views::Button::STATE_NORMAL,
                ui::ImageModel::FromVectorIcon(*vector_icon, icon_color_id,
                                               GetIconSize(type_)));

  SetFocusPainter(nullptr);
  SetFocusBehavior(views::View::FocusBehavior::ACCESSIBLE_ONLY);
  SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
  views::InstallCircleHighlightPathGenerator(this);
}

CloseButton::~CloseButton() = default;

bool CloseButton::DoesIntersectScreenRect(const gfx::Rect& screen_rect) const {
  gfx::Point origin = screen_rect.origin();
  View::ConvertPointFromScreen(this, &origin);
  return DoesIntersectRect(this, gfx::Rect(origin, screen_rect.size()));
}

void CloseButton::ResetListener() {
  SetCallback(views::Button::PressedCallback());
}

void CloseButton::OnThemeChanged() {
  views::ImageButton::OnThemeChanged();

  // TODO(minch): Add background blur as per spec. Background blur is quite
  // heavy, and we may have many close buttons showing at a time. They'll be
  // added separately so its easier to monitor performance.
  StyleUtil::ConfigureInkDropAttributes(
      this, StyleUtil::kBaseColor | StyleUtil::kInkDropOpacity);

  SchedulePaint();
}

gfx::Size CloseButton::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  const int size = GetCloseButtonSize(type_);
  return gfx::Size(size, size);
}

bool CloseButton::DoesIntersectRect(const views::View* target,
                                    const gfx::Rect& rect) const {
  DCHECK_EQ(target, this);
  gfx::Rect button_bounds = target->GetLocalBounds();
  const int button_size = GetCloseButtonSize(type_);
  // Only increase the hittest area for touch events (which have a non-empty
  // bounding box), not for mouse event.
  if (!views::UsePointBasedTargeting(rect)) {
    button_bounds.Inset(gfx::Insets::VH(-button_size / 2, -button_size / 2));
  }
  return button_bounds.Intersects(rect);
}

BEGIN_METADATA(CloseButton)
END_METADATA

}  // namespace ash