diff options
author | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-10 20:07:03 +0000 |
---|---|---|
committer | pkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-10 20:07:03 +0000 |
commit | 4c619ad0d418331d0f61b245149d056144b39739 (patch) | |
tree | 5432d63eb0abc4833124882ea776c931386fe84f /chrome | |
parent | 46095987bb01ae05e5844bc271ca099228772368 (diff) | |
download | chromium_src-4c619ad0d418331d0f61b245149d056144b39739.zip chromium_src-4c619ad0d418331d0f61b245149d056144b39739.tar.gz chromium_src-4c619ad0d418331d0f61b245149d056144b39739.tar.bz2 |
Fix various problems with work area change notifications so that we now (hopefully) move/resize correctly whenever the work area changes, no matter why.
BUG=8873
Review URL: http://codereview.chromium.org/67023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13533 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/views/widget/widget_win.cc | 133 | ||||
-rw-r--r-- | chrome/views/widget/widget_win.h | 28 |
2 files changed, 115 insertions, 46 deletions
diff --git a/chrome/views/widget/widget_win.cc b/chrome/views/widget/widget_win.cc index 2db81e4..7f64aea 100644 --- a/chrome/views/widget/widget_win.cc +++ b/chrome/views/widget/widget_win.cc @@ -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,10 +120,11 @@ static RegisteredClasses* registered_classes = NULL; // 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), window_style_(0), window_ex_style_(kWindowDefaultExStyle), use_layered_buffer_(true), @@ -115,7 +135,8 @@ WidgetWin::WidgetWin() is_mouse_down_(false), class_style_(CS_DBLCLKS), hwnd_(NULL), - close_widget_factory_(this) { + last_monitor_(NULL), + ignore_window_pos_changes_(false) { } WidgetWin::~WidgetWin() { @@ -124,10 +145,8 @@ WidgetWin::~WidgetWin() { void WidgetWin::Init(HWND parent, const gfx::Rect& bounds, bool has_own_focus_manager) { - toplevel_ = parent == NULL; - if (window_style_ == 0) - window_style_ = toplevel_ ? kWindowDefaultStyle : kWindowDefaultChildStyle; + window_style_ = parent ? kWindowDefaultChildStyle : kWindowDefaultStyle; // See if the style has been overridden. opaque_ = !(window_ex_style_ & WS_EX_TRANSPARENT); @@ -151,6 +170,9 @@ void WidgetWin::Init(HWND parent, const gfx::Rect& bounds, 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); @@ -665,8 +687,11 @@ void WidgetWin::OnRButtonDblClk(UINT flags, const CPoint& point) { } void WidgetWin::OnSettingChange(UINT flags, const wchar_t* section) { - if (toplevel_ && (flags == SPI_SETWORKAREA)) { - AdjustWindowToFitScreenSize(); + if (!GetParent() && (flags == SPI_SETWORKAREA)) { + // 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 +705,70 @@ void WidgetWin::OnThemeChanged() { 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 (!GetParent()) { + 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 +891,6 @@ void WidgetWin::ProcessMouseExited() { 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_) { diff --git a/chrome/views/widget/widget_win.h b/chrome/views/widget/widget_win.h index 1dcd0d7..ce78103 100644 --- a/chrome/views/widget/widget_win.h +++ b/chrome/views/widget/widget_win.h @@ -457,9 +457,7 @@ class WidgetWin : public Widget, virtual void OnVScroll(int scroll_type, short position, HWND scrollbar) { SetMsgHandled(FALSE); } - virtual void OnWindowPosChanging(WINDOWPOS* window_pos) { - SetMsgHandled(FALSE); - } + virtual void OnWindowPosChanging(WINDOWPOS* window_pos); virtual void OnWindowPosChanged(WINDOWPOS* window_pos) { SetMsgHandled(FALSE); } @@ -485,10 +483,6 @@ class WidgetWin : public Widget, void ProcessMouseMoved(const CPoint& point, UINT flags, bool is_nonclient); void ProcessMouseExited(); - // Makes sure the window still fits on screen after a settings change message - // from the OS, e.g. a screen resolution change. - virtual void AdjustWindowToFitScreenSize(); - // Handles re-laying out content in response to a window size change. virtual void ChangeSize(UINT size_param, const CSize& size); @@ -543,18 +537,22 @@ class WidgetWin : public Widget, // If necessary, this registers the window class. std::wstring GetWindowClassName(); + // Stops ignoring SetWindowPos() requests (see below). + void StopIgnoringPosChanges() { ignore_window_pos_changes_ = false; } + // The following factory is used for calls to close the WidgetWin // instance. ScopedRunnableMethodFactory<WidgetWin> close_widget_factory_; + // The following factory is used to ignore SetWindowPos() calls for short time + // periods. + ScopedRunnableMethodFactory<WidgetWin> ignore_pos_changes_factory_; + // The flags currently being used with TrackMouseEvent to track mouse // messages. 0 if there is no active tracking. The value of this member is // used when tracking is canceled. DWORD active_mouse_tracking_flags_; - // Whether or not this is a top level window. - bool toplevel_; - bool opaque_; // Window Styles used when creating the window. @@ -611,6 +609,16 @@ class WidgetWin : public Widget, // Our hwnd. HWND hwnd_; + + // The last-seen monitor containing us, and its work area. These are used to + // catch updates to the work area and react accordingly. + HMONITOR last_monitor_; + gfx::Rect last_work_area_; + + // When true, this flag makes us discard incoming SetWindowPos() requests that + // only change our position/size. (We still allow changes to Z-order, + // activation, etc.) + bool ignore_window_pos_changes_; }; } // namespace views |