19439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// Copyright (c) 2013 The Chromium Authors. All rights reserved. 29439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// Use of this source code is governed by a BSD-style license that can be 39439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// found in the LICENSE file. 49439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 59439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/views/panels/panel_stack_view.h" 69439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 79439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "base/logging.h" 89439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "base/strings/utf_string_conversions.h" 99439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/profiles/profile.h" 109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/panels/panel.h" 119439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/panels/panel_manager.h" 129439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/panels/stacked_panel_collection.h" 139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/ui/views/panels/panel_view.h" 149439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/gfx/animation/linear_animation.h" 159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/gfx/image/image_skia.h" 169439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/gfx/rect.h" 179439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/views/widget/widget.h" 189439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 199439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#if defined(OS_WIN) 209439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "base/win/windows_version.h" 219439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "chrome/browser/shell_integration.h" 229439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/base/win/shell.h" 239439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#include "ui/views/win/hwnd_util.h" 249439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly#endif 259439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 269439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellynamespace { 279439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// These values are experimental and subjective. 289439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyconst int kDefaultFramerateHz = 50; 299439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyconst int kSetBoundsAnimationMs = 180; 309439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 319439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly// The widget window that acts as a background window for the stack of panels. 329439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyclass PanelStackWindow : public views::WidgetObserver, 339439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public views::WidgetDelegateView { 349439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly public: 359439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly PanelStackWindow(const gfx::Rect& bounds, 369439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly NativePanelStackWindowDelegate* delegate); 3705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun virtual ~PanelStackWindow(); 389439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 399439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Overridden from views::WidgetDelegate: 4005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun virtual base::string16 GetWindowTitle() const OVERRIDE; 4105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun virtual gfx::ImageSkia GetWindowAppIcon() OVERRIDE; 429439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly virtual gfx::ImageSkia GetWindowIcon() OVERRIDE; 439439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly virtual views::Widget* GetWidget() OVERRIDE; 4405ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun virtual const views::Widget* GetWidget() const OVERRIDE; 4505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 469439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly // Overridden from views::WidgetObserver: 479439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly virtual void OnWidgetClosing(views::Widget* widget) OVERRIDE; 489439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE; 499439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 5005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun private: 5105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun views::Widget* window_; // Weak pointer, own us. 5205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun NativePanelStackWindowDelegate* delegate_; // Weak pointer. 5305ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 5405ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun DISALLOW_COPY_AND_ASSIGN(PanelStackWindow); 5505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun}; 5605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 5705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao LiejunPanelStackWindow::PanelStackWindow(const gfx::Rect& bounds, 589439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly NativePanelStackWindowDelegate* delegate) 599439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly : window_(NULL), 609439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly delegate_(delegate) { 619439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly window_ = new views::Widget; 629439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); 639439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly params.delegate = this; 6405ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun params.remove_standard_frame = true; 6505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun params.bounds = bounds; 6605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun window_->Init(params); 6705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); 6805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun window_->set_focus_on_creation(false); 6905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun window_->AddObserver(this); 7005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun window_->ShowInactive(); 7105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun} 729439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 739439a7fe517b858bc5e5c654b459315e4722feb2Nick PellyPanelStackWindow::~PanelStackWindow() { 749439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 7505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 7605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejunbase::string16 PanelStackWindow::GetWindowTitle() const { 7705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun return delegate_ ? delegate_->GetTitle() : base::string16(); 789439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 7905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 8005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejungfx::ImageSkia PanelStackWindow::GetWindowAppIcon() { 812e0da96e757a977154063f980d3f4e1abd41cf09Nick Pelly if (delegate_) { 829439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly gfx::Image app_icon = delegate_->GetIcon(); 839439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly if (!app_icon.IsEmpty()) 849439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return *app_icon.ToImageSkia(); 859439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly } 869439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return gfx::ImageSkia(); 879439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 889439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 899439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellygfx::ImageSkia PanelStackWindow::GetWindowIcon() { 9005ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun return GetWindowAppIcon(); 9105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun} 9205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 9305ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejunviews::Widget* PanelStackWindow::GetWidget() { 949439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return window_; 9505ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun} 9605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 9705ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejunconst views::Widget* PanelStackWindow::GetWidget() const { 9805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun return window_; 9905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun} 1009439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1013998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejunvoid PanelStackWindow::OnWidgetClosing(views::Widget* widget) { 1023998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun delegate_ = NULL; 1039439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 1049439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 1059439a7fe517b858bc5e5c654b459315e4722feb2Nick Pellyvoid PanelStackWindow::OnWidgetDestroying(views::Widget* widget) { 10605ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun window_ = NULL; 1079439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly} 10805ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun 10905ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun} 1109439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly 11105ff98bbefda39b9ff26f8bca132cfd0248745c6Tao Liejun// static 11205ff98bbefda39b9ff26f8bca132cfd0248745c6Tao LiejunNativePanelStackWindow* NativePanelStackWindow::Create( 1139439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly NativePanelStackWindowDelegate* delegate) { 1143998bf009acaf8cde4d7f837f8b8e41ae0a65141Tao Liejun#if defined(OS_WIN) 1159439a7fe517b858bc5e5c654b459315e4722feb2Nick Pelly return new PanelStackView(delegate); 116#else 117 NOTIMPLEMENTED(); 118 return NULL; 119#endif 120} 121 122PanelStackView::PanelStackView(NativePanelStackWindowDelegate* delegate) 123 : delegate_(delegate), 124 window_(NULL), 125 is_drawing_attention_(false), 126 animate_bounds_updates_(false), 127 bounds_updates_started_(false) { 128 DCHECK(delegate); 129 views::WidgetFocusManager::GetInstance()->AddFocusChangeListener(this); 130} 131 132PanelStackView::~PanelStackView() { 133#if defined(OS_WIN) 134 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); 135#endif 136} 137 138void PanelStackView::Close() { 139 delegate_ = NULL; 140 if (bounds_animator_) 141 bounds_animator_.reset(); 142 if (window_) 143 window_->Close(); 144 views::WidgetFocusManager::GetInstance()->RemoveFocusChangeListener(this); 145} 146 147void PanelStackView::AddPanel(Panel* panel) { 148 panels_.push_back(panel); 149 150 EnsureWindowCreated(); 151 MakeStackWindowOwnPanelWindow(panel, this); 152 UpdateStackWindowBounds(); 153 154 window_->UpdateWindowTitle(); 155 window_->UpdateWindowIcon(); 156} 157 158void PanelStackView::RemovePanel(Panel* panel) { 159 if (IsAnimatingPanelBounds()) { 160 // This panel is gone. 161 bounds_updates_.erase(panel); 162 163 // Abort the ongoing animation. 164 bounds_animator_->Stop(); 165 } 166 167 panels_.remove(panel); 168 169 MakeStackWindowOwnPanelWindow(panel, NULL); 170 UpdateStackWindowBounds(); 171} 172 173void PanelStackView::MergeWith(NativePanelStackWindow* another) { 174 PanelStackView* another_stack = static_cast<PanelStackView*>(another); 175 176 for (Panels::const_iterator iter = another_stack->panels_.begin(); 177 iter != another_stack->panels_.end(); ++iter) { 178 Panel* panel = *iter; 179 panels_.push_back(panel); 180 MakeStackWindowOwnPanelWindow(panel, this); 181 } 182 another_stack->panels_.clear(); 183 184 UpdateStackWindowBounds(); 185} 186 187bool PanelStackView::IsEmpty() const { 188 return panels_.empty(); 189} 190 191bool PanelStackView::HasPanel(Panel* panel) const { 192 return std::find(panels_.begin(), panels_.end(), panel) != panels_.end(); 193} 194 195void PanelStackView::MovePanelsBy(const gfx::Vector2d& delta) { 196 BeginBatchUpdatePanelBounds(false); 197 for (Panels::const_iterator iter = panels_.begin(); 198 iter != panels_.end(); ++iter) { 199 Panel* panel = *iter; 200 AddPanelBoundsForBatchUpdate(panel, panel->GetBounds() + delta); 201 } 202 EndBatchUpdatePanelBounds(); 203} 204 205void PanelStackView::BeginBatchUpdatePanelBounds(bool animate) { 206 // If the batch animation is still in progress, continue the animation 207 // with the new target bounds even we want to update the bounds instantly 208 // this time. 209 if (!bounds_updates_started_) { 210 animate_bounds_updates_ = animate; 211 bounds_updates_started_ = true; 212 } 213} 214 215void PanelStackView::AddPanelBoundsForBatchUpdate(Panel* panel, 216 const gfx::Rect& new_bounds) { 217 DCHECK(bounds_updates_started_); 218 219 // No need to track it if no change is needed. 220 if (panel->GetBounds() == new_bounds) 221 return; 222 223 // Old bounds are stored as the map value. 224 bounds_updates_[panel] = panel->GetBounds(); 225 226 // New bounds are directly applied to the valued stored in native panel 227 // window. 228 static_cast<PanelView*>(panel->native_panel())->set_cached_bounds_directly( 229 new_bounds); 230} 231 232void PanelStackView::EndBatchUpdatePanelBounds() { 233 DCHECK(bounds_updates_started_); 234 235 if (bounds_updates_.empty() || !animate_bounds_updates_) { 236 if (!bounds_updates_.empty()) { 237 UpdatePanelsBounds(); 238 bounds_updates_.clear(); 239 } 240 241 bounds_updates_started_ = false; 242 NotifyBoundsUpdateCompleted(); 243 return; 244 } 245 246 bounds_animator_.reset(new gfx::LinearAnimation( 247 PanelManager::AdjustTimeInterval(kSetBoundsAnimationMs), 248 kDefaultFramerateHz, 249 this)); 250 bounds_animator_->Start(); 251} 252 253void PanelStackView::NotifyBoundsUpdateCompleted() { 254 delegate_->PanelBoundsBatchUpdateCompleted(); 255 256#if defined(OS_WIN) 257 // Refresh the thumbnail each time when any bounds updates are done. 258 RefreshLivePreviewThumbnail(); 259#endif 260} 261 262bool PanelStackView::IsAnimatingPanelBounds() const { 263 return bounds_updates_started_ && animate_bounds_updates_; 264} 265 266void PanelStackView::Minimize() { 267#if defined(OS_WIN) 268 // When the stack window is minimized by the system, its snapshot could not 269 // be obtained. We need to capture the snapshot before the minimization. 270 if (thumbnailer_) 271 thumbnailer_->CaptureSnapshot(); 272#endif 273 274 window_->Minimize(); 275} 276 277bool PanelStackView::IsMinimized() const { 278 return window_ ? window_->IsMinimized() : false; 279} 280 281void PanelStackView::DrawSystemAttention(bool draw_attention) { 282 // The underlying call of FlashFrame, FlashWindowEx, seems not to work 283 // correctly if it is called more than once consecutively. 284 if (draw_attention == is_drawing_attention_) 285 return; 286 is_drawing_attention_ = draw_attention; 287 288#if defined(OS_WIN) 289 // Refresh the thumbnail when a panel could change something for the 290 // attention. 291 RefreshLivePreviewThumbnail(); 292 293 if (draw_attention) { 294 // The default implementation of Widget::FlashFrame only flashes 5 times. 295 // We need more than that. 296 FLASHWINFO fwi; 297 fwi.cbSize = sizeof(fwi); 298 fwi.hwnd = views::HWNDForWidget(window_); 299 fwi.dwFlags = FLASHW_ALL; 300 fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention; 301 fwi.dwTimeout = 0; 302 ::FlashWindowEx(&fwi); 303 } else { 304 // Calling FlashWindowEx with FLASHW_STOP flag does not always work. 305 // Occasionally the taskbar icon could still remain in the flashed state. 306 // To work around this problem, we recreate the underlying window. 307 views::Widget* old_window = window_; 308 window_ = CreateWindowWithBounds(GetStackWindowBounds()); 309 310 // New background window should also be minimized if the old one is. 311 if (old_window->IsMinimized()) 312 window_->Minimize(); 313 314 // Make sure the new background window stays at the same z-order as the old 315 // one. 316 ::SetWindowPos(views::HWNDForWidget(window_), 317 views::HWNDForWidget(old_window), 318 0, 0, 0, 0, 319 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 320 for (Panels::const_iterator iter = panels_.begin(); 321 iter != panels_.end(); ++iter) { 322 MakeStackWindowOwnPanelWindow(*iter, this); 323 } 324 325 // Serve the snapshot to the new backgroud window. 326 if (thumbnailer_.get()) 327 thumbnailer_->ReplaceWindow(views::HWNDForWidget(window_)); 328 329 window_->UpdateWindowTitle(); 330 window_->UpdateWindowIcon(); 331 old_window->Close(); 332 } 333#else 334 window_->FlashFrame(draw_attention); 335#endif 336} 337 338void PanelStackView::OnPanelActivated(Panel* panel) { 339 // Nothing to do. 340} 341 342void PanelStackView::OnNativeFocusChange(gfx::NativeView focused_before, 343 gfx::NativeView focused_now) { 344 // When the user selects the stacked panels via ALT-TAB or WIN-TAB, the 345 // background stack window, instead of the foreground panel window, receives 346 // WM_SETFOCUS message. To deal with this, we listen to the focus change event 347 // and activate the most recently active panel. 348 // Note that OnNativeFocusChange might be called when window_ has not be 349 // created yet. 350#if defined(OS_WIN) 351 if (!panels_.empty() && window_ && focused_now == window_->GetNativeView()) { 352 Panel* panel_to_focus = 353 panels_.front()->stack()->most_recently_active_panel(); 354 if (panel_to_focus) 355 panel_to_focus->Activate(); 356 } 357#endif 358} 359 360void PanelStackView::AnimationEnded(const gfx::Animation* animation) { 361 bounds_updates_started_ = false; 362 363 PanelManager* panel_manager = PanelManager::GetInstance(); 364 for (BoundsUpdates::const_iterator iter = bounds_updates_.begin(); 365 iter != bounds_updates_.end(); ++iter) { 366 panel_manager->OnPanelAnimationEnded(iter->first); 367 } 368 bounds_updates_.clear(); 369 370 NotifyBoundsUpdateCompleted(); 371} 372 373void PanelStackView::AnimationCanceled(const gfx::Animation* animation) { 374 // When the animation is aborted due to something like one of panels is gone, 375 // update panels to their taget bounds immediately. 376 UpdatePanelsBounds(); 377 378 AnimationEnded(animation); 379} 380 381void PanelStackView::AnimationProgressed(const gfx::Animation* animation) { 382 UpdatePanelsBounds(); 383} 384 385void PanelStackView::UpdatePanelsBounds() { 386#if defined(OS_WIN) 387 // Add an extra count for the background stack window. 388 HDWP defer_update = ::BeginDeferWindowPos(bounds_updates_.size() + 1); 389#endif 390 391 // Update the bounds for each panel in the update list. 392 gfx::Rect enclosing_bounds; 393 for (BoundsUpdates::const_iterator iter = bounds_updates_.begin(); 394 iter != bounds_updates_.end(); ++iter) { 395 Panel* panel = iter->first; 396 gfx::Rect target_bounds = panel->GetBounds(); 397 gfx::Rect current_bounds; 398 if (bounds_animator_ && bounds_animator_->is_animating()) { 399 current_bounds = bounds_animator_->CurrentValueBetween( 400 iter->second, target_bounds); 401 } else { 402 current_bounds = target_bounds; 403 } 404 405 PanelView* panel_view = static_cast<PanelView*>(panel->native_panel()); 406#if defined(OS_WIN) 407 DeferUpdateNativeWindowBounds(defer_update, 408 panel_view->window(), 409 current_bounds); 410#else 411 panel_view->SetPanelBoundsInstantly(current_bounds); 412#endif 413 414 enclosing_bounds = UnionRects(enclosing_bounds, current_bounds); 415 } 416 417 // Compute the stack window bounds that enclose those panels that are not 418 // in the batch update list. 419 for (Panels::const_iterator iter = panels_.begin(); 420 iter != panels_.end(); ++iter) { 421 Panel* panel = *iter; 422 if (bounds_updates_.find(panel) == bounds_updates_.end()) 423 enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds()); 424 } 425 426 // Update the bounds of the background stack window. 427#if defined(OS_WIN) 428 DeferUpdateNativeWindowBounds(defer_update, window_, enclosing_bounds); 429#else 430 window_->SetBounds(enclosing_bounds); 431#endif 432 433#if defined(OS_WIN) 434 ::EndDeferWindowPos(defer_update); 435#endif 436} 437 438gfx::Rect PanelStackView::GetStackWindowBounds() const { 439 gfx::Rect enclosing_bounds; 440 for (Panels::const_iterator iter = panels_.begin(); 441 iter != panels_.end(); ++iter) { 442 Panel* panel = *iter; 443 enclosing_bounds = UnionRects(enclosing_bounds, panel->GetBounds()); 444 } 445 return enclosing_bounds; 446} 447 448void PanelStackView::UpdateStackWindowBounds() { 449 window_->SetBounds(GetStackWindowBounds()); 450 451#if defined(OS_WIN) 452 // Refresh the thumbnail each time whne the stack window is changed, due to 453 // adding or removing a panel. 454 RefreshLivePreviewThumbnail(); 455#endif 456} 457 458// static 459void PanelStackView::MakeStackWindowOwnPanelWindow( 460 Panel* panel, PanelStackView* stack_window) { 461#if defined(OS_WIN) 462 // The panel widget window might already be gone when a panel is closed. 463 views::Widget* panel_window = 464 static_cast<PanelView*>(panel->native_panel())->window(); 465 if (!panel_window) 466 return; 467 468 HWND native_panel_window = views::HWNDForWidget(panel_window); 469 HWND native_stack_window = 470 stack_window ? views::HWNDForWidget(stack_window->window_) : NULL; 471 472 // The extended style WS_EX_APPWINDOW is used to force a top-level window onto 473 // the taskbar. In order for multiple stacked panels to appear as one, this 474 // bit needs to be cleared. 475 int value = ::GetWindowLong(native_panel_window, GWL_EXSTYLE); 476 ::SetWindowLong( 477 native_panel_window, 478 GWL_EXSTYLE, 479 native_stack_window ? (value & ~WS_EX_APPWINDOW) 480 : (value | WS_EX_APPWINDOW)); 481 482 // All the windows that share the same owner window will appear as a single 483 // window on the taskbar. 484 ::SetWindowLongPtr(native_panel_window, 485 GWLP_HWNDPARENT, 486 reinterpret_cast<LONG_PTR>(native_stack_window)); 487 488 // Make sure the background stack window always stays behind the panel window. 489 if (native_stack_window) { 490 ::SetWindowPos(native_stack_window, native_panel_window, 0, 0, 0, 0, 491 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); 492 } 493 494#else 495 NOTIMPLEMENTED(); 496#endif 497} 498 499views::Widget* PanelStackView::CreateWindowWithBounds(const gfx::Rect& bounds) { 500 PanelStackWindow* stack_window = new PanelStackWindow(bounds, delegate_); 501 views::Widget* window = stack_window->GetWidget(); 502 503#if defined(OS_WIN) 504 DCHECK(!panels_.empty()); 505 Panel* panel = panels_.front(); 506 ui::win::SetAppIdForWindow( 507 ShellIntegration::GetAppModelIdForProfile( 508 base::UTF8ToWide(panel->app_name()), panel->profile()->GetPath()), 509 views::HWNDForWidget(window)); 510 511 // Remove the filter for old window in case that we're recreating the window. 512 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); 513 514 // Listen to WM_MOVING message in order to move all panels windows on top of 515 // the background window altogether when the background window is being moved 516 // by the user. 517 ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window), this); 518#endif 519 520 return window; 521} 522 523void PanelStackView::EnsureWindowCreated() { 524 if (window_) 525 return; 526 527 // Empty size is not allowed so a temporary small size is passed. SetBounds 528 // will be called later to update the bounds. 529 window_ = CreateWindowWithBounds(gfx::Rect(0, 0, 1, 1)); 530 531#if defined(OS_WIN) 532 if (base::win::GetVersion() >= base::win::VERSION_WIN7) { 533 HWND native_window = views::HWNDForWidget(window_); 534 thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, this)); 535 thumbnailer_->Start(); 536 } 537#endif 538} 539 540#if defined(OS_WIN) 541bool PanelStackView::FilterMessage(HWND hwnd, 542 UINT message, 543 WPARAM w_param, 544 LPARAM l_param, 545 LRESULT* l_result) { 546 switch (message) { 547 case WM_MOVING: 548 // When the background window is being moved by the user, all panels 549 // should also move. 550 gfx::Rect new_stack_bounds(*(reinterpret_cast<LPRECT>(l_param))); 551 MovePanelsBy( 552 new_stack_bounds.origin() - panels_.front()->GetBounds().origin()); 553 break; 554 } 555 return false; 556} 557 558std::vector<HWND> PanelStackView::GetSnapshotWindowHandles() const { 559 std::vector<HWND> native_panel_windows; 560 for (Panels::const_iterator iter = panels_.begin(); 561 iter != panels_.end(); ++iter) { 562 Panel* panel = *iter; 563 native_panel_windows.push_back( 564 views::HWNDForWidget( 565 static_cast<PanelView*>(panel->native_panel())->window())); 566 } 567 return native_panel_windows; 568} 569 570void PanelStackView::RefreshLivePreviewThumbnail() { 571 // Don't refresh the thumbnail when the stack window is system minimized 572 // because the snapshot could not be retrieved. 573 if (!thumbnailer_.get() || IsMinimized()) 574 return; 575 thumbnailer_->InvalidateSnapshot(); 576} 577 578void PanelStackView::DeferUpdateNativeWindowBounds(HDWP defer_window_pos_info, 579 views::Widget* window, 580 const gfx::Rect& bounds) { 581 ::DeferWindowPos(defer_window_pos_info, 582 views::HWNDForWidget(window), 583 NULL, 584 bounds.x(), 585 bounds.y(), 586 bounds.width(), 587 bounds.height(), 588 SWP_NOACTIVATE | SWP_NOZORDER); 589} 590#endif 591