Chromium Code Reviews| Index: ui/views/color_chooser/color_chooser_view.cc |
| diff --git a/ui/views/color_chooser/color_chooser_view.cc b/ui/views/color_chooser/color_chooser_view.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b2ca2c1e86d406afb0a01ebfce72c30a9762bedf |
| --- /dev/null |
| +++ b/ui/views/color_chooser/color_chooser_view.cc |
| @@ -0,0 +1,323 @@ |
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "ui/views/color_chooser/color_chooser_view.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/logging.h" |
| +#include "base/message_loop.h" |
| +#include "base/string_number_conversions.h" |
| +#include "base/stringprintf.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "ui/gfx/canvas.h" |
| +#include "ui/base/keycodes/keyboard_codes.h" |
| +#include "ui/views/color_chooser/color_chooser_listener.h" |
| +#include "ui/views/controls/textfield/textfield.h" |
| +#include "ui/views/controls/textfield/textfield_controller.h" |
| +#include "ui/views/events/event.h" |
| +#include "ui/views/layout/box_layout.h" |
| +#include "ui/views/widget/widget.h" |
| + |
| +namespace { |
| + |
| +const int kHueBarWidth = 20; |
| +const int kSaturationValueSize = 200; |
| +const int kMarginWidth = 5; |
| +const int kSaturationValueIndicatorRadius = 3; |
|
Peter Kasting
2012/06/19 21:10:31
Nit: Looking at the screenshot, all the circles ar
Jun Mukai
2012/06/20 09:29:08
Changed to the triangular indicators as you said.
|
| +const int kHueIndicatorRadius = 2; |
| +const int kHueIndicatorSize = kHueIndicatorRadius * 2; |
| + |
| +string16 GetColorText(SkColor color) { |
| + return ASCIIToUTF16(base::StringPrintf("#%02x%02x%02x", |
| + SkColorGetR(color), |
| + SkColorGetG(color), |
| + SkColorGetB(color))); |
| +} |
| + |
| +bool GetColorFromText(const string16& text, SkColor* result) { |
| + if (text.size() != 6 && !(text.size() == 7 && text[0] == '#')) |
| + return false; |
| + |
| + std::string input = UTF16ToUTF8((text.size() == 6) ? text : text.substr(1)); |
| + std::vector<uint8> hex; |
| + if (!base::HexStringToBytes(input, &hex)) |
| + return false; |
| + |
| + *result = SkColorSetRGB(hex[0], hex[1], hex[2]); |
| + return true; |
| +} |
| + |
| +} // namespace |
| + |
| +namespace views { |
| + |
| +class ColorChooserView::HueView : public View { |
| + public: |
| + explicit HueView(ColorChooserView* chooser_view); |
| + |
| + void OnHueChanged(float hue); |
| + |
| + private: |
| + // View overrides: |
| + virtual gfx::Size GetPreferredSize() OVERRIDE; |
| + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; |
| + virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE; |
| + |
| + ColorChooserView* chooser_view_; |
| + int level_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(HueView); |
| +}; |
| + |
| +ColorChooserView::HueView::HueView(ColorChooserView* chooser_view) |
| + : chooser_view_(chooser_view), |
| + level_(0) { |
| + set_focusable(false); |
| +} |
| + |
| +void ColorChooserView::HueView::OnHueChanged(float hue) { |
| + int level = hue * GetPreferredSize().height() / 360.0f; |
| + if (level_ != level) { |
| + level_ = level; |
| + SchedulePaint(); |
| + } |
| +} |
| + |
| +gfx::Size ColorChooserView::HueView::GetPreferredSize() { |
| + // We put a small "indicator" circle on the both sides of the hue bar. |
| + return gfx::Size(kHueBarWidth + kHueIndicatorSize * 2, kSaturationValueSize); |
| +} |
| + |
| +void ColorChooserView::HueView::OnPaint(gfx::Canvas* canvas) { |
| + SkScalar hsv[3]; |
| + // In the hue bar, bightness and value for the color should be always 100%. |
|
Peter Kasting
2012/06/19 21:10:31
Nit: bightness [sic] -> saturation
Jun Mukai
2012/06/20 09:29:08
Done.
|
| + hsv[1] = SK_Scalar1; |
| + hsv[2] = SK_Scalar1; |
| + |
| + for (int y = 0; y < height(); ++y) { |
| + SkPaint paint; |
| + if (y == level_) { |
| + paint.setColor(SK_ColorBLACK); |
|
Peter Kasting
2012/06/19 21:10:31
This has the unfortunate side effect of making com
Jun Mukai
2012/06/20 09:29:08
Removed. I tried 50% saturation/value but it's st
|
| + } else { |
| + hsv[0] = 360.0f * y / height(); |
| + paint.setColor(SkHSVToColor(hsv)); |
| + } |
| + canvas->DrawLine(gfx::Point(kHueIndicatorSize, y), |
| + gfx::Point(width() - kHueIndicatorSize, y), |
| + paint); |
| + } |
| + |
| + SkPaint circle_paint; |
| + circle_paint.setColor(SK_ColorBLACK); |
| + circle_paint.setStyle(SkPaint::kStroke_Style); |
| + canvas->DrawCircle(gfx::Point(kHueIndicatorRadius, level_), |
| + kHueIndicatorRadius, |
| + circle_paint); |
| + canvas->DrawCircle(gfx::Point(width() - kHueIndicatorRadius, level_), |
| + kHueIndicatorRadius, |
| + circle_paint); |
| +} |
| + |
| +bool ColorChooserView::HueView::OnMousePressed(const MouseEvent& event) { |
| + level_ = std::max(0, std::min(height(), event.y())); |
| + chooser_view_->OnHueChosen(360.0f * level_ / height()); |
| + return true; |
| +} |
| + |
| +bool ColorChooserView::HueView::OnMouseDragged(const MouseEvent& event) { |
| + return OnMousePressed(event); |
| +} |
| + |
| +class ColorChooserView::SaturationValueView : public View { |
| + public: |
| + explicit SaturationValueView(ColorChooserView* chooser_view); |
| + |
| + void OnHueChanged(float hue); |
| + void OnSaturationValueChanged(float saturation, float value); |
| + |
| + private: |
| + // View overrides: |
| + virtual gfx::Size GetPreferredSize() OVERRIDE; |
| + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; |
| + virtual bool OnMousePressed(const MouseEvent& event) OVERRIDE; |
| + virtual bool OnMouseDragged(const MouseEvent& event) OVERRIDE; |
| + |
| + ColorChooserView* chooser_view_; |
| + float hue_; |
| + gfx::Point marker_position_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SaturationValueView); |
| +}; |
| + |
| +ColorChooserView::SaturationValueView::SaturationValueView( |
| + ColorChooserView* chooser_view) |
| + : chooser_view_(chooser_view), hue_(0) { |
| + set_focusable(false); |
| +} |
| + |
| +void ColorChooserView::SaturationValueView::OnHueChanged(float hue) { |
| + if (hue_ != hue) { |
| + hue_ = hue; |
| + SchedulePaint(); |
| + } |
| +} |
| + |
| +void ColorChooserView::SaturationValueView::OnSaturationValueChanged( |
| + float saturation, |
| + float value) { |
| + int x = saturation * kSaturationValueSize; |
| + int y = (1.0 - value) * kSaturationValueSize; |
| + if (gfx::Point(x, y) == marker_position_) |
| + return; |
| + |
| + marker_position_.set_x(x); |
| + marker_position_.set_y(y); |
| + SchedulePaint(); |
| + chooser_view_->OnSaturationValueChosen(saturation, value); |
| +} |
| + |
| +gfx::Size ColorChooserView::SaturationValueView::GetPreferredSize() { |
| + return gfx::Size(kSaturationValueSize, kSaturationValueSize); |
| +} |
| + |
| +void ColorChooserView::SaturationValueView::OnPaint(gfx::Canvas* canvas) { |
| + SkScalar hsv[3]; |
| + hsv[0] = hue_; |
| + for (int x = 0; x < width(); ++x) { |
| + hsv[1] = static_cast<float>(x) / width(); |
| + for (int y = 0; y < height(); ++y) { |
| + hsv[2] = 1.0 - static_cast<float>(y) / height(); |
| + SkPaint paint; |
| + paint.setColor(SkHSVToColor(255, hsv)); |
| + canvas->DrawPoint(gfx::Point(x, y), paint); |
| + } |
| + } |
| + |
| + SkPaint circle_paint; |
| + // The background is very dark at the bottom of the view. Use a white |
| + // marker in that case. |
| + circle_paint.setColor( |
| + (marker_position_.y() > width() * 3 / 4) ? SK_ColorWHITE : SK_ColorBLACK); |
| + circle_paint.setStyle(SkPaint::kStroke_Style); |
| + canvas->DrawCircle(marker_position_, |
| + kSaturationValueIndicatorRadius, circle_paint); |
| +} |
| + |
| +bool ColorChooserView::SaturationValueView::OnMousePressed( |
| + const MouseEvent& event) { |
| + float saturation = static_cast<float>(event.x()) / width(); |
| + float value = 1.0 - static_cast<float>(event.y()) / height(); |
| + saturation = std::max(0.0f, std::min(1.0f, saturation)); |
| + value = std::max(0.0f, std::min(1.0f, value)); |
| + OnSaturationValueChanged(saturation, value); |
| + return true; |
| +} |
| + |
| +bool ColorChooserView::SaturationValueView::OnMouseDragged( |
| + const MouseEvent& event) { |
| + return OnMousePressed(event); |
| +} |
| + |
| + |
| +ColorChooserView::ColorChooserView(ColorChooserListener* listener, |
| + SkColor initial_color) |
| + : listener_(listener) { |
| + DCHECK(listener_); |
| + |
| + set_focusable(false); |
| + set_background(Background::CreateSolidBackground(SK_ColorLTGRAY)); |
| + SetLayoutManager(new BoxLayout(BoxLayout::kVertical, kMarginWidth, |
| + kMarginWidth, kMarginWidth)); |
| + |
| + View* container = new View(); |
| + container->SetLayoutManager( |
| + new BoxLayout(BoxLayout::kHorizontal, 0, 0, kMarginWidth)); |
|
Peter Kasting
2012/06/19 21:10:31
Nit: For consistency:
container->SetLayoutManag
Jun Mukai
2012/06/20 09:29:08
Done.
|
| + saturation_value_ = new SaturationValueView(this); |
| + container->AddChildView(saturation_value_); |
| + hue_ = new HueView(this); |
| + container->AddChildView(hue_); |
| + AddChildView(container); |
| + |
| + textfield_ = new Textfield(); |
| + textfield_->SetController(this); |
| + AddChildView(textfield_); |
| + |
| + OnColorChanged(initial_color); |
| + MessageLoopForUI::current()->PostTask(FROM_HERE, |
| + base::Bind( |
|
Peter Kasting
2012/06/19 21:10:31
Nit: Why did you indent this and the next lines so
Jun Mukai
2012/06/20 09:29:08
Done.
|
| + &View::RequestFocus, |
| + base::Unretained(textfield_))); |
| +} |
| + |
| +ColorChooserView::~ColorChooserView() { |
| +} |
| + |
| +void ColorChooserView::OnOwningWindowClosed() { |
| + listener_ = NULL; |
| + Widget* widget = GetWidget(); |
| + if (widget && widget->IsVisible()) |
| + widget->Close(); |
| +} |
| + |
| +void ColorChooserView::OnColorChanged(SkColor color) { |
| + SkColorToHSV(color, hsv_); |
| + hue_->OnHueChanged(hsv_[0]); |
| + saturation_value_->OnHueChanged(hsv_[0]); |
| + saturation_value_->OnSaturationValueChanged(hsv_[1], hsv_[2]); |
| + textfield_->SetText(GetColorText(color)); |
| +} |
| + |
| +void ColorChooserView::OnHueChosen(float hue) { |
| + hsv_[0] = hue; |
| + SkColor color = SkHSVToColor(255, hsv_); |
| + listener_->OnColorChosen(color); |
| + saturation_value_->OnHueChanged(hue); |
| + textfield_->SetText(GetColorText(color)); |
| +} |
| + |
| +void ColorChooserView::OnSaturationValueChosen(float saturation, float value) { |
| + hsv_[1] = saturation; |
| + hsv_[2] = value; |
| + SkColor color = SkHSVToColor(255, hsv_); |
| + listener_->OnColorChosen(color); |
| + textfield_->SetText(GetColorText(color)); |
| +} |
| + |
| +ui::ModalType ColorChooserView::GetModalType() const { |
| + return ui::MODAL_TYPE_WINDOW; |
| +} |
| + |
| +void ColorChooserView::WindowClosing() { |
| + if (listener_) |
| + listener_->OnColorChooserDialogClosed(); |
| +} |
| + |
| +View* ColorChooserView::GetContentsView() { |
| + return this; |
| +} |
| + |
| +void ColorChooserView::ContentsChanged(Textfield* sender, |
| + const string16& new_contents) { |
| + SkColor color; |
| + if (GetColorFromText(new_contents, &color)) { |
| + SkColorToHSV(color, hsv_); |
| + listener_->OnColorChosen(color); |
| + hue_->OnHueChanged(hsv_[0]); |
| + saturation_value_->OnHueChanged(hsv_[0]); |
| + saturation_value_->OnSaturationValueChanged(hsv_[1], hsv_[2]); |
| + } |
| +} |
| + |
| +bool ColorChooserView::HandleKeyEvent(Textfield* sender, |
| + const KeyEvent& key_event) { |
| + if (key_event.key_code() != ui::VKEY_RETURN && |
| + key_event.key_code() != ui::VKEY_ESCAPE) |
| + return false; |
| + |
| + GetWidget()->Close(); |
| + return true; |
| +} |
| + |
| +} // namespace views |