// 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/system/phonehub/task_continuation_view.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/style/typography.h"
#include "ash/system/phonehub/continue_browsing_chip.h"
#include "ash/system/phonehub/phone_hub_view_ids.h"
#include "ash/system/phonehub/ui_constants.h"
#include "ash/system/tray/tray_constants.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/text_constants.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// Appearance constants in dip.
constexpr int kTaskContinuationChipHeight = 96;
constexpr int kTaskContinuationChipsInRow = 2;
constexpr int kTaskContinuationChipSpacing = 8;
constexpr int kTaskContinuationChipHorizontalSidePadding = 4;
constexpr int kTaskContinuationChipVerticalPadding = 4;
constexpr int kHeaderLabelLineHeight = 48;
gfx::Size GetTaskContinuationChipSize() {
int width =
(kTrayMenuWidth - kBubbleHorizontalSidePaddingDip * 2 -
kTaskContinuationChipHorizontalSidePadding * 2 -
kTaskContinuationChipSpacing * (kTaskContinuationChipsInRow - 1)) /
kTaskContinuationChipsInRow;
return gfx::Size(width, kTaskContinuationChipHeight);
}
class HeaderView : public views::Label {
METADATA_HEADER(HeaderView, views::Label)
public:
HeaderView() {
SetText(
l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_TASK_CONTINUATION_TITLE));
SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
SetVerticalAlignment(gfx::VerticalAlignment::ALIGN_MIDDLE);
SetAutoColorReadabilityEnabled(false);
SetSubpixelRenderingEnabled(false);
// TODO(b/322067753): Replace usage of |AshColorProvider| with
// |cros_tokens|.
SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
TypographyProvider::Get()->StyleLabel(ash::TypographyToken::kCrosButton1,
*this);
SetLineHeight(kHeaderLabelLineHeight);
}
~HeaderView() override = default;
HeaderView(HeaderView&) = delete;
HeaderView operator=(HeaderView&) = delete;
};
BEGIN_METADATA(HeaderView)
END_METADATA
} // namespace
TaskContinuationView::TaskContinuationView(
phonehub::PhoneModel* phone_model,
phonehub::UserActionRecorder* user_action_recorder)
: phone_model_(phone_model), user_action_recorder_(user_action_recorder) {
SetID(PhoneHubViewID::kTaskContinuationView);
phone_model_->AddObserver(this);
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
AddChildView(std::make_unique<HeaderView>());
chips_view_ = AddChildView(std::make_unique<TaskChipsView>());
Update();
}
TaskContinuationView::~TaskContinuationView() {
phone_model_->RemoveObserver(this);
}
void TaskContinuationView::OnModelChanged() {
Update();
}
TaskContinuationView::TaskChipsView::TaskChipsView() = default;
TaskContinuationView::TaskChipsView::~TaskChipsView() = default;
void TaskContinuationView::TaskChipsView::AddTaskChip(views::View* task_chip) {
size_t view_size = task_chips_.view_size();
task_chips_.Add(task_chip, view_size);
AddChildView(task_chip);
}
// views::View:
gfx::Size TaskContinuationView::TaskChipsView::CalculatePreferredSize(
const views::SizeBounds& available_size) const {
auto chip_size = GetTaskContinuationChipSize();
int width = chip_size.width() * kTaskContinuationChipsInRow +
kTaskContinuationChipSpacing +
2 * kTaskContinuationChipHorizontalSidePadding;
int rows_num =
std::ceil((double)task_chips_.view_size() / kTaskContinuationChipsInRow);
int height = (chip_size.height() + kTaskContinuationChipVerticalPadding) *
std::max(0, rows_num - 1) +
chip_size.height() +
2 * kTaskContinuationChipHorizontalSidePadding;
return gfx::Size(width, height);
}
void TaskContinuationView::TaskChipsView::Layout(PassKey) {
LayoutSuperclass<views::View>(this);
CalculateIdealBounds();
for (size_t i = 0; i < task_chips_.view_size(); ++i) {
auto* button = task_chips_.view_at(i);
button->SetBoundsRect(task_chips_.ideal_bounds(i));
}
}
void TaskContinuationView::TaskChipsView::Reset() {
task_chips_.Clear();
RemoveAllChildViews();
}
gfx::Point TaskContinuationView::TaskChipsView::GetButtonPosition(int index) {
auto chip_size = GetTaskContinuationChipSize();
int row = index / kTaskContinuationChipsInRow;
int column = index % kTaskContinuationChipsInRow;
int x = (chip_size.width() + kTaskContinuationChipSpacing) * column +
kTaskContinuationChipHorizontalSidePadding;
int y = (chip_size.height() + kTaskContinuationChipVerticalPadding) * row +
kTaskContinuationChipVerticalPadding;
return gfx::Point(x, y);
}
void TaskContinuationView::TaskChipsView::CalculateIdealBounds() {
for (size_t i = 0; i < task_chips_.view_size(); ++i) {
gfx::Rect tile_bounds =
gfx::Rect(GetButtonPosition(i), GetTaskContinuationChipSize());
task_chips_.set_ideal_bounds(i, tile_bounds);
}
}
BEGIN_METADATA(TaskContinuationView, TaskChipsView)
END_METADATA
void TaskContinuationView::Update() {
chips_view_->Reset();
if (!phone_model_->browser_tabs_model()) {
SetVisible(false);
return;
}
const phonehub::BrowserTabsModel& browser_tabs =
phone_model_->browser_tabs_model().value();
if (!browser_tabs.is_tab_sync_enabled() ||
browser_tabs.most_recent_tabs().empty()) {
SetVisible(false);
return;
}
int index = 0;
for (const phonehub::BrowserTabsModel::BrowserTabMetadata& metadata :
browser_tabs.most_recent_tabs()) {
chips_view_->AddTaskChip(new ContinueBrowsingChip(
metadata, index, browser_tabs.most_recent_tabs().size(),
user_action_recorder_));
index++;
}
PreferredSizeChanged();
SetVisible(true);
}
BEGIN_METADATA(TaskContinuationView)
END_METADATA
} // namespace ash