blob: 03afe6bcdd4f9d4c5fc0e155e3bd6d51d312a43b [file] [log] [blame]
// 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.
#include "ash/wm/splitview/split_view_utils.h"
#include "ash/accessibility/accessibility_controller.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/wm/screen_pinning_controller.h"
#include "ash/wm/splitview/split_view_constants.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "base/command_line.h"
#include "ui/aura/window_delegate.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/layer_animator.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/wm/core/window_util.h"
namespace ash {
namespace {
// The animation speed at which the highlights fade in or out.
constexpr base::TimeDelta kHighlightsFadeInOutMs =
base::TimeDelta::FromMilliseconds(250);
// The animation speed which the other highlight fades in or out.
constexpr base::TimeDelta kOtherFadeInOutMs =
base::TimeDelta::FromMilliseconds(133);
// The delay before the other highlight starts fading in.
constexpr base::TimeDelta kOtherFadeInDelayMs =
base::TimeDelta::FromMilliseconds(117);
// The animation speed at which the preview area fades out (when you snap a
// window).
constexpr base::TimeDelta kPreviewAreaFadeOutMs =
base::TimeDelta::FromMilliseconds(67);
// The time duration for the indicator label opacity animations.
constexpr base::TimeDelta kLabelAnimationMs =
base::TimeDelta::FromMilliseconds(83);
// The delay before the indicator labels start fading in.
constexpr base::TimeDelta kLabelAnimationDelayMs =
base::TimeDelta::FromMilliseconds(167);
// The time duration for the window transformation animations.
constexpr base::TimeDelta kWindowTransformMs =
base::TimeDelta::FromMilliseconds(250);
constexpr float kHighlightOpacity = 0.3f;
constexpr float kPreviewAreaHighlightOpacity = 0.18f;
// Gets the duration, tween type and delay before animation based on |type|.
void GetAnimationValuesForType(
SplitviewAnimationType type,
base::TimeDelta* out_duration,
gfx::Tween::Type* out_tween_type,
ui::LayerAnimator::PreemptionStrategy* out_preemption_strategy,
base::TimeDelta* out_delay) {
*out_preemption_strategy = ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET;
switch (type) {
case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN:
case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN:
case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN:
case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT:
case SPLITVIEW_ANIMATION_TEXT_FADE_IN_WITH_HIGHLIGHT:
case SPLITVIEW_ANIMATION_TEXT_FADE_OUT_WITH_HIGHLIGHT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT:
*out_duration = kHighlightsFadeInOutMs;
*out_tween_type = gfx::Tween::FAST_OUT_SLOW_IN;
return;
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN:
*out_delay = kOtherFadeInDelayMs;
*out_duration = kOtherFadeInOutMs;
*out_tween_type = gfx::Tween::LINEAR_OUT_SLOW_IN;
*out_preemption_strategy = ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
return;
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_OUT:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT:
*out_duration = kOtherFadeInOutMs;
*out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN;
return;
case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
*out_duration = kPreviewAreaFadeOutMs;
*out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN;
return;
case SPLITVIEW_ANIMATION_TEXT_FADE_IN:
*out_delay = kLabelAnimationDelayMs;
*out_duration = kLabelAnimationMs;
*out_tween_type = gfx::Tween::LINEAR_OUT_SLOW_IN;
*out_preemption_strategy = ui::LayerAnimator::ENQUEUE_NEW_ANIMATION;
return;
case SPLITVIEW_ANIMATION_TEXT_FADE_OUT:
*out_duration = kLabelAnimationMs;
*out_tween_type = gfx::Tween::FAST_OUT_LINEAR_IN;
return;
case SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM:
*out_duration = kWindowTransformMs;
*out_tween_type = gfx::Tween::FAST_OUT_SLOW_IN;
*out_preemption_strategy =
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET;
return;
}
NOTREACHED();
}
// Helper function to apply animation values to |settings|.
void ApplyAnimationSettings(
ui::ScopedLayerAnimationSettings* settings,
ui::LayerAnimator* animator,
ui::LayerAnimationElement::AnimatableProperties animated_property,
base::TimeDelta duration,
gfx::Tween::Type tween,
ui::LayerAnimator::PreemptionStrategy preemption_strategy,
base::TimeDelta delay) {
DCHECK_EQ(settings->GetAnimator(), animator);
settings->SetTransitionDuration(duration);
settings->SetTweenType(tween);
settings->SetPreemptionStrategy(preemption_strategy);
if (!delay.is_zero())
animator->SchedulePauseForProperties(delay, animated_property);
}
} // namespace
void DoSplitviewOpacityAnimation(ui::Layer* layer,
SplitviewAnimationType type) {
float target_opacity = 0.f;
switch (type) {
case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_OUT:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_OUT:
case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_OUT:
case SPLITVIEW_ANIMATION_TEXT_FADE_OUT:
case SPLITVIEW_ANIMATION_TEXT_FADE_OUT_WITH_HIGHLIGHT:
target_opacity = 0.f;
break;
case SPLITVIEW_ANIMATION_PREVIEW_AREA_FADE_IN:
target_opacity = kPreviewAreaHighlightOpacity;
break;
case SPLITVIEW_ANIMATION_HIGHLIGHT_FADE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_FADE_IN:
target_opacity = kHighlightOpacity;
break;
case SPLITVIEW_ANIMATION_OVERVIEW_ITEM_FADE_IN:
case SPLITVIEW_ANIMATION_TEXT_FADE_IN:
case SPLITVIEW_ANIMATION_TEXT_FADE_IN_WITH_HIGHLIGHT:
target_opacity = 1.f;
break;
default:
NOTREACHED() << "Not a valid split view opacity animation type.";
return;
}
if (layer->GetTargetOpacity() == target_opacity)
return;
base::TimeDelta duration;
gfx::Tween::Type tween;
ui::LayerAnimator::PreemptionStrategy preemption_strategy;
base::TimeDelta delay;
GetAnimationValuesForType(type, &duration, &tween, &preemption_strategy,
&delay);
ui::LayerAnimator* animator = layer->GetAnimator();
ui::ScopedLayerAnimationSettings settings(animator);
ApplyAnimationSettings(&settings, animator,
ui::LayerAnimationElement::OPACITY, duration, tween,
preemption_strategy, delay);
layer->SetOpacity(target_opacity);
}
void DoSplitviewTransformAnimation(ui::Layer* layer,
SplitviewAnimationType type,
const gfx::Transform& target_transform) {
if (layer->GetTargetTransform() == target_transform)
return;
switch (type) {
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_IN:
case SPLITVIEW_ANIMATION_OTHER_HIGHLIGHT_TEXT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_NIX_INSET:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_SLIDE_OUT:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_IN:
case SPLITVIEW_ANIMATION_PREVIEW_AREA_TEXT_SLIDE_OUT:
case SPLITVIEW_ANIMATION_SET_WINDOW_TRANSFORM:
break;
default:
NOTREACHED() << "Not a valid split view transform type.";
return;
}
base::TimeDelta duration;
gfx::Tween::Type tween;
ui::LayerAnimator::PreemptionStrategy preemption_strategy;
base::TimeDelta delay;
GetAnimationValuesForType(type, &duration, &tween, &preemption_strategy,
&delay);
ui::LayerAnimator* animator = layer->GetAnimator();
ui::ScopedLayerAnimationSettings settings(animator);
ApplyAnimationSettings(&settings, animator,
ui::LayerAnimationElement::TRANSFORM, duration, tween,
preemption_strategy, delay);
layer->SetTransform(target_transform);
}
bool IsClamshellSplitViewModeEnabled() {
return base::FeatureList::IsEnabled(
ash::features::kDragToSnapInClamshellMode);
}
bool ShouldAllowSplitView() {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAshDisableTabletSplitView)) {
return false;
}
if (!Shell::Get()
->tablet_mode_controller()
->IsTabletModeWindowManagerEnabled() &&
!IsClamshellSplitViewModeEnabled()) {
return false;
}
// Don't allow split view if we're in pinned mode.
if (Shell::Get()->screen_pinning_controller()->IsPinned())
return false;
// TODO(crubg.com/853588): Disallow window dragging and split screen while
// ChromeVox is on until they are in a usable state.
if (Shell::Get()->accessibility_controller()->spoken_feedback_enabled())
return false;
return true;
}
bool CanSnapInSplitview(aura::Window* window) {
if (!::wm::CanActivateWindow(window))
return false;
if (!wm::GetWindowState(window)->CanSnap())
return false;
if (window->delegate()) {
// If the window's minimum size is larger than half of the display's work
// area size, the window can't be snapped in this case.
const gfx::Size min_size = window->delegate()->GetMinimumSize();
const gfx::Rect display_area =
screen_util::GetDisplayWorkAreaBoundsInScreenForActiveDeskContainer(
window);
const bool is_landscape = (display_area.width() > display_area.height());
if ((is_landscape && min_size.width() > display_area.width() / 2) ||
(!is_landscape && min_size.height() > display_area.height() / 2)) {
return false;
}
}
return true;
}
} // namespace ash