blob: a3b5e5b087dafde141dc675e235e33d23197fc54 [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 "ui/keyboard/container_floating_behavior.h"
#include "ui/aura/window.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h"
#include "ui/events/event.h"
#include "ui/gfx/geometry/point.h"
#include "ui/gfx/geometry/size.h"
#include "ui/keyboard/display_util.h"
#include "ui/keyboard/drag_descriptor.h"
#include "ui/wm/core/window_animations.h"
namespace keyboard {
// Length of the animation to show and hide the keyboard.
constexpr int kAnimationDurationMs = 200;
// Distance the keyboard moves during the animation
constexpr int kAnimationDistance = 30;
ContainerFloatingBehavior::ContainerFloatingBehavior(Delegate* delegate)
: ContainerBehavior(delegate) {}
ContainerFloatingBehavior::~ContainerFloatingBehavior() = default;
mojom::ContainerType ContainerFloatingBehavior::GetType() const {
return mojom::ContainerType::kFloating;
}
void ContainerFloatingBehavior::DoHidingAnimation(
aura::Window* container,
::wm::ScopedHidingAnimationSettings* animation_settings) {
animation_settings->layer_animation_settings()->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
gfx::Transform transform;
transform.Translate(0, kAnimationDistance);
container->SetTransform(transform);
container->layer()->SetOpacity(0.f);
}
void ContainerFloatingBehavior::DoShowingAnimation(
aura::Window* container,
ui::ScopedLayerAnimationSettings* animation_settings) {
animation_settings->SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
animation_settings->SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
container->SetTransform(gfx::Transform());
container->layer()->SetOpacity(1.0);
}
void ContainerFloatingBehavior::InitializeShowAnimationStartingState(
aura::Window* container) {
aura::Window* root_window = container->GetRootWindow();
SetCanonicalBounds(container, root_window->bounds());
gfx::Transform transform;
transform.Translate(0, kAnimationDistance);
container->SetTransform(transform);
container->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
}
gfx::Rect ContainerFloatingBehavior::AdjustSetBoundsRequest(
const gfx::Rect& display_bounds,
const gfx::Rect& requested_bounds_in_screen) {
gfx::Rect keyboard_bounds_in_screen = requested_bounds_in_screen;
if (!default_position_in_screen_) {
// If the keyboard hasn't been shown yet, ignore the request and use
// default.
gfx::Point default_location = GetPositionForShowingKeyboard(
keyboard_bounds_in_screen.size(), display_bounds);
keyboard_bounds_in_screen =
gfx::Rect(default_location, keyboard_bounds_in_screen.size());
} else {
// Otherwise, simply make sure that the new bounds are not off the edge of
// the screen.
keyboard_bounds_in_screen = ContainKeyboardToScreenBounds(
keyboard_bounds_in_screen, display_bounds);
SavePosition(keyboard_bounds_in_screen, display_bounds.size());
}
return keyboard_bounds_in_screen;
}
void ContainerFloatingBehavior::SavePosition(
const gfx::Rect& keyboard_bounds_in_screen,
const gfx::Size& screen_size) {
int left_distance = keyboard_bounds_in_screen.x();
int right_distance = screen_size.width() - keyboard_bounds_in_screen.right();
int top_distance = keyboard_bounds_in_screen.y();
int bottom_distance =
screen_size.height() - keyboard_bounds_in_screen.bottom();
double available_width = left_distance + right_distance;
double available_height = top_distance + bottom_distance;
if (!default_position_in_screen_) {
default_position_in_screen_ = std::make_unique<KeyboardPosition>();
}
default_position_in_screen_->left_padding_allotment_ratio =
left_distance / available_width;
default_position_in_screen_->top_padding_allotment_ratio =
top_distance / available_height;
}
gfx::Rect ContainerFloatingBehavior::ContainKeyboardToScreenBounds(
const gfx::Rect& keyboard_bounds_in_screen,
const gfx::Rect& display_bounds) const {
int left = keyboard_bounds_in_screen.x();
int top = keyboard_bounds_in_screen.y();
int right = keyboard_bounds_in_screen.right();
int bottom = keyboard_bounds_in_screen.bottom();
// Prevent keyboard from appearing off screen or overlapping with the edge.
if (left < display_bounds.x()) {
left = display_bounds.x();
right = left + keyboard_bounds_in_screen.width();
}
if (right >= display_bounds.right()) {
right = display_bounds.right();
left = right - keyboard_bounds_in_screen.width();
}
if (top < display_bounds.y()) {
top = display_bounds.y();
bottom = top + keyboard_bounds_in_screen.height();
}
if (bottom >= display_bounds.bottom()) {
bottom = display_bounds.bottom();
top = bottom - keyboard_bounds_in_screen.height();
}
return gfx::Rect(left, top, right - left, bottom - top);
}
bool ContainerFloatingBehavior::IsOverscrollAllowed() const {
return false;
}
gfx::Point ContainerFloatingBehavior::GetPositionForShowingKeyboard(
const gfx::Size& keyboard_size,
const gfx::Rect& display_bounds) const {
// Start with the last saved position
gfx::Point top_left_offset;
KeyboardPosition* position = default_position_in_screen_.get();
if (position == nullptr) {
// If there is none, center the keyboard along the bottom of the screen.
top_left_offset.set_x(display_bounds.width() - keyboard_size.width() -
kDefaultDistanceFromScreenRight);
top_left_offset.set_y(display_bounds.height() - keyboard_size.height() -
kDefaultDistanceFromScreenBottom);
} else {
double left = (display_bounds.width() - keyboard_size.width()) *
position->left_padding_allotment_ratio;
double top = (display_bounds.height() - keyboard_size.height()) *
position->top_padding_allotment_ratio;
top_left_offset.set_x((int)left);
top_left_offset.set_y((int)top);
}
// Make sure that this location is valid according to the current size of the
// screen.
gfx::Rect keyboard_bounds =
gfx::Rect(top_left_offset.x() + display_bounds.x(),
top_left_offset.y() + display_bounds.y(), keyboard_size.width(),
keyboard_size.height());
gfx::Rect valid_keyboard_bounds =
ContainKeyboardToScreenBounds(keyboard_bounds, display_bounds);
return valid_keyboard_bounds.origin();
}
bool ContainerFloatingBehavior::IsDragHandle(
const gfx::Vector2d& offset,
const gfx::Size& keyboard_size) const {
return draggable_area_.Contains(offset.x(), offset.y());
}
bool ContainerFloatingBehavior::HandlePointerEvent(
const ui::LocatedEvent& event,
const display::Display& current_display) {
auto kb_offset = gfx::Vector2d(event.x(), event.y());
const gfx::Rect& keyboard_bounds_in_screen = delegate_->GetBoundsInScreen();
// Don't handle events if this runs in a partially initialized state.
if (keyboard_bounds_in_screen.height() <= 0)
return false;
ui::PointerId pointer_id = -1;
if (event.IsTouchEvent()) {
const ui::TouchEvent* te = event.AsTouchEvent();
pointer_id = te->pointer_details().id;
}
const ui::EventType type = event.type();
switch (type) {
case ui::ET_TOUCH_PRESSED:
case ui::ET_MOUSE_PRESSED:
if (!IsDragHandle(kb_offset, keyboard_bounds_in_screen.size())) {
drag_descriptor_ = nullptr;
} else if (type == ui::ET_MOUSE_PRESSED &&
!((const ui::MouseEvent*)&event)->IsOnlyLeftMouseButton()) {
// Mouse events are limited to just the left mouse button.
drag_descriptor_ = nullptr;
} else if (!drag_descriptor_) {
// If there is no active drag descriptor, start a new one.
bool drag_started_by_touch = (type == ui::ET_TOUCH_PRESSED);
drag_descriptor_.reset(
new DragDescriptor{keyboard_bounds_in_screen.origin(), kb_offset,
drag_started_by_touch, pointer_id});
}
break;
case ui::ET_MOUSE_DRAGGED:
case ui::ET_TOUCH_MOVED:
if (!drag_descriptor_) {
// do nothing
} else if (drag_descriptor_->is_touch_drag !=
(type == ui::ET_TOUCH_MOVED)) {
// If the event isn't of the same type that started the drag, end the
// drag to prevent confusion.
drag_descriptor_ = nullptr;
} else if (drag_descriptor_->pointer_id != pointer_id) {
// do nothing.
} else {
// Drag continues.
// If there is an active drag, use it to determine the new location
// of the keyboard.
const gfx::Point original_click_location =
drag_descriptor_->original_keyboard_location +
drag_descriptor_->original_click_offset;
const gfx::Point current_drag_location =
keyboard_bounds_in_screen.origin() + kb_offset;
const gfx::Vector2d cumulative_drag_offset =
current_drag_location - original_click_location;
const gfx::Point new_keyboard_location =
drag_descriptor_->original_keyboard_location +
cumulative_drag_offset;
gfx::Rect new_bounds_in_local =
gfx::Rect(new_keyboard_location, keyboard_bounds_in_screen.size());
DisplayUtil display_util;
const display::Display& new_display =
display_util.FindAdjacentDisplayIfPointIsNearMargin(
current_display, current_drag_location);
if (current_display.id() == new_display.id()) {
delegate_->MoveKeyboardWindow(new_bounds_in_local);
} else {
// Since the keyboard has jumped across screens, cancel the current
// drag descriptor as though the user has lifted their finger.
drag_descriptor_ = nullptr;
gfx::Rect new_bounds_in_screen =
new_bounds_in_local +
current_display.bounds().origin().OffsetFromOrigin();
gfx::Rect contained_new_bounds_in_screen =
ContainKeyboardToScreenBounds(new_bounds_in_screen,
new_display.bounds());
// Enqueue a transition to the adjacent display.
new_bounds_in_local =
contained_new_bounds_in_screen -
new_display.bounds().origin().OffsetFromOrigin();
delegate_->MoveKeyboardWindowToDisplay(new_display,
new_bounds_in_local);
}
SavePosition(delegate_->GetBoundsInScreen(), new_display.size());
return true;
}
break;
default:
drag_descriptor_ = nullptr;
break;
}
return false;
}
void ContainerFloatingBehavior::SetCanonicalBounds(
aura::Window* container,
const gfx::Rect& display_bounds) {
gfx::Point keyboard_location =
GetPositionForShowingKeyboard(container->bounds().size(), display_bounds);
gfx::Rect keyboard_bounds_in_screen =
gfx::Rect(keyboard_location, container->bounds().size());
SavePosition(keyboard_bounds_in_screen, display_bounds.size());
container->SetBounds(keyboard_bounds_in_screen);
}
bool ContainerFloatingBehavior::TextBlurHidesKeyboard() const {
return true;
}
gfx::Rect ContainerFloatingBehavior::GetOccludedBounds(
const gfx::Rect& visual_bounds_in_screen) const {
return {};
}
bool ContainerFloatingBehavior::OccludedBoundsAffectWorkspaceLayout() const {
return false;
}
void ContainerFloatingBehavior::SetDraggableArea(const gfx::Rect& rect) {
draggable_area_ = rect;
}
} // namespace keyboard