1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef ASH_WM_WINDOW_STATE_H_
6 #define ASH_WM_WINDOW_STATE_H_
7 
8 #include <memory>
9 
10 #include "ash/ash_export.h"
11 #include "ash/display/persistent_window_info.h"
12 #include "ash/wm/drag_details.h"
13 #include "base/gtest_prod_util.h"
14 #include "base/macros.h"
15 #include "base/observer_list.h"
16 #include "base/optional.h"
17 #include "ui/aura/window_observer.h"
18 #include "ui/base/ui_base_types.h"
19 #include "ui/compositor/layer_owner.h"
20 #include "ui/display/display.h"
21 #include "ui/gfx/animation/tween.h"
22 
23 namespace chromeos {
24 enum class WindowPinType;
25 enum class WindowStateType;
26 }
27 
28 namespace gfx {
29 class Rect;
30 }
31 
32 namespace ash {
33 class ClientControlledState;
34 class LockWindowState;
35 class TabletModeWindowState;
36 class WindowState;
37 class WindowStateDelegate;
38 class WindowStateObserver;
39 class WMEvent;
40 
41 // WindowState manages and defines ash specific window state and
42 // behavior. Ash specific per-window state (such as ones that controls
43 // window manager behavior) and ash specific window behavior (such as
44 // maximize, minimize, snap sizing etc) should be added here instead
45 // of defining separate functions (like |MaximizeWindow(aura::Window*
46 // window)|) or using aura Window property.
47 // The WindowState gets created when first accessed by
48 // |WindowState::Get()|, and deleted when the window is deleted.
49 // Prefer using this class instead of passing aura::Window* around in
50 // ash code as this is often what you need to interact with, and
51 // accessing the window using |window()| is cheap.
52 class ASH_EXPORT WindowState : public aura::WindowObserver {
53  public:
54   // The default duration for an animation between two sets of bounds.
55   static constexpr base::TimeDelta kBoundsChangeSlideDuration =
56       base::TimeDelta::FromMilliseconds(120);
57 
58   // A subclass of State class represents one of the window's states
59   // that corresponds to chromeos::WindowStateType in Ash environment, e.g.
60   // maximized, minimized or side snapped, as subclass.
61   // Each subclass defines its own behavior and transition for each WMEvent.
62   class State {
63    public:
State()64     State() {}
~State()65     virtual ~State() {}
66 
67     // Update WindowState based on |event|.
68     virtual void OnWMEvent(WindowState* window_state, const WMEvent* event) = 0;
69 
70     virtual chromeos::WindowStateType GetType() const = 0;
71 
72     // Gets called when the state object became active and the managed window
73     // needs to be adjusted to the State's requirement.
74     // The passed |previous_state| may be used to properly implement state
75     // transitions such as bound animations from the previous state.
76     // Note: This only gets called when the state object gets changed.
77     virtual void AttachState(WindowState* window_state,
78                              State* previous_state) = 0;
79 
80     // Gets called before the state objects gets deactivated / detached from the
81     // window, so that it can save the various states it is interested in.
82     // Note: This only gets called when the state object gets changed.
83     virtual void DetachState(WindowState* window_state) = 0;
84 
85     // Called when the window is being destroyed.
OnWindowDestroying(WindowState * window_state)86     virtual void OnWindowDestroying(WindowState* window_state) {}
87 
88    private:
89     DISALLOW_COPY_AND_ASSIGN(State);
90   };
91 
92   // Returns the WindowState for |window|. Creates WindowState if it doesn't
93   // exist. The returned value is owned by |window| (you should not delete it).
94   static WindowState* Get(aura::Window* window);
95   static const WindowState* Get(const aura::Window* window);
96 
97   // Returns the WindowState for the active window, null if there is no active
98   // window.
99   static WindowState* ForActiveWindow();
100 
101   // Call WindowState::Get() to instantiate this class.
102   ~WindowState() override;
103 
window()104   aura::Window* window() { return window_; }
window()105   const aura::Window* window() const { return window_; }
106 
107   bool HasDelegate() const;
108   void SetDelegate(std::unique_ptr<WindowStateDelegate> delegate);
109 
110   // Returns the window's current ash state type.
111   // Refer to chromeos::WindowStateType definition in wm_types.h as for why Ash
112   // has its own state type.
113   chromeos::WindowStateType GetStateType() const;
114 
115   // Predicates to check window state.
116   bool IsMinimized() const;
117   bool IsMaximized() const;
118   bool IsFullscreen() const;
119   bool IsSnapped() const;
120   bool IsPinned() const;
121   bool IsTrustedPinned() const;
122   bool IsPip() const;
123 
124   // True if the window's state type is chromeos::WindowStateType::kMaximized,
125   // chromeos::WindowStateType::kFullscreen or
126   // chromeos::WindowStateType::kPinned.
127   bool IsMaximizedOrFullscreenOrPinned() const;
128 
129   // True if the window's state type is chromeos::WindowStateType::kNormal or
130   // chromeos::WindowStateType::kDefault.
131   bool IsNormalStateType() const;
132 
133   bool IsNormalOrSnapped() const;
134 
135   bool IsActive() const;
136 
137   // Returns true if the window's location can be controlled by the user.
138   bool IsUserPositionable() const;
139 
140   // Checks if the window can change its state accordingly.
141   bool CanMaximize() const;
142   bool CanMinimize() const;
143   bool CanResize() const;
144   bool CanSnap() const;
145   bool CanActivate() const;
146 
147   // Returns true if the window has restore bounds.
148   bool HasRestoreBounds() const;
149 
150   // These methods use aura::WindowProperty to change the window's state
151   // instead of using WMEvent directly. This is to use the same mechanism as
152   // what views::Widget is using.
153   void Maximize();
154   void Minimize();
155   void Unminimize();
156 
157   void Activate();
158   void Deactivate();
159 
160   // Set the window state to normal.
161   // TODO(oshima): Change to use RESTORE event.
162   void Restore();
163 
164   // Caches, then disables z-ordering state and then stacks |window_| below
165   // |window_on_top| if |window_| currently has a special z-order.
166   void DisableZOrdering(aura::Window* window_on_top);
167 
168   // Restores the z-ordering state that a window might have cached.
169   void RestoreZOrdering();
170 
171   // Invoked when a WMevent occurs, which drives the internal
172   // state machine.
173   void OnWMEvent(const WMEvent* event);
174 
175   // TODO(oshima): Try hiding these methods and making them accessible only to
176   // state impl. State changes should happen through events (as much
177   // as possible).
178 
179   // Saves the current bounds to be used as a restore bounds.
180   void SaveCurrentBoundsForRestore();
181 
182   // Same as |GetRestoreBoundsInScreen| except that it returns the
183   // bounds in the parent's coordinates.
184   gfx::Rect GetRestoreBoundsInParent() const;
185 
186   // Returns the restore bounds property on the window in the virtual screen
187   // coordinates. The bounds can be NULL if the bounds property does not
188   // exist for the window. The window owns the bounds object.
189   gfx::Rect GetRestoreBoundsInScreen() const;
190 
191   // Same as |SetRestoreBoundsInScreen| except that the bounds is in the
192   // parent's coordinates.
193   void SetRestoreBoundsInParent(const gfx::Rect& bounds_in_parent);
194 
195   // Sets the restore bounds property on the window in the virtual screen
196   // coordinates.  Deletes existing bounds value if exists.
197   void SetRestoreBoundsInScreen(const gfx::Rect& bounds_in_screen);
198 
199   // Deletes and clears the restore bounds property on the window.
200   void ClearRestoreBounds();
201 
202   // Replace the State object of a window with a state handler which can
203   // implement a new window manager type. The passed object will be owned
204   // by this object and the returned object will be owned by the caller.
205   std::unique_ptr<State> SetStateObject(std::unique_ptr<State> new_state);
206 
207   // Updates |snapped_width_ratio_| based on |event|.
208   void UpdateSnappedWidthRatio(const WMEvent* event);
snapped_width_ratio()209   base::Optional<float> snapped_width_ratio() const {
210     return snapped_width_ratio_;
211   }
212 
213   // True if the window should be unminimized to the restore bounds, as
214   // opposed to the window's current bounds. |unminimized_to_restore_bounds_| is
215   // reset to the default value after the window is unminimized.
unminimize_to_restore_bounds()216   bool unminimize_to_restore_bounds() const {
217     return unminimize_to_restore_bounds_;
218   }
set_unminimize_to_restore_bounds(bool value)219   void set_unminimize_to_restore_bounds(bool value) {
220     unminimize_to_restore_bounds_ = value;
221   }
222 
223   // Gets/sets whether the shelf should be hidden when this window is
224   // fullscreen.
225   bool GetHideShelfWhenFullscreen() const;
226   void SetHideShelfWhenFullscreen(bool value);
227 
228   // Gets/sets whether the shelf should be autohidden when this window is
229   // fullscreen or active.
230   // Note: if true, this will override the logic controlled by
231   // hide_shelf_when_fullscreen.
autohide_shelf_when_maximized_or_fullscreen()232   bool autohide_shelf_when_maximized_or_fullscreen() const {
233     return autohide_shelf_when_maximized_or_fullscreen_;
234   }
235 
set_autohide_shelf_when_maximized_or_fullscreen(bool value)236   void set_autohide_shelf_when_maximized_or_fullscreen(bool value) {
237     autohide_shelf_when_maximized_or_fullscreen_ = value;
238   }
239 
240   // Gets/Sets the bounds of the window before it was moved by the auto window
241   // management. As long as it was not auto-managed, it will return NULL.
pre_auto_manage_window_bounds()242   const base::Optional<gfx::Rect> pre_auto_manage_window_bounds() {
243     return pre_auto_manage_window_bounds_;
244   }
245   void SetPreAutoManageWindowBounds(const gfx::Rect& bounds);
246 
247   // Gets/Sets the property that is used on window added to workspace event.
pre_added_to_workspace_window_bounds()248   const base::Optional<gfx::Rect> pre_added_to_workspace_window_bounds() {
249     return pre_added_to_workspace_window_bounds_;
250   }
251   void SetPreAddedToWorkspaceWindowBounds(const gfx::Rect& bounds);
252 
253   // Gets/Sets the persistent window info that is used on restoring persistent
254   // window bounds in multi-displays scenario.
persistent_window_info()255   const base::Optional<PersistentWindowInfo> persistent_window_info() {
256     return persistent_window_info_;
257   }
258   void SetPersistentWindowInfo(
259       const PersistentWindowInfo& persistent_window_info);
260   void ResetPersistentWindowInfo();
261 
262   // Layout related properties
263 
264   void AddObserver(WindowStateObserver* observer);
265   void RemoveObserver(WindowStateObserver* observer);
266 
267   // Whether the window is being dragged.
is_dragged()268   bool is_dragged() const { return !!drag_details_; }
269 
270   // Whether or not the window's position can be managed by the
271   // auto management logic.
272   bool GetWindowPositionManaged() const;
273   void SetWindowPositionManaged(bool managed);
274 
275   // Whether or not the window's position or size was changed by a user.
bounds_changed_by_user()276   bool bounds_changed_by_user() const { return bounds_changed_by_user_; }
277   void set_bounds_changed_by_user(bool bounds_changed_by_user);
278 
279   // True if the window should be offered a chance to consume special system
280   // keys such as brightness, volume, etc. that are usually handled by the
281   // shell.
282   bool CanConsumeSystemKeys() const;
283   void SetCanConsumeSystemKeys(bool can_consume_system_keys);
284 
285   // True if the window is in "immersive full screen mode" which is slightly
286   // different from the normal fullscreen mode by allowing the user to reveal
287   // the top portion of the window through a touch / mouse gesture. It might
288   // also allow the shelf to be shown in some situations.
289   bool IsInImmersiveFullscreen() const;
290 
291   // True if the window should not adjust the window's bounds when
292   // virtual keyboard bounds changes.
293   // TODO(oshima): This is hack. Replace this with proper
294   // implementation based on EnsureCaretNotInRect.
ignore_keyboard_bounds_change()295   bool ignore_keyboard_bounds_change() const {
296     return ignore_keyboard_bounds_change_;
297   }
set_ignore_keyboard_bounds_change(bool ignore_keyboard_bounds_change)298   void set_ignore_keyboard_bounds_change(bool ignore_keyboard_bounds_change) {
299     ignore_keyboard_bounds_change_ = ignore_keyboard_bounds_change;
300   }
301 
302   // True if the window bounds can be updated directly using SET_BOUNDS event.
set_allow_set_bounds_direct(bool value)303   void set_allow_set_bounds_direct(bool value) {
304     allow_set_bounds_direct_ = value;
305   }
allow_set_bounds_direct()306   bool allow_set_bounds_direct() const { return allow_set_bounds_direct_; }
307 
308   // Creates and takes ownership of a pointer to DragDetails when resizing is
309   // active. This should be done before a resizer gets created.
310   void CreateDragDetails(const gfx::PointF& point_in_parent,
311                          int window_component,
312                          ::wm::WindowMoveSource source);
313 
314   // Deletes and clears a pointer to DragDetails. This should be done when the
315   // resizer gets destroyed.
316   void DeleteDragDetails();
317 
318   // Sets the currently stored restore bounds and clears the restore bounds.
319   void SetAndClearRestoreBounds();
320 
321   // Notifies that the drag operation has been started.
322   void OnDragStarted(int window_component);
323 
324   // Notifies that the drag operation has been either completed or reverted.
325   // |location| is the last position of the pointer device used to drag.
326   void OnCompleteDrag(const gfx::PointF& location);
327   void OnRevertDrag(const gfx::PointF& location);
328 
329   // Notifies that the window lost the activation.
330   void OnActivationLost();
331 
332   // Returns a pointer to DragDetails during drag operations.
drag_details()333   const DragDetails* drag_details() const { return drag_details_.get(); }
drag_details()334   DragDetails* drag_details() { return drag_details_.get(); }
335 
336   // Returns the Display that this WindowState is on.
337   display::Display GetDisplay();
338 
339   class TestApi {
340    public:
GetStateImpl(WindowState * window_state)341     static State* GetStateImpl(WindowState* window_state) {
342       return window_state->current_state_.get();
343     }
344   };
345 
346  private:
347   friend class BaseState;
348   friend class ClientControlledState;
349   friend class DefaultState;
350   friend class LockWindowState;
351   friend class TabletModeWindowState;
352   friend class ScopedBoundsChangeAnimation;
353   FRIEND_TEST_ALL_PREFIXES(WindowAnimationsTest, CrossFadeToBounds);
354   FRIEND_TEST_ALL_PREFIXES(WindowAnimationsTest, CrossFadeHistograms);
355   FRIEND_TEST_ALL_PREFIXES(WindowAnimationsTest,
356                            CrossFadeToBoundsFromTransform);
357   FRIEND_TEST_ALL_PREFIXES(WindowStateTest, PipWindowMaskRecreated);
358   FRIEND_TEST_ALL_PREFIXES(WindowStateTest, PipWindowHasMaskLayer);
359 
360   // Animation type of updating window bounds. "IMMEDIATE" means update bounds
361   // directly without animation. "STEP_END" means update bounds at the end of
362   // the animation.
363   enum class BoundsChangeAnimationType { DEFAULT, IMMEDIATE, STEP_END };
364 
365   // A class can temporarily change the window bounds change animation type.
366   class ScopedBoundsChangeAnimation : public aura::WindowObserver {
367    public:
368     ScopedBoundsChangeAnimation(aura::Window* window,
369                                 BoundsChangeAnimationType animation_type);
370     ~ScopedBoundsChangeAnimation() override;
371 
372     // aura::WindowObserver:
373     void OnWindowDestroying(aura::Window* window) override;
374 
375    private:
376     aura::Window* window_;
377     BoundsChangeAnimationType previous_bounds_animation_type_;
378 
379     DISALLOW_COPY_AND_ASSIGN(ScopedBoundsChangeAnimation);
380   };
381 
382   explicit WindowState(aura::Window* window);
383 
delegate()384   WindowStateDelegate* delegate() { return delegate_.get(); }
bounds_animation_type()385   BoundsChangeAnimationType bounds_animation_type() {
386     return bounds_animation_type_;
387   }
388 
389   bool HasMaximumWidthOrHeight() const;
390 
391   // Returns the window's current z-ordering state.
392   ui::ZOrderLevel GetZOrdering() const;
393 
394   // Returns the window's current show state.
395   ui::WindowShowState GetShowState() const;
396 
397   // Return the window's current pin type.
398   chromeos::WindowPinType GetPinType() const;
399 
400   // Sets the window's bounds in screen coordinates.
401   void SetBoundsInScreen(const gfx::Rect& bounds_in_screen);
402 
403   // Adjusts the |bounds| so that they are flush with the edge of the
404   // workspace if the window represented by |window_state| is side snapped. It
405   // is called for workspace events.
406   void AdjustSnappedBounds(gfx::Rect* bounds);
407 
408   // Updates the window properties(show state, pin type) according to the
409   // current window state type.
410   // Note that this does not update the window bounds.
411   void UpdateWindowPropertiesFromStateType();
412 
413   void NotifyPreStateTypeChange(
414       chromeos::WindowStateType old_window_state_type);
415   void NotifyPostStateTypeChange(
416       chromeos::WindowStateType old_window_state_type);
417 
418   // Sets |bounds| as is and ensure the layer is aligned with pixel boundary.
419   void SetBoundsDirect(const gfx::Rect& bounds);
420 
421   // Sets the window's |bounds| with constraint where the size of the
422   // new bounds will not exceeds the size of the work area.
423   void SetBoundsConstrained(const gfx::Rect& bounds);
424 
425   // Sets the wndow's |bounds| and transitions to the new bounds with
426   // a scale animation, with duration specified by |duration|.
427   void SetBoundsDirectAnimated(
428       const gfx::Rect& bounds,
429       base::TimeDelta duration = kBoundsChangeSlideDuration,
430       gfx::Tween::Type animation_type = gfx::Tween::LINEAR);
431 
432   // Sets the window's |bounds| and transition to the new bounds with
433   // a cross fade animation.
434   void SetBoundsDirectCrossFade(const gfx::Rect& bounds);
435 
436   // Called before the state change and update PIP related state, such as next
437   // window animation type, upon state change.
438   void OnPrePipStateChange(chromeos::WindowStateType old_window_state_type);
439 
440   // Called after the state change and update PIP related state, such as next
441   // window animation type, upon state change.
442   void OnPostPipStateChange(chromeos::WindowStateType old_window_state_type);
443 
444   // Update the PIP bounds if necessary. This may need to happen when the
445   // display work area changes, or if system ui regions like the virtual
446   // keyboard position changes.
447   void UpdatePipBounds();
448 
449   // Collects PIP enter and exit metrics:
450   void CollectPipEnterExitMetrics(bool enter);
451 
452   // aura::WindowObserver:
453   void OnWindowPropertyChanged(aura::Window* window,
454                                const void* key,
455                                intptr_t old) override;
456   void OnWindowAddedToRootWindow(aura::Window* window) override;
457   void OnWindowDestroying(aura::Window* window) override;
458   void OnWindowBoundsChanged(aura::Window* window,
459                              const gfx::Rect& old_bounds,
460                              const gfx::Rect& new_bounds,
461                              ui::PropertyChangeReason reason) override;
462 
463   // The owner of this window settings.
464   aura::Window* window_;
465   std::unique_ptr<WindowStateDelegate> delegate_;
466 
467   bool bounds_changed_by_user_;
468   bool can_consume_system_keys_;
469   std::unique_ptr<DragDetails> drag_details_;
470 
471   bool unminimize_to_restore_bounds_;
472   bool ignore_keyboard_bounds_change_ = false;
473   bool hide_shelf_when_fullscreen_;
474   bool autohide_shelf_when_maximized_or_fullscreen_;
475   ui::ZOrderLevel cached_z_order_;
476   bool allow_set_bounds_direct_ = false;
477 
478   // A property to save the ratio between snapped window width and display
479   // workarea width. It is used to update snapped window width on
480   // AdjustSnappedBounds() when handling workspace events.
481   base::Optional<float> snapped_width_ratio_;
482 
483   // A property to remember the window position which was set before the
484   // auto window position manager changed the window bounds, so that it can
485   // get restored when only this one window gets shown.
486   base::Optional<gfx::Rect> pre_auto_manage_window_bounds_;
487 
488   // A property which resets when bounds is changed by user and sets when it
489   // is nullptr, and window is removing from a workspace.
490   base::Optional<gfx::Rect> pre_added_to_workspace_window_bounds_;
491 
492   // A property to remember the persistent window info used in multi-displays
493   // scenario to attempt to restore windows to their original bounds when
494   // displays are restored to their previous states.
495   base::Optional<PersistentWindowInfo> persistent_window_info_;
496 
497   base::ObserverList<WindowStateObserver>::Unchecked observer_list_;
498 
499   // True to ignore a property change event to avoid reentrance in
500   // UpdateWindowStateType()
501   bool ignore_property_change_;
502 
503   std::unique_ptr<State> current_state_;
504 
505   // The animation type for the bounds change.
506   BoundsChangeAnimationType bounds_animation_type_ =
507       BoundsChangeAnimationType::DEFAULT;
508 
509   // When the current (or last) PIP session started.
510   base::TimeTicks pip_start_time_;
511 
512   DISALLOW_COPY_AND_ASSIGN(WindowState);
513 };
514 
515 }  // namespace ash
516 
517 #endif  // ASH_WM_WINDOW_STATE_H_
518