// 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_OVERVIEW_OVERVIEW_ITEM_BASE_H_
#define ASH_WM_OVERVIEW_OVERVIEW_ITEM_BASE_H_
#include <memory>
#include <optional>
#include <vector>
#include "ash/ash_export.h"
#include "ash/style/system_shadow.h"
#include "ash/wm/overview/event_handler_delegate.h"
#include "ash/wm/overview/overview_types.h"
#include "base/cancelable_callback.h"
#include "base/memory/raw_ptr.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/views/widget/widget.h"
namespace gfx {
class RectF;
class RoundedCornersF;
} // namespace gfx
namespace ui {
class GestureEvent;
class MouseEvent;
} // namespace ui
namespace views {
class View;
} // namespace views
namespace ash {
class DragWindowController;
class OverviewGrid;
class OverviewItem;
class OverviewSession;
class RoundedLabelWidget;
class SystemShadow;
// Defines the interface for the overview item which will be implemented by
// `OverviewItem`, `OverviewGroupItem` or `OverviewDropTarget`. The
// `OverviewGrid` creates and owns the top-level concrete instance of this
// interface.
class ASH_EXPORT OverviewItemBase : public EventHandlerDelegate {
public:
OverviewItemBase(OverviewSession* overview_session,
OverviewGrid* overview_grid,
aura::Window* root_window);
OverviewItemBase(const OverviewItemBase&) = delete;
OverviewItemBase& operator=(const OverviewItemBase&) = delete;
~OverviewItemBase() override;
// Creates an instance of the `this` based on whether the given `window`
// belongs to a snap group or not.
static std::unique_ptr<OverviewItemBase> Create(
aura::Window* window,
OverviewSession* overview_session,
OverviewGrid* overview_grid);
void set_should_animate_when_entering(bool should_animate) {
should_animate_when_entering_ = should_animate;
}
bool should_animate_when_entering() const {
return should_animate_when_entering_;
}
bool should_animate_when_exiting() const {
return should_animate_when_exiting_;
}
void set_should_animate_when_exiting(bool should_animate) {
should_animate_when_exiting_ = should_animate;
}
void set_should_restack_on_animation_end(bool val) {
should_restack_on_animation_end_ = val;
}
aura::Window* root_window() { return root_window_; }
const aura::Window* root_window() const { return root_window_; }
OverviewGrid* overview_grid() { return overview_grid_; }
views::Widget* item_widget() { return item_widget_.get(); }
const views::Widget* item_widget() const { return item_widget_.get(); }
const gfx::RectF& target_bounds() const { return target_bounds_; }
bool is_moving_to_another_desk() const { return is_moving_to_another_desk_; }
bool animating_to_close() const { return animating_to_close_; }
void set_unclipped_size(std::optional<gfx::Size> unclipped_size) {
unclipped_size_ = unclipped_size;
}
void set_scrolling_bounds(std::optional<gfx::RectF> scrolling_bounds) {
scrolling_bounds_ = scrolling_bounds;
}
std::optional<gfx::RectF> scrolling_bounds() const {
return scrolling_bounds_;
}
bool should_use_spawn_animation() const {
return should_use_spawn_animation_;
}
// Returns true if `this` is currently being dragged.
bool IsDragItem() const;
// Shows/Hides window item during window dragging. Used when swiping up a
// window from shelf.
void SetVisibleDuringItemDragging(bool visible, bool animate);
// Refreshes visuals of the `shadow_` by setting the visibility and updating
// the bounds.
void RefreshShadowVisuals(bool shadow_visible);
// Updates the type for the `shadow_` while being dragged and dropped.
void UpdateShadowTypeForDrag(bool is_dragging);
// If in tablet mode, maybe forward events to `OverviewGridEventHandler` as we
// might want to process scroll events on `this`. `event_source_item`
// specifies the sender of the event.
void HandleGestureEventForTabletModeLayout(
ui::GestureEvent* event,
OverviewItemBase* event_source_item);
// Updates the opacity of `item_widget_`, all the window(s) owned by `this`
// and `cannot_snap_widget_`.
virtual void SetOpacity(float opacity);
// Returns the list of windows that we want to slide up or down when swiping
// on the shelf in tablet mode.
virtual aura::Window::Windows GetWindowsForHomeGesture();
// Hides the overview item. This is used to hide any overview items that may
// be present when entering the saved desk library. Animates `item_widget_`
// and the windows in the transient tree to 0 opacity if `animate` is true,
// otherwise just sets them to 0 opacity.
virtual void HideForSavedDeskLibrary(bool animate);
// Re-shows overview items that were hidden by the saved desk library. Called
// when exiting the saved desk library and going back to the overview grid.
// Fades the overview items in if `animate` is true, otherwise shows them
// immediately.
virtual void RevertHideForSavedDeskLibrary(bool animate);
// Updates and maybe creates the mirrors needed for multi-display dragging.
virtual void UpdateMirrorsForDragging(bool is_touch_dragging);
// Resets the mirrors needed for multi display dragging.
virtual void DestroyMirrorsForDragging();
// Returns the window associated with this, which can be a single window or
// a list of windows.
// TODO(michelefan): This is temporarily added to reduce the scope of the
// task, which will be replaced by `GetWindows()` in a follow-up cl.
virtual aura::Window* GetWindow() = 0;
// Returns the window(s) associated with this, which can be a single window or
// a list of windows.
virtual std::vector<raw_ptr<aura::Window, VectorExperimental>>
GetWindows() = 0;
// Returns true if all the windows represented by `this` are visible on all
// workspaces.
virtual bool HasVisibleOnAllDesksWindow() = 0;
// Returns true if the given `target` is contained within `this`.
virtual bool Contains(const aura::Window* target) const = 0;
// Returns the direct `OverviewItem` that represents the given `window`. This
// is temporarily added for the current overview tests, we should avoid using
// this API moving forward.
// TODO(b/297580539): Completely get rid of this API.
virtual OverviewItem* GetLeafItemForWindow(aura::Window* window) = 0;
// Restores and animates the managed window(s) to its non overview mode state.
// Animates if `animate` is true. If `reset_transform` equals true, the
// window's transform will be reset to identity transform when exiting
// overview mode. It's needed when dragging an Arc app window in overview mode
// to put it in split screen. In this case the restore of its transform needs
// to be deferred until the Arc app window is snapped successfully, otherwise
// the animation will look very ugly (the Arc app window enlarges itself to
// maximized window bounds and then shrinks to its snapped window bounds).
// Note if the window's transform is not reset here, it must be reset by
// someone else at some point.
virtual void RestoreWindow(bool reset_transform, bool animate) = 0;
// Sets the bounds of `this` to `target_bounds` in the `root_window_`. The
// bounds change will be animated as specified by `animation_type`.
virtual void SetBounds(const gfx::RectF& target_bounds,
OverviewAnimationType animation_type) = 0;
// TODO(http://b/297923747): Integrate continuous animation with snap groups.
virtual gfx::Transform ComputeTargetTransform(
const gfx::RectF& target_bounds) = 0;
// Returns the union of the original target bounds of all transformed windows
// represented by `this`, i.e. all regular (normal or transient descendants of
// the windows returned by `GetWindows()`).
virtual gfx::RectF GetWindowsUnionScreenBounds() const = 0;
// Returns the `target_bounds_` of the `this` with insets of the header.
virtual gfx::RectF GetTargetBoundsWithInsets() const = 0;
// Returns the transformed bound of `this`.
virtual gfx::RectF GetTransformedBounds() const = 0;
// Calculates and returns an optimal scale ratio. Only the given `height` is
// taken into account as the width can vary.
virtual float GetItemScale(int height) = 0;
// Increases the bounds of the dragged item.
virtual void ScaleUpSelectedItem(OverviewAnimationType animation_type) = 0;
// Ensures that a possibly minimized window becomes visible after restore.
virtual void EnsureVisible() = 0;
// Returns the focusable widgets contained in `this`.
virtual std::vector<views::Widget*> GetFocusableWidgets() = 0;
// Returns the backdrop view of `this`.
virtual views::View* GetBackDropView() const = 0;
// Returns true if `shadow_` should be created on the item, false otherwise.
virtual bool ShouldHaveShadow() const = 0;
// Updates the rounded corners and shadow on `this`.
virtual void UpdateRoundedCornersAndShadow() = 0;
virtual float GetOpacity() const = 0;
// Dispatched before entering overview.
virtual void PrepareForOverview() = 0;
virtual void SetShouldUseSpawnAnimation(bool value) = 0;
// Called when the starting animation is completed, or called immediately
// if there was no starting animation to do any necessary visual changes.
virtual void OnStartingAnimationComplete() = 0;
// Inserts the item back to its original stacking order so that the order of
// overview items is the same as when entering overview.
virtual void Restack() = 0;
// Called before dragging. Scales up the windows(s) hosted by `this` a little
// bit to indicate its selection and stacks the window(s) at the top of the Z
// order in order to keep them visible while being dragged around.
virtual void StartDrag() = 0;
virtual void OnOverviewItemDragStarted() = 0;
virtual void OnOverviewItemDragEnded(bool snap) = 0;
// Called when performing the continuous scroll on overview item to set
// transform and opacity with pre-calculated `target_transform`.
virtual void OnOverviewItemContinuousScroll(
const gfx::Transform& target_transform,
float scroll_ratio) = 0;
// Shows the cannot snap warning if currently in splitview, and the associated
// item cannot be snapped.
virtual void UpdateCannotSnapWarningVisibility(bool animate) = 0;
// Hides the cannot snap warning (if it was showing) until the next call to
// `UpdateCannotSnapWarningVisibility`.
virtual void HideCannotSnapWarning(bool animate) = 0;
// Called when `this` is dragged and dropped on the mini view of another
// desk, which prepares `this` for being removed from the grid, and the
// window(s) to restore its transform.
virtual void OnMovingItemToAnotherDesk() = 0;
// Called when the `OverviewGrid` shuts down to reset the `item_widget_` and
// remove window(s) from `ScopedOverviewHideWindows`.
virtual void Shutdown() = 0;
// Slides `this` up or down and then closes the associated window(s). Used in
// overview swipe to close.
virtual void AnimateAndCloseItem(bool up) = 0;
// Stops the current animation of `item_widget_`.
virtual void StopWidgetAnimation() = 0;
virtual OverviewItemFillMode GetOverviewItemFillMode() const = 0;
// Updates the `OverviewItemFillMode` for this item.
virtual void UpdateOverviewItemFillMode() = 0;
virtual const gfx::RoundedCornersF GetRoundedCorners() const = 0;
// EventHandlerDelegate:
void HandleMouseEvent(const ui::MouseEvent& event,
OverviewItemBase* event_source_item) override;
void HandleGestureEvent(ui::GestureEvent* event,
OverviewItemBase* event_source_item) override;
void set_target_bounds_for_testing(const gfx::RectF& target_bounds) {
target_bounds_ = target_bounds;
}
SystemShadow* shadow_for_testing() { return shadow_.get(); }
gfx::Rect get_shadow_content_bounds_for_testing() const {
return shadow_ ? shadow_.get()->GetContentBounds() : gfx::Rect();
}
DragWindowController* item_mirror_for_dragging_for_testing() {
return item_mirror_for_dragging_.get();
}
RoundedLabelWidget* get_cannot_snap_widget_for_testing() {
return cannot_snap_widget_.get();
}
const std::optional<gfx::Size>& unclipped_size_for_testing() const {
return unclipped_size_;
}
protected:
// Returns the widget init params needed to create the `item_widget_`.
views::Widget::InitParams CreateOverviewItemWidgetParams(
aura::Window* parent_window,
const std::string& widget_name,
bool accept_events) const;
// Creates the `shadow_` and stacks the shadow layer to be at the bottom after
// `item_widget_` has been created.
void CreateShadow();
// Drag event can be handled differently based on the concreate instance of
// `this`. For `OverviewItem`, the drag will be on window-level. For
// `OverviewGroupItem`, the drag will be on group-leve.
virtual void HandleDragEvent(const gfx::PointF& location_in_screen);
// The root window `this` is being displayed on.
raw_ptr<aura::Window> root_window_;
// Pointer to the overview session that owns the `overview_grid_` containing
// `this`. Guaranteed to be non-null for the lifetime of `this`.
const raw_ptr<OverviewSession> overview_session_;
// Pointer to the `OverviewGrid` that contains `this`. Guaranteed to be
// non-null for the lifetime of `this`.
const raw_ptr<OverviewGrid> overview_grid_;
bool prepared_for_overview_ = false;
// A widget stacked under the window(s). The widget has `OverviewItemView` or
// `OverviewGroupContainerView` as its contents view. The widget is backed by
// a NOT_DRAWN layer since most of its surface is transparent.
std::unique_ptr<views::Widget> item_widget_;
// The target bounds `this` is fit within. When in splitview, `item_widget_`
// is fit within these bounds, but the window itself is transformed to
// `unclipped_size_`, and then clipped.
gfx::RectF target_bounds_;
// The shadow around `this`.
std::unique_ptr<SystemShadow> shadow_;
// True when `this` is dragged and dropped on another desk's mini view and the
// transform needs to be restored immediately without any animations.
bool is_moving_to_another_desk_ = false;
// True if the window(s) are still alive so they can have a closing animation.
// These windows should not be used in calculations for
// `OverviewGrid::PositionWindows()`.
bool animating_to_close_ = false;
// True if `this` should animate during the entering animation.
bool should_animate_when_entering_ = true;
// True if `this` should animate during the exiting animation.
bool should_animate_when_exiting_ = true;
// True if we need to reorder the stacking order of the widgets after an
// animation.
bool should_restack_on_animation_end_ = false;
// A widget with text that may show up on top of the window(s) to notify
// users `this` cannot be snapped.
std::unique_ptr<RoundedLabelWidget> cannot_snap_widget_;
// Contains a value if there is a snapped window, or a window about to be
// snapped (triggering a splitview preview area), which will be set when items
// are positioned in OverviewGrid. `SetBounds()` calculates the actual bounds
// of `this`, but we want to maintain the aspect ratio of the windows, whose
// bounds are not set to split view size. In `OverviewItem::SetItemBounds()`,
// to this value instead of `target_bounds_`, and then apply clipping on the
// window to `target_bounds_`.
std::optional<gfx::Size> unclipped_size_ = std::nullopt;
// Cached bounds of `this` to avoid being calculated on each scroll update.
// Will be nullopt unless a grid scroll is underway.
std::optional<gfx::RectF> scrolling_bounds_ = std::nullopt;
// True if `this` should be added to an active overview session using the
// spawn animation on its first update, which implies an animation type of
// `OVERVIEW_ANIMATION_SPAWN_ITEM_IN_OVERVIEW`. False otherwise. Reset the
// value to false on spawn animation completed.
bool should_use_spawn_animation_ = false;
private:
friend class OverviewTestBase;
void HideItemWidgetWindow();
void ShowItemWidgetWindow();
// TODO(sammiequon): Current events go from `OverviewItemView` to
// `EventHandlerDelegate` to `OverviewSession` to
// `OverviewWindowDragController`. We may be able to shorten this pipeline.
void HandlePressEvent(const gfx::PointF& location_in_screen,
bool from_touch_gesture,
OverviewItemBase* event_source_item);
void HandleReleaseEvent(const gfx::PointF& location_in_screen);
void HandleLongPressEvent(const gfx::PointF& location_in_screen);
void HandleFlingStartEvent(const gfx::PointF& location_in_screen,
float velocity_x,
float velocity_y);
void HandleTapEvent(const gfx::PointF& location_in_screen,
OverviewItemBase* event_source_item);
void HandleGestureEndEvent();
// Cancellable callback to ensure that we are not going to hide the window
// after reverting the hide.
base::CancelableOnceClosure hide_window_in_overview_callback_;
// Used to block events from reaching the item widget when the overview item
// has been hidden.
std::unique_ptr<aura::ScopedWindowEventTargetingBlocker>
item_widget_event_blocker_;
// Responsible for mirrors that look like the `item_widget_` on all displays
// during dragging.
std::unique_ptr<DragWindowController> item_mirror_for_dragging_;
base::WeakPtrFactory<OverviewItemBase> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_WM_OVERVIEW_OVERVIEW_ITEM_BASE_H_