Commit 7967876c authored by Tetsui Ohkubo's avatar Tetsui Ohkubo Committed by Commit Bot

Implement UnifiedSystemTray animation

This CL implements transition animation between collapsed state and
expanded state of UnifiedSystemTray.

As seen in the UX prototype, we support state change from touch gesture
and CollapseButton. In case of touch dragging, we should show
intermediate state between transition, depending on how much it's
dragged.

To support this, this CL extends UnifiedSystemTrayView::SetExpanded to
take double instead of bool, and make it render arbitrary intermediate
state. See the design doc for detail.

Video:
http://dr/file/d/1260aGQRNzgN5Y2qH5FhovT7nBbPhqoR8/view?usp=sharing

UX prototype: http://shortn/_EAeqJNHpAg
Design doc: go/cros-qs-restyling (See "Support animation" section)

TEST=manual
BUG=826999

Change-Id: Id010ad58b1fcbf77eac7c37b911fd0d164d7fe57
Reviewed-on: https://chromium-review.googlesource.com/981915Reviewed-by: default avatarYoshiki Iguchi <yoshiki@chromium.org>
Commit-Queue: Tetsui Ohkubo <tetsui@chromium.org>
Cr-Commit-Position: refs/heads/master@{#546766}
parent 536a7046
......@@ -82,7 +82,7 @@ class ASH_EXPORT FeaturePodButton : public views::View,
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
bool visible_preferred() { return visible_preferred_; }
bool visible_preferred() const { return visible_preferred_; }
protected:
FeaturePodIconButton* icon_button() const { return icon_button_; }
......
......@@ -14,33 +14,47 @@ FeaturePodsContainerView::FeaturePodsContainerView() {}
FeaturePodsContainerView::~FeaturePodsContainerView() = default;
gfx::Size FeaturePodsContainerView::CalculatePreferredSize() const {
if (!expanded_) {
return gfx::Size(kTrayMenuWidth,
2 * kUnifiedFeaturePodCollapsedVerticalPadding +
kUnifiedFeaturePodCollapsedSize.height());
}
const int collapsed_height = 2 * kUnifiedFeaturePodCollapsedVerticalPadding +
kUnifiedFeaturePodCollapsedSize.height();
int visible_count = CountVisibleChildren();
int visible_count = 0;
for (int i = 0; i < child_count(); ++i) {
if (static_cast<const FeaturePodButton*>(child_at(i))->visible_preferred())
++visible_count;
}
// floor(visible_count / kUnifiedFeaturePodItemsInRow)
int number_of_lines = (visible_count + kUnifiedFeaturePodItemsInRow - 1) /
kUnifiedFeaturePodItemsInRow;
return gfx::Size(kTrayMenuWidth, kUnifiedFeaturePodVerticalPadding +
(kUnifiedFeaturePodVerticalPadding +
kUnifiedFeaturePodSize.height()) *
number_of_lines);
const int expanded_height =
kUnifiedFeaturePodVerticalPadding +
(kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
number_of_lines;
return gfx::Size(
kTrayMenuWidth,
static_cast<int>(collapsed_height * (1.0 - expanded_amount_) +
expanded_height * expanded_amount_));
}
void FeaturePodsContainerView::SetExpanded(bool expanded) {
if (expanded_ == expanded)
void FeaturePodsContainerView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
if (expanded_amount_ == expanded_amount)
return;
expanded_ = expanded;
for (int i = 0; i < child_count(); ++i) {
auto* child = static_cast<FeaturePodButton*>(child_at(i));
child->SetExpanded(expanded);
}
UpdateChildVisibility();
expanded_amount_ = expanded_amount;
PreferredSizeChanged();
if (expanded_amount == 0.0 || expanded_amount == 1.0) {
expanded_ = expanded_amount == 1.0;
for (int i = 0; i < child_count(); ++i) {
auto* child = static_cast<FeaturePodButton*>(child_at(i));
child->SetExpanded(expanded_);
}
UpdateChildVisibility();
// We have to call Layout() explicitly here.
Layout();
}
}
void FeaturePodsContainerView::ChildVisibilityChanged(View* child) {
......@@ -94,7 +108,12 @@ void FeaturePodsContainerView::LayoutExpanded() {
void FeaturePodsContainerView::LayoutCollapsed() {
DCHECK(!expanded_);
int visible_count = CountVisibleChildren();
int visible_count = 0;
for (int i = 0; i < child_count(); ++i) {
if (child_at(i)->visible())
++visible_count;
}
DCHECK(visible_count > 0 &&
visible_count <= kUnifiedFeaturePodMaxItemsInCollapsed);
......@@ -123,15 +142,6 @@ void FeaturePodsContainerView::LayoutCollapsed() {
kTrayMenuWidth);
}
int FeaturePodsContainerView::CountVisibleChildren() const {
int visible_count = 0;
for (int i = 0; i < child_count(); ++i) {
if (child_at(i)->visible())
++visible_count;
}
return visible_count;
}
void FeaturePodsContainerView::UpdateChildVisibility() {
DCHECK(!changing_visibility_);
changing_visibility_ = true;
......
......@@ -20,9 +20,10 @@ class ASH_EXPORT FeaturePodsContainerView : public views::View {
FeaturePodsContainerView();
~FeaturePodsContainerView() override;
// Change the expanded state. If collapsed, all the buttons are horizontally
// placed.
void SetExpanded(bool expanded);
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state. If collapsed, all the buttons are
// horizontally placed.
void SetExpandedAmount(double expanded_amount);
// Overridden views::View:
gfx::Size CalculatePreferredSize() const override;
......@@ -34,8 +35,13 @@ class ASH_EXPORT FeaturePodsContainerView : public views::View {
void LayoutCollapsed();
void UpdateChildVisibility();
int CountVisibleChildren() const;
// The last |expanded_amount| passed to SetExpandedAmount().
double expanded_amount_ = 1.0;
// True if the last state was expanded or collapsed. Changes when
// SetExpandedAmount is called with 0.0 or 1.0.
// TODO(tetsui): Remove this flag when the animation for this view is
// implemented.
bool expanded_ = true;
bool changing_visibility_ = false;
......
......@@ -72,7 +72,7 @@ TEST_F(FeaturePodsContainerViewTest, ExpandedAndCollapsed) {
for (auto* button : buttons_)
EXPECT_TRUE(button->visible());
container()->SetExpanded(false);
container()->SetExpandedAmount(0.0);
// In collapsed state, all buttons are laid out horizontally.
for (int i = 1; i < kUnifiedFeaturePodMaxItemsInCollapsed; ++i)
......@@ -89,9 +89,9 @@ TEST_F(FeaturePodsContainerViewTest, HiddenButtonRemainsHidden) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
// The button is invisible in expanded state.
buttons_.front()->SetVisible(false);
container()->SetExpanded(false);
container()->SetExpandedAmount(0.0);
EXPECT_FALSE(buttons_.front()->visible());
container()->SetExpanded(true);
container()->SetExpandedAmount(1.0);
EXPECT_FALSE(buttons_.front()->visible());
}
......@@ -99,7 +99,7 @@ TEST_F(FeaturePodsContainerViewTest, BecomeVisibleInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
// The button is invisible in expanded state.
buttons_.back()->SetVisible(false);
container()->SetExpanded(false);
container()->SetExpandedAmount(0.0);
// The button becomes visible in collapsed state.
buttons_.back()->SetVisible(true);
// As the container still has remaining space, the button will be visible.
......@@ -110,20 +110,20 @@ TEST_F(FeaturePodsContainerViewTest, StillHiddenInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
// The button is invisible in expanded state.
buttons_.back()->SetVisible(false);
container()->SetExpanded(false);
container()->SetExpandedAmount(0.0);
// The button becomes visible in collapsed state.
buttons_.back()->SetVisible(true);
// As the container doesn't have remaining space, the button won't be visible.
EXPECT_FALSE(buttons_.back()->visible());
container()->SetExpanded(true);
container()->SetExpandedAmount(1.0);
// The button becomes visible in expanded state.
EXPECT_TRUE(buttons_.back()->visible());
}
TEST_F(FeaturePodsContainerViewTest, DifferentButtonBecomeVisibleInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
container()->SetExpanded(false);
container()->SetExpandedAmount(0.0);
// The last button is not visible as it doesn't have enough space.
EXPECT_FALSE(buttons_.back()->visible());
// The first button becomes invisible.
......
......@@ -28,14 +28,30 @@
#include "ash/system/unified/quiet_mode_feature_pod_controller.h"
#include "ash/system/unified/unified_system_tray_view.h"
#include "ash/wm/lock_state_controller.h"
#include "base/numerics/ranges.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "ui/gfx/animation/slide_animation.h"
namespace ash {
namespace {
// Animation duration to collapse / expand the view in milliseconds.
const int kExpandAnimationDurationMs = 500;
// Threshold in pixel that fully collapses / expands the view through gesture.
const int kDragThreshold = 200;
} // namespace
UnifiedSystemTrayController::UnifiedSystemTrayController(
SystemTray* system_tray)
: system_tray_(system_tray) {}
: system_tray_(system_tray),
animation_(std::make_unique<gfx::SlideAnimation>(this)) {
animation_->Reset(1.0);
animation_->SetSlideDuration(kExpandAnimationDurationMs);
animation_->SetTweenType(gfx::Tween::EASE_IN_OUT);
}
UnifiedSystemTrayController::~UnifiedSystemTrayController() = default;
......@@ -78,8 +94,32 @@ void UnifiedSystemTrayController::HandlePowerAction() {
}
void UnifiedSystemTrayController::ToggleExpanded() {
expanded_ = !expanded_;
unified_view_->SetExpanded(expanded_);
if (animation_->IsShowing())
animation_->Hide();
else
animation_->Show();
}
void UnifiedSystemTrayController::BeginDrag(const gfx::Point& location) {
drag_init_point_ = location;
was_expanded_ = animation_->IsShowing();
}
void UnifiedSystemTrayController::UpdateDrag(const gfx::Point& location) {
animation_->Reset(GetDragExpandedAmount(location));
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::EndDrag(const gfx::Point& location) {
// If dragging is finished, animate to closer state.
if (GetDragExpandedAmount(location) > 0.5) {
animation_->Show();
} else {
// To animate to hidden state, first set SlideAnimation::IsShowing() to
// true.
animation_->Show();
animation_->Hide();
}
}
void UnifiedSystemTrayController::ShowNetworkDetailedView() {
......@@ -107,6 +147,22 @@ void UnifiedSystemTrayController::ShowIMEDetailedView() {
ShowSystemTrayDetailedView(system_tray_->GetTrayIME());
}
void UnifiedSystemTrayController::AnimationEnded(
const gfx::Animation* animation) {
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::AnimationProgressed(
const gfx::Animation* animation) {
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::AnimationCanceled(
const gfx::Animation* animation) {
animation_->Reset(std::round(animation_->GetCurrentValue()));
UpdateExpandedAmount();
}
void UnifiedSystemTrayController::InitFeaturePods() {
AddFeaturePodItem(std::make_unique<NetworkFeaturePodController>(this));
AddFeaturePodItem(std::make_unique<BluetoothFeaturePodController>(this));
......@@ -138,4 +194,23 @@ void UnifiedSystemTrayController::ShowSystemTrayDetailedView(
BubbleCreationType::BUBBLE_USE_EXISTING);
}
void UnifiedSystemTrayController::UpdateExpandedAmount() {
unified_view_->SetExpandedAmount(animation_->GetCurrentValue());
}
double UnifiedSystemTrayController::GetDragExpandedAmount(
const gfx::Point& location) const {
double y_diff = (location - drag_init_point_).y();
// If already expanded, only consider swiping down. Otherwise, only consider
// swiping up.
if (was_expanded_) {
return base::ClampToRange(1.0 - std::max(0.0, y_diff) / kDragThreshold, 0.0,
1.0);
} else {
return base::ClampToRange(std::max(0.0, -y_diff) / kDragThreshold, 0.0,
1.0);
}
}
} // namespace ash
......@@ -10,6 +10,12 @@
#include "ash/ash_export.h"
#include "base/macros.h"
#include "ui/gfx/animation/animation_delegate.h"
#include "ui/gfx/geometry/point.h"
namespace gfx {
class SlideAnimation;
} // namespace gfx
namespace ash {
......@@ -21,12 +27,12 @@ class UnifiedVolumeSliderController;
class UnifiedSystemTrayView;
// Controller class of UnifiedSystemTrayView. Handles events of the view.
class ASH_EXPORT UnifiedSystemTrayController {
class ASH_EXPORT UnifiedSystemTrayController : public gfx::AnimationDelegate {
public:
// |system_tray| is used to show detailed views which are still not
// implemented on UnifiedSystemTray.
UnifiedSystemTrayController(SystemTray* system_tray);
~UnifiedSystemTrayController();
~UnifiedSystemTrayController() override;
// Create the view. The created view is unowned.
UnifiedSystemTrayView* CreateView();
......@@ -42,6 +48,11 @@ class ASH_EXPORT UnifiedSystemTrayController {
// Toggle expanded state of UnifiedSystemTrayView. Called from the view.
void ToggleExpanded();
// Handle finger dragging and expand/collapse the view. Called from view.
void BeginDrag(const gfx::Point& location);
void UpdateDrag(const gfx::Point& location);
void EndDrag(const gfx::Point& location);
// Show the detailed view of network. Called from the view.
void ShowNetworkDetailedView();
// Show the detailed view of bluetooth. Called from the view.
......@@ -53,6 +64,11 @@ class ASH_EXPORT UnifiedSystemTrayController {
// Show the detailed view of IME. Called from the view.
void ShowIMEDetailedView();
// gfx::AnimationDelegate:
void AnimationEnded(const gfx::Animation* animation) override;
void AnimationProgressed(const gfx::Animation* animation) override;
void AnimationCanceled(const gfx::Animation* animation) override;
private:
// Initialize feature pod controllers and their views.
// If you want to add a new feature pod item, you have to add here.
......@@ -66,6 +82,16 @@ class ASH_EXPORT UnifiedSystemTrayController {
// UnifiedSystemTray.
void ShowSystemTrayDetailedView(SystemTrayItem* system_tray_item);
// Update how much the view is expanded based on |animation_|.
void UpdateExpandedAmount();
// Return touch drag amount between 0.0 and 1.0. If expanding, it increases
// towards 1.0. If collapsing, it decreases towards 0.0. If the view is
// dragged to the same direction as the current state, it does not change the
// value. For example, if the view is expanded and it's dragged to the top, it
// keeps returning 1.0.
double GetDragExpandedAmount(const gfx::Point& location) const;
// Only used to show detailed views which are still not implemented on
// UnifiedSystemTray. Unowned.
// TODO(tetsui): Remove reference to |system_tray|.
......@@ -85,7 +111,15 @@ class ASH_EXPORT UnifiedSystemTrayController {
std::unique_ptr<UnifiedBrightnessSliderController>
brightness_slider_controller_;
bool expanded_ = true;
// If the previous state is expanded or not. Only valid during dragging (from
// BeginDrag to EndDrag).
bool was_expanded_ = true;
// The last |location| passed to BeginDrag(). Only valid during dragging.
gfx::Point drag_init_point_;
// Animation between expanded and collapsed states.
std::unique_ptr<gfx::SlideAnimation> animation_;
DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTrayController);
};
......
......@@ -9,18 +9,62 @@
#include "ash/system/unified/feature_pods_container_view.h"
#include "ash/system/unified/top_shortcuts_view.h"
#include "ash/system/unified/unified_system_info_view.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ui/app_list/app_list_features.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
namespace ash {
UnifiedSlidersContainerView::UnifiedSlidersContainerView() = default;
UnifiedSlidersContainerView::~UnifiedSlidersContainerView() = default;
void UnifiedSlidersContainerView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
SetVisible(expanded_amount > 0.0);
expanded_amount_ = expanded_amount;
PreferredSizeChanged();
UpdateOpacity();
}
void UnifiedSlidersContainerView::Layout() {
int y = 0;
for (int i = 0; i < child_count(); ++i) {
views::View* child = child_at(i);
int height = child->GetHeightForWidth(kTrayMenuWidth);
child->SetBounds(0, y, kTrayMenuWidth, height);
y += height;
}
}
gfx::Size UnifiedSlidersContainerView::CalculatePreferredSize() const {
int height = 0;
for (int i = 0; i < child_count(); ++i)
height += child_at(i)->GetHeightForWidth(kTrayMenuWidth);
return gfx::Size(kTrayMenuWidth, height * expanded_amount_);
}
void UnifiedSlidersContainerView::UpdateOpacity() {
for (int i = 0; i < child_count(); ++i) {
views::View* child = child_at(i);
double opacity = 1.0;
if (child->y() > height())
opacity = 0.0;
else if (child->bounds().bottom() < height())
opacity = 1.0;
else
opacity = static_cast<double>(height() - child->y()) / child->height();
child->layer()->SetOpacity(opacity);
}
}
UnifiedSystemTrayView::UnifiedSystemTrayView(
UnifiedSystemTrayController* controller)
: controller_(controller),
top_shortcuts_view_(new TopShortcutsView(controller_)),
feature_pods_container_(new FeaturePodsContainerView()),
sliders_container_(new views::View),
sliders_container_(new UnifiedSlidersContainerView()),
system_info_view_(new UnifiedSystemInfoView()) {
DCHECK(controller_);
......@@ -34,9 +78,6 @@ UnifiedSystemTrayView::UnifiedSystemTrayView(
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
sliders_container_->SetLayoutManager(
std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical));
AddChildView(top_shortcuts_view_);
AddChildView(feature_pods_container_);
AddChildView(sliders_container_);
......@@ -50,14 +91,40 @@ void UnifiedSystemTrayView::AddFeaturePodButton(FeaturePodButton* button) {
}
void UnifiedSystemTrayView::AddSliderView(views::View* slider_view) {
slider_view->SetPaintToLayer();
slider_view->layer()->SetFillsBoundsOpaquely(false);
sliders_container_->AddChildView(slider_view);
}
void UnifiedSystemTrayView::SetExpanded(bool expanded) {
top_shortcuts_view_->SetExpanded(expanded);
feature_pods_container_->SetExpanded(expanded);
sliders_container_->SetVisible(expanded);
void UnifiedSystemTrayView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
if (expanded_amount == 1.0 || expanded_amount == 0.0)
top_shortcuts_view_->SetExpanded(expanded_amount == 1.0);
feature_pods_container_->SetExpandedAmount(expanded_amount);
sliders_container_->SetExpandedAmount(expanded_amount);
PreferredSizeChanged();
}
void UnifiedSystemTrayView::OnGestureEvent(ui::GestureEvent* event) {
gfx::Point screen_location = event->location();
ConvertPointToScreen(this, &screen_location);
switch (event->type()) {
case ui::ET_GESTURE_SCROLL_BEGIN:
controller_->BeginDrag(screen_location);
event->SetHandled();
break;
case ui::ET_GESTURE_SCROLL_UPDATE:
controller_->UpdateDrag(screen_location);
event->SetHandled();
break;
case ui::ET_GESTURE_END:
controller_->EndDrag(screen_location);
event->SetHandled();
break;
default:
break;
}
}
} // namespace ash
......@@ -15,6 +15,31 @@ class TopShortcutsView;
class UnifiedSystemInfoView;
class UnifiedSystemTrayController;
// Container view of slider views. If SetExpandedAmount() is called with 1.0,
// the behavior is same as vertiacal BoxLayout, but otherwise it shows
// intermediate state during animation.
class UnifiedSlidersContainerView : public views::View {
public:
UnifiedSlidersContainerView();
~UnifiedSlidersContainerView() override;
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state.
void SetExpandedAmount(double expanded_amount);
// views::View:
void Layout() override;
gfx::Size CalculatePreferredSize() const override;
private:
// Update opacity of each child slider views based on |expanded_amount_|.
void UpdateOpacity();
double expanded_amount_ = 1.0;
DISALLOW_COPY_AND_ASSIGN(UnifiedSlidersContainerView);
};
// View class of the main bubble in UnifiedSystemTray.
class UnifiedSystemTrayView : public views::View {
public:
......@@ -27,8 +52,12 @@ class UnifiedSystemTrayView : public views::View {
// Add slider view.
void AddSliderView(views::View* slider_view);
// Change the expanded state.
void SetExpanded(bool expanded);
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state.
void SetExpandedAmount(double expanded_amount);
// views::View:
void OnGestureEvent(ui::GestureEvent* event) override;
private:
// Unowned.
......@@ -37,7 +66,7 @@ class UnifiedSystemTrayView : public views::View {
// Owned by views hierarchy.
TopShortcutsView* top_shortcuts_view_;
FeaturePodsContainerView* feature_pods_container_;
views::View* sliders_container_;
UnifiedSlidersContainerView* sliders_container_;
UnifiedSystemInfoView* system_info_view_;
DISALLOW_COPY_AND_ASSIGN(UnifiedSystemTrayView);
......
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