| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/shelf/shelf.h" |
| |
| #include <memory> |
| |
| #include "ash/animation/animation_change_type.h" |
| #include "ash/app_list/app_list_controller_impl.h" |
| #include "ash/constants/ash_constants.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_switches.h" |
| #include "ash/metrics/login_unlock_throughput_recorder.h" |
| #include "ash/public/cpp/keyboard/keyboard_controller_observer.h" |
| #include "ash/public/cpp/shelf_item_delegate.h" |
| #include "ash/public/cpp/shelf_model.h" |
| #include "ash/public/cpp/shelf_types.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/shelf/desk_button_widget.h" |
| #include "ash/shelf/hotseat_widget.h" |
| #include "ash/shelf/login_shelf_widget.h" |
| #include "ash/shelf/scrollable_shelf_view.h" |
| #include "ash/shelf/shelf_controller.h" |
| #include "ash/shelf/shelf_focus_cycler.h" |
| #include "ash/shelf/shelf_layout_manager.h" |
| #include "ash/shelf/shelf_layout_manager_observer.h" |
| #include "ash/shelf/shelf_navigation_widget.h" |
| #include "ash/shelf/shelf_observer.h" |
| #include "ash/shelf/shelf_tooltip_manager.h" |
| #include "ash/shelf/shelf_widget.h" |
| #include "ash/shell.h" |
| #include "ash/system/status_area_widget.h" |
| #include "ash/wm/work_area_insets.h" |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/i18n/rtl.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/notreached.h" |
| #include "ui/display/types/display_constants.h" |
| #include "ui/gfx/geometry/rect.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| bool IsAppListBackground(ShelfBackgroundType background_type) { |
| switch (background_type) { |
| case ShelfBackgroundType::kHomeLauncher: |
| return true; |
| case ShelfBackgroundType::kDefaultBg: |
| case ShelfBackgroundType::kMaximized: |
| case ShelfBackgroundType::kOobe: |
| case ShelfBackgroundType::kLogin: |
| case ShelfBackgroundType::kLoginNonBlurredWallpaper: |
| case ShelfBackgroundType::kOverview: |
| case ShelfBackgroundType::kInApp: |
| return false; |
| } |
| } |
| |
| bool IsBottomAlignment(ShelfAlignment alignment) { |
| return alignment == ShelfAlignment::kBottom || |
| alignment == ShelfAlignment::kBottomLocked; |
| } |
| |
| } // namespace |
| |
| // Records smoothness of bounds animations for the HotseatWidget. |
| class HotseatWidgetAnimationMetricsReporter { |
| public: |
| // The different kinds of hotseat elements. |
| enum class HotseatElementType { |
| // The Hotseat Widget. |
| kWidget, |
| // The Hotseat Widget's translucent background. |
| kTranslucentBackground |
| }; |
| |
| explicit HotseatWidgetAnimationMetricsReporter( |
| HotseatElementType hotseat_element) |
| : hotseat_element_(hotseat_element) {} |
| ~HotseatWidgetAnimationMetricsReporter() = default; |
| |
| void ReportSmoothness(HotseatState target_state, int smoothness) { |
| switch (target_state) { |
| case HotseatState::kShownClamshell: |
| case HotseatState::kShownHomeLauncher: |
| if (hotseat_element_ == HotseatElementType::kWidget) { |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.HotseatWidgetAnimation.Widget.AnimationSmoothness." |
| "TransitionToShownHotseat", |
| smoothness); |
| } else { |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.HotseatWidgetAnimation.TranslucentBackground." |
| "AnimationSmoothness.TransitionToShownHotseat", |
| smoothness); |
| } |
| break; |
| case HotseatState::kExtended: |
| if (hotseat_element_ == HotseatElementType::kWidget) { |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.HotseatWidgetAnimation.Widget.AnimationSmoothness." |
| "TransitionToExtendedHotseat", |
| smoothness); |
| } else { |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.HotseatWidgetAnimation.TranslucentBackground." |
| "AnimationSmoothness.TransitionToExtendedHotseat", |
| smoothness); |
| } |
| break; |
| case HotseatState::kHidden: |
| if (hotseat_element_ == HotseatElementType::kWidget) { |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.HotseatWidgetAnimation.Widget.AnimationSmoothness." |
| "TransitionToHiddenHotseat", |
| smoothness); |
| } else { |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.HotseatWidgetAnimation.TranslucentBackground." |
| "AnimationSmoothness.TransitionToHiddenHotseat", |
| smoothness); |
| } |
| break; |
| case HotseatState::kNone: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| metrics_util::ReportCallback GetReportCallback(HotseatState target_state) { |
| DCHECK_NE(target_state, HotseatState::kNone); |
| return metrics_util::ForSmoothnessV3(base::BindRepeating( |
| &HotseatWidgetAnimationMetricsReporter::ReportSmoothness, |
| weak_ptr_factory_.GetWeakPtr(), target_state)); |
| } |
| |
| private: |
| // The element that is reporting an animation. |
| const HotseatElementType hotseat_element_; |
| |
| base::WeakPtrFactory<HotseatWidgetAnimationMetricsReporter> weak_ptr_factory_{ |
| this}; |
| }; |
| |
| // An animation metrics reporter for the shelf navigation widget. |
| class ASH_EXPORT NavigationWidgetAnimationMetricsReporter { |
| public: |
| NavigationWidgetAnimationMetricsReporter() = default; |
| |
| ~NavigationWidgetAnimationMetricsReporter() = default; |
| |
| NavigationWidgetAnimationMetricsReporter( |
| const NavigationWidgetAnimationMetricsReporter&) = delete; |
| NavigationWidgetAnimationMetricsReporter& operator=( |
| const NavigationWidgetAnimationMetricsReporter&) = delete; |
| |
| void ReportSmoothness(HotseatState target_hotseat_state, int smoothness) { |
| switch (target_hotseat_state) { |
| case HotseatState::kShownClamshell: |
| case HotseatState::kShownHomeLauncher: |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.NavigationWidget.Widget.AnimationSmoothness." |
| "TransitionToShownHotseat", |
| smoothness); |
| break; |
| case HotseatState::kExtended: |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.NavigationWidget.Widget.AnimationSmoothness." |
| "TransitionToExtendedHotseat", |
| smoothness); |
| break; |
| case HotseatState::kHidden: |
| UMA_HISTOGRAM_PERCENTAGE( |
| "Ash.NavigationWidget.Widget.AnimationSmoothness." |
| "TransitionToHiddenHotseat", |
| smoothness); |
| break; |
| case HotseatState::kNone: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| metrics_util::ReportCallback GetReportCallback( |
| HotseatState target_hotseat_state) { |
| DCHECK_NE(target_hotseat_state, HotseatState::kNone); |
| return metrics_util::ForSmoothnessV3(base::BindRepeating( |
| &NavigationWidgetAnimationMetricsReporter::ReportSmoothness, |
| weak_ptr_factory_.GetWeakPtr(), target_hotseat_state)); |
| } |
| |
| private: |
| base::WeakPtrFactory<NavigationWidgetAnimationMetricsReporter> |
| weak_ptr_factory_{this}; |
| }; |
| |
| // Shelf::AutoHideEventHandler ----------------------------------------------- |
| |
| // Forwards mouse and gesture events to ShelfLayoutManager for auto-hide. |
| class Shelf::AutoHideEventHandler : public ui::EventHandler { |
| public: |
| explicit AutoHideEventHandler(Shelf* shelf) : shelf_(shelf) { |
| Shell::Get()->AddPreTargetHandler(this); |
| } |
| |
| AutoHideEventHandler(const AutoHideEventHandler&) = delete; |
| AutoHideEventHandler& operator=(const AutoHideEventHandler&) = delete; |
| |
| ~AutoHideEventHandler() override { |
| Shell::Get()->RemovePreTargetHandler(this); |
| } |
| |
| // ui::EventHandler: |
| void OnMouseEvent(ui::MouseEvent* event) override { |
| shelf_->shelf_layout_manager()->UpdateAutoHideForMouseEvent( |
| event, static_cast<aura::Window*>(event->target())); |
| } |
| void OnGestureEvent(ui::GestureEvent* event) override { |
| shelf_->shelf_layout_manager()->ProcessGestureEventOfAutoHideShelf( |
| event, static_cast<aura::Window*>(event->target())); |
| } |
| void OnTouchEvent(ui::TouchEvent* event) override { |
| if (shelf_->auto_hide_behavior() != ShelfAutoHideBehavior::kAlways) |
| return; |
| |
| // The event target should be the shelf widget or the hotseat widget. |
| if (!shelf_->shelf_layout_manager()->IsShelfWindow( |
| static_cast<aura::Window*>(event->target()))) { |
| return; |
| } |
| |
| // The touch-pressing event may hide the shelf. Lock the shelf's auto hide |
| // state to give the shelf a chance to handle the touch event before it |
| // being hidden. |
| ShelfLayoutManager* shelf_layout_manager = shelf_->shelf_layout_manager(); |
| if (event->type() == ui::ET_TOUCH_PRESSED && shelf_->IsVisible()) { |
| shelf_layout_manager->LockAutoHideState(true); |
| } else if (event->type() == ui::ET_TOUCH_RELEASED || |
| event->type() == ui::ET_TOUCH_CANCELLED) { |
| // Unlock auto hide (and eventually recompute auto hide state). |
| shelf_layout_manager->LockAutoHideState(false); |
| } |
| } |
| |
| private: |
| raw_ptr<Shelf> shelf_; |
| }; |
| |
| // Shelf::AutoDimEventHandler ----------------------------------------------- |
| |
| // Handles mouse and touch events and determines whether ShelfLayoutManager |
| // should update shelf opacity for auto-dimming. |
| class Shelf::AutoDimEventHandler : public ui::EventHandler, |
| public ShelfObserver { |
| public: |
| explicit AutoDimEventHandler(Shelf* shelf) : shelf_(shelf) { |
| Shell::Get()->AddPreTargetHandler(this); |
| shelf_observation_.Observe(shelf_.get()); |
| UndimShelf(); |
| } |
| |
| AutoDimEventHandler(const AutoDimEventHandler&) = delete; |
| AutoDimEventHandler& operator=(const AutoDimEventHandler&) = delete; |
| |
| ~AutoDimEventHandler() override { |
| Shell::Get()->RemovePreTargetHandler(this); |
| } |
| |
| // ui::EventHandler: |
| void OnMouseEvent(ui::MouseEvent* event) override { |
| if (shelf_->shelf_layout_manager()->IsShelfWindow( |
| static_cast<aura::Window*>(event->target()))) { |
| UndimShelf(); |
| } |
| } |
| |
| void OnTouchEvent(ui::TouchEvent* event) override { |
| if (shelf_->shelf_layout_manager()->IsShelfWindow( |
| static_cast<aura::Window*>(event->target()))) { |
| UndimShelf(); |
| } |
| } |
| |
| void StartDimShelfTimer() { |
| dim_shelf_timer_.Start( |
| FROM_HERE, kDimDelay, |
| base::BindOnce(&AutoDimEventHandler::DimShelf, base::Unretained(this))); |
| } |
| |
| void DimShelf() { |
| // Attempt to dim the shelf. Stop the |dim_shelf_timer_| if successful. |
| if (shelf_->shelf_layout_manager()->SetDimmed(true)) |
| dim_shelf_timer_.Stop(); |
| } |
| |
| // Sets shelf as active and sets timer to mark shelf as inactive. |
| void UndimShelf() { |
| shelf_->shelf_layout_manager()->SetDimmed(false); |
| StartDimShelfTimer(); |
| } |
| |
| bool HasDimShelfTimer() { return dim_shelf_timer_.IsRunning(); } |
| |
| // ShelfObserver: |
| void OnAutoHideStateChanged(ShelfAutoHideState new_state) override { |
| // Shelf should be undimmed when it is shown. |
| if (new_state == ShelfAutoHideState::SHELF_AUTO_HIDE_SHOWN) |
| UndimShelf(); |
| } |
| |
| // ShelfObserver: |
| void OnShelfVisibilityStateChanged(ShelfVisibilityState new_state) override { |
| // Shelf should be undimmed when it is shown. |
| if (new_state != ShelfVisibilityState::SHELF_HIDDEN) |
| UndimShelf(); |
| } |
| |
| private: |
| // Unowned pointer to the shelf that owns this event handler. |
| raw_ptr<Shelf> shelf_; |
| // OneShotTimer that dims shelf due to inactivity. |
| base::OneShotTimer dim_shelf_timer_; |
| // An observer that notifies the AutoDimHandler that shelf visibility has |
| // changed. |
| base::ScopedObservation<Shelf, ShelfObserver> shelf_observation_{this}; |
| |
| // Delay before dimming the shelf. |
| const base::TimeDelta kDimDelay = base::Seconds(5); |
| }; |
| |
| // Shelf::ScopedDisableAutoHide ---------------------------------------------- |
| |
| Shelf::ScopedDisableAutoHide::ScopedDisableAutoHide(Shelf* shelf) |
| : shelf_(shelf->GetWeakPtr()) { |
| CHECK(shelf); |
| |
| ++shelf_->disable_auto_hide_; |
| if (shelf_->disable_auto_hide_ == 1) { |
| shelf_->UpdateVisibilityState(); |
| } |
| } |
| |
| Shelf::ScopedDisableAutoHide::~ScopedDisableAutoHide() { |
| if (!shelf_) { |
| return; |
| } |
| |
| --shelf_->disable_auto_hide_; |
| CHECK_GE(shelf_->disable_auto_hide_, 0); |
| if (shelf_->disable_auto_hide_ == 0) { |
| shelf_->UpdateVisibilityState(); |
| } |
| } |
| |
| // Shelf --------------------------------------------------------------------- |
| |
| Shelf::Shelf() |
| : shelf_locking_manager_(this), |
| shelf_focus_cycler_(std::make_unique<ShelfFocusCycler>(this)), |
| tooltip_(std::make_unique<ShelfTooltipManager>(this)) {} |
| |
| Shelf::~Shelf() = default; |
| |
| // static |
| Shelf* Shelf::ForWindow(aura::Window* window) { |
| return RootWindowController::ForWindow(window)->shelf(); |
| } |
| |
| // static |
| void Shelf::LaunchShelfItem(int item_index) { |
| const int item_count = ShelfModel::Get()->item_count(); |
| |
| // A negative argument will launch the last app. A positive argument will |
| // launch the app at the corresponding index, unless it's higher than the |
| // total number of apps, in which case we do nothing. |
| if (item_index >= item_count) |
| return; |
| |
| const int found_index = item_index >= 0 ? item_index : item_count - 1; |
| |
| // Set this one as active (or advance to the next item of its kind). |
| ActivateShelfItem(found_index); |
| } |
| |
| // static |
| void Shelf::ActivateShelfItem(int item_index) { |
| ActivateShelfItemOnDisplay(item_index, display::kInvalidDisplayId); |
| } |
| |
| // static |
| void Shelf::ActivateShelfItemOnDisplay(int item_index, int64_t display_id) { |
| const ShelfModel* shelf_model = ShelfModel::Get(); |
| const ShelfItem& item = shelf_model->items()[item_index]; |
| ShelfItemDelegate* item_delegate = shelf_model->GetShelfItemDelegate(item.id); |
| std::unique_ptr<ui::Event> event = std::make_unique<ui::KeyEvent>( |
| ui::ET_KEY_RELEASED, ui::VKEY_UNKNOWN, ui::EF_NONE); |
| item_delegate->ItemSelected(std::move(event), display_id, LAUNCH_FROM_SHELF, |
| base::DoNothing(), base::NullCallback()); |
| } |
| |
| // static |
| void Shelf::UpdateShelfVisibility() { |
| for (aura::Window* root : Shell::Get()->GetAllRootWindows()) { |
| Shelf::ForWindow(root)->UpdateVisibilityState(); |
| } |
| } |
| |
| void Shelf::CreateNavigationWidget(aura::Window* container) { |
| DCHECK(container); |
| DCHECK(!navigation_widget_); |
| navigation_widget_ = std::make_unique<ShelfNavigationWidget>( |
| this, hotseat_widget()->GetShelfView()); |
| navigation_widget_->Initialize(container); |
| navigation_widget_metrics_reporter_ = |
| std::make_unique<NavigationWidgetAnimationMetricsReporter>(); |
| } |
| |
| void Shelf::CreateDeskButtonWidget(aura::Window* container) { |
| CHECK(container); |
| CHECK(!desk_button_widget_); |
| CHECK(ash::features::IsDeskButtonEnabled()); |
| |
| desk_button_widget_ = std::make_unique<DeskButtonWidget>(this); |
| desk_button_widget_->Initialize(container); |
| } |
| |
| void Shelf::CreateHotseatWidget(aura::Window* container) { |
| DCHECK(container); |
| DCHECK(!hotseat_widget_); |
| hotseat_widget_ = std::make_unique<HotseatWidget>(); |
| translucent_background_metrics_reporter_ = |
| std::make_unique<HotseatWidgetAnimationMetricsReporter>( |
| HotseatWidgetAnimationMetricsReporter::HotseatElementType:: |
| kTranslucentBackground); |
| hotseat_widget_->Initialize(container, this); |
| shelf_widget_->RegisterHotseatWidget(hotseat_widget()); |
| hotseat_transition_metrics_reporter_ = |
| std::make_unique<HotseatWidgetAnimationMetricsReporter>( |
| HotseatWidgetAnimationMetricsReporter::HotseatElementType::kWidget); |
| } |
| |
| void Shelf::CreateStatusAreaWidget(aura::Window* shelf_container) { |
| DCHECK(shelf_container); |
| DCHECK(!status_area_widget_); |
| status_area_widget_ = |
| std::make_unique<StatusAreaWidget>(shelf_container, this); |
| status_area_widget_->Initialize(); |
| } |
| |
| void Shelf::CreateShelfWidget(aura::Window* root) { |
| DCHECK(!shelf_widget_); |
| aura::Window* shelf_container = |
| root->GetChildById(kShellWindowId_ShelfContainer); |
| shelf_widget_ = std::make_unique<ShelfWidget>(this); |
| |
| DCHECK(!shelf_layout_manager_); |
| shelf_layout_manager_ = shelf_widget_->shelf_layout_manager(); |
| shelf_layout_manager_->AddObserver(this); |
| |
| // Create the various shelf components. |
| CreateHotseatWidget(shelf_container); |
| CreateNavigationWidget(shelf_container); |
| if (ash::features::IsDeskButtonEnabled()) { |
| CreateDeskButtonWidget(shelf_container); |
| } |
| login_shelf_widget_ = |
| std::make_unique<LoginShelfWidget>(/*shelf=*/this, shelf_container); |
| |
| // Must occur after |shelf_widget_| is constructed because the system tray |
| // constructors call back into Shelf::shelf_widget(). |
| CreateStatusAreaWidget(shelf_container); |
| shelf_widget_->Initialize(shelf_container); |
| shelf_widget_->GetNativeWindow()->parent()->StackChildAtBottom( |
| shelf_widget_->GetNativeWindow()); |
| |
| // The Hotseat should be above everything in the shelf. |
| hotseat_widget()->StackAtTop(); |
| } |
| |
| void Shelf::ShutdownShelfWidget() { |
| for (auto& observer : observers_) |
| observer.OnShelfShuttingDown(); |
| |
| // Remove observers prior to destroying child widgets, this prevents |
| // activation changes from triggering during shutdown, see |
| // https://crbug.com/1307898. |
| shelf_widget_->Shutdown(); |
| |
| // The contents view of the hotseat widget may rely on the status area widget. |
| // So do explicit destruction here. |
| hotseat_widget_.reset(); |
| status_area_widget_.reset(); |
| navigation_widget_.reset(); |
| login_shelf_widget_.reset(); |
| } |
| |
| void Shelf::DestroyShelfWidget() { |
| DCHECK(shelf_widget_); |
| shelf_widget_.reset(); |
| } |
| |
| bool Shelf::IsVisible() const { |
| return shelf_layout_manager_->IsVisible(); |
| } |
| |
| const aura::Window* Shelf::GetWindow() const { |
| return shelf_widget_ ? shelf_widget_->GetNativeWindow() : nullptr; |
| } |
| |
| aura::Window* Shelf::GetWindow() { |
| return const_cast<aura::Window*>(const_cast<const Shelf*>(this)->GetWindow()); |
| } |
| |
| void Shelf::SetAlignment(ShelfAlignment alignment) { |
| if (!shelf_widget_) |
| return; |
| |
| if (alignment_ == alignment) |
| return; |
| |
| if (shelf_locking_manager_.is_locked() && |
| alignment != ShelfAlignment::kBottomLocked) { |
| shelf_locking_manager_.set_in_session_alignment(alignment); |
| return; |
| } |
| |
| bool needs_relayout = |
| !IsBottomAlignment(alignment_) || !IsBottomAlignment(alignment); |
| |
| ShelfAlignment old_alignment = alignment_; |
| alignment_ = alignment; |
| tooltip_->Close(); |
| if (needs_relayout) { |
| shelf_layout_manager_->HandleShelfAlignmentChange(); |
| Shell::Get()->NotifyShelfAlignmentChanged(GetWindow()->GetRootWindow(), |
| old_alignment); |
| } |
| } |
| |
| bool IsHorizontalAlignment(ShelfAlignment alignment) { |
| switch (alignment) { |
| case ShelfAlignment::kBottom: |
| case ShelfAlignment::kBottomLocked: |
| return true; |
| case ShelfAlignment::kLeft: |
| case ShelfAlignment::kRight: |
| return false; |
| } |
| NOTREACHED(); |
| return true; |
| } |
| |
| bool Shelf::IsHorizontalAlignment() const { |
| return ash::IsHorizontalAlignment(alignment_); |
| } |
| |
| void Shelf::SetAutoHideBehavior(ShelfAutoHideBehavior auto_hide_behavior) { |
| DCHECK(shelf_layout_manager_); |
| |
| if (auto_hide_behavior_ == auto_hide_behavior) |
| return; |
| |
| auto_hide_behavior_ = auto_hide_behavior; |
| |
| for (auto& observer : observers_) |
| observer.OnShelfAutoHideBehaviorChanged(); |
| } |
| |
| ShelfAutoHideState Shelf::GetAutoHideState() const { |
| return shelf_layout_manager_->auto_hide_state(); |
| } |
| |
| void Shelf::UpdateAutoHideState() { |
| shelf_layout_manager_->UpdateAutoHideState(); |
| } |
| |
| ShelfBackgroundType Shelf::GetBackgroundType() const { |
| return shelf_layout_manager_ ? shelf_layout_manager_->shelf_background_type() |
| : ShelfBackgroundType::kDefaultBg; |
| } |
| |
| void Shelf::UpdateVisibilityState() { |
| if (shelf_layout_manager_) |
| shelf_layout_manager_->UpdateVisibilityState(/*force_layout=*/false); |
| } |
| |
| void Shelf::MaybeUpdateShelfBackground() { |
| if (!shelf_layout_manager_) |
| return; |
| |
| shelf_layout_manager_->MaybeUpdateShelfBackground( |
| AnimationChangeType::ANIMATE); |
| } |
| |
| ShelfVisibilityState Shelf::GetVisibilityState() const { |
| return shelf_layout_manager_ ? shelf_layout_manager_->visibility_state() |
| : SHELF_HIDDEN; |
| } |
| |
| gfx::Rect Shelf::GetShelfBoundsInScreen() const { |
| return shelf_widget()->GetTargetBounds(); |
| } |
| |
| gfx::Rect Shelf::GetIdealBounds() const { |
| return shelf_layout_manager_->GetIdealBounds(); |
| } |
| |
| gfx::Rect Shelf::GetIdealBoundsForWorkAreaCalculation() { |
| return shelf_layout_manager_->GetIdealBoundsForWorkAreaCalculation(); |
| } |
| |
| gfx::Rect Shelf::GetScreenBoundsOfItemIconForWindow(aura::Window* window) { |
| if (!shelf_widget_) |
| return gfx::Rect(); |
| return shelf_widget_->GetScreenBoundsOfItemIconForWindow(window); |
| } |
| |
| bool Shelf::ProcessGestureEvent(const ui::GestureEvent& event) { |
| // Can be called at login screen. |
| if (!shelf_layout_manager_) |
| return false; |
| return shelf_layout_manager_->ProcessGestureEvent(event); |
| } |
| |
| void Shelf::ProcessMouseEvent(const ui::MouseEvent& event) { |
| if (shelf_layout_manager_) |
| shelf_layout_manager_->ProcessMouseEventFromShelf(event); |
| } |
| |
| void Shelf::ProcessScrollEvent(ui::ScrollEvent* event) { |
| if (event->finger_count() != 2 || event->type() != ui::ET_SCROLL) |
| return; |
| |
| if (!shelf_layout_manager_->is_active_session_state()) |
| return; |
| |
| // Introduce the swipe up gesture behind a flag over certain conditions. |
| if (!shelf_layout_manager_->IsBubbleLauncherShowOnGestureScrollAvailable()) |
| return; |
| |
| auto* app_list_controller = Shell::Get()->app_list_controller(); |
| DCHECK(app_list_controller); |
| |
| shelf_layout_manager_->ProcessScrollEventFromShelf(event); |
| event->SetHandled(); |
| } |
| |
| void Shelf::ProcessMouseWheelEvent(ui::MouseWheelEvent* event) { |
| if (!shelf_layout_manager_->is_active_session_state() || |
| !IsHorizontalAlignment()) |
| return; |
| |
| // Introduce the swipe up gesture behind a flag over certain conditions. |
| if (!shelf_layout_manager_->IsBubbleLauncherShowOnGestureScrollAvailable()) |
| return; |
| |
| auto* app_list_controller = Shell::Get()->app_list_controller(); |
| DCHECK(app_list_controller); |
| |
| shelf_layout_manager_->ProcessMouseWheelEventFromShelf(event); |
| event->SetHandled(); |
| } |
| |
| void Shelf::AddObserver(ShelfObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void Shelf::RemoveObserver(ShelfObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| void Shelf::NotifyShelfIconPositionsChanged() { |
| for (auto& observer : observers_) |
| observer.OnShelfIconPositionsChanged(); |
| } |
| |
| StatusAreaWidget* Shelf::GetStatusAreaWidget() const { |
| return shelf_widget_ ? shelf_widget_->status_area_widget() : nullptr; |
| } |
| |
| gfx::Rect Shelf::GetSystemTrayAnchorRect() const { |
| gfx::Rect work_area = GetWorkAreaInsets()->user_work_area_bounds(); |
| switch (alignment_) { |
| case ShelfAlignment::kBottom: |
| case ShelfAlignment::kBottomLocked: |
| return gfx::Rect(base::i18n::IsRTL() |
| ? work_area.x() |
| : work_area.right() - kShelfDisplayOffset, |
| work_area.bottom() - kShelfDisplayOffset, 0, 0); |
| case ShelfAlignment::kLeft: |
| return gfx::Rect(work_area.x(), work_area.bottom() - kShelfDisplayOffset, |
| 0, 0); |
| case ShelfAlignment::kRight: |
| return gfx::Rect(work_area.right() - kShelfDisplayOffset, |
| work_area.bottom() - kShelfDisplayOffset, 0, 0); |
| } |
| NOTREACHED(); |
| return gfx::Rect(); |
| } |
| |
| bool Shelf::ShouldHideOnSecondaryDisplay(session_manager::SessionState state) { |
| if (Shell::GetPrimaryRootWindowController()->shelf() == this) |
| return false; |
| |
| return state != session_manager::SessionState::ACTIVE; |
| } |
| |
| void Shelf::SetVirtualKeyboardBoundsForTesting(const gfx::Rect& bounds) { |
| KeyboardStateDescriptor state; |
| state.is_visible = !bounds.IsEmpty(); |
| state.visual_bounds = bounds; |
| state.occluded_bounds_in_screen = bounds; |
| state.displaced_bounds_in_screen = gfx::Rect(); |
| WorkAreaInsets* work_area_insets = GetWorkAreaInsets(); |
| work_area_insets->OnKeyboardVisibilityChanged(state.is_visible); |
| work_area_insets->OnKeyboardVisibleBoundsChanged(state.visual_bounds); |
| work_area_insets->OnKeyboardOccludedBoundsChanged( |
| state.occluded_bounds_in_screen); |
| work_area_insets->OnKeyboardDisplacingBoundsChanged( |
| state.displaced_bounds_in_screen); |
| work_area_insets->OnKeyboardAppearanceChanged(state); |
| } |
| |
| ShelfLockingManager* Shelf::GetShelfLockingManagerForTesting() { |
| return &shelf_locking_manager_; |
| } |
| |
| ShelfView* Shelf::GetShelfViewForTesting() { |
| return shelf_widget_->shelf_view_for_testing(); |
| } |
| |
| metrics_util::ReportCallback Shelf::GetHotseatTransitionReportCallback( |
| HotseatState target_state) { |
| return hotseat_transition_metrics_reporter_->GetReportCallback(target_state); |
| } |
| |
| metrics_util::ReportCallback Shelf::GetTranslucentBackgroundReportCallback( |
| HotseatState target_state) { |
| return translucent_background_metrics_reporter_->GetReportCallback( |
| target_state); |
| } |
| |
| metrics_util::ReportCallback Shelf::GetNavigationWidgetAnimationReportCallback( |
| HotseatState target_hotseat_state) { |
| return navigation_widget_metrics_reporter_->GetReportCallback( |
| target_hotseat_state); |
| } |
| |
| void Shelf::WillDeleteShelfLayoutManager() { |
| // Clear event handlers that might forward events to the destroyed instance. |
| auto_hide_event_handler_.reset(); |
| auto_dim_event_handler_.reset(); |
| navigation_widget_metrics_reporter_.reset(); |
| |
| DCHECK(shelf_layout_manager_); |
| shelf_layout_manager_->RemoveObserver(this); |
| shelf_layout_manager_ = nullptr; |
| } |
| |
| void Shelf::OnShelfVisibilityStateChanged(ShelfVisibilityState new_state) { |
| if (!auto_dim_event_handler_ && switches::IsUsingShelfAutoDim()) { |
| auto_dim_event_handler_ = std::make_unique<AutoDimEventHandler>(this); |
| } |
| |
| if (new_state != SHELF_AUTO_HIDE) { |
| auto_hide_event_handler_.reset(); |
| } else if (!auto_hide_event_handler_) { |
| auto_hide_event_handler_ = std::make_unique<AutoHideEventHandler>(this); |
| } |
| |
| for (auto& observer : observers_) { |
| observer.OnShelfVisibilityStateChanged(new_state); |
| } |
| } |
| |
| void Shelf::OnAutoHideStateChanged(ShelfAutoHideState new_state) { |
| for (auto& observer : observers_) |
| observer.OnAutoHideStateChanged(new_state); |
| } |
| |
| void Shelf::OnBackgroundUpdated(ShelfBackgroundType background_type, |
| AnimationChangeType change_type) { |
| // Shelf should undim when transitioning to show app list. |
| if (auto_dim_event_handler_ && IsAppListBackground(background_type)) |
| UndimShelf(); |
| |
| for (auto& observer : observers_) |
| observer.OnBackgroundTypeChanged(background_type, change_type); |
| } |
| |
| void Shelf::OnHotseatStateChanged(HotseatState old_state, |
| HotseatState new_state) { |
| for (auto& observer : observers_) |
| observer.OnHotseatStateChanged(old_state, new_state); |
| } |
| |
| void Shelf::OnWorkAreaInsetsChanged() { |
| for (auto& observer : observers_) |
| observer.OnShelfWorkAreaInsetsChanged(); |
| } |
| |
| void Shelf::DimShelf() { |
| auto_dim_event_handler_->DimShelf(); |
| } |
| |
| void Shelf::UndimShelf() { |
| auto_dim_event_handler_->UndimShelf(); |
| } |
| |
| bool Shelf::HasDimShelfTimer() { |
| return auto_dim_event_handler_->HasDimShelfTimer(); |
| } |
| |
| WorkAreaInsets* Shelf::GetWorkAreaInsets() const { |
| const aura::Window* window = GetWindow(); |
| DCHECK(window); |
| return WorkAreaInsets::ForWindow(window->GetRootWindow()); |
| } |
| |
| base::WeakPtr<Shelf> Shelf::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| } // namespace ash |