Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(988)

Side by Side Diff: chrome/views/custom_frame_window.cc

Issue 27317: Support DWM switching.... (Closed) Base URL: svn://chrome-svn.corp.google.com/chrome/trunk/src/
Patch Set: '' Created 11 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/views/custom_frame_window.h ('k') | chrome/views/default_non_client_view.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/views/custom_frame_window.h"
6
7 #include "base/gfx/point.h"
8 #include "base/gfx/size.h"
9 #include "chrome/common/gfx/chrome_canvas.h"
10 #include "chrome/common/gfx/path.h"
11 #include "chrome/common/l10n_util.h"
12 #include "chrome/common/win_util.h"
13 #include "chrome/views/client_view.h"
14 #include "chrome/views/default_non_client_view.h"
15 #include "chrome/views/root_view.h"
16 #include "chrome/views/window_delegate.h"
17 #include "grit/generated_resources.h"
18
19 namespace views {
20
21 // A scoping class that prevents a window from being able to redraw in response
22 // to invalidations that may occur within it for the lifetime of the object.
23 //
24 // Why would we want such a thing? Well, it turns out Windows has some
25 // "unorthodox" behavior when it comes to painting its non-client areas.
26 // Occasionally, Windows will paint portions of the default non-client area
27 // right over the top of the custom frame. This is not simply fixed by handling
28 // WM_NCPAINT/WM_PAINT, with some investigation it turns out that this
29 // rendering is being done *inside* the default implementation of some message
30 // handlers and functions:
31 // . WM_SETTEXT
32 // . WM_SETICON
33 // . WM_NCLBUTTONDOWN
34 // . EnableMenuItem, called from our WM_INITMENU handler
35 // The solution is to handle these messages and call DefWindowProc ourselves,
36 // but prevent the window from being able to update itself for the duration of
37 // the call. We do this with this class, which automatically calls its
38 // associated CustomFrameWindow's lock and unlock functions as it is created
39 // and destroyed. See documentation in those methods for the technique used.
40 //
41 // IMPORTANT: Do not use this scoping object for large scopes or periods of
42 // time! IT WILL PREVENT THE WINDOW FROM BEING REDRAWN! (duh).
43 //
44 // I would love to hear Raymond Chen's explanation for all this. And maybe a
45 // list of other messages that this applies to ;-)
46 class CustomFrameWindow::ScopedRedrawLock {
47 public:
48 explicit ScopedRedrawLock(CustomFrameWindow* window) : window_(window) {
49 window_->LockUpdates();
50 }
51
52 ~ScopedRedrawLock() {
53 window_->UnlockUpdates();
54 }
55
56 private:
57 // The window having its style changed.
58 CustomFrameWindow* window_;
59 };
60
61 HCURSOR CustomFrameWindow::resize_cursors_[6];
62
63 ///////////////////////////////////////////////////////////////////////////////
64 // CustomFrameWindow, public:
65
66 CustomFrameWindow::CustomFrameWindow(WindowDelegate* window_delegate)
67 : Window(window_delegate),
68 is_active_(false),
69 lock_updates_(false),
70 saved_window_style_(0) {
71 InitClass();
72 non_client_view_ = new DefaultNonClientView(this);
73 }
74
75 CustomFrameWindow::CustomFrameWindow(WindowDelegate* window_delegate,
76 NonClientView* non_client_view)
77 : Window(window_delegate) {
78 InitClass();
79 non_client_view_ = non_client_view;
80 }
81
82 CustomFrameWindow::~CustomFrameWindow() {
83 }
84
85 ///////////////////////////////////////////////////////////////////////////////
86 // CustomFrameWindow, Window overrides:
87
88 void CustomFrameWindow::Init(HWND parent, const gfx::Rect& bounds) {
89 // TODO(beng): (Cleanup) Right now, the only way to specify a different
90 // non-client view is to subclass this object and provide one
91 // by setting this member before calling Init.
92 if (!non_client_view_)
93 non_client_view_ = new DefaultNonClientView(this);
94 Window::Init(parent, bounds);
95
96 ResetWindowRegion();
97 }
98
99 void CustomFrameWindow::UpdateWindowTitle() {
100 // Layout winds up causing the title to be re-validated during
101 // string measurement.
102 non_client_view_->Layout();
103 // Must call the base class too so that places like the Task Bar get updated.
104 Window::UpdateWindowTitle();
105 }
106
107 void CustomFrameWindow::UpdateWindowIcon() {
108 // The icon will be re-validated during painting.
109 non_client_view_->SchedulePaint();
110 // Call the base class so that places like the Task Bar get updated.
111 Window::UpdateWindowIcon();
112 }
113
114 void CustomFrameWindow::EnableClose(bool enable) {
115 non_client_view_->EnableClose(enable);
116 // Make sure the SysMenu changes to reflect this change as well.
117 Window::EnableClose(enable);
118 }
119
120 void CustomFrameWindow::DisableInactiveRendering(bool disable) {
121 Window::DisableInactiveRendering(disable);
122 non_client_view_->set_paint_as_active(disable);
123 if (!disable)
124 non_client_view_->SchedulePaint();
125 }
126
127 void CustomFrameWindow::SizeWindowToDefault() {
128 gfx::Size pref = client_view()->GetPreferredSize();
129 DCHECK(pref.width() > 0 && pref.height() > 0);
130 gfx::Size window_size =
131 non_client_view_->CalculateWindowSizeForClientSize(pref.width(),
132 pref.height());
133 win_util::CenterAndSizeWindow(owning_window(), GetHWND(),
134 window_size.ToSIZE(), false);
135 }
136
137 ///////////////////////////////////////////////////////////////////////////////
138 // CustomFrameWindow, WidgetWin overrides:
139
140 static void EnableMenuItem(HMENU menu, UINT command, bool enabled) {
141 UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
142 EnableMenuItem(menu, command, flags);
143 }
144
145 void CustomFrameWindow::OnInitMenu(HMENU menu) {
146 bool is_minimized = IsMinimized();
147 bool is_maximized = IsMaximized();
148 bool is_restored = !is_minimized && !is_maximized;
149
150 ScopedRedrawLock lock(this);
151 EnableMenuItem(menu, SC_RESTORE, !is_restored);
152 EnableMenuItem(menu, SC_MOVE, is_restored);
153 EnableMenuItem(menu, SC_SIZE, window_delegate()->CanResize() && is_restored);
154 EnableMenuItem(menu, SC_MAXIMIZE,
155 window_delegate()->CanMaximize() && !is_maximized);
156 EnableMenuItem(menu, SC_MINIMIZE,
157 window_delegate()->CanMaximize() && !is_minimized);
158 }
159
160 void CustomFrameWindow::OnMouseLeave() {
161 bool process_mouse_exited = true;
162 POINT pt;
163 if (GetCursorPos(&pt)) {
164 LRESULT ht_component =
165 ::SendMessage(GetHWND(), WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y));
166 if (ht_component != HTNOWHERE) {
167 // If the mouse moved into a part of the window's non-client area, then
168 // don't send a mouse exited event since the mouse is still within the
169 // bounds of the ChromeView that's rendering the frame. Note that we do
170 // _NOT_ do this for windows with native frames, since in that case the
171 // mouse really will have left the bounds of the RootView.
172 process_mouse_exited = false;
173 }
174 }
175
176 if (process_mouse_exited)
177 ProcessMouseExited();
178 }
179
180 LRESULT CustomFrameWindow::OnNCActivate(BOOL active) {
181 is_active_ = !!active;
182
183 // We can get WM_NCACTIVATE before we're actually visible. If we're not
184 // visible, no need to paint.
185 if (IsWindowVisible(GetHWND())) {
186 non_client_view_->SchedulePaint();
187 // We need to force a paint now, as a user dragging a window will block
188 // painting operations while the move is in progress.
189 PaintNow(root_view_->GetScheduledPaintRect());
190 }
191
192 // Defering to our parent as it is important that the NCActivate message gets
193 // DefProc'ed or the task bar won't show our process as active.
194 // See bug http://crbug.com/4513.
195 return Window::OnNCActivate(active);
196 }
197
198 LRESULT CustomFrameWindow::OnNCCalcSize(BOOL mode, LPARAM l_param) {
199 // We need to repaint all when the window bounds change.
200 return WVR_REDRAW;
201 }
202
203 LRESULT CustomFrameWindow::OnNCHitTest(const CPoint& point) {
204 // NC points are in screen coordinates.
205 CPoint temp = point;
206 MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1);
207 return non_client_view_->NonClientHitTest(gfx::Point(temp.x, temp.y));
208 }
209
210 struct ClipState {
211 // The window being painted.
212 HWND parent;
213
214 // DC painting to.
215 HDC dc;
216
217 // Origin of the window in terms of the screen.
218 int x;
219 int y;
220 };
221
222 // See comments in OnNCPaint for details of this function.
223 static BOOL CALLBACK ClipDCToChild(HWND window, LPARAM param) {
224 ClipState* clip_state = reinterpret_cast<ClipState*>(param);
225 if (GetParent(window) == clip_state->parent && IsWindowVisible(window)) {
226 RECT bounds;
227 GetWindowRect(window, &bounds);
228 ExcludeClipRect(clip_state->dc,
229 bounds.left - clip_state->x,
230 bounds.top - clip_state->y,
231 bounds.right - clip_state->x,
232 bounds.bottom - clip_state->y);
233 }
234 return TRUE;
235 }
236
237 void CustomFrameWindow::OnNCPaint(HRGN rgn) {
238 // We have an NC region and need to paint it. We expand the NC region to
239 // include the dirty region of the root view. This is done to minimize
240 // paints.
241 CRect window_rect;
242 GetWindowRect(&window_rect);
243
244 if (window_rect.Width() != root_view_->width() ||
245 window_rect.Height() != root_view_->height()) {
246 // If the size of the window differs from the size of the root view it
247 // means we're being asked to paint before we've gotten a WM_SIZE. This can
248 // happen when the user is interactively resizing the window. To avoid
249 // mass flickering we don't do anything here. Once we get the WM_SIZE we'll
250 // reset the region of the window which triggers another WM_NCPAINT and
251 // all is well.
252 return;
253 }
254
255 CRect dirty_region;
256 // A value of 1 indicates paint all.
257 if (!rgn || rgn == reinterpret_cast<HRGN>(1)) {
258 dirty_region = CRect(0, 0, window_rect.Width(), window_rect.Height());
259 } else {
260 RECT rgn_bounding_box;
261 GetRgnBox(rgn, &rgn_bounding_box);
262 if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect))
263 return; // Dirty region doesn't intersect window bounds, bale.
264
265 // rgn_bounding_box is in screen coordinates. Map it to window coordinates.
266 OffsetRect(&dirty_region, -window_rect.left, -window_rect.top);
267 }
268
269 // In theory GetDCEx should do what we want, but I couldn't get it to work.
270 // In particular the docs mentiond DCX_CLIPCHILDREN, but as far as I can tell
271 // it doesn't work at all. So, instead we get the DC for the window then
272 // manually clip out the children.
273 HDC dc = GetWindowDC(GetHWND());
274 ClipState clip_state;
275 clip_state.x = window_rect.left;
276 clip_state.y = window_rect.top;
277 clip_state.parent = GetHWND();
278 clip_state.dc = dc;
279 EnumChildWindows(GetHWND(), &ClipDCToChild,
280 reinterpret_cast<LPARAM>(&clip_state));
281
282 RootView* root_view = GetRootView();
283 CRect old_paint_region = root_view->GetScheduledPaintRectConstrainedToSize();
284
285 if (!old_paint_region.IsRectEmpty()) {
286 // The root view has a region that needs to be painted. Include it in the
287 // region we're going to paint.
288
289 CRect tmp = dirty_region;
290 UnionRect(&dirty_region, &tmp, &old_paint_region);
291 }
292
293 root_view->SchedulePaint(gfx::Rect(dirty_region), false);
294
295 // ChromeCanvasPaints destructor does the actual painting. As such, wrap the
296 // following in a block to force paint to occur so that we can release the dc.
297 {
298 ChromeCanvasPaint canvas(dc, opaque(), dirty_region.left, dirty_region.top,
299 dirty_region.Width(), dirty_region.Height());
300
301 root_view->ProcessPaint(&canvas);
302 }
303
304 ReleaseDC(GetHWND(), dc);
305 }
306
307 void CustomFrameWindow::OnNCLButtonDown(UINT ht_component,
308 const CPoint& point) {
309 switch (ht_component) {
310 case HTCLOSE:
311 case HTMINBUTTON:
312 case HTMAXBUTTON: {
313 // When the mouse is pressed down in these specific non-client areas, we
314 // need to tell the RootView to send the mouse pressed event (which sets
315 // capture, allowing subsequent WM_LBUTTONUP (note, _not_ WM_NCLBUTTONUP)
316 // to fire so that the appropriate WM_SYSCOMMAND can be sent by the
317 // applicable button's ButtonListener. We _have_ to do this this way
318 // rather than letting Windows just send the syscommand itself (as would
319 // happen if we never did this dance) because for some insane reason
320 // DefWindowProc for WM_NCLBUTTONDOWN also renders the pressed window
321 // control button appearance, in the Windows classic style, over our
322 // view! Ick! By handling this message we prevent Windows from doing this
323 // undesirable thing, but that means we need to roll the sys-command
324 // handling ourselves.
325 ProcessNCMousePress(point, MK_LBUTTON);
326 return;
327 }
328 default:
329 Window::OnNCLButtonDown(ht_component, point);
330 /*
331 if (!IsMsgHandled()) {
332 // Window::OnNCLButtonDown set the message as unhandled. This normally
333 // means WidgetWin::ProcessWindowMessage will pass it to
334 // DefWindowProc. Sadly, DefWindowProc for WM_NCLBUTTONDOWN does weird
335 // non-client painting, so we need to call it directly here inside a
336 // scoped update lock.
337 ScopedRedrawLock lock(this);
338 DefWindowProc(GetHWND(), WM_NCLBUTTONDOWN, ht_component,
339 MAKELPARAM(point.x, point.y));
340 SetMsgHandled(TRUE);
341 }
342 */
343 break;
344 }
345 }
346
347 LRESULT CustomFrameWindow::OnNCUAHDrawCaption(UINT msg, WPARAM w_param,
348 LPARAM l_param) {
349 // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
350 // an explanation about why we need to handle this message.
351 SetMsgHandled(TRUE);
352 return 0;
353 }
354
355 LRESULT CustomFrameWindow::OnNCUAHDrawFrame(UINT msg, WPARAM w_param,
356 LPARAM l_param) {
357 // See comment in widget_win.h at the definition of WM_NCUAHDRAWCAPTION for
358 // an explanation about why we need to handle this message.
359 SetMsgHandled(TRUE);
360 return 0;
361 }
362
363 LRESULT CustomFrameWindow::OnSetCursor(HWND window, UINT hittest_code,
364 UINT message) {
365 int index = RC_NORMAL;
366 switch (hittest_code) {
367 case HTTOP:
368 case HTBOTTOM:
369 index = RC_VERTICAL;
370 break;
371 case HTTOPLEFT:
372 case HTBOTTOMRIGHT:
373 index = RC_NWSE;
374 break;
375 case HTTOPRIGHT:
376 case HTBOTTOMLEFT:
377 index = RC_NESW;
378 break;
379 case HTLEFT:
380 case HTRIGHT:
381 index = RC_HORIZONTAL;
382 break;
383 case HTCAPTION:
384 case HTCLIENT:
385 index = RC_NORMAL;
386 break;
387 }
388 SetCursor(resize_cursors_[index]);
389 return 0;
390 }
391
392 LRESULT CustomFrameWindow::OnSetIcon(UINT size_type, HICON new_icon) {
393 ScopedRedrawLock lock(this);
394 return DefWindowProc(GetHWND(), WM_SETICON, size_type,
395 reinterpret_cast<LPARAM>(new_icon));
396 }
397
398 LRESULT CustomFrameWindow::OnSetText(const wchar_t* text) {
399 ScopedRedrawLock lock(this);
400 return DefWindowProc(GetHWND(), WM_SETTEXT, NULL,
401 reinterpret_cast<LPARAM>(text));
402 }
403
404 void CustomFrameWindow::OnSize(UINT param, const CSize& size) {
405 Window::OnSize(param, size);
406
407 // ResetWindowRegion is going to trigger WM_NCPAINT. By doing it after we've
408 // invoked OnSize we ensure the RootView has been layed out.
409 ResetWindowRegion();
410 }
411
412 void CustomFrameWindow::OnSysCommand(UINT notification_code, CPoint click) {
413 // Windows uses the 4 lower order bits of |notification_code| for type-
414 // specific information so we must exclude this when comparing.
415 static const int sc_mask = 0xFFF0;
416 if ((notification_code & sc_mask) == SC_MINIMIZE ||
417 (notification_code & sc_mask) == SC_MAXIMIZE ||
418 (notification_code & sc_mask) == SC_RESTORE) {
419 non_client_view_->ResetWindowControls();
420 } else if ((notification_code & sc_mask) == SC_MOVE ||
421 (notification_code & sc_mask) == SC_SIZE) {
422 if (lock_updates_) {
423 // We were locked, before entering a resize or move modal loop. Now that
424 // we've begun to move the window, we need to unlock updates so that the
425 // sizing/moving feedback can be continuous.
426 UnlockUpdates();
427 }
428 }
429 Window::OnSysCommand(notification_code, click);
430 }
431
432 ///////////////////////////////////////////////////////////////////////////////
433 // CustomFrameWindow, private:
434
435 // static
436 void CustomFrameWindow::InitClass() {
437 static bool initialized = false;
438 if (!initialized) {
439 resize_cursors_[RC_NORMAL] = LoadCursor(NULL, IDC_ARROW);
440 resize_cursors_[RC_VERTICAL] = LoadCursor(NULL, IDC_SIZENS);
441 resize_cursors_[RC_HORIZONTAL] = LoadCursor(NULL, IDC_SIZEWE);
442 resize_cursors_[RC_NESW] = LoadCursor(NULL, IDC_SIZENESW);
443 resize_cursors_[RC_NWSE] = LoadCursor(NULL, IDC_SIZENWSE);
444 initialized = true;
445 }
446 }
447
448 void CustomFrameWindow::LockUpdates() {
449 lock_updates_ = true;
450 saved_window_style_ = GetWindowLong(GetHWND(), GWL_STYLE);
451 SetWindowLong(GetHWND(), GWL_STYLE, saved_window_style_ & ~WS_VISIBLE);
452 }
453
454 void CustomFrameWindow::UnlockUpdates() {
455 SetWindowLong(GetHWND(), GWL_STYLE, saved_window_style_);
456 lock_updates_ = false;
457 }
458
459 void CustomFrameWindow::ResetWindowRegion() {
460 // Changing the window region is going to force a paint. Only change the
461 // window region if the region really differs.
462 HRGN current_rgn = CreateRectRgn(0, 0, 0, 0);
463 int current_rgn_result = GetWindowRgn(GetHWND(), current_rgn);
464
465 CRect window_rect;
466 GetWindowRect(&window_rect);
467 HRGN new_region;
468 if (IsMaximized()) {
469 HMONITOR monitor = MonitorFromWindow(GetHWND(), MONITOR_DEFAULTTONEAREST);
470 MONITORINFO mi;
471 mi.cbSize = sizeof mi;
472 GetMonitorInfo(monitor, &mi);
473 CRect work_rect = mi.rcWork;
474 work_rect.OffsetRect(-window_rect.left, -window_rect.top);
475 new_region = CreateRectRgnIndirect(&work_rect);
476 } else {
477 gfx::Path window_mask;
478 non_client_view_->GetWindowMask(gfx::Size(window_rect.Width(),
479 window_rect.Height()),
480 &window_mask);
481 new_region = window_mask.CreateHRGN();
482 }
483
484 if (current_rgn_result == ERROR || !EqualRgn(current_rgn, new_region)) {
485 // SetWindowRgn takes ownership of the HRGN created by CreateHRGN.
486 SetWindowRgn(new_region, TRUE);
487 } else {
488 DeleteObject(new_region);
489 }
490
491 DeleteObject(current_rgn);
492 }
493
494 void CustomFrameWindow::ProcessNCMousePress(const CPoint& point, int flags) {
495 CPoint temp = point;
496 MapWindowPoints(HWND_DESKTOP, GetHWND(), &temp, 1);
497 UINT message_flags = 0;
498 if ((GetKeyState(VK_CONTROL) & 0x80) == 0x80)
499 message_flags |= MK_CONTROL;
500 if ((GetKeyState(VK_SHIFT) & 0x80) == 0x80)
501 message_flags |= MK_SHIFT;
502 message_flags |= flags;
503 ProcessMousePressed(temp, message_flags, false, false);
504 }
505
506 } // namespace views
OLDNEW
« no previous file with comments | « chrome/views/custom_frame_window.h ('k') | chrome/views/default_non_client_view.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698