Commit de1e5576 authored by Blake O'Hare's avatar Blake O'Hare Committed by Commit Bot

Persist keyboard position relative to the closest edge

This changes the persistence from a single coordinate into a struct
that takes into consideration which side of the screen the keyboard is
closest to.

If the keyboard, for example, is effectively docked to the bottom right
corner of the screen, and the user flips the orientation, the new
position will also be in the bottom right corner of the screen.

Change-Id: Ieed97751adb72272683f4fd943866e5aa4263d05
Reviewed-on: https://chromium-review.googlesource.com/872554Reviewed-by: default avatarYuichiro Hanada <yhanada@chromium.org>
Commit-Queue: Blake O'Hare <blakeo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#530445}
parent 0438fa7e
......@@ -60,9 +60,11 @@ class KEYBOARD_EXPORT ContainerBehavior {
virtual bool IsDragHandle(const gfx::Vector2d& offset,
const gfx::Size& keyboard_size) const = 0;
virtual void SavePosition(const gfx::Point& position) = 0;
virtual void SavePosition(const gfx::Rect& keyboard_bounds,
const gfx::Size& screen_size) = 0;
virtual void HandlePointerEvent(const ui::LocatedEvent& event) = 0;
virtual void HandlePointerEvent(const ui::LocatedEvent& event,
const gfx::Rect& display_bounds) = 0;
virtual ContainerType GetType() const = 0;
......
......@@ -69,7 +69,7 @@ const gfx::Rect ContainerFloatingBehavior::AdjustSetBoundsRequest(
const gfx::Rect& requested_bounds) {
gfx::Rect keyboard_bounds = requested_bounds;
if (UseDefaultPosition()) {
if (!default_position_) {
// If the keyboard hasn't been shown yet, ignore the request and use
// default.
gfx::Point default_location =
......@@ -80,18 +80,49 @@ const gfx::Rect ContainerFloatingBehavior::AdjustSetBoundsRequest(
// the screen.
keyboard_bounds =
ContainKeyboardToScreenBounds(keyboard_bounds, display_bounds);
SavePosition(keyboard_bounds, display_bounds.size());
}
return keyboard_bounds;
}
void ContainerFloatingBehavior::SavePosition(const gfx::Rect& keyboard_bounds,
const gfx::Size& screen_size) {
int left_distance = keyboard_bounds.x();
int right_distance = screen_size.width() - (keyboard_bounds.right());
int top_distance = keyboard_bounds.y();
int bottom_distance = screen_size.height() - (keyboard_bounds.bottom());
if (!default_position_) {
default_position_ = std::make_unique<KeyboardPosition>();
}
if (left_distance < right_distance) {
default_position_->horizontal_anchor_direction =
HorizontalAnchorDirection::LEFT;
default_position_->offset.set_x(left_distance);
} else {
default_position_->horizontal_anchor_direction =
HorizontalAnchorDirection::RIGHT;
default_position_->offset.set_x(right_distance);
}
if (top_distance < bottom_distance) {
default_position_->vertical_anchor_direction = VerticalAnchorDirection::TOP;
default_position_->offset.set_y(top_distance);
} else {
default_position_->vertical_anchor_direction =
VerticalAnchorDirection::BOTTOM;
default_position_->offset.set_y(bottom_distance);
}
}
gfx::Rect ContainerFloatingBehavior::ContainKeyboardToScreenBounds(
const gfx::Rect& keyboard_bounds,
const gfx::Rect& display_bounds) const {
int left = keyboard_bounds.x();
int top = keyboard_bounds.y();
int right = left + keyboard_bounds.width();
int bottom = top + keyboard_bounds.height();
int right = keyboard_bounds.right();
int bottom = keyboard_bounds.bottom();
// Prevent keyboard from appearing off screen or overlapping with the edge.
if (left < 0) {
......@@ -118,37 +149,43 @@ bool ContainerFloatingBehavior::IsOverscrollAllowed() const {
return false;
}
bool ContainerFloatingBehavior::UseDefaultPosition() const {
// (-1, -1) is used as a sentinel unset value.
return default_position_.x() == -1;
}
gfx::Point ContainerFloatingBehavior::GetPositionForShowingKeyboard(
const gfx::Size& keyboard_size,
const gfx::Rect& display_bounds) const {
// Start with the last saved position
gfx::Point position = default_position_;
if (UseDefaultPosition()) {
gfx::Point top_left_offset;
KeyboardPosition* position = default_position_.get();
if (position == nullptr) {
// If there is none, center the keyboard along the bottom of the screen.
position.set_x(display_bounds.width() - keyboard_size.width() -
kDefaultDistanceFromScreenRight);
position.set_y(display_bounds.height() - keyboard_size.height() -
kDefaultDistanceFromScreenBottom);
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 {
if (position->horizontal_anchor_direction ==
HorizontalAnchorDirection::LEFT) {
top_left_offset.set_x(position->offset.x());
} else {
top_left_offset.set_x(display_bounds.width() - position->offset.x() -
keyboard_size.width());
}
if (position->vertical_anchor_direction == VerticalAnchorDirection::TOP) {
top_left_offset.set_y(position->offset.y());
} else {
top_left_offset.set_y(display_bounds.height() - position->offset.y() -
keyboard_size.height());
}
}
// Make sure that this location is valid according to the current size of the
// screen.
gfx::Rect keyboard_bounds = gfx::Rect(position, keyboard_size);
gfx::Rect keyboard_bounds = gfx::Rect(top_left_offset, keyboard_size);
gfx::Rect valid_keyboard_bounds =
ContainKeyboardToScreenBounds(keyboard_bounds, display_bounds);
return valid_keyboard_bounds.origin();
}
void ContainerFloatingBehavior::SavePosition(const gfx::Point& position) {
default_position_ = position;
}
bool ContainerFloatingBehavior::IsDragHandle(
const gfx::Vector2d& offset,
const gfx::Size& keyboard_size) const {
......@@ -156,7 +193,8 @@ bool ContainerFloatingBehavior::IsDragHandle(
}
void ContainerFloatingBehavior::HandlePointerEvent(
const ui::LocatedEvent& event) {
const ui::LocatedEvent& event,
const gfx::Rect& display_bounds) {
// Cannot call UI-backed operations without a KeyboardController
DCHECK(controller_);
auto kb_offset = gfx::Vector2d(event.x(), event.y());
......@@ -205,7 +243,7 @@ void ContainerFloatingBehavior::HandlePointerEvent(
const gfx::Rect new_bounds =
gfx::Rect(new_keyboard_location, keyboard_bounds.size());
controller_->MoveKeyboard(new_bounds);
SavePosition(container->bounds().origin());
SavePosition(container->bounds(), display_bounds.size());
handle_drag = true;
}
if (!handle_drag && drag_descriptor_) {
......@@ -219,9 +257,10 @@ void ContainerFloatingBehavior::SetCanonicalBounds(
const gfx::Rect& display_bounds) {
gfx::Point keyboard_location =
GetPositionForShowingKeyboard(container->bounds().size(), display_bounds);
SavePosition(keyboard_location);
container->SetBounds(
gfx::Rect(keyboard_location, container->bounds().size()));
gfx::Rect keyboard_bounds =
gfx::Rect(keyboard_location, container->bounds().size());
SavePosition(keyboard_bounds, display_bounds.size());
container->SetBounds(keyboard_bounds);
}
bool ContainerFloatingBehavior::TextBlurHidesKeyboard() const {
......
......@@ -23,6 +23,25 @@ namespace keyboard {
constexpr int kDefaultDistanceFromScreenBottom = 20;
constexpr int kDefaultDistanceFromScreenRight = 20;
enum class HorizontalAnchorDirection {
LEFT,
RIGHT,
};
enum class VerticalAnchorDirection {
TOP,
BOTTOM,
};
struct KeyboardPosition {
// Whether the keyboard is anchored to the left or right side of the screen.
HorizontalAnchorDirection horizontal_anchor_direction;
// Whether the keyboard is anchored to the top or bottom side of the screen.
VerticalAnchorDirection vertical_anchor_direction;
// Distance from the sides of the screen that the keyboard is anchored to.
gfx::Point offset;
};
class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior {
public:
ContainerFloatingBehavior(KeyboardController* controller);
......@@ -42,8 +61,10 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior {
bool IsOverscrollAllowed() const override;
bool IsDragHandle(const gfx::Vector2d& offset,
const gfx::Size& keyboard_size) const override;
void SavePosition(const gfx::Point& position) override;
void HandlePointerEvent(const ui::LocatedEvent& event) override;
void SavePosition(const gfx::Rect& keyboard_bounds,
const gfx::Size& screen_size) override;
void HandlePointerEvent(const ui::LocatedEvent& event,
const gfx::Rect& display_bounds) override;
void SetCanonicalBounds(aura::Window* container,
const gfx::Rect& display_bounds) override;
ContainerType GetType() const override;
......@@ -52,6 +73,11 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior {
bool BoundsAffectWorkspaceLayout() const override;
bool SetDraggableArea(const gfx::Rect& rect) override;
// Calculate the position of the keyboard for when it is being shown.
gfx::Point GetPositionForShowingKeyboard(
const gfx::Size& keyboard_size,
const gfx::Rect& display_bounds) const;
private:
// Ensures that the keyboard is neither off the screen nor overlapping an
// edge.
......@@ -62,19 +88,11 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior {
// Saves the current keyboard location for use the next time it is displayed.
void UpdateLastPoint(const gfx::Point& position);
// Returns true if the keyboard has not been display/moved yet and the default
// position should be used.
bool UseDefaultPosition() const;
// Calculate the position of the keyboard for when it is being shown.
gfx::Point GetPositionForShowingKeyboard(
const gfx::Size& keyboard_size,
const gfx::Rect& display_bounds) const;
KeyboardController* controller_;
// TODO(blakeo): cache the default_position_ on a per-display basis.
gfx::Point default_position_ = gfx::Point(-1, -1);
std::unique_ptr<struct keyboard::KeyboardPosition> default_position_ =
nullptr;
// Current state of a cursor drag to move the keyboard, if one exists.
// Otherwise nullptr.
......
......@@ -32,7 +32,8 @@ TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequest) {
keyboard_height);
// Save an arbitrary position so that default location will not be used.
floating_behavior.SavePosition(gfx::Point(0, 0));
floating_behavior.SavePosition(
gfx::Rect(0, 0, keyboard_width, keyboard_height), workspace.size());
gfx::Rect result =
floating_behavior.AdjustSetBoundsRequest(workspace, center);
......@@ -49,6 +50,56 @@ TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequest) {
result);
}
TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequestVariousSides) {
ContainerFloatingBehavior floating_behavior(nullptr);
const int keyboard_width = 100;
const int keyboard_height = 100;
gfx::Size keyboard_size = gfx::Size(keyboard_width, keyboard_height);
gfx::Rect workspace_wide(0, 0, 1000, 500);
gfx::Rect workspace_tall(0, 0, 500, 1000);
gfx::Rect top_left(0, 0, keyboard_width, keyboard_height);
gfx::Rect top_right(900, 0, keyboard_width, keyboard_height);
gfx::Rect bottom_left(0, 400, keyboard_width, keyboard_height);
gfx::Rect bottom_right(900, 400, keyboard_width, keyboard_height);
// Save an arbitrary position so that default location will not be used.
floating_behavior.SavePosition(
gfx::Rect(0, 0, keyboard_width, keyboard_height), workspace_wide.size());
floating_behavior.AdjustSetBoundsRequest(workspace_wide, top_left);
gfx::Point result = floating_behavior.GetPositionForShowingKeyboard(
keyboard_size, workspace_wide);
ASSERT_EQ(gfx::Point(0, 0), result);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_tall);
ASSERT_EQ(gfx::Point(0, 0), result);
floating_behavior.AdjustSetBoundsRequest(workspace_wide, top_right);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_wide);
ASSERT_EQ(gfx::Point(900, 0), result);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_tall);
ASSERT_EQ(gfx::Point(400, 0), result);
floating_behavior.AdjustSetBoundsRequest(workspace_wide, bottom_left);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_wide);
ASSERT_EQ(gfx::Point(0, 400), result);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_tall);
ASSERT_EQ(gfx::Point(0, 900), result);
floating_behavior.AdjustSetBoundsRequest(workspace_wide, bottom_right);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_wide);
ASSERT_EQ(gfx::Point(900, 400), result);
result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size,
workspace_tall);
ASSERT_EQ(gfx::Point(400, 900), result);
}
TEST(ContainerFloatingBehaviorTest, DontSaveCoordinatesUntilKeyboardMoved) {
ContainerFloatingBehavior floating_behavior(nullptr);
......@@ -77,7 +128,8 @@ TEST(ContainerFloatingBehaviorTest, DontSaveCoordinatesUntilKeyboardMoved) {
// Simulate the user clicking and moving the keyboard to some arbitrary
// location (it doesn't matter where). Now that the coordinate is known to be
// user-determined.
floating_behavior.SavePosition(gfx::Point(10, 10));
floating_behavior.SavePosition(
gfx::Rect(10, 10, keyboard_width, keyboard_height), workspace.size());
// Move the keyboard somewhere else. The coordinates should be taken as-is
// without being adjusted.
......
......@@ -82,7 +82,8 @@ bool ContainerFullWidthBehavior::IsOverscrollAllowed() const {
return controller_ && !controller_->keyboard_locked();
}
void ContainerFullWidthBehavior::SavePosition(const gfx::Point& position) {
void ContainerFullWidthBehavior::SavePosition(const gfx::Rect& keyboard_bounds,
const gfx::Size& screen_size) {
// No-op. Nothing to save.
}
......@@ -93,7 +94,8 @@ bool ContainerFullWidthBehavior::IsDragHandle(
}
void ContainerFullWidthBehavior::HandlePointerEvent(
const ui::LocatedEvent& event) {
const ui::LocatedEvent& event,
const gfx::Rect& display_bounds) {
// No-op. Nothing special to do for pointer events.
}
......
......@@ -39,8 +39,10 @@ class KEYBOARD_EXPORT ContainerFullWidthBehavior : public ContainerBehavior {
bool IsOverscrollAllowed() const override;
bool IsDragHandle(const gfx::Vector2d& offset,
const gfx::Size& keyboard_size) const override;
void SavePosition(const gfx::Point& position) override;
void HandlePointerEvent(const ui::LocatedEvent& event) override;
void SavePosition(const gfx::Rect& keyboard_bounds,
const gfx::Size& screen_size) override;
void HandlePointerEvent(const ui::LocatedEvent& event,
const gfx::Rect& display_bounds) override;
void SetCanonicalBounds(aura::Window* container,
const gfx::Rect& display_bounds) override;
ContainerType GetType() const override;
......
......@@ -737,7 +737,8 @@ bool KeyboardController::IsOverscrollAllowed() const {
}
void KeyboardController::HandlePointerEvent(const ui::LocatedEvent& event) {
container_behavior_->HandlePointerEvent(event);
container_behavior_->HandlePointerEvent(
event, container_->GetRootWindow()->bounds());
}
void KeyboardController::SetContainerType(const ContainerType type) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment