12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295 |
- // Copyright 2019 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/shelf/hotseat_widget.h"
- #include <memory>
- #include <utility>
- #include "ash/constants/ash_features.h"
- #include "ash/focus_cycler.h"
- #include "ash/keyboard/ui/keyboard_ui_controller.h"
- #include "ash/public/cpp/shelf_config.h"
- #include "ash/public/cpp/shelf_model.h"
- #include "ash/public/cpp/shelf_types.h"
- #include "ash/public/cpp/wallpaper/wallpaper_controller_observer.h"
- #include "ash/shelf/hotseat_transition_animator.h"
- #include "ash/shelf/scrollable_shelf_view.h"
- #include "ash/shelf/shelf_app_button.h"
- #include "ash/shelf/shelf_layout_manager.h"
- #include "ash/shelf/shelf_navigation_widget.h"
- #include "ash/shelf/shelf_view.h"
- #include "ash/shell.h"
- #include "ash/style/system_shadow.h"
- #include "ash/system/status_area_widget.h"
- #include "ash/wallpaper/wallpaper_controller_impl.h"
- #include "ash/wm/overview/overview_controller.h"
- #include "ash/wm/overview/overview_observer.h"
- #include "ash/wm/tablet_mode/tablet_mode_controller.h"
- #include "base/i18n/rtl.h"
- #include "base/metrics/histogram_macros.h"
- #include "ui/aura/scoped_window_targeter.h"
- #include "ui/aura/window_targeter.h"
- #include "ui/compositor/animation_throughput_reporter.h"
- #include "ui/compositor/layer_animation_sequence.h"
- #include "ui/compositor/scoped_layer_animation_settings.h"
- #include "ui/gfx/color_analysis.h"
- #include "ui/gfx/color_palette.h"
- #include "ui/gfx/color_utils.h"
- #include "ui/gfx/geometry/rounded_corners_f.h"
- #include "ui/views/background.h"
- #include "ui/views/highlight_border.h"
- #include "ui/views/layout/fill_layout.h"
- #include "ui/views/view_targeter_delegate.h"
- #include "ui/views/widget/widget_delegate.h"
- namespace ash {
- namespace {
- void DoScopedAnimationSetting(
- ui::ScopedLayerAnimationSettings* animation_setter) {
- animation_setter->SetTransitionDuration(
- ShelfConfig::Get()->shelf_animation_duration());
- animation_setter->SetTweenType(gfx::Tween::EASE_OUT);
- animation_setter->SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- }
- // Returns whether there is special hotseat animation for |transition|.
- bool HasSpecialAnimation(HotseatWidget::StateTransition transition) {
- switch (transition) {
- case HotseatWidget::StateTransition::kHomeLauncherAndExtended:
- case HotseatWidget::StateTransition::kHomeLauncherAndHidden:
- return true;
- case HotseatWidget::StateTransition::kHiddenAndExtended:
- case HotseatWidget::StateTransition::kOther:
- return false;
- }
- }
- // Calculates the state transition type for the given previous state and
- // the target state.
- HotseatWidget::StateTransition CalculateHotseatStateTransition(
- HotseatState previous_state,
- HotseatState target_state) {
- if (previous_state == HotseatState::kNone ||
- target_state == HotseatState::kNone) {
- return HotseatWidget::StateTransition::kOther;
- }
- if (previous_state == target_state)
- return HotseatWidget::StateTransition::kOther;
- const bool related_to_homelauncher =
- (previous_state == HotseatState::kShownHomeLauncher ||
- target_state == HotseatState::kShownHomeLauncher);
- const bool related_to_extended = (previous_state == HotseatState::kExtended ||
- target_state == HotseatState::kExtended);
- const bool related_to_hidden = (previous_state == HotseatState::kHidden ||
- target_state == HotseatState::kHidden);
- if (related_to_homelauncher && related_to_extended)
- return HotseatWidget::StateTransition::kHomeLauncherAndExtended;
- if (related_to_homelauncher && related_to_hidden)
- return HotseatWidget::StateTransition::kHomeLauncherAndHidden;
- if (related_to_extended && related_to_hidden)
- return HotseatWidget::StateTransition::kHiddenAndExtended;
- return HotseatWidget::StateTransition::kOther;
- }
- // Base class for hotseat animation transition.
- class HotseatStateTransitionAnimation : public ui::LayerAnimationElement {
- public:
- HotseatStateTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
- double target_opacity,
- ui::Layer* hotseat_layer,
- HotseatWidget* hotseat_widget)
- : ui::LayerAnimationElement(
- LayerAnimationElement::BOUNDS | LayerAnimationElement::OPACITY,
- hotseat_layer->GetAnimator()->GetTransitionDuration()),
- target_widget_bounds_(target_bounds_in_screen),
- target_opacity_(target_opacity),
- tween_type_(hotseat_layer->GetAnimator()->tween_type()),
- hotseat_widget_(hotseat_widget) {}
- ~HotseatStateTransitionAnimation() override = default;
- HotseatStateTransitionAnimation(const HotseatStateTransitionAnimation& rhs) =
- delete;
- HotseatStateTransitionAnimation& operator=(
- const HotseatStateTransitionAnimation& rhs) = delete;
- protected:
- // ui::LayerAnimationElement:
- void OnGetTarget(TargetValue* target) const override {
- target->opacity = target_opacity_;
- target->bounds = target_widget_bounds_;
- }
- ScrollableShelfView* GetScrollableShelfView() {
- return hotseat_widget_->scrollable_shelf_view();
- }
- // Hotseat widget's target bounds in screen.
- gfx::Rect target_widget_bounds_;
- // Hotseat widget's initial opacity.
- double start_opacity_ = 0.f;
- // Hotseat widget's target opacity.
- double target_opacity_ = 0.f;
- gfx::Tween::Type tween_type_ = gfx::Tween::LINEAR;
- HotseatWidget* hotseat_widget_ = nullptr;
- };
- // Animation implemented specifically for the transition between the home
- // launcher state and the extended state.
- class HomeAndExtendedTransitionAnimation
- : public HotseatStateTransitionAnimation {
- public:
- HomeAndExtendedTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
- double target_opacity,
- ui::Layer* hotseat_layer,
- HotseatWidget* hotseat_widget)
- : HotseatStateTransitionAnimation(target_bounds_in_screen,
- target_opacity,
- hotseat_layer,
- hotseat_widget) {}
- ~HomeAndExtendedTransitionAnimation() override = default;
- HomeAndExtendedTransitionAnimation(
- const HomeAndExtendedTransitionAnimation& rhs) = delete;
- HomeAndExtendedTransitionAnimation& operator=(
- const HomeAndExtendedTransitionAnimation& rhs) = delete;
- private:
- // HotseatStateTransitionAnimation:
- void OnStart(ui::LayerAnimationDelegate* delegate) override {
- DCHECK(hotseat_widget_->GetShelfView()->shelf()->IsHorizontalAlignment());
- ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
- scrollable_shelf_view->set_is_padding_configured_externally(
- /*is_padding_configured_externally=*/true);
- // Save initial and target padding insets.
- initial_padding_insets_ = scrollable_shelf_view->edge_padding_insets();
- target_padding_insets_ =
- scrollable_shelf_view->CalculateMirroredEdgePadding(
- /*use_target_bounds=*/true);
- // Save initial opacity.
- start_opacity_ = hotseat_widget_->GetNativeView()->layer()->opacity();
- // Save initial hotseat background bounds.
- initial_hotseat_background_in_screen_ =
- hotseat_widget_->GetWindowBoundsInScreen();
- initial_hotseat_background_in_screen_.Inset(initial_padding_insets_);
- // Save target hotseat background bounds.
- target_hotseat_background_in_screen_ = target_widget_bounds_;
- target_hotseat_background_in_screen_.Inset(target_padding_insets_);
- }
- // HotseatStateTransitionAnimation:
- bool OnProgress(double current,
- ui::LayerAnimationDelegate* delegate) override {
- const double tweened = gfx::Tween::CalculateValue(tween_type_, current);
- // Set scrollable shelf view's padding insets.
- gfx::Insets insets_in_animation_progress;
- insets_in_animation_progress.set_left(gfx::Tween::LinearIntValueBetween(
- tweened, initial_padding_insets_.left(),
- target_padding_insets_.left()));
- insets_in_animation_progress.set_right(gfx::Tween::LinearIntValueBetween(
- tweened, initial_padding_insets_.right(),
- target_padding_insets_.right()));
- ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
- scrollable_shelf_view->SetEdgePaddingInsets(insets_in_animation_progress);
- // Update hotseat widget opacity.
- delegate->SetOpacityFromAnimation(
- gfx::Tween::DoubleValueBetween(tweened, start_opacity_,
- target_opacity_),
- ui::PropertyChangeReason::FROM_ANIMATION);
- // Calculate the hotseat widget's bounds.
- const gfx::Rect hotseat_background_in_progress =
- gfx::Tween::RectValueBetween(tweened,
- initial_hotseat_background_in_screen_,
- target_hotseat_background_in_screen_);
- gfx::Rect widget_bounds_in_progress = hotseat_background_in_progress;
- widget_bounds_in_progress.Inset(
- -scrollable_shelf_view->edge_padding_insets());
- // Update hotseat widget bounds.
- delegate->SetBoundsFromAnimation(widget_bounds_in_progress,
- ui::PropertyChangeReason::FROM_ANIMATION);
- // Do recovering when the animation ends.
- if (current == 1.f) {
- scrollable_shelf_view->set_is_padding_configured_externally(
- /*is_padding_configured_externally=*/false);
- }
- return true;
- }
- // HotseatStateTransitionAnimation:
- void OnAbort(ui::LayerAnimationDelegate* delegate) override {
- GetScrollableShelfView()->set_is_padding_configured_externally(
- /*is_padding_configured_externally=*/false);
- }
- // Scrollable shelf's initial padding insets.
- gfx::Insets initial_padding_insets_;
- // Scrollable shelf's target padding insets.
- gfx::Insets target_padding_insets_;
- // Hotseat background's initial bounds in screen.
- gfx::Rect initial_hotseat_background_in_screen_;
- // Hotseat background's target bounds in screen.
- gfx::Rect target_hotseat_background_in_screen_;
- };
- // Animation implemented specifically for the transition between the home
- // launcher state and the hidden state.
- class HomeAndHiddenTransitionAnimation
- : public HotseatStateTransitionAnimation {
- public:
- HomeAndHiddenTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
- double target_opacity,
- ui::Layer* hotseat_layer,
- HotseatWidget* hotseat_widget)
- : HotseatStateTransitionAnimation(target_bounds_in_screen,
- target_opacity,
- hotseat_layer,
- hotseat_widget) {}
- ~HomeAndHiddenTransitionAnimation() override = default;
- protected:
- // HotseatStateTransitionAnimation:
- void OnStart(ui::LayerAnimationDelegate* delegate) override {
- DCHECK(hotseat_widget_->GetShelfView()->shelf()->IsHorizontalAlignment());
- start_opacity_ = hotseat_widget_->GetNativeView()->layer()->opacity();
- if (hotseat_widget_->state() == HotseatState::kHidden)
- will_be_hidden_ = true;
- ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
- const gfx::Rect current_widget_bounds(
- hotseat_widget_->GetWindowBoundsInScreen());
- // Ensure that hotseat only has vertical movement during animation.
- if (will_be_hidden_) {
- animation_initial_bounds_ = current_widget_bounds;
- animation_target_bounds_ = current_widget_bounds;
- animation_target_bounds_.set_y(target_widget_bounds_.y());
- } else {
- animation_initial_bounds_ = target_widget_bounds_;
- animation_initial_bounds_.set_y(current_widget_bounds.y());
- // Ensure that hotseat is set with the target bounds at the end of
- // animation when hotseat is going to show in home launcher.
- animation_target_bounds_ = target_widget_bounds_;
- const gfx::Insets target_padding_insets =
- scrollable_shelf_view->CalculateMirroredEdgePadding(
- /*use_target_bounds=*/true);
- scrollable_shelf_view->SetEdgePaddingInsets(target_padding_insets);
- delegate->SetBoundsFromAnimation(
- animation_initial_bounds_, ui::PropertyChangeReason::FROM_ANIMATION);
- }
- }
- // HotseatStateTransitionAnimation:
- bool OnProgress(double current,
- ui::LayerAnimationDelegate* delegate) override {
- const double tweened = gfx::Tween::CalculateValue(tween_type_, current);
- delegate->SetOpacityFromAnimation(
- gfx::Tween::DoubleValueBetween(tweened, start_opacity_,
- target_opacity_),
- ui::PropertyChangeReason::FROM_ANIMATION);
- const gfx::Rect widget_bounds_in_progress = gfx::Tween::RectValueBetween(
- tweened, animation_initial_bounds_, animation_target_bounds_);
- const bool reach_end = current == 1.f;
- // When hotseat is going to be hidden, |animation_target_bounds_| is not
- // equal to |target_widget_bounds_|. So hotseat is set with the target
- // bounds at the end of animation. It does not bring animation regression
- // since hotseat is invisible to the user when setting bounds.
- delegate->SetBoundsFromAnimation(will_be_hidden_ && reach_end
- ? target_widget_bounds_
- : widget_bounds_in_progress,
- ui::PropertyChangeReason::FROM_ANIMATION);
- return true;
- }
- // HotseatStateTransitionAnimation:
- void OnAbort(ui::LayerAnimationDelegate* delegate) override {}
- private:
- // Whether hotseat widget is hidden after state transition animation.
- bool will_be_hidden_ = false;
- // Note that |animation_initial_bounds_| and |animation_target_bounds_| may
- // not be the hotseat's current bounds and |target_widget_bounds_|
- // respectively.
- gfx::Rect animation_initial_bounds_;
- gfx::Rect animation_target_bounds_;
- };
- // Custom window targeter for the hotseat. Used so the hotseat only processes
- // events that land on the visible portion of the hotseat, and only while the
- // hotseat is not animating.
- class HotseatWindowTargeter : public aura::WindowTargeter {
- public:
- explicit HotseatWindowTargeter(HotseatWidget* hotseat_widget)
- : hotseat_widget_(hotseat_widget) {}
- ~HotseatWindowTargeter() override = default;
- HotseatWindowTargeter(const HotseatWindowTargeter& other) = delete;
- HotseatWindowTargeter& operator=(const HotseatWindowTargeter& rhs) = delete;
- // aura::WindowTargeter:
- bool SubtreeShouldBeExploredForEvent(aura::Window* window,
- const ui::LocatedEvent& event) override {
- // Do not handle events if the hotseat window is animating as it may animate
- // over other items which want to process events.
- if (hotseat_widget_->GetLayer()->GetAnimator()->is_animating())
- return false;
- return aura::WindowTargeter::SubtreeShouldBeExploredForEvent(window, event);
- }
- bool GetHitTestRects(aura::Window* target,
- gfx::Rect* hit_test_rect_mouse,
- gfx::Rect* hit_test_rect_touch) const override {
- if (target == hotseat_widget_->GetNativeWindow()) {
- // Shrink the hit bounds from the size of the window to the size of the
- // hotseat translucent background.
- gfx::Rect hit_bounds = target->bounds();
- hit_bounds.ClampToCenteredSize(
- hotseat_widget_->GetTranslucentBackgroundSize());
- *hit_test_rect_mouse = *hit_test_rect_touch = hit_bounds;
- return true;
- }
- return aura::WindowTargeter::GetHitTestRects(target, hit_test_rect_mouse,
- hit_test_rect_touch);
- }
- private:
- // Unowned and guaranteed to be not null for the duration of |this|.
- HotseatWidget* const hotseat_widget_;
- };
- } // namespace
- class HotseatWidget::DelegateView : public HotseatTransitionAnimator::Observer,
- public views::WidgetDelegateView,
- public views::ViewTargeterDelegate,
- public OverviewObserver,
- public WallpaperControllerObserver {
- public:
- DelegateView() {
- SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
- SetPaintToLayer(ui::LAYER_NOT_DRAWN);
- }
- DelegateView(const DelegateView&) = delete;
- DelegateView& operator=(const DelegateView&) = delete;
- ~DelegateView() override;
- // views::ViewTargetDelegate:
- View* TargetForRect(View* root, const gfx::Rect& rect) override {
- // If a context menu for a shelf app button is shown, redirect all events to
- // the shelf app button. Context menus generally capture all events, but
- // shelf app buttons' context menu redirect gesture events to the hotseat
- // widget so shelf app button can continue handling drag events.
- // See also HotseatWidget::OnGestureEvent().
- views::View* item_with_context_menu =
- scrollable_shelf_view_->shelf_view()->GetShelfItemViewWithContextMenu();
- if (item_with_context_menu)
- return item_with_context_menu;
- return views::ViewTargeterDelegate::TargetForRect(root, rect);
- }
- // Initializes the view.
- void Init(ScrollableShelfView* scrollable_shelf_view,
- HotseatWidget* hotseat_widget);
- // Updates the hotseat background.
- void UpdateTranslucentBackground();
- void SetTranslucentBackground(const gfx::Rect& translucent_background_bounds);
- // Sets whether the background should be blurred as requested by the argument,
- // unless the feature flag is disabled or |disable_blur_for_animations_| is
- // true, in which case this disables background blur.
- void SetBackgroundBlur(bool enable_blur);
- // HotseatTransitionAnimator::Observer:
- void OnHotseatTransitionAnimationWillStart(HotseatState from_state,
- HotseatState to_state) override;
- void OnHotseatTransitionAnimationEnded(HotseatState from_state,
- HotseatState to_state) override;
- void OnHotseatTransitionAnimationAborted() override;
- // views::View:
- void OnThemeChanged() override;
- // views::WidgetDelegateView:
- bool CanActivate() const override;
- // OverviewObserver:
- void OnOverviewModeWillStart() override;
- void OnOverviewModeEndingAnimationComplete(bool canceled) override;
- // WallpaperControllerObserver:
- void OnWallpaperColorsChanged() override;
- void set_focus_cycler(FocusCycler* focus_cycler) {
- focus_cycler_ = focus_cycler;
- }
- int background_blur() const {
- return translucent_background_->layer()->background_blur();
- }
- bool is_translucent_background_visible_for_test() {
- return translucent_background_->layer()->GetTargetVisibility();
- }
- private:
- FocusCycler* focus_cycler_ = nullptr;
- // A background layer that may be visible depending on HotseatState.
- views::View* translucent_background_ = nullptr;
- ScrollableShelfView* scrollable_shelf_view_ = nullptr; // unowned.
- HotseatWidget* hotseat_widget_ = nullptr; // unowned.
- // Blur is disabled during animations to improve performance.
- int blur_lock_ = 0;
- // The most recent color that the |translucent_background_| has been animated
- // to.
- SkColor target_color_ = SK_ColorTRANSPARENT;
- std::unique_ptr<SystemShadow> shadow_;
- };
- HotseatWidget::DelegateView::~DelegateView() {
- WallpaperControllerImpl* wallpaper_controller =
- Shell::Get()->wallpaper_controller();
- OverviewController* overview_controller = Shell::Get()->overview_controller();
- if (wallpaper_controller)
- wallpaper_controller->RemoveObserver(this);
- if (overview_controller)
- overview_controller->RemoveObserver(this);
- }
- void HotseatWidget::DelegateView::Init(
- ScrollableShelfView* scrollable_shelf_view,
- HotseatWidget* hotseat_widget) {
- hotseat_widget_ = hotseat_widget;
- SetLayoutManager(std::make_unique<views::FillLayout>());
- WallpaperControllerImpl* wallpaper_controller =
- Shell::Get()->wallpaper_controller();
- OverviewController* overview_controller = Shell::Get()->overview_controller();
- if (wallpaper_controller)
- wallpaper_controller->AddObserver(this);
- if (overview_controller) {
- overview_controller->AddObserver(this);
- if (overview_controller->InOverviewSession())
- ++blur_lock_;
- }
- DCHECK(scrollable_shelf_view);
- scrollable_shelf_view_ = scrollable_shelf_view;
- // A container view added here is to prevent the `translucent_background_`
- // being stretched by the fill layout.
- auto* background_container_view =
- AddChildViewAt(std::make_unique<views::View>(), 0);
- background_container_view->SetEnabled(false);
- translucent_background_ =
- background_container_view->AddChildView(std::make_unique<views::View>());
- if (features::IsDarkLightModeEnabled()) {
- translucent_background_->SetPaintToLayer();
- translucent_background_->layer()->SetFillsBoundsOpaquely(false);
- } else {
- translucent_background_->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
- }
- translucent_background_->layer()->SetName("hotseat/Background");
- // Create a shadow and stack at the bottom.
- shadow_ = SystemShadow::CreateShadowOnTextureLayer(
- SystemShadow::Type::kElevation12);
- auto* parent_layer = translucent_background_->layer()->parent();
- auto* shadow_layer = shadow_->GetLayer();
- parent_layer->Add(shadow_layer);
- parent_layer->StackAtBottom(shadow_layer);
- }
- void HotseatWidget::DelegateView::UpdateTranslucentBackground() {
- if (!HotseatWidget::ShouldShowHotseatBackground()) {
- translucent_background_->SetVisible(false);
- SetBackgroundBlur(false);
- shadow_->GetLayer()->SetVisible(false);
- return;
- }
- DCHECK(scrollable_shelf_view_);
- SetTranslucentBackground(
- scrollable_shelf_view_->GetHotseatBackgroundBounds());
- // Hide the shadow when home launcher is showing in tablet mode.
- if (hotseat_widget_->state() == HotseatState::kShownHomeLauncher) {
- shadow_->GetLayer()->SetVisible(false);
- return;
- }
- // Update the shadow content bounds and corner radius.
- shadow_->GetLayer()->SetVisible(true);
- gfx::Rect background_bounds = translucent_background_->bounds();
- shadow_->SetRoundedCornerRadius(background_bounds.height() / 2);
- shadow_->SetContentBounds(background_bounds);
- }
- void HotseatWidget::DelegateView::SetTranslucentBackground(
- const gfx::Rect& background_bounds) {
- DCHECK(HotseatWidget::ShouldShowHotseatBackground());
- translucent_background_->SetVisible(true);
- SetBackgroundBlur(/*enable_blur=*/true);
- auto* animator = translucent_background_->layer()->GetAnimator();
- absl::optional<ui::AnimationThroughputReporter> reporter;
- if (hotseat_widget_ && hotseat_widget_->state() != HotseatState::kNone) {
- reporter.emplace(animator,
- hotseat_widget_->GetTranslucentBackgroundReportCallback());
- }
- if (ShelfConfig::Get()->GetDefaultShelfColor() != target_color_) {
- ui::ScopedLayerAnimationSettings color_animation_setter(animator);
- DoScopedAnimationSetting(&color_animation_setter);
- target_color_ = ShelfConfig::Get()->GetDefaultShelfColor();
- if (features::IsDarkLightModeEnabled()) {
- translucent_background_->SetBackground(
- views::CreateSolidBackground(target_color_));
- } else {
- translucent_background_->layer()->SetColor(target_color_);
- }
- }
- // Animate the bounds change if there's a change of width (for instance when
- // dragging an app into, or out of, the shelf) and meanwhile scrollable
- // shelf's bounds does not update at the same time.
- const bool animate_bounds =
- background_bounds.width() != translucent_background_->bounds().width() &&
- (scrollable_shelf_view_ &&
- !scrollable_shelf_view_->NeedUpdateToTargetBounds());
- absl::optional<ui::ScopedLayerAnimationSettings> bounds_animation_setter;
- if (animate_bounds) {
- bounds_animation_setter.emplace(animator);
- DoScopedAnimationSetting(&bounds_animation_setter.value());
- }
- const float radius = hotseat_widget_->GetHotseatSize() / 2.0f;
- gfx::RoundedCornersF rounded_corners = {radius, radius, radius, radius};
- if (translucent_background_->layer()->rounded_corner_radii() !=
- rounded_corners) {
- translucent_background_->layer()->SetRoundedCornerRadius(rounded_corners);
- if (features::IsDarkLightModeEnabled()) {
- translucent_background_->SetBorder(
- std::make_unique<views::HighlightBorder>(
- radius, views::HighlightBorder::Type::kHighlightBorder1,
- /*use_light_colors=*/!features::IsDarkLightModeEnabled()));
- }
- }
- const gfx::Rect mirrored_bounds = GetMirroredRect(background_bounds);
- if (translucent_background_->layer()->GetTargetBounds() != mirrored_bounds)
- translucent_background_->SetBoundsRect(mirrored_bounds);
- }
- void HotseatWidget::DelegateView::SetBackgroundBlur(bool enable_blur) {
- if (!features::IsBackgroundBlurEnabled() || blur_lock_ > 0)
- return;
- const int blur_radius =
- enable_blur ? ShelfConfig::Get()->shelf_blur_radius() : 0;
- if (translucent_background_->layer()->background_blur() != blur_radius)
- translucent_background_->layer()->SetBackgroundBlur(blur_radius);
- }
- void HotseatWidget::DelegateView::OnHotseatTransitionAnimationWillStart(
- HotseatState from_state,
- HotseatState to_state) {
- DCHECK_LE(blur_lock_, 2);
- SetBackgroundBlur(false);
- ++blur_lock_;
- }
- void HotseatWidget::DelegateView::OnHotseatTransitionAnimationEnded(
- HotseatState from_state,
- HotseatState to_state) {
- DCHECK_GT(blur_lock_, 0);
- --blur_lock_;
- SetBackgroundBlur(true);
- }
- void HotseatWidget::DelegateView::OnHotseatTransitionAnimationAborted() {
- DCHECK_GT(blur_lock_, 0);
- --blur_lock_;
- }
- void HotseatWidget::DelegateView::OnThemeChanged() {
- views::WidgetDelegateView::OnThemeChanged();
- // Only update the background when the `scrollable_shelf_view_` is
- // initialized.
- if (scrollable_shelf_view_)
- UpdateTranslucentBackground();
- }
- bool HotseatWidget::DelegateView::CanActivate() const {
- // We don't want mouse clicks to activate us, but we need to allow
- // activation when the user is using the keyboard (FocusCycler).
- return focus_cycler_ && focus_cycler_->widget_activating() == GetWidget();
- }
- void HotseatWidget::DelegateView::OnOverviewModeWillStart() {
- DCHECK_LE(blur_lock_, 2);
- SetBackgroundBlur(false);
- ++blur_lock_;
- }
- void HotseatWidget::DelegateView::OnOverviewModeEndingAnimationComplete(
- bool canceled) {
- DCHECK_GT(blur_lock_, 0);
- --blur_lock_;
- SetBackgroundBlur(true);
- }
- void HotseatWidget::DelegateView::OnWallpaperColorsChanged() {
- UpdateTranslucentBackground();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ScopedInStateTransition
- HotseatWidget::ScopedInStateTransition::ScopedInStateTransition(
- HotseatWidget* hotseat_widget,
- HotseatState old_state,
- HotseatState target_state)
- : hotseat_widget_(hotseat_widget) {
- hotseat_widget_->state_transition_in_progress_ =
- CalculateHotseatStateTransition(old_state, target_state);
- }
- HotseatWidget::ScopedInStateTransition::~ScopedInStateTransition() {
- hotseat_widget_->state_transition_in_progress_.reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // HotseatWidget
- HotseatWidget::HotseatWidget() : delegate_view_(new DelegateView()) {
- ShelfConfig::Get()->AddObserver(this);
- }
- HotseatWidget::~HotseatWidget() {
- ui::LayerAnimator* hotseat_layer_animator =
- GetNativeView()->layer()->GetAnimator();
- if (hotseat_layer_animator->is_animating())
- hotseat_layer_animator->AbortAllAnimations();
- ShelfConfig::Get()->RemoveObserver(this);
- shelf_->shelf_widget()->hotseat_transition_animator()->RemoveObserver(
- delegate_view_);
- // Remove ScrollableShelfView to avoid any children accessing NativeWidget
- // after its destruction in ~Widget() before RootView clears.
- // TODO(pbos): This is defensive, consider having children observe
- // destruction and/or check the result of GetNativeWidget() and others.
- GetContentsView()->RemoveChildViewT(scrollable_shelf_view_);
- }
- bool HotseatWidget::ShouldShowHotseatBackground() {
- return Shell::Get()->tablet_mode_controller() &&
- Shell::Get()->tablet_mode_controller()->InTabletMode();
- }
- void HotseatWidget::Initialize(aura::Window* container, Shelf* shelf) {
- DCHECK(container);
- DCHECK(shelf);
- shelf_ = shelf;
- views::Widget::InitParams params(
- views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- params.name = "HotseatWidget";
- params.delegate = delegate_view_;
- params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
- params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
- params.parent = container;
- params.layer_type = ui::LAYER_NOT_DRAWN;
- Init(std::move(params));
- set_focus_on_creation(false);
- scrollable_shelf_view_ = GetContentsView()->AddChildView(
- std::make_unique<ScrollableShelfView>(ShelfModel::Get(), shelf));
- delegate_view_->Init(scrollable_shelf_view(), this);
- delegate_view_->SetEnableArrowKeyTraversal(true);
- // The initialization of scrollable shelf should update the translucent
- // background which is stored in |delegate_view_|. So initializes
- // |scrollable_shelf_view_| after |delegate_view_|.
- scrollable_shelf_view_->Init();
- }
- void HotseatWidget::OnHotseatTransitionAnimatorCreated(
- HotseatTransitionAnimator* animator) {
- shelf_->shelf_widget()->hotseat_transition_animator()->AddObserver(
- delegate_view_);
- }
- void HotseatWidget::OnMouseEvent(ui::MouseEvent* event) {
- if (event->type() == ui::ET_MOUSE_PRESSED)
- keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
- views::Widget::OnMouseEvent(event);
- }
- void HotseatWidget::OnGestureEvent(ui::GestureEvent* event) {
- if (event->type() == ui::ET_GESTURE_TAP_DOWN)
- keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
- // Context menus for shelf app button forward gesture events to hotseat
- // widget, so the shelf app button can continue handling drag even after the
- // context menu starts capturing events. Ignore events not interesting to the
- // shelf app button in this state.
- ShelfAppButton* item_with_context_menu =
- GetShelfView()->GetShelfItemViewWithContextMenu();
- if (item_with_context_menu &&
- !ShelfAppButton::ShouldHandleEventFromContextMenu(event)) {
- event->SetHandled();
- return;
- }
- if (!event->handled())
- views::Widget::OnGestureEvent(event);
- // Ensure that the app button's drag state gets cleared on gesture end even if
- // the event doesn't get delivered to the app button.
- if (item_with_context_menu && event->type() == ui::ET_GESTURE_END)
- item_with_context_menu->ClearDragStateOnGestureEnd();
- }
- bool HotseatWidget::OnNativeWidgetActivationChanged(bool active) {
- if (!Widget::OnNativeWidgetActivationChanged(active))
- return false;
- scrollable_shelf_view_->OnFocusRingActivationChanged(active);
- return true;
- }
- void HotseatWidget::OnShelfConfigUpdated() {
- set_manually_extended(false);
- }
- bool HotseatWidget::IsExtended() const {
- DCHECK(GetShelfView()->shelf()->IsHorizontalAlignment());
- const int extended_bottom =
- display::Screen::GetScreen()
- ->GetDisplayNearestView(GetShelfView()->GetWidget()->GetNativeView())
- .bounds()
- .bottom() -
- (ShelfConfig::Get()->shelf_size() +
- ShelfConfig::Get()->hotseat_bottom_padding());
- return GetWindowBoundsInScreen().bottom() == extended_bottom;
- }
- void HotseatWidget::FocusFirstOrLastFocusableChild(bool last) {
- GetShelfView()->FindFirstOrLastFocusableChild(last)->RequestFocus();
- }
- void HotseatWidget::OnTabletModeChanged() {
- GetShelfView()->OnTabletModeChanged();
- }
- float HotseatWidget::CalculateShelfViewOpacity() const {
- const float target_opacity =
- GetShelfView()->shelf()->shelf_layout_manager()->GetOpacity();
- // Hotseat's shelf view should not be dimmed if hotseat is kExtended.
- return (state() == HotseatState::kExtended) ? 1.0f : target_opacity;
- }
- void HotseatWidget::UpdateTranslucentBackground() {
- delegate_view_->UpdateTranslucentBackground();
- }
- int HotseatWidget::CalculateHotseatYInScreen(
- HotseatState hotseat_target_state) const {
- DCHECK(shelf_->IsHorizontalAlignment());
- int hotseat_distance_from_bottom_of_display = 0;
- const int hotseat_size = GetHotseatSize();
- switch (hotseat_target_state) {
- case HotseatState::kShownClamshell:
- hotseat_distance_from_bottom_of_display = hotseat_size;
- break;
- case HotseatState::kShownHomeLauncher:
- // When the hotseat state is HotseatState::kShownHomeLauncher, the home
- // launcher is showing in tablet mode. Elevate the hotseat a few px to
- // match the navigation and status area.
- hotseat_distance_from_bottom_of_display =
- hotseat_size + ShelfConfig::Get()->hotseat_bottom_padding();
- break;
- case HotseatState::kHidden:
- // Show the hotseat offscreen.
- hotseat_distance_from_bottom_of_display = 0;
- break;
- case HotseatState::kExtended:
- // Show the hotseat at its extended position.
- hotseat_distance_from_bottom_of_display =
- ShelfConfig::Get()->in_app_shelf_size() +
- ShelfConfig::Get()->hotseat_bottom_padding() + hotseat_size;
- break;
- case HotseatState::kNone:
- NOTREACHED();
- }
- const int target_shelf_size =
- shelf_->shelf_widget()->GetTargetBounds().size().height();
- const int hotseat_y_in_shelf =
- -(hotseat_distance_from_bottom_of_display - target_shelf_size);
- const int shelf_y = shelf_->shelf_widget()->GetTargetBounds().y();
- return hotseat_y_in_shelf + shelf_y;
- }
- gfx::Size HotseatWidget::CalculateTargetBoundsSize(
- HotseatState hotseat_target_state) const {
- const gfx::Rect shelf_bounds = shelf_->shelf_widget()->GetTargetBounds();
- // |hotseat_size| is the height in horizontal alignment or the width in
- // vertical alignment.
- const int hotseat_size = GetHotseatSize();
- if (hotseat_target_state != HotseatState::kShownHomeLauncher &&
- hotseat_target_state != HotseatState::kShownClamshell) {
- DCHECK(shelf_->IsHorizontalAlignment());
- // Give the hotseat more space if it is shown outside of the shelf.
- return gfx::Size(shelf_bounds.width(), hotseat_size);
- }
- const gfx::Size status_size =
- shelf_->status_area_widget()->GetTargetBounds().size();
- const gfx::Rect nav_bounds = shelf_->navigation_widget()->GetVisibleBounds();
- // The navigation widget has extra padding on the hotseat side, to center the
- // buttons inside of it. Make sure to get the extra nav widget padding and
- // take it into account when calculating the hotseat size.
- const int nav_widget_padding =
- nav_bounds.size().IsEmpty()
- ? 0
- : ShelfConfig::Get()->control_button_edge_spacing(
- true /* is_primary_axis_edge */);
- // The minimum gap between hotseat widget and other shelf components including
- // the status area widget and shelf navigation widget (or the edge of display,
- // if the shelf navigation widget does not show).
- const int group_margin = ShelfConfig::Get()->GetAppIconGroupMargin();
- if (shelf_->IsHorizontalAlignment()) {
- const int width = shelf_bounds.width() - nav_bounds.size().width() +
- nav_widget_padding - 2 * group_margin -
- status_size.width();
- return gfx::Size(width, hotseat_size);
- }
- const int height = shelf_bounds.height() - nav_bounds.size().height() +
- nav_widget_padding - 2 * group_margin -
- status_size.height();
- return gfx::Size(hotseat_size, height);
- }
- void HotseatWidget::CalculateTargetBounds() {
- ShelfLayoutManager* layout_manager = shelf_->shelf_layout_manager();
- const HotseatState hotseat_target_state =
- layout_manager->CalculateHotseatState(layout_manager->visibility_state(),
- layout_manager->auto_hide_state());
- const gfx::Size hotseat_target_size =
- CalculateTargetBoundsSize(hotseat_target_state);
- if (hotseat_target_state == HotseatState::kShownHomeLauncher) {
- target_size_for_shown_state_ = hotseat_target_size;
- } else {
- target_size_for_shown_state_ =
- CalculateTargetBoundsSize(HotseatState::kShownHomeLauncher);
- }
- const gfx::Rect shelf_bounds = shelf_->shelf_widget()->GetTargetBounds();
- const gfx::Rect status_area_bounds =
- shelf_->status_area_widget()->GetTargetBounds();
- // The minimum gap between hotseat widget and other shelf components including
- // the status area widget and shelf navigation widget (or the edge of display,
- // if the shelf navigation widget does not show).
- const int group_margin = ShelfConfig::Get()->GetAppIconGroupMargin();
- gfx::Point hotseat_origin;
- if (shelf_->IsHorizontalAlignment()) {
- int hotseat_x;
- if (hotseat_target_state != HotseatState::kShownHomeLauncher &&
- hotseat_target_state != HotseatState::kShownClamshell) {
- hotseat_x = shelf_bounds.x();
- } else {
- hotseat_x = base::i18n::IsRTL()
- ? status_area_bounds.right() + group_margin
- : status_area_bounds.x() - group_margin -
- hotseat_target_size.width();
- }
- hotseat_origin =
- gfx::Point(hotseat_x, CalculateHotseatYInScreen(hotseat_target_state));
- } else {
- hotseat_origin =
- gfx::Point(shelf_bounds.x(), status_area_bounds.y() - group_margin -
- hotseat_target_size.height());
- }
- target_bounds_ = gfx::Rect(hotseat_origin, hotseat_target_size);
- // Check whether |target_bounds_| will change the state of app scaling. If
- // so, update |target_bounds_| here to avoid re-layout later.
- MaybeAdjustTargetBoundsForAppScaling(hotseat_target_state);
- }
- gfx::Rect HotseatWidget::GetTargetBounds() const {
- return target_bounds_;
- }
- void HotseatWidget::UpdateLayout(bool animate) {
- const LayoutInputs new_layout_inputs = GetLayoutInputs();
- if (layout_inputs_ == new_layout_inputs)
- return;
- // Never show this widget outside of an active session.
- if (!new_layout_inputs.is_active_session_state)
- Hide();
- ui::Layer* shelf_view_layer = GetShelfView()->layer();
- {
- ui::ScopedLayerAnimationSettings animation_setter(
- shelf_view_layer->GetAnimator());
- animation_setter.SetTransitionDuration(
- animate ? ShelfConfig::Get()->shelf_animation_duration()
- : base::Milliseconds(0));
- animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
- animation_setter.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- absl::optional<ui::AnimationThroughputReporter> reporter;
- if (animate && state_ != HotseatState::kNone) {
- reporter.emplace(animation_setter.GetAnimator(),
- shelf_->GetHotseatTransitionReportCallback(state_));
- }
- shelf_view_layer->SetOpacity(new_layout_inputs.shelf_view_opacity);
- }
- // If shelf view is invisible, the hotseat should be as well. Otherwise the
- // hotseat opacity should be 1.0f to preserve background blur.
- const double target_opacity =
- (new_layout_inputs.shelf_view_opacity == 0.f ? 0.f : 1.f);
- const gfx::Rect& target_bounds = new_layout_inputs.bounds;
- if (animate) {
- LayoutHotseatByAnimation(target_opacity, target_bounds);
- } else {
- ui::Layer* hotseat_layer = GetNativeView()->layer();
- // If the running bounds animation is not aborted, it will be interrupted
- // and set hotseat widget with the old target bounds which may differ from
- // |target_bounds| greatly and bring DCHECK errors. For example,
- // if hotseat animation is interrupted by the bounds setting triggered by
- // shelf alignment update, hotseat will be caught in an intermediate state
- // where the shelf alignment is new and the hotseat bounds are old.
- hotseat_layer->GetAnimator()->AbortAllAnimations();
- hotseat_layer->SetOpacity(target_opacity);
- hotseat_layer->SetTransform(gfx::Transform());
- SetBounds(target_bounds);
- }
- layout_inputs_ = new_layout_inputs;
- delegate_view_->UpdateTranslucentBackground();
- // Setting visibility during an animation causes the visibility property to
- // animate. Set the visibility property without an animation.
- if (new_layout_inputs.shelf_view_opacity != 0.0f &&
- new_layout_inputs.is_active_session_state) {
- ShowInactive();
- }
- }
- void HotseatWidget::UpdateTargetBoundsForGesture(int shelf_position) {
- if (shelf_->IsHorizontalAlignment())
- target_bounds_.set_y(shelf_position);
- else
- target_bounds_.set_x(shelf_position);
- }
- gfx::Size HotseatWidget::GetTranslucentBackgroundSize() const {
- DCHECK(scrollable_shelf_view_);
- return scrollable_shelf_view_->GetHotseatBackgroundBounds().size();
- }
- void HotseatWidget::SetFocusCycler(FocusCycler* focus_cycler) {
- delegate_view_->set_focus_cycler(focus_cycler);
- if (focus_cycler)
- focus_cycler->AddWidget(this);
- }
- ShelfView* HotseatWidget::GetShelfView() {
- DCHECK(scrollable_shelf_view_);
- return scrollable_shelf_view_->shelf_view();
- }
- int HotseatWidget::GetHotseatSize() const {
- return ShelfConfig::Get()->GetShelfButtonSize(target_hotseat_density_);
- }
- int HotseatWidget::GetHotseatFullDragAmount() const {
- ShelfConfig* shelf_config = ShelfConfig::Get();
- return shelf_config->shelf_size() + shelf_config->hotseat_bottom_padding() +
- GetHotseatSize();
- }
- bool HotseatWidget::UpdateTargetHotseatDensityIfNeeded() {
- if (CalculateTargetHotseatDensity() == target_hotseat_density_) {
- return false;
- }
- shelf_->shelf_layout_manager()->LayoutShelf(/*animate=*/true);
- return true;
- }
- int HotseatWidget::GetHotseatBackgroundBlurForTest() const {
- return delegate_view_->background_blur();
- }
- bool HotseatWidget::GetIsTranslucentBackgroundVisibleForTest() const {
- return delegate_view_->is_translucent_background_visible_for_test();
- }
- bool HotseatWidget::IsShowingShelfMenu() const {
- return GetShelfView()->IsShowingMenu();
- }
- bool HotseatWidget::EventTargetsShelfView(const ui::LocatedEvent& event) const {
- DCHECK_EQ(event.target(), GetNativeWindow());
- gfx::Point location_in_shelf_view = event.location();
- views::View::ConvertPointFromWidget(scrollable_shelf_view_,
- &location_in_shelf_view);
- return scrollable_shelf_view_->GetHotseatBackgroundBounds().Contains(
- location_in_shelf_view);
- }
- const ShelfView* HotseatWidget::GetShelfView() const {
- return const_cast<const ShelfView*>(
- const_cast<HotseatWidget*>(this)->GetShelfView());
- }
- metrics_util::ReportCallback
- HotseatWidget::GetTranslucentBackgroundReportCallback() {
- return shelf_->GetTranslucentBackgroundReportCallback(state_);
- }
- void HotseatWidget::SetState(HotseatState state) {
- if (state_ == state)
- return;
- state_ = state;
- // If the hotseat is not extended we can use the normal targeting as the
- // hidden parts of the hotseat will not block non-shelf items from taking
- if (state == HotseatState::kExtended) {
- hotseat_window_targeter_ = std::make_unique<aura::ScopedWindowTargeter>(
- GetNativeWindow(), std::make_unique<HotseatWindowTargeter>(this));
- } else {
- hotseat_window_targeter_.reset();
- }
- }
- ui::Layer* HotseatWidget::GetLayerForNudgeAnimation() {
- return delegate_view_->layer();
- }
- HotseatWidget::LayoutInputs HotseatWidget::GetLayoutInputs() const {
- const ShelfLayoutManager* layout_manager = shelf_->shelf_layout_manager();
- return {target_bounds_, CalculateShelfViewOpacity(),
- layout_manager->is_active_session_state()};
- }
- void HotseatWidget::MaybeAdjustTargetBoundsForAppScaling(
- HotseatState hotseat_target_state) {
- // Return early if app scaling state does not change.
- HotseatDensity new_target_hotseat_density = CalculateTargetHotseatDensity();
- if (new_target_hotseat_density == target_hotseat_density_)
- return;
- target_hotseat_density_ = new_target_hotseat_density;
- // Update app icons of shelf view.
- scrollable_shelf_view_->shelf_view()->OnShelfConfigUpdated();
- const gfx::Point adjusted_hotseat_origin = gfx::Point(
- target_bounds_.x(), CalculateHotseatYInScreen(hotseat_target_state));
- target_bounds_ =
- gfx::Rect(adjusted_hotseat_origin,
- gfx::Size(target_bounds_.width(), GetHotseatSize()));
- }
- HotseatDensity HotseatWidget::CalculateTargetHotseatDensity() const {
- // App scaling is only applied to the standard shelf. So the hotseat density
- // should not update in dense shelf.
- if (ShelfConfig::Get()->is_dense())
- return target_hotseat_density_;
- // TODO(crbug.com/1081476): Currently the scaling animation of hotseat bounds
- // and that of shelf icons do not synchronize due to performance issue. As a
- // result, shelf scaling is not applied to the hotseat state transition, such
- // as the transition from the home launcher state to the extended state.
- // Hotseat density relies on the hotseat bounds in the home launcher state
- // instead of the current hotseat state.
- // Try candidate button sizes in decreasing order. If shelf buttons in one
- // size can show without scrolling, return the density type corresponding to
- // that particular size; if no candidate size can make it, return
- // HotseatDensity::kDense.
- const std::vector<HotseatDensity> kCandidates = {HotseatDensity::kNormal,
- HotseatDensity::kSemiDense};
- for (const auto& candidate : kCandidates) {
- if (!scrollable_shelf_view_->RequiresScrollingForItemSize(
- target_size_for_shown_state_,
- ShelfConfig::Get()->GetShelfButtonSize(candidate))) {
- return candidate;
- }
- }
- return HotseatDensity::kDense;
- }
- void HotseatWidget::LayoutHotseatByAnimation(double target_opacity,
- const gfx::Rect& target_bounds) {
- ui::Layer* hotseat_layer = GetNativeView()->layer();
- // Bounds animations do not take transforms into account, but animations
- // between hidden and extended state use transform to animate. Clear any
- // transform that may have been set by the previous animation, and update
- // current bounds to match it.
- gfx::RectF current_bounds_f(GetNativeView()->GetBoundsInScreen());
- hotseat_layer->transform().TransformRect(¤t_bounds_f);
- gfx::Rect current_bounds = gfx::ToEnclosingRect(current_bounds_f);
- // If the bounds size has not changed, set the target bounds immediately, and
- // animate using transform.
- // Avoid transforms if the transition has a horizontal component - logic for
- // setting scrollable shelf padding depends on the horizontal in screen
- // bounds, and setting horizontal translation would interfere with that. Note
- // that generally, if horizontal widget position changes, so will its width.
- const bool animate_transform =
- target_bounds.size() == current_bounds.size() &&
- target_bounds.x() == current_bounds.x();
- if (animate_transform) {
- SetBounds(target_bounds);
- gfx::Transform initial_transform;
- initial_transform.Translate(current_bounds.origin() -
- target_bounds.origin());
- hotseat_layer->SetTransform(initial_transform);
- } else {
- hotseat_layer->SetTransform(gfx::Transform());
- SetBounds(current_bounds);
- }
- ui::ScopedLayerAnimationSettings animation_setter(
- hotseat_layer->GetAnimator());
- animation_setter.SetTransitionDuration(
- ShelfConfig::Get()->shelf_animation_duration());
- animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
- animation_setter.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- absl::optional<ui::AnimationThroughputReporter> reporter;
- if (state_ != HotseatState::kNone) {
- reporter.emplace(animation_setter.GetAnimator(),
- shelf_->GetHotseatTransitionReportCallback(state_));
- }
- if (animate_transform) {
- hotseat_layer->SetOpacity(target_opacity);
- hotseat_layer->SetTransform(gfx::Transform());
- return;
- }
- if (!state_transition_in_progress_.has_value()) {
- // Hotseat animation is not triggered by the update in |state_|. So apply
- // the normal bounds animation.
- StartNormalBoundsAnimation(target_opacity, target_bounds);
- return;
- }
- if (HasSpecialAnimation(*state_transition_in_progress_)) {
- StartHotseatTransitionAnimation(*state_transition_in_progress_,
- target_opacity, target_bounds);
- } else {
- StartNormalBoundsAnimation(target_opacity, target_bounds);
- }
- }
- void HotseatWidget::StartHotseatTransitionAnimation(
- StateTransition state_transition,
- double target_opacity,
- const gfx::Rect& target_bounds) {
- ui::Layer* hotseat_layer = GetNativeView()->layer();
- std::unique_ptr<ui::LayerAnimationElement> animation_elements;
- switch (state_transition) {
- case StateTransition::kHomeLauncherAndExtended:
- animation_elements = std::make_unique<HomeAndExtendedTransitionAnimation>(
- target_bounds, target_opacity, hotseat_layer,
- /*hotseat_widget=*/this);
- break;
- case StateTransition::kHomeLauncherAndHidden:
- animation_elements = std::make_unique<HomeAndHiddenTransitionAnimation>(
- target_bounds, target_opacity, hotseat_layer,
- /*hotseat_widget=*/this);
- break;
- case StateTransition::kHiddenAndExtended:
- case StateTransition::kOther:
- NOTREACHED();
- }
- auto* sequence =
- new ui::LayerAnimationSequence(std::move(animation_elements));
- hotseat_layer->GetAnimator()->StartAnimation(sequence);
- }
- void HotseatWidget::StartNormalBoundsAnimation(double target_opacity,
- const gfx::Rect& target_bounds) {
- GetNativeView()->layer()->SetOpacity(target_opacity);
- SetBounds(target_bounds);
- }
- } // namespace ash
|