// Copyright 2017 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. #ifndef UI_AURA_WINDOW_OCCLUSION_TRACKER_H_ #define UI_AURA_WINDOW_OCCLUSION_TRACKER_H_ #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/macros.h" #include "ui/aura/aura_export.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" #include "ui/compositor/layer_animation_observer.h" struct SkIRect; class SkRegion; namespace gfx { class Transform; } namespace aura { namespace test { class WindowOcclusionTrackerTestApi; } // Notifies tracked Windows when their occlusion state change. // // To start tracking the occlusion state of a Window, call // WindowOcclusionTracker::Track(). // // A Window is occluded if its bounds and transform are not animated and one of // these conditions is true: // - The Window is hidden (Window::IsVisible() is true). // - The bounds of the Window are completely covered by opaque and axis-aligned // Windows whose bounds and transform are not animated. // Note that an occluded window may be drawn on the screen by window switching // features such as "Alt-Tab" or "Overview". class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, public WindowObserver { public: // Prevents window occlusion state computations within its scope. If an event // that could cause window occlusion states to change occurs within the scope // of a ScopedPauseOcclusionTracking, window occlusion state computations are // delayed until all ScopedPauseOcclusionTracking objects have been destroyed. class AURA_EXPORT ScopedPauseOcclusionTracking { public: ScopedPauseOcclusionTracking(); ~ScopedPauseOcclusionTracking(); private: DISALLOW_COPY_AND_ASSIGN(ScopedPauseOcclusionTracking); }; // Start tracking the occlusion state of |window|. static void Track(Window* window); private: friend class test::WindowOcclusionTrackerTestApi; struct RootWindowState { // Number of Windows whose occlusion state is tracked under this root // Window. int num_tracked_windows = 0; // Whether the occlusion state of tracked Windows under this root is stale. bool dirty = false; }; WindowOcclusionTracker(); ~WindowOcclusionTracker() override; static WindowOcclusionTracker* GetInstance(); // Recomputes the occlusion state of tracked windows under roots marked as // dirty in |root_windows_| if there are no active // ScopedPauseOcclusionTracking instance. void MaybeComputeOcclusion(); // Recomputes the occlusion state of |window| and its descendants. // |parent_transform_relative_to_root| is the transform of |window->parent()| // relative to the root window. |clipped_bounds| is an optional mask for the // bounds of |window| and its descendants. |occluded_region| is a region // covered by windows which are on top of |window|. Returns true if at least // one window in the hierarchy starting at |window| is NOT_OCCLUDED. bool RecomputeOcclusionImpl( Window* window, const gfx::Transform& parent_transform_relative_to_root, const SkIRect* clipped_bounds, SkRegion* occluded_region); // Removes windows whose bounds and transform are not animated from // |animated_windows_|. Marks the root of those windows as dirty. void CleanupAnimatedWindows(); // If the bounds or transform of |window| are animated and |window| is not in // |animated_windows_|, adds |window| to |animated_windows_| and returns true. bool MaybeObserveAnimatedWindow(Window* window); // Calls SetOccluded() with |is_occluded| as argument for |window| and its // descendants. void SetWindowAndDescendantsAreOccluded(Window* window, bool is_occluded); // Updates the occlusion state of |window| in |tracked_windows_|, based on // |is_occluded| and window->IsVisible(). No-op if |window| is not in // |tracked_windows_|. void SetOccluded(Window* window, bool is_occluded); // Returns true if |window| is in |tracked_windows_|. bool WindowIsTracked(Window* window) const; // Returns true if |window| is in |animated_windows_|. bool WindowIsAnimated(Window* window) const; // If the root of |window| is not dirty and |predicate| is true, marks the // root of |window| as dirty. Then, calls MaybeComputeOcclusion(). // |predicate| is not evaluated if the root of |window| is already dirty when // this is called. template void MarkRootWindowAsDirtyAndMaybeComputeOcclusionIf(Window* window, Predicate predicate); // Marks |root_window| as dirty. void MarkRootWindowAsDirty(RootWindowState* root_window_state); // Returns true if |window| or one of its parents is in |animated_windows_|. bool WindowOrParentIsAnimated(Window* window) const; // Returns true if |window| or one of its descendants is in // |tracked_windows_| and visible. bool WindowOrDescendantIsTrackedAndVisible(Window* window) const; // Returns true if |window| or one of its descendants is visible, opaquely // fills its bounds and is not in |animated_windows_|. If // |assume_parent_opaque| is true, the function assumes that the combined // opacity of window->parent() is 1.0f. If |assume_window_opaque|, the // function assumes that the opacity of |window| is 1.0f. bool WindowOrDescendantIsOpaque(Window* window, bool assume_parent_opaque = false, bool assume_window_opaque = false) const; // Returns true if changing the transform, bounds or stacking order of // |window| could affect the occlusion state of a tracked window. bool WindowMoveMayAffectOcclusionStates(Window* window) const; // Called when a tracked |window| is added to a root window. void TrackedWindowAddedToRoot(Window* window); // Called when a tracked |window| is removed from a root window. void TrackedWindowRemovedFromRoot(Window* window); // Removes |this| from the observer list of |window| and its descendants, // except if they are in |tracked_windows_| or |windows_being_destroyed_|. void RemoveObserverFromWindowAndDescendants(Window* window); // Add |this| to the observer list of |window| and its descendants. void AddObserverToWindowAndDescendants(Window* window); // ui::LayerAnimationObserver: void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override; void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override; void OnLayerAnimationScheduled(ui::LayerAnimationSequence* sequence) override; // WindowObserver: void OnWindowHierarchyChanged(const HierarchyChangeParams& params) override; void OnWindowAdded(Window* window) override; void OnWillRemoveWindow(Window* window) override; void OnWindowVisibilityChanged(Window* window, bool visible) override; void OnWindowBoundsChanged(Window* window, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds, ui::PropertyChangeReason reason) override; void OnWindowOpacitySet(Window* window, ui::PropertyChangeReason reason) override; void OnWindowTransformed(Window* window, ui::PropertyChangeReason reason) override; void OnWindowStackingChanged(Window* window) override; void OnWindowDestroyed(Window* window) override; void OnWindowAddedToRootWindow(Window* window) override; void OnWindowRemovingFromRootWindow(Window* window, Window* new_root) override; void OnWindowLayerRecreated(Window* window) override; // Windows whose occlusion state is tracked. base::flat_map tracked_windows_; // Windows whose bounds or transform are animated. // // To reduce the overhead of the WindowOcclusionTracker, windows in this set // and their descendants are considered non-occluded and cannot occlude other // windows. A window is added to this set the first time that occlusion is // computed after it was animated. It is removed when the animation ends or is // aborted. base::flat_set animated_windows_; // Root Windows of Windows in |tracked_windows_|. base::flat_map root_windows_; // Number of times that the current call to MaybeComputeOcclusion() has // recomputed occlusion states. Always 0 when not in MaybeComputeOcclusion(). int num_times_occlusion_recomputed_ = 0; // Set to true when occlusion is recomputed too many times before it becomes // stable. Reset in // WindowOcclusionTrackerTestApi::WasOcclusionRecomputedTooManyTimes(). bool was_occlusion_recomputed_too_many_times_ = false; DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTracker); }; } // namespace aura #endif // UI_AURA_WINDOW_OCCLUSION_TRACKER_H_