| 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 |