chromium/chrome/browser/ui/ash/sharesheet/sharesheet_target_button.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/ash/sharesheet/sharesheet_target_button.h"

#include <memory>
#include <string>

#include "ash/bubble/bubble_utils.h"
#include "ash/public/cpp/ash_typography.h"
#include "ash/style/typography.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/ash/sharesheet/sharesheet_constants.h"
#include "chrome/browser/ui/ash/sharesheet/sharesheet_util.h"
#include "chrome/browser/ui/color/chrome_color_id.h"
#include "chromeos/constants/chromeos_features.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/image_model.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/layout/box_layout.h"

namespace {

// Sizes are in px.
// kButtonTextMaxWidth is button max width without padding.
constexpr int kButtonTextMaxWidth = 76;
constexpr int kButtonMaxLines = 2;
constexpr int kButtonPadding = 8;

}  // namespace

namespace ash {
namespace sharesheet {

// A button that represents a candidate share target.
// Only apps will have |icon| values, while share_actions will have a
// |vector_icon| which is used to generate a |ColorTrackingIconView|. If
// |icon| has a value |vector_icon| should be nullptr and vice versa. There
// should never be a case where both don't have values or both have values.
// It is safe to use |vector_icon| as a raw pointer because it has the same
// lifetime as the |SharesheetService|, which outlives|SharesheetTargetButton|
// as it is a transient UI invoked from the |SharesheetService|.
SharesheetTargetButton::SharesheetTargetButton(
    PressedCallback callback,
    const std::u16string& display_name,
    const std::u16string& secondary_display_name,
    const std::optional<gfx::ImageSkia> icon,
    const gfx::VectorIcon* vector_icon,
    bool is_dlp_blocked)
    : Button(std::move(callback)), vector_icon_(vector_icon) {
  SetFocusBehavior(View::FocusBehavior::ALWAYS);
  // TODO(crbug.com/40136695) Margins shouldn't be within
  // SharesheetTargetButton as the margins are different in |expanded_view_|.
  auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, gfx::Insets(kButtonPadding),
      kButtonPadding, true));
  layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kStart);
  layout->set_cross_axis_alignment(
      views::BoxLayout::CrossAxisAlignment::kCenter);

  image_ = AddChildView(std::make_unique<views::ImageView>());
  if (icon.has_value()) {
    image_->SetImage(icon.value());
    vector_icon_ = nullptr;
  }
  image_->SetCanProcessEventsWithinSubtree(false);

  auto label_view = std::make_unique<views::View>();
  label_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0, true));

  auto* label =
      label_view->AddChildView(std::make_unique<views::Label>(display_name));
  label->SetID(SharesheetViewID::TARGET_LABEL_VIEW_ID);
  label->SetEnabledColorId(cros_tokens::kTextColorSecondary);

  bubble_utils::ApplyStyle(label, TypographyToken::kCrosButton1);
  SetLabelProperties(label);

  std::u16string accessible_name = display_name;
  if (!secondary_display_name.empty() &&
      secondary_display_name != display_name) {
    auto* secondary_label = label_view->AddChildView(
        std::make_unique<views::Label>(secondary_display_name));
    secondary_label->SetEnabledColorId(cros_tokens::kTextColorSecondary);

    bubble_utils::ApplyStyle(secondary_label, TypographyToken::kCrosBody2);
    SetLabelProperties(secondary_label);
    accessible_name =
        base::StrCat({display_name, u" ", secondary_display_name});
    // As there is a secondary label, don't let the initial label stretch across
    // multiple lines.
    label->SetMultiLine(false);
    secondary_label->SetMultiLine(false);
  } else {
    label->SetMaxLines(kButtonMaxLines);
  }

  AddChildView(std::move(label_view));
  GetViewAccessibility().SetName(accessible_name);

  if (is_dlp_blocked) {
    SetState(ButtonState::STATE_DISABLED);
  }
}

void SharesheetTargetButton::OnThemeChanged() {
  views::Button::OnThemeChanged();

  if (!vector_icon_)
    return;

  // TODO(b/284175205): Convert this to an ImageModel after Jelly launches.
  auto* color_provider = GetColorProvider();
  SkColor icon_color = color_provider->GetColor(cros_tokens::kCrosSysPrimary);
  SkColor circle_color =
      color_provider->GetColor(cros_tokens::kCrosSysPrimaryContainer);

  gfx::ImageSkia icon = gfx::CreateVectorIcon(
      *vector_icon_, ::sharesheet::kIconSize / 2, icon_color);
  gfx::ImageSkia circle_icon =
      gfx::ImageSkiaOperations::CreateImageWithCircleBackground(
          ::sharesheet::kIconSize / 2, circle_color, icon);

  gfx::ShadowValues shadow_values;
  const SkColor shadow_color =
      GetColorProvider()->GetColor(kColorSharesheetTargetButtonIconShadow);
  shadow_values.push_back(
      gfx::ShadowValue(gfx::Vector2d(0, 1), 0, shadow_color));
  shadow_values.push_back(
      gfx::ShadowValue(gfx::Vector2d(0, 1), 2, shadow_color));
  gfx::ImageSkia circle_icon_with_shadow =
      gfx::ImageSkiaOperations::CreateImageWithDropShadow(circle_icon,
                                                          shadow_values);
  image_->SetImage(circle_icon_with_shadow);
}

void SharesheetTargetButton::SetLabelProperties(views::Label* label) {
  label->SetMultiLine(true);
  label->SetMaximumWidth(kButtonTextMaxWidth);
  label->SetHandlesTooltips(true);
  label->SetTooltipText(label->GetText());
  label->SetAutoColorReadabilityEnabled(false);
  label->SetHorizontalAlignment(gfx::ALIGN_CENTER);
}

BEGIN_METADATA(SharesheetTargetButton)
END_METADATA

}  // namespace sharesheet
}  // namespace ash