| // Copyright 2023 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_SCOPED_WINDOW_TUCKER_H_ |
| #define ASH_WM_SCOPED_WINDOW_TUCKER_H_ |
| |
| #include <memory> |
| |
| #include "ash/wm/mru_window_tracker.h" |
| #include "ash/wm/overview/overview_controller.h" |
| #include "ash/wm/overview/overview_observer.h" |
| #include "ash/wm/window_state.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/weak_ptr.h" |
| #include "ui/aura/scoped_window_event_targeting_blocker.h" |
| #include "ui/views/controls/button/button.h" |
| #include "ui/views/widget/unique_widget_ptr.h" |
| #include "ui/wm/public/activation_change_observer.h" |
| |
| namespace ash { |
| |
| constexpr char kTuckUserAction[] = "FloatWindowTucked"; |
| constexpr char kUntuckUserAction[] = "FloatWindowUntucked"; |
| |
| // Scoped class which makes modifications while a window is tucked. It owns a |
| // tuck handle widget that will bring the hidden window back onscreen. Users of |
| // the class need to ensure that window outlives instance of this class. |
| class ScopedWindowTucker : public wm::ActivationChangeObserver, |
| public OverviewObserver, |
| public aura::WindowObserver { |
| public: |
| static constexpr int kTuckHandleWidth = 20; |
| static constexpr int kTuckHandleHeight = 92; |
| |
| class Delegate { |
| public: |
| Delegate(); |
| virtual ~Delegate(); |
| |
| // Paint the tuck handle. |
| virtual void PaintTuckHandle(gfx::Canvas* canvas, int width, bool left) = 0; |
| |
| // Returns `kContainerIdsToMove` for the parent of the handle widget. |
| virtual int ParentContainerId() const = 0; |
| |
| // Updates the position of the window. |
| virtual void UpdateWindowPosition(aura::Window* window, bool left) = 0; |
| |
| // Destroys `this_`, which will untuck `window_` and set the window bounds |
| // back onscreen. |
| virtual void UntuckWindow(aura::Window* window) = 0; |
| |
| // Hides the window after the tuck animation is finished. This is so it will |
| // behave similarly to a minimized window in overview. |
| virtual void OnAnimateTuckEnded(aura::Window* window) = 0; |
| |
| // Returns proper bounds for tuck handle. |
| virtual gfx::Rect GetTuckHandleBounds( |
| bool left, |
| const gfx::Rect& window_bounds) const = 0; |
| |
| base::WeakPtr<Delegate> GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| private: |
| base::WeakPtrFactory<Delegate> weak_ptr_factory_{this}; |
| }; |
| |
| // Represents a tuck handle that untucks floated / PiP windows from offscreen. |
| class TuckHandleView : public views::Button, |
| public views::ViewTargeterDelegate { |
| public: |
| TuckHandleView(base::WeakPtr<Delegate> delegate, |
| base::RepeatingClosure callback, |
| bool left); |
| TuckHandleView(const TuckHandleView&) = delete; |
| TuckHandleView& operator=(const TuckHandleView&) = delete; |
| ~TuckHandleView() override; |
| |
| // views::Button: |
| void OnThemeChanged() override; |
| void PaintButtonContents(gfx::Canvas* canvas) override; |
| void OnGestureEvent(ui::GestureEvent* event) override; |
| |
| // views::ViewTargeterDelegate: |
| bool DoesIntersectRect(const views::View* target, |
| const gfx::Rect& rect) const override; |
| |
| private: |
| // The delegate held by `ScopedWindowTucker`. |
| base::WeakPtr<Delegate> scoped_window_tucker_delegate_; |
| |
| // Whether the tuck handle is on the left or right edge of the screen. A |
| // left tuck handle will have the chevron arrow pointing right and vice |
| // versa. |
| const bool left_; |
| }; |
| |
| // Creates an instance for `window` where `left` is the side of the screen |
| // that the tuck handle is on. |
| explicit ScopedWindowTucker(std::unique_ptr<Delegate> delegate, |
| aura::Window* window, |
| bool left); |
| ScopedWindowTucker(const ScopedWindowTucker&) = delete; |
| ScopedWindowTucker& operator=(const ScopedWindowTucker&) = delete; |
| ~ScopedWindowTucker() override; |
| |
| // Returns the target window the resizer was created for. |
| aura::Window* window() { return window_; } |
| |
| // Returns the tuck handle widget that this tucker manages. |
| views::Widget* tuck_handle_widget() { return tuck_handle_widget_.get(); } |
| |
| // Returns true if the window is tucked to the left of the screen edge. |
| bool left() const { return left_; } |
| |
| // Starts the tucking animation. |
| void AnimateTuck(); |
| |
| // Starts the untucking animation. Runs `callback` when the animation |
| // is completed. |
| void AnimateUntuck(base::OnceClosure callback); |
| |
| // Runs `delegate`'s `UntuckWindow()`. |
| void UntuckWindow(); |
| |
| // Runs `delegate`'s `OnAnimateTuckEnded()`. |
| void OnAnimateTuckEnded(); |
| |
| // wm::ActivationChangeObserver: |
| void OnWindowActivated(ActivationReason reason, |
| aura::Window* gained_active, |
| aura::Window* lost_active) override; |
| |
| // OverviewObserver: |
| void OnOverviewModeStarting() override; |
| void OnOverviewModeEndingAnimationComplete(bool canceled) override; |
| |
| // aura::WindowObserver: |
| void OnWindowBoundsChanged(aura::Window* window, |
| const gfx::Rect& old_bounds, |
| const gfx::Rect& new_bounds, |
| ui::PropertyChangeReason reason) override; |
| |
| private: |
| // Initializes the tuck handle widget. |
| void InitializeTuckHandleWidget(); |
| |
| // Slides the tuck handle offscreen and onscreen when entering and exiting |
| // overview mode respectively. |
| void OnOverviewModeChanged(bool in_overview); |
| |
| std::unique_ptr<Delegate> delegate_; |
| |
| // The window that is being tucked. Will be tucked and untucked by the tuck |
| // handle. |
| raw_ptr<aura::Window> window_; |
| |
| // True if the window is tucked to the left screen edge, false otherwise. |
| bool left_ = false; |
| |
| // Blocks events from hitting the window while `this` is alive. |
| aura::ScopedWindowEventTargetingBlocker event_blocker_; |
| |
| views::UniqueWidgetPtr tuck_handle_widget_ = |
| std::make_unique<views::Widget>(); |
| |
| base::ScopedObservation<OverviewController, OverviewObserver> |
| overview_observer_{this}; |
| |
| base::ScopedObservation<aura::Window, aura::WindowObserver> |
| window_observation_{this}; |
| |
| base::WeakPtrFactory<ScopedWindowTucker> weak_factory_{this}; |
| }; |
| |
| } // namespace ash |
| |
| #endif // ASH_WM_SCOPED_WINDOW_TUCKER_H_ |