Commit 3c45a416 authored by Weidong Guo's avatar Weidong Guo Committed by Commit Bot

Implement the parallex effect for launcher

Transition the y position of items in launcher to 0 and change the
opacity of these items while dragging between peeking and closed state.

Demo: https://drive.google.com/open?id=1U64zU-0VctEnjRvW7gFYei5-WqaBN-pg

Bug: 862891
Change-Id: Icad43167661ed31ed9a8514fd39708e3fe4a3689
Reviewed-on: https://chromium-review.googlesource.com/1147329
Commit-Queue: Weidong Guo <weidongg@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#578688}
parent 704c945f
......@@ -1265,6 +1265,10 @@ void AppListView::StartAnimationForState(AppListViewState target_state) {
settings.AddObserver(transition_animation_observer_.get());
layer->SetTransform(gfx::Transform());
// In transition animation, layout is only performed after it is complete,
// which makes the child views jump. So layout in advance here to avoid that.
Layout();
}
void AppListView::StartCloseAnimation(base::TimeDelta animation_duration) {
......@@ -1346,7 +1350,8 @@ void AppListView::UpdateYPositionAndOpacity(int y_position_in_screen,
gfx::Rect new_widget_bounds = fullscreen_widget_->GetWindowBoundsInScreen();
app_list_y_position_in_screen_ = std::min(
std::max(y_position_in_screen, GetDisplayNearestView().bounds().y()),
GetDisplayNearestView().bounds().bottom() - kShelfSize);
GetDisplayNearestView().bounds().bottom() -
AppListConfig::instance().shelf_height());
new_widget_bounds.set_y(app_list_y_position_in_screen_);
gfx::NativeView native_view = fullscreen_widget_->GetNativeView();
::wm::ConvertRectFromScreen(native_view->parent(), &new_widget_bounds);
......@@ -1377,17 +1382,40 @@ void AppListView::SetIsInDrag(bool is_in_drag) {
GetAppsContainerView()->UpdateControlVisibility(app_list_state_, is_in_drag_);
}
int AppListView::GetScreenBottom() {
int AppListView::GetScreenBottom() const {
return GetDisplayNearestView().bounds().bottom();
}
int AppListView::GetCurrentAppListHeight() const {
if (!fullscreen_widget_)
return kShelfSize;
return GetDisplayNearestView().size().height() -
return AppListConfig::instance().shelf_height();
return GetDisplayNearestView().bounds().bottom() -
fullscreen_widget_->GetWindowBoundsInScreen().y();
}
float AppListView::GetAppListTransitionProgress() const {
const float current_height = GetCurrentAppListHeight();
const float peeking_height =
AppListConfig::instance().peeking_app_list_height();
if (current_height <= peeking_height) {
// Currently transition progress is between closed and peeking state.
// Calculate the progress of this transition.
const float shelf_height =
GetScreenBottom() - GetDisplayNearestView().work_area().bottom();
DCHECK_LE(shelf_height, current_height);
return (current_height - shelf_height) / (peeking_height - shelf_height);
}
// Currently transition progress is between peeking and fullscreen state.
// Calculate the progress of this transition.
const float fullscreen_height_above_peeking =
GetDisplayNearestView().size().height() - peeking_height;
const float current_height_above_peeking = current_height - peeking_height;
DCHECK_GT(fullscreen_height_above_peeking, 0);
DCHECK_LE(current_height_above_peeking, fullscreen_height_above_peeking);
return 1 + current_height_above_peeking / fullscreen_height_above_peeking;
}
bool AppListView::IsHomeLauncherEnabledInTabletMode() const {
return is_tablet_mode_ && is_home_launcher_enabled_;
}
......@@ -1471,10 +1499,11 @@ void AppListView::OnDisplayMetricsChanged(const display::Display& display,
float AppListView::GetAppListBackgroundOpacityDuringDragging() {
float top_of_applist = fullscreen_widget_->GetWindowBoundsInScreen().y();
const int shelf_height = AppListConfig::instance().shelf_height();
float dragging_height =
std::max((GetScreenBottom() - kShelfSize - top_of_applist), 0.f);
std::max((GetScreenBottom() - shelf_height - top_of_applist), 0.f);
float coefficient =
std::min(dragging_height / (kNumOfShelfSize * kShelfSize), 1.0f);
std::min(dragging_height / (kNumOfShelfSize * shelf_height), 1.0f);
float shield_opacity =
is_background_blur_enabled_ ? kAppListOpacityWithBlur : kAppListOpacity;
// Assume shelf is opaque when start to drag down the launcher.
......
......@@ -194,11 +194,16 @@ class APP_LIST_EXPORT AppListView : public views::WidgetDelegateView,
gfx::Rect GetAppInfoDialogBounds() const;
// Gets current screen bottom.
int GetScreenBottom();
int GetScreenBottom() const;
// Returns current app list height above display bottom.
int GetCurrentAppListHeight() const;
// The progress of app list height transitioning from closed to fullscreen
// state. [0.0, 1.0] means the progress between closed and peeking state,
// while [1.0, 2.0] means the progress between peeking and fullscreen state.
float GetAppListTransitionProgress() const;
views::Widget* get_fullscreen_widget_for_test() const {
return fullscreen_widget_;
}
......
......@@ -40,8 +40,9 @@ constexpr int kSearchBoxMinimumTopPadding = 24;
// Height of suggestion chip container.
constexpr int kSuggestionChipContainerHeight = 32;
// Vertical spacing between search box and suggestion chips.
constexpr int kSearchBoxSuggestionChipSpacing = 24;
// The y position of suggestion chips in peeking and fullscreen state.
constexpr int kSuggestionChipPeekingY = 156;
constexpr int kSuggestionChipFullscreenY = 96;
// The ratio of allowed bounds for apps grid view to its maximum margin.
constexpr int kAppsGridMarginRatio = 16;
......@@ -52,6 +53,16 @@ constexpr int kAppsGridMinimumMargin = 8;
// The horizontal spacing between apps grid view and page switcher.
constexpr int kAppsGridPageSwitcherSpacing = 8;
// The range of app list transition progress in which the suggestion chips'
// opacity changes from 0 to 1.
constexpr float kSuggestionChipOpacityStartProgress = 0;
constexpr float kSuggestionChipOpacityEndProgress = 0.67;
// The range of app list transition progress in which the expand arrow'
// opacity changes from 0 to 1.
constexpr float kExpandArrowOpacityStartProgress = 0;
constexpr float kExpandArrowOpacityEndProgress = 0.62;
} // namespace
AppsContainerView::AppsContainerView(ContentsView* contents_view,
......@@ -175,6 +186,36 @@ void AppsContainerView::UpdateOpacity() {
0.f),
1.0f);
page_switcher_->layer()->SetOpacity(should_restore_opacity ? 1.0f : opacity);
const float progress =
contents_view_->app_list_view()->GetAppListTransitionProgress();
if (suggestion_chip_container_view_) {
// Changes the opacity of suggestion chips between 0 and 1 when app list
// transition progress changes between |kSuggestionChipOpacityStartProgress|
// and |kSuggestionChipOpacityEndProgress|.
float chips_opacity =
std::min(std::max((progress - kSuggestionChipOpacityStartProgress) /
(kSuggestionChipOpacityEndProgress -
kSuggestionChipOpacityStartProgress),
0.f),
1.0f);
suggestion_chip_container_view_->layer()->SetOpacity(
should_restore_opacity ? 1.0f : chips_opacity);
}
if (expand_arrow_view_) {
// Changes the opacity of expand arrow between 0 and 1 when app list
// transition progress changes between |kExpandArrowOpacityStartProgress|
// and |kExpandArrowOpacityEndProgress|.
float arrow_opacity =
std::min(std::max((progress - kExpandArrowOpacityStartProgress) /
(kExpandArrowOpacityEndProgress -
kExpandArrowOpacityStartProgress),
0.f),
1.0f);
expand_arrow_view_->layer()->SetOpacity(
should_restore_opacity ? 1.0f : arrow_opacity);
}
}
gfx::Size AppsContainerView::CalculatePreferredSize() const {
......@@ -196,6 +237,7 @@ void AppsContainerView::Layout() {
switch (show_state_) {
case SHOW_APPS: {
if (is_new_style_launcher_enabled_) {
// Layout expand arrow.
gfx::Rect arrow_rect(rect);
const gfx::Size arrow_size(expand_arrow_view_->GetPreferredSize());
arrow_rect.set_height(arrow_size.height());
......@@ -203,14 +245,27 @@ void AppsContainerView::Layout() {
expand_arrow_view_->SetBoundsRect(arrow_rect);
expand_arrow_view_->SchedulePaint();
// Layout suggestion chips.
gfx::Rect chip_container_rect(rect);
chip_container_rect.set_y(GetSearchBoxExpectedBounds().bottom() +
kSearchBoxSuggestionChipSpacing);
const float progress =
contents_view_->app_list_view()->GetAppListTransitionProgress();
if (progress <= 1) {
// Currently transition progress is between closed and peeking state.
chip_container_rect.set_y(gfx::Tween::IntValueBetween(
progress, 0, kSuggestionChipPeekingY));
} else {
// Currently transition progress is between peeking and fullscreen
// state.
chip_container_rect.set_y(
gfx::Tween::IntValueBetween(progress - 1, kSuggestionChipPeekingY,
kSuggestionChipFullscreenY));
}
chip_container_rect.set_height(kSuggestionChipContainerHeight);
suggestion_chip_container_view_->SetBoundsRect(chip_container_rect);
rect.Inset(0, chip_container_rect.bottom(), 0, 0);
}
// Layout apps grid.
gfx::Rect grid_rect = rect;
if (is_new_style_launcher_enabled_) {
// Switch the column and row size if apps grid's height is greater than
......@@ -256,6 +311,7 @@ void AppsContainerView::Layout() {
}
apps_grid_view_->SetBoundsRect(grid_rect);
// Layout page switcher.
gfx::Rect page_switcher_rect = rect;
page_switcher_rect.Inset(grid_rect.right() + kAppsGridPageSwitcherSpacing,
0, 0, 0);
......@@ -335,22 +391,15 @@ gfx::Rect AppsContainerView::GetPageBoundsForState(
gfx::Rect AppsContainerView::GetSearchBoxExpectedBounds() const {
gfx::Rect search_box_bounds(contents_view_->GetDefaultSearchBoxBounds());
const int current_height =
contents_view_->app_list_view()->GetCurrentAppListHeight();
const int peeking_height =
AppListConfig::instance().peeking_app_list_height();
if (current_height <= peeking_height) {
const float progress =
contents_view_->app_list_view()->GetAppListTransitionProgress();
if (progress <= 1) {
search_box_bounds.set_y(gfx::Tween::IntValueBetween(
static_cast<double>(current_height - kShelfSize) /
(peeking_height - kShelfSize),
AppListConfig::instance().search_box_closed_top_padding(),
progress, AppListConfig::instance().search_box_closed_top_padding(),
AppListConfig::instance().search_box_peeking_top_padding()));
} else {
const double peeking_to_fullscreen_height =
contents_view_->GetDisplaySize().height() - peeking_height;
DCHECK_GT(peeking_to_fullscreen_height, 0);
search_box_bounds.set_y(gfx::Tween::IntValueBetween(
(current_height - peeking_height) / peeking_to_fullscreen_height,
progress - 1,
AppListConfig::instance().search_box_peeking_top_padding(),
is_new_style_launcher_enabled_
? AppListConfig::instance().search_box_fullscreen_top_padding()
......@@ -376,10 +425,11 @@ int AppsContainerView::GetSearchBoxFinalTopPadding() const {
gfx::Rect AppsContainerView::GetPageBoundsDuringDragging(
ash::AppListState state) const {
const int shelf_height = AppListConfig::instance().shelf_height();
const float drag_amount = std::max(
0.f, static_cast<float>(
contents_view_->app_list_view()->GetCurrentAppListHeight() -
kShelfSize));
shelf_height));
const int peeking_height =
AppListConfig::instance().peeking_app_list_height();
......@@ -388,14 +438,14 @@ gfx::Rect AppsContainerView::GetPageBoundsDuringDragging(
AppListConfig::instance().search_box_peeking_top_padding() +
search_box::kSearchBoxPreferredHeight + kSearchBoxPeekingBottomPadding -
kSearchBoxBottomPadding;
if (drag_amount <= (peeking_height - kShelfSize)) {
if (drag_amount <= (peeking_height - shelf_height)) {
// App list is dragged from collapsed to peeking, which moved up at most
// |peeking_height - kShelfSize| (272px). The top padding of apps
// container view changes from |-kSearchBoxFullscreenBottomPadding| to
// |kSearchBoxPeekingTopPadding + kSearchBoxPreferredHeight +
// kSearchBoxPeekingBottomPadding - kSearchBoxFullscreenBottomPadding|.
y = std::ceil(((peeking_final_y + kSearchBoxBottomPadding) * drag_amount) /
(peeking_height - kShelfSize) -
(peeking_height - shelf_height) -
kSearchBoxBottomPadding);
} else {
// App list is dragged from peeking to fullscreen, which moved up at most
......@@ -406,7 +456,7 @@ gfx::Rect AppsContainerView::GetPageBoundsDuringDragging(
float peeking_to_fullscreen_height =
contents_view_->GetWorkAreaSize().height() - peeking_height;
y = std::ceil((final_y - peeking_final_y) *
(drag_amount - (peeking_height - kShelfSize)) /
(drag_amount - (peeking_height - shelf_height)) /
peeking_to_fullscreen_height +
peeking_final_y);
y = std::max(std::min(final_y, y), peeking_final_y);
......
......@@ -1696,8 +1696,9 @@ void AppsGridView::UpdateOpacity() {
// changes from |kSuggestedAppsOpacityStartFraction| to
// |kSuggestedAppsOpacityEndFraction|, the opacity of suggested apps changes
// from 0.f to 1.0f.
float fraction = std::max<float>(current_height - kShelfSize, 0) /
(peeking_height - kShelfSize);
const int shelf_height = AppListConfig::instance().shelf_height();
float fraction = std::max<float>(current_height - shelf_height, 0) /
(peeking_height - shelf_height);
float opacity =
std::min(std::max((fraction - kSuggestedAppsOpacityStartFraction) /
(kSuggestedAppsOpacityEndFraction -
......@@ -1768,7 +1769,7 @@ void AppsGridView::UpdateOpacity() {
// |kAllAppsOpacityEndPx| above the work area bottom.
float centerline_above_work_area = 0.f;
const float drag_amount_above_peeking = current_height - peeking_height;
const float opacity_factor = drag_amount_above_peeking / kShelfSize;
const float opacity_factor = drag_amount_above_peeking / shelf_height;
for (int i = 0; i < view_model_.view_size(); ++i) {
AppListItemView* item_view = GetItemViewAt(i);
if (item_view == drag_view_)
......@@ -1791,7 +1792,7 @@ void AppsGridView::UpdateOpacity() {
if ((index.page == 0 && index.slot < cols_ &&
contents_view_->app_list_view()->drag_started_from_peeking()) &&
((drag_amount_above_peeking >= 0 &&
drag_amount_above_peeking <= kShelfSize) ||
drag_amount_above_peeking <= shelf_height) ||
(drag_amount_above_peeking < 0 &&
centerline_above_work_area >= kAllAppsOpacityStartPx))) {
opacity = std::max(opacity * opacity_factor, 0.f);
......@@ -2310,7 +2311,9 @@ bool AppsGridView::IsPointWithinBottomDragBuffer(
(AppListConfig::instance().page_flip_zone_size());
const int kBottomDragBufferMax =
display.bounds().bottom() -
(contents_view_->app_list_view()->is_side_shelf() ? 0 : kShelfSize);
(contents_view_->app_list_view()->is_side_shelf()
? 0
: AppListConfig::instance().shelf_height());
return point_in_screen.y() > kBottomDragBufferMin &&
point_in_screen.y() < kBottomDragBufferMax;
......
......@@ -8,6 +8,7 @@
#include <utility>
#include "ash/app_list/views/app_list_view.h"
#include "ash/app_list/views/apps_container_view.h"
#include "ash/app_list/views/contents_view.h"
#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/cpp/app_list/app_list_constants.h"
......@@ -39,11 +40,13 @@ constexpr int kArrowDimension = 12;
constexpr int kInkDropRadius = 18;
constexpr int kCircleRadius = 18;
// The y position of circle center in peeking and fullscreen state.
// The y position of circle center in closed, peeking and fullscreen state.
constexpr int kCircleCenterClosedY = 18;
constexpr int kCircleCenterPeekingY = 42;
constexpr int kCircleCenterFullscreenY = 8;
// The arrow's y position in peeking and fullscreen state.
constexpr int kArrowClosedY = 12;
constexpr int kArrowPeekingY = 36;
constexpr int kArrowFullscreenY = 2;
......@@ -115,9 +118,6 @@ ExpandArrowView::ExpandArrowView(ContentsView* contents_view,
ExpandArrowView::~ExpandArrowView() = default;
void ExpandArrowView::PaintButtonContents(gfx::Canvas* canvas) {
const int current_height = app_list_view_->GetCurrentAppListHeight();
const int peeking_height =
AppListConfig::instance().peeking_app_list_height();
gfx::PointF circle_center(kTileWidth / 2, kCircleCenterPeekingY);
gfx::PointF arrow_origin((kTileWidth - kArrowDimension) / 2, kArrowPeekingY);
gfx::PointF arrow_points[kPointCount];
......@@ -125,27 +125,36 @@ void ExpandArrowView::PaintButtonContents(gfx::Canvas* canvas) {
arrow_points[i] = kPeekingPoints[i];
SkColor circle_color =
HasFocus() ? kFocusedBackgroundColor : kUnFocusedBackgroundColor;
if (current_height > peeking_height && is_new_style_launcher_enabled_) {
// If app list is transitioning from peeking to fullscreen state, the arrow
// and circle should move up, the arrow should transition to a horizontal
// line and the circle should become transparent.
const double peeking_to_fullscreen_height =
contents_view_->GetDisplaySize().height() - peeking_height;
DCHECK_GT(peeking_to_fullscreen_height, 0);
const double progress =
(current_height - peeking_height) / peeking_to_fullscreen_height;
circle_center.set_y(gfx::Tween::FloatValueBetween(
progress, kCircleCenterPeekingY, kCircleCenterFullscreenY));
arrow_origin.set_y(gfx::Tween::FloatValueBetween(progress, kArrowPeekingY,
kArrowFullscreenY));
for (size_t i = 0; i < kPointCount; ++i) {
arrow_points[i].set_y(gfx::Tween::FloatValueBetween(
progress, kPeekingPoints[i].y(), kFullscreenPoints[i].y()));
const float progress = app_list_view_->GetAppListTransitionProgress();
if (is_new_style_launcher_enabled_) {
if (progress <= 1) {
// Currently transition progress is between closed and peeking state.
// Change the y positions of arrow and circle.
circle_center.set_y(gfx::Tween::FloatValueBetween(
progress, kCircleCenterClosedY, kCircleCenterPeekingY));
arrow_origin.set_y(gfx::Tween::FloatValueBetween(progress, kArrowClosedY,
kArrowPeekingY));
} else {
const float peeking_to_full_progress = progress - 1;
// Currently transition progress is between peeking and fullscreen state.
// Change the y positions of arrow and circle. Also change the shape of
// the arrow and the opacity of the circle.
circle_center.set_y(gfx::Tween::FloatValueBetween(
peeking_to_full_progress, kCircleCenterPeekingY,
kCircleCenterFullscreenY));
arrow_origin.set_y(gfx::Tween::FloatValueBetween(
peeking_to_full_progress, kArrowPeekingY, kArrowFullscreenY));
for (size_t i = 0; i < kPointCount; ++i) {
arrow_points[i].set_y(gfx::Tween::FloatValueBetween(
peeking_to_full_progress, kPeekingPoints[i].y(),
kFullscreenPoints[i].y()));
}
circle_color = gfx::Tween::ColorValueBetween(
peeking_to_full_progress, circle_color, SK_ColorTRANSPARENT);
}
circle_color = gfx::Tween::ColorValueBetween(progress, circle_color,
SK_ColorTRANSPARENT);
} else if (animation_->is_animating()) {
}
if (animation_->is_animating() && progress <= 1) {
// If app list is peeking state or below peeking state, the arrow should
// keep runing transition animation.
arrow_origin.Offset(0, arrow_y_offset_);
......@@ -158,7 +167,7 @@ void ExpandArrowView::PaintButtonContents(gfx::Canvas* canvas) {
circle_flags.setStyle(cc::PaintFlags::kFill_Style);
canvas->DrawCircle(circle_center, kCircleRadius, circle_flags);
if (animation_->is_animating() && current_height <= peeking_height) {
if (animation_->is_animating() && progress <= 1) {
// Draw a pulse that expands around the circle.
cc::PaintFlags pulse_flags;
pulse_flags.setStyle(cc::PaintFlags::kStroke_Style);
......
......@@ -271,10 +271,11 @@ void SearchBoxView::UpdateOpacity() {
->ShouldShowSearchBox()) {
return;
}
const int shelf_height = AppListConfig::instance().shelf_height();
float fraction =
std::max<float>(0, contents->app_list_view()->GetCurrentAppListHeight() -
kShelfSize) /
(AppListConfig::instance().peeking_app_list_height() - kShelfSize);
shelf_height) /
(AppListConfig::instance().peeking_app_list_height() - shelf_height);
float opacity =
std::min(std::max((fraction - kOpacityStartFraction) /
......
......@@ -47,7 +47,8 @@ AppListConfig::AppListConfig()
folder_dropping_delay_(150),
folder_background_color_(SkColorSetRGB(0xFA, 0xFA, 0xFC)),
page_flip_zone_size_(40),
grid_tile_spacing_in_folder_(12) {
grid_tile_spacing_in_folder_(12),
shelf_height_(48) {
if (features::IsNewStyleLauncherEnabled()) {
grid_tile_width_ = 120;
grid_tile_height_ = 112;
......@@ -60,7 +61,7 @@ AppListConfig::AppListConfig()
grid_focus_corner_radius_ = 12;
app_title_max_line_height_ = 20;
peeking_app_list_height_ = 284;
search_box_closed_top_padding_ = 84;
search_box_closed_top_padding_ = 0;
search_box_peeking_top_padding_ = 84;
preferred_rows_ = 4;
page_spacing_ = 48;
......
......@@ -83,6 +83,7 @@ class ASH_PUBLIC_EXPORT AppListConfig {
int grid_tile_spacing_in_folder() const {
return grid_tile_spacing_in_folder_;
}
int shelf_height() const { return shelf_height_; }
gfx::Size grid_icon_size() const {
return gfx::Size(grid_icon_dimension_, grid_icon_dimension_);
......@@ -247,6 +248,9 @@ class ASH_PUBLIC_EXPORT AppListConfig {
// The spacing between tile views in folder.
int grid_tile_spacing_in_folder_;
// The height/width of the shelf from the bottom/side of the screen.
int shelf_height_;
};
} // namespace app_list
......
......@@ -107,12 +107,6 @@ const int kSearchBoxPeekingBottomPadding = 12;
// Bottom padding of search box.
const int kSearchBoxBottomPadding = 24;
// The height of the peeking app list from the bottom of the screen.
const int kPeekingAppListHeight = 320;
// The height/width of the shelf from the bottom/side of the screen.
const int kShelfSize = 48;
// Max pages allowed in a folder.
const size_t kMaxFolderPages = 3;
......
......@@ -64,9 +64,6 @@ ASH_PUBLIC_EXPORT extern const int kSearchBoxTopPadding;
ASH_PUBLIC_EXPORT extern const int kSearchBoxPeekingBottomPadding;
ASH_PUBLIC_EXPORT extern const int kSearchBoxBottomPadding;
ASH_PUBLIC_EXPORT extern const int kPeekingAppListHeight;
ASH_PUBLIC_EXPORT extern const int kShelfSize;
ASH_PUBLIC_EXPORT extern const size_t kMaxFolderPages;
ASH_PUBLIC_EXPORT extern const size_t kMaxFolderItemsPerPage;
ASH_PUBLIC_EXPORT extern const size_t kMaxFolderNameChars;
......
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