Add launcher nudge animation with text label.
In this cl, a new type of animation is implemented for launcher nudge. In particular, a label view will be shown behind the home button and expand the clickable area during the animation. Bug: 1308733 Change-Id: I1ac3ae15bd7029ce4986c2b0a465260a151cb013 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3584979 Reviewed-by: James Cook <jamescook@chromium.org> Reviewed-by: Toni Barzic <tbarzic@chromium.org> Commit-Queue: Wen-Chien Wang <wcwang@chromium.org> Cr-Commit-Position: refs/heads/main@{#1001614}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
62adc2c06e
commit
51a5d604d2
@ -259,6 +259,9 @@ This file contains the strings for ash.
|
||||
<message name="IDS_SHELF_PREVIOUS" desc="Tooltip for the shelf arrow button that scrolls the shelf backward">
|
||||
Previous
|
||||
</message>
|
||||
<message name="IDS_SHELF_LAUNCHER_NUDGE_TEXT" desc="The text shown to users beside the home button that shows the launcher UI when the device is in clamshell mode. This nudge text guides them to click on the home button to see all the apps in the launcher UI.">
|
||||
See all apps
|
||||
</message>
|
||||
|
||||
<!-- Status tray items -->
|
||||
<message name="IDS_ASH_STATUS_TRAY_ACCESSIBLE_DESCRIPTION" is_accessibility_with_no_ui="true" desc="The accessible description of the status tray and the information on it.">
|
||||
|
@ -0,0 +1 @@
|
||||
8f77a5e6dfd51e78a9f24cd60cc00057f66687dd
|
@ -12,6 +12,9 @@ void ApplyAshFontStyles(int context,
|
||||
switch (context) {
|
||||
case CONTEXT_SEARCH_RESULT_VIEW_INLINE_ANSWER_DETAILS:
|
||||
break;
|
||||
case CONTEXT_LAUNCHER_NUDGE_LABEL:
|
||||
details.size_delta = 1;
|
||||
break;
|
||||
case CONTEXT_SHARESHEET_BUBBLE_SMALL:
|
||||
details.size_delta = -2;
|
||||
break;
|
||||
|
@ -18,6 +18,9 @@ enum AshTextContext {
|
||||
// A button that appears in the launcher's status area.
|
||||
CONTEXT_LAUNCHER_BUTTON = ASH_TEXT_CONTEXT_START,
|
||||
|
||||
// Text label that used in launcher nudge label. Medium weight. 13pt size.
|
||||
CONTEXT_LAUNCHER_NUDGE_LABEL,
|
||||
|
||||
// A button that appears within a row of the tray popup.
|
||||
CONTEXT_TRAY_POPUP_BUTTON,
|
||||
|
||||
|
@ -8,12 +8,15 @@
|
||||
#include <memory>
|
||||
|
||||
#include "ash/app_list/app_list_controller_impl.h"
|
||||
#include "ash/public/cpp/ash_typography.h"
|
||||
#include "ash/public/cpp/shelf_config.h"
|
||||
#include "ash/public/cpp/shelf_types.h"
|
||||
#include "ash/shelf/shelf.h"
|
||||
#include "ash/shelf/shelf_focus_cycler.h"
|
||||
#include "ash/shelf/shelf_navigation_widget.h"
|
||||
#include "ash/shelf/shelf_view.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/style/ash_color_provider.h"
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
|
||||
#include "base/check_op.h"
|
||||
@ -30,6 +33,9 @@
|
||||
#include "ui/views/animation/animation_builder.h"
|
||||
#include "ui/views/animation/flood_fill_ink_drop_ripple.h"
|
||||
#include "ui/views/controls/button/button_controller.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/layout/fill_layout.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
namespace ash {
|
||||
namespace {
|
||||
@ -44,10 +50,30 @@ constexpr uint8_t kAssistantInvisibleAlpha = 138; // 54% alpha
|
||||
constexpr int kAnimationBounceUpOffset = 12;
|
||||
constexpr int kAnimationBounceDownOffset = 3;
|
||||
|
||||
// Constants used on `nudge_ripple_layer_` animation.
|
||||
constexpr base::TimeDelta kHomeButtonAnimationDuration =
|
||||
base::Milliseconds(250);
|
||||
constexpr base::TimeDelta kRippleAnimationDuration = base::Milliseconds(2000);
|
||||
|
||||
// Constants used on `nudge_label_` animation.
|
||||
//
|
||||
// The duration of the showing/hiding animation for nudge label.
|
||||
constexpr base::TimeDelta kNudgeLabelTransitionOnDuration =
|
||||
base::Milliseconds(300);
|
||||
constexpr base::TimeDelta kNudgeLabelTransitionOffDuration =
|
||||
base::Milliseconds(500);
|
||||
|
||||
// The duration of the fade out animation that animates `nudge_label_` when
|
||||
// users click on the home button while `nudge_label_` is showing.
|
||||
constexpr base::TimeDelta kNudgeLabelFadeOutDuration = base::Milliseconds(100);
|
||||
|
||||
// The duration that the nudge label is shown.
|
||||
constexpr base::TimeDelta kNudgeLabelShowingDuration = base::Seconds(6);
|
||||
|
||||
// The minimum space we want to keep between the `nudge_label_` and the first
|
||||
// app in hotseat. Used to determine if `nudge_label_` should be shown.
|
||||
constexpr int kMinSpaceBetweenNudgeLabelAndHotseat = 24;
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
@ -72,7 +98,7 @@ HomeButton::ScopedNoClipRect::~ScopedNoClipRect() {
|
||||
// HomeButton::ScopedNoClipRect ------------------------------------------------
|
||||
|
||||
HomeButton::HomeButton(Shelf* shelf)
|
||||
: ShelfControlButton(shelf, this), controller_(this) {
|
||||
: ShelfControlButton(shelf, this), shelf_(shelf), controller_(this) {
|
||||
SetAccessibleName(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_SHELF_APP_LIST_LAUNCHER_TITLE));
|
||||
button_controller()->set_notify_action(
|
||||
@ -85,6 +111,24 @@ HomeButton::HomeButton(Shelf* shelf)
|
||||
|
||||
HomeButton::~HomeButton() = default;
|
||||
|
||||
gfx::Size HomeButton::CalculatePreferredSize() const {
|
||||
// Take the preferred size of the nudge label into consideration when it is
|
||||
// visible. Note that the button width is already included in the label width.
|
||||
if (label_container_ && label_container_->GetVisible())
|
||||
return label_container_->GetPreferredSize();
|
||||
|
||||
return ShelfControlButton::CalculatePreferredSize();
|
||||
}
|
||||
|
||||
void HomeButton::Layout() {
|
||||
ShelfControlButton::Layout();
|
||||
|
||||
if (label_container_) {
|
||||
label_container_->SetSize(
|
||||
gfx::Size(label_container_->GetPreferredSize().width(), height()));
|
||||
}
|
||||
}
|
||||
|
||||
void HomeButton::OnGestureEvent(ui::GestureEvent* event) {
|
||||
if (!controller_.MaybeHandleGestureEvent(event))
|
||||
Button::OnGestureEvent(event);
|
||||
@ -130,6 +174,20 @@ void HomeButton::ButtonPressed(views::Button* sender,
|
||||
event.IsShiftDown() ? kShelfButtonFullscreen : kShelfButton;
|
||||
Shell::Get()->app_list_controller()->ToggleAppList(
|
||||
GetDisplayId(), show_source, event.time_stamp());
|
||||
|
||||
// If the home button is pressed, fade out the nudge label if it is showing.
|
||||
if (label_container_) {
|
||||
if (!label_container_->GetVisible()) {
|
||||
// If the nudge label is not visible and will not be animating, directly
|
||||
// remove them as the nudge won't be showing anymore.
|
||||
RemoveNudgeLabel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (label_nudge_timer_.IsRunning())
|
||||
label_nudge_timer_.AbandonAndStop();
|
||||
AnimateNudgeLabelFadeOut();
|
||||
}
|
||||
}
|
||||
|
||||
void HomeButton::OnAssistantAvailabilityChanged() {
|
||||
@ -160,94 +218,77 @@ HomeButton::CreateScopedNoClipRect() {
|
||||
shelf()->navigation_widget());
|
||||
}
|
||||
|
||||
bool HomeButton::CanShowNudgeLabel() const {
|
||||
if (!shelf_->IsHorizontalAlignment())
|
||||
return false;
|
||||
|
||||
// If there's no pinned app in shelf, shows the nudge label for the launcher
|
||||
// nudge.
|
||||
ShelfView* shelf_view = shelf_->hotseat_widget()->GetShelfView();
|
||||
int view_size = shelf_view->view_model()->view_size();
|
||||
if (view_size == 0)
|
||||
return true;
|
||||
|
||||
// Need to have nudge_label_ existing to calculate the space for itself.
|
||||
DCHECK(nudge_label_);
|
||||
|
||||
// For the calculation below, convert all points and rects to the root window
|
||||
// coordinate to make sure they are under the same coordinate.
|
||||
gfx::Rect first_app_bounds =
|
||||
shelf_view->view_model()->view_at(0)->GetMirroredBounds();
|
||||
first_app_bounds = shelf_view->ConvertRectToWidget(first_app_bounds);
|
||||
aura::Window* shelf_native_window =
|
||||
shelf_view->GetWidget()->GetNativeWindow();
|
||||
aura::Window::ConvertRectToTarget(shelf_native_window,
|
||||
shelf_native_window->GetRootWindow(),
|
||||
&first_app_bounds);
|
||||
|
||||
gfx::Rect label_rect =
|
||||
ConvertRectToWidget(label_container_->GetMirroredBounds());
|
||||
aura::Window* native_window = GetWidget()->GetNativeWindow();
|
||||
DCHECK_EQ(shelf_native_window->GetRootWindow(),
|
||||
native_window->GetRootWindow());
|
||||
aura::Window::ConvertRectToTarget(
|
||||
native_window, native_window->GetRootWindow(), &label_rect);
|
||||
|
||||
// Horizontal space between the `label_rect` and the first app in shelf, which
|
||||
// is also the app that is closest to the home button, is calculated here to
|
||||
// check if there's enough space to show the `nudge_label_`.
|
||||
int space = label_rect.ManhattanInternalDistance(first_app_bounds);
|
||||
return space >= kMinSpaceBetweenNudgeLabelAndHotseat;
|
||||
}
|
||||
|
||||
void HomeButton::StartNudgeAnimation() {
|
||||
// Create the ripple layer and its delegate for the nudge animation.
|
||||
nudge_ripple_layer_ = std::make_unique<ui::Layer>();
|
||||
float ripple_diameter = layer()->size().width();
|
||||
ripple_layer_delegate_ = std::make_unique<views::CircleLayerDelegate>(
|
||||
AshColorProvider::Get()->GetInkDropBaseColorAndOpacity().first,
|
||||
/*radius=*/ripple_diameter / 2);
|
||||
// Create the nudge label first to check if there is enough space to show it.
|
||||
if (!nudge_label_)
|
||||
CreateNudgeLabel();
|
||||
|
||||
// The bounds are set with respect to |shelf_container_layer| stated below.
|
||||
nudge_ripple_layer_->SetBounds(
|
||||
gfx::Rect(layer()->parent()->bounds().x() + bounds().x(),
|
||||
layer()->parent()->bounds().y() + bounds().y(), ripple_diameter,
|
||||
ripple_diameter));
|
||||
nudge_ripple_layer_->set_delegate(ripple_layer_delegate_.get());
|
||||
nudge_ripple_layer_->SetMasksToBounds(true);
|
||||
nudge_ripple_layer_->SetFillsBoundsOpaquely(false);
|
||||
const bool can_show_nudge_label = CanShowNudgeLabel();
|
||||
|
||||
// The position of the ripple layer is independent to the home button and its
|
||||
// parent shelf navigation widget. Therefore the ripple layer is added to the
|
||||
// shelf container layer, which is the parent layer of the shelf navigation
|
||||
// widget.
|
||||
ui::Layer* shelf_container_layer = GetWidget()->GetLayer()->parent();
|
||||
shelf_container_layer->Add(nudge_ripple_layer_.get());
|
||||
shelf_container_layer->StackBelow(nudge_ripple_layer_.get(),
|
||||
layer()->parent());
|
||||
|
||||
// Home button movement settings. Note that the navigation widget layer
|
||||
// contains the non-opaque part of the home button and is also animated along
|
||||
// with the home button.
|
||||
ui::Layer* widget_layer = GetWidget()->GetLayer();
|
||||
|
||||
gfx::PointF bounce_up_point = shelf()->SelectValueForShelfAlignment(
|
||||
gfx::PointF(0, -kAnimationBounceUpOffset),
|
||||
gfx::PointF(kAnimationBounceUpOffset, 0),
|
||||
gfx::PointF(-kAnimationBounceUpOffset, 0));
|
||||
gfx::PointF bounce_down_point = shelf()->SelectValueForShelfAlignment(
|
||||
gfx::PointF(0, kAnimationBounceDownOffset),
|
||||
gfx::PointF(-kAnimationBounceDownOffset, 0),
|
||||
gfx::PointF(kAnimationBounceDownOffset, 0));
|
||||
|
||||
gfx::Transform move_up;
|
||||
move_up.Translate(bounce_up_point.x(), bounce_up_point.y());
|
||||
gfx::Transform move_down;
|
||||
move_down.Translate(bounce_down_point.x(), bounce_down_point.y());
|
||||
|
||||
gfx::Transform initial_disc_scale;
|
||||
initial_disc_scale.Scale(0.1f, 0.1f);
|
||||
gfx::Transform initial_state =
|
||||
gfx::TransformAboutPivot(GetCenterPoint(), initial_disc_scale);
|
||||
|
||||
gfx::Transform final_disc_scale;
|
||||
final_disc_scale.Scale(3.0f, 3.0f);
|
||||
gfx::Transform scale_about_pivot =
|
||||
gfx::TransformAboutPivot(GetCenterPoint(), final_disc_scale);
|
||||
|
||||
// Remove clip_rect from the home button and its ancestors as the animation
|
||||
// goes beyond its size. The object is deleted once the animation ends.
|
||||
scoped_no_clip_rect_ = CreateScopedNoClipRect();
|
||||
|
||||
views::AnimationBuilder()
|
||||
views::AnimationBuilder builder;
|
||||
builder
|
||||
.SetPreemptionStrategy(
|
||||
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
|
||||
.OnStarted(base::BindOnce(&HomeButton::OnNudgeAnimationStarted,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.OnEnded(base::BindOnce(&HomeButton::OnNudgeAnimationEnded,
|
||||
.OnEnded(base::BindOnce(can_show_nudge_label
|
||||
? &HomeButton::OnLabelSlideInAnimationEnded
|
||||
: &HomeButton::OnNudgeAnimationEnded,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.Once()
|
||||
// Set up the animation of the ripple_layer
|
||||
.SetDuration(base::TimeDelta())
|
||||
.SetTransform(nudge_ripple_layer_.get(), initial_state)
|
||||
.SetOpacity(nudge_ripple_layer_.get(), 0.5f)
|
||||
.Then()
|
||||
.SetDuration(kRippleAnimationDuration)
|
||||
.SetTransform(nudge_ripple_layer_.get(), scale_about_pivot,
|
||||
gfx::Tween::ACCEL_0_40_DECEL_100)
|
||||
.SetOpacity(nudge_ripple_layer_.get(), 0.0f,
|
||||
gfx::Tween::ACCEL_0_80_DECEL_80)
|
||||
// Set up the animation of the widget_layer
|
||||
.At(base::Seconds(0))
|
||||
.SetDuration(kHomeButtonAnimationDuration)
|
||||
.SetTransform(widget_layer, move_up, gfx::Tween::FAST_OUT_SLOW_IN_3)
|
||||
.Then()
|
||||
.SetDuration(kHomeButtonAnimationDuration)
|
||||
.SetTransform(widget_layer, move_down, gfx::Tween::ACCEL_80_DECEL_20)
|
||||
.Then()
|
||||
.SetDuration(kHomeButtonAnimationDuration)
|
||||
.SetTransform(widget_layer, gfx::Transform(),
|
||||
gfx::Tween::FAST_OUT_SLOW_IN_3);
|
||||
.OnAborted(base::BindOnce(&HomeButton::OnNudgeAnimationEnded,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.Once();
|
||||
|
||||
if (can_show_nudge_label) {
|
||||
AnimateNudgeLabelSlideIn(builder);
|
||||
} else {
|
||||
AnimateNudgeBounce(builder);
|
||||
}
|
||||
|
||||
// Remove clip_rect from the home button and its ancestors as the animation
|
||||
// goes beyond its bounds. The object is deleted once the animation ends.
|
||||
scoped_no_clip_rect_ = CreateScopedNoClipRect();
|
||||
AnimateNudgeRipple(builder);
|
||||
}
|
||||
|
||||
void HomeButton::AddNudgeAnimationObserverForTest(
|
||||
@ -260,7 +301,7 @@ void HomeButton::RemoveNudgeAnimationObserverForTest(
|
||||
}
|
||||
|
||||
void HomeButton::PaintButtonContents(gfx::Canvas* canvas) {
|
||||
gfx::PointF circle_center(GetCenterPoint());
|
||||
gfx::PointF circle_center(gfx::Rect(size()).CenterPoint());
|
||||
|
||||
// Paint a white ring as the foreground for the app list circle. The ceil/dsf
|
||||
// math assures that the ring draws sharply and is centered at all scale
|
||||
@ -310,21 +351,241 @@ void HomeButton::OnThemeChanged() {
|
||||
ripple_layer_delegate_->set_color(
|
||||
AshColorProvider::Get()->GetInkDropBaseColorAndOpacity().first);
|
||||
}
|
||||
if (label_container_) {
|
||||
label_container_->layer()->SetColor(
|
||||
AshColorProvider::Get()->GetControlsLayerColor(
|
||||
AshColorProvider::ControlsLayerType::
|
||||
kControlBackgroundColorInactive));
|
||||
}
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
bool HomeButton::DoesIntersectRect(const views::View* target,
|
||||
const gfx::Rect& rect) const {
|
||||
DCHECK_EQ(target, this);
|
||||
gfx::Rect button_bounds = target->GetLocalBounds();
|
||||
// Increase clickable area for the button to account for clicks around the
|
||||
// spacing. This will not intercept events outside of the parent widget.
|
||||
button_bounds.Inset(
|
||||
gfx::Insets::VH(-ShelfConfig::Get()->control_button_edge_spacing(
|
||||
!shelf()->IsHorizontalAlignment()),
|
||||
-ShelfConfig::Get()->control_button_edge_spacing(
|
||||
shelf()->IsHorizontalAlignment())));
|
||||
return button_bounds.Intersects(rect);
|
||||
void HomeButton::CreateNudgeLabel() {
|
||||
DCHECK(!label_container_);
|
||||
const int home_button_width =
|
||||
ShelfControlButton::CalculatePreferredSize().width();
|
||||
|
||||
label_container_ = AddChildView(std::make_unique<views::View>());
|
||||
label_container_->SetLayoutManager(std::make_unique<views::FillLayout>());
|
||||
label_container_->SetBorder(
|
||||
views::CreateEmptyBorder(gfx::Insets::TLBR(0, home_button_width, 0, 16)));
|
||||
label_container_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
|
||||
label_container_->layer()->SetMasksToBounds(true);
|
||||
label_container_->layer()->SetColor(
|
||||
AshColorProvider::Get()->GetControlsLayerColor(
|
||||
AshColorProvider::ControlsLayerType::
|
||||
kControlBackgroundColorInactive));
|
||||
label_container_->layer()->SetRoundedCornerRadius(
|
||||
gfx::RoundedCornersF(home_button_width / 2.f));
|
||||
label_container_->layer()->SetName("NudgeLabelContainer");
|
||||
|
||||
// Create a view to clip the `nudge_label_` to the area right of the home
|
||||
// button during nudge label animation.
|
||||
auto* label_mask =
|
||||
label_container_->AddChildView(std::make_unique<views::View>());
|
||||
label_mask->SetLayoutManager(std::make_unique<views::FillLayout>());
|
||||
label_mask->SetBorder(
|
||||
views::CreateEmptyBorder(gfx::Insets::TLBR(0, 12, 0, 0)));
|
||||
label_mask->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
|
||||
label_mask->layer()->SetMasksToBounds(true);
|
||||
label_mask->layer()->SetName("NudgeLabelMask");
|
||||
|
||||
nudge_label_ = label_mask->AddChildView(std::make_unique<views::Label>(
|
||||
l10n_util::GetStringUTF16(IDS_SHELF_LAUNCHER_NUDGE_TEXT)));
|
||||
nudge_label_->SetAutoColorReadabilityEnabled(false);
|
||||
nudge_label_->SetPaintToLayer();
|
||||
nudge_label_->layer()->SetFillsBoundsOpaquely(false);
|
||||
nudge_label_->SetTextContext(CONTEXT_LAUNCHER_NUDGE_LABEL);
|
||||
nudge_label_->SetTextStyle(STYLE_EMPHASIZED);
|
||||
nudge_label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
|
||||
AshColorProvider::ContentLayerType::kTextColorPrimary));
|
||||
|
||||
Layout();
|
||||
label_container_->SetVisible(false);
|
||||
}
|
||||
|
||||
void HomeButton::AnimateNudgeRipple(views::AnimationBuilder& builder) {
|
||||
// Create the ripple layer and its delegate for the nudge animation.
|
||||
nudge_ripple_layer_.Reset(std::make_unique<ui::Layer>());
|
||||
ui::Layer* ripple_layer = nudge_ripple_layer_.layer();
|
||||
|
||||
float ripple_diameter = ShelfControlButton::CalculatePreferredSize().width();
|
||||
ripple_layer_delegate_ = std::make_unique<views::CircleLayerDelegate>(
|
||||
AshColorProvider::Get()->GetInkDropBaseColorAndOpacity().first,
|
||||
/*radius=*/ripple_diameter / 2);
|
||||
|
||||
// The bounds are set with respect to |shelf_container_layer| stated below.
|
||||
ripple_layer->SetBounds(
|
||||
gfx::Rect(layer()->parent()->bounds().x() + layer()->bounds().x(),
|
||||
layer()->parent()->bounds().y() + layer()->bounds().y(),
|
||||
ripple_diameter, ripple_diameter));
|
||||
|
||||
ripple_layer->set_delegate(ripple_layer_delegate_.get());
|
||||
ripple_layer->SetMasksToBounds(true);
|
||||
ripple_layer->SetFillsBoundsOpaquely(false);
|
||||
|
||||
// The position of the ripple layer is independent to the home button and its
|
||||
// parent shelf navigation widget. Therefore the ripple layer is added to the
|
||||
// shelf container layer, which is the parent layer of the shelf navigation
|
||||
// widget.
|
||||
ui::Layer* shelf_container_layer = GetWidget()->GetLayer()->parent();
|
||||
shelf_container_layer->Add(ripple_layer);
|
||||
shelf_container_layer->StackBelow(ripple_layer, layer()->parent());
|
||||
|
||||
// The point of the center of the round button.
|
||||
gfx::Point ripple_center = gfx::Rect(ripple_layer->size()).CenterPoint();
|
||||
|
||||
gfx::Transform initial_disc_scale;
|
||||
initial_disc_scale.Scale(0.1f, 0.1f);
|
||||
gfx::Transform initial_state =
|
||||
gfx::TransformAboutPivot(ripple_center, initial_disc_scale);
|
||||
|
||||
gfx::Transform final_disc_scale;
|
||||
final_disc_scale.Scale(3.0f, 3.0f);
|
||||
gfx::Transform scale_about_pivot =
|
||||
gfx::TransformAboutPivot(ripple_center, final_disc_scale);
|
||||
|
||||
builder.GetCurrentSequence()
|
||||
.At(base::TimeDelta())
|
||||
// Set up the animation of the `nudge_ripple_layer_`
|
||||
.SetDuration(base::TimeDelta())
|
||||
.SetTransform(ripple_layer, initial_state)
|
||||
.SetOpacity(ripple_layer, 0.5f)
|
||||
.Then()
|
||||
.SetDuration(kRippleAnimationDuration)
|
||||
.SetTransform(ripple_layer, scale_about_pivot,
|
||||
gfx::Tween::ACCEL_0_40_DECEL_100)
|
||||
.SetOpacity(ripple_layer, 0.0f, gfx::Tween::ACCEL_0_80_DECEL_80);
|
||||
}
|
||||
|
||||
void HomeButton::AnimateNudgeBounce(views::AnimationBuilder& builder) {
|
||||
gfx::PointF bounce_up_point = shelf()->SelectValueForShelfAlignment(
|
||||
gfx::PointF(0, -kAnimationBounceUpOffset),
|
||||
gfx::PointF(kAnimationBounceUpOffset, 0),
|
||||
gfx::PointF(-kAnimationBounceUpOffset, 0));
|
||||
gfx::PointF bounce_down_point = shelf()->SelectValueForShelfAlignment(
|
||||
gfx::PointF(0, kAnimationBounceDownOffset),
|
||||
gfx::PointF(-kAnimationBounceDownOffset, 0),
|
||||
gfx::PointF(kAnimationBounceDownOffset, 0));
|
||||
|
||||
gfx::Transform move_up;
|
||||
move_up.Translate(bounce_up_point.x(), bounce_up_point.y());
|
||||
gfx::Transform move_down;
|
||||
move_down.Translate(bounce_down_point.x(), bounce_down_point.y());
|
||||
|
||||
// Home button movement settings. Note that the navigation widget layer
|
||||
// contains the non-opaque part of the home button and is also animated along
|
||||
// with the home button.
|
||||
ui::Layer* widget_layer = GetWidget()->GetLayer();
|
||||
|
||||
// Set up the animation of the `widget_layer`, which bounce up and down during
|
||||
// the animation.
|
||||
builder.GetCurrentSequence()
|
||||
.At(base::TimeDelta())
|
||||
.SetDuration(kHomeButtonAnimationDuration)
|
||||
.SetTransform(widget_layer, move_up, gfx::Tween::FAST_OUT_SLOW_IN_3)
|
||||
.Then()
|
||||
.SetDuration(kHomeButtonAnimationDuration)
|
||||
.SetTransform(widget_layer, move_down, gfx::Tween::ACCEL_80_DECEL_20)
|
||||
.Then()
|
||||
.SetDuration(kHomeButtonAnimationDuration)
|
||||
.SetTransform(widget_layer, gfx::Transform(),
|
||||
gfx::Tween::FAST_OUT_SLOW_IN_3);
|
||||
}
|
||||
|
||||
void HomeButton::AnimateNudgeLabelSlideIn(views::AnimationBuilder& builder) {
|
||||
// Make sure the label is created.
|
||||
DCHECK(label_container_ && nudge_label_);
|
||||
|
||||
// Update the shelf layout to provide space for the navigation widget.
|
||||
label_container_->SetVisible(true);
|
||||
shelf_->shelf_layout_manager()->LayoutShelf(false);
|
||||
|
||||
const int home_button_width =
|
||||
ShelfControlButton::CalculatePreferredSize().width();
|
||||
const int label_visible_width = label_container_->width() - home_button_width;
|
||||
|
||||
gfx::Rect initial_container_clip_rect;
|
||||
gfx::Transform initial_transform;
|
||||
|
||||
// Set up the initial label transform and label container clip rect.
|
||||
if (base::i18n::IsRTL()) {
|
||||
initial_transform.Translate(label_visible_width, 0);
|
||||
initial_container_clip_rect = gfx::Rect(
|
||||
label_visible_width, 0, home_button_width, label_container_->height());
|
||||
} else {
|
||||
initial_transform.Translate(-label_visible_width, 0);
|
||||
initial_container_clip_rect =
|
||||
gfx::Rect(0, 0, home_button_width, label_container_->height());
|
||||
}
|
||||
|
||||
// Calculate the target clip rect on `label_container_`.
|
||||
gfx::Rect container_target_clip_rect = gfx::Rect(label_container_->size());
|
||||
|
||||
// Set up the animation of the `nudge_label_`
|
||||
builder.GetCurrentSequence()
|
||||
.At(base::TimeDelta())
|
||||
.SetDuration(base::TimeDelta())
|
||||
.SetTransform(nudge_label_->layer(), initial_transform)
|
||||
.SetClipRect(label_container_->layer(), initial_container_clip_rect)
|
||||
.SetOpacity(label_container_->layer(), 0)
|
||||
.Then()
|
||||
.SetDuration(kNudgeLabelTransitionOnDuration)
|
||||
.SetTransform(nudge_label_->layer(), gfx::Transform(),
|
||||
gfx::Tween::ACCEL_5_70_DECEL_90)
|
||||
.SetClipRect(label_container_->layer(), container_target_clip_rect,
|
||||
gfx::Tween::ACCEL_5_70_DECEL_90)
|
||||
.SetOpacity(label_container_->layer(), 1,
|
||||
gfx::Tween::ACCEL_5_70_DECEL_90);
|
||||
}
|
||||
|
||||
void HomeButton::AnimateNudgeLabelSlideOut() {
|
||||
const int home_button_width =
|
||||
ShelfControlButton::CalculatePreferredSize().width();
|
||||
const int label_visible_width = label_container_->width() - home_button_width;
|
||||
|
||||
gfx::Transform target_transform;
|
||||
gfx::Rect container_target_clip_rect;
|
||||
|
||||
// Calculate the target transform and clip rect.
|
||||
if (base::i18n::IsRTL()) {
|
||||
target_transform.Translate(label_visible_width, 0);
|
||||
container_target_clip_rect = gfx::Rect(
|
||||
label_visible_width, 0, home_button_width, label_container_->height());
|
||||
} else {
|
||||
target_transform.Translate(-label_visible_width, 0);
|
||||
container_target_clip_rect =
|
||||
gfx::Rect(0, 0, home_button_width, label_container_->height());
|
||||
}
|
||||
|
||||
views::AnimationBuilder()
|
||||
.SetPreemptionStrategy(
|
||||
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
|
||||
.OnEnded(base::BindOnce(&HomeButton::OnNudgeAnimationEnded,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.OnAborted(base::BindOnce(&HomeButton::OnNudgeAnimationEnded,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.Once()
|
||||
.SetDuration(kNudgeLabelTransitionOffDuration)
|
||||
.SetTransform(nudge_label_->layer(), target_transform,
|
||||
gfx::Tween::ACCEL_40_DECEL_100_3)
|
||||
.SetClipRect(label_container_->layer(), container_target_clip_rect,
|
||||
gfx::Tween::ACCEL_40_DECEL_100_3)
|
||||
.SetOpacity(label_container_->layer(), 0,
|
||||
gfx::Tween::ACCEL_40_DECEL_100_3);
|
||||
}
|
||||
|
||||
void HomeButton::AnimateNudgeLabelFadeOut() {
|
||||
views::AnimationBuilder()
|
||||
.SetPreemptionStrategy(
|
||||
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
|
||||
.OnEnded(base::BindOnce(&HomeButton::OnLabelFadeOutAnimationEnded,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.OnAborted(base::BindOnce(&HomeButton::OnLabelFadeOutAnimationEnded,
|
||||
weak_ptr_factory_.GetWeakPtr()))
|
||||
.Once()
|
||||
.SetDuration(kNudgeLabelFadeOutDuration)
|
||||
.SetOpacity(label_container_->layer(), 0, gfx::Tween::LINEAR);
|
||||
}
|
||||
|
||||
void HomeButton::OnNudgeAnimationStarted() {
|
||||
@ -335,9 +596,14 @@ void HomeButton::OnNudgeAnimationStarted() {
|
||||
void HomeButton::OnNudgeAnimationEnded() {
|
||||
// Delete the ripple layer and its delegate after the launcher nudge animation
|
||||
// is completed.
|
||||
nudge_ripple_layer_.reset();
|
||||
nudge_ripple_layer_.ReleaseLayer();
|
||||
ripple_layer_delegate_.reset();
|
||||
|
||||
if (label_container_) {
|
||||
label_container_->SetVisible(false);
|
||||
shelf_->shelf_layout_manager()->LayoutShelf(false);
|
||||
}
|
||||
|
||||
// Reset the clip rect after the animation is completed.
|
||||
scoped_no_clip_rect_.reset();
|
||||
|
||||
@ -345,4 +611,50 @@ void HomeButton::OnNudgeAnimationEnded() {
|
||||
observer.NudgeAnimationEnded(this);
|
||||
}
|
||||
|
||||
void HomeButton::OnLabelSlideInAnimationEnded() {
|
||||
for (auto& observer : observers_)
|
||||
observer.NudgeLabelShown(this);
|
||||
|
||||
// After the label is shown for `kNudgeLabelShowingDuration` amount of time,
|
||||
// move the label back to its original position.
|
||||
label_nudge_timer_.Start(
|
||||
FROM_HERE, kNudgeLabelShowingDuration,
|
||||
base::BindOnce(&HomeButton::AnimateNudgeLabelSlideOut,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void HomeButton::OnLabelFadeOutAnimationEnded() {
|
||||
OnNudgeAnimationEnded();
|
||||
|
||||
// If the label is faded out by clicking on it, remove the label as it is
|
||||
// assumed that the nudge won't be shown again.
|
||||
RemoveNudgeLabel();
|
||||
}
|
||||
|
||||
void HomeButton::RemoveNudgeLabel() {
|
||||
RemoveChildViewT(label_container_);
|
||||
label_container_ = nullptr;
|
||||
nudge_label_ = nullptr;
|
||||
}
|
||||
|
||||
bool HomeButton::DoesIntersectRect(const views::View* target,
|
||||
const gfx::Rect& rect) const {
|
||||
DCHECK_EQ(target, this);
|
||||
gfx::Rect button_bounds = target->GetLocalBounds();
|
||||
|
||||
// If the `label_container_` is visible, set all the area within the label
|
||||
// bounds clickable.
|
||||
if (label_container_ && label_container_->GetVisible())
|
||||
button_bounds = label_container_->layer()->bounds();
|
||||
|
||||
// Increase clickable area for the button to account for clicks around the
|
||||
// spacing. This will not intercept events outside of the parent widget.
|
||||
button_bounds.Inset(
|
||||
gfx::Insets::VH(-ShelfConfig::Get()->control_button_edge_spacing(
|
||||
!shelf()->IsHorizontalAlignment()),
|
||||
-ShelfConfig::Get()->control_button_edge_spacing(
|
||||
shelf()->IsHorizontalAlignment())));
|
||||
return button_bounds.Intersects(rect);
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -16,15 +16,18 @@
|
||||
#include "ui/views/view_targeter_delegate.h"
|
||||
|
||||
namespace views {
|
||||
class AnimationBuilder;
|
||||
class CircleLayerDelegate;
|
||||
class Label;
|
||||
} // namespace views
|
||||
|
||||
namespace ui {
|
||||
class Layer;
|
||||
class LayerOwner;
|
||||
}
|
||||
|
||||
namespace ash {
|
||||
|
||||
class Shelf;
|
||||
class ShelfButtonDelegate;
|
||||
class ShelfNavigationWidget;
|
||||
|
||||
@ -63,6 +66,9 @@ class ASH_EXPORT HomeButton : public ShelfControlButton,
|
||||
// Called when the nudge animation is started/ended.
|
||||
virtual void NudgeAnimationStarted(HomeButton* home_button) = 0;
|
||||
virtual void NudgeAnimationEnded(HomeButton* home_button) = 0;
|
||||
|
||||
// Called when the nudge label is animated to fully shown.
|
||||
virtual void NudgeLabelShown(HomeButton* home_button) = 0;
|
||||
};
|
||||
|
||||
static const char kViewClassName[];
|
||||
@ -74,6 +80,10 @@ class ASH_EXPORT HomeButton : public ShelfControlButton,
|
||||
|
||||
~HomeButton() override;
|
||||
|
||||
// views::View:
|
||||
gfx::Size CalculatePreferredSize() const override;
|
||||
void Layout() override;
|
||||
|
||||
// views::Button:
|
||||
void OnGestureEvent(ui::GestureEvent* event) override;
|
||||
const char* GetClassName() const override;
|
||||
@ -104,32 +114,67 @@ class ASH_EXPORT HomeButton : public ShelfControlButton,
|
||||
// returned ScopedNoClipRect.
|
||||
[[nodiscard]] std::unique_ptr<ScopedNoClipRect> CreateScopedNoClipRect();
|
||||
|
||||
// Checks if the `nudge_label_` can be shown for the launcher nudge.
|
||||
// NOTE: This must be called after `CreateNudgeLabel()`, where the
|
||||
// `nudge_label_` is created. This is because whether the nudge can be shown
|
||||
// depends on nudge_label_'s preferred size.
|
||||
bool CanShowNudgeLabel() const;
|
||||
|
||||
// Starts the launcher nudge animation.
|
||||
void StartNudgeAnimation();
|
||||
|
||||
void AddNudgeAnimationObserverForTest(NudgeAnimationObserver* observer);
|
||||
void RemoveNudgeAnimationObserverForTest(NudgeAnimationObserver* observer);
|
||||
|
||||
views::View* label_container_for_test() const { return label_container_; }
|
||||
|
||||
protected:
|
||||
// views::Button:
|
||||
void PaintButtonContents(gfx::Canvas* canvas) override;
|
||||
void OnThemeChanged() override;
|
||||
|
||||
private:
|
||||
// Creates `nudge_label_` for launcher nudge.
|
||||
void CreateNudgeLabel();
|
||||
|
||||
// Animation functions for launcher nudge.
|
||||
void AnimateNudgeRipple(views::AnimationBuilder& builder);
|
||||
void AnimateNudgeBounce(views::AnimationBuilder& builder);
|
||||
void AnimateNudgeLabelSlideIn(views::AnimationBuilder& builder);
|
||||
void AnimateNudgeLabelSlideOut();
|
||||
void AnimateNudgeLabelFadeOut();
|
||||
|
||||
// Callbacks for the nudge animation.
|
||||
void OnNudgeAnimationStarted();
|
||||
void OnNudgeAnimationEnded();
|
||||
void OnLabelSlideInAnimationEnded();
|
||||
void OnLabelFadeOutAnimationEnded();
|
||||
|
||||
// Removes the nudge label from the view hierarchy.
|
||||
void RemoveNudgeLabel();
|
||||
|
||||
// views::ViewTargeterDelegate:
|
||||
bool DoesIntersectRect(const views::View* target,
|
||||
const gfx::Rect& rect) const override;
|
||||
|
||||
// Callback for the nudge animation.
|
||||
void OnNudgeAnimationStarted();
|
||||
void OnNudgeAnimationEnded();
|
||||
Shelf* const shelf_;
|
||||
|
||||
// The controller used to determine the button's behavior.
|
||||
HomeButtonController controller_;
|
||||
|
||||
// The ripple layer in the launcher nudge animation. Only exists during the
|
||||
// nudge animation.
|
||||
std::unique_ptr<ui::Layer> nudge_ripple_layer_;
|
||||
ui::LayerOwner nudge_ripple_layer_;
|
||||
|
||||
// The label view and for launcher nudge animation.
|
||||
views::Label* nudge_label_ = nullptr;
|
||||
|
||||
// The container of `nudge_label_`. This is also responsible for painting the
|
||||
// background of the label.
|
||||
views::View* label_container_ = nullptr;
|
||||
|
||||
// The timer that counts down to hide the nudge_label_ from showing state.
|
||||
base::OneShotTimer label_nudge_timer_;
|
||||
|
||||
// The delegate used by |nudge_ripple_layer_|. Only exists during the
|
||||
// nudge animation.
|
||||
|
@ -5,17 +5,22 @@
|
||||
#include "ash/shelf/launcher_nudge_controller.h"
|
||||
|
||||
#include "ash/app_list/app_list_controller_impl.h"
|
||||
#include "ash/app_list/test/app_list_test_helper.h"
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/constants/ash_pref_names.h"
|
||||
#include "ash/session/session_controller_impl.h"
|
||||
#include "ash/shelf/home_button.h"
|
||||
#include "ash/shelf/scrollable_shelf_view.h"
|
||||
#include "ash/shelf/shelf_controller.h"
|
||||
#include "ash/shelf/shelf_test_util.h"
|
||||
#include "ash/shelf/shelf_view_test_api.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
|
||||
#include "base/json/values_util.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/test/task_environment.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
|
||||
|
||||
namespace ash {
|
||||
@ -43,7 +48,23 @@ class TestNudgeAnimationObserver : public HomeButton::NudgeAnimationObserver {
|
||||
|
||||
DCHECK_EQ(started_animation_count_, ended_animation_count_ + 1);
|
||||
++ended_animation_count_;
|
||||
run_loop_.Quit();
|
||||
animation_run_loop_.Quit();
|
||||
}
|
||||
void NudgeLabelShown(HomeButton* home_button) override {
|
||||
if (home_button != home_button_)
|
||||
return;
|
||||
|
||||
label_run_loop_.Quit();
|
||||
}
|
||||
|
||||
void WaitUntilLabelShown() {
|
||||
ASSERT_TRUE(home_button_->CanShowNudgeLabel());
|
||||
DCHECK_GE(started_animation_count_, ended_animation_count_);
|
||||
if (started_animation_count_ == ended_animation_count_)
|
||||
return;
|
||||
|
||||
// Block the test to wait until the label is shown.
|
||||
label_run_loop_.Run();
|
||||
}
|
||||
|
||||
void WaitUntilAnimationEnded() {
|
||||
@ -52,14 +73,15 @@ class TestNudgeAnimationObserver : public HomeButton::NudgeAnimationObserver {
|
||||
return;
|
||||
|
||||
// Block the test to wait until the animation ended.
|
||||
run_loop_.Run();
|
||||
animation_run_loop_.Run();
|
||||
}
|
||||
|
||||
// Returns the number of finished animation on this home_button_.
|
||||
int GetShownCount() const { return ended_animation_count_; }
|
||||
|
||||
private:
|
||||
base::RunLoop run_loop_;
|
||||
base::RunLoop animation_run_loop_;
|
||||
base::RunLoop label_run_loop_;
|
||||
HomeButton* const home_button_;
|
||||
|
||||
// Counts the number of started/ended animations.
|
||||
@ -86,6 +108,13 @@ class LauncherNudgeControllerTest : public AshTestBase {
|
||||
nudge_controller_->SetClockForTesting(
|
||||
task_environment()->GetMockClock(),
|
||||
task_environment()->GetMockTickClock());
|
||||
|
||||
scrollable_shelf_view_ = GetPrimaryShelf()
|
||||
->shelf_widget()
|
||||
->hotseat_widget()
|
||||
->scrollable_shelf_view();
|
||||
test_api_ = std::make_unique<ShelfViewTestAPI>(
|
||||
scrollable_shelf_view_->shelf_view());
|
||||
}
|
||||
|
||||
// Advances the mock clock in the task environment and wait until it is idle.
|
||||
@ -103,8 +132,19 @@ class LauncherNudgeControllerTest : public AshTestBase {
|
||||
return LauncherNudgeController::GetShownCount(pref_service);
|
||||
}
|
||||
|
||||
void AddAppShortcut(int& id) {
|
||||
ShelfItem item = ShelfTestUtil::AddAppShortcut(base::NumberToString(id++),
|
||||
TYPE_PINNED_APP);
|
||||
|
||||
// Wait for shelf view's bounds animation to end. Otherwise the scrollable
|
||||
// shelf's bounds are not updated yet.
|
||||
test_api_->RunMessageLoopUntilAnimationsDone();
|
||||
}
|
||||
|
||||
LauncherNudgeController* nudge_controller_;
|
||||
std::unique_ptr<TestNudgeAnimationObserver> observer_;
|
||||
ScrollableShelfView* scrollable_shelf_view_ = nullptr;
|
||||
std::unique_ptr<ShelfViewTestAPI> test_api_;
|
||||
|
||||
private:
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
@ -304,4 +344,82 @@ TEST_F(LauncherNudgeControllerTest,
|
||||
EXPECT_EQ(2, GetNudgeShownCount());
|
||||
}
|
||||
|
||||
TEST_F(LauncherNudgeControllerTest, NudgeLabelVisibilityTest) {
|
||||
// Set the animation duration mode to non-zero for the launcher nudge
|
||||
// animation to actually run in the tests.
|
||||
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
|
||||
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
|
||||
SimulateNewUserFirstLogin("user@gmail.com");
|
||||
EXPECT_EQ(GetNudgeShownCount(), 0);
|
||||
|
||||
HomeButton* home_button = LauncherNudgeController::GetHomeButtonForDisplay(
|
||||
GetPrimaryDisplay().id());
|
||||
TestNudgeAnimationObserver waiter(home_button);
|
||||
AdvanceClock(nudge_controller_->GetNudgeInterval(/*is_first_time=*/true));
|
||||
|
||||
// Wait until the label to be shown and check if the label is visible.
|
||||
waiter.WaitUntilLabelShown();
|
||||
views::View* label_container = home_button->label_container_for_test();
|
||||
EXPECT_TRUE(label_container && label_container->GetVisible());
|
||||
EXPECT_EQ(label_container->layer()->opacity(), 1);
|
||||
|
||||
// Wait until the label is hidden.
|
||||
AdvanceClock(base::TimeDelta(base::Seconds(6)));
|
||||
waiter.WaitUntilAnimationEnded();
|
||||
EXPECT_FALSE(label_container->GetVisible());
|
||||
EXPECT_EQ(label_container->layer()->opacity(), 0);
|
||||
EXPECT_EQ(GetNudgeShownCount(), 1);
|
||||
|
||||
TestNudgeAnimationObserver waiter2(home_button);
|
||||
AdvanceClock(nudge_controller_->GetNudgeInterval(/*is_first_time=*/false) -
|
||||
base::Seconds(6));
|
||||
waiter2.WaitUntilLabelShown();
|
||||
EXPECT_TRUE(label_container->GetVisible());
|
||||
|
||||
gfx::Point center = label_container->GetBoundsInScreen().CenterPoint();
|
||||
GetEventGenerator()->MoveMouseTo(center);
|
||||
|
||||
// Click on the nudge label should toggle the app list.
|
||||
GetEventGenerator()->ClickLeftButton();
|
||||
GetAppListTestHelper()->WaitUntilIdle();
|
||||
GetAppListTestHelper()->CheckVisibility(true);
|
||||
|
||||
// Clicking on the nudge label should animate it. Wait until the animation
|
||||
// ends.
|
||||
waiter2.WaitUntilAnimationEnded();
|
||||
|
||||
// The label is removed after it is clicked.
|
||||
EXPECT_FALSE(home_button->label_container_for_test());
|
||||
}
|
||||
|
||||
TEST_F(LauncherNudgeControllerTest, AnimationUsedDependsOnAvailableSpace) {
|
||||
// Set the animation duration mode to non-zero for the launcher nudge
|
||||
// animation to actually run in the tests.
|
||||
ui::ScopedAnimationDurationScaleMode non_zero_duration_mode(
|
||||
ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
|
||||
SimulateNewUserFirstLogin("user@gmail.com");
|
||||
EXPECT_EQ(GetNudgeShownCount(), 0);
|
||||
|
||||
HomeButton* home_button = LauncherNudgeController::GetHomeButtonForDisplay(
|
||||
GetPrimaryDisplay().id());
|
||||
|
||||
// Advance the clock to trigger the animation and create the label nudge.
|
||||
AdvanceClock(nudge_controller_->GetNudgeInterval(/*is_first_time=*/true));
|
||||
|
||||
// Without adding anything to shelf, there should be enough space to show
|
||||
// nudge label.
|
||||
EXPECT_TRUE(home_button->CanShowNudgeLabel());
|
||||
|
||||
int id = 0;
|
||||
// Add app shortcuts until the hotseat overflow.
|
||||
while (scrollable_shelf_view_->layout_strategy_for_test() ==
|
||||
ScrollableShelfView::kNotShowArrowButtons) {
|
||||
AddAppShortcut(id);
|
||||
}
|
||||
|
||||
// If the apps overflow in shelf, there should be no space for the label to be
|
||||
// shown.
|
||||
EXPECT_FALSE(home_button->CanShowNudgeLabel());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -825,18 +825,24 @@ gfx::Size ShelfNavigationWidget::CalculateIdealSize(
|
||||
if (!ShelfConfig::Get()->shelf_controls_shown())
|
||||
return gfx::Size();
|
||||
|
||||
int control_button_number;
|
||||
int controls_space = 0;
|
||||
const int control_size = ShelfConfig::Get()->control_size();
|
||||
|
||||
if (Shell::Get()->IsInTabletMode() && !only_visible_area) {
|
||||
// There are home button and back button. So the maximum is 2.
|
||||
control_button_number = 2;
|
||||
controls_space = control_size * 2 + ShelfConfig::Get()->button_spacing();
|
||||
} else {
|
||||
control_button_number = CalculateButtonCount();
|
||||
// Use CalculatePreferredSize here to take the launcher nudge label into
|
||||
// consider.
|
||||
controls_space += IsHomeButtonShown()
|
||||
? GetHomeButton()->CalculatePreferredSize().width()
|
||||
: 0;
|
||||
controls_space +=
|
||||
IsBackButtonShown(shelf_->IsHorizontalAlignment()) ? control_size : 0;
|
||||
controls_space +=
|
||||
(CalculateButtonCount() - 1) * ShelfConfig::Get()->button_spacing();
|
||||
}
|
||||
|
||||
const int control_size = ShelfConfig::Get()->control_size();
|
||||
int controls_space =
|
||||
control_button_number * control_size +
|
||||
(control_button_number - 1) * ShelfConfig::Get()->button_spacing();
|
||||
const int major_axis_spacing =
|
||||
2 * ShelfConfig::Get()->control_button_edge_spacing(
|
||||
shelf_->IsHorizontalAlignment());
|
||||
|
Reference in New Issue
Block a user