Chromium Code Reviews| Index: chrome/views/widget/widget_win.cc |
| =================================================================== |
| --- chrome/views/widget/widget_win.cc (revision 12889) |
| +++ chrome/views/widget/widget_win.cc (working copy) |
| @@ -19,6 +19,25 @@ |
| #include "chrome/views/widget/hwnd_notification_source.h" |
| #include "chrome/views/widget/root_view.h" |
| +namespace { |
| + |
| +bool GetMonitorAndWorkAreaForRect(const RECT& rect, |
| + HMONITOR* monitor, |
| + gfx::Rect* work_area) { |
| + DCHECK(monitor); |
| + DCHECK(work_area); |
| + *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST); |
| + if (!*monitor) |
| + return false; |
| + MONITORINFO monitor_info = { 0 }; |
| + monitor_info.cbSize = sizeof(monitor_info); |
| + GetMonitorInfo(*monitor, &monitor_info); |
| + *work_area = monitor_info.rcWork; |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| namespace views { |
| static const DWORD kWindowDefaultChildStyle = |
| @@ -101,7 +120,9 @@ |
| // WidgetWin, public |
| WidgetWin::WidgetWin() |
| - : active_mouse_tracking_flags_(0), |
| + : close_widget_factory_(this), |
| + ignore_pos_changes_factory_(this), |
| + active_mouse_tracking_flags_(0), |
| has_capture_(false), |
| current_action_(FA_NONE), |
| toplevel_(false), |
| @@ -115,7 +136,8 @@ |
| is_mouse_down_(false), |
| class_style_(CS_DBLCLKS), |
| hwnd_(NULL), |
| - close_widget_factory_(this) { |
| + last_monitor_(NULL), |
| + ignore_window_pos_changes_(false) { |
| } |
| WidgetWin::~WidgetWin() { |
| @@ -151,6 +173,9 @@ |
| TRACK_HWND_CREATION(hwnd_); |
| SetWindowSupportsRerouteMouseWheel(hwnd_); |
| + GetMonitorAndWorkAreaForRect(bounds.ToRECT(), &last_monitor_, |
| + &last_work_area_); |
| + |
| // The window procedure should have set the data for us. |
| DCHECK(win_util::GetWindowUserData(hwnd_) == this); |
| @@ -666,7 +691,10 @@ |
| void WidgetWin::OnSettingChange(UINT flags, const wchar_t* section) { |
| if (toplevel_ && (flags == SPI_SETWORKAREA)) { |
| - AdjustWindowToFitScreenSize(); |
| + // Fire a dummy SetWindowPos() call, so we'll trip the code in |
| + // OnWindowPosChanging() below that notices work area changes. |
| + ::SetWindowPos(GetNativeView(), 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | |
| + SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE | SWP_NOOWNERZORDER); |
| SetMsgHandled(TRUE); |
| } |
| } |
| @@ -680,6 +708,70 @@ |
| gfx::NativeTheme::instance()->CloseHandles(); |
| } |
| +void WidgetWin::OnWindowPosChanging(WINDOWPOS* window_pos) { |
| + if (ignore_window_pos_changes_) { |
| + // If somebody's trying to toggle our visibility, change the nonclient area, |
| + // change our Z-order, or activate us, we should probably let it go through. |
| + if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) | |
| + SWP_FRAMECHANGED)) && |
| + (window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) { |
| + // Just sizing/moving the window; ignore. |
| + window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW; |
| + window_pos->flags &= ~(SWP_SHOWWINDOW | SWP_HIDEWINDOW); |
| + } |
| + } else if (toplevel_) { |
|
Ben Goodger (Google)
2009/04/10 19:59:03
Can you just use !GetParent() here instead? I want
Peter Kasting
2009/04/10 20:05:02
Yep, and I can just remove toplevel_ entirely, goi
|
| + CRect window_rect; |
| + HMONITOR monitor; |
| + gfx::Rect work_area; |
| + if (GetWindowRect(&window_rect) && |
| + GetMonitorAndWorkAreaForRect(window_rect, &monitor, &work_area)) { |
| + if ((monitor == last_monitor_) && (work_area != last_work_area_)) { |
| + // The work area for the monitor we're on changed. Normally Windows |
| + // notifies us about this (and thus we're reaching here due to the |
| + // SetWindowPos() call in OnSettingChange() above), but with some |
| + // software (e.g. nVidia's nView desktop manager) the work area can |
| + // change asynchronous to any notification, and we're just sent a |
| + // SetWindowPos() call with a new (frequently incorrect) position/size. |
| + // In either case, the best response is to throw away the existing |
| + // position/size information in |window_pos| and recalculate it based on |
| + // the old window coordinates, adjusted for the change in the work area. |
| + if (IsZoomed()) { |
| + window_pos->x = window_rect.left + work_area.x() - last_work_area_.x(); |
| + window_pos->y = window_rect.top + work_area.y() - last_work_area_.y(); |
| + window_pos->cx = window_rect.Width() + work_area.width() - |
| + last_work_area_.width(); |
| + window_pos->cy = window_rect.Height() + work_area.height() - |
| + last_work_area_.height(); |
| + } else { |
| + gfx::Rect window_gfx_rect(window_rect); |
| + gfx::Rect new_window_rect = window_gfx_rect.AdjustToFit(work_area); |
| + window_pos->x = new_window_rect.x(); |
| + window_pos->y = new_window_rect.y(); |
| + window_pos->cx = new_window_rect.width(); |
| + window_pos->cy = new_window_rect.height(); |
| + } |
| + // WARNING! Don't set SWP_FRAMECHANGED here, it breaks moving the child |
| + // HWNDs for some reason. |
| + window_pos->flags &= ~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW); |
| + window_pos->flags |= SWP_NOCOPYBITS; |
| + |
| + // Now ignore all immediately-following SetWindowPos() changes. Windows |
| + // likes to (incorrectly) recalculate what our position/size should be |
| + // and send us further updates. |
| + ignore_window_pos_changes_ = true; |
| + DCHECK(ignore_pos_changes_factory_.empty()); |
| + MessageLoop::current()->PostTask(FROM_HERE, |
| + ignore_pos_changes_factory_.NewRunnableMethod( |
| + &WidgetWin::StopIgnoringPosChanges)); |
| + } |
| + last_monitor_ = monitor; |
| + last_work_area_ = work_area; |
| + } |
| + } |
| + |
| + SetMsgHandled(FALSE); |
| +} |
| + |
| void WidgetWin::OnFinalMessage(HWND window) { |
| if (delete_on_destroy_) |
| delete this; |
| @@ -802,34 +894,6 @@ |
| active_mouse_tracking_flags_ = 0; |
| } |
| -void WidgetWin::AdjustWindowToFitScreenSize() { |
| - // Desktop size has changed. Make sure we're still on screen. |
| - CRect wr; |
| - GetWindowRect(&wr); |
| - HMONITOR hmon = MonitorFromRect(&wr, MONITOR_DEFAULTTONEAREST); |
| - if (!hmon) { |
| - // No monitor available. |
| - return; |
| - } |
| - |
| - MONITORINFO mi; |
| - mi.cbSize = sizeof(mi); |
| - GetMonitorInfo(hmon, &mi); |
| - gfx::Rect window_rect(wr); |
| - gfx::Rect monitor_rect(mi.rcWork); |
| - gfx::Rect new_window_rect = window_rect.AdjustToFit(monitor_rect); |
| - if (!new_window_rect.Equals(window_rect)) { |
| - // New position differs from last, resize window. |
| - ::SetWindowPos(GetNativeView(), |
| - 0, |
| - new_window_rect.x(), |
| - new_window_rect.y(), |
| - new_window_rect.width(), |
| - new_window_rect.height(), |
| - SWP_NOACTIVATE | SWP_NOZORDER); |
| - } |
| -} |
| - |
| void WidgetWin::ChangeSize(UINT size_param, const CSize& size) { |
| CRect rect; |
| if (use_layered_buffer_) { |