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