OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "views/window/native_window_win.h" | 5 #include "views/window/native_window_win.h" |
6 | 6 |
7 #include <dwmapi.h> | |
8 #include <shellapi.h> | |
9 | |
10 #include "base/i18n/rtl.h" | 7 #include "base/i18n/rtl.h" |
11 #include "base/win/scoped_gdi_object.h" | |
12 #include "base/win/win_util.h" | |
13 #include "base/win/windows_version.h" | |
14 #include "ui/base/accessibility/accessibility_types.h" | 8 #include "ui/base/accessibility/accessibility_types.h" |
15 #include "ui/base/keycodes/keyboard_code_conversion_win.h" | 9 #include "ui/base/keycodes/keyboard_code_conversion_win.h" |
16 #include "ui/base/l10n/l10n_util_win.h" | 10 #include "ui/base/l10n/l10n_util_win.h" |
17 #include "ui/base/theme_provider.h" | 11 #include "ui/base/theme_provider.h" |
18 #include "ui/base/win/hwnd_util.h" | 12 #include "ui/base/win/hwnd_util.h" |
19 #include "ui/gfx/canvas_skia_paint.h" | |
20 #include "ui/gfx/font.h" | 13 #include "ui/gfx/font.h" |
21 #include "ui/gfx/path.h" | 14 #include "ui/gfx/path.h" |
22 #include "views/accessibility/native_view_accessibility_win.h" | 15 #include "views/accessibility/native_view_accessibility_win.h" |
23 #include "views/window/client_view.h" | 16 #include "views/window/client_view.h" |
24 #include "views/window/native_window_delegate.h" | 17 #include "views/window/native_window_delegate.h" |
25 #include "views/window/non_client_view.h" | 18 #include "views/window/non_client_view.h" |
26 #include "views/window/window_delegate.h" | 19 #include "views/window/window_delegate.h" |
27 | 20 |
28 namespace views { | 21 namespace views { |
29 namespace { | |
30 | |
31 // The thickness of an auto-hide taskbar in pixels. | |
32 static const int kAutoHideTaskbarThicknessPx = 2; | |
33 | |
34 bool GetMonitorAndRects(const RECT& rect, | |
35 HMONITOR* monitor, | |
36 gfx::Rect* monitor_rect, | |
37 gfx::Rect* work_area) { | |
38 DCHECK(monitor); | |
39 DCHECK(monitor_rect); | |
40 DCHECK(work_area); | |
41 *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); | |
42 if (!*monitor) | |
43 return false; | |
44 MONITORINFO monitor_info = { 0 }; | |
45 monitor_info.cbSize = sizeof(monitor_info); | |
46 GetMonitorInfo(*monitor, &monitor_info); | |
47 *monitor_rect = monitor_info.rcMonitor; | |
48 *work_area = monitor_info.rcWork; | |
49 return true; | |
50 } | |
51 | |
52 // Returns true if edge |edge| (one of ABE_LEFT, TOP, RIGHT, or BOTTOM) of | |
53 // monitor |monitor| has an auto-hiding taskbar that's always-on-top. | |
54 bool EdgeHasTopmostAutoHideTaskbar(UINT edge, HMONITOR monitor) { | |
55 APPBARDATA taskbar_data = { 0 }; | |
56 taskbar_data.cbSize = sizeof APPBARDATA; | |
57 taskbar_data.uEdge = edge; | |
58 HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR, | |
59 &taskbar_data)); | |
60 return ::IsWindow(taskbar) && (monitor != NULL) && | |
61 (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) && | |
62 (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST); | |
63 } | |
64 | |
65 HWND GetOwner(HWND window) { | |
66 return ::GetWindow(window, GW_OWNER); | |
67 } | |
68 | |
69 } // namespace | |
70 | |
71 namespace internal { | 22 namespace internal { |
72 | 23 |
73 void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, | 24 void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, |
74 gfx::Rect* child_rect, | 25 gfx::Rect* child_rect, |
75 int padding) { | 26 int padding) { |
76 DCHECK(child_rect); | 27 DCHECK(child_rect); |
77 | 28 |
78 // We use padding here because it allows some of the original web page to | 29 // We use padding here because it allows some of the original web page to |
79 // bleed through around the edges. | 30 // bleed through around the edges. |
80 int twice_padding = padding * 2; | 31 int twice_padding = padding * 2; |
(...skipping 26 matching lines...) Expand all Loading... |
107 child_rect->set_x(parent_rect.right() - child_rect->width() - padding); | 58 child_rect->set_x(parent_rect.right() - child_rect->width() - padding); |
108 } | 59 } |
109 | 60 |
110 } // namespace internal | 61 } // namespace internal |
111 | 62 |
112 //////////////////////////////////////////////////////////////////////////////// | 63 //////////////////////////////////////////////////////////////////////////////// |
113 // NativeWindowWin, public: | 64 // NativeWindowWin, public: |
114 | 65 |
115 NativeWindowWin::NativeWindowWin(internal::NativeWindowDelegate* delegate) | 66 NativeWindowWin::NativeWindowWin(internal::NativeWindowDelegate* delegate) |
116 : NativeWidgetWin(delegate->AsNativeWidgetDelegate()), | 67 : NativeWidgetWin(delegate->AsNativeWidgetDelegate()), |
117 delegate_(delegate), | 68 delegate_(delegate) { |
118 restored_enabled_(false), | |
119 ignore_window_pos_changes_(false), | |
120 ignore_pos_changes_factory_(this), | |
121 is_right_mouse_pressed_on_caption_(false), | |
122 last_monitor_(NULL) { | |
123 is_window_ = true; | 69 is_window_ = true; |
124 // Initialize these values to 0 so that subclasses can override the default | 70 // Initialize these values to 0 so that subclasses can override the default |
125 // behavior before calling Init. | 71 // behavior before calling Init. |
126 set_window_style(0); | 72 set_window_style(0); |
127 set_window_ex_style(0); | 73 set_window_ex_style(0); |
128 } | 74 } |
129 | 75 |
130 NativeWindowWin::~NativeWindowWin() { | 76 NativeWindowWin::~NativeWindowWin() { |
131 } | 77 } |
132 | 78 |
133 // static | |
134 gfx::Font NativeWindowWin::GetWindowTitleFont() { | |
135 NONCLIENTMETRICS ncm; | |
136 base::win::GetNonClientMetrics(&ncm); | |
137 l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); | |
138 base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); | |
139 return gfx::Font(caption_font); | |
140 } | |
141 | |
142 /////////////////////////////////////////////////////////////////////////////// | |
143 // NativeWindowWin, NativeWidgetWin overrides: | |
144 | |
145 void NativeWindowWin::InitNativeWidget(const Widget::InitParams& params) { | |
146 if (window_style() == 0) | |
147 set_window_style(CalculateWindowStyle()); | |
148 if (window_ex_style() == 0) | |
149 set_window_ex_style(CalculateWindowExStyle()); | |
150 | |
151 GetMonitorAndRects(params.bounds.ToRECT(), &last_monitor_, | |
152 &last_monitor_rect_, &last_work_area_); | |
153 | |
154 NativeWidgetWin::InitNativeWidget(params); | |
155 } | |
156 | |
157 void NativeWindowWin::OnDestroy() { | |
158 RestoreEnabledIfNecessary(); | |
159 NativeWidgetWin::OnDestroy(); | |
160 } | |
161 | |
162 LRESULT NativeWindowWin::OnMouseRange(UINT message, | |
163 WPARAM w_param, | |
164 LPARAM l_param) { | |
165 if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) { | |
166 is_right_mouse_pressed_on_caption_ = false; | |
167 ReleaseCapture(); | |
168 // |point| is in window coordinates, but WM_NCHITTEST and TrackPopupMenu() | |
169 // expect screen coordinates. | |
170 CPoint screen_point(l_param); | |
171 MapWindowPoints(GetNativeView(), HWND_DESKTOP, &screen_point, 1); | |
172 w_param = SendMessage(GetNativeView(), WM_NCHITTEST, 0, | |
173 MAKELPARAM(screen_point.x, screen_point.y)); | |
174 if (w_param == HTCAPTION || w_param == HTSYSMENU) { | |
175 UINT flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD; | |
176 if (base::i18n::IsRTL()) | |
177 flags |= TPM_RIGHTALIGN; | |
178 HMENU system_menu = GetSystemMenu(GetNativeView(), FALSE); | |
179 int id = TrackPopupMenu(system_menu, flags, screen_point.x, | |
180 screen_point.y, 0, GetNativeView(), NULL); | |
181 ExecuteSystemMenuCommand(id); | |
182 return 0; | |
183 } | |
184 } else if (message == WM_NCLBUTTONDOWN && | |
185 !GetWindow()->ShouldUseNativeFrame()) { | |
186 switch (w_param) { | |
187 case HTCLOSE: | |
188 case HTMINBUTTON: | |
189 case HTMAXBUTTON: { | |
190 // When the mouse is pressed down in these specific non-client areas, | |
191 // we need to tell the RootView to send the mouse pressed event (which | |
192 // sets capture, allowing subsequent WM_LBUTTONUP (note, _not_ | |
193 // WM_NCLBUTTONUP) to fire so that the appropriate WM_SYSCOMMAND can be | |
194 // sent by the applicable button's ButtonListener. We _have_ to do this | |
195 // way rather than letting Windows just send the syscommand itself (as | |
196 // would happen if we never did this dance) because for some insane | |
197 // reason DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed | |
198 // window control button appearance, in the Windows classic style, over | |
199 // our view! Ick! By handling this message we prevent Windows from | |
200 // doing this undesirable thing, but that means we need to roll the | |
201 // sys-command handling ourselves. | |
202 // Combine |w_param| with common key state message flags. | |
203 w_param |= ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)? MK_CONTROL : 0; | |
204 w_param |= ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)? MK_SHIFT : 0; | |
205 } | |
206 } | |
207 } else if (message == WM_NCRBUTTONDOWN && | |
208 (w_param == HTCAPTION || w_param == HTSYSMENU)) { | |
209 is_right_mouse_pressed_on_caption_ = true; | |
210 // We SetMouseCapture() to ensure we only show the menu when the button | |
211 // down and up are both on the caption. Note: this causes the button up to | |
212 // be WM_RBUTTONUP instead of WM_NCRBUTTONUP. | |
213 SetMouseCapture(); | |
214 } | |
215 | |
216 /* | |
217 TODO(beng): This fixes some situations where the windows-classic appearance | |
218 non-client area is rendered over our custom frame, however it | |
219 causes mouse-releases to the non-client area to be eaten, so it | |
220 can't be enabled. | |
221 if (message == WM_NCLBUTTONDOWN) { | |
222 // NativeWindowWin::OnNCLButtonDown set the message as un-handled. This | |
223 // normally means NativeWidgetWin::ProcessWindowMessage will pass it to | |
224 // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird | |
225 // non-client painting, so we need to call it directly here inside a | |
226 // scoped update lock. | |
227 ScopedRedrawLock lock(this); | |
228 NativeWidgetWin::OnMouseRange(message, w_param, l_param); | |
229 DefWindowProc(GetNativeView(), WM_NCLBUTTONDOWN, w_param, l_param); | |
230 SetMsgHandled(TRUE); | |
231 } | |
232 */ | |
233 | |
234 NativeWidgetWin::OnMouseRange(message, w_param, l_param); | |
235 return 0; | |
236 } | |
237 | |
238 LRESULT NativeWindowWin::OnNCCalcSize(BOOL mode, LPARAM l_param) { | |
239 // We only override the default handling if we need to specify a custom | |
240 // non-client edge width. Note that in most cases "no insets" means no | |
241 // custom width, but in fullscreen mode we want a custom width of 0. | |
242 gfx::Insets insets = GetClientAreaInsets(); | |
243 if (insets.empty() && !IsFullscreen()) | |
244 return NativeWidgetWin::OnNCCalcSize(mode, l_param); | |
245 | |
246 RECT* client_rect = mode ? | |
247 &reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0] : | |
248 reinterpret_cast<RECT*>(l_param); | |
249 client_rect->left += insets.left(); | |
250 client_rect->top += insets.top(); | |
251 client_rect->bottom -= insets.bottom(); | |
252 client_rect->right -= insets.right(); | |
253 if (IsMaximized()) { | |
254 // Find all auto-hide taskbars along the screen edges and adjust in by the | |
255 // thickness of the auto-hide taskbar on each such edge, so the window isn't | |
256 // treated as a "fullscreen app", which would cause the taskbars to | |
257 // disappear. | |
258 HMONITOR monitor = MonitorFromWindow(GetNativeView(), | |
259 MONITOR_DEFAULTTONULL); | |
260 if (!monitor) { | |
261 // We might end up here if the window was previously minimized and the | |
262 // user clicks on the taskbar button to restore it in the previously | |
263 // maximized position. In that case WM_NCCALCSIZE is sent before the | |
264 // window coordinates are restored to their previous values, so our | |
265 // (left,top) would probably be (-32000,-32000) like all minimized | |
266 // windows. So the above MonitorFromWindow call fails, but if we check | |
267 // the window rect given with WM_NCCALCSIZE (which is our previous | |
268 // restored window position) we will get the correct monitor handle. | |
269 monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); | |
270 if (!monitor) { | |
271 // This is probably an extreme case that we won't hit, but if we don't | |
272 // intersect any monitor, let us not adjust the client rect since our | |
273 // window will not be visible anyway. | |
274 return 0; | |
275 } | |
276 } | |
277 if (EdgeHasTopmostAutoHideTaskbar(ABE_LEFT, monitor)) | |
278 client_rect->left += kAutoHideTaskbarThicknessPx; | |
279 if (EdgeHasTopmostAutoHideTaskbar(ABE_TOP, monitor)) { | |
280 if (GetWindow()->ShouldUseNativeFrame()) { | |
281 // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of | |
282 // WM_NCHITTEST, having any nonclient area atop the window causes the | |
283 // caption buttons to draw onscreen but not respond to mouse | |
284 // hover/clicks. | |
285 // So for a taskbar at the screen top, we can't push the | |
286 // client_rect->top down; instead, we move the bottom up by one pixel, | |
287 // which is the smallest change we can make and still get a client area | |
288 // less than the screen size. This is visibly ugly, but there seems to | |
289 // be no better solution. | |
290 --client_rect->bottom; | |
291 } else { | |
292 client_rect->top += kAutoHideTaskbarThicknessPx; | |
293 } | |
294 } | |
295 if (EdgeHasTopmostAutoHideTaskbar(ABE_RIGHT, monitor)) | |
296 client_rect->right -= kAutoHideTaskbarThicknessPx; | |
297 if (EdgeHasTopmostAutoHideTaskbar(ABE_BOTTOM, monitor)) | |
298 client_rect->bottom -= kAutoHideTaskbarThicknessPx; | |
299 | |
300 // We cannot return WVR_REDRAW when there is nonclient area, or Windows | |
301 // exhibits bugs where client pixels and child HWNDs are mispositioned by | |
302 // the width/height of the upper-left nonclient area. | |
303 return 0; | |
304 } | |
305 | |
306 // If the window bounds change, we're going to relayout and repaint anyway. | |
307 // Returning WVR_REDRAW avoids an extra paint before that of the old client | |
308 // pixels in the (now wrong) location, and thus makes actions like resizing a | |
309 // window from the left edge look slightly less broken. | |
310 // We special case when left or top insets are 0, since these conditions | |
311 // actually require another repaint to correct the layout after glass gets | |
312 // turned on and off. | |
313 if (insets.left() == 0 || insets.top() == 0) | |
314 return 0; | |
315 return mode ? WVR_REDRAW : 0; | |
316 } | |
317 | |
318 namespace { | |
319 struct ClipState { | |
320 // The window being painted. | |
321 HWND parent; | |
322 | |
323 // DC painting to. | |
324 HDC dc; | |
325 | |
326 // Origin of the window in terms of the screen. | |
327 int x; | |
328 int y; | |
329 }; | |
330 | |
331 // See comments in OnNCPaint for details of this function. | |
332 static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) { | |
333 ClipState* clip_state = reinterpret_cast<ClipState*>(param); | |
334 if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) { | |
335 RECT bounds; | |
336 GetWindowRect(window, &bounds); | |
337 ExcludeClipRect(clip_state->dc, | |
338 bounds.left - clip_state->x, | |
339 bounds.top - clip_state->y, | |
340 bounds.right - clip_state->x, | |
341 bounds.bottom - clip_state->y); | |
342 } | |
343 return TRUE; | |
344 } | |
345 } // namespace | |
346 | |
347 void NativeWindowWin::OnNCPaint(HRGN rgn) { | |
348 // We only do non-client painting if we're not using the native frame. | |
349 // It's required to avoid some native painting artifacts from appearing when | |
350 // the window is resized. | |
351 if (GetWindow()->ShouldUseNativeFrame()) { | |
352 NativeWidgetWin::OnNCPaint(rgn); | |
353 return; | |
354 } | |
355 | |
356 // We have an NC region and need to paint it. We expand the NC region to | |
357 // include the dirty region of the root view. This is done to minimize | |
358 // paints. | |
359 CRect window_rect; | |
360 GetWindowRect(&window_rect); | |
361 | |
362 if (window_rect.Width() != GetWidget()->GetRootView()->width() || | |
363 window_rect.Height() != GetWidget()->GetRootView()->height()) { | |
364 // If the size of the window differs from the size of the root view it | |
365 // means we're being asked to paint before we've gotten a WM_SIZE. This can | |
366 // happen when the user is interactively resizing the window. To avoid | |
367 // mass flickering we don't do anything here. Once we get the WM_SIZE we'll | |
368 // reset the region of the window which triggers another WM_NCPAINT and | |
369 // all is well. | |
370 return; | |
371 } | |
372 | |
373 CRect dirty_region; | |
374 // A value of 1 indicates paint all. | |
375 if (!rgn || rgn == reinterpret_cast<HRGN>(1)) { | |
376 dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height()); | |
377 } else { | |
378 RECT rgn_bounding_box; | |
379 GetRgnBox(rgn, &rgn_bounding_box); | |
380 if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) | |
381 return; // Dirty region doesn't intersect window bounds, bale. | |
382 | |
383 // rgn_bounding_box is in screen coordinates. Map it to window coordinates. | |
384 OffsetRect(&dirty_region, -window_rect.left, -window_rect.top); | |
385 } | |
386 | |
387 // In theory GetDCEx should do what we want, but I couldn't get it to work. | |
388 // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell | |
389 // it doesn't work at all. So, instead we get the DC for the window then | |
390 // manually clip out the children. | |
391 HDC dc = GetWindowDC(GetNativeView()); | |
392 ClipState clip_state; | |
393 clip_state.x = window_rect.left; | |
394 clip_state.y = window_rect.top; | |
395 clip_state.parent = GetNativeView(); | |
396 clip_state.dc = dc; | |
397 EnumChildWindows(GetNativeView(), &ClipDCToChild, | |
398 reinterpret_cast<LPARAM>(&clip_state)); | |
399 | |
400 gfx::Rect old_paint_region = invalid_rect(); | |
401 | |
402 if (!old_paint_region.IsEmpty()) { | |
403 // The root view has a region that needs to be painted. Include it in the | |
404 // region we're going to paint. | |
405 | |
406 CRect old_paint_region_crect = old_paint_region.ToRECT(); | |
407 CRect tmp = dirty_region; | |
408 UnionRect(&dirty_region, &tmp, &old_paint_region_crect); | |
409 } | |
410 | |
411 GetWidget()->GetRootView()->SchedulePaintInRect(gfx::Rect(dirty_region)); | |
412 | |
413 // gfx::CanvasSkiaPaint's destructor does the actual painting. As such, wrap | |
414 // the following in a block to force paint to occur so that we can release | |
415 // the dc. | |
416 { | |
417 gfx::CanvasSkiaPaint canvas(dc, true, dirty_region.left, | |
418 dirty_region.top, dirty_region.Width(), | |
419 dirty_region.Height()); | |
420 delegate_->AsNativeWidgetDelegate()->OnNativeWidgetPaint(&canvas); | |
421 } | |
422 | |
423 ReleaseDC(GetNativeView(), dc); | |
424 // When using a custom frame, we want to avoid calling DefWindowProc() since | |
425 // that may render artifacts. | |
426 SetMsgHandled(!GetWindow()->ShouldUseNativeFrame()); | |
427 } | |
428 | |
429 void NativeWindowWin::OnWindowPosChanging(WINDOWPOS* window_pos) { | |
430 if (ignore_window_pos_changes_) { | |
431 // If somebody's trying to toggle our visibility, change the nonclient area, | |
432 // change our Z-order, or activate us, we should probably let it go through. | |
433 if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | | |
434 SWP_FRAMECHANGED)) && | |
435 (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { | |
436 // Just sizing/moving the window; ignore. | |
437 window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; | |
438 window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); | |
439 } | |
440 } else if (!GetParent()) { | |
441 CRect window_rect; | |
442 HMONITOR monitor; | |
443 gfx::Rect monitor_rect, work_area; | |
444 if (GetWindowRect(&window_rect) && | |
445 GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) { | |
446 if (monitor && (monitor == last_monitor_) && | |
447 (IsFullscreen() || ((monitor_rect == last_monitor_rect_) && | |
448 (work_area != last_work_area_)))) { | |
449 // A rect for the monitor we're on changed. Normally Windows notifies | |
450 // us about this (and thus we're reaching here due to the SetWindowPos() | |
451 // call in OnSettingChange() above), but with some software (e.g. | |
452 // nVidia's nView desktop manager) the work area can change asynchronous | |
453 // to any notification, and we're just sent a SetWindowPos() call with a | |
454 // new (frequently incorrect) position/size. In either case, the best | |
455 // response is to throw away the existing position/size information in | |
456 // |window_pos| and recalculate it based on the new work rect. | |
457 gfx::Rect new_window_rect; | |
458 if (IsFullscreen()) { | |
459 new_window_rect = monitor_rect; | |
460 } else if (IsZoomed()) { | |
461 new_window_rect = work_area; | |
462 int border_thickness = GetSystemMetrics(SM_CXSIZEFRAME); | |
463 new_window_rect.Inset(-border_thickness, -border_thickness); | |
464 } else { | |
465 new_window_rect = gfx::Rect(window_rect).AdjustToFit(work_area); | |
466 } | |
467 window_pos->x = new_window_rect.x(); | |
468 window_pos->y = new_window_rect.y(); | |
469 window_pos->cx = new_window_rect.width(); | |
470 window_pos->cy = new_window_rect.height(); | |
471 // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child | |
472 // HWNDs for some reason. | |
473 window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); | |
474 window_pos->flags |= SWP_NOCOPYBITS; | |
475 | |
476 // Now ignore all immediately-following SetWindowPos() changes. Windows | |
477 // likes to (incorrectly) recalculate what our position/size should be | |
478 // and send us further updates. | |
479 ignore_window_pos_changes_ = true; | |
480 DCHECK(ignore_pos_changes_factory_.empty()); | |
481 MessageLoop::current()->PostTask(FROM_HERE, | |
482 ignore_pos_changes_factory_.NewRunnableMethod( | |
483 &NativeWindowWin::StopIgnoringPosChanges)); | |
484 } | |
485 last_monitor_ = monitor; | |
486 last_monitor_rect_ = monitor_rect; | |
487 last_work_area_ = work_area; | |
488 } | |
489 } | |
490 | |
491 NativeWidgetWin::OnWindowPosChanging(window_pos); | |
492 } | |
493 | |
494 //////////////////////////////////////////////////////////////////////////////// | 79 //////////////////////////////////////////////////////////////////////////////// |
495 // NativeWindowWin, NativeWindow implementation: | 80 // NativeWindowWin, NativeWindow implementation: |
496 | 81 |
497 Window* NativeWindowWin::GetWindow() { | 82 Window* NativeWindowWin::GetWindow() { |
498 return delegate_->AsWindow(); | 83 return delegate_->AsWindow(); |
499 } | 84 } |
500 | 85 |
501 const Window* NativeWindowWin::GetWindow() const { | 86 const Window* NativeWindowWin::GetWindow() const { |
502 return delegate_->AsWindow(); | 87 return delegate_->AsWindow(); |
503 } | 88 } |
504 | 89 |
505 NativeWidget* NativeWindowWin::AsNativeWidget() { | 90 NativeWidget* NativeWindowWin::AsNativeWidget() { |
506 return this; | 91 return this; |
507 } | 92 } |
508 | 93 |
509 const NativeWidget* NativeWindowWin::AsNativeWidget() const { | 94 const NativeWidget* NativeWindowWin::AsNativeWidget() const { |
510 return this; | 95 return this; |
511 } | 96 } |
512 | 97 |
513 void NativeWindowWin::BecomeModal() { | |
514 // We implement modality by crawling up the hierarchy of windows starting | |
515 // at the owner, disabling all of them so that they don't receive input | |
516 // messages. | |
517 HWND start = GetOwner(GetNativeView()); | |
518 while (start) { | |
519 ::EnableWindow(start, FALSE); | |
520 start = ::GetParent(start); | |
521 } | |
522 } | |
523 | |
524 //////////////////////////////////////////////////////////////////////////////// | |
525 // NativeWindowWin, private: | |
526 | |
527 void NativeWindowWin::RestoreEnabledIfNecessary() { | |
528 if (delegate_->IsModal() && !restored_enabled_) { | |
529 restored_enabled_ = true; | |
530 // If we were run modally, we need to undo the disabled-ness we inflicted on | |
531 // the owner's parent hierarchy. | |
532 HWND start = GetOwner(GetNativeView()); | |
533 while (start) { | |
534 ::EnableWindow(start, TRUE); | |
535 start = ::GetParent(start); | |
536 } | |
537 } | |
538 } | |
539 | |
540 DWORD NativeWindowWin::CalculateWindowStyle() { | |
541 DWORD window_styles = | |
542 WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_SYSMENU | WS_CAPTION; | |
543 bool can_resize = GetWindow()->window_delegate()->CanResize(); | |
544 bool can_maximize = GetWindow()->window_delegate()->CanMaximize(); | |
545 if (can_maximize) { | |
546 window_styles |= WS_OVERLAPPEDWINDOW; | |
547 } else if (can_resize) { | |
548 window_styles |= WS_OVERLAPPED | WS_THICKFRAME; | |
549 } | |
550 if (delegate_->IsDialogBox()) { | |
551 window_styles |= DS_MODALFRAME; | |
552 // NOTE: Turning this off means we lose the close button, which is bad. | |
553 // Turning it on though means the user can maximize or size the window | |
554 // from the system menu, which is worse. We may need to provide our own | |
555 // menu to get the close button to appear properly. | |
556 // window_styles &= ~WS_SYSMENU; | |
557 } | |
558 return window_styles; | |
559 } | |
560 | |
561 DWORD NativeWindowWin::CalculateWindowExStyle() { | |
562 return delegate_->IsDialogBox() ? WS_EX_DLGMODALFRAME : 0; | |
563 } | |
564 | |
565 //////////////////////////////////////////////////////////////////////////////// | 98 //////////////////////////////////////////////////////////////////////////////// |
566 // NativeWindow, public: | 99 // NativeWindow, public: |
567 | 100 |
568 // static | 101 // static |
569 NativeWindow* NativeWindow::CreateNativeWindow( | 102 NativeWindow* NativeWindow::CreateNativeWindow( |
570 internal::NativeWindowDelegate* delegate) { | 103 internal::NativeWindowDelegate* delegate) { |
571 return new NativeWindowWin(delegate); | 104 return new NativeWindowWin(delegate); |
572 } | 105 } |
573 | 106 |
574 } // namespace views | 107 } // namespace views |
OLD | NEW |