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/controls/table/table_header.h"
7 #include "third_party/skia/include/core/SkColor.h"
8 #include "third_party/skia/include/core/SkPaint.h"
9 #include "third_party/skia/include/core/SkPath.h"
10 #include "ui/base/cursor/cursor.h"
11 #include "ui/gfx/canvas.h"
12 #include "ui/gfx/text_utils.h"
13 #include "ui/native_theme/native_theme.h"
14 #include "ui/views/background.h"
15 #include "ui/views/controls/table/table_utils.h"
16 #include "ui/views/controls/table/table_view.h"
17 #include "ui/views/native_cursor.h"
23 const int kVerticalPadding
= 4;
25 // The minimum width we allow a column to go down to.
26 const int kMinColumnWidth
= 10;
28 // Distace from edge columns can be resized by.
29 const int kResizePadding
= 5;
31 // Amount of space above/below the separator.
32 const int kSeparatorPadding
= 4;
34 const SkColor kTextColor
= SK_ColorBLACK
;
35 const SkColor kBackgroundColor1
= SkColorSetRGB(0xF9, 0xF9, 0xF9);
36 const SkColor kBackgroundColor2
= SkColorSetRGB(0xE8, 0xE8, 0xE8);
37 const SkColor kSeparatorColor
= SkColorSetRGB(0xAA, 0xAA, 0xAA);
39 // Size of the sort indicator (doesn't include padding).
40 const int kSortIndicatorSize
= 8;
45 const char TableHeader::kViewClassName
[] = "TableHeader";
47 const int TableHeader::kHorizontalPadding
= 7;
49 const int TableHeader::kSortIndicatorWidth
= kSortIndicatorSize
+
50 TableHeader::kHorizontalPadding
* 2;
52 typedef std::vector
<TableView::VisibleColumn
> Columns
;
54 TableHeader::TableHeader(TableView
* table
) : table_(table
) {
55 set_background(Background::CreateVerticalGradientBackground(
56 kBackgroundColor1
, kBackgroundColor2
));
59 TableHeader::~TableHeader() {
62 void TableHeader::Layout() {
63 SetBounds(x(), y(), table_
->width(), GetPreferredSize().height());
66 void TableHeader::OnPaint(gfx::Canvas
* canvas
) {
67 // Paint the background and a separator at the bottom. The separator color
68 // matches that of the border around the scrollview.
69 OnPaintBackground(canvas
);
70 SkColor border_color
= GetNativeTheme()->GetSystemColor(
71 ui::NativeTheme::kColorId_UnfocusedBorderColor
);
72 canvas
->DrawLine(gfx::Point(0, height() - 1),
73 gfx::Point(width(), height() - 1), border_color
);
75 const Columns
& columns
= table_
->visible_columns();
76 const int sorted_column_id
= table_
->sort_descriptors().empty() ? -1 :
77 table_
->sort_descriptors()[0].column_id
;
78 for (size_t i
= 0; i
< columns
.size(); ++i
) {
79 if (columns
[i
].width
>= 2) {
80 const int separator_x
= GetMirroredXInView(
81 columns
[i
].x
+ columns
[i
].width
- 1);
82 canvas
->DrawLine(gfx::Point(separator_x
, kSeparatorPadding
),
83 gfx::Point(separator_x
, height() - kSeparatorPadding
),
87 const int x
= columns
[i
].x
+ kHorizontalPadding
;
88 int width
= columns
[i
].width
- kHorizontalPadding
- kHorizontalPadding
;
92 const int title_width
=
93 gfx::GetStringWidth(columns
[i
].column
.title
, font_list_
);
94 const bool paint_sort_indicator
=
95 (columns
[i
].column
.id
== sorted_column_id
&&
96 title_width
+ kSortIndicatorWidth
<= width
);
98 if (paint_sort_indicator
&&
99 columns
[i
].column
.alignment
== ui::TableColumn::RIGHT
) {
100 width
-= kSortIndicatorWidth
;
103 canvas
->DrawStringRectWithFlags(
104 columns
[i
].column
.title
, font_list_
, kTextColor
,
105 gfx::Rect(GetMirroredXWithWidthInView(x
, width
), kVerticalPadding
,
106 width
, height() - kVerticalPadding
* 2),
107 TableColumnAlignmentToCanvasAlignment(columns
[i
].column
.alignment
));
109 if (paint_sort_indicator
) {
111 paint
.setColor(kTextColor
);
112 paint
.setStyle(SkPaint::kFill_Style
);
113 paint
.setAntiAlias(true);
116 ui::TableColumn::Alignment alignment
= columns
[i
].column
.alignment
;
117 if (base::i18n::IsRTL()) {
118 if (alignment
== ui::TableColumn::LEFT
)
119 alignment
= ui::TableColumn::RIGHT
;
120 else if (alignment
== ui::TableColumn::RIGHT
)
121 alignment
= ui::TableColumn::LEFT
;
124 case ui::TableColumn::LEFT
:
125 indicator_x
= x
+ title_width
;
127 case ui::TableColumn::CENTER
:
128 indicator_x
= x
+ width
/ 2;
130 case ui::TableColumn::RIGHT
:
131 indicator_x
= x
+ width
;
135 const int scale
= base::i18n::IsRTL() ? -1 : 1;
136 indicator_x
+= (kSortIndicatorWidth
- kSortIndicatorSize
) / 2;
137 indicator_x
= GetMirroredXInView(indicator_x
);
138 int indicator_y
= height() / 2 - kSortIndicatorSize
/ 2;
139 SkPath indicator_path
;
140 if (table_
->sort_descriptors()[0].ascending
) {
141 indicator_path
.moveTo(
142 SkIntToScalar(indicator_x
),
143 SkIntToScalar(indicator_y
+ kSortIndicatorSize
));
144 indicator_path
.lineTo(
145 SkIntToScalar(indicator_x
+ kSortIndicatorSize
* scale
),
146 SkIntToScalar(indicator_y
+ kSortIndicatorSize
));
147 indicator_path
.lineTo(
148 SkIntToScalar(indicator_x
+ kSortIndicatorSize
/ 2 * scale
),
149 SkIntToScalar(indicator_y
));
151 indicator_path
.moveTo(SkIntToScalar(indicator_x
),
152 SkIntToScalar(indicator_y
));
153 indicator_path
.lineTo(
154 SkIntToScalar(indicator_x
+ kSortIndicatorSize
* scale
),
155 SkIntToScalar(indicator_y
));
156 indicator_path
.lineTo(
157 SkIntToScalar(indicator_x
+ kSortIndicatorSize
/ 2 * scale
),
158 SkIntToScalar(indicator_y
+ kSortIndicatorSize
));
160 indicator_path
.close();
161 canvas
->DrawPath(indicator_path
, paint
);
166 const char* TableHeader::GetClassName() const {
167 return kViewClassName
;
170 gfx::Size
TableHeader::GetPreferredSize() const {
171 return gfx::Size(1, kVerticalPadding
* 2 + font_list_
.GetHeight());
174 gfx::NativeCursor
TableHeader::GetCursor(const ui::MouseEvent
& event
) {
175 return GetResizeColumn(GetMirroredXInView(event
.x())) != -1 ?
176 GetNativeColumnResizeCursor() : View::GetCursor(event
);
179 bool TableHeader::OnMousePressed(const ui::MouseEvent
& event
) {
180 if (event
.IsOnlyLeftMouseButton()) {
185 // Return false so that context menus on ancestors work.
189 bool TableHeader::OnMouseDragged(const ui::MouseEvent
& event
) {
190 ContinueResize(event
);
194 void TableHeader::OnMouseReleased(const ui::MouseEvent
& event
) {
195 const bool was_resizing
= resize_details_
!= NULL
;
196 resize_details_
.reset();
197 if (!was_resizing
&& event
.IsOnlyLeftMouseButton())
198 ToggleSortOrder(event
);
201 void TableHeader::OnMouseCaptureLost() {
203 table_
->SetVisibleColumnWidth(resize_details_
->column_index
,
204 resize_details_
->initial_width
);
206 resize_details_
.reset();
209 void TableHeader::OnGestureEvent(ui::GestureEvent
* event
) {
210 switch (event
->type()) {
211 case ui::ET_GESTURE_TAP
:
212 if (!resize_details_
.get())
213 ToggleSortOrder(*event
);
215 case ui::ET_GESTURE_SCROLL_BEGIN
:
218 case ui::ET_GESTURE_SCROLL_UPDATE
:
219 ContinueResize(*event
);
221 case ui::ET_GESTURE_SCROLL_END
:
222 resize_details_
.reset();
230 bool TableHeader::StartResize(const ui::LocatedEvent
& event
) {
234 const int index
= GetResizeColumn(GetMirroredXInView(event
.x()));
238 resize_details_
.reset(new ColumnResizeDetails
);
239 resize_details_
->column_index
= index
;
240 resize_details_
->initial_x
= event
.root_location().x();
241 resize_details_
->initial_width
= table_
->visible_columns()[index
].width
;
245 void TableHeader::ContinueResize(const ui::LocatedEvent
& event
) {
249 const int scale
= base::i18n::IsRTL() ? -1 : 1;
250 const int delta
= scale
*
251 (event
.root_location().x() - resize_details_
->initial_x
);
252 table_
->SetVisibleColumnWidth(
253 resize_details_
->column_index
,
254 std::max(kMinColumnWidth
, resize_details_
->initial_width
+ delta
));
257 void TableHeader::ToggleSortOrder(const ui::LocatedEvent
& event
) {
258 if (table_
->visible_columns().empty())
261 const int x
= GetMirroredXInView(event
.x());
262 const int index
= GetClosestVisibleColumnIndex(table_
, x
);
263 const TableView::VisibleColumn
& column(table_
->visible_columns()[index
]);
264 if (x
>= column
.x
&& x
< column
.x
+ column
.width
&& event
.y() >= 0 &&
265 event
.y() < height())
266 table_
->ToggleSortOrder(index
);
269 int TableHeader::GetResizeColumn(int x
) const {
270 const Columns
& columns(table_
->visible_columns());
274 const int index
= GetClosestVisibleColumnIndex(table_
, x
);
275 DCHECK_NE(-1, index
);
276 const TableView::VisibleColumn
& column(table_
->visible_columns()[index
]);
277 if (index
> 0 && x
>= column
.x
- kResizePadding
&&
278 x
<= column
.x
+ kResizePadding
) {
281 const int max_x
= column
.x
+ column
.width
;
282 return (x
>= max_x
- kResizePadding
&& x
<= max_x
+ kResizePadding
) ?