blob: 65da19040612f32c3f3e248bbf7de0681ab2cc7d [file] [log] [blame]
// Copyright 2014 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 ASH_WM_OVERVIEW_OVERVIEW_GRID_H_
#define ASH_WM_OVERVIEW_OVERVIEW_GRID_H_
#include <stddef.h>
#include <memory>
#include <vector>
#include "ash/public/cpp/wallpaper_controller_observer.h"
#include "ash/rotator/screen_rotation_animator_observer.h"
#include "ash/wm/overview/overview_session.h"
#include "ash/wm/window_state.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "ui/aura/window.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
namespace views {
class Widget;
}
namespace ash {
class DesksBarView;
class FpsCounter;
class OverviewGridEventHandler;
class OverviewItem;
class PresentationTimeRecorder;
// Represents a grid of windows in the Overview Mode in a particular root
// window, and manages a selection widget that can be moved with the arrow keys.
// The idea behind the movement strategy is that it should be possible to access
// any window pressing a given arrow key repeatedly.
// +-------+ +-------+ +-------+
// | 0 | | 1 | | 2 |
// +-------+ +-------+ +-------+
// +-------+ +-------+ +-------+
// | 3 | | 4 | | 5 |
// +-------+ +-------+ +-------+
// +-------+
// | 6 |
// +-------+
// Example sequences:
// - Going right to left
// 0, 1, 2, 3, 4, 5, 6
// The selector is switched to the next window grid (if available) or wrapped if
// it reaches the end of its movement sequence.
class ASH_EXPORT OverviewGrid : public ScreenRotationAnimatorObserver,
public WallpaperControllerObserver {
public:
OverviewGrid(aura::Window* root_window,
const std::vector<aura::Window*>& window_list,
OverviewSession* overview_session,
const gfx::Rect& bounds_in_screen);
~OverviewGrid() override;
// Exits overview mode.
void Shutdown();
// Prepares the windows in this grid for overview. This will restore all
// minimized windows and ensure they are visible.
void PrepareForOverview();
// Positions all the windows in rows of equal height scaling each window to
// fit that height. Optionally animates the windows to their targets when
// |animate| is true. Items in |ignored_items| are not positioned. This is for
// dragging. |transition| specifies the overview state when this function is
// called.
void PositionWindows(bool animate,
const base::flat_set<OverviewItem*>& ignored_items = {},
OverviewSession::OverviewTransition transition =
OverviewSession::OverviewTransition::kInOverview);
// Returns the OverviewItem if a window is contained in any of the
// OverviewItems this grid owns. Returns nullptr if no such a OverviewItem
// exist.
OverviewItem* GetOverviewItemContaining(const aura::Window* window) const;
// Adds |window| at the specified |index|. |window| cannot already be on the
// grid. If |reposition| is true, repositions all items except those in
// |ignored_items|. If |animate| is true, animates the repositioning.
// |animate| has no effect if |reposition| is false.
//
// Note: This function should only be called by |OverviewSession::AddItem|.
// |overview_session_| keeps count of all overview items, but this function
// does not update the tally.
void AddItem(aura::Window* window,
bool reposition,
bool animate,
const base::flat_set<OverviewItem*>& ignored_items,
size_t index);
// Similar to the above function, but adds the window to the end of the grid.
void AppendItem(aura::Window* window, bool reposition, bool animate);
// Removes |overview_item| from the grid. |overview_item| cannot already be
// absent from the grid. No items are repositioned.
//
// Note: This function should only be called by |OverviewSession::RemoveItem|
// and |OverviewGrid::Shutdown|. |overview_session_| keeps count of all
// overview items, but this function does not update the tally. If
// |item_destroying| is true, we may want to notify |overview_session_| that
// there are no longer any items. Calls |PositionWindows| to animate the items
// to their new locations if |reposition| is true.
void RemoveItem(OverviewItem* overview_item,
bool item_destroying = false,
bool reposition = false);
// Adds a drop target for |dragged_item|, at the index immediately following
// |dragged_item|. Repositions all items except |dragged_item|, so that the
// drop target takes the place of |dragged_item|. Does not animate the
// repositioning or fade in the drop target. The visual effect is that the
// drop target was already present but was covered by |dragged_item|.
void AddDropTargetForDraggingFromOverview(OverviewItem* dragged_item);
// Removes the drop target from the grid.
void RemoveDropTarget();
// Sets bounds for the window grid and positions all windows in the grid,
// except windows in |ignored_items|.
void SetBoundsAndUpdatePositions(
const gfx::Rect& bounds_in_screen,
const base::flat_set<OverviewItem*>& ignored_items,
bool animate);
// Updates overview bounds and hides the drop target when a preview area is
// shown.
void RearrangeDuringDrag(aura::Window* dragged_window,
IndicatorState indicator_state);
// Updates the desks bar widget bounds if necessary.
void MaybeUpdateDesksWidgetBounds();
// Updates the appearance of the drop target to visually indicate when the
// dragged window is being dragged over it. For dragging from the top, pass
// null for |dragged_item|.
void UpdateDropTargetBackgroundVisibility(
OverviewItem* dragged_item,
const gfx::PointF& location_in_screen);
void UpdateCannotSnapWarningVisibility();
// Called when any OverviewItem on any OverviewGrid has started/ended being
// dragged.
void OnSelectorItemDragStarted(OverviewItem* item);
void OnSelectorItemDragEnded(bool snap);
// Called when a window (either it's browser window or an app window)
// start/continue/end being dragged in tablet mode.
void OnWindowDragStarted(aura::Window* dragged_window, bool animate);
void OnWindowDragContinued(aura::Window* dragged_window,
const gfx::PointF& location_in_screen,
IndicatorState indicator_state);
void OnWindowDragEnded(aura::Window* dragged_window,
const gfx::PointF& location_in_screen,
bool should_drop_window_into_overview,
bool snap);
// Returns true if |window| is the placeholder window from the drop target.
bool IsDropTargetWindow(aura::Window* window) const;
// Returns the overview item that accociates with |drop_target_widget_|.
// Returns nullptr if overview does not have the drop target.
OverviewItem* GetDropTarget();
// ScreenRotationAnimatorObserver:
void OnScreenCopiedBeforeRotation() override;
void OnScreenRotationAnimationFinished(ScreenRotationAnimator* animator,
bool canceled) override;
// WallpaperControllerObserver:
void OnWallpaperChanging() override;
void OnWallpaperChanged() override;
// Called when overview starting animation completes.
void OnStartingAnimationComplete(bool canceled);
// Checks if the grid needs to have the wallpaper animated. Returns false if
// one of the grids windows covers the the entire workspace, true otherwise.
bool ShouldAnimateWallpaper() const;
// Calculates |should_animate_when_entering_| and
// |should_animate_when_exiting_| of the overview items based on where
// the first MRU window covering the available workspace is found.
// |selected_item| is not nullptr if |selected_item| is the selected item when
// exiting overview mode. |target_bounds| are the bounds that the items will
// be in overview. If |tranisition| is exit, |target_bounds| should be empty
// and the overview bounds should be queried from |window_list_|.
void CalculateWindowListAnimationStates(
OverviewItem* selected_item,
OverviewSession::OverviewTransition transition,
const std::vector<gfx::RectF>& target_bounds);
// Do not animate the entire window list during exiting the overview. It's
// used when splitview and overview mode are both active, selecting a window
// will put the window in splitview mode and also end the overview mode. In
// this case the windows in OverviewGrid should not animate when exiting the
// overivew mode. These windows will use ZERO tween so that transforms will
// reset at the end of animation.
void SetWindowListNotAnimatedWhenExiting();
// Starts a nudge, with |item| being the item that may be deleted. This method
// calculates which items in |window_list_| are to be updated, and their
// destination bounds and fills |nudge_data_| accordingly.
void StartNudge(OverviewItem* item);
// Moves items in |nudge_data_| towards their destination bounds based on
// |value|, which must be between 0.0 and 1.0.
void UpdateNudge(OverviewItem* item, double value);
// Clears |nudge_data_|.
void EndNudge();
// Called after PositionWindows when entering overview from the home launcher
// screen. Translates all windows vertically and animates to their final
// locations.
void SlideWindowsIn();
// Update the y position and opacity of the entire grid. Does this by
// transforming the windows in |window_list_|. If |callback| is non null, the
// transformation and opacity change should be animated. The animation
// settings will be set by the caller via |callback|. Returns the settings of
// the first window we are animating; the caller will observe this animation.
// The returned object may be nullptr.
std::unique_ptr<ui::ScopedLayerAnimationSettings> UpdateYPositionAndOpacity(
int new_y,
float opacity,
OverviewSession::UpdateAnimationSettingsCallback callback);
// Returns the window of the overview item that contains |location_in_screen|.
// |ignored_item| is excluded from consideration. Overview items covered by
// |ignored_item| are eligible.
aura::Window* GetTargetWindowOnLocation(const gfx::PointF& location_in_screen,
OverviewItem* ignored_item);
// Returns true when the desks bar view is showing desks mini views (or will
// show them once it is created).
bool IsDesksBarViewActive() const;
// Gets the effective bounds of this grid (the area in which the windows are
// positioned, taking into account the availability of the Desks bar).
gfx::Rect GetGridEffectiveBounds() const;
// Called when a window is being dragged in Overview Mode. If
// |update_desks_bar_drag_details| is true, it will update the drag details
// (screen_location, and whether that location intersects with the
// desks bar widget). |for_drop| should be set to true if this is called when
// the item is being dropped when the drag is complete.
// Returns true if |screen_location| does intersect with the DesksBarView.
bool IntersectsWithDesksBar(const gfx::Point& screen_location,
bool update_desks_bar_drag_details,
bool for_drop);
// Updates the drag details for DesksBarView to end the drag and move the
// window of |drag_item| to another desk if it was dropped on a mini_view of
// a desk that is different than that of the active desk.
// Returns true if the window was successfully moved to another desk.
bool MaybeDropItemOnDeskMiniView(const gfx::Point& screen_location,
OverviewItem* drag_item);
// Prepares the |scroll_offset_min_| as a limit for |scroll_offset| from
// scrolling or positioning windows too far offscreen.
void StartScroll();
// |delta| is used for updating |scroll_offset_| with new scroll values so
// that windows in tablet overview mode get positioned accordingly. Returns
// true if the grid was moved to the edge.
bool UpdateScrollOffset(float delta);
void EndScroll();
// Returns true if the grid has no more windows.
bool empty() const { return window_list_.empty(); }
// Returns how many overview items are in the grid.
size_t size() const { return window_list_.size(); }
// Returns the root window in which the grid displays the windows.
const aura::Window* root_window() const { return root_window_; }
OverviewSession* overview_session() { return overview_session_; }
const std::vector<std::unique_ptr<OverviewItem>>& window_list() const {
return window_list_;
}
const DesksBarView* desks_bar_view() const { return desks_bar_view_; }
const gfx::Rect bounds() const { return bounds_; }
bool should_animate_when_exiting() const {
return should_animate_when_exiting_;
}
void set_suspend_reposition(bool value) { suspend_reposition_ = value; }
views::Widget* drop_target_widget() { return drop_target_widget_.get(); }
float scroll_offset() const { return scroll_offset_; }
OverviewGridEventHandler* grid_event_handler() {
return grid_event_handler_.get();
}
private:
class TargetWindowObserver;
friend class OverviewSessionTest;
// Struct which holds data required to perform nudges.
struct NudgeData {
size_t index;
gfx::RectF src;
gfx::RectF dst;
};
// If the Virtual Desks feature is enabled, it initializes the widget that
// contains the DeskBarView contents.
void MaybeInitDesksWidget();
// Gets the layout of the overview items. Layout is done in 2 stages
// maintaining fixed MRU ordering.
// 1. Optimal height is determined. In this stage |height| is bisected to find
// maximum height which still allows all the windows to fit.
// 2. Row widths are balanced. In this stage the available width is reduced
// until some windows are no longer fitting or until the difference between
// the narrowest and the widest rows starts growing.
// Overall this achieves the goals of maximum size for previews (or maximum
// row height which is equivalent assuming fixed height), balanced rows and
// minimal wasted space.
std::vector<gfx::RectF> GetWindowRects(
const base::flat_set<OverviewItem*>& ignored_items);
// Gets the layout of the overview items. Currently only for tablet mode.
// Positions up to six windows into two rows of equal height, scaling each
// window to fit that height. Additional windows are placed off-screen.
// |ignored_items| won't be shown along with the other windows in overview
// mode.
std::vector<gfx::RectF> GetWindowRectsForTabletModeLayout(
const base::flat_set<OverviewItem*>& ignored_items);
// Attempts to fit all |out_rects| inside |bounds|. The method ensures that
// the |out_rects| vector has appropriate size and populates it with the
// values placing rects next to each other left-to-right in rows of equal
// |height|. While fitting |out_rects| several metrics are collected that can
// be used by the caller. |out_max_bottom| specifies the bottom that the rects
// are extending to. |out_min_right| and |out_max_right| report the right
// bound of the narrowest and the widest rows respectively. In-values of the
// |out_max_bottom|, |out_min_right| and |out_max_right| parameters are
// ignored and their values are always initialized inside this method. Returns
// true on success and false otherwise.
bool FitWindowRectsInBounds(
const gfx::Rect& bounds,
int height,
const base::flat_set<OverviewItem*>& ignored_items,
std::vector<gfx::RectF>* out_rects,
int* out_max_bottom,
int* out_min_right,
int* out_max_right);
// Returns the overview item iterator that contains |window|.
std::vector<std::unique_ptr<OverviewItem>>::iterator
GetOverviewItemIterContainingWindow(aura::Window* window);
// Returns the index of |item| in |window_list_|.
size_t GetOverviewItemIndex(OverviewItem* item) const;
// Adds the |dragged_window| into overview on drag ended. Might need to update
// the window's bounds if it has been resized.
void AddDraggedWindowIntoOverviewOnDragEnd(aura::Window* dragged_window);
// Root window the grid is in.
aura::Window* root_window_;
// Pointer to the OverviewSession that spawned this grid.
OverviewSession* overview_session_;
// Vector containing all the windows in this grid.
std::vector<std::unique_ptr<OverviewItem>> window_list_;
// Widget that contains the DeskBarView contents when the Virtual Desks
// feature is enabled.
std::unique_ptr<views::Widget> desks_widget_;
// The contents view of the above |desks_widget_| if created.
DesksBarView* desks_bar_view_ = nullptr;
// The drop target widget. The drop target is created when a window or
// overview item is being dragged, and is destroyed when the drag ends or
// overview mode is ended. The drop target is hidden when a snap preview area
// is shown. You can drop a window into overview by dragging to the drop
// target or by dragging to almost anywhere while the drop target is shown. A
// plus sign in the center of the drop target indicates tab dragging.
std::unique_ptr<views::Widget> drop_target_widget_;
// The observer of the target window, which is the window that the dragged
// tabs are going to merge into after the drag ends. After the dragged tabs
// merge into the target window, and if the target window is a minimized
// window in overview and is not destroyed yet, we need to update the overview
// minimized widget's content view so that it reflects the merge.
std::unique_ptr<TargetWindowObserver> target_window_observer_;
// True if the overview grid should animate when exiting overview mode. Note
// even if it's true, it doesn't mean all window items in the grid should
// animate when exiting overview, instead each window item's animation status
// is controlled by its own |should_animate_when_exiting_|. But if it's false,
// all window items in the grid don't have animation.
bool should_animate_when_exiting_ = true;
// This OverviewGrid's total bounds in screen coordinates.
gfx::Rect bounds_;
// Collection of the items which should be nudged. This should only be
// non-empty if a nudge is in progress.
std::vector<NudgeData> nudge_data_;
// Measures the animation smoothness of overview animation.
std::unique_ptr<FpsCounter> fps_counter_;
// True to skip |PositionWindows()|. Used to avoid O(n^2) layout when
// reposition windows in tablet overview mode.
bool suspend_reposition_ = false;
// Used by |GetWindowRectsForTabletModeLayout| to shift the x position of the
// overview items.
float scroll_offset_ = 0;
// Value to clamp |scroll_offset| so scrolling stays limited to windows that
// are visible in tablet overview mode.
float scroll_offset_min_ = 0;
// Sum of the deltas passed by |UpdateScrollOffset|, this is so we can ignore
// deltas that are too small for performance reasons.
float scroll_current_delta_ = 0.f;
// Cached values of the item bounds so that they do not have to be calculated
// on each scroll update.
std::vector<gfx::RectF> items_scrolling_bounds_;
// Handles events that are not handled by the OverviewItems.
std::unique_ptr<OverviewGridEventHandler> grid_event_handler_;
// Records the presentation time of scrolling the grid in overview mode.
std::unique_ptr<PresentationTimeRecorder> presentation_time_recorder_;
DISALLOW_COPY_AND_ASSIGN(OverviewGrid);
};
} // namespace ash
#endif // ASH_WM_OVERVIEW_OVERVIEW_GRID_H_