color_chooser_view.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/string_number_conversions.h" 9#include "base/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 23namespace { 24 25const int kHueBarWidth = 20; 26const int kSaturationValueSize = 200; 27const int kMarginWidth = 5; 28const int kSaturationValueIndicatorSize = 6; 29const int kHueIndicatorSize = 5; 30const int kBorderWidth = 1; 31const int kTextfieldLengthInChars = 14; 32 33string16 GetColorText(SkColor color) { 34 return ASCIIToUTF16(base::StringPrintf("#%02x%02x%02x", 35 SkColorGetR(color), 36 SkColorGetG(color), 37 SkColorGetB(color))); 38} 39 40bool 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. 55class 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 90namespace 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. 97class 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 117ColorChooserView::HueView::HueView(ColorChooserView* chooser_view) 118 : chooser_view_(chooser_view), 119 level_(0) { 120 set_focusable(false); 121} 122 123void 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 134void 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 146gfx::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 152void 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. 208class 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 230ColorChooserView::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 238void ColorChooserView::SaturationValueView::OnHueChanged(SkScalar hue) { 239 if (hue_ != hue) { 240 hue_ = hue; 241 SchedulePaint(); 242 } 243} 244 245void 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 chooser_view_->OnSaturationValueChosen(saturation, value); 260} 261 262void ColorChooserView::SaturationValueView::ProcessEventAtLocation( 263 const gfx::Point& point) { 264 SkScalar scalar_size = SkIntToScalar(kSaturationValueSize - 1); 265 SkScalar saturation = SkScalarDiv( 266 SkIntToScalar(point.x() - kBorderWidth), scalar_size); 267 SkScalar value = SK_Scalar1 - SkScalarDiv( 268 SkIntToScalar(point.y() - kBorderWidth), scalar_size); 269 saturation = SkScalarPin(saturation, 0, SK_Scalar1); 270 value = SkScalarPin(value, 0, SK_Scalar1); 271 OnSaturationValueChanged(saturation, value); 272} 273 274gfx::Size ColorChooserView::SaturationValueView::GetPreferredSize() { 275 return gfx::Size(kSaturationValueSize + kBorderWidth * 2, 276 kSaturationValueSize + kBorderWidth * 2); 277} 278 279void 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. 312class 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 322ColorChooserView::SelectedColorPatchView::SelectedColorPatchView() { 323 set_focusable(false); 324 SetVisible(true); 325 set_border(Border::CreateSolidBorder(kBorderWidth, SK_ColorGRAY)); 326} 327 328void 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 340ColorChooserView::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 380ColorChooserView::~ColorChooserView() { 381} 382 383void 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 392void 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 402void 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 413View* ColorChooserView::GetInitiallyFocusedView() { 414 return textfield_; 415} 416 417ui::ModalType ColorChooserView::GetModalType() const { 418 return ui::MODAL_TYPE_WINDOW; 419} 420 421void ColorChooserView::WindowClosing() { 422 if (listener_) 423 listener_->OnColorChooserDialogClosed(); 424} 425 426View* ColorChooserView::GetContentsView() { 427 return this; 428} 429 430void 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 444bool 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