blob: 739c91c69687203ab00008703785f6bdd95034e1 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/assistant/ui/main_stage/suggestion_chip_view.h"
#include <algorithm>
#include <memory>
#include <utility>
#include "ash/assistant/ui/assistant_ui_constants.h"
#include "ash/assistant/ui/assistant_view_delegate.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/views/background.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// Appearance.
constexpr SkColor kBackgroundColor = SK_ColorWHITE;
constexpr SkColor kFocusColor = SkColorSetA(gfx::kGoogleGrey900, 0x14);
constexpr SkColor kStrokeColor = SkColorSetA(gfx::kGoogleGrey900, 0x24);
constexpr SkColor kTextColor = gfx::kGoogleGrey700;
constexpr int kStrokeWidthDip = 1;
constexpr int kIconMarginDip = 8;
constexpr int kIconSizeDip = 16;
constexpr int kChipPaddingDip = 16;
constexpr int kPreferredHeightDip = 32;
} // namespace
// SuggestionChipView ----------------------------------------------------------
// static
constexpr char SuggestionChipView::kClassName[];
SuggestionChipView::SuggestionChipView(AssistantViewDelegate* delegate,
const AssistantSuggestion& suggestion,
views::ButtonListener* listener)
: Button(listener), delegate_(delegate), suggestion_id_(suggestion.id) {
InitLayout(suggestion);
}
SuggestionChipView::~SuggestionChipView() = default;
const char* SuggestionChipView::GetClassName() const {
return kClassName;
}
gfx::Size SuggestionChipView::CalculatePreferredSize() const {
const int preferred_width = views::View::CalculatePreferredSize().width();
return gfx::Size(preferred_width, GetHeightForWidth(preferred_width));
}
int SuggestionChipView::GetHeightForWidth(int width) const {
return kPreferredHeightDip;
}
void SuggestionChipView::ChildVisibilityChanged(views::View* child) {
// When icon visibility is modified we need to update layout padding.
if (child == icon_view_) {
const int padding_left_dip =
icon_view_->GetVisible() ? kIconMarginDip : kChipPaddingDip;
layout_manager_->set_inside_border_insets(
gfx::Insets(0, padding_left_dip, 0, kChipPaddingDip));
}
PreferredSizeChanged();
}
void SuggestionChipView::InitLayout(const AssistantSuggestion& suggestion) {
const base::string16 text = base::UTF8ToUTF16(suggestion.text);
// Accessibility.
SetAccessibleName(text);
// Focus.
// Note that we don't install the default focus ring as we use custom
// highlighting instead.
SetFocusBehavior(FocusBehavior::ALWAYS);
SetInstallFocusRingOnFocus(false);
// Layout.
// Note that padding differs depending on icon visibility.
const int padding_left_dip =
suggestion.icon_url.is_empty() ? kChipPaddingDip : kIconMarginDip;
layout_manager_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
gfx::Insets(0, padding_left_dip, 0, kChipPaddingDip), kIconMarginDip));
layout_manager_->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
// Icon.
icon_view_ = AddChildView(std::make_unique<views::ImageView>());
icon_view_->SetImageSize(gfx::Size(kIconSizeDip, kIconSizeDip));
icon_view_->SetPreferredSize(gfx::Size(kIconSizeDip, kIconSizeDip));
// Download our icon if necessary. Note that we *don't* hide the associated
// view while an image is being downloaded. This prevents layout jank that
// would otherwise occur when the image is finally rendered.
if (suggestion.icon_url.is_empty()) {
icon_view_->SetVisible(false);
} else {
delegate_->DownloadImage(suggestion.icon_url,
base::BindOnce(&SuggestionChipView::SetIcon,
weak_factory_.GetWeakPtr()));
}
// Text.
text_view_ = AddChildView(std::make_unique<views::Label>());
text_view_->SetAutoColorReadabilityEnabled(false);
text_view_->SetEnabledColor(kTextColor);
text_view_->SetSubpixelRenderingEnabled(false);
text_view_->SetFontList(
assistant::ui::GetDefaultFontList().DeriveWithSizeDelta(1));
SetText(text);
}
void SuggestionChipView::OnPaintBackground(gfx::Canvas* canvas) {
cc::PaintFlags flags;
flags.setAntiAlias(true);
gfx::Rect bounds = GetContentsBounds();
// Background.
flags.setColor(kBackgroundColor);
canvas->DrawRoundRect(bounds, height() / 2, flags);
if (HasFocus()) {
flags.setColor(kFocusColor);
canvas->DrawRoundRect(bounds, height() / 2, flags);
}
// Border.
// Stroke should be drawn within our contents bounds.
bounds.Inset(gfx::Insets(kStrokeWidthDip));
// Stroke.
flags.setColor(kStrokeColor);
flags.setStrokeWidth(kStrokeWidthDip);
flags.setStyle(cc::PaintFlags::Style::kStroke_Style);
canvas->DrawRoundRect(bounds, height() / 2, flags);
}
void SuggestionChipView::OnFocus() {
SchedulePaint();
NotifyAccessibilityEvent(ax::mojom::Event::kSelection, true);
}
void SuggestionChipView::OnBlur() {
SchedulePaint();
}
bool SuggestionChipView::OnKeyPressed(const ui::KeyEvent& event) {
if (event.key_code() == ui::VKEY_SPACE)
return false;
return Button::OnKeyPressed(event);
}
void SuggestionChipView::SetIcon(const gfx::ImageSkia& icon) {
icon_view_->SetImage(icon);
icon_view_->SetVisible(!icon.isNull());
}
void SuggestionChipView::SetText(const base::string16& text) {
text_view_->SetText(text);
}
const base::string16& SuggestionChipView::GetText() const {
return text_view_->GetText();
}
} // namespace ash