Commit d8e28f5c authored by David Black's avatar David Black Committed by Chromium LUCI CQ

Refactor screen captures and downloads sections.

Previously the recent files container managed both screen captures and
downloads type holding space item views. Now, separate view classes
are added to manage each respective section. As a result, these
sections will now animate independently of one another.

There is opportunity for a lot of additional refactoring that will be
done in a follow up CL. The existing pinned files section and new
screen captures and downloads sections can inherit from a common view
class. The existing pinned files container and recent files container
can also inherit from a common view class.

Bug: 1154998
Change-Id: I27af3c70859a8cd3da91b3ea7b4668910ebe7ca8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2577938
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarToni Baržić <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#834490}
parent 3524fc4c
......@@ -978,6 +978,8 @@ component("ash") {
"system/enterprise/enterprise_domain_observer.h",
"system/gesture_education/gesture_education_notification_controller.cc",
"system/gesture_education/gesture_education_notification_controller.h",
"system/holding_space/downloads_section.cc",
"system/holding_space/downloads_section.h",
"system/holding_space/holding_space_color_provider_impl.cc",
"system/holding_space/holding_space_color_provider_impl.h",
"system/holding_space/holding_space_drag_util.cc",
......@@ -1006,6 +1008,8 @@ component("ash") {
"system/holding_space/pinned_files_container.h",
"system/holding_space/recent_files_container.cc",
"system/holding_space/recent_files_container.h",
"system/holding_space/screen_captures_section.cc",
"system/holding_space/screen_captures_section.h",
"system/ime/ime_feature_pod_controller.cc",
"system/ime/ime_feature_pod_controller.h",
"system/ime/ime_observer.h",
......
// Copyright 2020 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/system/holding_space/downloads_section.h"
#include <memory>
#include "ash/public/cpp/holding_space/holding_space_client.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_controller.h"
#include "ash/public/cpp/holding_space/holding_space_item.h"
#include "ash/public/cpp/holding_space/holding_space_metrics.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/holding_space/holding_space_item_chip_view.h"
#include "ash/system/holding_space/holding_space_item_chips_container.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "base/bind.h"
#include "base/containers/adapters.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
namespace {
// Helpers ---------------------------------------------------------------------
// Returns if an item of the specified `type` belongs in this section.
bool BelongsToSection(HoldingSpaceItem::Type type) {
return type == HoldingSpaceItem::Type::kDownload ||
type == HoldingSpaceItem::Type::kNearbyShare;
}
// Header-----------------------------------------------------------------------
class Header : public views::Button {
public:
Header() {
SetAccessibleName(
l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_DOWNLOADS_TITLE));
SetCallback(
base::BindRepeating(&Header::OnPressed, base::Unretained(this)));
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
kHoldingSpaceDownloadsHeaderSpacing));
// Label.
auto* label = AddChildView(std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_DOWNLOADS_TITLE)));
label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
label->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
layout->SetFlexForView(label, 1);
TrayPopupUtils::SetLabelFontList(label,
TrayPopupUtils::FontStyle::kSubHeader);
// Chevron.
auto* chevron = AddChildView(std::make_unique<views::ImageView>());
chevron->SetFlipCanvasOnPaintForRTLUI(true);
chevron->SetImage(gfx::CreateVectorIcon(
kChevronRightIcon, kHoldingSpaceDownloadsChevronIconSize,
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary)));
}
private:
void OnPressed() {
holding_space_metrics::RecordDownloadsAction(
holding_space_metrics::DownloadsAction::kClick);
HoldingSpaceController::Get()->client()->OpenDownloads(base::DoNothing());
}
};
} // namespace
// DownloadsSection ------------------------------------------------------------
DownloadsSection::DownloadsSection(HoldingSpaceItemViewDelegate* delegate)
: HoldingSpaceItemViewsContainer(delegate) {
SetVisible(false);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
kHoldingSpaceContainerChildSpacing));
// Header.
header_ = AddChildView(std::make_unique<Header>());
header_->SetPaintToLayer();
header_->layer()->SetFillsBoundsOpaquely(false);
header_->SetVisible(false);
// Container.
container_ = AddChildView(std::make_unique<HoldingSpaceItemChipsContainer>());
container_->SetVisible(false);
}
DownloadsSection::~DownloadsSection() = default;
void DownloadsSection::ChildVisibilityChanged(views::View* child) {
// This section should be visible iff it has visible children.
bool visible = false;
for (const views::View* c : children()) {
if (c->GetVisible()) {
visible = true;
break;
}
}
if (visible != GetVisible())
SetVisible(visible);
HoldingSpaceItemViewsContainer::ChildVisibilityChanged(child);
}
void DownloadsSection::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
if (details.parent != container_)
return;
// Update visibility when becoming empty or non-empty. Note that in the case
// of a view being added, `ViewHierarchyChanged()` is called *after* the view
// has been parented but in the case of a view being removed, it is called
// *before* the view is unparented.
if (container_->children().size() == 1u) {
header_->SetVisible(details.is_add);
container_->SetVisible(details.is_add);
}
}
bool DownloadsSection::ContainsHoldingSpaceItemView(
const HoldingSpaceItem* item) {
return base::Contains(views_by_item_id_, item->id());
}
bool DownloadsSection::ContainsHoldingSpaceItemViews() {
return !views_by_item_id_.empty();
}
bool DownloadsSection::WillAddHoldingSpaceItemView(
const HoldingSpaceItem* item) {
return BelongsToSection(item->type());
}
void DownloadsSection::AddHoldingSpaceItemView(const HoldingSpaceItem* item) {
DCHECK(item->IsFinalized());
DCHECK(BelongsToSection(item->type()));
DCHECK(!base::Contains(views_by_item_id_, item->id()));
// Remove the last download view if we are already at max capacity.
if (container_->children().size() == kMaxDownloads) {
std::unique_ptr<views::View> view =
container_->RemoveChildViewT(container_->children().back());
views_by_item_id_.erase(
HoldingSpaceItemView::Cast(view.get())->item()->id());
}
// Add the download view to the front in order to sort by recency.
views_by_item_id_[item->id()] = container_->AddChildViewAt(
std::make_unique<HoldingSpaceItemChipView>(delegate(), item), 0);
}
void DownloadsSection::RemoveAllHoldingSpaceItemViews() {
views_by_item_id_.clear();
container_->RemoveAllChildViews(true);
}
// TODO(dmblack): Handle grow/shrink of container.
void DownloadsSection::AnimateIn(ui::LayerAnimationObserver* observer) {
for (auto& view_by_item_id : views_by_item_id_)
view_by_item_id.second->AnimateIn(observer);
}
// TODO(dmblack): Handle animate out of `header_` if this section is going away
// permanently.
void DownloadsSection::AnimateOut(ui::LayerAnimationObserver* observer) {
for (auto& view_by_item_id : views_by_item_id_)
view_by_item_id.second->AnimateOut(observer);
}
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_SYSTEM_HOLDING_SPACE_DOWNLOADS_SECTION_H_
#define ASH_SYSTEM_HOLDING_SPACE_DOWNLOADS_SECTION_H_
#include <map>
#include "ash/system/holding_space/holding_space_item_views_container.h"
namespace ash {
class HoldingSpaceItemChipsContainer;
class HoldingSpaceItemView;
// Section for downloads in the `RecentFilesContainer`.
class DownloadsSection : public HoldingSpaceItemViewsContainer {
public:
explicit DownloadsSection(HoldingSpaceItemViewDelegate* delegate);
DownloadsSection(const DownloadsSection& other) = delete;
DownloadsSection& operator=(const DownloadsSection& other) = delete;
~DownloadsSection() override;
// HoldingSpaceItemViewsContainer:
void ChildVisibilityChanged(views::View* child) override;
void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;
bool ContainsHoldingSpaceItemView(const HoldingSpaceItem* item) override;
bool ContainsHoldingSpaceItemViews() override;
bool WillAddHoldingSpaceItemView(const HoldingSpaceItem* item) override;
void AddHoldingSpaceItemView(const HoldingSpaceItem* item) override;
void RemoveAllHoldingSpaceItemViews() override;
void AnimateIn(ui::LayerAnimationObserver* observer) override;
void AnimateOut(ui::LayerAnimationObserver* observer) override;
private:
// Owned by view hierarchy.
HoldingSpaceItemChipsContainer* container_ = nullptr;
views::View* header_ = nullptr;
std::map<std::string, HoldingSpaceItemView*> views_by_item_id_;
};
} // namespace ash
#endif // ASH_SYSTEM_HOLDING_SPACE_DOWNLOADS_SECTION_H_
......@@ -128,18 +128,13 @@ HoldingSpaceTrayBubble::HoldingSpaceTrayBubble(
std::make_unique<PinnedFilesContainer>(&delegate_));
bubble_container->SetFlexForChild(pinned_files_container_, 1);
SetupViewLayer(pinned_files_container_);
pinned_files_container_->Init();
// Add recent files container.
recent_files_container_ = bubble_container->AddChildView(
std::make_unique<RecentFilesContainer>(&delegate_));
SetupViewLayer(recent_files_container_);
// Populate both containers if holding space model has already been attached.
HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
if (model) {
pinned_files_container_->OnHoldingSpaceModelAttached(model);
recent_files_container_->OnHoldingSpaceModelAttached(model);
}
recent_files_container_->Init();
// Show the bubble.
bubble_wrapper_ = std::make_unique<TrayBubbleWrapper>(
......
......@@ -133,6 +133,12 @@ PinnedFilesContainer::PinnedFilesContainer(
PinnedFilesContainer::~PinnedFilesContainer() = default;
void PinnedFilesContainer::Init() {
HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
if (model)
OnHoldingSpaceModelAttached(model);
}
void PinnedFilesContainer::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
// We only care about `item_chips_container_` becoming empty and non-empty.
......
......@@ -26,6 +26,9 @@ class PinnedFilesContainer : public HoldingSpaceItemViewsContainer {
PinnedFilesContainer& operator=(const PinnedFilesContainer& other) = delete;
~PinnedFilesContainer() override;
// Initializes the container.
void Init();
// HoldingSpaceItemViewsContainer:
void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;
bool ContainsHoldingSpaceItemView(const HoldingSpaceItem* item) override;
......
......@@ -5,53 +5,38 @@
#ifndef ASH_SYSTEM_HOLDING_SPACE_RECENT_FILES_CONTAINER_H_
#define ASH_SYSTEM_HOLDING_SPACE_RECENT_FILES_CONTAINER_H_
#include <map>
#include <vector>
#include "ash/system/holding_space/holding_space_item_views_container.h"
namespace views {
class Label;
} // namespace views
#include "ui/views/view.h"
namespace ash {
class HoldingSpaceItemChipsContainer;
class HoldingSpaceItemView;
class HoldingSpaceItemViewDelegate;
class HoldingSpaceItemViewsContainer;
// Container for the recent files (e.g. screen captures, downloads, etc).
class RecentFilesContainer : public HoldingSpaceItemViewsContainer {
// Container for the recent files downloads and screen captures sections.
class RecentFilesContainer : public views::View {
public:
explicit RecentFilesContainer(HoldingSpaceItemViewDelegate* delegate);
RecentFilesContainer(const RecentFilesContainer& other) = delete;
RecentFilesContainer& operator=(const RecentFilesContainer& other) = delete;
~RecentFilesContainer() override;
// Initializes the container.
void Init();
// Resets the container. Called when the tray bubble starts closing to
// stop observing the holding space controller/model to ensure that no new
// items are created while the bubble widget is being asynchronously closed.
void Reset();
private:
// HoldingSpaceItemViewsContainer:
void ChildPreferredSizeChanged(views::View* child) override;
void ChildVisibilityChanged(views::View* child) override;
void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;
bool ContainsHoldingSpaceItemView(const HoldingSpaceItem* item) override;
bool ContainsHoldingSpaceItemViews() override;
bool WillAddHoldingSpaceItemView(const HoldingSpaceItem* item) override;
void AddHoldingSpaceItemView(const HoldingSpaceItem* item) override;
void RemoveAllHoldingSpaceItemViews() override;
void AnimateIn(ui::LayerAnimationObserver* observer) override;
void AnimateOut(ui::LayerAnimationObserver* observer) override;
private:
void AddHoldingSpaceScreenCaptureView(const HoldingSpaceItem* item);
void AddHoldingSpaceDownloadView(const HoldingSpaceItem* item);
void OnScreenCapturesContainerViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details);
void OnDownloadsContainerViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details);
// Owned by view hierarchy.
views::View* screen_captures_container_ = nullptr;
views::Label* screen_captures_label_ = nullptr;
HoldingSpaceItemChipsContainer* downloads_container_ = nullptr;
views::View* downloads_header_ = nullptr;
std::map<std::string, HoldingSpaceItemView*> views_by_item_id_;
// Views owned by view hierarchy.
std::vector<HoldingSpaceItemViewsContainer*> sections_;
};
} // namespace ash
......
// Copyright 2020 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/system/holding_space/screen_captures_section.h"
#include <memory>
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_item.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/holding_space/holding_space_item_screen_capture_view.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "base/bind.h"
#include "base/containers/adapters.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.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/flex_layout.h"
#include "ui/views/view_class_properties.h"
namespace ash {
namespace {
// Helpers ---------------------------------------------------------------------
// Returns if an item of the specified `type` belongs in this section.
bool BelongsToSection(HoldingSpaceItem::Type type) {
return type == HoldingSpaceItem::Type::kScreenshot ||
type == HoldingSpaceItem::Type::kScreenRecording;
}
} // namespace
// ScreenCapturesSection -------------------------------------------------------
ScreenCapturesSection::ScreenCapturesSection(
HoldingSpaceItemViewDelegate* delegate)
: HoldingSpaceItemViewsContainer(delegate) {
SetVisible(false);
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(),
kHoldingSpaceContainerChildSpacing));
// Label.
label_ = AddChildView(std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_ASH_HOLDING_SPACE_SCREEN_CAPTURES_TITLE)));
label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
label_->SetPaintToLayer();
label_->layer()->SetFillsBoundsOpaquely(false);
label_->SetVisible(false);
TrayPopupUtils::SetLabelFontList(label_,
TrayPopupUtils::FontStyle::kSubHeader);
// Container.
container_ = AddChildView(std::make_unique<views::View>());
container_->SetVisible(false);
container_->SetLayoutManager(std::make_unique<views::FlexLayout>())
->SetOrientation(views::LayoutOrientation::kHorizontal)
.SetDefault(views::kMarginsKey,
gfx::Insets(/*top=*/0, /*left=*/0, /*bottom=*/0,
/*right=*/kHoldingSpaceScreenCaptureSpacing));
}
ScreenCapturesSection::~ScreenCapturesSection() = default;
void ScreenCapturesSection::ChildVisibilityChanged(views::View* child) {
// This section should be visible iff it has visible children.
bool visible = false;
for (const views::View* c : children()) {
if (c->GetVisible()) {
visible = true;
break;
}
}
if (visible != GetVisible())
SetVisible(visible);
HoldingSpaceItemViewsContainer::ChildVisibilityChanged(child);
}
void ScreenCapturesSection::ViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details) {
if (details.parent != container_)
return;
// Update visibility when becoming empty or non-empty. Note that in the case
// of a view being added, `ViewHierarchyChanged()` is called *after* the view
// has been parented but in the case of a view being removed, it is called
// *before* the view is unparented.
if (container_->children().size() == 1u) {
label_->SetVisible(details.is_add);
container_->SetVisible(details.is_add);
}
}
bool ScreenCapturesSection::ContainsHoldingSpaceItemView(
const HoldingSpaceItem* item) {
return base::Contains(views_by_item_id_, item->id());
}
bool ScreenCapturesSection::ContainsHoldingSpaceItemViews() {
return !views_by_item_id_.empty();
}
bool ScreenCapturesSection::WillAddHoldingSpaceItemView(
const HoldingSpaceItem* item) {
return BelongsToSection(item->type());
}
void ScreenCapturesSection::AddHoldingSpaceItemView(
const HoldingSpaceItem* item) {
DCHECK(item->IsFinalized());
DCHECK(BelongsToSection(item->type()));
DCHECK(!base::Contains(views_by_item_id_, item->id()));
// Remove the last screen capture view if we are already at max capacity.
if (container_->children().size() == kMaxScreenCaptures) {
std::unique_ptr<views::View> view =
container_->RemoveChildViewT(container_->children().back());
views_by_item_id_.erase(
HoldingSpaceItemView::Cast(view.get())->item()->id());
}
// Add the screen capture view to the front in order to sort by recency.
views_by_item_id_[item->id()] = container_->AddChildViewAt(
std::make_unique<HoldingSpaceItemScreenCaptureView>(delegate(), item), 0);
}
void ScreenCapturesSection::RemoveAllHoldingSpaceItemViews() {
views_by_item_id_.clear();
container_->RemoveAllChildViews(true);
}
// TODO(dmblack): Handle grow/shrink of container.
void ScreenCapturesSection::AnimateIn(ui::LayerAnimationObserver* observer) {
for (auto& view_by_item_id : views_by_item_id_)
view_by_item_id.second->AnimateIn(observer);
}
// TODO(dmblack): Handle animate out of `label_` if this section is going away
// permanently.
void ScreenCapturesSection::AnimateOut(ui::LayerAnimationObserver* observer) {
for (auto& view_by_item_id : views_by_item_id_)
view_by_item_id.second->AnimateOut(observer);
}
} // namespace ash
// Copyright 2020 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.
#ifndef ASH_SYSTEM_HOLDING_SPACE_SCREEN_CAPTURES_SECTION_H_
#define ASH_SYSTEM_HOLDING_SPACE_SCREEN_CAPTURES_SECTION_H_
#include <map>
#include "ash/system/holding_space/holding_space_item_views_container.h"
namespace views {
class Label;
} // namespace views
namespace ash {
class HoldingSpaceItemView;
// Section for screen captures in the `RecentFilesContainer`.
class ScreenCapturesSection : public HoldingSpaceItemViewsContainer {
public:
explicit ScreenCapturesSection(HoldingSpaceItemViewDelegate* delegate);
ScreenCapturesSection(const ScreenCapturesSection& other) = delete;
ScreenCapturesSection& operator=(const ScreenCapturesSection& other) = delete;
~ScreenCapturesSection() override;
// HoldingSpaceItemViewsContainer:
void ChildVisibilityChanged(views::View* child) override;
void ViewHierarchyChanged(const views::ViewHierarchyChangedDetails&) override;
bool ContainsHoldingSpaceItemView(const HoldingSpaceItem* item) override;
bool ContainsHoldingSpaceItemViews() override;
bool WillAddHoldingSpaceItemView(const HoldingSpaceItem* item) override;
void AddHoldingSpaceItemView(const HoldingSpaceItem* item) override;
void RemoveAllHoldingSpaceItemViews() override;
void AnimateIn(ui::LayerAnimationObserver* observer) override;
void AnimateOut(ui::LayerAnimationObserver* observer) override;
private:
void AddHoldingSpaceScreenCaptureView(const HoldingSpaceItem* item);
void AddHoldingSpaceDownloadView(const HoldingSpaceItem* item);
void OnScreenCapturesContainerViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details);
void OnDownloadsContainerViewHierarchyChanged(
const views::ViewHierarchyChangedDetails& details);
// Owned by view hierarchy.
views::View* container_ = nullptr;
views::Label* label_ = nullptr;
std::map<std::string, HoldingSpaceItemView*> views_by_item_id_;
};
} // namespace ash
#endif // ASH_SYSTEM_HOLDING_SPACE_SCREEN_CAPTURES_SECTION_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment