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 "ui/base/cursor/cursor.h"
9 #include "ui/gfx/canvas.h"
10 #include "ui/gfx/text_utils.h"
11 #include "ui/native_theme/native_theme.h"
12 #include "ui/views/background.h"
13 #include "ui/views/controls/table/table_utils.h"
14 #include "ui/views/controls/table/table_view.h"
15 #include "ui/views/native_cursor.h"
21 const int kVerticalPadding
= 4;
23 // The minimum width we allow a column to go down to.
24 const int kMinColumnWidth
= 10;
26 // Distace from edge columns can be resized by.
27 const int kResizePadding
= 5;
29 // Amount of space above/below the separator.
30 const int kSeparatorPadding
= 4;
32 const SkColor kTextColor
= SK_ColorBLACK
;
33 const SkColor kBackgroundColor1
= SkColorSetRGB(0xF9, 0xF9, 0xF9);
34 const SkColor kBackgroundColor2
= SkColorSetRGB(0xE8, 0xE8, 0xE8);
35 const SkColor kSeparatorColor
= SkColorSetRGB(0xAA, 0xAA, 0xAA);
37 // Size of the sort indicator (doesn't include padding).
38 const int kSortIndicatorSize
= 8;
43 const char TableHeader::kViewClassName
[] = "TableHeader";
45 const int TableHeader::kHorizontalPadding
= 7;
47 const int TableHeader::kSortIndicatorWidth
= kSortIndicatorSize
+
48 TableHeader::kHorizontalPadding
* 2;
50 typedef std::vector
<TableView::VisibleColumn
> Columns
;
52 TableHeader::TableHeader(TableView
* table
) : table_(table
) {
53 set_background(Background::CreateVerticalGradientBackground(
54 kBackgroundColor1
, kBackgroundColor2
));
57 TableHeader::~TableHeader() {
60 void TableHeader::Layout() {
61 SetBounds(x(), y(), table_
->width(), GetPreferredSize().height());
64 void TableHeader::OnPaint(gfx::Canvas
* canvas
) {
65 // Paint the background and a separator at the bottom. The separator color
66 // matches that of the border around the scrollview.
67 OnPaintBackground(canvas
);
68 SkColor border_color
= GetNativeTheme()->GetSystemColor(
69 ui::NativeTheme::kColorId_UnfocusedBorderColor
);
70 canvas
->DrawLine(gfx::Point(0, height() - 1),
71 gfx::Point(width(), height() - 1), border_color
);
73 const Columns
& columns
= table_
->visible_columns();
74 const int sorted_column_id
= table_
->sort_descriptors().empty() ? -1 :
75 table_
->sort_descriptors()[0].column_id
;
76 for (size_t i
= 0; i
< columns
.size(); ++i
) {
77 if (columns
[i
].width
>= 2) {
78 const int separator_x
= GetMirroredXInView(
79 columns
[i
].x
+ columns
[i
].width
- 1);
80 canvas
->DrawLine(gfx::Point(separator_x
, kSeparatorPadding
),
81 gfx::Point(separator_x
, height() - kSeparatorPadding
),
85 const int x
= columns
[i
].x
+ kHorizontalPadding
;
86 int width
= columns
[i
].width
- kHorizontalPadding
- kHorizontalPadding
;
90 const int title_width
=
91 gfx::GetStringWidth(columns
[i
].column
.title
, font_list_
);
92 const bool paint_sort_indicator
=
93 (columns
[i
].column
.id
== sorted_column_id
&&
94 title_width
+ kSortIndicatorWidth
<= width
);
96 if (paint_sort_indicator
&&
97 columns
[i
].column
.alignment
== ui::TableColumn::RIGHT
) {
98 width
-= kSortIndicatorWidth
;
101 canvas
->DrawStringRectWithFlags(
102 columns
[i
].column
.title
, font_list_
, kTextColor
,
103 gfx::Rect(GetMirroredXWithWidthInView(x
, width
), kVerticalPadding
,
104 width
, height() - kVerticalPadding
* 2),
105 TableColumnAlignmentToCanvasAlignment(columns
[i
].column
.alignment
));
107 if (paint_sort_indicator
) {
109 paint
.setColor(kTextColor
);
110 paint
.setStyle(SkPaint::kFill_Style
);
111 paint
.setAntiAlias(true);
114 ui::TableColumn::Alignment alignment
= columns
[i
].column
.alignment
;
115 if (base::i18n::IsRTL()) {
116 if (alignment
== ui::TableColumn::LEFT
)
117 alignment
= ui::TableColumn::RIGHT
;
118 else if (alignment
== ui::TableColumn::RIGHT
)
119 alignment
= ui::TableColumn::LEFT
;
122 case ui::TableColumn::LEFT
:
123 indicator_x
= x
+ title_width
;
125 case ui::TableColumn::CENTER
:
126 indicator_x
= x
+ width
/ 2;
128 case ui::TableColumn::RIGHT
:
129 indicator_x
= x
+ width
;
133 const int scale
= base::i18n::IsRTL() ? -1 : 1;
134 indicator_x
+= (kSortIndicatorWidth
- kSortIndicatorSize
) / 2;
135 indicator_x
= GetMirroredXInView(indicator_x
);
136 int indicator_y
= height() / 2 - kSortIndicatorSize
/ 2;
137 SkPath indicator_path
;
138 if (table_
->sort_descriptors()[0].ascending
) {
139 indicator_path
.moveTo(
140 SkIntToScalar(indicator_x
),
141 SkIntToScalar(indicator_y
+ kSortIndicatorSize
));
142 indicator_path
.lineTo(
143 SkIntToScalar(indicator_x
+ kSortIndicatorSize
* scale
),
144 SkIntToScalar(indicator_y
+ kSortIndicatorSize
));
145 indicator_path
.lineTo(
146 SkIntToScalar(indicator_x
+ kSortIndicatorSize
/ 2 * scale
),
147 SkIntToScalar(indicator_y
));
149 indicator_path
.moveTo(SkIntToScalar(indicator_x
),
150 SkIntToScalar(indicator_y
));
151 indicator_path
.lineTo(
152 SkIntToScalar(indicator_x
+ kSortIndicatorSize
* scale
),
153 SkIntToScalar(indicator_y
));
154 indicator_path
.lineTo(
155 SkIntToScalar(indicator_x
+ kSortIndicatorSize
/ 2 * scale
),
156 SkIntToScalar(indicator_y
+ kSortIndicatorSize
));
158 indicator_path
.close();
159 canvas
->DrawPath(indicator_path
, paint
);
164 const char* TableHeader::GetClassName() const {
165 return kViewClassName
;
168 gfx::Size
TableHeader::GetPreferredSize() const {
169 return gfx::Size(1, kVerticalPadding
* 2 + font_list_
.GetHeight());
172 gfx::NativeCursor
TableHeader::GetCursor(const ui::MouseEvent
& event
) {
173 return GetResizeColumn(GetMirroredXInView(event
.x())) != -1 ?
174 GetNativeColumnResizeCursor() : View::GetCursor(event
);
177 bool TableHeader::OnMousePressed(const ui::MouseEvent
& event
) {
178 if (event
.IsOnlyLeftMouseButton()) {
183 // Return false so that context menus on ancestors work.
187 bool TableHeader::OnMouseDragged(const ui::MouseEvent
& event
) {
188 ContinueResize(event
);
192 void TableHeader::OnMouseReleased(const ui::MouseEvent
& event
) {
193 const bool was_resizing
= resize_details_
!= NULL
;
194 resize_details_
.reset();
195 if (!was_resizing
&& event
.IsOnlyLeftMouseButton())
196 ToggleSortOrder(event
);
199 void TableHeader::OnMouseCaptureLost() {
201 table_
->SetVisibleColumnWidth(resize_details_
->column_index
,
202 resize_details_
->initial_width
);
204 resize_details_
.reset();
207 void TableHeader::OnGestureEvent(ui::GestureEvent
* event
) {
208 switch (event
->type()) {
209 case ui::ET_GESTURE_TAP
:
210 if (!resize_details_
.get())
211 ToggleSortOrder(*event
);
213 case ui::ET_GESTURE_SCROLL_BEGIN
:
216 case ui::ET_GESTURE_SCROLL_UPDATE
:
217 ContinueResize(*event
);
219 case ui::ET_GESTURE_SCROLL_END
:
220 resize_details_
.reset();
228 bool TableHeader::StartResize(const ui::LocatedEvent
& event
) {
232 const int index
= GetResizeColumn(GetMirroredXInView(event
.x()));
236 resize_details_
.reset(new ColumnResizeDetails
);
237 resize_details_
->column_index
= index
;
238 resize_details_
->initial_x
= event
.root_location().x();
239 resize_details_
->initial_width
= table_
->visible_columns()[index
].width
;
243 void TableHeader::ContinueResize(const ui::LocatedEvent
& event
) {
247 const int scale
= base::i18n::IsRTL() ? -1 : 1;
248 const int delta
= scale
*
249 (event
.root_location().x() - resize_details_
->initial_x
);
250 table_
->SetVisibleColumnWidth(
251 resize_details_
->column_index
,
252 std::max(kMinColumnWidth
, resize_details_
->initial_width
+ delta
));
255 void TableHeader::ToggleSortOrder(const ui::LocatedEvent
& event
) {
256 if (table_
->visible_columns().empty())
259 const int x
= GetMirroredXInView(event
.x());
260 const int index
= GetClosestVisibleColumnIndex(table_
, x
);
261 const TableView::VisibleColumn
& column(table_
->visible_columns()[index
]);
262 if (x
>= column
.x
&& x
< column
.x
+ column
.width
&& event
.y() >= 0 &&
263 event
.y() < height())
264 table_
->ToggleSortOrder(index
);
267 int TableHeader::GetResizeColumn(int x
) const {
268 const Columns
& columns(table_
->visible_columns());
272 const int index
= GetClosestVisibleColumnIndex(table_
, x
);
273 DCHECK_NE(-1, index
);
274 const TableView::VisibleColumn
& column(table_
->visible_columns()[index
]);
275 if (index
> 0 && x
>= column
.x
- kResizePadding
&&
276 x
<= column
.x
+ kResizePadding
) {
279 const int max_x
= column
.x
+ column
.width
;
280 return (x
>= max_x
- kResizePadding
&& x
<= max_x
+ kResizePadding
) ?