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