OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "views/window/non_client_view.h" | |
6 | |
7 #include "ui/base/accessibility/accessible_view_state.h" | |
8 #include "ui/base/hit_test.h" | |
9 #include "views/widget/root_view.h" | |
10 #include "views/widget/widget.h" | |
11 #include "views/window/client_view.h" | |
12 | |
13 namespace views { | |
14 | |
15 // static | |
16 const int NonClientFrameView::kFrameShadowThickness = 1; | |
17 const int NonClientFrameView::kClientEdgeThickness = 1; | |
18 const char NonClientFrameView::kViewClassName[] = | |
19 "views/window/NonClientFrameView"; | |
20 | |
21 const char NonClientView::kViewClassName[] = | |
22 "views/window/NonClientView"; | |
23 | |
24 // The frame view and the client view are always at these specific indices, | |
25 // because the RootView message dispatch sends messages to items higher in the | |
26 // z-order first and we always want the client view to have first crack at | |
27 // handling mouse messages. | |
28 static const int kFrameViewIndex = 0; | |
29 static const int kClientViewIndex = 1; | |
30 | |
31 //////////////////////////////////////////////////////////////////////////////// | |
32 // NonClientView, public: | |
33 | |
34 NonClientView::NonClientView() | |
35 : client_view_(NULL) { | |
36 } | |
37 | |
38 NonClientView::~NonClientView() { | |
39 // This value may have been reset before the window hierarchy shuts down, | |
40 // so we need to manually remove it. | |
41 RemoveChildView(frame_view_.get()); | |
42 } | |
43 | |
44 void NonClientView::SetFrameView(NonClientFrameView* frame_view) { | |
45 // See comment in header about ownership. | |
46 frame_view->set_parent_owned(false); | |
47 if (frame_view_.get()) | |
48 RemoveChildView(frame_view_.get()); | |
49 frame_view_.reset(frame_view); | |
50 if (parent()) | |
51 AddChildViewAt(frame_view_.get(), kFrameViewIndex); | |
52 } | |
53 | |
54 bool NonClientView::CanClose() { | |
55 return client_view_->CanClose(); | |
56 } | |
57 | |
58 void NonClientView::WindowClosing() { | |
59 client_view_->WidgetClosing(); | |
60 } | |
61 | |
62 void NonClientView::UpdateFrame() { | |
63 Widget* widget = GetWidget(); | |
64 SetFrameView(widget->CreateNonClientFrameView()); | |
65 widget->ThemeChanged(); | |
66 Layout(); | |
67 SchedulePaint(); | |
68 widget->UpdateFrameAfterFrameChange(); | |
69 } | |
70 | |
71 void NonClientView::SetInactiveRenderingDisabled(bool disable) { | |
72 frame_view_->SetInactiveRenderingDisabled(disable); | |
73 } | |
74 | |
75 gfx::Rect NonClientView::GetWindowBoundsForClientBounds( | |
76 const gfx::Rect client_bounds) const { | |
77 return frame_view_->GetWindowBoundsForClientBounds(client_bounds); | |
78 } | |
79 | |
80 int NonClientView::NonClientHitTest(const gfx::Point& point) { | |
81 // The NonClientFrameView is responsible for also asking the ClientView. | |
82 return frame_view_->NonClientHitTest(point); | |
83 } | |
84 | |
85 void NonClientView::GetWindowMask(const gfx::Size& size, | |
86 gfx::Path* window_mask) { | |
87 frame_view_->GetWindowMask(size, window_mask); | |
88 } | |
89 | |
90 void NonClientView::EnableClose(bool enable) { | |
91 frame_view_->EnableClose(enable); | |
92 } | |
93 | |
94 void NonClientView::ResetWindowControls() { | |
95 frame_view_->ResetWindowControls(); | |
96 } | |
97 | |
98 void NonClientView::UpdateWindowIcon() { | |
99 frame_view_->UpdateWindowIcon(); | |
100 } | |
101 | |
102 void NonClientView::LayoutFrameView() { | |
103 // First layout the NonClientFrameView, which determines the size of the | |
104 // ClientView... | |
105 frame_view_->SetBounds(0, 0, width(), height()); | |
106 | |
107 // We need to manually call Layout here because layout for the frame view can | |
108 // change independently of the bounds changing - e.g. after the initial | |
109 // display of the window the metrics of the native window controls can change, | |
110 // which does not change the bounds of the window but requires a re-layout to | |
111 // trigger a repaint. We override OnBoundsChanged() for the NonClientFrameView | |
112 // to do nothing so that SetBounds above doesn't cause Layout to be called | |
113 // twice. | |
114 frame_view_->Layout(); | |
115 } | |
116 | |
117 void NonClientView::SetAccessibleName(const string16& name) { | |
118 accessible_name_ = name; | |
119 } | |
120 | |
121 //////////////////////////////////////////////////////////////////////////////// | |
122 // NonClientView, View overrides: | |
123 | |
124 gfx::Size NonClientView::GetPreferredSize() { | |
125 // TODO(pkasting): This should probably be made to look similar to | |
126 // GetMinimumSize() below. This will require implementing GetPreferredSize() | |
127 // better in the various frame views. | |
128 gfx::Rect client_bounds(gfx::Point(), client_view_->GetPreferredSize()); | |
129 return GetWindowBoundsForClientBounds(client_bounds).size(); | |
130 } | |
131 | |
132 gfx::Size NonClientView::GetMinimumSize() { | |
133 return frame_view_->GetMinimumSize(); | |
134 } | |
135 | |
136 void NonClientView::Layout() { | |
137 LayoutFrameView(); | |
138 | |
139 // Then layout the ClientView, using those bounds. | |
140 client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView()); | |
141 | |
142 // We need to manually call Layout on the ClientView as well for the same | |
143 // reason as above. | |
144 client_view_->Layout(); | |
145 } | |
146 | |
147 void NonClientView::ViewHierarchyChanged(bool is_add, View* parent, | |
148 View* child) { | |
149 // Add our two child views here as we are added to the Widget so that if we | |
150 // are subsequently resized all the parent-child relationships are | |
151 // established. | |
152 if (is_add && GetWidget() && child == this) { | |
153 AddChildViewAt(frame_view_.get(), kFrameViewIndex); | |
154 AddChildViewAt(client_view_, kClientViewIndex); | |
155 } | |
156 } | |
157 | |
158 void NonClientView::GetAccessibleState(ui::AccessibleViewState* state) { | |
159 state->role = ui::AccessibilityTypes::ROLE_WINDOW; | |
160 state->name = accessible_name_; | |
161 } | |
162 | |
163 std::string NonClientView::GetClassName() const { | |
164 return kViewClassName; | |
165 } | |
166 | |
167 views::View* NonClientView::GetEventHandlerForPoint(const gfx::Point& point) { | |
168 // Because of the z-ordering of our child views (the client view is positioned | |
169 // over the non-client frame view, if the client view ever overlaps the frame | |
170 // view visually (as it does for the browser window), then it will eat mouse | |
171 // events for the window controls. We override this method here so that we can | |
172 // detect this condition and re-route the events to the non-client frame view. | |
173 // The assumption is that the frame view's implementation of HitTest will only | |
174 // return true for area not occupied by the client view. | |
175 gfx::Point point_in_child_coords(point); | |
176 View::ConvertPointToView(this, frame_view_.get(), &point_in_child_coords); | |
177 if (frame_view_->HitTest(point_in_child_coords)) | |
178 return frame_view_->GetEventHandlerForPoint(point_in_child_coords); | |
179 | |
180 return View::GetEventHandlerForPoint(point); | |
181 } | |
182 | |
183 //////////////////////////////////////////////////////////////////////////////// | |
184 // NonClientFrameView, public: | |
185 | |
186 void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) { | |
187 // See comment in Widget::SetInactiveRenderingDisabled as to why we don't | |
188 // conditionally invoke ShouldPaintAsActiveChanged. | |
189 paint_as_active_ = disable; | |
190 ShouldPaintAsActiveChanged(); | |
191 } | |
192 | |
193 //////////////////////////////////////////////////////////////////////////////// | |
194 // NonClientFrameView, View overrides: | |
195 | |
196 bool NonClientFrameView::HitTest(const gfx::Point& l) const { | |
197 // For the default case, we assume the non-client frame view never overlaps | |
198 // the client view. | |
199 return !GetWidget()->client_view()->bounds().Contains(l); | |
200 } | |
201 | |
202 //////////////////////////////////////////////////////////////////////////////// | |
203 // NonClientFrameView, protected: | |
204 | |
205 int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point, | |
206 int top_resize_border_height, | |
207 int resize_border_thickness, | |
208 int top_resize_corner_height, | |
209 int resize_corner_width, | |
210 bool can_resize) { | |
211 // Tricky: In XP, native behavior is to return HTTOPLEFT and HTTOPRIGHT for | |
212 // a |resize_corner_size|-length strip of both the side and top borders, but | |
213 // only to return HTBOTTOMLEFT/HTBOTTOMRIGHT along the bottom border + corner | |
214 // (not the side border). Vista goes further and doesn't return these on any | |
215 // of the side borders. We allow callers to match either behavior. | |
216 int component; | |
217 if (point.x() < resize_border_thickness) { | |
218 if (point.y() < top_resize_corner_height) | |
219 component = HTTOPLEFT; | |
220 else if (point.y() >= (height() - resize_border_thickness)) | |
221 component = HTBOTTOMLEFT; | |
222 else | |
223 component = HTLEFT; | |
224 } else if (point.x() >= (width() - resize_border_thickness)) { | |
225 if (point.y() < top_resize_corner_height) | |
226 component = HTTOPRIGHT; | |
227 else if (point.y() >= (height() - resize_border_thickness)) | |
228 component = HTBOTTOMRIGHT; | |
229 else | |
230 component = HTRIGHT; | |
231 } else if (point.y() < top_resize_border_height) { | |
232 if (point.x() < resize_corner_width) | |
233 component = HTTOPLEFT; | |
234 else if (point.x() >= (width() - resize_corner_width)) | |
235 component = HTTOPRIGHT; | |
236 else | |
237 component = HTTOP; | |
238 } else if (point.y() >= (height() - resize_border_thickness)) { | |
239 if (point.x() < resize_corner_width) | |
240 component = HTBOTTOMLEFT; | |
241 else if (point.x() >= (width() - resize_corner_width)) | |
242 component = HTBOTTOMRIGHT; | |
243 else | |
244 component = HTBOTTOM; | |
245 } else { | |
246 return HTNOWHERE; | |
247 } | |
248 | |
249 // If the window can't be resized, there are no resize boundaries, just | |
250 // window borders. | |
251 return can_resize ? component : HTBORDER; | |
252 } | |
253 | |
254 bool NonClientFrameView::ShouldPaintAsActive() const { | |
255 return GetWidget()->IsActive() || paint_as_active_; | |
256 } | |
257 | |
258 void NonClientFrameView::ShouldPaintAsActiveChanged() { | |
259 if (!paint_as_active_) | |
260 SchedulePaint(); | |
261 } | |
262 | |
263 void NonClientFrameView::GetAccessibleState(ui::AccessibleViewState* state) { | |
264 state->role = ui::AccessibilityTypes::ROLE_WINDOW; | |
265 } | |
266 | |
267 std::string NonClientFrameView::GetClassName() const { | |
268 return kViewClassName; | |
269 } | |
270 | |
271 void NonClientFrameView::OnBoundsChanged(const gfx::Rect& previous_bounds) { | |
272 // Overridden to do nothing. The NonClientView manually calls Layout on the | |
273 // FrameView when it is itself laid out, see comment in NonClientView::Layout. | |
274 } | |
275 | |
276 } // namespace views | |
OLD | NEW |