| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 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 "chrome/browser/ui/views/panels/panel_stack_view.h" | |
| 6 | |
| 7 #include "base/logging.h" | |
| 8 #include "base/macros.h" | |
| 9 #include "base/strings/utf_string_conversions.h" | |
| 10 #include "build/build_config.h" | |
| 11 #include "chrome/browser/profiles/profile.h" | |
| 12 #include "chrome/browser/ui/panels/panel.h" | |
| 13 #include "chrome/browser/ui/panels/panel_manager.h" | |
| 14 #include "chrome/browser/ui/panels/stacked_panel_collection.h" | |
| 15 #include "chrome/browser/ui/views/panels/panel_view.h" | |
| 16 #include "ui/gfx/animation/linear_animation.h" | |
| 17 #include "ui/gfx/geometry/rect.h" | |
| 18 #include "ui/gfx/image/image_skia.h" | |
| 19 #include "ui/views/widget/widget.h" | |
| 20 | |
| 21 #if defined(OS_WIN) | |
| 22 #include "base/win/windows_version.h" | |
| 23 #include "chrome/browser/shell_integration_win.h" | |
| 24 #include "ui/base/win/shell.h" | |
| 25 #include "ui/views/win/hwnd_util.h" | |
| 26 #endif | |
| 27 | |
| 28 namespace { | |
| 29 // These values are experimental and subjective. | |
| 30 const int kDefaultFramerateHz = 50; | |
| 31 const int kSetBoundsAnimationMs = 180; | |
| 32 | |
| 33 // The widget window that acts as a background window for the stack of panels. | |
| 34 class PanelStackWindow : public views::WidgetObserver, | |
| 35 public views::WidgetDelegateView { | |
| 36 public: | |
| 37 PanelStackWindow(const gfx::Rect& bounds, | |
| 38 NativePanelStackWindowDelegate* delegate); | |
| 39 ~PanelStackWindow() override; | |
| 40 | |
| 41 // Overridden from views::WidgetDelegate: | |
| 42 base::string16 GetWindowTitle() const override; | |
| 43 gfx::ImageSkia GetWindowAppIcon() override; | |
| 44 gfx::ImageSkia GetWindowIcon() override; | |
| 45 views::Widget* GetWidget() override; | |
| 46 const views::Widget* GetWidget() const override; | |
| 47 | |
| 48 // Overridden from views::WidgetObserver: | |
| 49 void OnWidgetClosing(views::Widget* widget) override; | |
| 50 void OnWidgetDestroying(views::Widget* widget) override; | |
| 51 | |
| 52 private: | |
| 53 views::Widget* window_; // Weak pointer, own us. | |
| 54 NativePanelStackWindowDelegate* delegate_; // Weak pointer. | |
| 55 | |
| 56 DISALLOW_COPY_AND_ASSIGN(PanelStackWindow); | |
| 57 }; | |
| 58 | |
| 59 PanelStackWindow::PanelStackWindow(const gfx::Rect& bounds, | |
| 60 NativePanelStackWindowDelegate* delegate) | |
| 61 : window_(NULL), | |
| 62 delegate_(delegate) { | |
| 63 window_ = new views::Widget; | |
| 64 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); | |
| 65 params.delegate = this; | |
| 66 params.remove_standard_frame = true; | |
| 67 params.bounds = bounds; | |
| 68 window_->Init(params); | |
| 69 window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); | |
| 70 window_->set_focus_on_creation(false); | |
| 71 window_->AddObserver(this); | |
| 72 window_->ShowInactive(); | |
| 73 } | |
| 74 | |
| 75 PanelStackWindow::~PanelStackWindow() { | |
| 76 } | |
| 77 | |
| 78 base::string16 PanelStackWindow::GetWindowTitle() const { | |
| 79 return delegate_ ? delegate_->GetTitle() : base::string16(); | |
| 80 } | |
| 81 | |
| 82 gfx::ImageSkia PanelStackWindow::GetWindowAppIcon() { | |
| 83 if (delegate_) { | |
| 84 gfx::Image app_icon = delegate_->GetIcon(); | |
| 85 if (!app_icon.IsEmpty()) | |
| 86 return *app_icon.ToImageSkia(); | |
| 87 } | |
| 88 return gfx::ImageSkia(); | |
| 89 } | |
| 90 | |
| 91 gfx::ImageSkia PanelStackWindow::GetWindowIcon() { | |
| 92 return GetWindowAppIcon(); | |
| 93 } | |
| 94 | |
| 95 views::Widget* PanelStackWindow::GetWidget() { | |
| 96 return window_; | |
| 97 } | |
| 98 | |
| 99 const views::Widget* PanelStackWindow::GetWidget() const { | |
| 100 return window_; | |
| 101 } | |
| 102 | |
| 103 void PanelStackWindow::OnWidgetClosing(views::Widget* widget) { | |
| 104 delegate_ = NULL; | |
| 105 } | |
| 106 | |
| 107 void PanelStackWindow::OnWidgetDestroying(views::Widget* widget) { | |
| 108 window_ = NULL; | |
| 109 } | |
| 110 | |
| 111 } // namespace | |
| 112 | |
| 113 // static | |
| 114 NativePanelStackWindow* NativePanelStackWindow::Create( | |
| 115 NativePanelStackWindowDelegate* delegate) { | |
| 116 #if defined(OS_WIN) | |
| 117 return new PanelStackView(delegate); | |
| 118 #else | |
| 119 NOTIMPLEMENTED(); | |
| 120 return NULL; | |
| 121 #endif | |
| 122 } | |
| 123 | |
| 124 PanelStackView::PanelStackView(NativePanelStackWindowDelegate* delegate) | |
| 125 : delegate_(delegate), | |
| 126 window_(NULL), | |
| 127 is_drawing_attention_(false), | |
| 128 animate_bounds_updates_(false), | |
| 129 bounds_updates_started_(false) { | |
| 130 DCHECK(delegate); | |
| 131 views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this); | |
| 132 } | |
| 133 | |
| 134 PanelStackView::~PanelStackView() { | |
| 135 #if defined(OS_WIN) | |
| 136 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); | |
| 137 #endif | |
| 138 } | |
| 139 | |
| 140 void PanelStackView::Close() { | |
| 141 delegate_ = NULL; | |
| 142 if (bounds_animator_) | |
| 143 bounds_animator_.reset(); | |
| 144 if (window_) | |
| 145 window_->Close(); | |
| 146 views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this); | |
| 147 } | |
| 148 | |
| 149 void PanelStackView::AddPanel(Panel* panel) { | |
| 150 panels_.push_back(panel); | |
| 151 | |
| 152 EnsureWindowCreated(); | |
| 153 MakeStackWindowOwnPanelWindow(panel, this); | |
| 154 UpdateStackWindowBounds(); | |
| 155 | |
| 156 window_->UpdateWindowTitle(); | |
| 157 window_->UpdateWindowIcon(); | |
| 158 } | |
| 159 | |
| 160 void PanelStackView::RemovePanel(Panel* panel) { | |
| 161 if (IsAnimatingPanelBounds()) { | |
| 162 // This panel is gone. | |
| 163 bounds_updates_.erase(panel); | |
| 164 | |
| 165 // Abort the ongoing animation. | |
| 166 bounds_animator_->Stop(); | |
| 167 } | |
| 168 | |
| 169 panels_.remove(panel); | |
| 170 | |
| 171 MakeStackWindowOwnPanelWindow(panel, NULL); | |
| 172 UpdateStackWindowBounds(); | |
| 173 } | |
| 174 | |
| 175 void PanelStackView::MergeWith(NativePanelStackWindow* another) { | |
| 176 PanelStackView* another_stack = static_cast<PanelStackView*>(another); | |
| 177 | |
| 178 for (Panels::const_iterator iter = another_stack->panels_.begin(); | |
| 179 iter != another_stack->panels_.end(); ++iter) { | |
| 180 Panel* panel = *iter; | |
| 181 panels_.push_back(panel); | |
| 182 MakeStackWindowOwnPanelWindow(panel, this); | |
| 183 } | |
| 184 another_stack->panels_.clear(); | |
| 185 | |
| 186 UpdateStackWindowBounds(); | |
| 187 } | |
| 188 | |
| 189 bool PanelStackView::IsEmpty() const { | |
| 190 return panels_.empty(); | |
| 191 } | |
| 192 | |
| 193 bool PanelStackView::HasPanel(Panel* panel) const { | |
| 194 return std::find(panels_.begin(), panels_.end(), panel) != panels_.end(); | |
| 195 } | |
| 196 | |
| 197 void PanelStackView::MovePanelsBy(const gfx::Vector2d& delta) { | |
| 198 BeginBatchUpdatePanelBounds(false); | |
| 199 for (Panels::const_iterator iter = panels_.begin(); | |
| 200 iter != panels_.end(); ++iter) { | |
| 201 Panel* panel = *iter; | |
| 202 AddPanelBoundsForBatchUpdate(panel, panel->GetBounds() + delta); | |
| 203 } | |
| 204 EndBatchUpdatePanelBounds(); | |
| 205 } | |
| 206 | |
| 207 void PanelStackView::BeginBatchUpdatePanelBounds(bool animate) { | |
| 208 // If the batch animation is still in progress, continue the animation | |
| 209 // with the new target bounds even we want to update the bounds instantly | |
| 210 // this time. | |
| 211 if (!bounds_updates_started_) { | |
| 212 animate_bounds_updates_ = animate; | |
| 213 bounds_updates_started_ = true; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 void PanelStackView::AddPanelBoundsForBatchUpdate(Panel* panel, | |
| 218 const gfx::Rect& new_bounds) { | |
| 219 DCHECK(bounds_updates_started_); | |
| 220 | |
| 221 // No need to track it if no change is needed. | |
| 222 if (panel->GetBounds() == new_bounds) | |
| 223 return; | |
| 224 | |
| 225 // Old bounds are stored as the map value. | |
| 226 bounds_updates_[panel] = panel->GetBounds(); | |
| 227 | |
| 228 // New bounds are directly applied to the valued stored in native panel | |
| 229 // window. | |
| 230 static_cast<PanelView*>(panel->native_panel())->set_cached_bounds_directly( | |
| 231 new_bounds); | |
| 232 } | |
| 233 | |
| 234 void PanelStackView::EndBatchUpdatePanelBounds() { | |
| 235 DCHECK(bounds_updates_started_); | |
| 236 | |
| 237 if (bounds_updates_.empty() || !animate_bounds_updates_) { | |
| 238 if (!bounds_updates_.empty()) { | |
| 239 UpdatePanelsBounds(); | |
| 240 bounds_updates_.clear(); | |
| 241 } | |
| 242 | |
| 243 bounds_updates_started_ = false; | |
| 244 NotifyBoundsUpdateCompleted(); | |
| 245 return; | |
| 246 } | |
| 247 | |
| 248 bounds_animator_.reset(new gfx::LinearAnimation( | |
| 249 PanelManager::AdjustTimeInterval(kSetBoundsAnimationMs), | |
| 250 kDefaultFramerateHz, | |
| 251 this)); | |
| 252 bounds_animator_->Start(); | |
| 253 } | |
| 254 | |
| 255 void PanelStackView::NotifyBoundsUpdateCompleted() { | |
| 256 delegate_->PanelBoundsBatchUpdateCompleted(); | |
| 257 | |
| 258 #if defined(OS_WIN) | |
| 259 // Refresh the thumbnail each time when any bounds updates are done. | |
| 260 RefreshLivePreviewThumbnail(); | |
| 261 #endif | |
| 262 } | |
| 263 | |
| 264 bool PanelStackView::IsAnimatingPanelBounds() const { | |
| 265 return bounds_updates_started_ && animate_bounds_updates_; | |
| 266 } | |
| 267 | |
| 268 void PanelStackView::Minimize() { | |
| 269 #if defined(OS_WIN) | |
| 270 // When the stack window is minimized by the system, its snapshot could not | |
| 271 // be obtained. We need to capture the snapshot before the minimization. | |
| 272 if (thumbnailer_) | |
| 273 thumbnailer_->CaptureSnapshot(); | |
| 274 #endif | |
| 275 | |
| 276 window_->Minimize(); | |
| 277 } | |
| 278 | |
| 279 bool PanelStackView::IsMinimized() const { | |
| 280 return window_ ? window_->IsMinimized() : false; | |
| 281 } | |
| 282 | |
| 283 void PanelStackView::DrawSystemAttention(bool draw_attention) { | |
| 284 // The underlying call of FlashFrame, FlashWindowEx, seems not to work | |
| 285 // correctly if it is called more than once consecutively. | |
| 286 if (draw_attention == is_drawing_attention_) | |
| 287 return; | |
| 288 is_drawing_attention_ = draw_attention; | |
| 289 | |
| 290 #if defined(OS_WIN) | |
| 291 // Refresh the thumbnail when a panel could change something for the | |
| 292 // attention. | |
| 293 RefreshLivePreviewThumbnail(); | |
| 294 | |
| 295 if (draw_attention) { | |
| 296 // The default implementation of Widget::FlashFrame only flashes 5 times. | |
| 297 // We need more than that. | |
| 298 FLASHWINFO fwi; | |
| 299 fwi.cbSize = sizeof(fwi); | |
| 300 fwi.hwnd = views::HWNDForWidget(window_); | |
| 301 fwi.dwFlags = FLASHW_ALL; | |
| 302 fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention; | |
| 303 fwi.dwTimeout = 0; | |
| 304 ::FlashWindowEx(&fwi); | |
| 305 } else { | |
| 306 // Calling FlashWindowEx with FLASHW_STOP flag does not always work. | |
| 307 // Occasionally the taskbar icon could still remain in the flashed state. | |
| 308 // To work around this problem, we recreate the underlying window. | |
| 309 views::Widget* old_window = window_; | |
| 310 window_ = CreateWindowWithBounds(GetStackWindowBounds()); | |
| 311 | |
| 312 // New background window should also be minimized if the old one is. | |
| 313 if (old_window->IsMinimized()) | |
| 314 window_->Minimize(); | |
| 315 | |
| 316 // Make sure the new background window stays at the same z-order as the old | |
| 317 // one. | |
| 318 ::SetWindowPos(views::HWNDForWidget(window_), | |
| 319 views::HWNDForWidget(old_window), | |
| 320 0, 0, 0, 0, | |
| 321 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); | |
| 322 for (Panels::const_iterator iter = panels_.begin(); | |
| 323 iter != panels_.end(); ++iter) { | |
| 324 MakeStackWindowOwnPanelWindow(*iter, this); | |
| 325 } | |
| 326 | |
| 327 // Serve the snapshot to the new backgroud window. | |
| 328 if (thumbnailer_.get()) | |
| 329 thumbnailer_->ReplaceWindow(views::HWNDForWidget(window_)); | |
| 330 | |
| 331 window_->UpdateWindowTitle(); | |
| 332 window_->UpdateWindowIcon(); | |
| 333 old_window->Close(); | |
| 334 } | |
| 335 #else | |
| 336 window_->FlashFrame(draw_attention); | |
| 337 #endif | |
| 338 } | |
| 339 | |
| 340 void PanelStackView::OnPanelActivated(Panel* panel) { | |
| 341 // Nothing to do. | |
| 342 } | |
| 343 | |
| 344 void PanelStackView::OnNativeFocusChanged(gfx::NativeView focused_now) { | |
| 345 // When the user selects the stacked panels via ALT-TAB or WIN-TAB, the | |
| 346 // background stack window, instead of the foreground panel window, receives | |
| 347 // WM_SETFOCUS message. To deal with this, we listen to the focus change event | |
| 348 // and activate the most recently active panel. | |
| 349 // Note that OnNativeFocusChanged might be called when window_ has not been | |
| 350 // created yet. | |
| 351 #if defined(OS_WIN) | |
| 352 if (!panels_.empty() && window_ && focused_now == window_->GetNativeView()) { | |
| 353 Panel* panel_to_focus = | |
| 354 panels_.front()->stack()->most_recently_active_panel(); | |
| 355 if (panel_to_focus) | |
| 356 panel_to_focus->Activate(); | |
| 357 } | |
| 358 #endif | |
| 359 } | |
| 360 | |
| 361 void PanelStackView::AnimationEnded(const gfx::Animation* animation) { | |
| 362 bounds_updates_started_ = false; | |
| 363 | |
| 364 PanelManager* panel_manager = PanelManager::GetInstance(); | |
| 365 for (BoundsUpdates::const_iterator iter = bounds_updates_.begin(); | |
| 366 iter != bounds_updates_.end(); ++iter) { | |
| 367 panel_manager->OnPanelAnimationEnded(iter->first); | |
| 368 } | |
| 369 bounds_updates_.clear(); | |
| 370 | |
| 371 NotifyBoundsUpdateCompleted(); | |
| 372 } | |
| 373 | |
| 374 void PanelStackView::AnimationCanceled(const gfx::Animation* animation) { | |
| 375 // When the animation is aborted due to something like one of panels is gone, | |
| 376 // update panels to their taget bounds immediately. | |
| 377 UpdatePanelsBounds(); | |
| 378 | |
| 379 AnimationEnded(animation); | |
| 380 } | |
| 381 | |
| 382 void PanelStackView::AnimationProgressed(const gfx::Animation* animation) { | |
| 383 UpdatePanelsBounds(); | |
| 384 } | |
| 385 | |
| 386 void PanelStackView::UpdatePanelsBounds() { | |
| 387 #if defined(OS_WIN) | |
| 388 // Add an extra count for the background stack window. | |
| 389 HDWP defer_update = ::BeginDeferWindowPos(bounds_updates_.size() + 1); | |
| 390 #endif | |
| 391 | |
| 392 // Update the bounds for each panel in the update list. | |
| 393 gfx::Rect enclosing_bounds; | |
| 394 for (BoundsUpdates::const_iterator iter = bounds_updates_.begin(); | |
| 395 iter != bounds_updates_.end(); ++iter) { | |
| 396 Panel* panel = iter->first; | |
| 397 gfx::Rect target_bounds = panel->GetBounds(); | |
| 398 gfx::Rect current_bounds; | |
| 399 if (bounds_animator_ && bounds_animator_->is_animating()) { | |
| 400 current_bounds = bounds_animator_->CurrentValueBetween( | |
| 401 iter->second, target_bounds); | |
| 402 } else { | |
| 403 current_bounds = target_bounds; | |
| 404 } | |
| 405 | |
| 406 PanelView* panel_view = static_cast<PanelView*>(panel->native_panel()); | |
| 407 #if defined(OS_WIN) | |
| 408 DeferUpdateNativeWindowBounds(defer_update, | |
| 409 panel_view->window(), | |
| 410 current_bounds); | |
| 411 #else | |
| 412 panel_view->SetPanelBoundsInstantly(current_bounds); | |
| 413 #endif | |
| 414 | |
| 415 enclosing_bounds = UnionRects(enclosing_bounds, current_bounds); | |
| 416 } | |
| 417 | |
| 418 // Compute the stack window bounds that enclose those panels that are not | |
| 419 // in the batch update list. | |
| 420 for (Panels::const_iterator iter = panels_.begin(); | |
| 421 iter != panels_.end(); ++iter) { | |
| 422 Panel* panel = *iter; | |
| 423 if (bounds_updates_.find(panel) == bounds_updates_.end()) | |
| 424 enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds()); | |
| 425 } | |
| 426 | |
| 427 // Update the bounds of the background stack window. | |
| 428 #if defined(OS_WIN) | |
| 429 DeferUpdateNativeWindowBounds(defer_update, window_, enclosing_bounds); | |
| 430 #else | |
| 431 window_->SetBounds(enclosing_bounds); | |
| 432 #endif | |
| 433 | |
| 434 #if defined(OS_WIN) | |
| 435 ::EndDeferWindowPos(defer_update); | |
| 436 #endif | |
| 437 } | |
| 438 | |
| 439 gfx::Rect PanelStackView::GetStackWindowBounds() const { | |
| 440 gfx::Rect enclosing_bounds; | |
| 441 for (Panels::const_iterator iter = panels_.begin(); | |
| 442 iter != panels_.end(); ++iter) { | |
| 443 Panel* panel = *iter; | |
| 444 enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds()); | |
| 445 } | |
| 446 return enclosing_bounds; | |
| 447 } | |
| 448 | |
| 449 void PanelStackView::UpdateStackWindowBounds() { | |
| 450 window_->SetBounds(GetStackWindowBounds()); | |
| 451 | |
| 452 #if defined(OS_WIN) | |
| 453 // Refresh the thumbnail each time whne the stack window is changed, due to | |
| 454 // adding or removing a panel. | |
| 455 RefreshLivePreviewThumbnail(); | |
| 456 #endif | |
| 457 } | |
| 458 | |
| 459 // static | |
| 460 void PanelStackView::MakeStackWindowOwnPanelWindow( | |
| 461 Panel* panel, PanelStackView* stack_window) { | |
| 462 #if defined(OS_WIN) | |
| 463 // The panel widget window might already be gone when a panel is closed. | |
| 464 views::Widget* panel_window = | |
| 465 static_cast<PanelView*>(panel->native_panel())->window(); | |
| 466 if (!panel_window) | |
| 467 return; | |
| 468 | |
| 469 HWND native_panel_window = views::HWNDForWidget(panel_window); | |
| 470 HWND native_stack_window = | |
| 471 stack_window ? views::HWNDForWidget(stack_window->window_) : NULL; | |
| 472 | |
| 473 // The extended style WS_EX_APPWINDOW is used to force a top-level window onto | |
| 474 // the taskbar. In order for multiple stacked panels to appear as one, this | |
| 475 // bit needs to be cleared. | |
| 476 int value = ::GetWindowLong(native_panel_window, GWL_EXSTYLE); | |
| 477 ::SetWindowLong( | |
| 478 native_panel_window, | |
| 479 GWL_EXSTYLE, | |
| 480 native_stack_window ? (value & ~WS_EX_APPWINDOW) | |
| 481 : (value | WS_EX_APPWINDOW)); | |
| 482 | |
| 483 // All the windows that share the same owner window will appear as a single | |
| 484 // window on the taskbar. | |
| 485 ::SetWindowLongPtr(native_panel_window, | |
| 486 GWLP_HWNDPARENT, | |
| 487 reinterpret_cast<LONG_PTR>(native_stack_window)); | |
| 488 | |
| 489 // Make sure the background stack window always stays behind the panel window. | |
| 490 if (native_stack_window) { | |
| 491 ::SetWindowPos(native_stack_window, native_panel_window, 0, 0, 0, 0, | |
| 492 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); | |
| 493 } | |
| 494 | |
| 495 #else | |
| 496 NOTIMPLEMENTED(); | |
| 497 #endif | |
| 498 } | |
| 499 | |
| 500 views::Widget* PanelStackView::CreateWindowWithBounds(const gfx::Rect& bounds) { | |
| 501 PanelStackWindow* stack_window = new PanelStackWindow(bounds, delegate_); | |
| 502 views::Widget* window = stack_window->GetWidget(); | |
| 503 | |
| 504 #if defined(OS_WIN) | |
| 505 DCHECK(!panels_.empty()); | |
| 506 Panel* panel = panels_.front(); | |
| 507 ui::win::SetAppIdForWindow( | |
| 508 shell_integration::win::GetAppModelIdForProfile( | |
| 509 base::UTF8ToWide(panel->app_name()), panel->profile()->GetPath()), | |
| 510 views::HWNDForWidget(window)); | |
| 511 | |
| 512 // Remove the filter for old window in case that we're recreating the window. | |
| 513 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); | |
| 514 | |
| 515 // Listen to WM_MOVING message in order to move all panels windows on top of | |
| 516 // the background window altogether when the background window is being moved | |
| 517 // by the user. | |
| 518 ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window), this); | |
| 519 #endif | |
| 520 | |
| 521 return window; | |
| 522 } | |
| 523 | |
| 524 void PanelStackView::EnsureWindowCreated() { | |
| 525 if (window_) | |
| 526 return; | |
| 527 | |
| 528 // Empty size is not allowed so a temporary small size is passed. SetBounds | |
| 529 // will be called later to update the bounds. | |
| 530 window_ = CreateWindowWithBounds(gfx::Rect(0, 0, 1, 1)); | |
| 531 | |
| 532 #if defined(OS_WIN) | |
| 533 if (base::win::GetVersion() >= base::win::VERSION_WIN7) { | |
| 534 HWND native_window = views::HWNDForWidget(window_); | |
| 535 thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, this)); | |
| 536 thumbnailer_->Start(); | |
| 537 } | |
| 538 #endif | |
| 539 } | |
| 540 | |
| 541 #if defined(OS_WIN) | |
| 542 bool PanelStackView::FilterMessage(HWND hwnd, | |
| 543 UINT message, | |
| 544 WPARAM w_param, | |
| 545 LPARAM l_param, | |
| 546 LRESULT* l_result) { | |
| 547 switch (message) { | |
| 548 case WM_MOVING: | |
| 549 // When the background window is being moved by the user, all panels | |
| 550 // should also move. | |
| 551 gfx::Rect new_stack_bounds(*(reinterpret_cast<LPRECT>(l_param))); | |
| 552 MovePanelsBy( | |
| 553 new_stack_bounds.origin() - panels_.front()->GetBounds().origin()); | |
| 554 break; | |
| 555 } | |
| 556 return false; | |
| 557 } | |
| 558 | |
| 559 std::vector<HWND> PanelStackView::GetSnapshotWindowHandles() const { | |
| 560 std::vector<HWND> native_panel_windows; | |
| 561 for (Panels::const_iterator iter = panels_.begin(); | |
| 562 iter != panels_.end(); ++iter) { | |
| 563 Panel* panel = *iter; | |
| 564 native_panel_windows.push_back( | |
| 565 views::HWNDForWidget( | |
| 566 static_cast<PanelView*>(panel->native_panel())->window())); | |
| 567 } | |
| 568 return native_panel_windows; | |
| 569 } | |
| 570 | |
| 571 void PanelStackView::RefreshLivePreviewThumbnail() { | |
| 572 // Don't refresh the thumbnail when the stack window is system minimized | |
| 573 // because the snapshot could not be retrieved. | |
| 574 if (!thumbnailer_.get() || IsMinimized()) | |
| 575 return; | |
| 576 thumbnailer_->InvalidateSnapshot(); | |
| 577 } | |
| 578 | |
| 579 void PanelStackView::DeferUpdateNativeWindowBounds(HDWP defer_window_pos_info, | |
| 580 views::Widget* window, | |
| 581 const gfx::Rect& bounds) { | |
| 582 ::DeferWindowPos(defer_window_pos_info, | |
| 583 views::HWNDForWidget(window), | |
| 584 NULL, | |
| 585 bounds.x(), | |
| 586 bounds.y(), | |
| 587 bounds.width(), | |
| 588 bounds.height(), | |
| 589 SWP_NOACTIVATE | SWP_NOZORDER); | |
| 590 } | |
| 591 #endif | |
| OLD | NEW |