summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorpkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-10 20:07:03 +0000
committerpkasting@chromium.org <pkasting@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-10 20:07:03 +0000
commit4c619ad0d418331d0f61b245149d056144b39739 (patch)
tree5432d63eb0abc4833124882ea776c931386fe84f /chrome
parent46095987bb01ae05e5844bc271ca099228772368 (diff)
downloadchromium_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.cc133
-rw-r--r--chrome/views/widget/widget_win.h28
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