OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "athena/wm/split_view_controller.h" | |
6 | |
7 #include <cmath> | |
8 | |
9 #include "athena/screen/public/screen_manager.h" | |
10 #include "athena/wm/public/window_manager.h" | |
11 #include "athena/wm/window_list_provider_impl.h" | |
12 #include "base/bind.h" | |
13 #include "ui/aura/scoped_window_targeter.h" | |
14 #include "ui/aura/window.h" | |
15 #include "ui/aura/window_targeter.h" | |
16 #include "ui/compositor/closure_animation_observer.h" | |
17 #include "ui/compositor/layer.h" | |
18 #include "ui/compositor/scoped_layer_animation_settings.h" | |
19 #include "ui/events/event_handler.h" | |
20 #include "ui/gfx/display.h" | |
21 #include "ui/gfx/screen.h" | |
22 #include "ui/views/background.h" | |
23 #include "ui/views/layout/box_layout.h" | |
24 #include "ui/views/widget/root_view.h" | |
25 #include "ui/views/widget/root_view_targeter.h" | |
26 #include "ui/views/widget/widget.h" | |
27 #include "ui/wm/core/window_util.h" | |
28 #include "ui/wm/public/activation_client.h" | |
29 | |
30 namespace athena { | |
31 | |
32 namespace { | |
33 | |
34 const int kDragHandleWidth = 4; | |
35 const int kDragHandleHeight = 80; | |
36 const int kDragHandleMargin = 1; | |
37 const int kDividerWidth = kDragHandleWidth + 2 * kDragHandleMargin; | |
38 | |
39 // Max distance from the scroll end position to the middle of the screen where | |
40 // we would go into the split view mode. | |
41 const float kMaxDistanceFromMiddle = 120.0f; | |
42 | |
43 // The minimum x-velocity required for a fling to disengage split view mode | |
44 // when targeted to the drag handle. | |
45 const float kMinFlingVelocity = 800.0f; | |
46 | |
47 enum WindowToActivate { | |
48 // Do not activate either of |left_window_| or |right_window_|. | |
49 WINDOW_NONE, | |
50 // Activate |left_window_|. | |
51 WINDOW_LEFT, | |
52 // Activate |right_window_|. | |
53 WINDOW_RIGHT | |
54 }; | |
55 | |
56 // Always returns the same target. | |
57 class StaticViewTargeterDelegate : public views::ViewTargeterDelegate { | |
58 public: | |
59 explicit StaticViewTargeterDelegate(views::View* target) : target_(target) {} | |
60 | |
61 ~StaticViewTargeterDelegate() override {} | |
62 | |
63 private: | |
64 // views::ViewTargeterDelegate: | |
65 virtual views::View* TargetForRect(views::View* root, | |
66 const gfx::Rect& rect) override { | |
67 return target_; | |
68 } | |
69 | |
70 // Not owned. | |
71 views::View* target_; | |
72 | |
73 DISALLOW_COPY_AND_ASSIGN(StaticViewTargeterDelegate); | |
74 }; | |
75 | |
76 // Expands the effective target area of the window of the widget containing the | |
77 // specified view. If the view is large enough to begin with, there should be | |
78 // no change from the default targeting behavior. | |
79 class PriorityWindowTargeter : public aura::WindowTargeter, | |
80 public aura::WindowObserver { | |
81 public: | |
82 explicit PriorityWindowTargeter(views::View* priority_view) | |
83 : priority_view_(priority_view) { | |
84 CHECK(priority_view->GetWidget()); | |
85 window_ = priority_view->GetWidget()->GetNativeWindow(); | |
86 CHECK(window_); | |
87 window_->AddObserver(this); | |
88 } | |
89 | |
90 ~PriorityWindowTargeter() override { window_->RemoveObserver(this); } | |
91 | |
92 private: | |
93 // aura::WindowTargeter: | |
94 ui::EventTarget* FindTargetForLocatedEvent(ui::EventTarget* root, | |
95 ui::LocatedEvent* event) override { | |
96 if (!window_ || (event->type() != ui::ET_TOUCH_PRESSED)) | |
97 return WindowTargeter::FindTargetForLocatedEvent(root, event); | |
98 CHECK_EQ(window_, priority_view_->GetWidget()->GetNativeWindow()); | |
99 | |
100 // Bounds of the view in root window's coordinates. | |
101 gfx::Rect view_bounds = priority_view_->GetBoundsInScreen(); | |
102 // If there is a transform on the window's layer - apply it. | |
103 gfx::Transform window_transform = window_->layer()->transform(); | |
104 gfx::RectF transformed_bounds_f = view_bounds; | |
105 window_transform.TransformRect(&transformed_bounds_f); | |
106 gfx::Rect transformed_bounds = gfx::Rect(transformed_bounds_f.x(), | |
107 transformed_bounds_f.y(), | |
108 transformed_bounds_f.width(), | |
109 transformed_bounds_f.height()); | |
110 // Now expand the bounds to be at least | |
111 // kMinTouchDimension x kMinTouchDimension and target the event to the | |
112 // window if it falls within the expanded bounds | |
113 gfx::Point center = transformed_bounds.CenterPoint(); | |
114 gfx::Rect extension_rect = gfx::Rect( | |
115 center.x() - kMinTouchDimension / 2, | |
116 center.y() - kMinTouchDimension / 2, | |
117 kMinTouchDimension, | |
118 kMinTouchDimension); | |
119 gfx::Rect extended_bounds = | |
120 gfx::UnionRects(transformed_bounds, extension_rect); | |
121 if (extended_bounds.Contains(event->root_location())) { | |
122 root->ConvertEventToTarget(window_, event); | |
123 return window_; | |
124 } | |
125 | |
126 return WindowTargeter::FindTargetForLocatedEvent(root, event); | |
127 } | |
128 | |
129 // aura::WindowObserver: | |
130 void OnWindowDestroying(aura::Window* window) override { | |
131 DCHECK_EQ(window, window_); | |
132 window_->RemoveObserver(this); | |
133 window_ = nullptr; | |
134 } | |
135 | |
136 // Minimum dimension of a target to be comfortably touchable. | |
137 // The effective touch target area of |priority_window_| gets expanded so | |
138 // that it's width and height is ayt least |kMinTouchDimension|. | |
139 int const kMinTouchDimension = 26; | |
140 | |
141 aura::Window* window_; | |
142 views::View* priority_view_; | |
143 | |
144 DISALLOW_COPY_AND_ASSIGN(PriorityWindowTargeter); | |
145 }; | |
146 | |
147 // Returns a target transform required to transform |from| to |to|. | |
148 gfx::Transform GetTransformForBounds(const gfx::Rect& from, | |
149 const gfx::Rect& to) { | |
150 gfx::Transform transform; | |
151 transform.Translate(to.x() - from.x(), to.y() - from.y()); | |
152 transform.Scale(to.width() / static_cast<float>(from.width()), | |
153 to.height() / static_cast<float>(from.height())); | |
154 return transform; | |
155 } | |
156 | |
157 bool IsLandscapeOrientation(gfx::Display::Rotation rotation) { | |
158 return rotation == gfx::Display::ROTATE_0 || | |
159 rotation == gfx::Display::ROTATE_180; | |
160 } | |
161 | |
162 } // namespace | |
163 | |
164 SplitViewController::SplitViewController( | |
165 aura::Window* container, | |
166 WindowListProviderImpl* window_list_provider) | |
167 : state_(INACTIVE), | |
168 container_(container), | |
169 window_list_provider_(window_list_provider), | |
170 left_window_(nullptr), | |
171 right_window_(nullptr), | |
172 divider_position_(0), | |
173 divider_scroll_start_position_(0), | |
174 divider_widget_(nullptr), | |
175 drag_handle_(nullptr), | |
176 weak_factory_(this) { | |
177 window_list_provider_->AddObserver(this); | |
178 } | |
179 | |
180 SplitViewController::~SplitViewController() { | |
181 window_list_provider_->RemoveObserver(this); | |
182 } | |
183 | |
184 bool SplitViewController::CanActivateSplitViewMode() const { | |
185 // TODO(mfomitchev): return false in full screen. | |
186 return (!IsSplitViewModeActive() && | |
187 window_list_provider_->GetWindowList().size() >= 2 && | |
188 IsLandscapeOrientation(gfx::Screen::GetNativeScreen()-> | |
189 GetDisplayNearestWindow(container_).rotation())); | |
190 } | |
191 | |
192 bool SplitViewController::IsSplitViewModeActive() const { | |
193 return state_ == ACTIVE; | |
194 } | |
195 | |
196 void SplitViewController::ActivateSplitMode(aura::Window* left, | |
197 aura::Window* right, | |
198 aura::Window* to_activate) { | |
199 const aura::Window::Windows& windows = window_list_provider_->GetWindowList(); | |
200 aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); | |
201 if (state_ == ACTIVE) { | |
202 if (!left && left_window_ != right) | |
203 left = left_window_; | |
204 if (!right && right_window_ != left) | |
205 right = right_window_; | |
206 } | |
207 | |
208 if (!left && iter != windows.rend()) { | |
209 left = *iter; | |
210 iter++; | |
211 if (left == right && iter != windows.rend()) { | |
212 left = *iter; | |
213 iter++; | |
214 } | |
215 } | |
216 | |
217 if (!right && iter != windows.rend()) { | |
218 right = *iter; | |
219 iter++; | |
220 if (right == left && iter != windows.rend()) { | |
221 right = *iter; | |
222 iter++; | |
223 } | |
224 } | |
225 | |
226 to_hide_.clear(); | |
227 if (left_window_ && left_window_ != left && left_window_ != right) | |
228 to_hide_.push_back(left_window_); | |
229 if (right_window_ && right_window_ != left && right_window_ != right) | |
230 to_hide_.push_back(right_window_); | |
231 | |
232 left_window_ = left; | |
233 right_window_ = right; | |
234 | |
235 divider_position_ = GetDefaultDividerPosition(); | |
236 SetState(ACTIVE); | |
237 UpdateLayout(true); | |
238 | |
239 aura::client::ActivationClient* activation_client = | |
240 aura::client::GetActivationClient(container_->GetRootWindow()); | |
241 aura::Window* active_window = activation_client->GetActiveWindow(); | |
242 if (to_activate) { | |
243 CHECK(to_activate == left_window_ || to_activate == right_window_); | |
244 wm::ActivateWindow(to_activate); | |
245 } else if (active_window != left_window_ && | |
246 active_window != right_window_) { | |
247 // A window which does not belong to an activity could be active. | |
248 wm::ActivateWindow(left_window_); | |
249 } | |
250 active_window = activation_client->GetActiveWindow(); | |
251 | |
252 if (active_window == left_window_) | |
253 window_list_provider_->StackWindowBehindTo(right_window_, left_window_); | |
254 else | |
255 window_list_provider_->StackWindowBehindTo(left_window_, right_window_); | |
256 } | |
257 | |
258 void SplitViewController::ReplaceWindow(aura::Window* window, | |
259 aura::Window* replace_with) { | |
260 CHECK(IsSplitViewModeActive()); | |
261 CHECK(replace_with); | |
262 CHECK(window == left_window_ || window == right_window_); | |
263 CHECK(replace_with != left_window_ && replace_with != right_window_); | |
264 DCHECK(window_list_provider_->IsValidWindow(replace_with)); | |
265 | |
266 aura::Window* not_replaced = nullptr; | |
267 if (window == left_window_) { | |
268 left_window_ = replace_with; | |
269 not_replaced = right_window_; | |
270 } else { | |
271 right_window_ = replace_with; | |
272 not_replaced = left_window_; | |
273 } | |
274 UpdateLayout(false); | |
275 | |
276 wm::ActivateWindow(replace_with); | |
277 window_list_provider_->StackWindowBehindTo(not_replaced, replace_with); | |
278 | |
279 window->SetTransform(gfx::Transform()); | |
280 window->Hide(); | |
281 } | |
282 | |
283 void SplitViewController::DeactivateSplitMode() { | |
284 CHECK_EQ(ACTIVE, state_); | |
285 SetState(INACTIVE); | |
286 UpdateLayout(false); | |
287 left_window_ = right_window_ = nullptr; | |
288 } | |
289 | |
290 void SplitViewController::InitializeDivider() { | |
291 CHECK(!divider_widget_); | |
292 CHECK(!drag_handle_); | |
293 | |
294 drag_handle_ = CreateDragHandleView(DRAG_HANDLE_HORIZONTAL, | |
295 this, | |
296 kDragHandleWidth, | |
297 kDragHandleHeight); | |
298 views::View* content_view = new views::View; | |
299 content_view->set_background( | |
300 views::Background::CreateSolidBackground(SK_ColorBLACK)); | |
301 views::BoxLayout* layout = | |
302 new views::BoxLayout(views::BoxLayout::kHorizontal, | |
303 kDragHandleMargin, | |
304 kDragHandleMargin, | |
305 0); | |
306 layout->set_main_axis_alignment(views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); | |
307 layout->set_cross_axis_alignment( | |
308 views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); | |
309 content_view->SetLayoutManager(layout); | |
310 content_view->AddChildView(drag_handle_); | |
311 | |
312 divider_widget_ = new views::Widget(); | |
313 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | |
314 params.parent = container_; | |
315 params.bounds = gfx::Rect(-kDividerWidth / 2, | |
316 0, | |
317 kDividerWidth, | |
318 container_->bounds().height()); | |
319 divider_widget_->Init(params); | |
320 divider_widget_->SetContentsView(content_view); | |
321 | |
322 // Install a static view targeter on the root view which always targets | |
323 // divider_view. | |
324 // TODO(mfomitchev,tdanderson): This should not be needed: | |
325 // 1. crbug.com/414339 - divider_view is the only view and it completely | |
326 // overlaps the root view. | |
327 // 2. The logic in ViewTargeterDelegate::TargetForRect could be improved to | |
328 // work better for views that are narrow in one dimension and long in | |
329 // another dimension. | |
330 views::internal::RootView* root_view = | |
331 static_cast<views::internal::RootView*>(divider_widget_->GetRootView()); | |
332 view_targeter_delegate_.reset(new StaticViewTargeterDelegate(drag_handle_)); | |
333 views::ViewTargeter* targeter = | |
334 new views::RootViewTargeter(view_targeter_delegate_.get(), root_view); | |
335 divider_widget_->GetRootView()->SetEventTargeter( | |
336 scoped_ptr<views::ViewTargeter>(targeter)); | |
337 } | |
338 | |
339 void SplitViewController::HideDivider() { | |
340 divider_widget_->Hide(); | |
341 window_targeter_.reset(); | |
342 } | |
343 | |
344 void SplitViewController::ShowDivider() { | |
345 divider_widget_->Show(); | |
346 if (!window_targeter_) { | |
347 scoped_ptr<ui::EventTargeter> window_targeter = | |
348 scoped_ptr<ui::EventTargeter>(new PriorityWindowTargeter(drag_handle_)); | |
349 window_targeter_.reset( | |
350 new aura::ScopedWindowTargeter(container_, window_targeter.Pass())); | |
351 } | |
352 } | |
353 | |
354 gfx::Rect SplitViewController::GetLeftAreaBounds() { | |
355 gfx::Rect work_area = | |
356 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); | |
357 return gfx::Rect( | |
358 0, 0, divider_position_ - kDividerWidth / 2, work_area.height()); | |
359 } | |
360 | |
361 gfx::Rect SplitViewController::GetRightAreaBounds() { | |
362 gfx::Rect work_area = | |
363 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); | |
364 int container_width = container_->bounds().width(); | |
365 return gfx::Rect(divider_position_ + kDividerWidth / 2, | |
366 0, | |
367 container_width - divider_position_ - kDividerWidth / 2, | |
368 work_area.height()); | |
369 } | |
370 | |
371 void SplitViewController::OnWindowAddedToList(aura::Window* added_window) { | |
372 } | |
373 | |
374 void SplitViewController::OnWindowRemovedFromList(aura::Window* removed_window, | |
375 int index) { | |
376 if (!IsSplitViewModeActive() || | |
377 (removed_window != left_window_ && removed_window != right_window_)) { | |
378 return; | |
379 } | |
380 | |
381 DCHECK(!window_list_provider_->IsWindowInList(removed_window)); | |
382 | |
383 const aura::Window::Windows windows = window_list_provider_->GetWindowList(); | |
384 CHECK_GE(static_cast<int>(windows.size()), 1); | |
385 DCHECK_GE(index, static_cast<int>(windows.size() - 1)); | |
386 DCHECK_LE(index, static_cast<int>(windows.size())); | |
387 | |
388 if (windows.size() == 1) { | |
389 DeactivateSplitMode(); | |
390 return; | |
391 } | |
392 | |
393 aura::Window* next_window = *(windows.rbegin() + 1); | |
394 if (removed_window == left_window_) { | |
395 CHECK(right_window_ == windows.back()); | |
396 left_window_ = next_window; | |
397 } else { | |
398 CHECK(left_window_ == windows.back()); | |
399 CHECK(removed_window == right_window_); | |
400 right_window_ = next_window; | |
401 } | |
402 UpdateLayout(false); | |
403 } | |
404 | |
405 void SplitViewController::SetState(SplitViewController::State state) { | |
406 if (state_ == state) | |
407 return; | |
408 | |
409 if (divider_widget_ == nullptr) | |
410 InitializeDivider(); | |
411 | |
412 state_ = state; | |
413 | |
414 ScreenManager::Get()->SetRotationLocked(state_ != INACTIVE); | |
415 if (state == INACTIVE) | |
416 HideDivider(); | |
417 else | |
418 ShowDivider(); | |
419 } | |
420 | |
421 void SplitViewController::UpdateLayout(bool animate) { | |
422 CHECK(left_window_); | |
423 CHECK(right_window_); | |
424 // Splitview can be activated from SplitViewController::ActivateSplitMode or | |
425 // SplitViewController::ScrollEnd. Additionally we don't want to rotate the | |
426 // screen while engaging splitview (i.e. state_ == SCROLLING). | |
427 if (state_ == INACTIVE && !animate) { | |
428 gfx::Rect work_area = | |
429 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area(); | |
430 aura::Window* top_window = window_list_provider_->GetWindowList().back(); | |
431 if (top_window != left_window_) { | |
432 // TODO(mfomitchev): Use to_hide_ instead | |
433 left_window_->Hide(); | |
434 right_window_->SetBounds(gfx::Rect(work_area.size())); | |
435 } | |
436 if (top_window != right_window_) { | |
437 left_window_->SetBounds(gfx::Rect(work_area.size())); | |
438 // TODO(mfomitchev): Use to_hide_ instead | |
439 right_window_->Hide(); | |
440 } | |
441 SetWindowTransforms( | |
442 gfx::Transform(), gfx::Transform(), gfx::Transform(), false); | |
443 return; | |
444 } | |
445 | |
446 left_window_->Show(); | |
447 right_window_->Show(); | |
448 | |
449 gfx::Transform divider_transform; | |
450 divider_transform.Translate(divider_position_, 0); | |
451 if (state_ == ACTIVE) { | |
452 if (animate) { | |
453 gfx::Transform left_transform = | |
454 GetTransformForBounds(left_window_->bounds(), GetLeftAreaBounds()); | |
455 gfx::Transform right_transform = | |
456 GetTransformForBounds(right_window_->bounds(), GetRightAreaBounds()); | |
457 SetWindowTransforms( | |
458 left_transform, right_transform, divider_transform, true); | |
459 } else { | |
460 left_window_->SetBounds(GetLeftAreaBounds()); | |
461 right_window_->SetBounds(GetRightAreaBounds()); | |
462 SetWindowTransforms( | |
463 gfx::Transform(), gfx::Transform(), divider_transform, false); | |
464 } | |
465 } else { | |
466 gfx::Transform left_transform; | |
467 gfx::Transform right_transform; | |
468 gfx::Rect left_area_bounds = GetLeftAreaBounds(); | |
469 gfx::Rect right_area_bounds = GetRightAreaBounds(); | |
470 // If the width of the window is greater than the width of the area which it | |
471 // is supposed to occupy - translate the window. Otherwise scale the window | |
472 // up to fill the target area. | |
473 if (left_window_->bounds().width() >= left_area_bounds.width()) { | |
474 left_transform.Translate( | |
475 left_area_bounds.right() - left_window_->bounds().right(), 0); | |
476 } else { | |
477 left_transform = | |
478 GetTransformForBounds(left_window_->bounds(), left_area_bounds); | |
479 } | |
480 if (right_window_->bounds().width() >= right_area_bounds.width()) { | |
481 right_transform.Translate( | |
482 right_area_bounds.x() - right_window_->bounds().x(), 0); | |
483 } else { | |
484 right_transform = | |
485 GetTransformForBounds(right_window_->bounds(), right_area_bounds); | |
486 } | |
487 SetWindowTransforms( | |
488 left_transform, right_transform, divider_transform, animate); | |
489 } | |
490 // Note: |left_window_| and |right_window_| may be nullptr if calling | |
491 // SetWindowTransforms(): | |
492 // - caused the in-progress animation to abort. | |
493 // - started a zero duration animation. | |
494 } | |
495 | |
496 void SplitViewController::SetWindowTransforms( | |
497 const gfx::Transform& left_transform, | |
498 const gfx::Transform& right_transform, | |
499 const gfx::Transform& divider_transform, | |
500 bool animate) { | |
501 if (animate) { | |
502 ui::ScopedLayerAnimationSettings left_settings( | |
503 left_window_->layer()->GetAnimator()); | |
504 left_settings.SetPreemptionStrategy( | |
505 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
506 left_window_->SetTransform(left_transform); | |
507 | |
508 ui::ScopedLayerAnimationSettings divider_widget_settings( | |
509 divider_widget_->GetNativeWindow()->layer()->GetAnimator()); | |
510 divider_widget_settings.SetPreemptionStrategy( | |
511 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
512 divider_widget_->GetNativeWindow()->SetTransform(divider_transform); | |
513 | |
514 ui::ScopedLayerAnimationSettings right_settings( | |
515 right_window_->layer()->GetAnimator()); | |
516 right_settings.SetPreemptionStrategy( | |
517 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | |
518 right_settings.AddObserver(new ui::ClosureAnimationObserver( | |
519 base::Bind(&SplitViewController::OnAnimationCompleted, | |
520 weak_factory_.GetWeakPtr()))); | |
521 right_window_->SetTransform(right_transform); | |
522 } else { | |
523 left_window_->SetTransform(left_transform); | |
524 divider_widget_->GetNativeWindow()->SetTransform(divider_transform); | |
525 right_window_->SetTransform(right_transform); | |
526 } | |
527 } | |
528 | |
529 void SplitViewController::OnAnimationCompleted() { | |
530 // Animation can be cancelled when deactivated. | |
531 if (left_window_ == nullptr) | |
532 return; | |
533 UpdateLayout(false); | |
534 | |
535 for (size_t i = 0; i < to_hide_.size(); ++i) | |
536 to_hide_[i]->Hide(); | |
537 to_hide_.clear(); | |
538 | |
539 if (state_ == INACTIVE) { | |
540 left_window_ = nullptr; | |
541 right_window_ = nullptr; | |
542 } | |
543 } | |
544 | |
545 int SplitViewController::GetDefaultDividerPosition() { | |
546 return container_->GetBoundsInScreen().width() / 2; | |
547 } | |
548 | |
549 float SplitViewController::GetMaxDistanceFromMiddleForTest() const { | |
550 return kMaxDistanceFromMiddle; | |
551 } | |
552 | |
553 float SplitViewController::GetMinFlingVelocityForTest() const { | |
554 return kMinFlingVelocity; | |
555 } | |
556 | |
557 /////////////////////////////////////////////////////////////////////////////// | |
558 // DragHandleScrollDelegate: | |
559 | |
560 void SplitViewController::HandleScrollBegin(float delta) { | |
561 CHECK(state_ == ACTIVE); | |
562 state_ = SCROLLING; | |
563 divider_scroll_start_position_ = GetDefaultDividerPosition(); | |
564 divider_position_ = divider_scroll_start_position_ + delta; | |
565 UpdateLayout(false); | |
566 } | |
567 | |
568 void SplitViewController::HandleScrollEnd(float velocity) { | |
569 if (state_ != SCROLLING) | |
570 return; | |
571 | |
572 int delta = GetDefaultDividerPosition() - divider_position_; | |
573 WindowToActivate window = WINDOW_NONE; | |
574 if (std::abs(velocity) > kMinFlingVelocity) | |
575 window = velocity > 0 ? WINDOW_LEFT : WINDOW_RIGHT; | |
576 else if (std::abs(delta) > kMaxDistanceFromMiddle) | |
577 window = delta > 0 ? WINDOW_RIGHT : WINDOW_LEFT; | |
578 | |
579 switch (window) { | |
580 case WINDOW_NONE: | |
581 divider_position_ = GetDefaultDividerPosition(); | |
582 SetState(ACTIVE); | |
583 break; | |
584 case WINDOW_LEFT: | |
585 divider_position_ = container_->GetBoundsInScreen().width(); | |
586 SetState(INACTIVE); | |
587 wm::ActivateWindow(left_window_); | |
588 break; | |
589 case WINDOW_RIGHT: | |
590 divider_position_ = 0; | |
591 SetState(INACTIVE); | |
592 wm::ActivateWindow(right_window_); | |
593 break; | |
594 } | |
595 | |
596 UpdateLayout(true); | |
597 } | |
598 | |
599 void SplitViewController::HandleScrollUpdate(float delta) { | |
600 if (state_ != SCROLLING) | |
601 return; | |
602 divider_position_ = divider_scroll_start_position_ + delta; | |
603 UpdateLayout(false); | |
604 } | |
605 | |
606 /////////////////////////////////////////////////////////////////////////////// | |
607 // WindowManagerObserver: | |
608 | |
609 void SplitViewController::OnOverviewModeEnter() { | |
610 if (divider_widget_) | |
611 HideDivider(); | |
612 } | |
613 | |
614 void SplitViewController::OnOverviewModeExit() { | |
615 if (state_ != INACTIVE) | |
616 ShowDivider(); | |
617 } | |
618 | |
619 void SplitViewController::OnSplitViewModeEnter() { | |
620 } | |
621 | |
622 void SplitViewController::OnSplitViewModeExit() { | |
623 } | |
624 | |
625 } // namespace athena | |
OLD | NEW |