chromium/ash/capture_mode/key_item_view.cc

// Copyright 2022 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/capture_mode/key_item_view.h"

#include <memory>

#include "ash/capture_mode/capture_mode_util.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/color/color_id.h"
#include "ui/color/color_provider.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/highlight_border.h"
#include "ui/views/widget/widget.h"

namespace ash {

namespace {

constexpr int kKeyItemMinWidth = 56;
constexpr int kKeyItemHeight = 56;
constexpr int kKeyItemVerticalPadding = 16;
constexpr int kKeyItemHorizontalPadding = 20;
constexpr gfx::Size kIconSize{20, 20};
constexpr char kGoogleSansFont[] = "Google Sans";
constexpr int kKeyItemViewFontSize = 18;
constexpr int kKeyItemViewLineHeight = 24;

SkColor GetColor() {
  return capture_mode_util::GetColorProviderForNativeTheme()->GetColor(
      cros_tokens::kCrosSysSystemBaseElevated);
}

}  // namespace

KeyItemView::KeyItemView(ui::KeyboardCode key_code)
    : key_code_(key_code),
      shadow_(SystemShadow::CreateShadowOnTextureLayer(
          SystemShadow::Type::kElevation4)) {
  SetPaintToLayer();
  SetBackground(
      views::CreateRoundedRectBackground(GetColor(), kKeyItemHeight / 2));
  layer()->SetFillsBoundsOpaquely(false);

  capture_mode_util::SetHighlightBorder(
      this, kKeyItemHeight / 2,
      views::HighlightBorder::Type::kHighlightBorderOnShadow);

  shadow_->SetRoundedCornerRadius(kKeyItemHeight / 2);
}

KeyItemView::~KeyItemView() = default;

void KeyItemView::AddedToWidget() {
  // Since the layer of the shadow has to be added as a sibling to this view's
  // layer, we need to wait until the view is added to the widget.
  auto* parent = layer()->parent();
  parent->Add(shadow_->GetLayer());
  parent->StackAtBottom(shadow_->GetLayer());

  // Make the shadow observe the color provider source change to update the
  // colors.
  shadow_->ObserveColorProviderSource(GetWidget());
}

void KeyItemView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
  // The shadow layer is a sibling of this view's layer, and should have the
  // same bounds. Need to update the location of the shadow when the key items
  // in the combo change position and/or size.
  shadow_->SetContentBounds(layer()->bounds());
}

void KeyItemView::OnThemeChanged() {
  views::View::OnThemeChanged();
  GetBackground()->SetNativeControlColor(GetColor());
  SchedulePaint();
}

void KeyItemView::Layout(PassKey) {
  const auto bounds = GetContentsBounds();
  if (icon_) {
    icon_->SetBoundsRect(bounds);
  }

  if (label_) {
    label_->SetBoundsRect(bounds);
  }
}

gfx::Size KeyItemView::CalculatePreferredSize(
    const views::SizeBounds& available_size) const {
  // Return the fixed size if the key item contains icon or label with a single
  // character.
  if (icon_ || (label_ && label_->GetText().length() == 1)) {
    return gfx::Size(kKeyItemMinWidth, kKeyItemHeight);
  }

  int width = 0;
  for (const views::View* child : children()) {
    const auto child_size = child->GetPreferredSize();
    width += child_size.width();
  }

  width = std::max(width, kKeyItemMinWidth);
  return gfx::Size(width, kKeyItemHeight);
}

void KeyItemView::SetIcon(const gfx::VectorIcon& icon) {
  if (!icon_) {
    icon_ = AddChildView(std::make_unique<views::ImageView>());
    icon_->SetHorizontalAlignment(views::ImageView::Alignment::kCenter);
    icon_->SetVerticalAlignment(views::ImageView::Alignment::kCenter);
  }

  icon_->SetImage(
      ui::ImageModel::FromVectorIcon(icon, cros_tokens::kCrosSysOnSurface));
  icon_->SetImageSize(kIconSize);
}

void KeyItemView::SetText(const std::u16string& text) {
  if (!label_) {
    label_ = AddChildView(std::make_unique<views::Label>());
    label_->SetEnabledColorId(cros_tokens::kCrosSysOnSurface);
    label_->SetElideBehavior(gfx::ElideBehavior::NO_ELIDE);
    label_->SetFontList(gfx::FontList({kGoogleSansFont}, gfx::Font::NORMAL,
                                      kKeyItemViewFontSize,
                                      gfx::Font::Weight::MEDIUM));
    label_->SetLineHeight(kKeyItemViewLineHeight);
  }

  // Set the border only when necessary which is the multi-character case and
  // clear the border for the single-character case in case the border was set
  // with the multi-character settings before.
  label_->SetBorder(
      text.length() == 1
          ? nullptr
          : views::CreateEmptyBorder(gfx::Insets::VH(
                kKeyItemVerticalPadding, kKeyItemHorizontalPadding)));
  label_->SetText(text);
}

BEGIN_METADATA(KeyItemView)
END_METADATA

}  // namespace ash