Home | History | Annotate | Download | only in color_chooser
      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/color_chooser/color_chooser_view.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/strings/string_number_conversions.h"
      9 #include "base/strings/stringprintf.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "ui/base/events/event.h"
     12 #include "ui/base/keycodes/keyboard_codes.h"
     13 #include "ui/gfx/canvas.h"
     14 #include "ui/views/background.h"
     15 #include "ui/views/border.h"
     16 #include "ui/views/color_chooser/color_chooser_listener.h"
     17 #include "ui/views/controls/textfield/textfield.h"
     18 #include "ui/views/controls/textfield/textfield_controller.h"
     19 #include "ui/views/layout/box_layout.h"
     20 #include "ui/views/layout/grid_layout.h"
     21 #include "ui/views/widget/widget.h"
     22 
     23 namespace {
     24 
     25 const int kHueBarWidth = 20;
     26 const int kSaturationValueSize = 200;
     27 const int kMarginWidth = 5;
     28 const int kSaturationValueIndicatorSize = 6;
     29 const int kHueIndicatorSize = 5;
     30 const int kBorderWidth = 1;
     31 const int kTextfieldLengthInChars = 14;
     32 
     33 string16 GetColorText(SkColor color) {
     34   return ASCIIToUTF16(base::StringPrintf("#%02x%02x%02x",
     35                                          SkColorGetR(color),
     36                                          SkColorGetG(color),
     37                                          SkColorGetB(color)));
     38 }
     39 
     40 bool GetColorFromText(const string16& text, SkColor* result) {
     41   if (text.size() != 6 && !(text.size() == 7 && text[0] == '#'))
     42     return false;
     43 
     44   std::string input = UTF16ToUTF8((text.size() == 6) ? text : text.substr(1));
     45   std::vector<uint8> hex;
     46   if (!base::HexStringToBytes(input, &hex))
     47     return false;
     48 
     49   *result = SkColorSetRGB(hex[0], hex[1], hex[2]);
     50   return true;
     51 }
     52 
     53 // A view that processes mouse events and gesture events using a common
     54 // interface.
     55 class LocatedEventHandlerView : public views::View {
     56  public:
     57   virtual ~LocatedEventHandlerView() {}
     58 
     59  protected:
     60   LocatedEventHandlerView() {}
     61 
     62   // Handles an event (mouse or gesture) at the specified location.
     63   virtual void ProcessEventAtLocation(const gfx::Point& location) = 0;
     64 
     65   // views::View overrides:
     66   virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
     67     ProcessEventAtLocation(event.location());
     68     return true;
     69   }
     70 
     71   virtual bool OnMouseDragged(const ui::MouseEvent& event) OVERRIDE {
     72     ProcessEventAtLocation(event.location());
     73     return true;
     74   }
     75 
     76   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
     77     if (event->type() == ui::ET_GESTURE_TAP ||
     78         event->type() == ui::ET_GESTURE_TAP_DOWN ||
     79         event->IsScrollGestureEvent()) {
     80       ProcessEventAtLocation(event->location());
     81       event->SetHandled();
     82     }
     83   }
     84 
     85   DISALLOW_COPY_AND_ASSIGN(LocatedEventHandlerView);
     86 };
     87 
     88 }  // namespace
     89 
     90 namespace views {
     91 
     92 ////////////////////////////////////////////////////////////////////////////////
     93 // ColorChooserView::HueView
     94 //
     95 // The class to choose the hue of the color.  It draws a vertical bar and
     96 // the indicator for the currently selected hue.
     97 class ColorChooserView::HueView : public LocatedEventHandlerView {
     98  public:
     99   explicit HueView(ColorChooserView* chooser_view);
    100 
    101   void OnHueChanged(SkScalar hue);
    102 
    103  private:
    104   // LocatedEventHandlerView overrides:
    105   virtual void ProcessEventAtLocation(const gfx::Point& point) OVERRIDE;
    106 
    107   // View overrides:
    108   virtual gfx::Size GetPreferredSize() OVERRIDE;
    109   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
    110 
    111   ColorChooserView* chooser_view_;
    112   int level_;
    113 
    114   DISALLOW_COPY_AND_ASSIGN(HueView);
    115 };
    116 
    117 ColorChooserView::HueView::HueView(ColorChooserView* chooser_view)
    118     : chooser_view_(chooser_view),
    119       level_(0) {
    120   set_focusable(false);
    121 }
    122 
    123 void ColorChooserView::HueView::OnHueChanged(SkScalar hue) {
    124   SkScalar height = SkIntToScalar(kSaturationValueSize - 1);
    125   SkScalar hue_max = SkIntToScalar(360);
    126   int level = SkScalarDiv(SkScalarMul(hue_max - hue, height), hue_max);
    127   level += kBorderWidth;
    128   if (level_ != level) {
    129     level_ = level;
    130     SchedulePaint();
    131   }
    132 }
    133 
    134 void ColorChooserView::HueView::ProcessEventAtLocation(
    135     const gfx::Point& point) {
    136   level_ = std::max(kBorderWidth,
    137                     std::min(height() - 1 - kBorderWidth, point.y()));
    138   int base_height = kSaturationValueSize - 1;
    139   chooser_view_->OnHueChosen(SkScalarDiv(
    140       SkScalarMul(SkIntToScalar(360),
    141                   SkIntToScalar(base_height - (level_ - kBorderWidth))),
    142       SkIntToScalar(base_height)));
    143   SchedulePaint();
    144 }
    145 
    146 gfx::Size ColorChooserView::HueView::GetPreferredSize() {
    147   // We put indicators on the both sides of the hue bar.
    148   return gfx::Size(kHueBarWidth + kHueIndicatorSize * 2 + kBorderWidth * 2,
    149                    kSaturationValueSize + kBorderWidth * 2);
    150 }
    151 
    152 void ColorChooserView::HueView::OnPaint(gfx::Canvas* canvas) {
    153   SkScalar hsv[3];
    154   // In the hue bar, saturation and value for the color should be always 100%.
    155   hsv[1] = SK_Scalar1;
    156   hsv[2] = SK_Scalar1;
    157 
    158   canvas->FillRect(gfx::Rect(kHueIndicatorSize, 0,
    159                              kHueBarWidth + kBorderWidth, height() - 1),
    160                    SK_ColorGRAY);
    161   int base_left = kHueIndicatorSize + kBorderWidth;
    162   for (int y = 0; y < kSaturationValueSize; ++y) {
    163     hsv[0] = SkScalarDiv(SkScalarMul(SkIntToScalar(360),
    164                                      SkIntToScalar(
    165                                          kSaturationValueSize - 1 - y)),
    166                     SkIntToScalar(kSaturationValueSize - 1));
    167     canvas->FillRect(gfx::Rect(base_left, y + kBorderWidth, kHueBarWidth, 1),
    168                      SkHSVToColor(hsv));
    169   }
    170 
    171   // Put the triangular indicators besides.
    172   SkPath left_indicator_path;
    173   SkPath right_indicator_path;
    174   left_indicator_path.moveTo(
    175       SK_ScalarHalf, SkIntToScalar(level_ - kHueIndicatorSize));
    176   left_indicator_path.lineTo(
    177       kHueIndicatorSize, SkIntToScalar(level_));
    178   left_indicator_path.lineTo(
    179       SK_ScalarHalf, SkIntToScalar(level_ + kHueIndicatorSize));
    180   left_indicator_path.lineTo(
    181       SK_ScalarHalf, SkIntToScalar(level_ - kHueIndicatorSize));
    182   right_indicator_path.moveTo(
    183       SkIntToScalar(width()) - SK_ScalarHalf,
    184       SkIntToScalar(level_ - kHueIndicatorSize));
    185   right_indicator_path.lineTo(
    186       SkIntToScalar(width() - kHueIndicatorSize) - SK_ScalarHalf,
    187       SkIntToScalar(level_));
    188   right_indicator_path.lineTo(
    189       SkIntToScalar(width()) - SK_ScalarHalf,
    190       SkIntToScalar(level_ + kHueIndicatorSize));
    191   right_indicator_path.lineTo(
    192       SkIntToScalar(width()) - SK_ScalarHalf,
    193       SkIntToScalar(level_ - kHueIndicatorSize));
    194 
    195   SkPaint indicator_paint;
    196   indicator_paint.setColor(SK_ColorBLACK);
    197   indicator_paint.setStyle(SkPaint::kFill_Style);
    198   canvas->DrawPath(left_indicator_path, indicator_paint);
    199   canvas->DrawPath(right_indicator_path, indicator_paint);
    200 }
    201 
    202 ////////////////////////////////////////////////////////////////////////////////
    203 // ColorChooserView::SaturationValueView
    204 //
    205 // The class to choose the saturation and the value of the color.  It draws
    206 // a square area and the indicator for the currently selected saturation and
    207 // value.
    208 class ColorChooserView::SaturationValueView : public LocatedEventHandlerView {
    209  public:
    210   explicit SaturationValueView(ColorChooserView* chooser_view);
    211 
    212   void OnHueChanged(SkScalar hue);
    213   void OnSaturationValueChanged(SkScalar saturation, SkScalar value);
    214 
    215  private:
    216   // LocatedEventHandlerView overrides:
    217   virtual void ProcessEventAtLocation(const gfx::Point& point) OVERRIDE;
    218 
    219   // View overrides:
    220   virtual gfx::Size GetPreferredSize() OVERRIDE;
    221   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
    222 
    223   ColorChooserView* chooser_view_;
    224   SkScalar hue_;
    225   gfx::Point marker_position_;
    226 
    227   DISALLOW_COPY_AND_ASSIGN(SaturationValueView);
    228 };
    229 
    230 ColorChooserView::SaturationValueView::SaturationValueView(
    231     ColorChooserView* chooser_view)
    232     : chooser_view_(chooser_view),
    233       hue_(0) {
    234   set_focusable(false);
    235   set_border(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY));
    236 }
    237 
    238 void ColorChooserView::SaturationValueView::OnHueChanged(SkScalar hue) {
    239   if (hue_ != hue) {
    240     hue_ = hue;
    241     SchedulePaint();
    242   }
    243 }
    244 
    245 void ColorChooserView::SaturationValueView::OnSaturationValueChanged(
    246     SkScalar saturation,
    247     SkScalar value) {
    248   SkScalar scalar_size = SkIntToScalar(kSaturationValueSize - 1);
    249   int x = SkScalarFloorToInt(SkScalarMul(saturation, scalar_size)) +
    250       kBorderWidth;
    251   int y = SkScalarFloorToInt(SkScalarMul(SK_Scalar1 - value, scalar_size)) +
    252       kBorderWidth;
    253   if (gfx::Point(x, y) == marker_position_)
    254     return;
    255 
    256   marker_position_.set_x(x);
    257   marker_position_.set_y(y);
    258   SchedulePaint();
    259 }
    260 
    261 void ColorChooserView::SaturationValueView::ProcessEventAtLocation(
    262     const gfx::Point& point) {
    263   SkScalar scalar_size = SkIntToScalar(kSaturationValueSize - 1);
    264   SkScalar saturation = SkScalarDiv(
    265       SkIntToScalar(point.x() - kBorderWidth), scalar_size);
    266   SkScalar value = SK_Scalar1 - SkScalarDiv(
    267       SkIntToScalar(point.y() - kBorderWidth), scalar_size);
    268   saturation = SkScalarPin(saturation, 0, SK_Scalar1);
    269   value = SkScalarPin(value, 0, SK_Scalar1);
    270   OnSaturationValueChanged(saturation, value);
    271   chooser_view_->OnSaturationValueChosen(saturation, value);
    272 }
    273 
    274 gfx::Size ColorChooserView::SaturationValueView::GetPreferredSize() {
    275   return gfx::Size(kSaturationValueSize + kBorderWidth * 2,
    276                    kSaturationValueSize + kBorderWidth * 2);
    277 }
    278 
    279 void ColorChooserView::SaturationValueView::OnPaint(gfx::Canvas* canvas) {
    280   SkScalar hsv[3];
    281   hsv[0] = hue_;
    282   SkScalar scalar_size = SkIntToScalar(kSaturationValueSize - 1);
    283   for (int x = kBorderWidth; x < width() - kBorderWidth; ++x) {
    284     hsv[1] = SkScalarDiv(SkIntToScalar(x), scalar_size);
    285     for (int y = kBorderWidth; y < height() - kBorderWidth; ++y) {
    286       hsv[2] = SK_Scalar1 - SkScalarDiv(SkIntToScalar(y), scalar_size);
    287       canvas->FillRect(gfx::Rect(x, y, 1, 1), SkHSVToColor(255, hsv));
    288     }
    289   }
    290 
    291   // The background is very dark at the bottom of the view.  Use a white
    292   // marker in that case.
    293   SkColor indicator_color =
    294       (marker_position_.y() > width() * 3 / 4) ? SK_ColorWHITE : SK_ColorBLACK;
    295   canvas->FillRect(
    296       gfx::Rect(marker_position_.x(),
    297                 marker_position_.y() - kSaturationValueIndicatorSize,
    298                 1, kSaturationValueIndicatorSize * 2 + 1),
    299       indicator_color);
    300   canvas->FillRect(
    301       gfx::Rect(marker_position_.x() - kSaturationValueIndicatorSize,
    302                 marker_position_.y(),
    303                 kSaturationValueIndicatorSize * 2 + 1, 1),
    304       indicator_color);
    305   OnPaintBorder(canvas);
    306 }
    307 
    308 ////////////////////////////////////////////////////////////////////////////////
    309 // ColorChooserView::SelectedColorPatchView
    310 //
    311 // A view to simply show the selected color in a rectangle.
    312 class ColorChooserView::SelectedColorPatchView : public views::View {
    313  public:
    314   SelectedColorPatchView();
    315 
    316   void SetColor(SkColor color);
    317 
    318  private:
    319   DISALLOW_COPY_AND_ASSIGN(SelectedColorPatchView);
    320 };
    321 
    322 ColorChooserView::SelectedColorPatchView::SelectedColorPatchView() {
    323   set_focusable(false);
    324   SetVisible(true);
    325   set_border(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY));
    326 }
    327 
    328 void ColorChooserView::SelectedColorPatchView::SetColor(SkColor color) {
    329   if (!background())
    330     set_background(Background::CreateSolidBackground(color));
    331   else
    332     background()->SetNativeControlColor(color);
    333   SchedulePaint();
    334 }
    335 
    336 ////////////////////////////////////////////////////////////////////////////////
    337 // ColorChooserView
    338 //
    339 
    340 ColorChooserView::ColorChooserView(ColorChooserListener* listener,
    341                                    SkColor initial_color)
    342     : listener_(listener) {
    343   DCHECK(listener_);
    344 
    345   set_focusable(false);
    346   set_background(Background::CreateSolidBackground(SK_ColorLTGRAY));
    347   SetLayoutManager(new BoxLayout(BoxLayout::kVertical, kMarginWidth,
    348                                  kMarginWidth, kMarginWidth));
    349 
    350   View* container = new View();
    351   container->SetLayoutManager(new BoxLayout(BoxLayout::kHorizontal, 0, 0,
    352                                             kMarginWidth));
    353   saturation_value_ = new SaturationValueView(this);
    354   container->AddChildView(saturation_value_);
    355   hue_ = new HueView(this);
    356   container->AddChildView(hue_);
    357   AddChildView(container);
    358 
    359   View* container2 = new View();
    360   GridLayout* layout = new GridLayout(container2);
    361   container2->SetLayoutManager(layout);
    362   ColumnSet* columns = layout->AddColumnSet(0);
    363   columns->AddColumn(
    364       GridLayout::LEADING, GridLayout::FILL, 0, GridLayout::USE_PREF, 0, 0);
    365   columns->AddPaddingColumn(0, kMarginWidth);
    366   columns->AddColumn(
    367       GridLayout::FILL, GridLayout::FILL, 1, GridLayout::USE_PREF, 0, 0);
    368   layout->StartRow(0, 0);
    369   textfield_ = new Textfield();
    370   textfield_->SetController(this);
    371   textfield_->set_default_width_in_chars(kTextfieldLengthInChars);
    372   layout->AddView(textfield_);
    373   selected_color_patch_ = new SelectedColorPatchView();
    374   layout->AddView(selected_color_patch_);
    375   AddChildView(container2);
    376 
    377   OnColorChanged(initial_color);
    378 }
    379 
    380 ColorChooserView::~ColorChooserView() {
    381 }
    382 
    383 void ColorChooserView::OnColorChanged(SkColor color) {
    384   SkColorToHSV(color, hsv_);
    385   hue_->OnHueChanged(hsv_[0]);
    386   saturation_value_->OnHueChanged(hsv_[0]);
    387   saturation_value_->OnSaturationValueChanged(hsv_[1], hsv_[2]);
    388   selected_color_patch_->SetColor(color);
    389   textfield_->SetText(GetColorText(color));
    390 }
    391 
    392 void ColorChooserView::OnHueChosen(SkScalar hue) {
    393   hsv_[0] = hue;
    394   SkColor color = SkHSVToColor(255, hsv_);
    395   if (listener_)
    396     listener_->OnColorChosen(color);
    397   saturation_value_->OnHueChanged(hue);
    398   selected_color_patch_->SetColor(color);
    399   textfield_->SetText(GetColorText(color));
    400 }
    401 
    402 void ColorChooserView::OnSaturationValueChosen(SkScalar saturation,
    403                                                SkScalar value) {
    404   hsv_[1] = saturation;
    405   hsv_[2] = value;
    406   SkColor color = SkHSVToColor(255, hsv_);
    407   if (listener_)
    408     listener_->OnColorChosen(color);
    409   selected_color_patch_->SetColor(color);
    410   textfield_->SetText(GetColorText(color));
    411 }
    412 
    413 View* ColorChooserView::GetInitiallyFocusedView() {
    414   return textfield_;
    415 }
    416 
    417 ui::ModalType ColorChooserView::GetModalType() const {
    418   return ui::MODAL_TYPE_WINDOW;
    419 }
    420 
    421 void ColorChooserView::WindowClosing() {
    422   if (listener_)
    423     listener_->OnColorChooserDialogClosed();
    424 }
    425 
    426 View* ColorChooserView::GetContentsView() {
    427   return this;
    428 }
    429 
    430 void ColorChooserView::ContentsChanged(Textfield* sender,
    431                                        const string16& new_contents) {
    432   SkColor color = SK_ColorBLACK;
    433   if (GetColorFromText(new_contents, &color)) {
    434     SkColorToHSV(color, hsv_);
    435     if (listener_)
    436       listener_->OnColorChosen(color);
    437     hue_->OnHueChanged(hsv_[0]);
    438     saturation_value_->OnHueChanged(hsv_[0]);
    439     saturation_value_->OnSaturationValueChanged(hsv_[1], hsv_[2]);
    440     selected_color_patch_->SetColor(color);
    441   }
    442 }
    443 
    444 bool ColorChooserView::HandleKeyEvent(Textfield* sender,
    445                                       const ui::KeyEvent& key_event) {
    446   if (key_event.key_code() != ui::VKEY_RETURN &&
    447       key_event.key_code() != ui::VKEY_ESCAPE)
    448     return false;
    449 
    450   GetWidget()->Close();
    451   return true;
    452 }
    453 
    454 }  // namespace views
    455