Add ICU message format support
[chromium-blink-merge.git] / ui / views / controls / table / table_header.cc
blob06a26fce5f5b5dd665c51b81522d0ab415f143bf
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"
19 namespace views {
21 namespace {
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;
42 } // namespace
44 // static
45 const char TableHeader::kViewClassName[] = "TableHeader";
46 // static
47 const int TableHeader::kHorizontalPadding = 7;
48 // static
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),
84 kSeparatorColor);
87 const int x = columns[i].x + kHorizontalPadding;
88 int width = columns[i].width - kHorizontalPadding - kHorizontalPadding;
89 if (width <= 0)
90 continue;
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) {
110 SkPaint paint;
111 paint.setColor(kTextColor);
112 paint.setStyle(SkPaint::kFill_Style);
113 paint.setAntiAlias(true);
115 int indicator_x = 0;
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;
123 switch (alignment) {
124 case ui::TableColumn::LEFT:
125 indicator_x = x + title_width;
126 break;
127 case ui::TableColumn::CENTER:
128 indicator_x = x + width / 2;
129 break;
130 case ui::TableColumn::RIGHT:
131 indicator_x = x + width;
132 break;
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));
150 } else {
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()) {
181 StartResize(event);
182 return true;
185 // Return false so that context menus on ancestors work.
186 return false;
189 bool TableHeader::OnMouseDragged(const ui::MouseEvent& event) {
190 ContinueResize(event);
191 return true;
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() {
202 if (is_resizing()) {
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);
214 break;
215 case ui::ET_GESTURE_SCROLL_BEGIN:
216 StartResize(*event);
217 break;
218 case ui::ET_GESTURE_SCROLL_UPDATE:
219 ContinueResize(*event);
220 break;
221 case ui::ET_GESTURE_SCROLL_END:
222 resize_details_.reset();
223 break;
224 default:
225 return;
227 event->SetHandled();
230 bool TableHeader::StartResize(const ui::LocatedEvent& event) {
231 if (is_resizing())
232 return false;
234 const int index = GetResizeColumn(GetMirroredXInView(event.x()));
235 if (index == -1)
236 return false;
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;
242 return true;
245 void TableHeader::ContinueResize(const ui::LocatedEvent& event) {
246 if (!is_resizing())
247 return;
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())
259 return;
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());
271 if (columns.empty())
272 return -1;
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) {
279 return index - 1;
281 const int max_x = column.x + column.width;
282 return (x >= max_x - kResizePadding && x <= max_x + kResizePadding) ?
283 index : -1;
286 } // namespace views