Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 | |
|
sky
2013/08/22 20:18:22
nit: remove newline.
flackr
2013/08/22 21:09:29
Done.
| |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 2 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 3 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 4 // found in the LICENSE file. |
| 4 | 5 |
| 5 #include "ash/wm/window_selector.h" | 6 #include "ash/wm/overview/window_selector_window.h" |
| 6 | 7 |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "ash/screen_ash.h" | |
| 10 #include "ash/shell.h" | 8 #include "ash/shell.h" |
| 11 #include "ash/shell_window_ids.h" | |
| 12 #include "ash/wm/window_selector_delegate.h" | |
| 13 #include "ash/wm/window_util.h" | |
| 14 #include "base/memory/scoped_ptr.h" | |
| 15 #include "third_party/skia/include/core/SkColor.h" | |
| 16 #include "ui/aura/client/aura_constants.h" | 9 #include "ui/aura/client/aura_constants.h" |
| 17 #include "ui/aura/client/screen_position_client.h" | 10 #include "ui/aura/client/screen_position_client.h" |
| 18 #include "ui/aura/root_window.h" | 11 #include "ui/aura/root_window.h" |
| 19 #include "ui/aura/window.h" | 12 #include "ui/aura/window.h" |
| 20 #include "ui/base/events/event.h" | |
| 21 #include "ui/compositor/layer_animation_observer.h" | 13 #include "ui/compositor/layer_animation_observer.h" |
| 22 #include "ui/compositor/scoped_layer_animation_settings.h" | 14 #include "ui/compositor/scoped_layer_animation_settings.h" |
| 23 #include "ui/gfx/display.h" | 15 #include "ui/gfx/display.h" |
| 24 #include "ui/gfx/interpolated_transform.h" | 16 #include "ui/gfx/interpolated_transform.h" |
| 25 #include "ui/gfx/transform_util.h" | 17 #include "ui/gfx/transform_util.h" |
| 26 #include "ui/views/corewm/shadow_types.h" | 18 #include "ui/views/corewm/shadow_types.h" |
| 27 #include "ui/views/corewm/window_animations.h" | 19 #include "ui/views/corewm/window_animations.h" |
| 28 #include "ui/views/corewm/window_util.h" | 20 #include "ui/views/corewm/window_util.h" |
| 29 #include "ui/views/widget/widget.h" | 21 #include "ui/views/widget/widget.h" |
| 30 | 22 |
| 31 namespace ash { | 23 namespace ash { |
| 32 | 24 |
| 33 namespace { | 25 namespace { |
| 34 | 26 |
| 35 const float kCardAspectRatio = 4.0f / 3.0f; | 27 const int kOverviewWindowTransitionMilliseconds = 100; |
| 36 const int kWindowMargin = 30; | |
| 37 const int kMinCardsMajor = 3; | |
| 38 const int kOverviewTransitionMilliseconds = 100; | |
| 39 const SkColor kWindowSelectorSelectionColor = SK_ColorBLACK; | |
| 40 const float kWindowSelectorSelectionOpacity = 0.5f; | |
| 41 const int kWindowSelectorSelectionPadding = 15; | |
| 42 | 28 |
| 43 // Creates a copy of |window| with |recreated_layer| in the |target_root|. | 29 // Creates a copy of |window| with |recreated_layer| in the |target_root|. |
| 44 views::Widget* CreateCopyOfWindow(aura::RootWindow* target_root, | 30 views::Widget* CreateCopyOfWindow(aura::RootWindow* target_root, |
| 45 aura::Window* src_window, | 31 aura::Window* src_window, |
| 46 ui::Layer* recreated_layer) { | 32 ui::Layer* recreated_layer) { |
| 47 views::Widget* widget = new views::Widget; | 33 views::Widget* widget = new views::Widget; |
| 48 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); | 34 views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
| 49 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | 35 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
| 50 params.parent = src_window->parent(); | 36 params.parent = src_window->parent(); |
| 51 params.can_activate = false; | 37 params.can_activate = false; |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 144 } | 130 } |
| 145 | 131 |
| 146 // The animation settings used for window selector animations. | 132 // The animation settings used for window selector animations. |
| 147 class WindowSelectorAnimationSettings | 133 class WindowSelectorAnimationSettings |
| 148 : public ui::ScopedLayerAnimationSettings { | 134 : public ui::ScopedLayerAnimationSettings { |
| 149 public: | 135 public: |
| 150 WindowSelectorAnimationSettings(aura::Window* window) : | 136 WindowSelectorAnimationSettings(aura::Window* window) : |
| 151 ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) { | 137 ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) { |
| 152 SetPreemptionStrategy( | 138 SetPreemptionStrategy( |
| 153 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); | 139 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| 154 SetTransitionDuration( | 140 SetTransitionDuration(base::TimeDelta::FromMilliseconds( |
| 155 base::TimeDelta::FromMilliseconds(kOverviewTransitionMilliseconds)); | 141 kOverviewWindowTransitionMilliseconds)); |
| 156 } | 142 } |
| 157 | 143 |
| 158 virtual ~WindowSelectorAnimationSettings() { | 144 virtual ~WindowSelectorAnimationSettings() { |
| 159 } | 145 } |
| 160 }; | 146 }; |
| 161 | 147 |
| 162 } // namespace | 148 } // namespace |
| 163 | 149 |
| 164 // TODO(flackr): Split up into separate file under subdirectory in ash/wm. | |
| 165 class WindowSelectorWindow { | |
| 166 public: | |
| 167 explicit WindowSelectorWindow(aura::Window* window); | |
| 168 virtual ~WindowSelectorWindow(); | |
| 169 | |
| 170 aura::Window* window() { return window_; } | |
| 171 const aura::Window* window() const { return window_; } | |
| 172 | |
| 173 // Returns true if this window selector window contains the |target|. This is | |
| 174 // used to determine if an event targetted this window. | |
| 175 bool Contains(const aura::Window* target) const; | |
| 176 | |
| 177 // Restores this window on exit rather than returning it to a minimized state | |
| 178 // if it was minimized on entering overview mode. | |
| 179 void RestoreWindowOnExit(); | |
| 180 | |
| 181 // Informs the WindowSelectorWindow that the window being watched was | |
| 182 // destroyed. This resets the internal window pointer to avoid calling | |
| 183 // anything on the window at destruction time. | |
| 184 void OnWindowDestroyed(); | |
| 185 | |
| 186 // Applies a transform to the window to fit within |target_bounds| while | |
| 187 // maintaining its aspect ratio. | |
| 188 void TransformToFitBounds(aura::RootWindow* root_window, | |
| 189 const gfx::Rect& target_bounds); | |
| 190 | |
| 191 gfx::Rect bounds() { return fit_bounds_; } | |
| 192 | |
| 193 private: | |
| 194 // A weak pointer to the real window in the overview. | |
| 195 aura::Window* window_; | |
| 196 | |
| 197 // A copy of the window used to transition the window to another root. | |
| 198 views::Widget* window_copy_; | |
| 199 | |
| 200 // A weak pointer to a deep copy of the window's layers. | |
| 201 ui::Layer* layer_; | |
| 202 | |
| 203 // If true, the window was minimized and should be restored if the window | |
| 204 // was not selected. | |
| 205 bool minimized_; | |
| 206 | |
| 207 // The original transform of the window before entering overview mode. | |
| 208 gfx::Transform original_transform_; | |
| 209 | |
| 210 // The bounds this window is fit to. | |
| 211 gfx::Rect fit_bounds_; | |
| 212 | |
| 213 DISALLOW_COPY_AND_ASSIGN(WindowSelectorWindow); | |
| 214 }; | |
| 215 | |
| 216 WindowSelectorWindow::WindowSelectorWindow(aura::Window* window) | 150 WindowSelectorWindow::WindowSelectorWindow(aura::Window* window) |
| 217 : window_(window), | 151 : window_(window), |
| 218 window_copy_(NULL), | 152 window_copy_(NULL), |
| 219 layer_(NULL), | 153 layer_(NULL), |
| 220 minimized_(window->GetProperty(aura::client::kShowStateKey) == | 154 minimized_(window->GetProperty(aura::client::kShowStateKey) == |
| 221 ui::SHOW_STATE_MINIMIZED), | 155 ui::SHOW_STATE_MINIMIZED), |
| 222 original_transform_(window->layer()->GetTargetTransform()) { | 156 original_transform_(window->layer()->GetTargetTransform()) { |
| 223 if (minimized_) | 157 if (minimized_) |
| 224 window_->Show(); | 158 window_->Show(); |
| 225 } | 159 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 296 window_copy_ = CreateCopyOfWindow(root_window, window_, layer_); | 230 window_copy_ = CreateCopyOfWindow(root_window, window_, layer_); |
| 297 } | 231 } |
| 298 WindowSelectorAnimationSettings animation_settings( | 232 WindowSelectorAnimationSettings animation_settings( |
| 299 window_copy_->GetNativeWindow()); | 233 window_copy_->GetNativeWindow()); |
| 300 window_copy_->GetNativeWindow()->SetTransform(transform); | 234 window_copy_->GetNativeWindow()->SetTransform(transform); |
| 301 } | 235 } |
| 302 WindowSelectorAnimationSettings animation_settings(window_); | 236 WindowSelectorAnimationSettings animation_settings(window_); |
| 303 window_->SetTransform(transform); | 237 window_->SetTransform(transform); |
| 304 } | 238 } |
| 305 | 239 |
| 306 // A comparator for locating a given target window. | |
| 307 struct WindowSelectorWindowComparator | |
| 308 : public std::unary_function<WindowSelectorWindow*, bool> { | |
| 309 explicit WindowSelectorWindowComparator(const aura::Window* target_window) | |
| 310 : target(target_window) { | |
| 311 } | |
| 312 | |
| 313 bool operator()(const WindowSelectorWindow* window) const { | |
| 314 return target == window->window(); | |
| 315 } | |
| 316 | |
| 317 const aura::Window* target; | |
| 318 }; | |
| 319 | |
| 320 WindowSelector::WindowSelector(const WindowList& windows, | |
| 321 WindowSelector::Mode mode, | |
| 322 WindowSelectorDelegate* delegate) | |
| 323 : mode_(mode), | |
| 324 delegate_(delegate), | |
| 325 selected_window_(0), | |
| 326 selection_root_(NULL) { | |
| 327 DCHECK(delegate_); | |
| 328 for (size_t i = 0; i < windows.size(); ++i) { | |
| 329 windows[i]->AddObserver(this); | |
| 330 windows_.push_back(new WindowSelectorWindow(windows[i])); | |
| 331 } | |
| 332 if (mode == WindowSelector::CYCLE) | |
| 333 selection_root_ = ash::Shell::GetActiveRootWindow(); | |
| 334 PositionWindows(); | |
| 335 ash::Shell::GetInstance()->AddPreTargetHandler(this); | |
| 336 } | |
| 337 | |
| 338 WindowSelector::~WindowSelector() { | |
| 339 for (size_t i = 0; i < windows_.size(); i++) { | |
| 340 windows_[i]->window()->RemoveObserver(this); | |
| 341 } | |
| 342 ash::Shell::GetInstance()->RemovePreTargetHandler(this); | |
| 343 } | |
| 344 | |
| 345 void WindowSelector::Step(WindowSelector::Direction direction) { | |
| 346 DCHECK(windows_.size() > 0); | |
| 347 if (!selection_widget_) | |
| 348 InitializeSelectionWidget(); | |
| 349 selected_window_ = (selected_window_ + windows_.size() + | |
| 350 (direction == WindowSelector::FORWARD ? 1 : -1)) % windows_.size(); | |
| 351 UpdateSelectionLocation(true); | |
| 352 } | |
| 353 | |
| 354 void WindowSelector::SelectWindow() { | |
| 355 delegate_->OnWindowSelected(windows_[selected_window_]->window()); | |
| 356 } | |
| 357 | |
| 358 void WindowSelector::OnEvent(ui::Event* event) { | |
| 359 // If the event is targetted at any of the windows in the overview, then | |
| 360 // prevent it from propagating. | |
| 361 aura::Window* target = static_cast<aura::Window*>(event->target()); | |
| 362 for (size_t i = 0; i < windows_.size(); ++i) { | |
| 363 if (windows_[i]->Contains(target)) { | |
| 364 // TODO(flackr): StopPropogation prevents generation of gesture events. | |
| 365 // We should find a better way to prevent events from being delivered to | |
| 366 // the window, perhaps a transparent window in front of the target window | |
| 367 // or using EventClientImpl::CanProcessEventsWithinSubtree. | |
| 368 event->StopPropagation(); | |
| 369 break; | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 // This object may not be valid after this call as a selection event can | |
| 374 // trigger deletion of the window selector. | |
| 375 ui::EventHandler::OnEvent(event); | |
| 376 } | |
| 377 | |
| 378 void WindowSelector::OnMouseEvent(ui::MouseEvent* event) { | |
| 379 if (event->type() != ui::ET_MOUSE_RELEASED) | |
| 380 return; | |
| 381 WindowSelectorWindow* target = GetEventTarget(event); | |
| 382 if (!target) | |
| 383 return; | |
| 384 | |
| 385 HandleSelectionEvent(target); | |
| 386 } | |
| 387 | |
| 388 void WindowSelector::OnTouchEvent(ui::TouchEvent* event) { | |
| 389 if (event->type() != ui::ET_TOUCH_PRESSED) | |
| 390 return; | |
| 391 WindowSelectorWindow* target = GetEventTarget(event); | |
| 392 if (!target) | |
| 393 return; | |
| 394 | |
| 395 HandleSelectionEvent(target); | |
| 396 } | |
| 397 | |
| 398 void WindowSelector::OnWindowDestroyed(aura::Window* window) { | |
| 399 ScopedVector<WindowSelectorWindow>::iterator iter = | |
| 400 std::find_if(windows_.begin(), windows_.end(), | |
| 401 WindowSelectorWindowComparator(window)); | |
| 402 DCHECK(iter != windows_.end()); | |
| 403 size_t deleted_index = iter - windows_.begin(); | |
| 404 (*iter)->OnWindowDestroyed(); | |
| 405 windows_.erase(iter); | |
| 406 if (windows_.empty()) { | |
| 407 delegate_->OnSelectionCanceled(); | |
| 408 return; | |
| 409 } | |
| 410 if (selected_window_ >= deleted_index) { | |
| 411 if (selected_window_ > deleted_index) | |
| 412 selected_window_--; | |
| 413 selected_window_ = selected_window_ % windows_.size(); | |
| 414 UpdateSelectionLocation(true); | |
| 415 } | |
| 416 | |
| 417 PositionWindows(); | |
| 418 } | |
| 419 | |
| 420 WindowSelectorWindow* WindowSelector::GetEventTarget(ui::LocatedEvent* event) { | |
| 421 aura::Window* target = static_cast<aura::Window*>(event->target()); | |
| 422 // If the target window doesn't actually contain the event location (i.e. | |
| 423 // mouse down over the window and mouse up elsewhere) then do not select the | |
| 424 // window. | |
| 425 if (!target->HitTest(event->location())) | |
| 426 return NULL; | |
| 427 | |
| 428 for (size_t i = 0; i < windows_.size(); i++) { | |
| 429 if (windows_[i]->Contains(target)) | |
| 430 return windows_[i]; | |
| 431 } | |
| 432 return NULL; | |
| 433 } | |
| 434 | |
| 435 void WindowSelector::HandleSelectionEvent(WindowSelectorWindow* target) { | |
| 436 // The selected window should not be minimized when window selection is | |
| 437 // ended. | |
| 438 target->RestoreWindowOnExit(); | |
| 439 delegate_->OnWindowSelected(target->window()); | |
| 440 } | |
| 441 | |
| 442 void WindowSelector::PositionWindows() { | |
| 443 if (selection_root_) { | |
| 444 DCHECK_EQ(mode_, CYCLE); | |
| 445 std::vector<WindowSelectorWindow*> windows; | |
| 446 for (size_t i = 0; i < windows_.size(); ++i) | |
| 447 windows.push_back(windows_[i]); | |
| 448 PositionWindowsOnRoot(selection_root_, windows); | |
| 449 } else { | |
| 450 DCHECK_EQ(mode_, OVERVIEW); | |
| 451 Shell::RootWindowList root_window_list = Shell::GetAllRootWindows(); | |
| 452 for (size_t i = 0; i < root_window_list.size(); ++i) | |
| 453 PositionWindowsFromRoot(root_window_list[i]); | |
| 454 } | |
| 455 } | |
| 456 | |
| 457 void WindowSelector::PositionWindowsFromRoot(aura::RootWindow* root_window) { | |
| 458 std::vector<WindowSelectorWindow*> windows; | |
| 459 for (size_t i = 0; i < windows_.size(); ++i) { | |
| 460 if (windows_[i]->window()->GetRootWindow() == root_window) | |
| 461 windows.push_back(windows_[i]); | |
| 462 } | |
| 463 PositionWindowsOnRoot(root_window, windows); | |
| 464 } | |
| 465 | |
| 466 void WindowSelector::PositionWindowsOnRoot( | |
| 467 aura::RootWindow* root_window, | |
| 468 const std::vector<WindowSelectorWindow*>& windows) { | |
| 469 if (windows.empty()) | |
| 470 return; | |
| 471 | |
| 472 gfx::Size window_size; | |
| 473 gfx::Rect total_bounds = ScreenAsh::ConvertRectToScreen(root_window, | |
| 474 ScreenAsh::GetDisplayWorkAreaBoundsInParent( | |
| 475 Shell::GetContainer(root_window, | |
| 476 internal::kShellWindowId_DefaultContainer))); | |
| 477 | |
| 478 // Find the minimum number of windows per row that will fit all of the | |
| 479 // windows on screen. | |
| 480 size_t columns = std::max( | |
| 481 total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1, | |
| 482 static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() / | |
| 483 (kCardAspectRatio * total_bounds.height()))))); | |
| 484 size_t rows = ((windows.size() + columns - 1) / columns); | |
| 485 window_size.set_width(std::min( | |
| 486 static_cast<int>(total_bounds.width() / columns), | |
| 487 static_cast<int>(total_bounds.height() * kCardAspectRatio / rows))); | |
| 488 window_size.set_height(window_size.width() / kCardAspectRatio); | |
| 489 | |
| 490 // Calculate the X and Y offsets necessary to center the grid. | |
| 491 int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 : | |
| 492 (columns - windows.size()) * window_size.width()) + | |
| 493 (total_bounds.width() - columns * window_size.width())) / 2; | |
| 494 int y_offset = total_bounds.y() + (total_bounds.height() - | |
| 495 rows * window_size.height()) / 2; | |
| 496 for (size_t i = 0; i < windows.size(); ++i) { | |
| 497 gfx::Transform transform; | |
| 498 int column = i % columns; | |
| 499 int row = i / columns; | |
| 500 gfx::Rect target_bounds(window_size.width() * column + x_offset, | |
| 501 window_size.height() * row + y_offset, | |
| 502 window_size.width(), | |
| 503 window_size.height()); | |
| 504 target_bounds.Inset(kWindowMargin, kWindowMargin); | |
| 505 windows[i]->TransformToFitBounds(root_window, target_bounds); | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 void WindowSelector::InitializeSelectionWidget() { | |
| 510 selection_widget_.reset(new views::Widget); | |
| 511 views::Widget::InitParams params; | |
| 512 params.type = views::Widget::InitParams::TYPE_POPUP; | |
| 513 params.can_activate = false; | |
| 514 params.keep_on_top = false; | |
| 515 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 516 params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; | |
| 517 params.parent = Shell::GetContainer( | |
| 518 selection_root_, | |
| 519 internal::kShellWindowId_DefaultContainer); | |
| 520 params.accept_events = false; | |
| 521 selection_widget_->set_focus_on_creation(false); | |
| 522 selection_widget_->Init(params); | |
| 523 views::View* content_view = new views::View; | |
| 524 content_view->set_background( | |
| 525 views::Background::CreateSolidBackground(kWindowSelectorSelectionColor)); | |
| 526 selection_widget_->SetContentsView(content_view); | |
| 527 UpdateSelectionLocation(false); | |
| 528 selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom( | |
| 529 selection_widget_->GetNativeWindow()); | |
| 530 selection_widget_->Show(); | |
| 531 selection_widget_->GetNativeWindow()->layer()->SetOpacity( | |
| 532 kWindowSelectorSelectionOpacity); | |
| 533 } | |
| 534 | |
| 535 void WindowSelector::UpdateSelectionLocation(bool animate) { | |
| 536 if (!selection_widget_) | |
| 537 return; | |
| 538 gfx::Rect target_bounds = windows_[selected_window_]->bounds(); | |
| 539 target_bounds.Inset(-kWindowSelectorSelectionPadding, | |
| 540 -kWindowSelectorSelectionPadding); | |
| 541 if (animate) { | |
| 542 WindowSelectorAnimationSettings animation_settings( | |
| 543 selection_widget_->GetNativeWindow()); | |
| 544 selection_widget_->SetBounds(target_bounds); | |
| 545 } else { | |
| 546 selection_widget_->SetBounds(target_bounds); | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 } // namespace ash | 240 } // namespace ash |
| OLD | NEW |