Commit ef9025a5 authored by mfomitchev's avatar mfomitchev Committed by Commit bot

Adding split view divider widget.

Adding split view divider widget which could be dragged to exit the split view.
Resubmit of https://codereview.chromium.org/545393002

TBR=mukai@chromium.org
BUG=403207, 408691

Review URL: https://codereview.chromium.org/598083002

Cr-Commit-Position: refs/heads/master@{#296464}
parent f173cbc4
...@@ -51,11 +51,6 @@ ...@@ -51,11 +51,6 @@
'activity/public/activity_view_manager.h', 'activity/public/activity_view_manager.h',
'activity/public/activity_view_model.h', 'activity/public/activity_view_model.h',
'athena_export.h', 'athena_export.h',
'util/container_priorities.h',
'util/fill_layout_manager.cc',
'util/fill_layout_manager.h',
'util/switches.cc',
'util/switches.h',
'env/athena_env_impl.cc', 'env/athena_env_impl.cc',
'env/public/athena_env.h', 'env/public/athena_env.h',
'home/app_list_view_delegate.cc', 'home/app_list_view_delegate.cc',
...@@ -100,6 +95,13 @@ ...@@ -100,6 +95,13 @@
'system/time_view.h', 'system/time_view.h',
'system/public/system_ui.h', 'system/public/system_ui.h',
'system/system_ui_impl.cc', 'system/system_ui_impl.cc',
'util/container_priorities.h',
'util/drag_handle.cc',
'util/drag_handle.h',
'util/fill_layout_manager.cc',
'util/fill_layout_manager.h',
'util/switches.cc',
'util/switches.h',
'wm/bezel_controller.cc', 'wm/bezel_controller.cc',
'wm/bezel_controller.h', 'wm/bezel_controller.h',
'wm/overview_toolbar.cc', 'wm/overview_toolbar.cc',
...@@ -267,7 +269,6 @@ ...@@ -267,7 +269,6 @@
], ],
'sources': [ 'sources': [
'activity/activity_manager_unittest.cc', 'activity/activity_manager_unittest.cc',
'util/fill_layout_manager_unittest.cc',
'content/app_activity_unittest.cc', 'content/app_activity_unittest.cc',
'env/athena_env_unittest.cc', 'env/athena_env_unittest.cc',
'home/athena_start_page_view_unittest.cc', 'home/athena_start_page_view_unittest.cc',
...@@ -278,6 +279,8 @@ ...@@ -278,6 +279,8 @@
'resource_manager/resource_manager_unittest.cc', 'resource_manager/resource_manager_unittest.cc',
'screen/screen_manager_unittest.cc', 'screen/screen_manager_unittest.cc',
'test/athena_unittests.cc', 'test/athena_unittests.cc',
'util/drag_handle_unittest.cc',
'util/fill_layout_manager_unittest.cc',
'wm/split_view_controller_unittest.cc', 'wm/split_view_controller_unittest.cc',
'wm/window_list_provider_impl_unittest.cc', 'wm/window_list_provider_impl_unittest.cc',
'wm/window_manager_unittest.cc', 'wm/window_manager_unittest.cc',
......
...@@ -2,4 +2,5 @@ include_rules = [ ...@@ -2,4 +2,5 @@ include_rules = [
"+athena/athena_export.h", "+athena/athena_export.h",
"+ui/aura", "+ui/aura",
"+ui/compositor", "+ui/compositor",
"+ui/views",
] ]
// Copyright 2014 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 "athena/util/drag_handle.h"
#include "ui/views/background.h"
#include "ui/views/view.h"
namespace athena {
namespace {
const SkColor kDragHandleColorNormal = SK_ColorGRAY;
const SkColor kDragHandleColorHot = SK_ColorWHITE;
// This view notifies its delegate of the touch scroll gestures performed on it.
class DragHandleView : public views::View {
public:
DragHandleView(DragHandleScrollDirection scroll_direction,
DragHandleScrollDelegate* delegate,
int preferred_width,
int preferred_height);
virtual ~DragHandleView();
private:
void SetColor(SkColor color);
void SetIsScrolling(bool scrolling);
// views::View:
virtual gfx::Size GetPreferredSize() const OVERRIDE;
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
bool scroll_in_progress_;
DragHandleScrollDelegate* delegate_;
DragHandleScrollDirection scroll_direction_;
SkColor color_;
float scroll_start_location_;
const int preferred_width_;
const int preferred_height_;
DISALLOW_COPY_AND_ASSIGN(DragHandleView);
};
DragHandleView::DragHandleView(DragHandleScrollDirection scroll_direction,
DragHandleScrollDelegate* delegate,
int preferred_width,
int preferred_height)
: scroll_in_progress_(false),
delegate_(delegate),
scroll_direction_(scroll_direction),
color_(SK_ColorTRANSPARENT),
preferred_width_(preferred_width),
preferred_height_(preferred_height) {
SetColor(kDragHandleColorNormal);
}
DragHandleView::~DragHandleView() {
}
void DragHandleView::SetColor(SkColor color) {
if (color_ == color)
return;
color_ = color;
set_background(views::Background::CreateSolidBackground(color_));
SchedulePaint();
}
void DragHandleView::SetIsScrolling(bool scrolling) {
if (scroll_in_progress_ == scrolling)
return;
scroll_in_progress_ = scrolling;
if (!scroll_in_progress_)
scroll_start_location_ = 0;
}
// views::View:
gfx::Size DragHandleView::GetPreferredSize() const {
return gfx::Size(preferred_width_, preferred_height_);
}
void DragHandleView::OnGestureEvent(ui::GestureEvent* event) {
SkColor change_color = SK_ColorTRANSPARENT;
if (event->type() == ui::ET_GESTURE_BEGIN &&
event->details().touch_points() == 1) {
change_color = kDragHandleColorHot;
} else if (event->type() == ui::ET_GESTURE_END &&
event->details().touch_points() == 1) {
change_color = kDragHandleColorNormal;
}
if (change_color != SK_ColorTRANSPARENT) {
SetColor(change_color);
event->SetHandled();
return;
}
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
if (scroll_in_progress_)
return;
float delta;
if (scroll_direction_ == DRAG_HANDLE_VERTICAL) {
delta = event->details().scroll_y_hint();
scroll_start_location_ = event->root_location().y();
} else {
delta = event->details().scroll_x_hint();
scroll_start_location_ = event->root_location().x();
}
delegate_->HandleScrollBegin(delta);
SetIsScrolling(true);
event->SetHandled();
} else if (event->type() == ui::ET_GESTURE_SCROLL_END ||
event->type() == ui::ET_SCROLL_FLING_START) {
if (!scroll_in_progress_)
return;
delegate_->HandleScrollEnd();
SetColor(kDragHandleColorNormal);
SetIsScrolling(false);
event->SetHandled();
} else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
if (!scroll_in_progress_)
return;
float delta = scroll_direction_ == DRAG_HANDLE_VERTICAL
? event->root_location().y() - scroll_start_location_
: event->root_location().x() - scroll_start_location_;
delegate_->HandleScrollUpdate(delta);
event->SetHandled();
}
}
} // namespace
views::View* CreateDragHandleView(DragHandleScrollDirection scroll_direction,
DragHandleScrollDelegate* delegate,
int preferred_width,
int preferred_height) {
views::View* view = new DragHandleView(
scroll_direction, delegate, preferred_width, preferred_height);
return view;
}
} // namespace athena
// Copyright 2014 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.
#ifndef ATHENA_UTIL_DRAG_HANDLE_H_
#define ATHENA_UTIL_DRAG_HANDLE_H_
#include "athena/athena_export.h"
namespace views {
class View;
}
namespace athena {
class DragHandleScrollDelegate {
public:
virtual ~DragHandleScrollDelegate() {}
// Beginning of a scroll gesture.
virtual void HandleScrollBegin(float delta) = 0;
// End of the current scroll gesture.
virtual void HandleScrollEnd() = 0;
// Update of the scroll position for the currently active scroll gesture.
virtual void HandleScrollUpdate(float delta) = 0;
};
enum DragHandleScrollDirection { DRAG_HANDLE_VERTICAL, DRAG_HANDLE_HORIZONTAL };
// Creates a handle view which notifies the delegate of the scrolls performed on
// it.
ATHENA_EXPORT views::View* CreateDragHandleView(
DragHandleScrollDirection scroll_direction,
DragHandleScrollDelegate* delegate,
int preferred_width,
int preferred_height);
} // namespace athena
#endif // ATHENA_UTIL_DRAG_HANDLE_H_
// Copyright 2014 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 "athena/util/drag_handle.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/window.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
namespace athena {
class DragHandleDelegateTest : public DragHandleScrollDelegate {
public:
explicit DragHandleDelegateTest()
: begin_delta_(0),
got_scroll_end_(false),
update_delta_(0) {}
virtual ~DragHandleDelegateTest() {}
void Reset() {
begin_delta_ = 0;
got_scroll_end_ = false;
update_delta_ = 0;
}
float begin_delta() { return begin_delta_; }
bool got_scroll_end() { return got_scroll_end_; }
float update_delta() { return update_delta_; }
private:
// DragHandleScrollDelegate:
virtual void HandleScrollBegin(float delta) OVERRIDE {
begin_delta_ = delta;
}
virtual void HandleScrollEnd() OVERRIDE {
got_scroll_end_ = true;
}
virtual void HandleScrollUpdate(float delta) OVERRIDE {
update_delta_ = delta;
}
float begin_delta_;
bool got_scroll_end_;
float update_delta_;
DISALLOW_COPY_AND_ASSIGN(DragHandleDelegateTest);
};
typedef aura::test::AuraTestBase DragHandleTest;
const int kDragHandleWidth = 10;
const int kDragHandleHeight = 100;
ui::GestureEvent CreateGestureEvent(ui::EventType type,
float x,
float delta_x) {
ui::GestureEvent event(
x,
1,
0,
base::TimeDelta::FromInternalValue(base::Time::Now().ToInternalValue()),
type == ui::ET_GESTURE_SCROLL_END
? ui::GestureEventDetails(type)
: ui::GestureEventDetails(type, delta_x, 0));
event.set_root_location(gfx::PointF(x, 1));
return event;
}
TEST_F(DragHandleTest, ScrollTest) {
DragHandleDelegateTest delegate;
scoped_ptr<views::View> drag_handle(
CreateDragHandleView(DragHandleScrollDirection::DRAG_HANDLE_HORIZONTAL,
&delegate,
kDragHandleWidth,
kDragHandleHeight));
views::Widget widget;
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.parent = root_window();
params.accept_events = true;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget.Init(params);
widget.SetContentsView(drag_handle.get());
const gfx::Size& size = gfx::Size(kDragHandleWidth, kDragHandleHeight);
widget.SetSize(size);
widget.SetBounds(gfx::Rect(0, 0, size.width(), size.height()));
const float begin_x = 4.0;
const float begin_delta = 10.0;
const float update_delta = 15.0;
ui::GestureEvent scroll_begin_model =
CreateGestureEvent(ui::ET_GESTURE_SCROLL_BEGIN, begin_x, begin_delta);
ui::GestureEvent scroll_update_model =
CreateGestureEvent(ui::ET_GESTURE_SCROLL_UPDATE,
begin_x + update_delta,
update_delta - begin_delta);
ui::GestureEvent scroll_end_model =
CreateGestureEvent(ui::ET_GESTURE_SCROLL_END, begin_x + update_delta, 0);
ui::GestureEvent fling_start_model =
CreateGestureEvent(ui::ET_SCROLL_FLING_START, begin_x + update_delta, 0);
// Normal scroll
ui::GestureEvent e(scroll_begin_model);
widget.OnGestureEvent(&e);
EXPECT_EQ(begin_delta, delegate.begin_delta());
EXPECT_EQ(0, delegate.update_delta());
EXPECT_FALSE(delegate.got_scroll_end());
e = ui::GestureEvent(scroll_update_model);
widget.OnGestureEvent(&e);
EXPECT_EQ(update_delta, delegate.update_delta());
EXPECT_FALSE(delegate.got_scroll_end());
e = ui::GestureEvent(scroll_end_model);
widget.OnGestureEvent(&e);
EXPECT_EQ(update_delta, delegate.update_delta());
EXPECT_TRUE(delegate.got_scroll_end());
delegate.Reset();
// Scroll ending with a fling
e = ui::GestureEvent(scroll_begin_model);
widget.OnGestureEvent(&e);
e = ui::GestureEvent(scroll_update_model);
widget.OnGestureEvent(&e);
e = ui::GestureEvent(fling_start_model);
widget.OnGestureEvent(&e);
EXPECT_TRUE(delegate.got_scroll_end());
delegate.Reset();
drag_handle.reset();
}
} // namespace athena
...@@ -9,5 +9,6 @@ include_rules = [ ...@@ -9,5 +9,6 @@ include_rules = [
"+ui/compositor", "+ui/compositor",
"+ui/events", "+ui/events",
"+ui/gfx", "+ui/gfx",
"+ui/views",
"+ui/wm", "+ui/wm",
] ]
...@@ -46,6 +46,21 @@ float GetDistance(const gfx::PointF& location, ...@@ -46,6 +46,21 @@ float GetDistance(const gfx::PointF& location,
: point_in_screen.x() - GetDisplay(window).bounds().width(); : point_in_screen.x() - GetDisplay(window).bounds().width();
} }
// Returns the bezel corresponding to the |location| in |window| or BEZEL_NONE
// if the location is outside of the bezel area.
// Only implemented for LEFT and RIGHT bezels.
BezelController::Bezel GetBezel(const gfx::PointF& location,
aura::Window* window) {
int screen_width = GetDisplay(window).bounds().width();
gfx::Point point_in_screen(gfx::ToRoundedPoint(location));
wm::ConvertPointToScreen(window, &point_in_screen);
if (point_in_screen.x() < kBezelWidth)
return BezelController::BEZEL_LEFT;
if (point_in_screen.x() > screen_width - kBezelWidth)
return BezelController::BEZEL_RIGHT;
return BezelController::BEZEL_NONE;
}
} // namespace } // namespace
BezelController::BezelController(aura::Window* container) BezelController::BezelController(aura::Window* container)
...@@ -69,9 +84,9 @@ void BezelController::SetState(BezelController::State state, ...@@ -69,9 +84,9 @@ void BezelController::SetState(BezelController::State state,
return; return;
if (state == BEZEL_SCROLLING_TWO_FINGERS) if (state == BEZEL_SCROLLING_TWO_FINGERS)
left_right_delegate_->ScrollBegin(scroll_bezel_, scroll_delta); left_right_delegate_->BezelScrollBegin(scroll_bezel_, scroll_delta);
else if (state_ == BEZEL_SCROLLING_TWO_FINGERS) else if (state_ == BEZEL_SCROLLING_TWO_FINGERS)
left_right_delegate_->ScrollEnd(); left_right_delegate_->BezelScrollEnd();
state_ = state; state_ = state;
if (state == NONE) { if (state == NONE) {
scroll_bezel_ = BEZEL_NONE; scroll_bezel_ = BEZEL_NONE;
...@@ -79,18 +94,6 @@ void BezelController::SetState(BezelController::State state, ...@@ -79,18 +94,6 @@ void BezelController::SetState(BezelController::State state,
} }
} }
// Only implemented for LEFT and RIGHT bezels ATM.
BezelController::Bezel BezelController::GetBezel(const gfx::PointF& location) {
int screen_width = GetDisplay(container_).bounds().width();
if (location.x() < kBezelWidth) {
return BEZEL_LEFT;
} else if (location.x() > screen_width - kBezelWidth) {
return BEZEL_RIGHT;
} else {
return BEZEL_NONE;
}
}
void BezelController::OnGestureEvent(ui::GestureEvent* event) { void BezelController::OnGestureEvent(ui::GestureEvent* event) {
// TODO(mfomitchev): Currently we aren't retargetting or consuming any of the // TODO(mfomitchev): Currently we aren't retargetting or consuming any of the
// touch events. This means that content can prevent the generation of gesture // touch events. This means that content can prevent the generation of gesture
...@@ -105,24 +108,35 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { ...@@ -105,24 +108,35 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
if (!ShouldProcessGesture(type)) if (!ShouldProcessGesture(type))
return; return;
const ui::GestureEventDetails& event_details = event->details();
int num_touch_points = event_details.touch_points();
if (num_touch_points == 1 && type == ui::ET_GESTURE_BEGIN) {
// Reset the state when the first finger touches and starts a gesture.
// Normally, the state gets reset when the last finger is lifted and we
// receive ET_GESTURE_END. However ET_GESTURE_END doesn't always get
// dispatched. (E.g. if the gesture target was hidden or deleted).
// Since we can't rely on receiving ET_GESTURE_END when the last finger is
// lifted, we also reset the state on ET_GESTURE_BEGIN when the first
// finger touches the screen.
SetState(NONE);
}
if (scroll_target_ && event->target() != scroll_target_) if (scroll_target_ && event->target() != scroll_target_)
return; return;
const gfx::PointF& event_location = event->location_f(); const gfx::PointF& event_location = event->location_f();
const ui::GestureEventDetails& event_details = event->details();
int num_touch_points = event_details.touch_points();
float scroll_delta = kScrollDeltaNone; float scroll_delta = kScrollDeltaNone;
if (scroll_bezel_ != BEZEL_NONE) { aura::Window* target_window = static_cast<aura::Window*>(event->target());
aura::Window* target_window = static_cast<aura::Window*>(event->target()); if (scroll_bezel_ != BEZEL_NONE)
scroll_delta = GetDistance(event_location, target_window, scroll_bezel_); scroll_delta = GetDistance(event_location, target_window, scroll_bezel_);
}
if (type == ui::ET_GESTURE_BEGIN) { if (type == ui::ET_GESTURE_BEGIN) {
if (num_touch_points > 2) { if (num_touch_points > 2) {
SetState(IGNORE_CURRENT_SCROLL); SetState(IGNORE_CURRENT_SCROLL);
return; return;
} }
BezelController::Bezel event_bezel = GetBezel(event->location_f()); BezelController::Bezel event_bezel =
GetBezel(event->location_f(), target_window);
switch (state_) { switch (state_) {
case NONE: case NONE:
scroll_bezel_ = event_bezel; scroll_bezel_ = event_bezel;
...@@ -174,14 +188,14 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { ...@@ -174,14 +188,14 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) {
DCHECK_EQ(num_touch_points, 2); DCHECK_EQ(num_touch_points, 2);
SetState(BEZEL_SCROLLING_TWO_FINGERS, scroll_delta); SetState(BEZEL_SCROLLING_TWO_FINGERS, scroll_delta);
if (left_right_delegate_->CanScroll()) if (left_right_delegate_->BezelCanScroll())
event->SetHandled(); event->SetHandled();
} else if (type == ui::ET_GESTURE_SCROLL_UPDATE) { } else if (type == ui::ET_GESTURE_SCROLL_UPDATE) {
if (state_ != BEZEL_SCROLLING_TWO_FINGERS) if (state_ != BEZEL_SCROLLING_TWO_FINGERS)
return; return;
left_right_delegate_->ScrollUpdate(scroll_delta); left_right_delegate_->BezelScrollUpdate(scroll_delta);
if (left_right_delegate_->CanScroll()) if (left_right_delegate_->BezelCanScroll())
event->SetHandled(); event->SetHandled();
} }
} }
......
...@@ -37,18 +37,18 @@ class BezelController : public ui::EventHandler { ...@@ -37,18 +37,18 @@ class BezelController : public ui::EventHandler {
// Beginning of a bezel scroll gesture started from the |bezel|. // Beginning of a bezel scroll gesture started from the |bezel|.
// |delta| is the difference between the x-coordinate of the current scroll // |delta| is the difference between the x-coordinate of the current scroll
// position and the bezel. It will be zero or negative for the right bezel. // position and the bezel. It will be zero or negative for the right bezel.
virtual void ScrollBegin(Bezel bezel, float delta) = 0; virtual void BezelScrollBegin(Bezel bezel, float delta) = 0;
// End of the current bezel scroll // End of the current bezel scroll
virtual void ScrollEnd() = 0; virtual void BezelScrollEnd() = 0;
// Update of the scroll position for the currently active bezel scroll. // Update of the scroll position for the currently active bezel scroll.
// |delta| has the same meaning as in ScrollBegin(). // |delta| has the same meaning as in ScrollBegin().
virtual void ScrollUpdate(float delta) = 0; virtual void BezelScrollUpdate(float delta) = 0;
// Should return false if the delegate isn't going to react to the scroll // Should return false if the delegate isn't going to react to the scroll
// events. // events.
virtual bool CanScroll() = 0; virtual bool BezelCanScroll() = 0;
}; };
explicit BezelController(aura::Window* container); explicit BezelController(aura::Window* container);
...@@ -75,10 +75,6 @@ class BezelController : public ui::EventHandler { ...@@ -75,10 +75,6 @@ class BezelController : public ui::EventHandler {
// BEZEL_SROLLING states. // BEZEL_SROLLING states.
void SetState(State state, float scroll_delta); void SetState(State state, float scroll_delta);
// Returns the bezel corresponding to the |location| or BEZEL_NONE if the
// location is outside of the bezel area.
Bezel GetBezel(const gfx::PointF& location);
// ui::EventHandler overrides // ui::EventHandler overrides
virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
......
...@@ -10,23 +10,128 @@ ...@@ -10,23 +10,128 @@
#include "athena/wm/public/window_list_provider.h" #include "athena/wm/public/window_list_provider.h"
#include "athena/wm/public/window_manager.h" #include "athena/wm/public/window_manager.h"
#include "base/bind.h" #include "base/bind.h"
#include "ui/aura/scoped_window_targeter.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_targeter.h"
#include "ui/compositor/closure_animation_observer.h" #include "ui/compositor/closure_animation_observer.h"
#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event_handler.h" #include "ui/events/event_handler.h"
#include "ui/gfx/display.h" #include "ui/gfx/display.h"
#include "ui/gfx/screen.h" #include "ui/gfx/screen.h"
#include "ui/views/background.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/root_view_targeter.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h" #include "ui/wm/core/window_util.h"
namespace athena { namespace athena {
namespace { namespace {
// Returns a target transform which is suitable for animating a windows's const int kDragHandleWidth = 4;
// bounds. const int kDragHandleHeight = 80;
gfx::Transform GetTargetTransformForBoundsAnimation(const gfx::Rect& from, const int kDragHandleMargin = 1;
const gfx::Rect& to) { const int kDividerWidth = kDragHandleWidth + 2 * kDragHandleMargin;
// Always returns the same target.
class StaticViewTargeterDelegate : public views::ViewTargeterDelegate {
public:
explicit StaticViewTargeterDelegate(views::View* target) : target_(target) {}
virtual ~StaticViewTargeterDelegate() {}
private:
// views::ViewTargeterDelegate:
virtual views::View* TargetForRect(views::View* root,
const gfx::Rect& rect) OVERRIDE {
return target_;
}
// Not owned.
views::View* target_;
DISALLOW_COPY_AND_ASSIGN(StaticViewTargeterDelegate);
};
// Expands the effective target area of the window of the widget containing the
// specified view. If the view is large enough to begin with, there should be
// no change from the default targeting behavior.
class PriorityWindowTargeter : public aura::WindowTargeter,
public aura::WindowObserver {
public:
explicit PriorityWindowTargeter(views::View* priority_view)
: priority_view_(priority_view) {
CHECK(priority_view->GetWidget());
window_ = priority_view->GetWidget()->GetNativeWindow();
CHECK(window_);
window_->AddObserver(this);
}
virtual ~PriorityWindowTargeter() {
window_->RemoveObserver(this);
}
private:
// aura::WindowTargeter:
virtual ui::EventTarget* FindTargetForLocatedEvent(
ui::EventTarget* root,
ui::LocatedEvent* event) OVERRIDE {
if (!window_ || (event->type() != ui::ET_TOUCH_PRESSED))
return WindowTargeter::FindTargetForLocatedEvent(root, event);
CHECK_EQ(window_, priority_view_->GetWidget()->GetNativeWindow());
// Bounds of the view in root window's coordinates.
gfx::Rect view_bounds = priority_view_->GetBoundsInScreen();
// If there is a transform on the window's layer - apply it.
gfx::Transform window_transform = window_->layer()->transform();
gfx::RectF transformed_bounds_f = view_bounds;
window_transform.TransformRect(&transformed_bounds_f);
gfx::Rect transformed_bounds = gfx::Rect(transformed_bounds_f.x(),
transformed_bounds_f.y(),
transformed_bounds_f.width(),
transformed_bounds_f.height());
// Now expand the bounds to be at least
// kMinTouchDimension x kMinTouchDimension and target the event to the
// window if it falls within the expanded bounds
gfx::Point center = transformed_bounds.CenterPoint();
gfx::Rect extension_rect = gfx::Rect(
center.x() - kMinTouchDimension / 2,
center.y() - kMinTouchDimension / 2,
kMinTouchDimension,
kMinTouchDimension);
gfx::Rect extended_bounds =
gfx::UnionRects(transformed_bounds, extension_rect);
if (extended_bounds.Contains(event->root_location())) {
root->ConvertEventToTarget(window_, event);
return window_;
}
return WindowTargeter::FindTargetForLocatedEvent(root, event);
}
// aura::WindowObserver:
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {
DCHECK_EQ(window, window_);
window_->RemoveObserver(this);
window_ = NULL;
}
// Minimum dimension of a target to be comfortably touchable.
// The effective touch target area of |priority_window_| gets expanded so
// that it's width and height is ayt least |kMinTouchDimension|.
int const kMinTouchDimension = 26;
aura::Window* window_;
views::View* priority_view_;
DISALLOW_COPY_AND_ASSIGN(PriorityWindowTargeter);
};
// Returns a target transform required to transform |from| to |to|.
gfx::Transform GetTransformForBounds(const gfx::Rect& from,
const gfx::Rect& to) {
gfx::Transform transform; gfx::Transform transform;
transform.Translate(to.x() - from.x(), to.y() - from.y()); transform.Translate(to.x() - from.x(), to.y() - from.y());
transform.Scale(to.width() / static_cast<float>(from.width()), transform.Scale(to.width() / static_cast<float>(from.width()),
...@@ -49,7 +154,10 @@ SplitViewController::SplitViewController( ...@@ -49,7 +154,10 @@ SplitViewController::SplitViewController(
window_list_provider_(window_list_provider), window_list_provider_(window_list_provider),
left_window_(NULL), left_window_(NULL),
right_window_(NULL), right_window_(NULL),
separator_position_(0), divider_position_(0),
divider_scroll_start_position_(0),
divider_widget_(NULL),
drag_handle_(NULL),
weak_factory_(this) { weak_factory_(this) {
} }
...@@ -103,6 +211,7 @@ void SplitViewController::ActivateSplitMode(aura::Window* left, ...@@ -103,6 +211,7 @@ void SplitViewController::ActivateSplitMode(aura::Window* left,
if (right_window_ && right_window_ != left && right_window_ != right) if (right_window_ && right_window_ != left && right_window_ != right)
to_hide_.push_back(right_window_); to_hide_.push_back(right_window_);
divider_position_ = GetDefaultDividerPosition();
SetState(ACTIVE); SetState(ACTIVE);
right_window_ = right; right_window_ = right;
left_window_ = left; left_window_ = left;
...@@ -134,41 +243,125 @@ void SplitViewController::DeactivateSplitMode() { ...@@ -134,41 +243,125 @@ void SplitViewController::DeactivateSplitMode() {
left_window_ = right_window_ = NULL; left_window_ = right_window_ = NULL;
} }
gfx::Rect SplitViewController::GetLeftTargetBounds() { void SplitViewController::InitializeDivider() {
CHECK(!divider_widget_);
CHECK(!drag_handle_);
drag_handle_ = CreateDragHandleView(DRAG_HANDLE_HORIZONTAL,
this,
kDragHandleWidth,
kDragHandleHeight);
views::View* content_view = new views::View;
content_view->set_background(
views::Background::CreateSolidBackground(SK_ColorBLACK));
views::BoxLayout* layout =
new views::BoxLayout(views::BoxLayout::kHorizontal,
kDragHandleMargin,
kDragHandleMargin,
0);
layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER);
layout->set_cross_axis_alignment(
views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER);
content_view->SetLayoutManager(layout);
content_view->AddChildView(drag_handle_);
divider_widget_ = new views::Widget();
views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP);
params.parent = container_;
params.bounds = gfx::Rect(-kDividerWidth / 2,
0,
kDividerWidth,
container_->bounds().height());
divider_widget_->Init(params);
divider_widget_->SetContentsView(content_view);
// Install a static view targeter on the root view which always targets
// divider_view.
// TODO(mfomitchev,tdanderson): This should not be needed:
// 1. crbug.com/414339 - divider_view is the only view and it completely
// overlaps the root view.
// 2. The logic in ViewTargeterDelegate::TargetForRect could be improved to
// work better for views that are narrow in one dimension and long in
// another dimension.
views::internal::RootView* root_view =
static_cast<views::internal::RootView*>(divider_widget_->GetRootView());
view_targeter_delegate_.reset(new StaticViewTargeterDelegate(drag_handle_));
views::ViewTargeter* targeter =
new views::RootViewTargeter(view_targeter_delegate_.get(), root_view);
divider_widget_->GetRootView()->SetEventTargeter(
scoped_ptr<views::ViewTargeter>(targeter));
}
void SplitViewController::HideDivider() {
divider_widget_->Hide();
window_targeter_.reset();
}
void SplitViewController::ShowDivider() {
divider_widget_->Show();
if (!window_targeter_) {
scoped_ptr<ui::EventTargeter> window_targeter =
scoped_ptr<ui::EventTargeter>(new PriorityWindowTargeter(drag_handle_));
window_targeter_.reset(
new aura::ScopedWindowTargeter(container_, window_targeter.Pass()));
}
}
gfx::Rect SplitViewController::GetLeftAreaBounds() {
gfx::Rect work_area = gfx::Rect work_area =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area();
return gfx::Rect(0, 0, container_->bounds().width() / 2, work_area.height()); return gfx::Rect(
0, 0, divider_position_ - kDividerWidth / 2, work_area.height());
} }
gfx::Rect SplitViewController::GetRightTargetBounds() { gfx::Rect SplitViewController::GetRightAreaBounds() {
gfx::Rect work_area = gfx::Rect work_area =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area();
int container_width = container_->bounds().width(); int container_width = container_->bounds().width();
return gfx::Rect( return gfx::Rect(divider_position_ + kDividerWidth / 2,
container_width / 2, 0, container_width / 2, work_area.height()); 0,
container_width - divider_position_ - kDividerWidth / 2,
work_area.height());
} }
void SplitViewController::SetState(SplitViewController::State state) { void SplitViewController::SetState(SplitViewController::State state) {
if (state_ == state) if (state_ == state)
return; return;
if (divider_widget_ == NULL)
InitializeDivider();
state_ = state; state_ = state;
ScreenManager::Get()->SetRotationLocked(state_ != INACTIVE); ScreenManager::Get()->SetRotationLocked(state_ != INACTIVE);
if (state == INACTIVE)
HideDivider();
else
ShowDivider();
} }
void SplitViewController::UpdateLayout(bool animate) { void SplitViewController::UpdateLayout(bool animate) {
CHECK(left_window_); CHECK(left_window_);
CHECK(right_window_); CHECK(right_window_);
// Splitview can be activated from SplitViewController::ActivateSplitMode or // Splitview can be activated from SplitViewController::ActivateSplitMode or
// SplitViewController::ScrollEnd. Additionally we don't want to rotate the // SplitViewController::ScrollEnd. Additionally we don't want to rotate the
// screen while engaging splitview (i.e. state_ == SCROLLING). // screen while engaging splitview (i.e. state_ == SCROLLING).
if (state_ == INACTIVE && !animate) { if (state_ == INACTIVE && !animate) {
if (!wm::IsActiveWindow(left_window_)) gfx::Rect work_area =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area();
aura::Window* top_window = window_list_provider_->GetWindowList().back();
if (top_window != left_window_) {
// TODO(mfomitchev): Use to_hide_ instead
left_window_->Hide(); left_window_->Hide();
if (!wm::IsActiveWindow(right_window_)) right_window_->SetBounds(gfx::Rect(work_area.size()));
}
if (top_window != right_window_) {
left_window_->SetBounds(gfx::Rect(work_area.size()));
// TODO(mfomitchev): Use to_hide_ instead
right_window_->Hide(); right_window_->Hide();
SetWindowTransforms(gfx::Transform(), gfx::Transform(), false); }
SetWindowTransforms(
gfx::Transform(), gfx::Transform(), gfx::Transform(), false);
return; return;
} }
...@@ -177,25 +370,46 @@ void SplitViewController::UpdateLayout(bool animate) { ...@@ -177,25 +370,46 @@ void SplitViewController::UpdateLayout(bool animate) {
window_list_provider_->MoveToFront(right_window_); window_list_provider_->MoveToFront(right_window_);
window_list_provider_->MoveToFront(left_window_); window_list_provider_->MoveToFront(left_window_);
gfx::Transform divider_transform;
divider_transform.Translate(divider_position_, 0);
if (state_ == ACTIVE) { if (state_ == ACTIVE) {
if (animate) { if (animate) {
gfx::Transform left_transform = GetTargetTransformForBoundsAnimation( gfx::Transform left_transform =
left_window_->bounds(), GetLeftTargetBounds()); GetTransformForBounds(left_window_->bounds(), GetLeftAreaBounds());
gfx::Transform right_transform = GetTargetTransformForBoundsAnimation( gfx::Transform right_transform =
right_window_->bounds(), GetRightTargetBounds()); GetTransformForBounds(right_window_->bounds(), GetRightAreaBounds());
SetWindowTransforms(left_transform, right_transform, true); SetWindowTransforms(
left_transform, right_transform, divider_transform, true);
} else { } else {
left_window_->SetBounds(GetLeftTargetBounds()); left_window_->SetBounds(GetLeftAreaBounds());
right_window_->SetBounds(GetRightTargetBounds()); right_window_->SetBounds(GetRightAreaBounds());
SetWindowTransforms(gfx::Transform(), gfx::Transform(), false); SetWindowTransforms(
gfx::Transform(), gfx::Transform(), divider_transform, false);
} }
} else { } else {
gfx::Transform left_transform; gfx::Transform left_transform;
left_transform.Translate(separator_position_ - container_->bounds().width(),
0);
gfx::Transform right_transform; gfx::Transform right_transform;
right_transform.Translate(separator_position_, 0); gfx::Rect left_area_bounds = GetLeftAreaBounds();
SetWindowTransforms(left_transform, right_transform, animate); gfx::Rect right_area_bounds = GetRightAreaBounds();
// If the width of the window is greater than the width of the area which it
// is supposed to occupy - translate the window. Otherwise scale the window
// up to fill the target area.
if (left_window_->bounds().width() >= left_area_bounds.width()) {
left_transform.Translate(
left_area_bounds.right() - left_window_->bounds().right(), 0);
} else {
left_transform =
GetTransformForBounds(left_window_->bounds(), left_area_bounds);
}
if (right_window_->bounds().width() >= right_area_bounds.width()) {
right_transform.Translate(
right_area_bounds.x() - right_window_->bounds().x(), 0);
} else {
right_transform =
GetTransformForBounds(right_window_->bounds(), right_area_bounds);
}
SetWindowTransforms(
left_transform, right_transform, divider_transform, animate);
} }
// Note: |left_window_| and |right_window_| may be NULL if calling // Note: |left_window_| and |right_window_| may be NULL if calling
// SetWindowTransforms(): // SetWindowTransforms():
...@@ -206,6 +420,7 @@ void SplitViewController::UpdateLayout(bool animate) { ...@@ -206,6 +420,7 @@ void SplitViewController::UpdateLayout(bool animate) {
void SplitViewController::SetWindowTransforms( void SplitViewController::SetWindowTransforms(
const gfx::Transform& left_transform, const gfx::Transform& left_transform,
const gfx::Transform& right_transform, const gfx::Transform& right_transform,
const gfx::Transform& divider_transform,
bool animate) { bool animate) {
if (animate) { if (animate) {
ui::ScopedLayerAnimationSettings left_settings( ui::ScopedLayerAnimationSettings left_settings(
...@@ -214,6 +429,12 @@ void SplitViewController::SetWindowTransforms( ...@@ -214,6 +429,12 @@ void SplitViewController::SetWindowTransforms(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
left_window_->SetTransform(left_transform); left_window_->SetTransform(left_transform);
ui::ScopedLayerAnimationSettings divider_widget_settings(
divider_widget_->GetNativeWindow()->layer()->GetAnimator());
divider_widget_settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
divider_widget_->GetNativeWindow()->SetTransform(divider_transform);
ui::ScopedLayerAnimationSettings right_settings( ui::ScopedLayerAnimationSettings right_settings(
right_window_->layer()->GetAnimator()); right_window_->layer()->GetAnimator());
right_settings.SetPreemptionStrategy( right_settings.SetPreemptionStrategy(
...@@ -224,6 +445,7 @@ void SplitViewController::SetWindowTransforms( ...@@ -224,6 +445,7 @@ void SplitViewController::SetWindowTransforms(
right_window_->SetTransform(right_transform); right_window_->SetTransform(right_transform);
} else { } else {
left_window_->SetTransform(left_transform); left_window_->SetTransform(left_transform);
divider_widget_->GetNativeWindow()->SetTransform(divider_transform);
right_window_->SetTransform(right_transform); right_window_->SetTransform(right_transform);
} }
} }
...@@ -244,30 +466,24 @@ void SplitViewController::OnAnimationCompleted() { ...@@ -244,30 +466,24 @@ void SplitViewController::OnAnimationCompleted() {
} }
} }
void SplitViewController::UpdateSeparatorPositionFromScrollDelta(float delta) { int SplitViewController::GetDefaultDividerPosition() {
gfx::Screen* screen = gfx::Screen::GetScreenFor(container_); return container_->GetBoundsInScreen().width() / 2;
const gfx::Rect& display_bounds =
screen->GetDisplayNearestWindow(container_).bounds();
gfx::Rect container_bounds = container_->GetBoundsInScreen();
separator_position_ =
delta > 0 ? ((int)delta) + display_bounds.x() - container_bounds.x()
: display_bounds.right() - container_bounds.x() + delta;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// BezelController::ScrollDelegate: // BezelController::ScrollDelegate:
void SplitViewController::ScrollBegin(BezelController::Bezel bezel, void SplitViewController::BezelScrollBegin(BezelController::Bezel bezel,
float delta) { float delta) {
if (!CanScroll()) if (!BezelCanScroll())
return; return;
SetState(SCROLLING); SetState(SCROLLING);
const aura::Window::Windows& windows = window_list_provider_->GetWindowList(); const aura::Window::Windows& windows = window_list_provider_->GetWindowList();
CHECK(windows.size() >= 2); CHECK(windows.size() >= 2);
aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); aura::Window::Windows::const_reverse_iterator iter = windows.rbegin();
aura::Window* current_window = *(iter); aura::Window* current_window = *(iter);
CHECK(wm::IsActiveWindow(current_window));
if (delta > 0) { if (delta > 0) {
right_window_ = current_window; right_window_ = current_window;
...@@ -280,43 +496,90 @@ void SplitViewController::ScrollBegin(BezelController::Bezel bezel, ...@@ -280,43 +496,90 @@ void SplitViewController::ScrollBegin(BezelController::Bezel bezel,
CHECK(left_window_); CHECK(left_window_);
CHECK(right_window_); CHECK(right_window_);
UpdateSeparatorPositionFromScrollDelta(delta); // Calculate divider_scroll_start_position_
gfx::Screen* screen = gfx::Screen::GetScreenFor(container_);
const gfx::Rect& display_bounds =
screen->GetDisplayNearestWindow(container_).bounds();
gfx::Rect container_bounds = container_->GetBoundsInScreen();
divider_scroll_start_position_ =
delta > 0 ? display_bounds.x() - container_bounds.x()
: display_bounds.right() - container_bounds.x();
divider_position_ = divider_scroll_start_position_ + delta;
UpdateLayout(false); UpdateLayout(false);
} }
void SplitViewController::ScrollEnd() { void SplitViewController::BezelScrollEnd() {
if (state_ != SCROLLING) if (state_ != SCROLLING)
return; return;
// Max distance from the scroll end position to the middle of the screen where // Max distance from the scroll end position to the middle of the screen where
// we would go into the split view mode. // we would go into the split view mode.
const int kMaxDistanceFromMiddle = 120; const int kMaxDistanceFromMiddle = 120;
int container_width = container_->GetBoundsInScreen().width(); const int default_divider_position = GetDefaultDividerPosition();
if (std::abs(container_width / 2 - separator_position_) <= if (std::abs(default_divider_position - divider_position_) <=
kMaxDistanceFromMiddle) { kMaxDistanceFromMiddle) {
divider_position_ = default_divider_position;
SetState(ACTIVE); SetState(ACTIVE);
separator_position_ = container_width / 2; } else if (divider_position_ < default_divider_position) {
} else if (separator_position_ < container_width / 2) { divider_position_ = 0;
separator_position_ = 0;
SetState(INACTIVE); SetState(INACTIVE);
wm::ActivateWindow(right_window_); wm::ActivateWindow(right_window_);
} else { } else {
separator_position_ = container_width; divider_position_ = container_->GetBoundsInScreen().width();
SetState(INACTIVE); SetState(INACTIVE);
wm::ActivateWindow(left_window_); wm::ActivateWindow(left_window_);
} }
UpdateLayout(true); UpdateLayout(true);
} }
void SplitViewController::ScrollUpdate(float delta) { void SplitViewController::BezelScrollUpdate(float delta) {
if (state_ != SCROLLING) if (state_ != SCROLLING)
return; return;
UpdateSeparatorPositionFromScrollDelta(delta); divider_position_ = divider_scroll_start_position_ + delta;
UpdateLayout(false); UpdateLayout(false);
} }
bool SplitViewController::CanScroll() { bool SplitViewController::BezelCanScroll() {
return CanActivateSplitViewMode(); return CanActivateSplitViewMode();
} }
///////////////////////////////////////////////////////////////////////////////
// DragHandleScrollDelegate:
void SplitViewController::HandleScrollBegin(float delta) {
CHECK(state_ == ACTIVE);
state_ = SCROLLING;
divider_scroll_start_position_ = GetDefaultDividerPosition();
divider_position_ = divider_scroll_start_position_ + delta;
UpdateLayout(false);
}
void SplitViewController::HandleScrollEnd() {
BezelScrollEnd();
}
void SplitViewController::HandleScrollUpdate(float delta) {
BezelScrollUpdate(delta);
}
///////////////////////////////////////////////////////////////////////////////
// WindowManagerObserver:
void SplitViewController::OnOverviewModeEnter() {
if (divider_widget_)
HideDivider();
}
void SplitViewController::OnOverviewModeExit() {
if (state_ != INACTIVE)
ShowDivider();
}
void SplitViewController::OnSplitViewModeEnter() {
}
void SplitViewController::OnSplitViewModeExit() {
}
} // namespace athena } // namespace athena
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
#define ATHENA_WM_SPLIT_VIEW_CONTROLLER_H_ #define ATHENA_WM_SPLIT_VIEW_CONTROLLER_H_
#include "athena/athena_export.h" #include "athena/athena_export.h"
#include "athena/util/drag_handle.h"
#include "athena/wm/bezel_controller.h" #include "athena/wm/bezel_controller.h"
#include "athena/wm/public/window_manager_observer.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
...@@ -15,13 +17,26 @@ class Rect; ...@@ -15,13 +17,26 @@ class Rect;
class Transform; class Transform;
} }
namespace aura {
class ScopedWindowTargeter;
class Window;
class WindowTargeter;
}
namespace views {
class ViewTargeterDelegate;
class Widget;
}
namespace athena { namespace athena {
class WindowListProvider; class WindowListProvider;
// Responsible for entering split view mode, exiting from split view mode, and // Responsible for entering split view mode, exiting from split view mode, and
// laying out the windows in split view mode. // laying out the windows in split view mode.
class ATHENA_EXPORT SplitViewController class ATHENA_EXPORT SplitViewController
: public BezelController::ScrollDelegate { : public BezelController::ScrollDelegate,
public DragHandleScrollDelegate,
public WindowManagerObserver {
public: public:
SplitViewController(aura::Window* container, SplitViewController(aura::Window* container,
WindowListProvider* window_list_provider); WindowListProvider* window_list_provider);
...@@ -45,12 +60,10 @@ class ATHENA_EXPORT SplitViewController ...@@ -45,12 +60,10 @@ class ATHENA_EXPORT SplitViewController
void ReplaceWindow(aura::Window* window, void ReplaceWindow(aura::Window* window,
aura::Window* replace_with); aura::Window* replace_with);
// Returns the bounds that the left and right windows will have once split // Returns the bounds of the left and right parts of the |container_| based
// view is active and they are done animating. If |left_window_| and // on the current value of |divider_position_|.
// |right_window_| are still animating this may be different than their gfx::Rect GetLeftAreaBounds();
// current bounds. gfx::Rect GetRightAreaBounds();
gfx::Rect GetLeftTargetBounds();
gfx::Rect GetRightTargetBounds();
aura::Window* left_window() { return left_window_; } aura::Window* left_window() { return left_window_; }
aura::Window* right_window() { return right_window_; } aura::Window* right_window() { return right_window_; }
...@@ -61,7 +74,7 @@ class ATHENA_EXPORT SplitViewController ...@@ -61,7 +74,7 @@ class ATHENA_EXPORT SplitViewController
// NULL. // NULL.
INACTIVE, INACTIVE,
// Two windows |left_window_| and |right_window| are shown side by side and // Two windows |left_window_| and |right_window| are shown side by side and
// there is a horizontal scroll in progress which is dragging the separator // there is a horizontal scroll in progress which is dragging the divider
// between the two windows. // between the two windows.
SCROLLING, SCROLLING,
// Split View mode is active with |left_window_| and |right_window| showing // Split View mode is active with |left_window_| and |right_window| showing
...@@ -70,22 +83,42 @@ class ATHENA_EXPORT SplitViewController ...@@ -70,22 +83,42 @@ class ATHENA_EXPORT SplitViewController
}; };
void SetState(State state); void SetState(State state);
void InitializeDivider();
void HideDivider();
void ShowDivider();
void UpdateLayout(bool animate); void UpdateLayout(bool animate);
void SetWindowTransforms(const gfx::Transform& left_transform, void SetWindowTransforms(const gfx::Transform& left_transform,
const gfx::Transform& right_transform, const gfx::Transform& right_transform,
const gfx::Transform& divider_transform,
bool animate); bool animate);
// Called when the animation initiated by SetWindowTransforms() completes. // Called when the animation initiated by SetWindowTransforms() completes.
void OnAnimationCompleted(); void OnAnimationCompleted();
void UpdateSeparatorPositionFromScrollDelta(float delta); // Returns the default divider position for when the split view mode is
// active and the divider is not being dragged.
int GetDefaultDividerPosition();
// BezelController::ScrollDelegate: // BezelController::ScrollDelegate:
virtual void ScrollBegin(BezelController::Bezel bezel, float delta) OVERRIDE; virtual void BezelScrollBegin(BezelController::Bezel bezel,
virtual void ScrollEnd() OVERRIDE; float delta) OVERRIDE;
virtual void ScrollUpdate(float delta) OVERRIDE; virtual void BezelScrollEnd() OVERRIDE;
virtual bool CanScroll() OVERRIDE; virtual void BezelScrollUpdate(float delta) OVERRIDE;
virtual bool BezelCanScroll() OVERRIDE;
// DragHandleScrollDelegate:
virtual void HandleScrollBegin(float delta) OVERRIDE;
virtual void HandleScrollEnd() OVERRIDE;
virtual void HandleScrollUpdate(float delta) OVERRIDE;
// WindowManagerObserver:
virtual void OnOverviewModeEnter() OVERRIDE;
virtual void OnOverviewModeExit() OVERRIDE;
virtual void OnSplitViewModeEnter() OVERRIDE;
virtual void OnSplitViewModeExit() OVERRIDE;
State state_; State state_;
...@@ -99,9 +132,23 @@ class ATHENA_EXPORT SplitViewController ...@@ -99,9 +132,23 @@ class ATHENA_EXPORT SplitViewController
aura::Window* left_window_; aura::Window* left_window_;
aura::Window* right_window_; aura::Window* right_window_;
// Position of the separator between left_window_ and right_window_ in // X-Coordinate of the (center of the) divider between left_window_ and
// container_ coordinates (along the x axis). // right_window_ in |container_| coordinates.
int separator_position_; int divider_position_;
// Meaningful only when state_ is SCROLLING.
// X-Coordinate of the divider when the scroll started.
int divider_scroll_start_position_;
// Visually separates the windows and contains the drag handle.
views::Widget* divider_widget_;
// The drag handle which can be used when split view is active to exit the
// split view mode.
views::View* drag_handle_;
scoped_ptr<aura::ScopedWindowTargeter> window_targeter_;
scoped_ptr<views::ViewTargeterDelegate> view_targeter_delegate_;
// Windows which should be hidden when the animation initiated by // Windows which should be hidden when the animation initiated by
// UpdateLayout() completes. // UpdateLayout() completes.
......
...@@ -149,6 +149,7 @@ WindowManagerImpl::WindowManagerImpl() { ...@@ -149,6 +149,7 @@ WindowManagerImpl::WindowManagerImpl() {
bezel_controller_.reset(new BezelController(container_.get())); bezel_controller_.reset(new BezelController(container_.get()));
split_view_controller_.reset( split_view_controller_.reset(
new SplitViewController(container_.get(), window_list_provider_.get())); new SplitViewController(container_.get(), window_list_provider_.get()));
AddObserver(split_view_controller_.get());
bezel_controller_->set_left_right_delegate(split_view_controller_.get()); bezel_controller_->set_left_right_delegate(split_view_controller_.get());
container_->AddPreTargetHandler(bezel_controller_.get()); container_->AddPreTargetHandler(bezel_controller_.get());
title_drag_controller_.reset(new TitleDragController(container_.get(), this)); title_drag_controller_.reset(new TitleDragController(container_.get(), this));
...@@ -162,6 +163,7 @@ WindowManagerImpl::WindowManagerImpl() { ...@@ -162,6 +163,7 @@ WindowManagerImpl::WindowManagerImpl() {
WindowManagerImpl::~WindowManagerImpl() { WindowManagerImpl::~WindowManagerImpl() {
overview_.reset(); overview_.reset();
RemoveObserver(split_view_controller_.get());
split_view_controller_.reset(); split_view_controller_.reset();
window_list_provider_.reset(); window_list_provider_.reset();
if (container_) { if (container_) {
......
...@@ -318,8 +318,8 @@ TEST_F(WindowManagerTest, SplitModeActivationByShortcut) { ...@@ -318,8 +318,8 @@ TEST_F(WindowManagerTest, SplitModeActivationByShortcut) {
int width = int width =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().width(); gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().width();
EXPECT_EQ(width / 2, w1->bounds().width()); EXPECT_EQ(w1->bounds().width(), w2->bounds().width());
EXPECT_EQ(width / 2, w2->bounds().width()); EXPECT_GE(width / 2, w1->bounds().width());
// Toggle back to normal mode. // Toggle back to normal mode.
generator.PressKey(ui::VKEY_F6, ui::EF_CONTROL_DOWN); generator.PressKey(ui::VKEY_F6, ui::EF_CONTROL_DOWN);
......
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