1 // Copyright (c) 2012 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.
5 #include "ui/views/window/non_client_view.h"
7 #include "ui/base/accessibility/accessible_view_state.h"
8 #include "ui/base/hit_test.h"
9 #include "ui/views/widget/root_view.h"
10 #include "ui/views/widget/widget.h"
11 #include "ui/views/window/client_view.h"
16 const int NonClientFrameView::kFrameShadowThickness = 1;
17 const int NonClientFrameView::kClientEdgeThickness = 1;
18 const char NonClientFrameView::kViewClassName[] =
19 "ui/views/window/NonClientFrameView";
21 const char NonClientView::kViewClassName[] =
22 "ui/views/window/NonClientView";
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 // The overlay view is always on top (index == child_count() - 1).
32 ////////////////////////////////////////////////////////////////////////////////
33 // NonClientView, public:
35 NonClientView::NonClientView()
40 NonClientView::~NonClientView() {
41 // This value may have been reset before the window hierarchy shuts down,
42 // so we need to manually remove it.
43 RemoveChildView(frame_view_.get());
46 void NonClientView::SetFrameView(NonClientFrameView* frame_view) {
47 // See comment in header about ownership.
48 frame_view->set_owned_by_client();
49 if (frame_view_.get())
50 RemoveChildView(frame_view_.get());
51 frame_view_.reset(frame_view);
53 AddChildViewAt(frame_view_.get(), kFrameViewIndex);
56 void NonClientView::SetOverlayView(View* view) {
58 RemoveChildView(overlay_view_);
65 AddChildView(overlay_view_);
68 bool NonClientView::CanClose() {
69 return client_view_->CanClose();
72 void NonClientView::WindowClosing() {
73 client_view_->WidgetClosing();
76 void NonClientView::UpdateFrame(bool layout) {
77 Widget* widget = GetWidget();
78 SetFrameView(widget->CreateNonClientFrameView());
79 widget->ThemeChanged();
86 void NonClientView::SetInactiveRenderingDisabled(bool disable) {
87 frame_view_->SetInactiveRenderingDisabled(disable);
90 gfx::Rect NonClientView::GetWindowBoundsForClientBounds(
91 const gfx::Rect client_bounds) const {
92 return frame_view_->GetWindowBoundsForClientBounds(client_bounds);
95 int NonClientView::NonClientHitTest(const gfx::Point& point) {
96 // The NonClientFrameView is responsible for also asking the ClientView.
97 return frame_view_->NonClientHitTest(point);
100 void NonClientView::GetWindowMask(const gfx::Size& size,
101 gfx::Path* window_mask) {
102 frame_view_->GetWindowMask(size, window_mask);
105 void NonClientView::ResetWindowControls() {
106 frame_view_->ResetWindowControls();
109 void NonClientView::UpdateWindowIcon() {
110 frame_view_->UpdateWindowIcon();
113 void NonClientView::UpdateWindowTitle() {
114 frame_view_->UpdateWindowTitle();
117 void NonClientView::LayoutFrameView() {
118 // First layout the NonClientFrameView, which determines the size of the
120 frame_view_->SetBounds(0, 0, width(), height());
122 // We need to manually call Layout here because layout for the frame view can
123 // change independently of the bounds changing - e.g. after the initial
124 // display of the window the metrics of the native window controls can change,
125 // which does not change the bounds of the window but requires a re-layout to
126 // trigger a repaint. We override OnBoundsChanged() for the NonClientFrameView
127 // to do nothing so that SetBounds above doesn't cause Layout to be called
129 frame_view_->Layout();
132 void NonClientView::SetAccessibleName(const string16& name) {
133 accessible_name_ = name;
136 ////////////////////////////////////////////////////////////////////////////////
137 // NonClientView, View overrides:
139 gfx::Size NonClientView::GetPreferredSize() {
140 // TODO(pkasting): This should probably be made to look similar to
141 // GetMinimumSize() below. This will require implementing GetPreferredSize()
142 // better in the various frame views.
143 gfx::Rect client_bounds(gfx::Point(), client_view_->GetPreferredSize());
144 return GetWindowBoundsForClientBounds(client_bounds).size();
147 gfx::Size NonClientView::GetMinimumSize() {
148 return frame_view_->GetMinimumSize();
151 gfx::Size NonClientView::GetMaximumSize() {
152 return frame_view_->GetMaximumSize();
155 void NonClientView::Layout() {
158 // Then layout the ClientView, using those bounds.
159 client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView());
161 // We need to manually call Layout on the ClientView as well for the same
163 client_view_->Layout();
165 if (overlay_view_ && overlay_view_->visible())
166 overlay_view_->SetBoundsRect(GetLocalBounds());
169 void NonClientView::ViewHierarchyChanged(
170 const ViewHierarchyChangedDetails& details) {
171 // Add our two child views here as we are added to the Widget so that if we
172 // are subsequently resized all the parent-child relationships are
174 if (details.is_add && GetWidget() && details.child == this) {
175 AddChildViewAt(frame_view_.get(), kFrameViewIndex);
176 AddChildViewAt(client_view_, kClientViewIndex);
178 AddChildView(overlay_view_);
182 void NonClientView::GetAccessibleState(ui::AccessibleViewState* state) {
183 state->role = ui::AccessibilityTypes::ROLE_CLIENT;
184 state->name = accessible_name_;
187 const char* NonClientView::GetClassName() const {
188 return kViewClassName;
191 views::View* NonClientView::GetEventHandlerForPoint(const gfx::Point& point) {
192 // Because of the z-ordering of our child views (the client view is positioned
193 // over the non-client frame view, if the client view ever overlaps the frame
194 // view visually (as it does for the browser window), then it will eat mouse
195 // events for the window controls. We override this method here so that we can
196 // detect this condition and re-route the events to the non-client frame view.
197 // The assumption is that the frame view's implementation of HitTest will only
198 // return true for area not occupied by the client view.
199 if (frame_view_->parent() == this) {
200 // During the reset of the frame_view_ it's possible to be in this code
201 // after it's been removed from the view hierarchy but before it's been
202 // removed from the NonClientView.
203 gfx::Point point_in_child_coords(point);
204 View::ConvertPointToTarget(this, frame_view_.get(), &point_in_child_coords);
205 if (frame_view_->HitTestPoint(point_in_child_coords))
206 return frame_view_->GetEventHandlerForPoint(point_in_child_coords);
209 return View::GetEventHandlerForPoint(point);
212 views::View* NonClientView::GetTooltipHandlerForPoint(const gfx::Point& point) {
213 // The same logic as for |GetEventHandlerForPoint()| applies here.
214 if (frame_view_->parent() == this) {
215 // During the reset of the frame_view_ it's possible to be in this code
216 // after it's been removed from the view hierarchy but before it's been
217 // removed from the NonClientView.
218 gfx::Point point_in_child_coords(point);
219 View::ConvertPointToTarget(this, frame_view_.get(), &point_in_child_coords);
220 views::View* handler =
221 frame_view_->GetTooltipHandlerForPoint(point_in_child_coords);
226 return View::GetTooltipHandlerForPoint(point);
229 ////////////////////////////////////////////////////////////////////////////////
230 // NonClientFrameView, public:
232 void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) {
233 if (paint_as_active_ == disable)
236 paint_as_active_ = disable;
237 ShouldPaintAsActiveChanged();
240 int NonClientFrameView::GetHTComponentForFrame(const gfx::Point& point,
241 int top_resize_border_height,
242 int resize_border_thickness,
243 int top_resize_corner_height,
244 int resize_corner_width,
246 // Tricky: In XP, native behavior is to return HTTOPLEFT and HTTOPRIGHT for
247 // a |resize_corner_size|-length strip of both the side and top borders, but
248 // only to return HTBOTTOMLEFT/HTBOTTOMRIGHT along the bottom border + corner
249 // (not the side border). Vista goes further and doesn't return these on any
250 // of the side borders. We allow callers to match either behavior.
252 if (point.x() < resize_border_thickness) {
253 if (point.y() < top_resize_corner_height)
254 component = HTTOPLEFT;
255 else if (point.y() >= (height() - resize_border_thickness))
256 component = HTBOTTOMLEFT;
259 } else if (point.x() >= (width() - resize_border_thickness)) {
260 if (point.y() < top_resize_corner_height)
261 component = HTTOPRIGHT;
262 else if (point.y() >= (height() - resize_border_thickness))
263 component = HTBOTTOMRIGHT;
266 } else if (point.y() < top_resize_border_height) {
267 if (point.x() < resize_corner_width)
268 component = HTTOPLEFT;
269 else if (point.x() >= (width() - resize_corner_width))
270 component = HTTOPRIGHT;
273 } else if (point.y() >= (height() - resize_border_thickness)) {
274 if (point.x() < resize_corner_width)
275 component = HTBOTTOMLEFT;
276 else if (point.x() >= (width() - resize_corner_width))
277 component = HTBOTTOMRIGHT;
279 component = HTBOTTOM;
284 // If the window can't be resized, there are no resize boundaries, just
286 return can_resize ? component : HTBORDER;
289 ////////////////////////////////////////////////////////////////////////////////
290 // NonClientFrameView, View overrides:
292 bool NonClientFrameView::HitTestRect(const gfx::Rect& rect) const {
293 // For the default case, we assume the non-client frame view never overlaps
295 return !GetWidget()->client_view()->bounds().Intersects(rect);
298 ////////////////////////////////////////////////////////////////////////////////
299 // NonClientFrameView, protected:
301 bool NonClientFrameView::ShouldPaintAsActive() const {
302 return GetWidget()->IsActive() || paint_as_active_;
305 void NonClientFrameView::ShouldPaintAsActiveChanged() {
309 void NonClientFrameView::GetAccessibleState(ui::AccessibleViewState* state) {
310 state->role = ui::AccessibilityTypes::ROLE_CLIENT;
313 const char* NonClientFrameView::GetClassName() const {
314 return kViewClassName;
317 void NonClientFrameView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
318 // Overridden to do nothing. The NonClientView manually calls Layout on the
319 // FrameView when it is itself laid out, see comment in NonClientView::Layout.