OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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_view.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <map> | |
9 #include <utility> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/macros.h" | |
13 #include "base/run_loop.h" | |
14 #include "base/strings/utf_string_conversions.h" | |
15 #include "build/build_config.h" | |
16 #include "chrome/app/chrome_command_ids.h" | |
17 #include "chrome/browser/lifetime/keep_alive_types.h" | |
18 #include "chrome/browser/lifetime/scoped_keep_alive.h" | |
19 #include "chrome/browser/profiles/profile.h" | |
20 #include "chrome/browser/ui/panels/panel.h" | |
21 #include "chrome/browser/ui/panels/panel_bounds_animation.h" | |
22 #include "chrome/browser/ui/panels/panel_manager.h" | |
23 #include "chrome/browser/ui/panels/stacked_panel_collection.h" | |
24 #include "chrome/browser/ui/views/panels/panel_frame_view.h" | |
25 #include "content/public/browser/render_view_host.h" | |
26 #include "content/public/browser/render_widget_host_view.h" | |
27 #include "content/public/browser/web_contents.h" | |
28 #include "ui/content_accelerators/accelerator_util.h" | |
29 #include "ui/display/screen.h" | |
30 #include "ui/gfx/image/image.h" | |
31 #include "ui/gfx/path.h" | |
32 #include "ui/views/controls/button/image_button.h" | |
33 #include "ui/views/controls/webview/webview.h" | |
34 #include "ui/views/widget/widget.h" | |
35 | |
36 #if defined(OS_WIN) | |
37 #include "base/win/windows_version.h" | |
38 #include "chrome/browser/shell_integration_win.h" | |
39 #include "chrome/browser/ui/views/panels/taskbar_window_thumbnailer_win.h" | |
40 #include "ui/base/win/shell.h" | |
41 #include "ui/gfx/icon_util.h" | |
42 #include "ui/views/win/hwnd_util.h" | |
43 #endif | |
44 | |
45 #if defined(USE_X11) && !defined(OS_CHROMEOS) | |
46 #include "chrome/browser/shell_integration_linux.h" | |
47 #include "chrome/browser/ui/views/panels/x11_panel_resizer.h" | |
48 #include "chrome/browser/web_applications/web_app.h" | |
49 #include "ui/aura/window.h" | |
50 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" | |
51 #endif | |
52 | |
53 namespace { | |
54 | |
55 #if defined(OS_WIN) | |
56 // If the height of a stacked panel shrinks below this threshold during the | |
57 // user resizing, it will be treated as minimized. | |
58 const int kStackedPanelHeightShrinkThresholdToBecomeMinimized = | |
59 panel::kTitlebarHeight + 20; | |
60 #endif | |
61 | |
62 // Supported accelerators. | |
63 // Note: We can't use the accelerator table defined in chrome/browser/ui/views | |
64 // due to checkdeps violation. | |
65 struct AcceleratorMapping { | |
66 ui::KeyboardCode keycode; | |
67 int modifiers; | |
68 int command_id; | |
69 }; | |
70 const AcceleratorMapping kPanelAcceleratorMap[] = { | |
71 { ui::VKEY_W, ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, | |
72 { ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_CLOSE_WINDOW }, | |
73 { ui::VKEY_F4, ui::EF_ALT_DOWN, IDC_CLOSE_WINDOW }, | |
74 { ui::VKEY_R, ui::EF_CONTROL_DOWN, IDC_RELOAD }, | |
75 { ui::VKEY_F5, ui::EF_NONE, IDC_RELOAD }, | |
76 { ui::VKEY_R, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, | |
77 IDC_RELOAD_BYPASSING_CACHE }, | |
78 { ui::VKEY_F5, ui::EF_CONTROL_DOWN, IDC_RELOAD_BYPASSING_CACHE }, | |
79 { ui::VKEY_F5, ui::EF_SHIFT_DOWN, IDC_RELOAD_BYPASSING_CACHE }, | |
80 { ui::VKEY_ESCAPE, ui::EF_NONE, IDC_STOP }, | |
81 { ui::VKEY_OEM_MINUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, | |
82 { ui::VKEY_SUBTRACT, ui::EF_CONTROL_DOWN, IDC_ZOOM_MINUS }, | |
83 { ui::VKEY_0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, | |
84 { ui::VKEY_NUMPAD0, ui::EF_CONTROL_DOWN, IDC_ZOOM_NORMAL }, | |
85 { ui::VKEY_OEM_PLUS, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, | |
86 { ui::VKEY_ADD, ui::EF_CONTROL_DOWN, IDC_ZOOM_PLUS }, | |
87 { ui::VKEY_I, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, IDC_DEV_TOOLS }, | |
88 { ui::VKEY_J, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, | |
89 IDC_DEV_TOOLS_CONSOLE }, | |
90 }; | |
91 | |
92 const std::map<ui::Accelerator, int>& GetAcceleratorTable() { | |
93 static std::map<ui::Accelerator, int>* accelerators = NULL; | |
94 if (!accelerators) { | |
95 accelerators = new std::map<ui::Accelerator, int>(); | |
96 for (size_t i = 0; i < arraysize(kPanelAcceleratorMap); ++i) { | |
97 ui::Accelerator accelerator(kPanelAcceleratorMap[i].keycode, | |
98 kPanelAcceleratorMap[i].modifiers); | |
99 (*accelerators)[accelerator] = kPanelAcceleratorMap[i].command_id; | |
100 } | |
101 } | |
102 return *accelerators; | |
103 } | |
104 | |
105 // NativePanelTesting implementation. | |
106 class NativePanelTestingViews : public NativePanelTesting { | |
107 public: | |
108 explicit NativePanelTestingViews(PanelView* panel_view); | |
109 ~NativePanelTestingViews() override; | |
110 | |
111 private: | |
112 void PressLeftMouseButtonTitlebar(const gfx::Point& mouse_location, | |
113 panel::ClickModifier modifier) override; | |
114 void ReleaseMouseButtonTitlebar(panel::ClickModifier modifier) override; | |
115 void DragTitlebar(const gfx::Point& mouse_location) override; | |
116 void CancelDragTitlebar() override; | |
117 void FinishDragTitlebar() override; | |
118 bool VerifyDrawingAttention() const override; | |
119 bool VerifyActiveState(bool is_active) override; | |
120 bool VerifyAppIcon() const override; | |
121 bool VerifySystemMinimizeState() const override; | |
122 bool IsWindowVisible() const override; | |
123 bool IsWindowSizeKnown() const override; | |
124 bool IsAnimatingBounds() const override; | |
125 bool IsButtonVisible(panel::TitlebarButtonType button_type) const override; | |
126 panel::CornerStyle GetWindowCornerStyle() const override; | |
127 bool EnsureApplicationRunOnForeground() override; | |
128 | |
129 PanelView* panel_view_; | |
130 }; | |
131 | |
132 NativePanelTestingViews::NativePanelTestingViews(PanelView* panel_view) | |
133 : panel_view_(panel_view) { | |
134 } | |
135 | |
136 NativePanelTestingViews::~NativePanelTestingViews() { | |
137 } | |
138 | |
139 void NativePanelTestingViews::PressLeftMouseButtonTitlebar( | |
140 const gfx::Point& mouse_location, panel::ClickModifier modifier) { | |
141 panel_view_->OnTitlebarMousePressed(mouse_location); | |
142 } | |
143 | |
144 void NativePanelTestingViews::ReleaseMouseButtonTitlebar( | |
145 panel::ClickModifier modifier) { | |
146 panel_view_->OnTitlebarMouseReleased(modifier); | |
147 } | |
148 | |
149 void NativePanelTestingViews::DragTitlebar(const gfx::Point& mouse_location) { | |
150 panel_view_->OnTitlebarMouseDragged(mouse_location); | |
151 } | |
152 | |
153 void NativePanelTestingViews::CancelDragTitlebar() { | |
154 panel_view_->OnTitlebarMouseCaptureLost(); | |
155 } | |
156 | |
157 void NativePanelTestingViews::FinishDragTitlebar() { | |
158 panel_view_->OnTitlebarMouseReleased(panel::NO_MODIFIER); | |
159 } | |
160 | |
161 bool NativePanelTestingViews::VerifyDrawingAttention() const { | |
162 base::RunLoop().RunUntilIdle(); | |
163 return panel_view_->GetFrameView()->GetPaintState() == | |
164 PanelFrameView::PAINT_FOR_ATTENTION; | |
165 } | |
166 | |
167 bool NativePanelTestingViews::VerifyActiveState(bool is_active) { | |
168 return panel_view_->GetFrameView()->GetPaintState() == | |
169 (is_active ? PanelFrameView::PAINT_AS_ACTIVE | |
170 : PanelFrameView::PAINT_AS_INACTIVE); | |
171 } | |
172 | |
173 bool NativePanelTestingViews::VerifyAppIcon() const { | |
174 #if defined(OS_WIN) | |
175 // We only care about Windows 7 and later. | |
176 if (base::win::GetVersion() < base::win::VERSION_WIN7) | |
177 return true; | |
178 | |
179 HWND native_window = views::HWNDForWidget(panel_view_->window()); | |
180 HICON app_icon = reinterpret_cast<HICON>( | |
181 ::SendMessage(native_window, WM_GETICON, ICON_BIG, 0L)); | |
182 if (!app_icon) | |
183 return false; | |
184 std::unique_ptr<SkBitmap> bitmap(IconUtil::CreateSkBitmapFromHICON(app_icon)); | |
185 return bitmap.get() && | |
186 bitmap->width() == panel::kPanelAppIconSize && | |
187 bitmap->height() == panel::kPanelAppIconSize; | |
188 #else | |
189 return true; | |
190 #endif | |
191 } | |
192 | |
193 bool NativePanelTestingViews::VerifySystemMinimizeState() const { | |
194 #if defined(OS_WIN) | |
195 HWND native_window = views::HWNDForWidget(panel_view_->window()); | |
196 WINDOWPLACEMENT placement; | |
197 if (!::GetWindowPlacement(native_window, &placement)) | |
198 return false; | |
199 if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED) | |
200 return true; | |
201 | |
202 // If the panel window has owner window, as in stacked mode, check its owner | |
203 // window. Note that owner window, instead of parent window, is returned | |
204 // though GWL_HWNDPARENT contains 'parent'. | |
205 HWND owner_window = | |
206 reinterpret_cast<HWND>(::GetWindowLongPtr(native_window, | |
207 GWLP_HWNDPARENT)); | |
208 if (!owner_window || !::GetWindowPlacement(owner_window, &placement)) | |
209 return false; | |
210 return placement.showCmd == SW_MINIMIZE || | |
211 placement.showCmd == SW_SHOWMINIMIZED; | |
212 #else | |
213 return true; | |
214 #endif | |
215 } | |
216 | |
217 bool NativePanelTestingViews::IsWindowVisible() const { | |
218 return panel_view_->window()->IsVisible(); | |
219 } | |
220 | |
221 bool NativePanelTestingViews::IsWindowSizeKnown() const { | |
222 return true; | |
223 } | |
224 | |
225 bool NativePanelTestingViews::IsAnimatingBounds() const { | |
226 return panel_view_->IsAnimatingBounds(); | |
227 } | |
228 | |
229 bool NativePanelTestingViews::IsButtonVisible( | |
230 panel::TitlebarButtonType button_type) const { | |
231 PanelFrameView* frame_view = panel_view_->GetFrameView(); | |
232 | |
233 switch (button_type) { | |
234 case panel::CLOSE_BUTTON: | |
235 return frame_view->close_button()->visible(); | |
236 case panel::MINIMIZE_BUTTON: | |
237 return frame_view->minimize_button()->visible(); | |
238 case panel::RESTORE_BUTTON: | |
239 return frame_view->restore_button()->visible(); | |
240 default: | |
241 NOTREACHED(); | |
242 } | |
243 return false; | |
244 } | |
245 | |
246 panel::CornerStyle NativePanelTestingViews::GetWindowCornerStyle() const { | |
247 return panel_view_->GetFrameView()->corner_style(); | |
248 } | |
249 | |
250 bool NativePanelTestingViews::EnsureApplicationRunOnForeground() { | |
251 // Not needed on views. | |
252 return true; | |
253 } | |
254 | |
255 } // namespace | |
256 | |
257 // static | |
258 NativePanel* Panel::CreateNativePanel(Panel* panel, | |
259 const gfx::Rect& bounds, | |
260 bool always_on_top) { | |
261 return new PanelView(panel, bounds, always_on_top); | |
262 } | |
263 | |
264 // The panel window has to be created as always-on-top. We cannot create it | |
265 // as non-always-on-top and then change it to always-on-top because Windows | |
266 // system might deny making a window always-on-top if the application is not | |
267 // a foreground application. | |
268 PanelView::PanelView(Panel* panel, const gfx::Rect& bounds, bool always_on_top) | |
269 : panel_(panel), | |
270 bounds_(bounds), | |
271 window_(NULL), | |
272 window_closed_(false), | |
273 web_view_(NULL), | |
274 always_on_top_(always_on_top), | |
275 focused_(false), | |
276 user_resizing_(false), | |
277 #if defined(OS_WIN) | |
278 user_resizing_interior_stacked_panel_edge_(false), | |
279 #endif | |
280 mouse_pressed_(false), | |
281 mouse_dragging_state_(NO_DRAGGING), | |
282 is_drawing_attention_(false), | |
283 force_to_paint_as_inactive_(false), | |
284 old_focused_view_(NULL) { | |
285 window_ = new views::Widget; | |
286 views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); | |
287 params.delegate = this; | |
288 params.remove_standard_frame = true; | |
289 params.keep_on_top = always_on_top; | |
290 params.visible_on_all_workspaces = always_on_top; | |
291 params.bounds = bounds; | |
292 | |
293 #if defined(USE_X11) && !defined(OS_CHROMEOS) | |
294 params.wm_class_name = web_app::GetWMClassFromAppName(panel->app_name()); | |
295 params.wm_class_class = shell_integration_linux::GetProgramClassClass(); | |
296 #endif | |
297 | |
298 window_->Init(params); | |
299 window_->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); | |
300 window_->set_focus_on_creation(false); | |
301 window_->AddObserver(this); | |
302 | |
303 #if !defined(USE_ASH) | |
304 // Prevent the browser process from shutting down while this window is open. | |
305 // Chrome OS already has a mechanism to always stay alive and skips this. | |
306 keep_alive_.reset(new ScopedKeepAlive(KeepAliveOrigin::PANEL_VIEW, | |
307 KeepAliveRestartOption::DISABLED)); | |
308 #endif // !defined(USE_ASH) | |
309 | |
310 web_view_ = new views::WebView(NULL); | |
311 AddChildView(web_view_); | |
312 | |
313 // Register accelarators supported by panels. | |
314 views::FocusManager* focus_manager = GetFocusManager(); | |
315 const std::map<ui::Accelerator, int>& accelerator_table = | |
316 GetAcceleratorTable(); | |
317 for (std::map<ui::Accelerator, int>::const_iterator iter = | |
318 accelerator_table.begin(); | |
319 iter != accelerator_table.end(); ++iter) { | |
320 focus_manager->RegisterAccelerator( | |
321 iter->first, ui::AcceleratorManager::kNormalPriority, this); | |
322 } | |
323 | |
324 #if defined(OS_WIN) | |
325 ui::win::SetAppIdForWindow( | |
326 shell_integration::win::GetAppModelIdForProfile( | |
327 base::UTF8ToWide(panel->app_name()), panel->profile()->GetPath()), | |
328 views::HWNDForWidget(window_)); | |
329 ui::win::PreventWindowFromPinning(views::HWNDForWidget(window_)); | |
330 #endif | |
331 | |
332 #if defined(USE_X11) && !defined(OS_CHROMEOS) | |
333 // Swap the default non client event handler with one which handles resizes | |
334 // for panels entirely within Chrome. This is needed because it is not | |
335 // possible to tell when a resize performed by the window manager ends. | |
336 views::DesktopWindowTreeHostX11* host = | |
337 views::DesktopWindowTreeHostX11::GetHostForXID( | |
338 window_->GetNativeView()->GetHost()->GetAcceleratedWidget()); | |
339 std::unique_ptr<ui::EventHandler> resizer( | |
340 new X11PanelResizer(panel_.get(), window_->GetNativeWindow())); | |
341 host->SwapNonClientEventHandler(std::move(resizer)); | |
342 #endif | |
343 } | |
344 | |
345 PanelView::~PanelView() { | |
346 } | |
347 | |
348 void PanelView::ShowPanel() { | |
349 if (window_->IsVisible()) | |
350 return; | |
351 window_->Show(); | |
352 panel_->manager()->OnPanelAnimationEnded(panel_.get()); | |
353 } | |
354 | |
355 void PanelView::ShowPanelInactive() { | |
356 if (window_->IsVisible()) | |
357 return; | |
358 window_->ShowInactive(); | |
359 // No animation is used for initial creation of a panel on Win. | |
360 // Signal immediately that pending actions can be performed. | |
361 panel_->manager()->OnPanelAnimationEnded(panel_.get()); | |
362 } | |
363 | |
364 gfx::Rect PanelView::GetPanelBounds() const { | |
365 return bounds_; | |
366 } | |
367 | |
368 void PanelView::SetPanelBounds(const gfx::Rect& bounds) { | |
369 SetBoundsInternal(bounds, true); | |
370 } | |
371 | |
372 void PanelView::SetPanelBoundsInstantly(const gfx::Rect& bounds) { | |
373 SetBoundsInternal(bounds, false); | |
374 } | |
375 | |
376 void PanelView::SetBoundsInternal(const gfx::Rect& new_bounds, bool animate) { | |
377 if (bounds_ == new_bounds) | |
378 return; | |
379 | |
380 bounds_ = new_bounds; | |
381 | |
382 if (!animate) { | |
383 // If no animation is in progress, apply bounds change instantly. Otherwise, | |
384 // continue the animation with new target bounds. | |
385 if (!IsAnimatingBounds()) | |
386 SetWidgetBounds(bounds_); | |
387 return; | |
388 } | |
389 | |
390 animation_start_bounds_ = window_->GetWindowBoundsInScreen(); | |
391 | |
392 bounds_animator_.reset(new PanelBoundsAnimation( | |
393 this, panel_.get(), animation_start_bounds_, new_bounds)); | |
394 bounds_animator_->Start(); | |
395 } | |
396 | |
397 #if defined(OS_WIN) | |
398 bool PanelView::FilterMessage(HWND hwnd, | |
399 UINT message, | |
400 WPARAM w_param, | |
401 LPARAM l_param, | |
402 LRESULT* l_result) { | |
403 switch (message) { | |
404 case WM_SIZING: | |
405 if (w_param == WMSZ_BOTTOM) | |
406 user_resizing_interior_stacked_panel_edge_ = true; | |
407 break; | |
408 } | |
409 return false; | |
410 } | |
411 #endif | |
412 | |
413 void PanelView::AnimationEnded(const gfx::Animation* animation) { | |
414 panel_->manager()->OnPanelAnimationEnded(panel_.get()); | |
415 } | |
416 | |
417 void PanelView::AnimationProgressed(const gfx::Animation* animation) { | |
418 gfx::Rect new_bounds = bounds_animator_->CurrentValueBetween( | |
419 animation_start_bounds_, bounds_); | |
420 SetWidgetBounds(new_bounds); | |
421 } | |
422 | |
423 void PanelView::SetWidgetBounds(const gfx::Rect& new_bounds) { | |
424 #if defined(OS_WIN) | |
425 // An overlapped window is a top-level window that has a titlebar, border, | |
426 // and client area. The Windows system will automatically put the shadow | |
427 // around the whole window. Also the system will enforce the minimum height | |
428 // (38 pixels based on observation) for the overlapped window such that it | |
429 // will always has the space for the titlebar. | |
430 // | |
431 // On contrast, a popup window is a bare minimum window without border and | |
432 // titlebar by default. It is often used for the popup menu and the window | |
433 // with short life. The Windows system does not add the shadow around the | |
434 // whole window though CS_DROPSHADOW class style could be passed to add the | |
435 // drop shadow which is only around the right and bottom edges. | |
436 // | |
437 // The height of the title-only or minimized panel is smaller than the minimum | |
438 // overlapped window height. If the panel still uses the overlapped window | |
439 // style, Windows system will automatically increase the window height. To | |
440 // work around this limitation, we temporarily change the window style to | |
441 // popup when the height to set is smaller than the minimum overlapped window | |
442 // height and then restore the window style to overlapped when the height | |
443 // grows. | |
444 static const int kMinimumOverlappedWindowHeight = 38; | |
445 gfx::Rect old_bounds = GetWidget()->GetRestoredBounds(); | |
446 if (old_bounds.height() > kMinimumOverlappedWindowHeight && | |
447 new_bounds.height() <= kMinimumOverlappedWindowHeight) { | |
448 // When the panel height shrinks below the minimum overlapped window height, | |
449 // change the window style to popup such that we can show the title-only | |
450 // and minimized panel without additional height being added by the system. | |
451 UpdateWindowAttribute(GWL_STYLE, | |
452 WS_POPUP, | |
453 WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU, | |
454 true); | |
455 } else if (old_bounds.height() <= kMinimumOverlappedWindowHeight && | |
456 new_bounds.height() > kMinimumOverlappedWindowHeight) { | |
457 // Change the window style back to overlappped when the panel height grow | |
458 // taller than the minimum overlapped window height. | |
459 UpdateWindowAttribute(GWL_STYLE, | |
460 WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU, | |
461 WS_POPUP, | |
462 true); | |
463 } | |
464 #endif | |
465 | |
466 GetWidget()->SetBounds(new_bounds); | |
467 } | |
468 | |
469 void PanelView::ClosePanel() { | |
470 // We're already closing. Do nothing. | |
471 if (window_closed_) | |
472 return; | |
473 | |
474 if (!panel_->ShouldCloseWindow()) | |
475 return; | |
476 | |
477 // Cancel any currently running animation since we're closing down. | |
478 if (bounds_animator_.get()) | |
479 bounds_animator_.reset(); | |
480 | |
481 if (panel_->GetWebContents()) { | |
482 // Still have web contents. Allow renderer to shut down. | |
483 // When web contents are destroyed, we will be called back again. | |
484 panel_->OnWindowClosing(); | |
485 return; | |
486 } | |
487 | |
488 panel_->OnNativePanelClosed(); | |
489 if (window_) | |
490 window_->Close(); | |
491 window_closed_ = true; | |
492 } | |
493 | |
494 void PanelView::ActivatePanel() { | |
495 window_->Activate(); | |
496 } | |
497 | |
498 void PanelView::DeactivatePanel() { | |
499 if (!focused_) | |
500 return; | |
501 | |
502 #if defined(OS_WIN) | |
503 // Need custom behavior for always-on-top panels to avoid | |
504 // the OS activating a minimized panel when this one is | |
505 // deactivated. | |
506 if (always_on_top_) { | |
507 ::SetForegroundWindow(::GetDesktopWindow()); | |
508 return; | |
509 } | |
510 #endif | |
511 | |
512 window_->Deactivate(); | |
513 } | |
514 | |
515 bool PanelView::IsPanelActive() const { | |
516 return focused_; | |
517 } | |
518 | |
519 void PanelView::PreventActivationByOS(bool prevent_activation) { | |
520 #if defined(OS_WIN) | |
521 // Set the flags "NoActivate" to make sure the minimized panels do not get | |
522 // activated by the OS. In addition, set "AppWindow" to make sure the | |
523 // minimized panels do appear in the taskbar and Alt-Tab menu if it is not | |
524 // in a stack. | |
525 int value_to_change = WS_EX_NOACTIVATE; | |
526 if (!panel_->stack()) | |
527 value_to_change |= WS_EX_APPWINDOW; | |
528 if (prevent_activation) | |
529 UpdateWindowAttribute(GWL_EXSTYLE, value_to_change, 0, false); | |
530 else | |
531 UpdateWindowAttribute(GWL_EXSTYLE, 0, value_to_change, false); | |
532 #endif | |
533 } | |
534 | |
535 gfx::NativeWindow PanelView::GetNativePanelWindow() { | |
536 return window_->GetNativeWindow(); | |
537 } | |
538 | |
539 void PanelView::UpdatePanelTitleBar() { | |
540 UpdateWindowTitle(); | |
541 UpdateWindowIcon(); | |
542 } | |
543 | |
544 void PanelView::UpdatePanelLoadingAnimations(bool should_animate) { | |
545 GetFrameView()->UpdateThrobber(); | |
546 } | |
547 | |
548 void PanelView::PanelCut() { | |
549 // Nothing to do since we do not have panel-specific system menu. | |
550 NOTREACHED(); | |
551 } | |
552 | |
553 void PanelView::PanelCopy() { | |
554 // Nothing to do since we do not have panel-specific system menu. | |
555 NOTREACHED(); | |
556 } | |
557 | |
558 void PanelView::PanelPaste() { | |
559 // Nothing to do since we do not have panel-specific system menu. | |
560 NOTREACHED(); | |
561 } | |
562 | |
563 void PanelView::DrawAttention(bool draw_attention) { | |
564 DCHECK((panel_->attention_mode() & Panel::USE_PANEL_ATTENTION) != 0); | |
565 | |
566 if (is_drawing_attention_ == draw_attention) | |
567 return; | |
568 is_drawing_attention_ = draw_attention; | |
569 GetFrameView()->SchedulePaint(); | |
570 | |
571 if ((panel_->attention_mode() & Panel::USE_SYSTEM_ATTENTION) != 0) { | |
572 #if defined(OS_WIN) | |
573 // The default implementation of Widget::FlashFrame only flashes 5 times. | |
574 // We need more than that. | |
575 FLASHWINFO fwi; | |
576 fwi.cbSize = sizeof(fwi); | |
577 fwi.hwnd = views::HWNDForWidget(window_); | |
578 if (draw_attention) { | |
579 fwi.dwFlags = FLASHW_ALL; | |
580 fwi.uCount = panel::kNumberOfTimesToFlashPanelForAttention; | |
581 fwi.dwTimeout = 0; | |
582 } else { | |
583 // TODO(jianli): calling FlashWindowEx with FLASHW_STOP flag for the | |
584 // panel window has the same problem as the stack window. However, | |
585 // we cannot take the similar fix since there is no background window | |
586 // to replace for the regular panel window. More investigation is needed. | |
587 fwi.dwFlags = FLASHW_STOP; | |
588 } | |
589 ::FlashWindowEx(&fwi); | |
590 #else | |
591 window_->FlashFrame(draw_attention); | |
592 #endif | |
593 } | |
594 } | |
595 | |
596 bool PanelView::IsDrawingAttention() const { | |
597 return is_drawing_attention_; | |
598 } | |
599 | |
600 void PanelView::HandlePanelKeyboardEvent( | |
601 const content::NativeWebKeyboardEvent& event) { | |
602 views::FocusManager* focus_manager = GetFocusManager(); | |
603 if (focus_manager->shortcut_handling_suspended()) | |
604 return; | |
605 | |
606 ui::Accelerator accelerator = | |
607 ui::GetAcceleratorFromNativeWebKeyboardEvent(event); | |
608 focus_manager->ProcessAccelerator(accelerator); | |
609 } | |
610 | |
611 void PanelView::FullScreenModeChanged(bool is_full_screen) { | |
612 if (is_full_screen) { | |
613 if (window_->IsVisible() && always_on_top_) | |
614 window_->Hide(); | |
615 } else { | |
616 if (!window_->IsVisible()) { | |
617 ShowPanelInactive(); | |
618 | |
619 #if defined(OS_WIN) | |
620 // When hiding and showing again a top-most window that belongs to a | |
621 // background application (i.e. the application is not a foreground one), | |
622 // the window may loose top-most placement even though its WS_EX_TOPMOST | |
623 // bit is still set. Re-issuing SetWindowsPos() returns the window to its | |
624 // top-most placement. | |
625 if (always_on_top_) | |
626 window_->SetAlwaysOnTop(true); | |
627 #endif | |
628 } | |
629 } | |
630 } | |
631 | |
632 bool PanelView::IsPanelAlwaysOnTop() const { | |
633 return always_on_top_; | |
634 } | |
635 | |
636 void PanelView::SetPanelAlwaysOnTop(bool on_top) { | |
637 if (always_on_top_ == on_top) | |
638 return; | |
639 always_on_top_ = on_top; | |
640 | |
641 window_->SetAlwaysOnTop(on_top); | |
642 window_->SetVisibleOnAllWorkspaces(on_top); | |
643 window_->non_client_view()->Layout(); | |
644 window_->client_view()->Layout(); | |
645 } | |
646 | |
647 void PanelView::UpdatePanelMinimizeRestoreButtonVisibility() { | |
648 GetFrameView()->UpdateTitlebarMinimizeRestoreButtonVisibility(); | |
649 } | |
650 | |
651 void PanelView::SetWindowCornerStyle(panel::CornerStyle corner_style) { | |
652 GetFrameView()->SetWindowCornerStyle(corner_style); | |
653 } | |
654 | |
655 void PanelView::PanelExpansionStateChanging(Panel::ExpansionState old_state, | |
656 Panel::ExpansionState new_state) { | |
657 #if defined(OS_WIN) | |
658 // Live preview is only available since Windows 7. | |
659 if (base::win::GetVersion() < base::win::VERSION_WIN7) | |
660 return; | |
661 | |
662 if (panel_->collection()->type() != PanelCollection::DOCKED) | |
663 return; | |
664 | |
665 bool is_minimized = old_state != Panel::EXPANDED; | |
666 bool will_be_minimized = new_state != Panel::EXPANDED; | |
667 if (is_minimized == will_be_minimized) | |
668 return; | |
669 | |
670 HWND native_window = views::HWNDForWidget(window_); | |
671 | |
672 if (!thumbnailer_.get()) { | |
673 DCHECK(native_window); | |
674 thumbnailer_.reset(new TaskbarWindowThumbnailerWin(native_window, NULL)); | |
675 } | |
676 | |
677 // Cache the image at this point. | |
678 if (will_be_minimized) { | |
679 // If the panel is still active (we will deactivate the minimizd panel at | |
680 // later time), we need to paint it immediately as inactive so that we can | |
681 // take a snapshot of inactive panel. | |
682 if (focused_) { | |
683 force_to_paint_as_inactive_ = true; | |
684 ::RedrawWindow(native_window, NULL, NULL, | |
685 RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW); | |
686 } | |
687 | |
688 // Start the thumbnailer and capture the snapshot now. | |
689 thumbnailer_->Start(); | |
690 thumbnailer_->CaptureSnapshot(); | |
691 } else { | |
692 force_to_paint_as_inactive_ = false; | |
693 thumbnailer_->Stop(); | |
694 } | |
695 | |
696 #endif | |
697 } | |
698 | |
699 gfx::Size PanelView::WindowSizeFromContentSize( | |
700 const gfx::Size& content_size) const { | |
701 gfx::Size frame = GetFrameView()->NonClientAreaSize(); | |
702 return gfx::Size(content_size.width() + frame.width(), | |
703 content_size.height() + frame.height()); | |
704 } | |
705 | |
706 gfx::Size PanelView::ContentSizeFromWindowSize( | |
707 const gfx::Size& window_size) const { | |
708 gfx::Size frame = GetFrameView()->NonClientAreaSize(); | |
709 return gfx::Size(window_size.width() - frame.width(), | |
710 window_size.height() - frame.height()); | |
711 } | |
712 | |
713 int PanelView::TitleOnlyHeight() const { | |
714 return panel::kTitlebarHeight; | |
715 } | |
716 | |
717 void PanelView::MinimizePanelBySystem() { | |
718 window_->Minimize(); | |
719 } | |
720 | |
721 bool PanelView::IsPanelMinimizedBySystem() const { | |
722 return window_->IsMinimized(); | |
723 } | |
724 | |
725 bool PanelView::IsPanelShownOnActiveDesktop() const { | |
726 #if defined(OS_WIN) | |
727 // Virtual desktop is not supported by the native Windows system. | |
728 return true; | |
729 #else | |
730 NOTIMPLEMENTED(); | |
731 return true; | |
732 #endif | |
733 } | |
734 | |
735 void PanelView::ShowShadow(bool show) { | |
736 #if defined(OS_WIN) | |
737 // The overlapped window has the shadow while the popup window does not have | |
738 // the shadow. | |
739 int overlap_style = WS_OVERLAPPED | WS_THICKFRAME | WS_SYSMENU; | |
740 int popup_style = WS_POPUP; | |
741 UpdateWindowAttribute(GWL_STYLE, | |
742 show ? overlap_style : popup_style, | |
743 show ? popup_style : overlap_style, | |
744 true); | |
745 #endif | |
746 } | |
747 | |
748 void PanelView::AttachWebContents(content::WebContents* contents) { | |
749 web_view_->SetWebContents(contents); | |
750 } | |
751 | |
752 void PanelView::DetachWebContents(content::WebContents* contents) { | |
753 web_view_->SetWebContents(NULL); | |
754 } | |
755 | |
756 NativePanelTesting* PanelView::CreateNativePanelTesting() { | |
757 return new NativePanelTestingViews(this); | |
758 } | |
759 | |
760 void PanelView::OnDisplayChanged() { | |
761 panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged(); | |
762 } | |
763 | |
764 void PanelView::OnWorkAreaChanged() { | |
765 panel_->manager()->display_settings_provider()->OnDisplaySettingsChanged(); | |
766 } | |
767 | |
768 bool PanelView::WillProcessWorkAreaChange() const { | |
769 return true; | |
770 } | |
771 | |
772 views::View* PanelView::GetContentsView() { | |
773 return this; | |
774 } | |
775 | |
776 views::NonClientFrameView* PanelView::CreateNonClientFrameView( | |
777 views::Widget* widget) { | |
778 PanelFrameView* frame_view = new PanelFrameView(this); | |
779 frame_view->Init(); | |
780 return frame_view; | |
781 } | |
782 | |
783 bool PanelView::CanResize() const { | |
784 return true; | |
785 } | |
786 | |
787 bool PanelView::CanMaximize() const { | |
788 return false; | |
789 } | |
790 | |
791 bool PanelView::CanMinimize() const { | |
792 return false; | |
793 } | |
794 | |
795 base::string16 PanelView::GetWindowTitle() const { | |
796 return panel_->GetWindowTitle(); | |
797 } | |
798 | |
799 gfx::ImageSkia PanelView::GetWindowAppIcon() { | |
800 gfx::Image app_icon = panel_->app_icon(); | |
801 if (app_icon.IsEmpty()) | |
802 return GetWindowIcon(); | |
803 else | |
804 return *app_icon.ToImageSkia(); | |
805 } | |
806 | |
807 gfx::ImageSkia PanelView::GetWindowIcon() { | |
808 gfx::Image icon = panel_->GetCurrentPageIcon(); | |
809 return icon.IsEmpty() ? gfx::ImageSkia() : *icon.ToImageSkia(); | |
810 } | |
811 | |
812 void PanelView::WindowClosing() { | |
813 // When closing a panel via window.close, API or the close button, | |
814 // ClosePanel() is called first, destroying the native |window_| | |
815 // which results in this method being called. ClosePanel() sets | |
816 // |window_closed_| to NULL. | |
817 // If we still have a |window_closed_| here, the close was triggered by the | |
818 // OS, (e.g. clicking on taskbar menu), which destroys the native |window_| | |
819 // without invoking ClosePanel() beforehand. | |
820 if (!window_closed_) { | |
821 panel_->OnWindowClosing(); | |
822 ClosePanel(); | |
823 DCHECK(window_closed_); | |
824 } | |
825 } | |
826 | |
827 void PanelView::DeleteDelegate() { | |
828 delete this; | |
829 } | |
830 | |
831 void PanelView::OnWindowBeginUserBoundsChange() { | |
832 user_resizing_ = true; | |
833 panel_->OnPanelStartUserResizing(); | |
834 | |
835 #if defined(OS_WIN) | |
836 StackedPanelCollection* stack = panel_->stack(); | |
837 if (stack) { | |
838 // Listen to WM_SIZING message in order to find out whether the interior | |
839 // edge is being resized such that the specific maximum size could be | |
840 // passed to the system. | |
841 if (panel_->stack()->GetPanelBelow(panel_.get())) { | |
842 ui::HWNDSubclass::AddFilterToTarget(views::HWNDForWidget(window_), this); | |
843 user_resizing_interior_stacked_panel_edge_ = false; | |
844 } | |
845 | |
846 // Keep track of the original full size of the resizing panel such that it | |
847 // can be restored to this size once it is shrunk to minimized state. | |
848 original_full_size_of_resizing_panel_ = panel_->full_size(); | |
849 | |
850 // Keep track of the original full size of the panel below the resizing | |
851 // panel such that it can be restored to this size once it is shrunk to | |
852 // minimized state. | |
853 Panel* below_panel = stack->GetPanelBelow(panel_.get()); | |
854 if (below_panel && !below_panel->IsMinimized()) { | |
855 original_full_size_of_panel_below_resizing_panel_ = | |
856 below_panel->full_size(); | |
857 } | |
858 } | |
859 #endif | |
860 } | |
861 | |
862 void PanelView::OnWindowEndUserBoundsChange() { | |
863 user_resizing_ = false; | |
864 panel_->OnPanelEndUserResizing(); | |
865 | |
866 // No need to proceed with post-resizing update when there is no size change. | |
867 gfx::Rect new_bounds = window_->GetWindowBoundsInScreen(); | |
868 if (bounds_ == new_bounds) | |
869 return; | |
870 bounds_ = new_bounds; | |
871 | |
872 panel_->IncreaseMaxSize(bounds_.size()); | |
873 panel_->set_full_size(bounds_.size()); | |
874 | |
875 #if defined(OS_WIN) | |
876 StackedPanelCollection* stack = panel_->stack(); | |
877 if (stack) { | |
878 // No need to listen to WM_SIZING message any more. | |
879 ui::HWNDSubclass::RemoveFilterFromAllTargets(this); | |
880 | |
881 // If the height of resizing panel shrinks close to the titlebar height, | |
882 // treate it as minimized. This could occur when the user is dragging | |
883 // 1) the top edge of the top panel downward to shrink it; or | |
884 // 2) the bottom edge of any panel upward to shrink it. | |
885 if (panel_->GetBounds().height() < | |
886 kStackedPanelHeightShrinkThresholdToBecomeMinimized) { | |
887 stack->MinimizePanel(panel_.get()); | |
888 panel_->set_full_size(original_full_size_of_resizing_panel_); | |
889 } | |
890 | |
891 // If the height of panel below the resizing panel shrinks close to the | |
892 // titlebar height, treat it as minimized. This could occur when the user | |
893 // is dragging the bottom edge of non-bottom panel downward to expand it | |
894 // and also shrink the panel below. | |
895 Panel* below_panel = stack->GetPanelBelow(panel_.get()); | |
896 if (below_panel && !below_panel->IsMinimized() && | |
897 below_panel->GetBounds().height() < | |
898 kStackedPanelHeightShrinkThresholdToBecomeMinimized) { | |
899 stack->MinimizePanel(below_panel); | |
900 below_panel->set_full_size( | |
901 original_full_size_of_panel_below_resizing_panel_); | |
902 } | |
903 } | |
904 #endif | |
905 | |
906 panel_->collection()->RefreshLayout(); | |
907 } | |
908 | |
909 views::Widget* PanelView::GetWidget() { | |
910 return window_; | |
911 } | |
912 | |
913 const views::Widget* PanelView::GetWidget() const { | |
914 return window_; | |
915 } | |
916 | |
917 void PanelView::UpdateLoadingAnimations(bool should_animate) { | |
918 GetFrameView()->UpdateThrobber(); | |
919 } | |
920 | |
921 void PanelView::UpdateWindowTitle() { | |
922 window_->UpdateWindowTitle(); | |
923 GetFrameView()->UpdateTitle(); | |
924 } | |
925 | |
926 void PanelView::UpdateWindowIcon() { | |
927 window_->UpdateWindowIcon(); | |
928 GetFrameView()->UpdateIcon(); | |
929 } | |
930 | |
931 void PanelView::Layout() { | |
932 // |web_view_| might not be created yet when the window is first created. | |
933 if (web_view_) | |
934 web_view_->SetBounds(0, 0, width(), height()); | |
935 } | |
936 | |
937 gfx::Size PanelView::GetMinimumSize() const { | |
938 // If the panel is minimized, it can be rendered to very small size, like | |
939 // 4-pixel lines when it is docked. Otherwise, its size should not be less | |
940 // than its minimum size. | |
941 return panel_->IsMinimized() ? gfx::Size() : | |
942 gfx::Size(panel::kPanelMinWidth, panel::kPanelMinHeight); | |
943 } | |
944 | |
945 gfx::Size PanelView::GetMaximumSize() const { | |
946 // If the user is resizing a stacked panel by its bottom edge, make sure its | |
947 // height cannot grow more than what the panel below it could offer. This is | |
948 // because growing a stacked panel by y amount will shrink the panel below it | |
949 // by same amount and we do not want the panel below it being shrunk to be | |
950 // smaller than the titlebar. | |
951 #if defined(OS_WIN) | |
952 if (panel_->stack() && user_resizing_interior_stacked_panel_edge_) { | |
953 Panel* below_panel = panel_->stack()->GetPanelBelow(panel_.get()); | |
954 if (below_panel && !below_panel->IsMinimized()) { | |
955 return gfx::Size(0, below_panel->GetBounds().bottom() - | |
956 panel_->GetBounds().y() - panel::kTitlebarHeight); | |
957 } | |
958 } | |
959 #endif | |
960 return gfx::Size(); | |
961 } | |
962 | |
963 bool PanelView::AcceleratorPressed(const ui::Accelerator& accelerator) { | |
964 if (mouse_pressed_ && accelerator.key_code() == ui::VKEY_ESCAPE) { | |
965 OnTitlebarMouseCaptureLost(); | |
966 return true; | |
967 } | |
968 | |
969 // No other accelerator is allowed when the drag begins. | |
970 if (mouse_dragging_state_ == DRAGGING_STARTED) | |
971 return true; | |
972 | |
973 const std::map<ui::Accelerator, int>& accelerator_table = | |
974 GetAcceleratorTable(); | |
975 std::map<ui::Accelerator, int>::const_iterator iter = | |
976 accelerator_table.find(accelerator); | |
977 DCHECK(iter != accelerator_table.end()); | |
978 return panel_->ExecuteCommandIfEnabled(iter->second); | |
979 } | |
980 | |
981 void PanelView::OnWidgetDestroying(views::Widget* widget) { | |
982 window_ = NULL; | |
983 } | |
984 | |
985 void PanelView::OnWidgetActivationChanged(views::Widget* widget, bool active) { | |
986 #if defined(OS_WIN) | |
987 // WM_NCACTIVATED could be sent when an active window is being destroyed on | |
988 // Windows. We need to guard against this. | |
989 if (window_closed_) | |
990 return; | |
991 | |
992 // The panel window is in focus (actually accepting keystrokes) if it is | |
993 // active and belongs to a foreground application. | |
994 bool focused = | |
995 active && views::HWNDForWidget(widget) == ::GetForegroundWindow(); | |
996 #else | |
997 bool focused = active; | |
998 #endif // OS_WIN | |
999 | |
1000 if (focused_ == focused) | |
1001 return; | |
1002 focused_ = focused; | |
1003 | |
1004 // Expand the panel if the minimized panel is activated by means other than | |
1005 // clicking on its titlebar. This is the workaround to support restoring the | |
1006 // minimized panel by other means, like alt-tabbing, win-tabbing, or clicking | |
1007 // the taskbar icon. Note that this workaround does not work for one edge | |
1008 // case: the mouse happens to be at the minimized panel when the user tries to | |
1009 // bring up the panel with the above alternatives. | |
1010 // When the user clicks on the minimized panel, the panel expansion will be | |
1011 // done when we process the mouse button pressed message. | |
1012 #if defined(OS_WIN) | |
1013 if (focused_ && panel_->IsMinimized() && | |
1014 panel_->collection()->type() == PanelCollection::DOCKED && | |
1015 !display::Screen::GetScreen()->IsWindowUnderCursor( | |
1016 widget->GetNativeWindow())) { | |
1017 panel_->Restore(); | |
1018 } | |
1019 #endif | |
1020 | |
1021 panel()->OnActiveStateChanged(focused); | |
1022 | |
1023 // Give web contents view a chance to set focus to the appropriate element. | |
1024 if (focused_) { | |
1025 content::WebContents* web_contents = panel_->GetWebContents(); | |
1026 if (web_contents) | |
1027 web_contents->RestoreFocus(); | |
1028 } | |
1029 } | |
1030 | |
1031 void PanelView::OnWidgetBoundsChanged(views::Widget* widget, | |
1032 const gfx::Rect& new_bounds) { | |
1033 if (user_resizing_) | |
1034 panel()->collection()->OnPanelResizedByMouse(panel(), new_bounds); | |
1035 } | |
1036 | |
1037 bool PanelView::OnTitlebarMousePressed(const gfx::Point& mouse_location) { | |
1038 mouse_pressed_ = true; | |
1039 mouse_dragging_state_ = NO_DRAGGING; | |
1040 last_mouse_location_ = mouse_location; | |
1041 return true; | |
1042 } | |
1043 | |
1044 bool PanelView::OnTitlebarMouseDragged(const gfx::Point& mouse_location) { | |
1045 if (!mouse_pressed_) | |
1046 return false; | |
1047 | |
1048 if (mouse_dragging_state_ == NO_DRAGGING && | |
1049 ExceededDragThreshold(mouse_location - last_mouse_location_)) { | |
1050 // When a drag begins, we do not want to the client area to still receive | |
1051 // the focus. We do not need to do this for the unfocused minimized panel. | |
1052 if (!panel_->IsMinimized()) { | |
1053 old_focused_view_ = GetFocusManager()->GetFocusedView(); | |
1054 GetFocusManager()->SetFocusedView(GetFrameView()); | |
1055 } | |
1056 | |
1057 panel_->manager()->StartDragging(panel_.get(), last_mouse_location_); | |
1058 mouse_dragging_state_ = DRAGGING_STARTED; | |
1059 } | |
1060 if (mouse_dragging_state_ == DRAGGING_STARTED) { | |
1061 panel_->manager()->Drag(mouse_location); | |
1062 | |
1063 // Once in drag, update |last_mouse_location_| on each drag fragment, since | |
1064 // we already dragged the panel up to the current mouse location. | |
1065 last_mouse_location_ = mouse_location; | |
1066 } | |
1067 return true; | |
1068 } | |
1069 | |
1070 bool PanelView::OnTitlebarMouseReleased(panel::ClickModifier modifier) { | |
1071 if (mouse_dragging_state_ != NO_DRAGGING) { | |
1072 // Ensure dragging a minimized panel does not leave it activated. | |
1073 // Windows activates a panel on mouse-down, regardless of our attempts | |
1074 // to prevent activation of a minimized panel. Now that we know mouse-down | |
1075 // resulted in a mouse-drag, we need to ensure the minimized panel is | |
1076 // deactivated. | |
1077 if (panel_->IsMinimized() && focused_) | |
1078 panel_->Deactivate(); | |
1079 | |
1080 if (mouse_dragging_state_ == DRAGGING_STARTED) { | |
1081 // When a drag ends, restore the focus. | |
1082 if (old_focused_view_) { | |
1083 GetFocusManager()->SetFocusedView(old_focused_view_); | |
1084 old_focused_view_ = NULL; | |
1085 } | |
1086 return EndDragging(false); | |
1087 } | |
1088 | |
1089 // The panel drag was cancelled before the mouse is released. Do not | |
1090 // treat this as a click. | |
1091 return true; | |
1092 } | |
1093 | |
1094 panel_->OnTitlebarClicked(modifier); | |
1095 return true; | |
1096 } | |
1097 | |
1098 bool PanelView::OnTitlebarMouseCaptureLost() { | |
1099 if (mouse_dragging_state_ == DRAGGING_STARTED) | |
1100 return EndDragging(true); | |
1101 return true; | |
1102 } | |
1103 | |
1104 bool PanelView::EndDragging(bool cancelled) { | |
1105 // Only handle clicks that started in our window. | |
1106 if (!mouse_pressed_) | |
1107 return false; | |
1108 mouse_pressed_ = false; | |
1109 | |
1110 mouse_dragging_state_ = DRAGGING_ENDED; | |
1111 panel_->manager()->EndDragging(cancelled); | |
1112 return true; | |
1113 } | |
1114 | |
1115 PanelFrameView* PanelView::GetFrameView() const { | |
1116 return static_cast<PanelFrameView*>(window_->non_client_view()->frame_view()); | |
1117 } | |
1118 | |
1119 bool PanelView::IsAnimatingBounds() const { | |
1120 if (bounds_animator_.get() && bounds_animator_->is_animating()) | |
1121 return true; | |
1122 StackedPanelCollection* stack = panel_->stack(); | |
1123 if (!stack) | |
1124 return false; | |
1125 return stack->IsAnimatingPanelBounds(panel_.get()); | |
1126 } | |
1127 | |
1128 #if defined(OS_WIN) | |
1129 void PanelView::UpdateWindowAttribute(int attribute_index, | |
1130 int attribute_value_to_set, | |
1131 int attribute_value_to_reset, | |
1132 bool update_frame) { | |
1133 HWND native_window = views::HWNDForWidget(window_); | |
1134 int value = ::GetWindowLong(native_window, attribute_index); | |
1135 int expected_value = value; | |
1136 if (attribute_value_to_set) | |
1137 expected_value |= attribute_value_to_set; | |
1138 if (attribute_value_to_reset) | |
1139 expected_value &= ~attribute_value_to_reset; | |
1140 if (value != expected_value) | |
1141 ::SetWindowLong(native_window, attribute_index, expected_value); | |
1142 | |
1143 // Per MSDN, if any of the frame styles is changed, SetWindowPos with the | |
1144 // SWP_FRAMECHANGED flag must be called in order for the cached window data | |
1145 // to be updated properly. | |
1146 // http://msdn.microsoft.com/en-us/library/windows/desktop/ms633591(v=vs.85).a
spx | |
1147 if (update_frame) { | |
1148 ::SetWindowPos(native_window, NULL, 0, 0, 0, 0, | |
1149 SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | | |
1150 SWP_NOZORDER | SWP_NOACTIVATE); | |
1151 } | |
1152 } | |
1153 #endif | |
OLD | NEW |