#include "ash/wm/splitview/split_view_highlight_view.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/shell.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/splitview/split_view_types.h"
#include "base/i18n/rtl.h"
#include "base/memory/raw_ptr.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
#include "ui/compositor/layer.h"
#include "ui/views/background.h"
#include "ui/views/highlight_border.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
constexpr int kHighlightScreenRoundRectRadius = 12;
class ClippingObserver : public ui::ImplicitAnimationObserver,
public views::ViewObserver {
public:
ClippingObserver(views::View* view, std::optional<gfx::Rect> bounds)
: view_(view), bounds_(bounds) {
view_->AddObserver(this);
}
~ClippingObserver() override { view_->RemoveObserver(this); }
void OnImplicitAnimationsCompleted() override {
view_->layer()->SetClipRect(gfx::Rect());
if (bounds_)
view_->SetBoundsRect(*bounds_);
delete this;
}
void OnViewIsDeleting(views::View* observed_view) override {
DCHECK_EQ(view_, observed_view);
delete this;
}
private:
const raw_ptr<views::View> view_;
std::optional<gfx::Rect> bounds_;
};
}
SplitViewHighlightView::SplitViewHighlightView(bool is_right_or_bottom)
: is_right_or_bottom_(is_right_or_bottom) {
SetPaintToLayer();
layer()->SetFillsBoundsOpaquely(false);
SetBackground(views::CreateRoundedRectBackground(
cros_tokens::kCrosSysPrimary, kHighlightScreenRoundRectRadius));
SetBorder(std::make_unique<views::HighlightBorder>(
kHighlightScreenRoundRectRadius,
views::HighlightBorder::Type::kHighlightBorderNoShadow));
}
SplitViewHighlightView::~SplitViewHighlightView() = default;
void SplitViewHighlightView::SetBounds(
const gfx::Rect& bounds,
const std::optional<SplitviewAnimationType>& animation_type) {
if (bounds == this->bounds())
return;
if (!animation_type) {
SetBoundsRect(bounds);
return;
}
const gfx::Rect old_bounds = this->bounds();
const bool grows = bounds.size().GetArea() > old_bounds.size().GetArea();
if (grows)
SetBoundsRect(bounds);
gfx::Point start_origin, end_origin;
const bool nix_animation =
*animation_type == SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET;
if (is_right_or_bottom_ || nix_animation) {
gfx::Vector2d clip_offset = bounds.origin() - old_bounds.origin();
DCHECK(GetWidget());
if (base::i18n::IsRTL() &&
IsLayoutHorizontal(GetWidget()->GetNativeWindow()) && !nix_animation) {
clip_offset = gfx::Vector2d(bounds.width() - old_bounds.width(), 0);
}
clip_offset.set_x(std::abs(clip_offset.x()));
clip_offset.set_y(std::abs(clip_offset.y()));
if (grows)
start_origin += clip_offset;
else
end_origin += clip_offset;
}
layer()->SetClipRect(gfx::Rect(start_origin, old_bounds.size()));
DoSplitviewClipRectAnimation(
layer(), *animation_type, gfx::Rect(end_origin, bounds.size()),
std::make_unique<ClippingObserver>(
this, grows ? std::nullopt : std::make_optional(bounds)));
}
void SplitViewHighlightView::OnWindowDraggingStateChanged(
SplitViewDragIndicators::WindowDraggingState window_dragging_state,
SplitViewDragIndicators::WindowDraggingState previous_window_dragging_state,
bool previews_only,
bool can_dragged_window_be_snapped) {
if (window_dragging_state ==
SplitViewDragIndicators::WindowDraggingState::kFromTop &&
!IsCurrentScreenOrientationLandscape() && !is_right_or_bottom_) {
return;
}
if (window_dragging_state ==
SplitViewDragIndicators::WindowDraggingState::kOtherDisplay) {
DoSplitviewOpacityAnimation(layer(),
SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT);
return;
}
const SnapPosition preview_position =
SplitViewDragIndicators::GetSnapPosition(window_dragging_state);
const SnapPosition previous_preview_position =
SplitViewDragIndicators::GetSnapPosition(previous_window_dragging_state);
aura::Window* window = GetWidget()->GetNativeWindow();
if (window_dragging_state ==
SplitViewDragIndicators::WindowDraggingState::kNoDrag) {
if (previous_preview_position == SnapPosition::kNone) {
DoSplitviewOpacityAnimation(layer(),
SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT);
return;
}
if (is_right_or_bottom_ !=
IsPhysicallyLeftOrTop(previous_preview_position, window)) {
DoSplitviewOpacityAnimation(layer(),
SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT);
}
return;
}
if (preview_position != SnapPosition::kNone) {
DoSplitviewOpacityAnimation(
layer(),
is_right_or_bottom_ != IsPhysicallyLeftOrTop(preview_position, window)
? SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN
: SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_OUT);
return;
}
if (previous_preview_position != SnapPosition::kNone) {
if (is_right_or_bottom_ !=
IsPhysicallyLeftOrTop(previous_preview_position, window)) {
DoSplitviewOpacityAnimation(
layer(),
previews_only
? SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT
: can_dragged_window_be_snapped
? SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN
: SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN_CANNOT_SNAP);
} else {
DCHECK_EQ(0.f, layer()->GetTargetOpacity());
if (!previews_only) {
DoSplitviewOpacityAnimation(
layer(),
can_dragged_window_be_snapped
? SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN
: SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN_CANNOT_SNAP);
}
}
return;
}
DCHECK_EQ(0.f, layer()->GetTargetOpacity());
if (!previews_only) {
DoSplitviewOpacityAnimation(
layer(), can_dragged_window_be_snapped
? SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN
: SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN_CANNOT_SNAP);
return;
}
}
BEGIN_METADATA(SplitViewHighlightView)
END_METADATA
}