• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/table/table_header.h"
6 
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"
16 
17 namespace views {
18 
19 namespace {
20 
21 const int kVerticalPadding = 4;
22 
23 // The minimum width we allow a column to go down to.
24 const int kMinColumnWidth = 10;
25 
26 // Distace from edge columns can be resized by.
27 const int kResizePadding = 5;
28 
29 // Amount of space above/below the separator.
30 const int kSeparatorPadding = 4;
31 
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);
36 
37 // Size of the sort indicator (doesn't include padding).
38 const int kSortIndicatorSize = 8;
39 
40 }  // namespace
41 
42 // static
43 const int TableHeader::kHorizontalPadding = 7;
44 // static
45 const int TableHeader::kSortIndicatorWidth = kSortIndicatorSize +
46     TableHeader::kHorizontalPadding * 2;
47 
48 typedef std::vector<TableView::VisibleColumn> Columns;
49 
TableHeader(TableView * table)50 TableHeader::TableHeader(TableView* table) : table_(table) {
51   set_background(Background::CreateVerticalGradientBackground(
52                      kBackgroundColor1, kBackgroundColor2));
53 }
54 
~TableHeader()55 TableHeader::~TableHeader() {
56 }
57 
Layout()58 void TableHeader::Layout() {
59   SetBounds(x(), y(), table_->width(), GetPreferredSize().height());
60 }
61 
OnPaint(gfx::Canvas * canvas)62 void TableHeader::OnPaint(gfx::Canvas* canvas) {
63   // Paint the background and a separator at the bottom. The separator color
64   // matches that of the border around the scrollview.
65   OnPaintBackground(canvas);
66   SkColor border_color = GetNativeTheme()->GetSystemColor(
67       ui::NativeTheme::kColorId_UnfocusedBorderColor);
68   canvas->DrawLine(gfx::Point(0, height() - 1),
69                    gfx::Point(width(), height() - 1), border_color);
70 
71   const Columns& columns = table_->visible_columns();
72   const int sorted_column_id = table_->sort_descriptors().empty() ? -1 :
73       table_->sort_descriptors()[0].column_id;
74   for (size_t i = 0; i < columns.size(); ++i) {
75     if (columns[i].width >= 2) {
76       const int separator_x = GetMirroredXInView(
77           columns[i].x + columns[i].width - 1);
78       canvas->DrawLine(gfx::Point(separator_x, kSeparatorPadding),
79                        gfx::Point(separator_x, height() - kSeparatorPadding),
80                        kSeparatorColor);
81     }
82 
83     const int x = columns[i].x + kHorizontalPadding;
84     int width = columns[i].width - kHorizontalPadding - kHorizontalPadding;
85     if (width <= 0)
86       continue;
87 
88     const int title_width =
89         gfx::GetStringWidth(columns[i].column.title, font_list_);
90     const bool paint_sort_indicator =
91         (columns[i].column.id == sorted_column_id &&
92          title_width + kSortIndicatorWidth <= width);
93 
94     if (paint_sort_indicator &&
95         columns[i].column.alignment == ui::TableColumn::RIGHT) {
96       width -= kSortIndicatorWidth;
97     }
98 
99     canvas->DrawStringRectWithFlags(
100         columns[i].column.title, font_list_, kTextColor,
101         gfx::Rect(GetMirroredXWithWidthInView(x, width), kVerticalPadding,
102                   width, height() - kVerticalPadding * 2),
103         TableColumnAlignmentToCanvasAlignment(columns[i].column.alignment));
104 
105     if (paint_sort_indicator) {
106       SkPaint paint;
107       paint.setColor(kTextColor);
108       paint.setStyle(SkPaint::kFill_Style);
109       paint.setAntiAlias(true);
110 
111       int indicator_x = 0;
112       ui::TableColumn::Alignment alignment = columns[i].column.alignment;
113       if (base::i18n::IsRTL()) {
114         if (alignment == ui::TableColumn::LEFT)
115           alignment = ui::TableColumn::RIGHT;
116         else if (alignment == ui::TableColumn::RIGHT)
117           alignment = ui::TableColumn::LEFT;
118       }
119       switch (alignment) {
120         case ui::TableColumn::LEFT:
121           indicator_x = x + title_width;
122           break;
123         case ui::TableColumn::CENTER:
124           indicator_x = x + width / 2;
125           break;
126         case ui::TableColumn::RIGHT:
127           indicator_x = x + width;
128           break;
129       }
130 
131       const int scale = base::i18n::IsRTL() ? -1 : 1;
132       indicator_x += (kSortIndicatorWidth - kSortIndicatorSize) / 2;
133       indicator_x = GetMirroredXInView(indicator_x);
134       int indicator_y = height() / 2 - kSortIndicatorSize / 2;
135       SkPath indicator_path;
136       if (table_->sort_descriptors()[0].ascending) {
137         indicator_path.moveTo(
138             SkIntToScalar(indicator_x),
139             SkIntToScalar(indicator_y + kSortIndicatorSize));
140         indicator_path.lineTo(
141             SkIntToScalar(indicator_x + kSortIndicatorSize * scale),
142             SkIntToScalar(indicator_y + kSortIndicatorSize));
143         indicator_path.lineTo(
144             SkIntToScalar(indicator_x + kSortIndicatorSize / 2 * scale),
145             SkIntToScalar(indicator_y));
146       } else {
147         indicator_path.moveTo(SkIntToScalar(indicator_x),
148                               SkIntToScalar(indicator_y));
149         indicator_path.lineTo(
150             SkIntToScalar(indicator_x + kSortIndicatorSize * scale),
151             SkIntToScalar(indicator_y));
152         indicator_path.lineTo(
153             SkIntToScalar(indicator_x + kSortIndicatorSize / 2 * scale),
154             SkIntToScalar(indicator_y + kSortIndicatorSize));
155       }
156       indicator_path.close();
157       canvas->DrawPath(indicator_path, paint);
158     }
159   }
160 }
161 
GetPreferredSize() const162 gfx::Size TableHeader::GetPreferredSize() const {
163   return gfx::Size(1, kVerticalPadding * 2 + font_list_.GetHeight());
164 }
165 
GetCursor(const ui::MouseEvent & event)166 gfx::NativeCursor TableHeader::GetCursor(const ui::MouseEvent& event) {
167   return GetResizeColumn(GetMirroredXInView(event.x())) != -1 ?
168       GetNativeColumnResizeCursor() : View::GetCursor(event);
169 }
170 
OnMousePressed(const ui::MouseEvent & event)171 bool TableHeader::OnMousePressed(const ui::MouseEvent& event) {
172   if (event.IsOnlyLeftMouseButton()) {
173     StartResize(event);
174     return true;
175   }
176 
177   // Return false so that context menus on ancestors work.
178   return false;
179 }
180 
OnMouseDragged(const ui::MouseEvent & event)181 bool TableHeader::OnMouseDragged(const ui::MouseEvent& event) {
182   ContinueResize(event);
183   return true;
184 }
185 
OnMouseReleased(const ui::MouseEvent & event)186 void TableHeader::OnMouseReleased(const ui::MouseEvent& event) {
187   const bool was_resizing = resize_details_ != NULL;
188   resize_details_.reset();
189   if (!was_resizing && event.IsOnlyLeftMouseButton())
190     ToggleSortOrder(event);
191 }
192 
OnMouseCaptureLost()193 void TableHeader::OnMouseCaptureLost() {
194   if (is_resizing()) {
195     table_->SetVisibleColumnWidth(resize_details_->column_index,
196                                   resize_details_->initial_width);
197   }
198   resize_details_.reset();
199 }
200 
OnGestureEvent(ui::GestureEvent * event)201 void TableHeader::OnGestureEvent(ui::GestureEvent* event) {
202   switch (event->type()) {
203     case ui::ET_GESTURE_TAP:
204       if (!resize_details_.get())
205         ToggleSortOrder(*event);
206       break;
207     case ui::ET_GESTURE_SCROLL_BEGIN:
208       StartResize(*event);
209       break;
210     case ui::ET_GESTURE_SCROLL_UPDATE:
211       ContinueResize(*event);
212       break;
213     case ui::ET_GESTURE_SCROLL_END:
214       resize_details_.reset();
215       break;
216     default:
217       return;
218   }
219   event->SetHandled();
220 }
221 
StartResize(const ui::LocatedEvent & event)222 bool TableHeader::StartResize(const ui::LocatedEvent& event) {
223   if (is_resizing())
224     return false;
225 
226   const int index = GetResizeColumn(GetMirroredXInView(event.x()));
227   if (index == -1)
228     return false;
229 
230   resize_details_.reset(new ColumnResizeDetails);
231   resize_details_->column_index = index;
232   resize_details_->initial_x = event.root_location().x();
233   resize_details_->initial_width = table_->visible_columns()[index].width;
234   return true;
235 }
236 
ContinueResize(const ui::LocatedEvent & event)237 void TableHeader::ContinueResize(const ui::LocatedEvent& event) {
238   if (!is_resizing())
239     return;
240 
241   const int scale = base::i18n::IsRTL() ? -1 : 1;
242   const int delta = scale *
243       (event.root_location().x() - resize_details_->initial_x);
244   table_->SetVisibleColumnWidth(
245       resize_details_->column_index,
246       std::max(kMinColumnWidth, resize_details_->initial_width + delta));
247 }
248 
ToggleSortOrder(const ui::LocatedEvent & event)249 void TableHeader::ToggleSortOrder(const ui::LocatedEvent& event) {
250   if (table_->visible_columns().empty())
251     return;
252 
253   const int x = GetMirroredXInView(event.x());
254   const int index = GetClosestVisibleColumnIndex(table_, x);
255   const TableView::VisibleColumn& column(table_->visible_columns()[index]);
256   if (x >= column.x && x < column.x + column.width && event.y() >= 0 &&
257       event.y() < height())
258     table_->ToggleSortOrder(index);
259 }
260 
GetResizeColumn(int x) const261 int TableHeader::GetResizeColumn(int x) const {
262   const Columns& columns(table_->visible_columns());
263   if (columns.empty())
264     return -1;
265 
266   const int index = GetClosestVisibleColumnIndex(table_, x);
267   DCHECK_NE(-1, index);
268   const TableView::VisibleColumn& column(table_->visible_columns()[index]);
269   if (index > 0 && x >= column.x - kResizePadding &&
270       x <= column.x + kResizePadding) {
271     return index - 1;
272   }
273   const int max_x = column.x + column.width;
274   return (x >= max_x - kResizePadding && x <= max_x + kResizePadding) ?
275       index : -1;
276 }
277 
278 }  // namespace views
279