- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / window / non_client_view.cc
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.
4
5 #include "ui/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 "ui/views/widget/root_view.h"
10 #include "ui/views/widget/widget.h"
11 #include "ui/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     "ui/views/window/NonClientFrameView";
20
21 const char NonClientView::kViewClassName[] =
22     "ui/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 // The overlay view is always on top (index == child_count() - 1).
31
32 ////////////////////////////////////////////////////////////////////////////////
33 // NonClientView, public:
34
35 NonClientView::NonClientView()
36     : client_view_(NULL),
37       overlay_view_(NULL) {
38 }
39
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());
44 }
45
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);
52   if (parent())
53     AddChildViewAt(frame_view_.get(), kFrameViewIndex);
54 }
55
56 void NonClientView::SetOverlayView(View* view) {
57   if (overlay_view_)
58     RemoveChildView(overlay_view_);
59
60   if (!view)
61     return;
62
63   overlay_view_ = view;
64   if (parent())
65     AddChildView(overlay_view_);
66 }
67
68 bool NonClientView::CanClose() {
69   return client_view_->CanClose();
70 }
71
72 void NonClientView::WindowClosing() {
73   client_view_->WidgetClosing();
74 }
75
76 void NonClientView::UpdateFrame(bool layout) {
77   Widget* widget = GetWidget();
78   SetFrameView(widget->CreateNonClientFrameView());
79   widget->ThemeChanged();
80   if (layout) {
81     Layout();
82     SchedulePaint();
83   }
84 }
85
86 void NonClientView::SetInactiveRenderingDisabled(bool disable) {
87   frame_view_->SetInactiveRenderingDisabled(disable);
88 }
89
90 gfx::Rect NonClientView::GetWindowBoundsForClientBounds(
91     const gfx::Rect client_bounds) const {
92   return frame_view_->GetWindowBoundsForClientBounds(client_bounds);
93 }
94
95 int NonClientView::NonClientHitTest(const gfx::Point& point) {
96   // The NonClientFrameView is responsible for also asking the ClientView.
97   return frame_view_->NonClientHitTest(point);
98 }
99
100 void NonClientView::GetWindowMask(const gfx::Size& size,
101                                   gfx::Path* window_mask) {
102   frame_view_->GetWindowMask(size, window_mask);
103 }
104
105 void NonClientView::ResetWindowControls() {
106   frame_view_->ResetWindowControls();
107 }
108
109 void NonClientView::UpdateWindowIcon() {
110   frame_view_->UpdateWindowIcon();
111 }
112
113 void NonClientView::UpdateWindowTitle() {
114   frame_view_->UpdateWindowTitle();
115 }
116
117 void NonClientView::LayoutFrameView() {
118   // First layout the NonClientFrameView, which determines the size of the
119   // ClientView...
120   frame_view_->SetBounds(0, 0, width(), height());
121
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
128   // twice.
129   frame_view_->Layout();
130 }
131
132 void NonClientView::SetAccessibleName(const string16& name) {
133   accessible_name_ = name;
134 }
135
136 ////////////////////////////////////////////////////////////////////////////////
137 // NonClientView, View overrides:
138
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();
145 }
146
147 gfx::Size NonClientView::GetMinimumSize() {
148   return frame_view_->GetMinimumSize();
149 }
150
151 gfx::Size NonClientView::GetMaximumSize() {
152   return frame_view_->GetMaximumSize();
153 }
154
155 void NonClientView::Layout() {
156   LayoutFrameView();
157
158   // Then layout the ClientView, using those bounds.
159   client_view_->SetBoundsRect(frame_view_->GetBoundsForClientView());
160
161   // We need to manually call Layout on the ClientView as well for the same
162   // reason as above.
163   client_view_->Layout();
164
165   if (overlay_view_ && overlay_view_->visible())
166     overlay_view_->SetBoundsRect(GetLocalBounds());
167 }
168
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
173   // established.
174   if (details.is_add && GetWidget() && details.child == this) {
175     AddChildViewAt(frame_view_.get(), kFrameViewIndex);
176     AddChildViewAt(client_view_, kClientViewIndex);
177     if (overlay_view_)
178       AddChildView(overlay_view_);
179   }
180 }
181
182 void NonClientView::GetAccessibleState(ui::AccessibleViewState* state) {
183   state->role = ui::AccessibilityTypes::ROLE_CLIENT;
184   state->name = accessible_name_;
185 }
186
187 const char* NonClientView::GetClassName() const {
188   return kViewClassName;
189 }
190
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);
207   }
208
209   return View::GetEventHandlerForPoint(point);
210 }
211
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);
222     if (handler)
223       return handler;
224   }
225
226   return View::GetTooltipHandlerForPoint(point);
227 }
228
229 ////////////////////////////////////////////////////////////////////////////////
230 // NonClientFrameView, public:
231
232 void NonClientFrameView::SetInactiveRenderingDisabled(bool disable) {
233   if (paint_as_active_ == disable)
234     return;
235
236   paint_as_active_ = disable;
237   ShouldPaintAsActiveChanged();
238 }
239
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,
245                                                bool can_resize) {
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.
251   int component;
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;
257     else
258       component = HTLEFT;
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;
264     else
265       component = HTRIGHT;
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;
271     else
272       component = HTTOP;
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;
278     else
279       component = HTBOTTOM;
280   } else {
281     return HTNOWHERE;
282   }
283
284   // If the window can't be resized, there are no resize boundaries, just
285   // window borders.
286   return can_resize ? component : HTBORDER;
287 }
288
289 ////////////////////////////////////////////////////////////////////////////////
290 // NonClientFrameView, View overrides:
291
292 bool NonClientFrameView::HitTestRect(const gfx::Rect& rect) const {
293   // For the default case, we assume the non-client frame view never overlaps
294   // the client view.
295   return !GetWidget()->client_view()->bounds().Intersects(rect);
296 }
297
298 ////////////////////////////////////////////////////////////////////////////////
299 // NonClientFrameView, protected:
300
301 bool NonClientFrameView::ShouldPaintAsActive() const {
302   return GetWidget()->IsActive() || paint_as_active_;
303 }
304
305 void NonClientFrameView::ShouldPaintAsActiveChanged() {
306   SchedulePaint();
307 }
308
309 void NonClientFrameView::GetAccessibleState(ui::AccessibleViewState* state) {
310   state->role = ui::AccessibilityTypes::ROLE_CLIENT;
311 }
312
313 const char* NonClientFrameView::GetClassName() const {
314   return kViewClassName;
315 }
316
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.
320 }
321
322 }  // namespace views