| 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 "ash/common/wm/window_cycle_list.h" | |
| 6 | |
| 7 #include <list> | |
| 8 #include <map> | |
| 9 | |
| 10 #include "ash/common/wm/mru_window_tracker.h" | |
| 11 #include "ash/common/wm/window_state.h" | |
| 12 #include "ash/common/wm_shell.h" | |
| 13 #include "ash/common/wm_window.h" | |
| 14 #include "ash/public/cpp/shell_window_ids.h" | |
| 15 #include "ash/root_window_controller.h" | |
| 16 #include "base/command_line.h" | |
| 17 #include "ui/accessibility/ax_node_data.h" | |
| 18 #include "ui/compositor/scoped_layer_animation_settings.h" | |
| 19 #include "ui/display/display.h" | |
| 20 #include "ui/display/screen.h" | |
| 21 #include "ui/gfx/canvas.h" | |
| 22 #include "ui/views/background.h" | |
| 23 #include "ui/views/border.h" | |
| 24 #include "ui/views/controls/label.h" | |
| 25 #include "ui/views/layout/box_layout.h" | |
| 26 #include "ui/views/painter.h" | |
| 27 #include "ui/views/view.h" | |
| 28 #include "ui/views/widget/widget.h" | |
| 29 #include "ui/views/widget/widget_delegate.h" | |
| 30 #include "ui/wm/core/visibility_controller.h" | |
| 31 | |
| 32 namespace ash { | |
| 33 | |
| 34 namespace { | |
| 35 | |
| 36 bool g_disable_initial_delay = false; | |
| 37 | |
| 38 // Used for the highlight view and the shield (black background). | |
| 39 constexpr float kBackgroundCornerRadius = 4.f; | |
| 40 | |
| 41 // This background paints a |Painter| but fills the view's layer's size rather | |
| 42 // than the view's size. | |
| 43 class LayerFillBackgroundPainter : public views::Background { | |
| 44 public: | |
| 45 explicit LayerFillBackgroundPainter(std::unique_ptr<views::Painter> painter) | |
| 46 : painter_(std::move(painter)) {} | |
| 47 | |
| 48 ~LayerFillBackgroundPainter() override {} | |
| 49 | |
| 50 void Paint(gfx::Canvas* canvas, views::View* view) const override { | |
| 51 views::Painter::PaintPainterAt(canvas, painter_.get(), | |
| 52 gfx::Rect(view->layer()->size())); | |
| 53 } | |
| 54 | |
| 55 private: | |
| 56 std::unique_ptr<views::Painter> painter_; | |
| 57 | |
| 58 DISALLOW_COPY_AND_ASSIGN(LayerFillBackgroundPainter); | |
| 59 }; | |
| 60 | |
| 61 } // namespace | |
| 62 | |
| 63 // This view represents a single WmWindow by displaying a title and a thumbnail | |
| 64 // of the window's contents. | |
| 65 class WindowPreviewView : public views::View, public aura::WindowObserver { | |
| 66 public: | |
| 67 explicit WindowPreviewView(WmWindow* window) | |
| 68 : window_title_(new views::Label), | |
| 69 preview_background_(new views::View), | |
| 70 mirror_view_(window->CreateViewWithRecreatedLayers().release()), | |
| 71 window_observer_(this) { | |
| 72 window_observer_.Add(window->aura_window()); | |
| 73 window_title_->SetText(window->GetTitle()); | |
| 74 window_title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 75 window_title_->SetEnabledColor(SK_ColorWHITE); | |
| 76 window_title_->SetAutoColorReadabilityEnabled(false); | |
| 77 // Background is not fully opaque, so subpixel rendering won't look good. | |
| 78 window_title_->SetSubpixelRenderingEnabled(false); | |
| 79 // The base font is 12pt (for English) so this comes out to 14pt. | |
| 80 const int kLabelSizeDelta = 2; | |
| 81 window_title_->SetFontList( | |
| 82 window_title_->font_list().DeriveWithSizeDelta(kLabelSizeDelta)); | |
| 83 const int kAboveLabelPadding = 5; | |
| 84 const int kBelowLabelPadding = 10; | |
| 85 window_title_->SetBorder( | |
| 86 views::CreateEmptyBorder(kAboveLabelPadding, 0, kBelowLabelPadding, 0)); | |
| 87 AddChildView(window_title_); | |
| 88 | |
| 89 // Preview padding is black at 50% opacity. | |
| 90 preview_background_->set_background( | |
| 91 views::Background::CreateSolidBackground( | |
| 92 SkColorSetA(SK_ColorBLACK, 0xFF / 2))); | |
| 93 AddChildView(preview_background_); | |
| 94 | |
| 95 AddChildView(mirror_view_); | |
| 96 | |
| 97 SetFocusBehavior(FocusBehavior::ALWAYS); | |
| 98 } | |
| 99 ~WindowPreviewView() override {} | |
| 100 | |
| 101 // views::View: | |
| 102 gfx::Size GetPreferredSize() const override { | |
| 103 gfx::Size size = GetSizeForPreviewArea(); | |
| 104 size.Enlarge(0, window_title_->GetPreferredSize().height()); | |
| 105 return size; | |
| 106 } | |
| 107 | |
| 108 void Layout() override { | |
| 109 const gfx::Size preview_area_size = GetSizeForPreviewArea(); | |
| 110 // The window title is positioned above the preview area. | |
| 111 window_title_->SetBounds(0, 0, width(), | |
| 112 height() - preview_area_size.height()); | |
| 113 | |
| 114 gfx::Rect preview_area_bounds(preview_area_size); | |
| 115 preview_area_bounds.set_y(height() - preview_area_size.height()); | |
| 116 mirror_view_->SetSize(GetMirrorViewScaledSize()); | |
| 117 if (mirror_view_->size() == preview_area_size) { | |
| 118 // Padding is not needed, hide the background and set the mirror view | |
| 119 // to take up the entire preview area. | |
| 120 mirror_view_->SetPosition(preview_area_bounds.origin()); | |
| 121 preview_background_->SetVisible(false); | |
| 122 return; | |
| 123 } | |
| 124 | |
| 125 // Padding is needed, so show the background and set the mirror view to be | |
| 126 // centered within it. | |
| 127 preview_background_->SetBoundsRect(preview_area_bounds); | |
| 128 preview_background_->SetVisible(true); | |
| 129 preview_area_bounds.ClampToCenteredSize(mirror_view_->size()); | |
| 130 mirror_view_->SetPosition(preview_area_bounds.origin()); | |
| 131 } | |
| 132 | |
| 133 void GetAccessibleNodeData(ui::AXNodeData* node_data) override { | |
| 134 node_data->role = ui::AX_ROLE_WINDOW; | |
| 135 node_data->SetName(window_title_->text()); | |
| 136 } | |
| 137 | |
| 138 // aura::WindowObserver: | |
| 139 void OnWindowDestroying(aura::Window* window) override { | |
| 140 window_observer_.Remove(window); | |
| 141 } | |
| 142 | |
| 143 void OnWindowTitleChanged(aura::Window* window) override { | |
| 144 window_title_->SetText(window->GetTitle()); | |
| 145 } | |
| 146 | |
| 147 private: | |
| 148 // The maximum width of a window preview. | |
| 149 static const int kMaxPreviewWidth = 512; | |
| 150 // All previews are the same height (this is achieved via a combination of | |
| 151 // scaling and padding). | |
| 152 static const int kFixedPreviewHeight = 256; | |
| 153 | |
| 154 // Returns the size for the mirror view, scaled to fit within the max bounds. | |
| 155 // Scaling is always 1:1 and we only scale down, never up. | |
| 156 gfx::Size GetMirrorViewScaledSize() const { | |
| 157 gfx::Size mirror_pref_size = mirror_view_->GetPreferredSize(); | |
| 158 | |
| 159 if (mirror_pref_size.width() > kMaxPreviewWidth || | |
| 160 mirror_pref_size.height() > kFixedPreviewHeight) { | |
| 161 float scale = std::min( | |
| 162 kMaxPreviewWidth / static_cast<float>(mirror_pref_size.width()), | |
| 163 kFixedPreviewHeight / static_cast<float>(mirror_pref_size.height())); | |
| 164 mirror_pref_size = | |
| 165 gfx::ScaleToFlooredSize(mirror_pref_size, scale, scale); | |
| 166 } | |
| 167 | |
| 168 return mirror_pref_size; | |
| 169 } | |
| 170 | |
| 171 // Returns the size for the entire preview area (mirror view and additional | |
| 172 // padding). All previews will be the same height, so if the mirror view isn't | |
| 173 // tall enough we will add top and bottom padding. Previews can range in width | |
| 174 // from kMaxPreviewWidth down to half that value. Again, padding will be added | |
| 175 // to the sides to achieve this if the preview is too narrow. | |
| 176 gfx::Size GetSizeForPreviewArea() const { | |
| 177 gfx::Size mirror_size = GetMirrorViewScaledSize(); | |
| 178 float aspect_ratio = | |
| 179 static_cast<float>(mirror_size.width()) / mirror_size.height(); | |
| 180 gfx::Size preview_size = mirror_size; | |
| 181 // Very narrow windows get vertical bars of padding on the sides. | |
| 182 if (aspect_ratio < 0.5f) | |
| 183 preview_size.set_width(mirror_size.height() / 2); | |
| 184 | |
| 185 // All previews are the same height (this may add padding on top and | |
| 186 // bottom). | |
| 187 preview_size.set_height(kFixedPreviewHeight); | |
| 188 // Previews should never be narrower than half their max width (128dip). | |
| 189 preview_size.set_width( | |
| 190 std::max(preview_size.width(), kMaxPreviewWidth / 2)); | |
| 191 | |
| 192 return preview_size; | |
| 193 } | |
| 194 | |
| 195 // Displays the title of the window above the preview. | |
| 196 views::Label* window_title_; | |
| 197 // When visible, shows a darkened background area behind |mirror_view_| | |
| 198 // (effectively padding the preview to fit the desired bounds). | |
| 199 views::View* preview_background_; | |
| 200 // The view that actually renders a thumbnail version of the window. | |
| 201 views::View* mirror_view_; | |
| 202 | |
| 203 ScopedObserver<aura::Window, aura::WindowObserver> window_observer_; | |
| 204 | |
| 205 DISALLOW_COPY_AND_ASSIGN(WindowPreviewView); | |
| 206 }; | |
| 207 | |
| 208 // A view that shows a collection of windows the user can tab through. | |
| 209 class WindowCycleView : public views::WidgetDelegateView { | |
| 210 public: | |
| 211 explicit WindowCycleView(const WindowCycleList::WindowList& windows) | |
| 212 : mirror_container_(new views::View()), | |
| 213 highlight_view_(new views::View()), | |
| 214 target_window_(nullptr) { | |
| 215 DCHECK(!windows.empty()); | |
| 216 SetPaintToLayer(); | |
| 217 layer()->SetFillsBoundsOpaquely(false); | |
| 218 layer()->SetMasksToBounds(true); | |
| 219 layer()->SetOpacity(0.0); | |
| 220 { | |
| 221 ui::ScopedLayerAnimationSettings animate_fade(layer()->GetAnimator()); | |
| 222 animate_fade.SetTransitionDuration( | |
| 223 base::TimeDelta::FromMilliseconds(100)); | |
| 224 layer()->SetOpacity(1.0); | |
| 225 } | |
| 226 | |
| 227 const int kInsideBorderPaddingDip = 64; | |
| 228 const int kBetweenChildPaddingDip = 10; | |
| 229 views::BoxLayout* layout = new views::BoxLayout( | |
| 230 views::BoxLayout::kHorizontal, kInsideBorderPaddingDip, | |
| 231 kInsideBorderPaddingDip, kBetweenChildPaddingDip); | |
| 232 layout->set_cross_axis_alignment( | |
| 233 views::BoxLayout::CROSS_AXIS_ALIGNMENT_START); | |
| 234 mirror_container_->SetLayoutManager(layout); | |
| 235 mirror_container_->SetPaintToLayer(); | |
| 236 mirror_container_->layer()->SetFillsBoundsOpaquely(false); | |
| 237 | |
| 238 for (WmWindow* window : windows) { | |
| 239 // |mirror_container_| owns |view|. | |
| 240 views::View* view = new WindowPreviewView(window); | |
| 241 window_view_map_[window] = view; | |
| 242 mirror_container_->AddChildView(view); | |
| 243 } | |
| 244 | |
| 245 // The background needs to be painted to fill the layer, not the View, | |
| 246 // because the layer animates bounds changes but the View's bounds change | |
| 247 // immediately. | |
| 248 highlight_view_->set_background(new LayerFillBackgroundPainter( | |
| 249 views::Painter::CreateRoundRectWith1PxBorderPainter( | |
| 250 SkColorSetA(SK_ColorWHITE, 0x4D), SkColorSetA(SK_ColorWHITE, 0x33), | |
| 251 kBackgroundCornerRadius))); | |
| 252 highlight_view_->SetPaintToLayer(); | |
| 253 | |
| 254 highlight_view_->layer()->SetFillsBoundsOpaquely(false); | |
| 255 | |
| 256 AddChildView(highlight_view_); | |
| 257 AddChildView(mirror_container_); | |
| 258 } | |
| 259 | |
| 260 ~WindowCycleView() override {} | |
| 261 | |
| 262 void SetTargetWindow(WmWindow* target) { | |
| 263 target_window_ = target; | |
| 264 if (GetWidget()) { | |
| 265 Layout(); | |
| 266 if (target_window_) | |
| 267 window_view_map_[target_window_]->RequestFocus(); | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 void HandleWindowDestruction(WmWindow* destroying_window, | |
| 272 WmWindow* new_target) { | |
| 273 auto view_iter = window_view_map_.find(destroying_window); | |
| 274 views::View* preview = view_iter->second; | |
| 275 views::View* parent = preview->parent(); | |
| 276 DCHECK_EQ(mirror_container_, parent); | |
| 277 window_view_map_.erase(view_iter); | |
| 278 delete preview; | |
| 279 // With one of its children now gone, we must re-layout |mirror_container_|. | |
| 280 // This must happen before SetTargetWindow() to make sure our own Layout() | |
| 281 // works correctly when it's calculating highlight bounds. | |
| 282 parent->Layout(); | |
| 283 SetTargetWindow(new_target); | |
| 284 } | |
| 285 | |
| 286 void DestroyContents() { | |
| 287 window_view_map_.clear(); | |
| 288 RemoveAllChildViews(true); | |
| 289 } | |
| 290 | |
| 291 // views::WidgetDelegateView overrides: | |
| 292 gfx::Size GetPreferredSize() const override { | |
| 293 return mirror_container_->GetPreferredSize(); | |
| 294 } | |
| 295 | |
| 296 void Layout() override { | |
| 297 if (!target_window_ || bounds().IsEmpty()) | |
| 298 return; | |
| 299 | |
| 300 bool first_layout = mirror_container_->bounds().IsEmpty(); | |
| 301 // If |mirror_container_| has not yet been laid out, we must lay it and its | |
| 302 // descendants out so that the calculations based on |target_view| work | |
| 303 // properly. | |
| 304 if (first_layout) | |
| 305 mirror_container_->SizeToPreferredSize(); | |
| 306 | |
| 307 views::View* target_view = window_view_map_[target_window_]; | |
| 308 gfx::RectF target_bounds(target_view->GetLocalBounds()); | |
| 309 views::View::ConvertRectToTarget(target_view, mirror_container_, | |
| 310 &target_bounds); | |
| 311 gfx::Rect container_bounds(mirror_container_->GetPreferredSize()); | |
| 312 // Case one: the container is narrower than the screen. Center the | |
| 313 // container. | |
| 314 int x_offset = (width() - container_bounds.width()) / 2; | |
| 315 if (x_offset < 0) { | |
| 316 // Case two: the container is wider than the screen. Center the target | |
| 317 // view by moving the list just enough to ensure the target view is in the | |
| 318 // center. | |
| 319 x_offset = width() / 2 - | |
| 320 mirror_container_->GetMirroredXInView( | |
| 321 target_bounds.CenterPoint().x()); | |
| 322 | |
| 323 // However, the container must span the screen, i.e. the maximum x is 0 | |
| 324 // and the minimum for its right boundary is the width of the screen. | |
| 325 x_offset = std::min(x_offset, 0); | |
| 326 x_offset = std::max(x_offset, width() - container_bounds.width()); | |
| 327 } | |
| 328 container_bounds.set_x(x_offset); | |
| 329 mirror_container_->SetBoundsRect(container_bounds); | |
| 330 | |
| 331 // Calculate the target preview's bounds relative to |this|. | |
| 332 views::View::ConvertRectToTarget(mirror_container_, this, &target_bounds); | |
| 333 const int kHighlightPaddingDip = 5; | |
| 334 target_bounds.Inset(gfx::InsetsF(-kHighlightPaddingDip)); | |
| 335 target_bounds.set_x( | |
| 336 GetMirroredXWithWidthInView(target_bounds.x(), target_bounds.width())); | |
| 337 highlight_view_->SetBoundsRect(gfx::ToEnclosingRect(target_bounds)); | |
| 338 | |
| 339 // Enable animations only after the first Layout() pass. | |
| 340 if (first_layout) { | |
| 341 // The preview list animates bounds changes (other animatable properties | |
| 342 // never change). | |
| 343 mirror_container_->layer()->SetAnimator( | |
| 344 ui::LayerAnimator::CreateImplicitAnimator()); | |
| 345 // The selection highlight also animates all bounds changes and never | |
| 346 // changes other animatable properties. | |
| 347 highlight_view_->layer()->SetAnimator( | |
| 348 ui::LayerAnimator::CreateImplicitAnimator()); | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 void OnPaintBackground(gfx::Canvas* canvas) override { | |
| 353 // We can't set a bg on the mirror container itself because the highlight | |
| 354 // view needs to be on top of the bg but behind the target windows. | |
| 355 const gfx::RectF shield_bounds(mirror_container_->bounds()); | |
| 356 cc::PaintFlags flags; | |
| 357 flags.setColor(SkColorSetA(SK_ColorBLACK, 0xE6)); | |
| 358 flags.setStyle(cc::PaintFlags::kFill_Style); | |
| 359 float corner_radius = 0.f; | |
| 360 if (shield_bounds.width() < width()) { | |
| 361 flags.setAntiAlias(true); | |
| 362 corner_radius = kBackgroundCornerRadius; | |
| 363 } | |
| 364 canvas->DrawRoundRect(shield_bounds, corner_radius, flags); | |
| 365 } | |
| 366 | |
| 367 View* GetInitiallyFocusedView() override { | |
| 368 return window_view_map_[target_window_]; | |
| 369 } | |
| 370 | |
| 371 WmWindow* target_window() { return target_window_; } | |
| 372 | |
| 373 private: | |
| 374 std::map<WmWindow*, views::View*> window_view_map_; | |
| 375 views::View* mirror_container_; | |
| 376 views::View* highlight_view_; | |
| 377 WmWindow* target_window_; | |
| 378 | |
| 379 DISALLOW_COPY_AND_ASSIGN(WindowCycleView); | |
| 380 }; | |
| 381 | |
| 382 WindowCycleList::WindowCycleList(const WindowList& windows) | |
| 383 : windows_(windows), | |
| 384 screen_observer_(this) { | |
| 385 if (!ShouldShowUi()) | |
| 386 WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(true); | |
| 387 | |
| 388 for (WmWindow* window : windows_) | |
| 389 window->aura_window()->AddObserver(this); | |
| 390 | |
| 391 if (ShouldShowUi()) { | |
| 392 if (g_disable_initial_delay) { | |
| 393 InitWindowCycleView(); | |
| 394 } else { | |
| 395 show_ui_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(150), | |
| 396 this, &WindowCycleList::InitWindowCycleView); | |
| 397 } | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 WindowCycleList::~WindowCycleList() { | |
| 402 if (!ShouldShowUi()) | |
| 403 WmShell::Get()->mru_window_tracker()->SetIgnoreActivations(false); | |
| 404 | |
| 405 for (WmWindow* window : windows_) | |
| 406 window->aura_window()->RemoveObserver(this); | |
| 407 | |
| 408 if (!windows_.empty() && user_did_accept_) { | |
| 409 WmWindow* target_window = windows_[current_index_]; | |
| 410 target_window->Show(); | |
| 411 target_window->GetWindowState()->Activate(); | |
| 412 } | |
| 413 | |
| 414 if (cycle_ui_widget_) | |
| 415 cycle_ui_widget_->Close(); | |
| 416 | |
| 417 // |this| is responsible for notifying |cycle_view_| when windows are | |
| 418 // destroyed. Since |this| is going away, clobber |cycle_view_|. Otherwise | |
| 419 // there will be a race where a window closes after now but before the | |
| 420 // Widget::Close() call above actually destroys |cycle_view_|. See | |
| 421 // crbug.com/681207 | |
| 422 if (cycle_view_) | |
| 423 cycle_view_->DestroyContents(); | |
| 424 } | |
| 425 | |
| 426 void WindowCycleList::Step(WindowCycleController::Direction direction) { | |
| 427 if (windows_.empty()) | |
| 428 return; | |
| 429 | |
| 430 // When there is only one window, we should give feedback to the user. If the | |
| 431 // window is minimized, we should also show it. | |
| 432 if (windows_.size() == 1) { | |
| 433 windows_[0]->Animate(::wm::WINDOW_ANIMATION_TYPE_BOUNCE); | |
| 434 windows_[0]->Show(); | |
| 435 windows_[0]->GetWindowState()->Activate(); | |
| 436 return; | |
| 437 } | |
| 438 | |
| 439 DCHECK(static_cast<size_t>(current_index_) < windows_.size()); | |
| 440 | |
| 441 if (!cycle_view_ && current_index_ == 0) { | |
| 442 // Special case the situation where we're cycling forward but the MRU window | |
| 443 // is not active. This occurs when all windows are minimized. The starting | |
| 444 // window should be the first one rather than the second. | |
| 445 if (direction == WindowCycleController::FORWARD && !windows_[0]->IsActive()) | |
| 446 current_index_ = -1; | |
| 447 } | |
| 448 | |
| 449 // We're in a valid cycle, so step forward or backward. | |
| 450 current_index_ += direction == WindowCycleController::FORWARD ? 1 : -1; | |
| 451 | |
| 452 // Wrap to window list size. | |
| 453 current_index_ = (current_index_ + windows_.size()) % windows_.size(); | |
| 454 DCHECK(windows_[current_index_]); | |
| 455 | |
| 456 if (ShouldShowUi()) { | |
| 457 if (current_index_ > 1) | |
| 458 InitWindowCycleView(); | |
| 459 | |
| 460 if (cycle_view_) | |
| 461 cycle_view_->SetTargetWindow(windows_[current_index_]); | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 // static | |
| 466 void WindowCycleList::DisableInitialDelayForTesting() { | |
| 467 g_disable_initial_delay = true; | |
| 468 } | |
| 469 | |
| 470 void WindowCycleList::OnWindowDestroying(aura::Window* window) { | |
| 471 window->RemoveObserver(this); | |
| 472 | |
| 473 WindowList::iterator i = | |
| 474 std::find(windows_.begin(), windows_.end(), WmWindow::Get(window)); | |
| 475 // TODO(oshima): Change this back to DCHECK once crbug.com/483491 is fixed. | |
| 476 CHECK(i != windows_.end()); | |
| 477 int removed_index = static_cast<int>(i - windows_.begin()); | |
| 478 windows_.erase(i); | |
| 479 if (current_index_ > removed_index || | |
| 480 current_index_ == static_cast<int>(windows_.size())) { | |
| 481 current_index_--; | |
| 482 } | |
| 483 | |
| 484 if (cycle_view_) { | |
| 485 WmWindow* new_target_window = | |
| 486 windows_.empty() ? nullptr : windows_[current_index_]; | |
| 487 cycle_view_->HandleWindowDestruction(WmWindow::Get(window), | |
| 488 new_target_window); | |
| 489 if (windows_.empty()) { | |
| 490 // This deletes us. | |
| 491 WmShell::Get()->window_cycle_controller()->CancelCycling(); | |
| 492 return; | |
| 493 } | |
| 494 } | |
| 495 } | |
| 496 | |
| 497 void WindowCycleList::OnDisplayAdded(const display::Display& new_display) {} | |
| 498 | |
| 499 void WindowCycleList::OnDisplayRemoved(const display::Display& old_display) {} | |
| 500 | |
| 501 void WindowCycleList::OnDisplayMetricsChanged(const display::Display& display, | |
| 502 uint32_t changed_metrics) { | |
| 503 if (cycle_ui_widget_ && | |
| 504 display.id() == | |
| 505 display::Screen::GetScreen() | |
| 506 ->GetDisplayNearestWindow(cycle_ui_widget_->GetNativeView()) | |
| 507 .id() && | |
| 508 (changed_metrics & (DISPLAY_METRIC_BOUNDS | DISPLAY_METRIC_ROTATION))) { | |
| 509 WmShell::Get()->window_cycle_controller()->CancelCycling(); | |
| 510 // |this| is deleted. | |
| 511 return; | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 bool WindowCycleList::ShouldShowUi() { | |
| 516 return windows_.size() > 1; | |
| 517 } | |
| 518 | |
| 519 void WindowCycleList::InitWindowCycleView() { | |
| 520 if (cycle_view_) | |
| 521 return; | |
| 522 | |
| 523 cycle_view_ = new WindowCycleView(windows_); | |
| 524 cycle_view_->SetTargetWindow(windows_[current_index_]); | |
| 525 | |
| 526 views::Widget* widget = new views::Widget; | |
| 527 views::Widget::InitParams params; | |
| 528 params.delegate = cycle_view_; | |
| 529 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
| 530 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; | |
| 531 params.accept_events = true; | |
| 532 params.name = "WindowCycleList (Alt+Tab)"; | |
| 533 // TODO(estade): make sure nothing untoward happens when the lock screen | |
| 534 // or a system modal dialog is shown. | |
| 535 WmWindow* root_window = WmShell::Get()->GetRootWindowForNewWindows(); | |
| 536 root_window->GetRootWindowController()->ConfigureWidgetInitParamsForContainer( | |
| 537 widget, kShellWindowId_OverlayContainer, ¶ms); | |
| 538 gfx::Rect widget_rect = root_window->GetDisplayNearestWindow().bounds(); | |
| 539 const int widget_height = cycle_view_->GetPreferredSize().height(); | |
| 540 widget_rect.set_y(widget_rect.y() + | |
| 541 (widget_rect.height() - widget_height) / 2); | |
| 542 widget_rect.set_height(widget_height); | |
| 543 params.bounds = widget_rect; | |
| 544 widget->Init(params); | |
| 545 | |
| 546 screen_observer_.Add(display::Screen::GetScreen()); | |
| 547 widget->Show(); | |
| 548 cycle_ui_widget_ = widget; | |
| 549 } | |
| 550 | |
| 551 } // namespace ash | |
| OLD | NEW |