| OLD | NEW |
| (Empty) |
| 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 | |
| 50 TableHeader::TableHeader(TableView* table) : table_(table) { | |
| 51 set_background(Background::CreateVerticalGradientBackground( | |
| 52 kBackgroundColor1, kBackgroundColor2)); | |
| 53 } | |
| 54 | |
| 55 TableHeader::~TableHeader() { | |
| 56 } | |
| 57 | |
| 58 void TableHeader::Layout() { | |
| 59 SetBounds(x(), y(), table_->width(), GetPreferredSize().height()); | |
| 60 } | |
| 61 | |
| 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 | |
| 162 gfx::Size TableHeader::GetPreferredSize() const { | |
| 163 return gfx::Size(1, kVerticalPadding * 2 + font_list_.GetHeight()); | |
| 164 } | |
| 165 | |
| 166 gfx::NativeCursor TableHeader::GetCursor(const ui::MouseEvent& event) { | |
| 167 return GetResizeColumn(GetMirroredXInView(event.x())) != -1 ? | |
| 168 GetNativeColumnResizeCursor() : View::GetCursor(event); | |
| 169 } | |
| 170 | |
| 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 | |
| 181 bool TableHeader::OnMouseDragged(const ui::MouseEvent& event) { | |
| 182 ContinueResize(event); | |
| 183 return true; | |
| 184 } | |
| 185 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
| 261 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 | |
| OLD | NEW |