OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "athena/wm/split_view_controller.h" | 5 #include "athena/wm/split_view_controller.h" |
6 | 6 |
| 7 #include "athena/wm/public/window_manager.h" |
| 8 #include "athena/wm/window_list_provider.h" |
| 9 #include "base/bind.h" |
7 #include "ui/aura/window.h" | 10 #include "ui/aura/window.h" |
| 11 #include "ui/compositor/layer_animation_observer.h" |
| 12 #include "ui/compositor/scoped_layer_animation_settings.h" |
8 #include "ui/events/event_handler.h" | 13 #include "ui/events/event_handler.h" |
| 14 #include "ui/gfx/display.h" |
| 15 #include "ui/gfx/screen.h" |
9 | 16 |
10 namespace athena { | 17 namespace athena { |
11 | 18 namespace { |
12 SplitViewController::SplitViewController() { | 19 |
| 20 // An animation observer that runs a callback at the end of the animation, and |
| 21 // destroys itself. |
| 22 class CallbackAnimationObserver : public ui::ImplicitAnimationObserver { |
| 23 public: |
| 24 CallbackAnimationObserver(const base::Closure& closure) |
| 25 : closure_(closure) { |
| 26 } |
| 27 |
| 28 virtual ~CallbackAnimationObserver() {} |
| 29 |
| 30 private: |
| 31 // Overridden from ui::ImplicitAnimationObserver: |
| 32 virtual void OnImplicitAnimationsCompleted() OVERRIDE { |
| 33 if (!closure_.is_null()) |
| 34 closure_.Run(); |
| 35 delete this; |
| 36 } |
| 37 |
| 38 const base::Closure closure_; |
| 39 |
| 40 DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver); |
| 41 }; |
| 42 |
| 43 } // namespace |
| 44 |
| 45 SplitViewController::SplitViewController( |
| 46 aura::Window* container, |
| 47 WindowListProvider* window_list_provider, |
| 48 WindowManager* window_manager) |
| 49 : weak_factory_(this), |
| 50 state_(INACTIVE), |
| 51 container_(container), |
| 52 window_manager_(window_manager), |
| 53 window_list_provider_(window_list_provider), |
| 54 current_window_(NULL), |
| 55 left_window_(NULL), |
| 56 right_window_(NULL), |
| 57 separator_position_(0) { |
| 58 window_manager->AddObserver(this); |
13 } | 59 } |
14 | 60 |
15 SplitViewController::~SplitViewController() { | 61 SplitViewController::~SplitViewController() { |
16 } | 62 } |
17 | 63 |
| 64 bool SplitViewController::IsSplitViewModeActive() { |
| 65 return state_ == ACTIVE; |
| 66 } |
| 67 |
| 68 void SplitViewController::Layout(bool animate) { |
| 69 if (left_window_) { |
| 70 CHECK(right_window_); |
| 71 gfx::Transform left_transform; |
| 72 gfx::Transform right_transform; |
| 73 int container_width = container_->GetBoundsInScreen().width(); |
| 74 if (state_ == ACTIVE) { |
| 75 left_transform.Scale(.5, 1); |
| 76 right_transform.Scale(.5, 1); |
| 77 right_transform.Translate(container_width, 0); |
| 78 } else { |
| 79 left_transform.Translate(separator_position_ - container_width, 0); |
| 80 right_transform.Translate(separator_position_, 0); |
| 81 } |
| 82 left_window_->Show(); |
| 83 right_window_->Show(); |
| 84 SetWindowTransform(left_window_, left_transform, animate); |
| 85 SetWindowTransform(right_window_, right_transform, animate); |
| 86 } |
| 87 } |
| 88 |
| 89 void SplitViewController::SetWindowTransform( |
| 90 aura::Window* window, const gfx::Transform& transform, bool animate) { |
| 91 if (animate) { |
| 92 scoped_refptr<ui::LayerAnimator> animator = |
| 93 window->layer()->GetAnimator(); |
| 94 ui::ScopedLayerAnimationSettings settings(animator); |
| 95 settings.SetPreemptionStrategy( |
| 96 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 97 settings.AddObserver(new CallbackAnimationObserver( |
| 98 base::Bind(&SplitViewController::AnimationCompleted, |
| 99 weak_factory_.GetWeakPtr()))); |
| 100 //settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250)); |
| 101 window->SetTransform(transform); |
| 102 |
| 103 // TODO: In the end of the animation we need to hide the window that's off |
| 104 // screen and also set transforms for all windows to gfx::Transform |
| 105 // (for the case where one window is animated off screen). |
| 106 } else { |
| 107 window->SetTransform(transform); |
| 108 } |
| 109 } |
| 110 |
| 111 void SplitViewController::AnimationCompleted() { |
| 112 if (state_ == ACTIVE) { |
| 113 gfx::Rect bounds_size = gfx::Rect(container_->GetBoundsInScreen().size()); |
| 114 int container_width = bounds_size.width(); |
| 115 bounds_size.set_width(container_width / 2); |
| 116 left_window_->SetBounds(bounds_size); |
| 117 left_window_->SetTransform(gfx::Transform()); |
| 118 right_window_->SetBounds(bounds_size); |
| 119 gfx::Transform right_transform; |
| 120 right_transform.Translate(container_width / 2, 0); |
| 121 right_window_->SetTransform(right_transform); |
| 122 } else { |
| 123 int container_width = container_->GetBoundsInScreen().width(); |
| 124 left_window_->SetTransform(gfx::Transform()); |
| 125 right_window_->SetTransform(gfx::Transform()); |
| 126 if (separator_position_ == 0) |
| 127 left_window_->Hide(); |
| 128 else if (separator_position_ == container_width) |
| 129 right_window_->Hide(); |
| 130 } |
| 131 } |
| 132 |
| 133 void SplitViewController::UpdateSeparatorPositionFromScrollDelta(float delta) { |
| 134 gfx::Screen* screen = gfx::Screen::GetScreenFor(container_); |
| 135 const gfx::Rect& display_bounds = |
| 136 screen->GetDisplayNearestWindow(container_).bounds(); |
| 137 gfx::Rect container_bounds = container_->GetBoundsInScreen(); |
| 138 separator_position_ = delta > 0 ? |
| 139 ((int) delta) + display_bounds.x() - container_bounds.x() : |
| 140 display_bounds.right() - container_bounds.x() + delta; |
| 141 } |
| 142 |
| 143 aura::Window* SplitViewController::GetCurrentWindow() { |
| 144 if (!current_window_) { |
| 145 aura::Window::Windows windows = window_list_provider_->GetWindowList(); |
| 146 if (windows.empty()) |
| 147 return NULL; |
| 148 current_window_ = windows.back(); |
| 149 } |
| 150 return current_window_; |
| 151 } |
| 152 |
| 153 /////////////////////////////////////////////////////////////////////////////// |
| 154 // Begin BezelController::ScrollDelegate overrides. |
18 void SplitViewController::ScrollBegin(BezelController::Bezel bezel, | 155 void SplitViewController::ScrollBegin(BezelController::Bezel bezel, |
19 float delta) { | 156 float delta) { |
20 } | 157 if (!CanScroll()) |
21 | 158 return; |
| 159 state_ = SCROLLING; |
| 160 aura::Window* current_window = GetCurrentWindow(); |
| 161 CHECK(current_window); |
| 162 |
| 163 aura::Window::Windows windows = window_list_provider_->GetWindowList(); |
| 164 CHECK(windows.size() >= 2); |
| 165 aura::Window::Windows::const_iterator it = std::find( |
| 166 windows.begin(), windows.end(), current_window); |
| 167 CHECK(it != windows.end()); |
| 168 |
| 169 if (delta > 0) { |
| 170 right_window_ = current_window; |
| 171 // reverse iterator points to the position before normal iterator |it| |
| 172 aura::Window::Windows::const_reverse_iterator rev_it(it); |
| 173 // circle to end if needed. |
| 174 left_window_ = rev_it == windows.rend() ? windows.back() : *(rev_it); |
| 175 } else { |
| 176 left_window_ = current_window; |
| 177 ++it; |
| 178 // circle to front if needed. |
| 179 right_window_ = it == windows.end() ? windows.front() : *it; |
| 180 } |
| 181 |
| 182 CHECK(left_window_); |
| 183 CHECK(right_window_); |
| 184 |
| 185 // TODO (mfomitchev): |
| 186 // HACK until we are properly hiding off-screen windows in window manager |
| 187 // Loop through all windows and hide them |
| 188 for (it = windows.begin(); it != windows.end(); ++it) { |
| 189 if (*it != left_window_ && *it != right_window_) |
| 190 (*it)->Hide(); |
| 191 } |
| 192 // END HACK |
| 193 |
| 194 UpdateSeparatorPositionFromScrollDelta(delta); |
| 195 Layout(false); |
| 196 } |
| 197 |
| 198 // Max distance from the scroll end position to the middle of the screen where |
| 199 // we would go into the split view mode. |
| 200 const int kMaxDistanceFromMiddle = 120; |
22 void SplitViewController::ScrollEnd() { | 201 void SplitViewController::ScrollEnd() { |
| 202 if (state_ != SCROLLING) |
| 203 return; |
| 204 |
| 205 int cont_width = container_->GetBoundsInScreen().width(); |
| 206 if (abs(cont_width / 2 - separator_position_) <= kMaxDistanceFromMiddle) { |
| 207 state_ = ACTIVE; |
| 208 separator_position_ = cont_width / 2; |
| 209 } else if (separator_position_ < cont_width / 2) { |
| 210 separator_position_ = 0; |
| 211 current_window_ = right_window_; |
| 212 state_ = INACTIVE; |
| 213 } else { |
| 214 separator_position_ = cont_width; |
| 215 current_window_ = left_window_; |
| 216 state_ = INACTIVE; |
| 217 } |
| 218 Layout(true); |
23 } | 219 } |
24 | 220 |
25 void SplitViewController::ScrollUpdate(float delta) { | 221 void SplitViewController::ScrollUpdate(float delta) { |
| 222 if (state_ != SCROLLING) |
| 223 return; |
| 224 UpdateSeparatorPositionFromScrollDelta(delta); |
| 225 Layout(false); |
26 } | 226 } |
27 | 227 |
28 bool SplitViewController::CanScroll() { | 228 bool SplitViewController::CanScroll() { |
29 return false; | 229 // TODO (mfomitchev): return false in vertical orientation, in full screen. |
30 } | 230 bool result = (!window_manager_->IsOverviewModeActive() && |
| 231 !IsSplitViewModeActive() && |
| 232 window_list_provider_->GetWindowList().size() >= 2); |
| 233 return result; |
| 234 } |
| 235 |
| 236 /////////////////////////////////////////////////////////////////////////////// |
| 237 // WindowManagerObserver overrides |
| 238 void SplitViewController::OnOverviewModeEnter() { |
| 239 if (state_ == ACTIVE) { |
| 240 CHECK(left_window_); |
| 241 CHECK(right_window_); |
| 242 window_list_provider_->MoveToFront(right_window_); |
| 243 window_list_provider_->MoveToFront(left_window_); |
| 244 // TODO (mfomitchev): This shouldn't be done here, but the overview mode's |
| 245 // transition animation currently looks bad if the starting transform of |
| 246 // any window is not gfx::Transform(). |
| 247 right_window_->SetTransform(gfx::Transform()); |
| 248 } else if (current_window_) { |
| 249 window_list_provider_->MoveToFront(current_window_); |
| 250 } |
| 251 current_window_ = NULL; |
| 252 left_window_ = NULL; |
| 253 right_window_ = NULL; |
| 254 state_ = INACTIVE; |
| 255 } |
| 256 |
| 257 void SplitViewController::OnOverviewModeExit() {} |
31 | 258 |
32 } // namespace athena | 259 } // namespace athena |
OLD | NEW |