slider.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/controls/slider.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h"
95e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/stringprintf.h"
10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "grit/ui_resources.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkCanvas.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkColor.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPaint.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/accessibility/ax_view_state.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
17d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/slide_animation.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/point.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kSlideValueChangeDurationMS = 150;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBarImagesActive[] = {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_ACTIVE_LEFT,
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_ACTIVE_CENTER,
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_PRESSED_CENTER,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_PRESSED_RIGHT,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBarImagesDisabled[] = {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_LEFT,
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_CENTER,
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_CENTER,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_RIGHT,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The image chunks.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum BorderElements {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LEFT,
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CENTER_LEFT,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CENTER_RIGHT,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RIGHT,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views {
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Slider::Slider(SliderListener* listener, Orientation orientation)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : listener_(listener),
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      orientation_(orientation),
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value_(0.f),
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keyboard_increment_(0.1f),
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      animating_value_(0.f),
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value_is_valid_(false),
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      accessibility_events_enabled_(true),
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      focus_border_color_(0),
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bar_active_images_(kBarImagesActive),
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bar_disabled_images_(kBarImagesDisabled) {
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnableCanvasFlippingForRTLUI(true);
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SetFocusable(true);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateState(true);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Slider::~Slider() {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::SetValue(float value) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetValueInternal(value, VALUE_CHANGED_BY_API);
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::SetKeyboardIncrement(float increment) {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  keyboard_increment_ = increment;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::SetValueInternal(float value, SliderChangeReason reason) {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool old_value_valid = value_is_valid_;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  value_is_valid_ = true;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value < 0.0)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   value = 0.0;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (value > 1.0)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value = 1.0;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value_ == value)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float old_value = value_;
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  value_ = value;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (listener_)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_->SliderValueChanged(this, value_, old_value, reason);
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (old_value_valid && base::MessageLoop::current()) {
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Do not animate when setting the value of the slider for the first time.
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // There is no message-loop when running tests. So we cannot animate then.
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    animating_value_ = old_value;
98d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    move_animation_.reset(new gfx::SlideAnimation(this));
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_animation_->SetSlideDuration(kSlideValueChangeDurationMS);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_animation_->Show();
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AnimationProgressed(move_animation_.get());
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (accessibility_events_enabled_ && GetWidget()) {
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    NotifyAccessibilityEvent(
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ui::AX_EVENT_VALUE_CHANGED, true);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::PrepareForMove(const gfx::Point& point) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try to remember the position of the mouse cursor on the button.
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Insets inset = GetInsets();
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect content = GetContentsBounds();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float value = move_animation_.get() && move_animation_->is_animating() ?
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        animating_value_ : value_;
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For the horizontal orientation.
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int thumb_x = value * (content.width() - thumb_->width());
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int candidate_x = (base::i18n::IsRTL() ?
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      width() - (point.x() - inset.left()) :
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      point.x() - inset.left()) - thumb_x;
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (candidate_x >= 0 && candidate_x < thumb_->width())
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_x(candidate_x);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_x(thumb_->width() / 2);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For the vertical orientation.
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int thumb_y = (1.0 - value) * (content.height() - thumb_->height());
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int candidate_y = point.y() - thumb_y;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (candidate_y >= 0 && candidate_y < thumb_->height())
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_y(candidate_y);
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_y(thumb_->height() / 2);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::MoveButtonTo(const gfx::Point& point) {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Insets inset = GetInsets();
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Calculate the value.
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL) {
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int amount = base::i18n::IsRTL() ?
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        width() - inset.left() - point.x() - initial_button_offset_.x() :
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        point.x() - inset.left() - initial_button_offset_.x();
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetValueInternal(static_cast<float>(amount) /
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         (width() - inset.width() - thumb_->width()),
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     VALUE_CHANGED_BY_USER);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetValueInternal(
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        1.0f - static_cast<float>(point.y() - initial_button_offset_.y()) /
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (height() - thumb_->height()),
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        VALUE_CHANGED_BY_USER);
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::UpdateState(bool control_on) {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (control_on) {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thumb_ = rb.GetImageNamed(IDR_SLIDER_ACTIVE_THUMB).ToImageSkia();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < 4; ++i)
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      images_[i] = rb.GetImageNamed(bar_active_images_[i]).ToImageSkia();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thumb_ = rb.GetImageNamed(IDR_SLIDER_DISABLED_THUMB).ToImageSkia();
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < 4; ++i)
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      images_[i] = rb.GetImageNamed(bar_disabled_images_[i]).ToImageSkia();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bar_height_ = images_[LEFT]->height();
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void Slider::SetAccessibleName(const base::string16& name) {
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  accessible_name_ = name;
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void Slider::OnPaintFocus(gfx::Canvas* canvas) {
175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!HasFocus())
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!focus_border_color_) {
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    canvas->DrawFocusRect(GetLocalBounds());
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  } else if (HasFocus()) {
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    canvas->DrawSolidFocusRect(
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        gfx::Rect(1, 1, width() - 3, height() - 3),
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        focus_border_color_);
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
186a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)gfx::Size Slider::GetPreferredSize() {
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kSizeMajor = 200;
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kSizeMinor = 40;
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL)
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return gfx::Size(std::max(width(), kSizeMajor), kSizeMinor);
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Size(kSizeMinor, std::max(height(), kSizeMajor));
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::OnPaint(gfx::Canvas* canvas) {
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect content = GetContentsBounds();
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float value = move_animation_.get() && move_animation_->is_animating() ?
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      animating_value_ : value_;
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL) {
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint slider bar with image resources.
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Inset the slider bar a little bit, so that the left or the right end of
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the slider bar will not be exposed under the thumb button when the thumb
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // button slides to the left most or right most position.
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kBarInsetX = 2;
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int bar_width = content.width() - kBarInsetX * 2;
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int bar_cy = content.height() / 2 - bar_height_ / 2;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int w = content.width() - thumb_->width();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int full = value * w;
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int middle = std::max(full, images_[LEFT]->width());
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Save();
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Translate(gfx::Vector2d(kBarInsetX, bar_cy));
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*images_[LEFT], 0, 0);
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*images_[RIGHT],
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_width - images_[RIGHT]->width(),
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->TileImageInt(*images_[CENTER_LEFT],
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         images_[LEFT]->width(),
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0,
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         middle - images_[LEFT]->width(),
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_height_);
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->TileImageInt(*images_[CENTER_RIGHT],
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         middle,
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0,
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_width - middle - images_[RIGHT]->width(),
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_height_);
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Restore();
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint slider thumb.
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int button_cx = content.x() + full;
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int thumb_y = content.height() / 2 - thumb_->height() / 2;
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*thumb_, button_cx, thumb_y);
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(jennyz): draw vertical slider bar with resources.
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(sad): The painting code should use NativeTheme for various
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // platforms.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kButtonRadius = thumb_->width() / 2;
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kLineThickness = bar_height_ / 2;
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkColor kFullColor = SkColorSetARGB(125, 0, 0, 0);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkColor kEmptyColor = SkColorSetARGB(50, 0, 0, 0);
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int h = content.height() - thumb_->height();
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int full = value * h;
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int empty = h - full;
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int x = content.width() / 2 - kLineThickness / 2;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->FillRect(gfx::Rect(x, content.y() + kButtonRadius,
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               kLineThickness, empty),
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kEmptyColor);
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->FillRect(gfx::Rect(x, content.y() + empty + 2 * kButtonRadius,
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               kLineThickness, full),
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kFullColor);
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(mtomasz): We draw a thumb here because so far it is the same
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for horizontal and vertical orientations. If it is different, then
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we will need a separate resource.
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int button_cy = content.y() + h - full;
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int thumb_x = content.width() / 2 - thumb_->width() / 2;
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*thumb_, thumb_x, button_cy);
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  View::OnPaint(canvas);
264a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  OnPaintFocus(canvas);
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Slider::OnMousePressed(const ui::MouseEvent& event) {
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!event.IsOnlyLeftMouseButton())
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (listener_)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_->SliderDragStarted(this);
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForMove(event.location());
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MoveButtonTo(event.location());
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Slider::OnMouseDragged(const ui::MouseEvent& event) {
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MoveButtonTo(event.location());
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::OnMouseReleased(const ui::MouseEvent& event) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (listener_)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_->SliderDragEnded(this);
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Slider::OnKeyPressed(const ui::KeyEvent& event) {
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL) {
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (event.key_code() == ui::VKEY_LEFT) {
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetValueInternal(value_ - keyboard_increment_, VALUE_CHANGED_BY_USER);
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (event.key_code() == ui::VKEY_RIGHT) {
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetValueInternal(value_ + keyboard_increment_, VALUE_CHANGED_BY_USER);
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (event.key_code() == ui::VKEY_DOWN) {
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetValueInternal(value_ - keyboard_increment_, VALUE_CHANGED_BY_USER);
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (event.key_code() == ui::VKEY_UP) {
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SetValueInternal(value_ + keyboard_increment_, VALUE_CHANGED_BY_USER);
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return true;
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return false;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void Slider::OnGestureEvent(ui::GestureEvent* event) {
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN ||
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      event->type() == ui::ET_GESTURE_TAP_DOWN) {
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PrepareForMove(event->location());
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MoveButtonTo(event->location());
3132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    event->SetHandled();
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE ||
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)             event->type() == ui::ET_GESTURE_SCROLL_END) {
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    MoveButtonTo(event->location());
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    event->SetHandled();
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
321d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void Slider::AnimationProgressed(const gfx::Animation* animation) {
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  animating_value_ = animation->CurrentValueBetween(animating_value_, value_);
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
326a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void Slider::GetAccessibleState(ui::AXViewState* state) {
327a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state->role = ui::AX_ROLE_SLIDER;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->name = accessible_name_;
3295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  state->value = base::UTF8ToUTF16(
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::StringPrintf("%d%%", (int)(value_ * 100 + 0.5)));
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace views
334