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 |