chromium/ash/clipboard/views/clipboard_history_text_item_view.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 "ash/clipboard/views/clipboard_history_text_item_view.h"

#include <string>

#include "ash/bubble/bubble_utils.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "ash/clipboard/views/clipboard_history_label.h"
#include "ash/clipboard/views/clipboard_history_view_constants.h"
#include "ash/style/typography.h"
#include "base/functional/bind.h"
#include "chromeos/constants/chromeos_features.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/box_layout_view.h"
#include "ui/views/view_class_properties.h"

namespace ash {
namespace {

// NOTE: Returns default display text elide behavior if `item` is `nullptr`.
gfx::ElideBehavior GetDisplayTextElideBehavior(
    const ClipboardHistoryItem* item) {
  constexpr auto kDefaultValue = gfx::ELIDE_TAIL;
  return item ? item->display_text_elide_behavior().value_or(kDefaultValue)
              : kDefaultValue;
}

// NOTE: Returns default display text max lines if `item` is `nullptr`.
size_t GetDisplayTextMaxLines(const ClipboardHistoryItem* item) {
  const size_t default_value =
      chromeos::features::IsClipboardHistoryRefreshEnabled()
          ? ClipboardHistoryViews::kTextItemMaxLines
          : 1u;
  return item ? item->display_text_max_lines().value_or(default_value)
              : default_value;
}

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// TextContentsView

class ClipboardHistoryTextItemView::TextContentsView
    : public ClipboardHistoryTextItemView::ContentsView {
  METADATA_HEADER(TextContentsView, ContentsView)

 public:
  explicit TextContentsView(const ClipboardHistoryTextItemView* container) {
    const auto* item = container->GetClipboardHistoryItem();
    const auto display_text_elide_behavior = GetDisplayTextElideBehavior(item);
    const auto display_text_max_lines = GetDisplayTextMaxLines(item);

    auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
        views::BoxLayout::Orientation::kHorizontal));
    layout->set_cross_axis_alignment(
        views::BoxLayout::CrossAxisAlignment::kCenter);

    AddChildView(
        views::Builder<views::BoxLayoutView>()
            .SetOrientation(views::BoxLayout::Orientation::kVertical)
            .SetCrossAxisAlignment(views::BoxLayout::CrossAxisAlignment::kStart)
            .SetProperty(views::kBoxLayoutFlexKey,
                         views::BoxLayoutFlexSpecification())
            .AddChild(views::Builder<views::Label>(
                          std::make_unique<ClipboardHistoryLabel>(
                              container->text_, display_text_elide_behavior,
                              display_text_max_lines))
                          .SetID(clipboard_history_util::kDisplayTextLabelID))
            .AfterBuild(base::BindOnce(
                [](const ClipboardHistoryItem* item,
                   views::BoxLayoutView* labels_container) {
                  if (item && item->secondary_display_text()) {
                    views::Builder<views::View>(labels_container)
                        .AddChild(views::Builder<views::Label>(
                            bubble_utils::CreateLabel(
                                TypographyToken::kCrosAnnotation2,
                                *item->secondary_display_text(),
                                cros_tokens::kCrosSysSecondary)))
                        .SetID(clipboard_history_util::
                                   kSecondaryDisplayTextLabelID)
                        .BuildChildren();
                  }
                },
                item))
            .Build());
  }

  TextContentsView(const TextContentsView& rhs) = delete;
  TextContentsView& operator=(const TextContentsView& rhs) = delete;
  ~TextContentsView() override = default;

 private:
  // ContentsView:
  SkPath GetClipPath() override {
    if (!chromeos::features::IsClipboardHistoryRefreshEnabled() ||
        !is_delete_button_visible()) {
      return SkPath();
    }

    const SkRect contents_bounds = gfx::RectToSkRect(GetContentsBounds());
    const auto width = contents_bounds.width();
    // Ensure that the clip path is tall enough for the full corner cutout to be
    // drawn. No visual problem presents if this ultimately makes the clip path
    // taller than the contents.
    const auto height = std::max(contents_bounds.height(),
                                 ClipboardHistoryViews::kCornerCutoutHeight);

    return SkPath()
        // Start at the top-left corner.
        .moveTo(0.f, 0.f)
        // Draw a vertical line to the bottom-left corner.
        .rLineTo(0.f, height)
        // Draw a horizontal line to the bottom-right corner.
        .rLineTo(width, 0.f)
        // Draw a vertical line to the start of the top-right corner's cutout.
        .lineTo(width, ClipboardHistoryViews::kCornerCutoutHeight)
        // Draw the top-right corner's cutout.
        .rCubicTo(0.f, -8.f, -6.7f, -10.f, -10.f, -10.f)
        .rLineTo(-4.f, 0.f)
        .rCubicTo(-7.7f, 0.f, -14.f, -6.3f, -14.f, -14.f)
        .rLineTo(0.f, -4.f)
        .rCubicTo(0.f, -3.3f, -2.f, -10.f, -10.f, -10.f)
        // Draw a horizontal line back to the starting point.
        .lineTo(0.f, 0.f)
        .close();
  }
};

BEGIN_METADATA(ClipboardHistoryTextItemView, TextContentsView)
END_METADATA

////////////////////////////////////////////////////////////////////////////////
// ClipboardHistoryTextItemView

ClipboardHistoryTextItemView::ClipboardHistoryTextItemView(
    const base::UnguessableToken& item_id,
    const ClipboardHistory* clipboard_history,
    views::MenuItemView* container)
    : ClipboardHistoryItemView(item_id, clipboard_history, container),
      text_(GetClipboardHistoryItem()->display_text()) {
  GetViewAccessibility().SetName(text_);
}

ClipboardHistoryTextItemView::~ClipboardHistoryTextItemView() = default;

std::unique_ptr<ClipboardHistoryTextItemView::ContentsView>
ClipboardHistoryTextItemView::CreateContentsView() {
  return std::make_unique<TextContentsView>(this);
}

BEGIN_METADATA(ClipboardHistoryTextItemView)
END_METADATA

}  // namespace ash