Commit c4212d51 authored by David Black's avatar David Black Committed by Commit Bot

Rough in holding space content forward entry point.

When enabled, the holding space tray will have an icon representative of
the holding space model contents.

As holding space items are added to the model, the icon is updated to
include a new visual representation of the added item. As holding space
items are removed from the model, stale representations are removed.

The visual representations for items are rendered using ui::Layers added
to the holding space tray icon which only exist when visible in the
viewport. This is to limit the number of layers if the user happens to
have a large number of holding space items.

Note that this CL just roughs things in. Still TODO:
- Animate bounds changes.
- Animate icon visibility changes.
- Render ImakeSkia's for items to their layers.
- Handle side shelf.

Changes are guarded by a new FeatureParam and disabled by default.

Screenshot: https://screenshot.googleplex.com/6g64LPbEUNc8o8U

Bug: 1142572
Change-Id: I3eada919a6d28ccfb208d1b15b8990d4762f3dd8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2499882Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarToni Baržić <tbarzic@chromium.org>
Commit-Queue: David Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/master@{#821778}
parent 08c8a4ef
......@@ -973,6 +973,8 @@ component("ash") {
"system/holding_space/holding_space_tray_bubble.h",
"system/holding_space/holding_space_tray_icon.cc",
"system/holding_space/holding_space_tray_icon.h",
"system/holding_space/holding_space_tray_icon_item.cc",
"system/holding_space/holding_space_tray_icon_item.h",
"system/holding_space/pinned_files_container.cc",
"system/holding_space/pinned_files_container.h",
"system/holding_space/recent_files_container.cc",
......
......@@ -138,6 +138,10 @@ const base::Feature kMaintainShelfStateWhenEnteringOverview{
const base::Feature kTemporaryHoldingSpace{"TemporaryHoldingSpace",
base::FEATURE_DISABLED_BY_DEFAULT};
const base::FeatureParam<bool>
kTemporaryHoldingSpaceContentForwardEntryPointEnabled{
&kTemporaryHoldingSpace, "content-forward-entry-point-enabled", false};
const base::Feature kDragUnpinnedAppToPin{"DragUnpinnedAppToPin",
base::FEATURE_DISABLED_BY_DEFAULT};
......@@ -293,6 +297,10 @@ bool IsTemporaryHoldingSpaceEnabled() {
return base::FeatureList::IsEnabled(kTemporaryHoldingSpace);
}
bool IsTemporaryHoldingSpaceContentForwardEntryPointEnabled() {
return kTemporaryHoldingSpaceContentForwardEntryPointEnabled.Get();
}
bool IsDragUnpinnedAppToPinEnabled() {
return base::FeatureList::IsEnabled(kDragUnpinnedAppToPin);
}
......
......@@ -254,6 +254,8 @@ ASH_PUBLIC_EXPORT bool IsMaintainShelfStateWhenEnteringOverviewEnabled();
ASH_PUBLIC_EXPORT bool IsTemporaryHoldingSpaceEnabled();
ASH_PUBLIC_EXPORT bool IsTemporaryHoldingSpaceContentForwardEntryPointEnabled();
ASH_PUBLIC_EXPORT bool IsDragUnpinnedAppToPinEnabled();
// These two functions are supposed to be temporary functions to set or get
......
......@@ -37,6 +37,7 @@ constexpr gfx::Size kHoldingSpaceScreenCaptureSize(104, 80);
constexpr gfx::Insets kHoldingSpaceScreenCapturesContainerPadding(8, 0);
constexpr float kHoldingSpaceSelectedOverlayOpacity = 0.24f;
constexpr int kHoldingSpaceTrayIconMainAxisMargin = 6;
constexpr int kHoldingSpaceTrayIconMaxVisibleItems = 3;
constexpr int kHoldingSpaceTrayIconSize = 20;
// Context menu commands.
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "ash/system/holding_space/holding_space_tray.h"
#include <memory>
#include "ash/accessibility/accessibility_controller_impl.h"
......
......@@ -4,10 +4,15 @@
#include "ash/system/holding_space/holding_space_tray_icon.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/holding_space/holding_space_tray_icon_item.h"
#include "ash/system/tray/tray_constants.h"
#include "base/containers/unique_ptr_adapters.h"
#include "base/stl_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/image_view.h"
......@@ -18,12 +23,17 @@ namespace ash {
HoldingSpaceTrayIcon::HoldingSpaceTrayIcon() {
InitLayout();
if (features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled())
controller_observer_.Add(HoldingSpaceController::Get());
}
HoldingSpaceTrayIcon::~HoldingSpaceTrayIcon() = default;
int HoldingSpaceTrayIcon::GetPreferredMainAxisMargin() const {
return kHoldingSpaceTrayIconMainAxisMargin;
return features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled()
? 0
: kHoldingSpaceTrayIconMainAxisMargin;
}
void HoldingSpaceTrayIcon::OnLocaleChanged() {
......@@ -36,6 +46,16 @@ base::string16 HoldingSpaceTrayIcon::GetTooltipText(
}
void HoldingSpaceTrayIcon::InitLayout() {
if (features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled()) {
SetPreferredSize(gfx::Size(kTrayItemSize, kTrayItemSize));
// As holding space items are added to the model, child layers will be added
// to `this` view's layer to represent them.
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
return;
}
SetLayoutManager(std::make_unique<views::FillLayout>());
// Image.
......@@ -50,6 +70,90 @@ void HoldingSpaceTrayIcon::InitLayout() {
image_view->SetCanProcessEventsWithinSubtree(false);
}
void HoldingSpaceTrayIcon::OnHoldingSpaceModelAttached(
HoldingSpaceModel* model) {
DCHECK(features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled());
model_observer_.Add(model);
for (const std::unique_ptr<HoldingSpaceItem>& item : model->items())
OnHoldingSpaceItemAdded(item.get());
}
void HoldingSpaceTrayIcon::OnHoldingSpaceModelDetached(
HoldingSpaceModel* model) {
DCHECK(features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled());
model_observer_.Remove(model);
for (const std::unique_ptr<HoldingSpaceItem>& item : model->items())
OnHoldingSpaceItemRemoved(item.get());
}
// TODO(crbug.com/1142572): Handle only finalized items.
void HoldingSpaceTrayIcon::OnHoldingSpaceItemAdded(
const HoldingSpaceItem* item) {
DCHECK(features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled());
for (std::unique_ptr<HoldingSpaceTrayIconItem>& icon_item : icon_items_)
icon_item->AnimateShift();
icon_items_.push_back(std::make_unique<HoldingSpaceTrayIconItem>(this, item));
icon_items_.back()->AnimateIn();
UpdatePreferredSize();
}
void HoldingSpaceTrayIcon::OnHoldingSpaceItemRemoved(
const HoldingSpaceItem* item) {
DCHECK(features::IsTemporaryHoldingSpaceContentForwardEntryPointEnabled());
size_t index = icon_items_.size();
for (size_t i = 0u; i < icon_items_.size(); ++i) {
if (icon_items_[i]->item() == item) {
index = i;
break;
}
}
DCHECK_LT(index, icon_items_.size());
removed_icon_items_.push_back(std::move(icon_items_[index]));
icon_items_.erase(icon_items_.begin() + index);
for (int i = index - 1; i >= 0; --i)
icon_items_[i]->AnimateUnshift();
removed_icon_items_.back()->AnimateOut(base::BindOnce(
&HoldingSpaceTrayIcon::OnHoldingSpaceTrayIconItemAnimatedOut,
base::Unretained(this),
base::Unretained(removed_icon_items_.back().get())));
UpdatePreferredSize();
}
// TODO(crbug.com/1142572): Implement.
void HoldingSpaceTrayIcon::OnHoldingSpaceItemFinalized(
const HoldingSpaceItem* item) {
NOTIMPLEMENTED();
}
// TODO(crbug.com/1142572): Handle side shelf.
void HoldingSpaceTrayIcon::UpdatePreferredSize() {
const int num_visible_items = std::min(kHoldingSpaceTrayIconMaxVisibleItems,
static_cast<int>(icon_items_.size()));
int preferred_width = kTrayItemSize;
if (num_visible_items > 1)
preferred_width += (num_visible_items - 1) * kTrayItemSize / 2;
if (preferred_width != GetPreferredSize().width())
SetPreferredSize(gfx::Size(preferred_width, kTrayItemSize));
}
void HoldingSpaceTrayIcon::OnHoldingSpaceTrayIconItemAnimatedOut(
HoldingSpaceTrayIconItem* icon_item) {
base::EraseIf(removed_icon_items_, base::MatchesUniquePtr(icon_item));
}
BEGIN_METADATA(HoldingSpaceTrayIcon, views::View)
END_METADATA
......
......@@ -5,15 +5,27 @@
#ifndef ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_TRAY_ICON_H_
#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_TRAY_ICON_H_
#include <memory>
#include <vector>
#include "ash/ash_export.h"
#include "ash/public/cpp/holding_space/holding_space_controller.h"
#include "ash/public/cpp/holding_space/holding_space_controller_observer.h"
#include "ash/public/cpp/holding_space/holding_space_model.h"
#include "ash/public/cpp/holding_space/holding_space_model_observer.h"
#include "base/scoped_observer.h"
#include "ui/views/metadata/metadata_header_macros.h"
#include "ui/views/view.h"
namespace ash {
class HoldingSpaceTrayIconItem;
// TODO(crbug.com/1142572): Implement content forward icon w/ motion spec.
// The icon used to represent holding space in its tray in the shelf.
class ASH_EXPORT HoldingSpaceTrayIcon : public views::View {
class ASH_EXPORT HoldingSpaceTrayIcon : public views::View,
public HoldingSpaceControllerObserver,
public HoldingSpaceModelObserver {
public:
METADATA_HEADER(HoldingSpaceTrayIcon);
......@@ -32,7 +44,34 @@ class ASH_EXPORT HoldingSpaceTrayIcon : public views::View {
// views::View:
base::string16 GetTooltipText(const gfx::Point& point) const override;
// HoldingSpaceControllerObserver:
void OnHoldingSpaceModelAttached(HoldingSpaceModel* model) override;
void OnHoldingSpaceModelDetached(HoldingSpaceModel* model) override;
// HoldingSpaceModelObserver:
void OnHoldingSpaceItemAdded(const HoldingSpaceItem* item) override;
void OnHoldingSpaceItemRemoved(const HoldingSpaceItem* item) override;
void OnHoldingSpaceItemFinalized(const HoldingSpaceItem* item) override;
void InitLayout();
void UpdatePreferredSize();
// Invoked when the specified icon item has completed animated out. At this
// point it is owned by `removed_icon_items_` and should be destroyed.
void OnHoldingSpaceTrayIconItemAnimatedOut(HoldingSpaceTrayIconItem*);
// An icon item is added to the tray icon to visually represent each holding
// space item. Upon creation, icon items are added to `icon_items_` where they
// are owned until being animated out. Upon being animated out, icon items are
// moved to `removed_icon_items_` where they are owned until the out animation
// completes and are subsequently destroyed.
std::vector<std::unique_ptr<HoldingSpaceTrayIconItem>> icon_items_;
std::vector<std::unique_ptr<HoldingSpaceTrayIconItem>> removed_icon_items_;
ScopedObserver<HoldingSpaceController, HoldingSpaceControllerObserver>
controller_observer_{this};
ScopedObserver<HoldingSpaceModel, HoldingSpaceModelObserver> model_observer_{
this};
};
} // 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/holding_space_tray_icon_item.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/shelf_config.h"
#include "ash/system/holding_space/holding_space_tray_icon.h"
#include "ash/system/tray/tray_constants.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
namespace ash {
namespace {
// Performs set up of the specified `animation_settings`.
void SetUpAnimation(ui::ScopedLayerAnimationSettings* animation_settings) {
animation_settings->SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
animation_settings->SetTransitionDuration(
ShelfConfig::Get()->shelf_animation_duration());
animation_settings->SetTweenType(gfx::Tween::EASE_OUT);
}
} // namespace
// HoldingSpaceTrayIconItem ----------------------------------------------------
HoldingSpaceTrayIconItem::HoldingSpaceTrayIconItem(HoldingSpaceTrayIcon* icon,
const HoldingSpaceItem* item)
: icon_(icon), item_(item) {}
HoldingSpaceTrayIconItem::~HoldingSpaceTrayIconItem() = default;
// TODO(crbug.com/1142572): Handle side shelf.
void HoldingSpaceTrayIconItem::AnimateIn() {
CreateLayer();
gfx::Transform pre_transform(transform_);
pre_transform.Translate(0, -kTrayItemSize);
layer_->SetTransform(pre_transform);
icon_->layer()->Add(layer_.get());
ui::ScopedLayerAnimationSettings animation_settings(layer_->GetAnimator());
SetUpAnimation(&animation_settings);
layer_->SetTransform(transform_);
}
// TODO(crbug.com/1142572): Handle side shelf.
void HoldingSpaceTrayIconItem::AnimateOut(
base::OnceClosure animate_out_closure) {
animate_out_closure_ = std::move(animate_out_closure);
if (!layer_) {
std::move(animate_out_closure_).Run();
return;
}
ui::ScopedLayerAnimationSettings animation_settings(layer_->GetAnimator());
SetUpAnimation(&animation_settings);
animation_settings.AddObserver(this);
layer_->SetOpacity(0.f);
layer_->SetVisible(false);
}
// TODO(crbug.com/1142572): Handle side shelf and RTL.
void HoldingSpaceTrayIconItem::AnimateShift() {
transform_.Translate(kTrayItemSize / 2, 0);
if (!layer_)
return;
ui::ScopedLayerAnimationSettings animation_settings(layer_->GetAnimator());
SetUpAnimation(&animation_settings);
animation_settings.AddObserver(this);
layer_->SetTransform(transform_);
if (!NeedsLayer()) {
layer_->SetOpacity(0.f);
layer_->SetVisible(false);
}
}
// TODO(crbug.com/1142572): Handle side shelf and RTL.
void HoldingSpaceTrayIconItem::AnimateUnshift() {
transform_.Translate(-kTrayItemSize / 2, 0);
if (!layer_ && !NeedsLayer())
return;
if (!layer_) {
CreateLayer();
gfx::Transform pre_transform(transform_);
pre_transform.Translate(kTrayItemSize / 2, 0);
layer_->SetTransform(pre_transform);
layer_->SetOpacity(0.f);
icon_->layer()->Add(layer_.get());
icon_->layer()->StackAtBottom(layer_.get());
}
layer_->SetVisible(true);
ui::ScopedLayerAnimationSettings animation_settings(layer_->GetAnimator());
SetUpAnimation(&animation_settings);
layer_->SetTransform(transform_);
layer_->SetOpacity(1.f);
}
void HoldingSpaceTrayIconItem::OnImplicitAnimationsCompleted() {
if (layer_->visible())
return;
icon_->layer()->Remove(layer_.get());
layer_.reset();
if (animate_out_closure_) {
// NOTE: Running `animate_out_closure_` may delete `this`.
std::move(animate_out_closure_).Run();
}
}
// TODO(crbug.com/1142572): Layer should visually represent `item_`.
void HoldingSpaceTrayIconItem::CreateLayer() {
DCHECK(!layer_);
layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
layer_->SetBounds(gfx::Rect(0, 0, kTrayItemSize, kTrayItemSize));
layer_->SetColor(SkColorSetA(SK_ColorWHITE, 0.26 * 0xFF));
layer_->SetIsFastRoundedCorner(true);
layer_->SetRoundedCornerRadius(gfx::RoundedCornersF(kTrayItemSize / 2));
layer_->SetTransform(transform_);
}
// TODO(crbug.com/1142572): Handle side shelf.
bool HoldingSpaceTrayIconItem::NeedsLayer() const {
const float x = transform_.To2dTranslation().x();
return x < kHoldingSpaceTrayIconMaxVisibleItems * kTrayItemSize / 2;
}
} // 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_HOLDING_SPACE_TRAY_ICON_ITEM_H_
#define ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_TRAY_ICON_ITEM_H_
#include <memory>
#include <string>
#include "ash/ash_export.h"
#include "base/callback.h"
#include "ui/compositor/layer_animation_observer.h"
namespace ui {
class Layer;
} // namespace ui
namespace ash {
class HoldingSpaceItem;
class HoldingSpaceTrayIcon;
// Class to visually represent a single holding space item within the holding
// space tray icon in the shelf. While determined to be within the icon's
// viewport, each instance will manage a layer for the holding space tray icon.
class ASH_EXPORT HoldingSpaceTrayIconItem
: public ui::ImplicitAnimationObserver {
public:
HoldingSpaceTrayIconItem(HoldingSpaceTrayIcon*, const HoldingSpaceItem*);
HoldingSpaceTrayIconItem(const HoldingSpaceTrayIconItem&) = delete;
HoldingSpaceTrayIconItem& operator=(const HoldingSpaceTrayIconItem&) = delete;
~HoldingSpaceTrayIconItem() override;
// Animates this item in.
void AnimateIn();
// Animates this item out, invoking the specified closure on completion.
void AnimateOut(base::OnceClosure animate_out_closure);
// Animates a shift of this item.
void AnimateShift();
// Animates an unshift of this item.
void AnimateUnshift();
// Returns the holding space `item_` visually represented by this instance.
const HoldingSpaceItem* item() const { return item_; }
private:
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override;
// Creates the `layer_` for this item. Note that `layer_` may be created
// multiple times throughout this instance's lifetime as `layer_` will only
// exist while in the viewport for the holding space tray `icon_`.
void CreateLayer();
// Returns whether this item needs a layer for its current `transform_`. Since
// we only maintain `layer_` while it appears in the viewport for the holding
// space tray `icon_`, this is used to gate creation/deletion of `layer_`.
bool NeedsLayer() const;
HoldingSpaceTrayIcon* const icon_;
const HoldingSpaceItem* item_;
// This is a proxy for `layer_`'s transform and represents the target
// position of this item. Because `layer_` only exists while in `icon_`'s
// viewport, we need to manage transform ourselves and continue to update it
// even when `layer_` doesn't exist.
gfx::Transform transform_;
// The layer serving as the visual representation of the associated holding
// space `item_` in the holding space `icon_` in the shelf. This only exists
// while in the `icon_`s viewport as determined by the current `transform_`.
std::unique_ptr<ui::Layer> layer_;
// Closure to invoke on completion of `AnimateOut()`. It is expected that this
// instance may be deleted during invocation.
base::OnceClosure animate_out_closure_;
};
} // namespace ash
#endif // ASH_SYSTEM_HOLDING_SPACE_HOLDING_SPACE_TRAY_ICON_ITEM_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