Chromium Code Reviews| 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 "ash/common/wm/window_cycle_list.h" | 5 #include "ash/common/wm/window_cycle_list.h" |
| 6 | 6 |
| 7 #include <list> | 7 #include <list> |
| 8 #include <map> | 8 #include <map> |
| 9 | 9 |
| 10 #include "ash/common/ash_switches.h" | 10 #include "ash/common/ash_switches.h" |
| 11 #include "ash/common/shell_window_ids.h" | 11 #include "ash/common/shell_window_ids.h" |
| 12 #include "ash/common/wm/forwarding_layer_delegate.h" | 12 #include "ash/common/wm/forwarding_layer_delegate.h" |
| 13 #include "ash/common/wm/mru_window_tracker.h" | 13 #include "ash/common/wm/mru_window_tracker.h" |
| 14 #include "ash/common/wm/window_state.h" | 14 #include "ash/common/wm/window_state.h" |
| 15 #include "ash/common/wm_root_window_controller.h" | 15 #include "ash/common/wm_root_window_controller.h" |
| 16 #include "ash/common/wm_shell.h" | 16 #include "ash/common/wm_shell.h" |
| 17 #include "ash/common/wm_window.h" | 17 #include "ash/common/wm_window.h" |
| 18 #include "base/command_line.h" | 18 #include "base/command_line.h" |
| 19 #include "ui/accessibility/ax_view_state.h" | 19 #include "ui/accessibility/ax_view_state.h" |
| 20 #include "ui/compositor/scoped_layer_animation_settings.h" | |
| 20 #include "ui/views/background.h" | 21 #include "ui/views/background.h" |
| 21 #include "ui/views/border.h" | 22 #include "ui/views/border.h" |
| 22 #include "ui/views/controls/label.h" | 23 #include "ui/views/controls/label.h" |
| 23 #include "ui/views/layout/box_layout.h" | 24 #include "ui/views/layout/box_layout.h" |
| 24 #include "ui/views/painter.h" | 25 #include "ui/views/painter.h" |
| 25 #include "ui/views/view.h" | 26 #include "ui/views/view.h" |
| 26 #include "ui/views/widget/widget.h" | 27 #include "ui/views/widget/widget.h" |
| 27 #include "ui/views/widget/widget_delegate.h" | 28 #include "ui/views/widget/widget_delegate.h" |
| 28 #include "ui/wm/core/visibility_controller.h" | 29 #include "ui/wm/core/visibility_controller.h" |
| 29 | 30 |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 223 // A view that shows a collection of windows the user can tab through. | 224 // A view that shows a collection of windows the user can tab through. |
| 224 class WindowCycleView : public views::WidgetDelegateView { | 225 class WindowCycleView : public views::WidgetDelegateView { |
| 225 public: | 226 public: |
| 226 explicit WindowCycleView(const WindowCycleList::WindowList& windows) | 227 explicit WindowCycleView(const WindowCycleList::WindowList& windows) |
| 227 : mirror_container_(new views::View()), | 228 : mirror_container_(new views::View()), |
| 228 highlight_view_(new views::View()), | 229 highlight_view_(new views::View()), |
| 229 target_window_(nullptr) { | 230 target_window_(nullptr) { |
| 230 DCHECK(!windows.empty()); | 231 DCHECK(!windows.empty()); |
| 231 SetPaintToLayer(true); | 232 SetPaintToLayer(true); |
| 232 layer()->SetFillsBoundsOpaquely(false); | 233 layer()->SetFillsBoundsOpaquely(false); |
| 234 layer()->SetOpacity(0.0); | |
| 235 { | |
| 236 ui::ScopedLayerAnimationSettings animate_fade(layer()->GetAnimator()); | |
| 237 animate_fade.SetTransitionDuration( | |
| 238 base::TimeDelta::FromMilliseconds(100)); | |
|
sky
2016/08/11 19:36:27
Please document why this uses a shorter time than
Evan Stade
2016/08/11 20:00:11
I don't know if there's really a reason beyond "th
sky
2016/08/11 20:04:44
Fair enough.
| |
| 239 layer()->SetOpacity(1.0); | |
| 240 } | |
| 233 | 241 |
| 234 set_background(views::Background::CreateSolidBackground( | 242 set_background(views::Background::CreateSolidBackground( |
| 235 SkColorSetA(SK_ColorBLACK, 0xCC))); | 243 SkColorSetA(SK_ColorBLACK, 0xCC))); |
| 236 | 244 |
| 237 const int kInsideBorderPaddingDip = 64; | 245 const int kInsideBorderPaddingDip = 64; |
| 238 const int kBetweenChildPaddingDip = 10; | 246 const int kBetweenChildPaddingDip = 10; |
| 239 views::BoxLayout* layout = new views::BoxLayout( | 247 views::BoxLayout* layout = new views::BoxLayout( |
| 240 views::BoxLayout::kHorizontal, kInsideBorderPaddingDip, | 248 views::BoxLayout::kHorizontal, kInsideBorderPaddingDip, |
| 241 kInsideBorderPaddingDip, kBetweenChildPaddingDip); | 249 kInsideBorderPaddingDip, kBetweenChildPaddingDip); |
| 242 layout->set_cross_axis_alignment( | 250 layout->set_cross_axis_alignment( |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 266 kHighlightCornerRadius)))); | 274 kHighlightCornerRadius)))); |
| 267 highlight_view_->SetPaintToLayer(true); | 275 highlight_view_->SetPaintToLayer(true); |
| 268 highlight_view_->layer()->SetFillsBoundsOpaquely(false); | 276 highlight_view_->layer()->SetFillsBoundsOpaquely(false); |
| 269 // The selection highlight also animates all bounds changes and never | 277 // The selection highlight also animates all bounds changes and never |
| 270 // changes other animatable properties. | 278 // changes other animatable properties. |
| 271 highlight_view_->layer()->SetAnimator( | 279 highlight_view_->layer()->SetAnimator( |
| 272 ui::LayerAnimator::CreateImplicitAnimator()); | 280 ui::LayerAnimator::CreateImplicitAnimator()); |
| 273 | 281 |
| 274 AddChildView(highlight_view_); | 282 AddChildView(highlight_view_); |
| 275 AddChildView(mirror_container_); | 283 AddChildView(mirror_container_); |
| 276 SetTargetWindow(windows.front()); | |
| 277 } | 284 } |
| 278 | 285 |
| 279 ~WindowCycleView() override {} | 286 ~WindowCycleView() override {} |
| 280 | 287 |
| 281 void SetTargetWindow(WmWindow* target) { | 288 void SetTargetWindow(WmWindow* target) { |
| 282 target_window_ = target; | 289 target_window_ = target; |
| 283 if (GetWidget()) { | 290 if (GetWidget()) { |
| 284 Layout(); | 291 Layout(); |
| 285 DCHECK(Contains(GetFocusManager()->GetFocusedView())); | 292 DCHECK(Contains(GetFocusManager()->GetFocusedView())); |
| 286 window_view_map_[target_window_]->RequestFocus(); | 293 window_view_map_[target_window_]->RequestFocus(); |
| 287 } | 294 } |
| 288 } | 295 } |
| 289 | 296 |
| 290 void HandleWindowDestruction(WmWindow* destroying_window, | 297 void HandleWindowDestruction(WmWindow* destroying_window, |
| 291 WmWindow* new_target) { | 298 WmWindow* new_target) { |
| 292 auto view_iter = window_view_map_.find(destroying_window); | 299 auto view_iter = window_view_map_.find(destroying_window); |
| 293 view_iter->second->parent()->RemoveChildView(view_iter->second); | 300 view_iter->second->parent()->RemoveChildView(view_iter->second); |
| 294 window_view_map_.erase(view_iter); | 301 window_view_map_.erase(view_iter); |
| 295 SetTargetWindow(new_target); | 302 SetTargetWindow(new_target); |
| 296 } | 303 } |
| 297 | 304 |
| 298 // views::WidgetDelegateView overrides: | 305 // views::WidgetDelegateView overrides: |
| 299 gfx::Size GetPreferredSize() const override { | 306 gfx::Size GetPreferredSize() const override { |
| 300 return mirror_container_->GetPreferredSize(); | 307 return mirror_container_->GetPreferredSize(); |
| 301 } | 308 } |
| 302 | 309 |
| 303 void Layout() override { | 310 void Layout() override { |
| 304 // Possible if the last window is deleted. | 311 if (!target_window_ || bounds().IsEmpty()) |
| 305 if (!target_window_) | |
| 306 return; | 312 return; |
| 307 | 313 |
| 308 // The preview list (|mirror_container_|) starts flush to the left of | 314 // The preview list (|mirror_container_|) starts flush to the left of |
| 309 // the screen but moves to the left (off the edge of the screen) as the use | 315 // the screen but moves to the left (off the edge of the screen) as the use |
| 310 // iterates over the previews. The list will move just enough to ensure the | 316 // iterates over the previews. The list will move just enough to ensure the |
| 311 // highlighted preview is at or to the left of the center of the workspace. | 317 // highlighted preview is at or to the left of the center of the workspace. |
| 312 views::View* target_view = window_view_map_[target_window_]; | 318 views::View* target_view = window_view_map_[target_window_]; |
| 313 gfx::RectF target_bounds(target_view->GetLocalBounds()); | 319 gfx::RectF target_bounds(target_view->GetLocalBounds()); |
| 314 views::View::ConvertRectToTarget(target_view, mirror_container_, | 320 views::View::ConvertRectToTarget(target_view, mirror_container_, |
| 315 &target_bounds); | 321 &target_bounds); |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 395 } | 401 } |
| 396 | 402 |
| 397 WindowCycleList::WindowCycleList(const WindowList& windows) | 403 WindowCycleList::WindowCycleList(const WindowList& windows) |
| 398 : windows_(windows), current_index_(0), cycle_view_(nullptr) { | 404 : windows_(windows), current_index_(0), cycle_view_(nullptr) { |
| 399 WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(true); | 405 WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(true); |
| 400 | 406 |
| 401 for (WmWindow* window : windows_) | 407 for (WmWindow* window : windows_) |
| 402 window->AddObserver(this); | 408 window->AddObserver(this); |
| 403 | 409 |
| 404 if (ShouldShowUi()) { | 410 if (ShouldShowUi()) { |
| 405 cycle_view_ = new WindowCycleView(windows_); | 411 show_ui_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(150), |
|
sky
2016/08/11 19:36:27
Document why the delay.
Evan Stade
2016/08/11 20:00:11
It is documented in the header file above the memb
sky
2016/08/11 20:04:44
What you have is fine. I clearly didn't look enoug
| |
| 406 | 412 this, &WindowCycleList::InitWindowCycleView); |
| 407 WmWindow* root_window = WmShell::Get()->GetRootWindowForNewWindows(); | |
| 408 views::Widget* widget = new views::Widget; | |
| 409 views::Widget::InitParams params; | |
| 410 params.delegate = cycle_view_; | |
| 411 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
| 412 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 413 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | |
| 414 params.accept_events = true; | |
| 415 // TODO(estade): make sure nothing untoward happens when the lock screen | |
| 416 // or a system modal dialog is shown. | |
| 417 root_window->GetRootWindowController() | |
| 418 ->ConfigureWidgetInitParamsForContainer( | |
| 419 widget, kShellWindowId_OverlayContainer, ¶ms); | |
| 420 widget->Init(params); | |
| 421 | |
| 422 // TODO(estade): right now this just extends past the edge of the screen if | |
| 423 // there are too many windows. Handle this more gracefully. Also, if | |
| 424 // the display metrics change, cancel the UI. | |
| 425 gfx::Rect widget_rect = widget->GetWorkAreaBoundsInScreen(); | |
| 426 int widget_height = cycle_view_->GetPreferredSize().height(); | |
| 427 widget_rect.set_y((widget_rect.height() - widget_height) / 2); | |
| 428 widget_rect.set_height(widget_height); | |
| 429 widget->SetBounds(widget_rect); | |
| 430 widget->Show(); | |
| 431 cycle_ui_widget_.reset(widget); | |
| 432 } | 413 } |
| 433 } | 414 } |
| 434 | 415 |
| 435 WindowCycleList::~WindowCycleList() { | 416 WindowCycleList::~WindowCycleList() { |
| 436 WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(false); | 417 WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(false); |
| 437 for (WmWindow* window : windows_) | 418 for (WmWindow* window : windows_) |
| 438 window->RemoveObserver(this); | 419 window->RemoveObserver(this); |
| 439 | 420 |
| 440 if (showing_window_) | 421 if (showing_window_) { |
| 441 showing_window_->CancelRestore(); | 422 showing_window_->CancelRestore(); |
| 442 | 423 } else if (!windows_.empty()) { |
| 443 if (cycle_view_ && cycle_view_->target_window()) { | 424 WmWindow* target_window = windows_[current_index_]; |
| 444 cycle_view_->target_window()->Show(); | 425 target_window->Show(); |
| 445 cycle_view_->target_window()->GetWindowState()->Activate(); | 426 target_window->GetWindowState()->Activate(); |
| 446 } | 427 } |
| 447 } | 428 } |
| 448 | 429 |
| 449 void WindowCycleList::Step(WindowCycleController::Direction direction) { | 430 void WindowCycleList::Step(WindowCycleController::Direction direction) { |
| 450 if (windows_.empty()) | 431 if (windows_.empty()) |
| 451 return; | 432 return; |
| 452 | 433 |
| 453 // When there is only one window, we should give feedback to the user. If the | 434 // When there is only one window, we should give feedback to the user. If the |
| 454 // window is minimized, we should also show it. | 435 // window is minimized, we should also show it. |
| 455 if (windows_.size() == 1) { | 436 if (windows_.size() == 1) { |
| 456 windows_[0]->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE); | 437 windows_[0]->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE); |
| 457 windows_[0]->Show(); | 438 windows_[0]->Show(); |
| 458 windows_[0]->GetWindowState()->Activate(); | 439 windows_[0]->GetWindowState()->Activate(); |
| 459 return; | 440 return; |
| 460 } | 441 } |
| 461 | 442 |
| 462 DCHECK(static_cast<size_t>(current_index_) < windows_.size()); | 443 DCHECK(static_cast<size_t>(current_index_) < windows_.size()); |
| 463 | 444 |
| 464 // We're in a valid cycle, so step forward or backward. | 445 // We're in a valid cycle, so step forward or backward. |
| 465 current_index_ += direction == WindowCycleController::FORWARD ? 1 : -1; | 446 current_index_ += direction == WindowCycleController::FORWARD ? 1 : -1; |
| 466 | 447 |
| 467 // Wrap to window list size. | 448 // Wrap to window list size. |
| 468 current_index_ = (current_index_ + windows_.size()) % windows_.size(); | 449 current_index_ = (current_index_ + windows_.size()) % windows_.size(); |
| 469 DCHECK(windows_[current_index_]); | 450 DCHECK(windows_[current_index_]); |
| 470 | 451 |
| 471 if (cycle_view_) { | 452 if (ShouldShowUi()) { |
| 472 cycle_view_->SetTargetWindow(windows_[current_index_]); | 453 if (current_index_ > 1) |
| 473 return; | 454 InitWindowCycleView(); |
| 455 | |
| 456 if (cycle_view_) | |
| 457 cycle_view_->SetTargetWindow(windows_[current_index_]); | |
| 458 } else { | |
| 459 // Make sure the next window is visible. | |
| 460 showing_window_.reset(new ScopedShowWindow); | |
| 461 showing_window_->Show(windows_[current_index_]); | |
| 474 } | 462 } |
| 475 | |
| 476 // Make sure the next window is visible. | |
| 477 showing_window_.reset(new ScopedShowWindow); | |
| 478 showing_window_->Show(windows_[current_index_]); | |
| 479 } | 463 } |
| 480 | 464 |
| 481 void WindowCycleList::OnWindowDestroying(WmWindow* window) { | 465 void WindowCycleList::OnWindowDestroying(WmWindow* window) { |
| 482 window->RemoveObserver(this); | 466 window->RemoveObserver(this); |
| 483 | 467 |
| 484 WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window); | 468 WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window); |
| 485 // TODO(oshima): Change this back to DCHECK once crbug.com/483491 is fixed. | 469 // TODO(oshima): Change this back to DCHECK once crbug.com/483491 is fixed. |
| 486 CHECK(i != windows_.end()); | 470 CHECK(i != windows_.end()); |
| 487 int removed_index = static_cast<int>(i - windows_.begin()); | 471 int removed_index = static_cast<int>(i - windows_.begin()); |
| 488 windows_.erase(i); | 472 windows_.erase(i); |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 502 } | 486 } |
| 503 } | 487 } |
| 504 } | 488 } |
| 505 | 489 |
| 506 bool WindowCycleList::ShouldShowUi() { | 490 bool WindowCycleList::ShouldShowUi() { |
| 507 return windows_.size() > 1 && | 491 return windows_.size() > 1 && |
| 508 base::CommandLine::ForCurrentProcess()->HasSwitch( | 492 base::CommandLine::ForCurrentProcess()->HasSwitch( |
| 509 switches::kAshEnableWindowCycleUi); | 493 switches::kAshEnableWindowCycleUi); |
| 510 } | 494 } |
| 511 | 495 |
| 496 void WindowCycleList::InitWindowCycleView() { | |
| 497 if (cycle_view_) | |
| 498 return; | |
| 499 | |
| 500 cycle_view_ = new WindowCycleView(windows_); | |
| 501 cycle_view_->SetTargetWindow(windows_[current_index_]); | |
| 502 | |
| 503 WmWindow* root_window = WmShell::Get()->GetRootWindowForNewWindows(); | |
| 504 views::Widget* widget = new views::Widget; | |
| 505 views::Widget::InitParams params; | |
| 506 params.delegate = cycle_view_; | |
|
sky
2016/08/11 19:36:26
It's very common in ash code to set a name on thes
Evan Stade
2016/08/11 20:00:10
Done.
| |
| 507 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
| 508 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 509 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | |
| 510 params.accept_events = true; | |
| 511 // TODO(estade): make sure nothing untoward happens when the lock screen | |
| 512 // or a system modal dialog is shown. | |
| 513 root_window->GetRootWindowController()->ConfigureWidgetInitParamsForContainer( | |
| 514 widget, kShellWindowId_OverlayContainer, ¶ms); | |
| 515 widget->Init(params); | |
| 516 | |
| 517 // TODO(estade): right now this just extends past the edge of the screen if | |
| 518 // there are too many windows. Handle this more gracefully. Also, if | |
| 519 // the display metrics change, cancel the UI. | |
| 520 gfx::Rect widget_rect = widget->GetWorkAreaBoundsInScreen(); | |
| 521 int widget_height = cycle_view_->GetPreferredSize().height(); | |
| 522 widget_rect.set_y((widget_rect.height() - widget_height) / 2); | |
| 523 widget_rect.set_height(widget_height); | |
| 524 widget->SetBounds(widget_rect); | |
| 525 widget->Show(); | |
| 526 cycle_ui_widget_.reset(widget); | |
| 527 } | |
| 528 | |
| 512 } // namespace ash | 529 } // namespace ash |
| OLD | NEW |