| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef ASH_WM_FLOAT_FLOAT_CONTROLLER_H_ |
| #define ASH_WM_FLOAT_FLOAT_CONTROLLER_H_ |
| |
| #include <memory> |
| |
| #include "ash/ash_export.h" |
| #include "ash/public/cpp/tablet_mode_observer.h" |
| #include "ash/rotator/screen_rotation_animator_observer.h" |
| #include "ash/shell_observer.h" |
| #include "ash/wm/desks/desks_controller.h" |
| #include "base/scoped_multi_source_observation.h" |
| #include "base/scoped_observation.h" |
| #include "chromeos/ui/base/window_state_type.h" |
| #include "chromeos/ui/frame/multitask_menu/float_controller_base.h" |
| #include "ui/display/display_observer.h" |
| |
| namespace aura { |
| class Window; |
| } // namespace aura |
| |
| namespace views { |
| class Widget; |
| } // namespace views |
| |
| namespace ash { |
| |
| class Shell; |
| class TabletModeController; |
| class WorkspaceEventHandler; |
| |
| // This controller allows windows to be on top of all app windows, but below |
| // pips. When a window is 'floated', it remains always on top for the user so |
| // that they can complete secondary tasks. Floated window stays in the |
| // `kShellWindowId_FloatContainer`. |
| class ASH_EXPORT FloatController : public TabletModeObserver, |
| public display::DisplayObserver, |
| public ShellObserver, |
| public DesksController::Observer, |
| public chromeos::FloatControllerBase, |
| public ScreenRotationAnimatorObserver { |
| public: |
| // The possible corners that a floated window can be placed in tablet mode. |
| // The default is `kBottomRight` and this is changed by dragging the window. |
| enum class MagnetismCorner { |
| kTopLeft = 0, |
| kTopRight, |
| kBottomLeft, |
| kBottomRight, |
| }; |
| |
| FloatController(); |
| FloatController(const FloatController&) = delete; |
| FloatController& operator=(const FloatController&) = delete; |
| ~FloatController() override; |
| |
| // Returns float window bounds in clamshell mode in root window coordinates. |
| static gfx::Rect GetPreferredFloatWindowClamshellBounds(aura::Window* window); |
| |
| // Gets the ideal float bounds of `window` in tablet mode if it were to be |
| // floated, in root window coordinates. |
| static gfx::Rect GetPreferredFloatWindowTabletBounds(aura::Window* window); |
| |
| // Untucks `floated_window`. Does nothing if the window is already untucked. |
| void MaybeUntuckFloatedWindowForTablet(aura::Window* floated_window); |
| |
| // Checks if `floated_window` is tucked. |
| bool IsFloatedWindowTuckedForTablet(const aura::Window* floated_window) const; |
| |
| // Returns true if `floated_window` is not tucked and magnetized to the |
| // bottom. Used by the shelf layout manager to determine what window to use |
| // for the drag window from shelf feature. |
| bool IsFloatedWindowAlignedWithShelf(aura::Window* floated_window) const; |
| |
| // Gets the tuck handle for a floated and tucked window. |
| views::Widget* GetTuckHandleWidget(const aura::Window* floated_window) const; |
| |
| // Called by the resizer when a drag is completed. Updates the bounds and |
| // magnetism of the `floated_window`. |
| void OnDragCompletedForTablet(aura::Window* floated_window); |
| |
| // TODO(shidi): Temporary passing `floated_window` here, will follow-up in |
| // desk logic to use only `active_floated_window_`. |
| // Called by the resizer when a drag is completed by a fling or swipe gesture |
| // event. Updates the magnetism of the window and then tucks the window |
| // offscreen based on `velocity_x` and `velocity_y`. |
| void OnFlingOrSwipeForTablet(aura::Window* floated_window, |
| float velocity_x, |
| float velocity_y); |
| |
| // Returns the desk where floated window belongs to if window is floated and |
| // registered under `floated_window_info_map_`, otherwise returns nullptr. |
| const Desk* FindDeskOfFloatedWindow(const aura::Window* window) const; |
| // Returns the floated window that belongs to `desk`. If `desk` doesn't have a |
| // floated window, returns nullptr. |
| aura::Window* FindFloatedWindowOfDesk(const Desk* desk) const; |
| |
| // Called when moving all `original_desk`'s windows out to `target_desk` due |
| // to the removal of `original_desk`. This function takes care of floated |
| // window (if any) since it doesn't belong to the desk container. Note: during |
| // desk removal/combination, `floated_window` will be unfloated if |
| // `target_desk` has a floated window. |
| void OnMovingAllWindowsOutToDesk(Desk* original_desk, Desk* target_desk); |
| |
| // Called when moving the `floated_window` from `active_desk` to |
| // `target_desk`. This function takes care of floated window since it doesn't |
| // belong to the desk container. Note: Unlike `OnMovingAllWindowsOutToDesk` |
| // above, if `target_desk` has a floated window, it will be unfloated, while |
| // `floated_window` remains floated. Note: When dragging `floated_window` to a |
| // different display, we need to map `floated_window` to the desk container |
| // with same ID on target display's root. |
| void OnMovingFloatedWindowToDesk(aura::Window* floated_window, |
| Desk* active_desk, |
| Desk* target_desk, |
| aura::Window* target_root); |
| |
| void ClearWorkspaceEventHandler(aura::Window* root); |
| |
| // TabletModeObserver: |
| void OnTabletModeStarted() override; |
| void OnTabletModeEnding() override; |
| void OnTabletControllerDestroyed() override; |
| |
| // DesksController::Observer: |
| void OnDeskActivationChanged(const Desk* activated, |
| const Desk* deactivated) override; |
| |
| // display::DisplayObserver: |
| void OnDisplayMetricsChanged(const display::Display& display, |
| uint32_t metrics) override; |
| |
| // ShellObserver: |
| void OnRootWindowAdded(aura::Window* root_window) override; |
| void OnRootWindowWillShutdown(aura::Window* root_window) override; |
| void OnPinnedStateChanged(aura::Window* pinned_window) override; |
| |
| // chromeos::FloatControllerBase: |
| void ToggleFloat(aura::Window* window) override; |
| |
| // ScreenRotationAnimatorObserver: |
| void OnScreenCopiedBeforeRotation() override; |
| void OnScreenRotationAnimationFinished(ScreenRotationAnimator* animator, |
| bool canceled) override; |
| |
| private: |
| class FloatedWindowInfo; |
| friend class DefaultState; |
| friend class TabletModeWindowState; |
| friend class ClientControlledState; |
| friend class WindowFloatTest; |
| FRIEND_TEST_ALL_PREFIXES(WindowFloatMetricsTest, FloatWindowCountPerSession); |
| FRIEND_TEST_ALL_PREFIXES(WindowFloatMetricsTest, |
| FloatWindowMovedToAnotherDeskCountPerSession); |
| |
| // Calls `FloatImpl()` and additionally updates the magnetism if needed. |
| void FloatForTablet(aura::Window* window, |
| chromeos::WindowStateType old_state_type); |
| |
| // Floats/Unfloats `window`. Only one floating window is allowed per desk, |
| // floating a new window on the same desk or moving a floated window to that |
| // desk will unfloat the other floated window (if any). |
| // Note: currently window can only be floated from an active desk. |
| void FloatImpl(aura::Window* window); |
| void UnfloatImpl(aura::Window* window); |
| |
| // Unfloats `floated_window` from the desk it belongs to. |
| void ResetFloatedWindow(aura::Window* floated_window); |
| |
| // Returns the `FloatedWindowInfo` for the given window if it's floated, or |
| // nullptr otherwise. |
| FloatedWindowInfo* MaybeGetFloatedWindowInfo( |
| const aura::Window* window) const; |
| |
| // This is called by `FloatedWindowInfo::OnWindowDestroying` to remove |
| // `floated_window` from `floated_window_info_map_`. |
| void OnFloatedWindowDestroying(aura::Window* floated_window); |
| |
| // Used to map floated window to to its FloatedWindowInfo. |
| // Contains extra info for a floated window such as its pre-float auto managed |
| // state and tablet mode magnetism. |
| base::flat_map<aura::Window*, std::unique_ptr<FloatedWindowInfo>> |
| floated_window_info_map_; |
| |
| // Workspace event handler which handles double click events to change to |
| // maximized state as well as horizontally and vertically maximize. We create |
| // one per root window. |
| base::flat_map<aura::Window*, std::unique_ptr<WorkspaceEventHandler>> |
| workspace_event_handlers_; |
| |
| // Float window counter within a session, used for |
| // `kFloatWindowCountsPerSessionHistogramName`. |
| int floated_window_counter_ = 0; |
| // Counts of how many floated window are moved to another desk within a |
| // session. `kFloatWindowMoveToAnotherDeskCountsHistogramName` |
| int floated_window_move_to_another_desk_counter_ = 0; |
| |
| base::ScopedMultiSourceObservation<ScreenRotationAnimator, |
| ScreenRotationAnimatorObserver> |
| screen_rotation_observations_{this}; |
| |
| base::ScopedObservation<TabletModeController, TabletModeObserver> |
| tablet_mode_observation_{this}; |
| |
| base::ScopedObservation<DesksController, DesksController::Observer> |
| desks_controller_observation_{this}; |
| |
| absl::optional<display::ScopedOptionalDisplayObserver> display_observer_; |
| base::ScopedObservation<Shell, ShellObserver> shell_observation_{this}; |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_WM_FLOAT_FLOAT_CONTROLLER_H_ |