- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / controls / single_split_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/controls/single_split_view.h"
6
7 #include "skia/ext/skia_utils_win.h"
8 #include "ui/base/accessibility/accessible_view_state.h"
9 #include "ui/gfx/canvas.h"
10 #include "ui/views/background.h"
11 #include "ui/views/controls/single_split_view_listener.h"
12
13 #if defined(USE_AURA)
14 #include "ui/base/cursor/cursor.h"
15 #endif
16
17 namespace views {
18
19 // static
20 const char SingleSplitView::kViewClassName[] = "SingleSplitView";
21
22 // Size of the divider in pixels.
23 static const int kDividerSize = 4;
24
25 SingleSplitView::SingleSplitView(View* leading,
26                                  View* trailing,
27                                  Orientation orientation,
28                                  SingleSplitViewListener* listener)
29     : is_horizontal_(orientation == HORIZONTAL_SPLIT),
30       divider_offset_(-1),
31       resize_leading_on_bounds_change_(true),
32       resize_disabled_(false),
33       listener_(listener) {
34   AddChildView(leading);
35   AddChildView(trailing);
36 #if defined(OS_WIN)
37   set_background(
38       views::Background::CreateSolidBackground(
39           skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE))));
40 #endif
41 }
42
43 void SingleSplitView::Layout() {
44   gfx::Rect leading_bounds;
45   gfx::Rect trailing_bounds;
46   CalculateChildrenBounds(bounds(), &leading_bounds, &trailing_bounds);
47
48   if (has_children()) {
49     if (child_at(0)->visible())
50       child_at(0)->SetBoundsRect(leading_bounds);
51     if (child_count() > 1) {
52       if (child_at(1)->visible())
53         child_at(1)->SetBoundsRect(trailing_bounds);
54     }
55   }
56
57   SchedulePaint();
58
59   // Invoke super's implementation so that the children are layed out.
60   View::Layout();
61 }
62
63 const char* SingleSplitView::GetClassName() const {
64   return kViewClassName;
65 }
66
67 void SingleSplitView::GetAccessibleState(ui::AccessibleViewState* state) {
68   state->role = ui::AccessibilityTypes::ROLE_GROUPING;
69   state->name = accessible_name_;
70 }
71
72 gfx::Size SingleSplitView::GetPreferredSize() {
73   int width = 0;
74   int height = 0;
75   for (int i = 0; i < 2 && i < child_count(); ++i) {
76     View* view = child_at(i);
77     gfx::Size pref = view->GetPreferredSize();
78     if (is_horizontal_) {
79       width += pref.width();
80       height = std::max(height, pref.height());
81     } else {
82       width = std::max(width, pref.width());
83       height += pref.height();
84     }
85   }
86   if (is_horizontal_)
87     width += GetDividerSize();
88   else
89     height += GetDividerSize();
90   return gfx::Size(width, height);
91 }
92
93 gfx::NativeCursor SingleSplitView::GetCursor(const ui::MouseEvent& event) {
94   if (!IsPointInDivider(event.location()))
95     return gfx::kNullCursor;
96 #if defined(USE_AURA)
97   return is_horizontal_ ?
98       ui::kCursorEastWestResize : ui::kCursorNorthSouthResize;
99 #elif defined(OS_WIN)
100   static HCURSOR we_resize_cursor = LoadCursor(NULL, IDC_SIZEWE);
101   static HCURSOR ns_resize_cursor = LoadCursor(NULL, IDC_SIZENS);
102   return is_horizontal_ ? we_resize_cursor : ns_resize_cursor;
103 #endif
104 }
105
106 int SingleSplitView::GetDividerSize() const {
107   bool both_visible = child_count() > 1 && child_at(0)->visible() &&
108       child_at(1)->visible();
109   return both_visible && !resize_disabled_ ? kDividerSize : 0;
110 }
111
112 void SingleSplitView::CalculateChildrenBounds(
113     const gfx::Rect& bounds,
114     gfx::Rect* leading_bounds,
115     gfx::Rect* trailing_bounds) const {
116   bool is_leading_visible = has_children() && child_at(0)->visible();
117   bool is_trailing_visible = child_count() > 1 && child_at(1)->visible();
118
119   if (!is_leading_visible && !is_trailing_visible) {
120     *leading_bounds = gfx::Rect();
121     *trailing_bounds = gfx::Rect();
122     return;
123   }
124
125   int divider_at;
126
127   if (!is_trailing_visible) {
128     divider_at = GetPrimaryAxisSize(bounds.width(), bounds.height());
129   } else if (!is_leading_visible) {
130     divider_at = 0;
131   } else {
132     divider_at =
133         CalculateDividerOffset(divider_offset_, this->bounds(), bounds);
134     divider_at = NormalizeDividerOffset(divider_at, bounds);
135   }
136
137   int divider_size = GetDividerSize();
138
139   if (is_horizontal_) {
140     *leading_bounds = gfx::Rect(0, 0, divider_at, bounds.height());
141     *trailing_bounds =
142         gfx::Rect(divider_at + divider_size, 0,
143                   std::max(0, bounds.width() - divider_at - divider_size),
144                   bounds.height());
145   } else {
146     *leading_bounds = gfx::Rect(0, 0, bounds.width(), divider_at);
147     *trailing_bounds =
148         gfx::Rect(0, divider_at + divider_size, bounds.width(),
149                   std::max(0, bounds.height() - divider_at - divider_size));
150   }
151 }
152
153 void SingleSplitView::SetAccessibleName(const string16& name) {
154   accessible_name_ = name;
155 }
156
157 bool SingleSplitView::OnMousePressed(const ui::MouseEvent& event) {
158   if (!IsPointInDivider(event.location()))
159     return false;
160   drag_info_.initial_mouse_offset = GetPrimaryAxisSize(event.x(), event.y());
161   drag_info_.initial_divider_offset =
162       NormalizeDividerOffset(divider_offset_, bounds());
163   return true;
164 }
165
166 bool SingleSplitView::OnMouseDragged(const ui::MouseEvent& event) {
167   if (child_count() < 2)
168     return false;
169
170   int delta_offset = GetPrimaryAxisSize(event.x(), event.y()) -
171       drag_info_.initial_mouse_offset;
172   if (is_horizontal_ && base::i18n::IsRTL())
173     delta_offset *= -1;
174   // Honor the first child's minimum size when resizing.
175   gfx::Size min = child_at(0)->GetMinimumSize();
176   int new_size = std::max(GetPrimaryAxisSize(min.width(), min.height()),
177                           drag_info_.initial_divider_offset + delta_offset);
178
179   // Honor the second child's minimum size, and don't let the view
180   // get bigger than our width.
181   min = child_at(1)->GetMinimumSize();
182   new_size = std::min(GetPrimaryAxisSize() - kDividerSize -
183       GetPrimaryAxisSize(min.width(), min.height()), new_size);
184
185   if (new_size != divider_offset_) {
186     set_divider_offset(new_size);
187     if (!listener_ || listener_->SplitHandleMoved(this))
188       Layout();
189   }
190   return true;
191 }
192
193 void SingleSplitView::OnMouseCaptureLost() {
194   if (child_count() < 2)
195     return;
196
197   if (drag_info_.initial_divider_offset != divider_offset_) {
198     set_divider_offset(drag_info_.initial_divider_offset);
199     if (!listener_ || listener_->SplitHandleMoved(this))
200       Layout();
201   }
202 }
203
204 void SingleSplitView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
205   divider_offset_ = CalculateDividerOffset(divider_offset_, previous_bounds,
206                                            bounds());
207 }
208
209 bool SingleSplitView::IsPointInDivider(const gfx::Point& p) {
210   if (resize_disabled_)
211     return false;
212
213   if (child_count() < 2)
214     return false;
215
216   if (!child_at(0)->visible() || !child_at(1)->visible())
217     return false;
218
219   int divider_relative_offset;
220   if (is_horizontal_) {
221     divider_relative_offset =
222         p.x() - child_at(base::i18n::IsRTL() ? 1 : 0)->width();
223   } else {
224     divider_relative_offset = p.y() - child_at(0)->height();
225   }
226   return (divider_relative_offset >= 0 &&
227       divider_relative_offset < GetDividerSize());
228 }
229
230 int SingleSplitView::CalculateDividerOffset(
231     int divider_offset,
232     const gfx::Rect& previous_bounds,
233     const gfx::Rect& new_bounds) const {
234   if (resize_leading_on_bounds_change_ && divider_offset != -1) {
235     // We do not update divider_offset on minimize (to zero) and on restore
236     // (to largest value). As a result we get back to the original value upon
237     // window restore.
238     bool is_minimize_or_restore =
239         previous_bounds.height() == 0 || new_bounds.height() == 0;
240     if (!is_minimize_or_restore) {
241       if (is_horizontal_)
242         divider_offset += new_bounds.width() - previous_bounds.width();
243       else
244         divider_offset += new_bounds.height() - previous_bounds.height();
245
246       if (divider_offset < 0)
247         divider_offset = GetDividerSize();
248     }
249   }
250   return divider_offset;
251 }
252
253 int SingleSplitView::NormalizeDividerOffset(int divider_offset,
254                                             const gfx::Rect& bounds) const {
255   int primary_axis_size = GetPrimaryAxisSize(bounds.width(), bounds.height());
256   if (divider_offset < 0)
257     // primary_axis_size may < GetDividerSize during initial layout.
258     return std::max(0, (primary_axis_size - GetDividerSize()) / 2);
259   return std::min(divider_offset,
260                   std::max(primary_axis_size - GetDividerSize(), 0));
261 }
262
263 }  // namespace views