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