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/window_win.h" | 5 #include "views/window/window_win.h" |
6 | 6 |
7 #include <dwmapi.h> | 7 #include <dwmapi.h> |
8 #include <shellapi.h> | 8 #include <shellapi.h> |
9 | 9 |
10 #include "app/win/win_util.h" | |
11 #include "base/i18n/rtl.h" | 10 #include "base/i18n/rtl.h" |
| 11 #include "base/win/scoped_gdi_object.h" |
| 12 #include "base/win/win_util.h" |
12 #include "base/win/windows_version.h" | 13 #include "base/win/windows_version.h" |
13 #include "gfx/canvas_skia_paint.h" | 14 #include "gfx/canvas_skia_paint.h" |
14 #include "gfx/font.h" | 15 #include "gfx/font.h" |
15 #include "gfx/icon_util.h" | 16 #include "gfx/icon_util.h" |
16 #include "gfx/path.h" | 17 #include "gfx/path.h" |
17 #include "ui/base/keycodes/keyboard_code_conversion_win.h" | 18 #include "ui/base/keycodes/keyboard_code_conversion_win.h" |
| 19 #include "ui/base/l10n/l10n_util_win.h" |
18 #include "ui/base/theme_provider.h" | 20 #include "ui/base/theme_provider.h" |
19 #include "ui/base/win/hwnd_util.h" | 21 #include "ui/base/win/hwnd_util.h" |
20 #include "views/accessibility/view_accessibility.h" | 22 #include "views/accessibility/view_accessibility.h" |
21 #include "views/widget/root_view.h" | 23 #include "views/widget/root_view.h" |
22 #include "views/window/client_view.h" | 24 #include "views/window/client_view.h" |
23 #include "views/window/custom_frame_view.h" | 25 #include "views/window/custom_frame_view.h" |
24 #include "views/window/native_frame_view.h" | 26 #include "views/window/native_frame_view.h" |
25 #include "views/window/non_client_view.h" | 27 #include "views/window/non_client_view.h" |
26 #include "views/window/window_delegate.h" | 28 #include "views/window/window_delegate.h" |
27 | 29 |
28 namespace { | 30 namespace { |
29 | 31 |
30 static const int kDragFrameWindowAlpha = 200; | 32 static const int kDragFrameWindowAlpha = 200; |
31 | 33 |
| 34 // The thickness of an auto-hide taskbar in pixels. |
| 35 static const int kAutoHideTaskbarThicknessPx = 2; |
| 36 |
32 bool GetMonitorAndRects(const RECT& rect, | 37 bool GetMonitorAndRects(const RECT& rect, |
33 HMONITOR* monitor, | 38 HMONITOR* monitor, |
34 gfx::Rect* monitor_rect, | 39 gfx::Rect* monitor_rect, |
35 gfx::Rect* work_area) { | 40 gfx::Rect* work_area) { |
36 DCHECK(monitor); | 41 DCHECK(monitor); |
37 DCHECK(monitor_rect); | 42 DCHECK(monitor_rect); |
38 DCHECK(work_area); | 43 DCHECK(work_area); |
39 *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); | 44 *monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL); |
40 if (!*monitor) | 45 if (!*monitor) |
41 return false; | 46 return false; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
81 MONITORINFO mi = {0}; | 86 MONITORINFO mi = {0}; |
82 mi.cbSize = sizeof(mi); | 87 mi.cbSize = sizeof(mi); |
83 GetMonitorInfo(monitor, &mi); | 88 GetMonitorInfo(monitor, &mi); |
84 parent_rect = mi.rcWork; | 89 parent_rect = mi.rcWork; |
85 } else { | 90 } else { |
86 NOTREACHED() << "Unable to get default monitor"; | 91 NOTREACHED() << "Unable to get default monitor"; |
87 } | 92 } |
88 } | 93 } |
89 | 94 |
90 gfx::Rect actual_bounds = bounds; | 95 gfx::Rect actual_bounds = bounds; |
91 app::win::EnsureRectIsVisibleInRect(gfx::Rect(parent_rect), &actual_bounds, | 96 views::internal::EnsureRectIsVisibleInRect(gfx::Rect(parent_rect), |
92 padding); | 97 &actual_bounds, padding); |
93 | 98 |
94 SetWindowPos(child_window, insert_after_window, actual_bounds.x(), | 99 SetWindowPos(child_window, insert_after_window, actual_bounds.x(), |
95 actual_bounds.y(), actual_bounds.width(), | 100 actual_bounds.y(), actual_bounds.width(), |
96 actual_bounds.height(), flags); | 101 actual_bounds.height(), flags); |
97 } | 102 } |
98 | 103 |
| 104 // Returns true if edge |edge| (one of ABE_LEFT, TOP, RIGHT, or BOTTOM) of |
| 105 // monitor |monitor| has an auto-hiding taskbar that's always-on-top. |
| 106 bool EdgeHasTopmostAutoHideTaskbar(UINT edge, HMONITOR monitor) { |
| 107 APPBARDATA taskbar_data = { 0 }; |
| 108 taskbar_data.cbSize = sizeof APPBARDATA; |
| 109 taskbar_data.uEdge = edge; |
| 110 HWND taskbar = reinterpret_cast<HWND>(SHAppBarMessage(ABM_GETAUTOHIDEBAR, |
| 111 &taskbar_data)); |
| 112 return ::IsWindow(taskbar) && (monitor != NULL) && |
| 113 (MonitorFromWindow(taskbar, MONITOR_DEFAULTTONULL) == monitor) && |
| 114 (GetWindowLong(taskbar, GWL_EXSTYLE) & WS_EX_TOPMOST); |
| 115 } |
| 116 |
99 } // namespace | 117 } // namespace |
100 | 118 |
101 namespace views { | 119 namespace views { |
102 | 120 |
| 121 namespace internal { |
| 122 |
| 123 void EnsureRectIsVisibleInRect(const gfx::Rect& parent_rect, |
| 124 gfx::Rect* child_rect, |
| 125 int padding) { |
| 126 DCHECK(child_rect); |
| 127 |
| 128 // We use padding here because it allows some of the original web page to |
| 129 // bleed through around the edges. |
| 130 int twice_padding = padding * 2; |
| 131 |
| 132 // FIRST, clamp width and height so we don't open child windows larger than |
| 133 // the containing parent. |
| 134 if (child_rect->width() > (parent_rect.width() + twice_padding)) |
| 135 child_rect->set_width(std::max(0, parent_rect.width() - twice_padding)); |
| 136 if (child_rect->height() > parent_rect.height() + twice_padding) |
| 137 child_rect->set_height(std::max(0, parent_rect.height() - twice_padding)); |
| 138 |
| 139 // SECOND, clamp x,y position to padding,padding so we don't position child |
| 140 // windows in hyperspace. |
| 141 // TODO(mpcomplete): I don't see what the second check in each 'if' does that |
| 142 // isn't handled by the LAST set of 'ifs'. Maybe we can remove it. |
| 143 if (child_rect->x() < parent_rect.x() || |
| 144 child_rect->x() > parent_rect.right()) { |
| 145 child_rect->set_x(parent_rect.x() + padding); |
| 146 } |
| 147 if (child_rect->y() < parent_rect.y() || |
| 148 child_rect->y() > parent_rect.bottom()) { |
| 149 child_rect->set_y(parent_rect.y() + padding); |
| 150 } |
| 151 |
| 152 // LAST, nudge the window back up into the client area if its x,y position is |
| 153 // within the parent bounds but its width/height place it off-screen. |
| 154 if (child_rect->bottom() > parent_rect.bottom()) |
| 155 child_rect->set_y(parent_rect.bottom() - child_rect->height() - padding); |
| 156 if (child_rect->right() > parent_rect.right()) |
| 157 child_rect->set_x(parent_rect.right() - child_rect->width() - padding); |
| 158 } |
| 159 |
| 160 } // namespace internal |
| 161 |
103 // A scoping class that prevents a window from being able to redraw in response | 162 // A scoping class that prevents a window from being able to redraw in response |
104 // to invalidations that may occur within it for the lifetime of the object. | 163 // to invalidations that may occur within it for the lifetime of the object. |
105 // | 164 // |
106 // Why would we want such a thing? Well, it turns out Windows has some | 165 // Why would we want such a thing? Well, it turns out Windows has some |
107 // "unorthodox" behavior when it comes to painting its non-client areas. | 166 // "unorthodox" behavior when it comes to painting its non-client areas. |
108 // Occasionally, Windows will paint portions of the default non-client area | 167 // Occasionally, Windows will paint portions of the default non-client area |
109 // right over the top of the custom frame. This is not simply fixed by handling | 168 // right over the top of the custom frame. This is not simply fixed by handling |
110 // WM_NCPAINT/WM_PAINT, with some investigation it turns out that this | 169 // WM_NCPAINT/WM_PAINT, with some investigation it turns out that this |
111 // rendering is being done *inside* the default implementation of some message | 170 // rendering is being done *inside* the default implementation of some message |
112 // handlers and functions: | 171 // handlers and functions: |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
235 SendMessage(GetNativeView(), WM_SYSCOMMAND, command, 0); | 294 SendMessage(GetNativeView(), WM_SYSCOMMAND, command, 0); |
236 } | 295 } |
237 | 296 |
238 namespace { | 297 namespace { |
239 static BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) { | 298 static BOOL CALLBACK SendDwmCompositionChanged(HWND window, LPARAM param) { |
240 SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0); | 299 SendMessage(window, WM_DWMCOMPOSITIONCHANGED, 0, 0); |
241 return TRUE; | 300 return TRUE; |
242 } | 301 } |
243 } // namespace | 302 } // namespace |
244 | 303 |
245 void WindowWin::FrameTypeChanged() { | |
246 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
247 // We need to toggle the rendering policy of the DWM/glass frame as we | |
248 // change from opaque to glass. "Non client rendering enabled" means that | |
249 // the DWM's glass non-client rendering is enabled, which is why | |
250 // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the | |
251 // DWM doesn't render glass, and so is used in the custom frame case. | |
252 DWMNCRENDERINGPOLICY policy = | |
253 non_client_view_->UseNativeFrame() ? DWMNCRP_ENABLED | |
254 : DWMNCRP_DISABLED; | |
255 DwmSetWindowAttribute(GetNativeView(), DWMWA_NCRENDERING_POLICY, | |
256 &policy, sizeof(DWMNCRENDERINGPOLICY)); | |
257 } | |
258 | |
259 // Send a frame change notification, since the non-client metrics have | |
260 // changed. | |
261 SetWindowPos(NULL, 0, 0, 0, 0, | |
262 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | | |
263 SWP_NOOWNERZORDER | SWP_NOACTIVATE); | |
264 | |
265 // The frame type change results in the client rect changing size, but this | |
266 // does not explicitly send a WM_SIZE, so we need to force the root view to | |
267 // be resized now. | |
268 LayoutRootView(); | |
269 | |
270 // Update the non-client view with the correct frame view for the active frame | |
271 // type. | |
272 non_client_view_->UpdateFrame(); | |
273 | |
274 // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want | |
275 // to notify our children too, since we can have MDI child windows who need to | |
276 // update their appearance. | |
277 EnumChildWindows(GetNativeView(), &SendDwmCompositionChanged, NULL); | |
278 } | |
279 | |
280 //////////////////////////////////////////////////////////////////////////////// | 304 //////////////////////////////////////////////////////////////////////////////// |
281 // WindowWin, Window implementation: | 305 // WindowWin, Window implementation: |
282 | 306 |
283 void WindowWin::Show() { | 307 void WindowWin::Show() { |
284 int show_state = GetShowState(); | 308 int show_state = GetShowState(); |
285 if (saved_maximized_state_) | 309 if (saved_maximized_state_) |
286 show_state = SW_SHOWMAXIMIZED; | 310 show_state = SW_SHOWMAXIMIZED; |
287 Show(show_state); | 311 Show(show_state); |
288 } | 312 } |
289 | 313 |
(...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
537 return GetNativeView(); | 561 return GetNativeView(); |
538 } | 562 } |
539 | 563 |
540 bool WindowWin::ShouldUseNativeFrame() const { | 564 bool WindowWin::ShouldUseNativeFrame() const { |
541 ui::ThemeProvider* tp = GetThemeProvider(); | 565 ui::ThemeProvider* tp = GetThemeProvider(); |
542 if (!tp) | 566 if (!tp) |
543 return WidgetWin::IsAeroGlassEnabled(); | 567 return WidgetWin::IsAeroGlassEnabled(); |
544 return tp->ShouldUseNativeFrame(); | 568 return tp->ShouldUseNativeFrame(); |
545 } | 569 } |
546 | 570 |
| 571 void WindowWin::FrameTypeChanged() { |
| 572 // Called when the frame type could possibly be changing (theme change or |
| 573 // DWM composition change). |
| 574 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { |
| 575 // We need to toggle the rendering policy of the DWM/glass frame as we |
| 576 // change from opaque to glass. "Non client rendering enabled" means that |
| 577 // the DWM's glass non-client rendering is enabled, which is why |
| 578 // DWMNCRP_ENABLED is used for the native frame case. _DISABLED means the |
| 579 // DWM doesn't render glass, and so is used in the custom frame case. |
| 580 DWMNCRENDERINGPOLICY policy = |
| 581 non_client_view_->UseNativeFrame() ? DWMNCRP_ENABLED |
| 582 : DWMNCRP_DISABLED; |
| 583 DwmSetWindowAttribute(GetNativeView(), DWMWA_NCRENDERING_POLICY, |
| 584 &policy, sizeof(DWMNCRENDERINGPOLICY)); |
| 585 } |
| 586 |
| 587 // Send a frame change notification, since the non-client metrics have |
| 588 // changed. |
| 589 SetWindowPos(NULL, 0, 0, 0, 0, |
| 590 SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | |
| 591 SWP_NOOWNERZORDER | SWP_NOACTIVATE); |
| 592 |
| 593 // The frame type change results in the client rect changing size, but this |
| 594 // does not explicitly send a WM_SIZE, so we need to force the root view to |
| 595 // be resized now. |
| 596 LayoutRootView(); |
| 597 |
| 598 // Update the non-client view with the correct frame view for the active frame |
| 599 // type. |
| 600 non_client_view_->UpdateFrame(); |
| 601 |
| 602 // WM_DWMCOMPOSITIONCHANGED is only sent to top level windows, however we want |
| 603 // to notify our children too, since we can have MDI child windows who need to |
| 604 // update their appearance. |
| 605 EnumChildWindows(GetNativeView(), &SendDwmCompositionChanged, NULL); |
| 606 } |
| 607 |
| 608 |
| 609 // static |
| 610 gfx::Font WindowWin::GetWindowTitleFont() { |
| 611 NONCLIENTMETRICS ncm; |
| 612 base::win::GetNonClientMetrics(&ncm); |
| 613 l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); |
| 614 base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); |
| 615 return gfx::Font(caption_font); |
| 616 } |
| 617 |
547 /////////////////////////////////////////////////////////////////////////////// | 618 /////////////////////////////////////////////////////////////////////////////// |
548 // WindowWin, protected: | 619 // WindowWin, protected: |
549 | 620 |
550 WindowWin::WindowWin(WindowDelegate* window_delegate) | 621 WindowWin::WindowWin(WindowDelegate* window_delegate) |
551 : WidgetWin(), | 622 : WidgetWin(), |
552 focus_on_creation_(true), | 623 focus_on_creation_(true), |
553 window_delegate_(window_delegate), | 624 window_delegate_(window_delegate), |
554 non_client_view_(new NonClientView(this)), | 625 non_client_view_(new NonClientView(this)), |
555 owning_hwnd_(NULL), | 626 owning_hwnd_(NULL), |
556 minimum_size_(100, 100), | 627 minimum_size_(100, 100), |
(...skipping 279 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
836 // the window rect given with WM_NCCALCSIZE (which is our previous | 907 // the window rect given with WM_NCCALCSIZE (which is our previous |
837 // restored window position) we will get the correct monitor handle. | 908 // restored window position) we will get the correct monitor handle. |
838 monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); | 909 monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL); |
839 if (!monitor) { | 910 if (!monitor) { |
840 // This is probably an extreme case that we won't hit, but if we don't | 911 // This is probably an extreme case that we won't hit, but if we don't |
841 // intersect any monitor, let us not adjust the client rect since our | 912 // intersect any monitor, let us not adjust the client rect since our |
842 // window will not be visible anyway. | 913 // window will not be visible anyway. |
843 return 0; | 914 return 0; |
844 } | 915 } |
845 } | 916 } |
846 if (app::win::EdgeHasTopmostAutoHideTaskbar(ABE_LEFT, monitor)) | 917 if (EdgeHasTopmostAutoHideTaskbar(ABE_LEFT, monitor)) |
847 client_rect->left += app::win::kAutoHideTaskbarThicknessPx; | 918 client_rect->left += kAutoHideTaskbarThicknessPx; |
848 if (app::win::EdgeHasTopmostAutoHideTaskbar(ABE_TOP, monitor)) { | 919 if (EdgeHasTopmostAutoHideTaskbar(ABE_TOP, monitor)) { |
849 if (GetNonClientView()->UseNativeFrame()) { | 920 if (GetNonClientView()->UseNativeFrame()) { |
850 // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of | 921 // Tricky bit. Due to a bug in DwmDefWindowProc()'s handling of |
851 // WM_NCHITTEST, having any nonclient area atop the window causes the | 922 // WM_NCHITTEST, having any nonclient area atop the window causes the |
852 // caption buttons to draw onscreen but not respond to mouse | 923 // caption buttons to draw onscreen but not respond to mouse |
853 // hover/clicks. | 924 // hover/clicks. |
854 // So for a taskbar at the screen top, we can't push the | 925 // So for a taskbar at the screen top, we can't push the |
855 // client_rect->top down; instead, we move the bottom up by one pixel, | 926 // client_rect->top down; instead, we move the bottom up by one pixel, |
856 // which is the smallest change we can make and still get a client area | 927 // which is the smallest change we can make and still get a client area |
857 // less than the screen size. This is visibly ugly, but there seems to | 928 // less than the screen size. This is visibly ugly, but there seems to |
858 // be no better solution. | 929 // be no better solution. |
859 --client_rect->bottom; | 930 --client_rect->bottom; |
860 } else { | 931 } else { |
861 client_rect->top += app::win::kAutoHideTaskbarThicknessPx; | 932 client_rect->top += kAutoHideTaskbarThicknessPx; |
862 } | 933 } |
863 } | 934 } |
864 if (app::win::EdgeHasTopmostAutoHideTaskbar(ABE_RIGHT, monitor)) | 935 if (EdgeHasTopmostAutoHideTaskbar(ABE_RIGHT, monitor)) |
865 client_rect->right -= app::win::kAutoHideTaskbarThicknessPx; | 936 client_rect->right -= kAutoHideTaskbarThicknessPx; |
866 if (app::win::EdgeHasTopmostAutoHideTaskbar(ABE_BOTTOM, monitor)) | 937 if (EdgeHasTopmostAutoHideTaskbar(ABE_BOTTOM, monitor)) |
867 client_rect->bottom -= app::win::kAutoHideTaskbarThicknessPx; | 938 client_rect->bottom -= kAutoHideTaskbarThicknessPx; |
868 | 939 |
869 // We cannot return WVR_REDRAW when there is nonclient area, or Windows | 940 // We cannot return WVR_REDRAW when there is nonclient area, or Windows |
870 // exhibits bugs where client pixels and child HWNDs are mispositioned by | 941 // exhibits bugs where client pixels and child HWNDs are mispositioned by |
871 // the width/height of the upper-left nonclient area. | 942 // the width/height of the upper-left nonclient area. |
872 return 0; | 943 return 0; |
873 } | 944 } |
874 | 945 |
875 // If the window bounds change, we're going to relayout and repaint anyway. | 946 // If the window bounds change, we're going to relayout and repaint anyway. |
876 // Returning WVR_REDRAW avoids an extra paint before that of the old client | 947 // Returning WVR_REDRAW avoids an extra paint before that of the old client |
877 // pixels in the (now wrong) location, and thus makes actions like resizing a | 948 // pixels in the (now wrong) location, and thus makes actions like resizing a |
(...skipping 697 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1575 Window::CloseSecondaryWidget(root_view->GetWidget()); | 1646 Window::CloseSecondaryWidget(root_view->GetWidget()); |
1576 return TRUE; | 1647 return TRUE; |
1577 } | 1648 } |
1578 } // namespace | 1649 } // namespace |
1579 | 1650 |
1580 void Window::CloseAllSecondaryWindows() { | 1651 void Window::CloseAllSecondaryWindows() { |
1581 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0); | 1652 EnumThreadWindows(GetCurrentThreadId(), WindowCallbackProc, 0); |
1582 } | 1653 } |
1583 | 1654 |
1584 } // namespace views | 1655 } // namespace views |
OLD | NEW |