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)
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <algorithm>
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "base/message_loop/message_loop.h"
115e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)#include "base/strings/stringprintf.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkCanvas.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkColor.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "third_party/skia/include/core/SkPaint.h"
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ui/accessibility/ax_view_state.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/base/resource/resource_bundle.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
19d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/gfx/animation/slide_animation.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/canvas.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/point.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/gfx/rect.h"
2303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "ui/resources/grit/ui_resources.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ui/views/widget/widget.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kSlideValueChangeDurationMS = 150;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBarImagesActive[] = {
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_ACTIVE_LEFT,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_ACTIVE_CENTER,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_PRESSED_CENTER,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_PRESSED_RIGHT,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const int kBarImagesDisabled[] = {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_LEFT,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_CENTER,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_CENTER,
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IDR_SLIDER_DISABLED_RIGHT,
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The image chunks.
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum BorderElements {
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  LEFT,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CENTER_LEFT,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CENTER_RIGHT,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RIGHT,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
50f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace views {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Slider::Slider(SliderListener* listener, Orientation orientation)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : listener_(listener),
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      orientation_(orientation),
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value_(0.f),
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      keyboard_increment_(0.1f),
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      animating_value_(0.f),
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      value_is_valid_(false),
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      accessibility_events_enabled_(true),
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      focus_border_color_(0),
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bar_active_images_(kBarImagesActive),
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      bar_disabled_images_(kBarImagesDisabled) {
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EnableCanvasFlippingForRTLUI(true);
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SetFocusable(true);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UpdateState(true);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Slider::~Slider() {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::SetValue(float value) {
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SetValueInternal(value, VALUE_CHANGED_BY_API);
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::SetKeyboardIncrement(float increment) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  keyboard_increment_ = increment;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::SetValueInternal(float value, SliderChangeReason reason) {
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool old_value_valid = value_is_valid_;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  value_is_valid_ = true;
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value < 0.0)
86f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    value = 0.0;
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else if (value > 1.0)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    value = 1.0;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (value_ == value)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float old_value = value_;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  value_ = value;
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (listener_)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    listener_->SliderValueChanged(this, value_, old_value, reason);
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (old_value_valid && base::MessageLoop::current()) {
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Do not animate when setting the value of the slider for the first time.
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // There is no message-loop when running tests. So we cannot animate then.
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    animating_value_ = old_value;
100d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    move_animation_.reset(new gfx::SlideAnimation(this));
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_animation_->SetSlideDuration(kSlideValueChangeDurationMS);
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    move_animation_->Show();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    AnimationProgressed(move_animation_.get());
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SchedulePaint();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (accessibility_events_enabled_ && GetWidget()) {
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    NotifyAccessibilityEvent(
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        ui::AX_EVENT_VALUE_CHANGED, true);
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::PrepareForMove(const gfx::Point& point) {
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Try to remember the position of the mouse cursor on the button.
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Insets inset = GetInsets();
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect content = GetContentsBounds();
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float value = move_animation_.get() && move_animation_->is_animating() ?
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        animating_value_ : value_;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For the horizontal orientation.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int thumb_x = value * (content.width() - thumb_->width());
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int candidate_x = (base::i18n::IsRTL() ?
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      width() - (point.x() - inset.left()) :
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      point.x() - inset.left()) - thumb_x;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (candidate_x >= 0 && candidate_x < thumb_->width())
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_x(candidate_x);
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_x(thumb_->width() / 2);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // For the vertical orientation.
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int thumb_y = (1.0 - value) * (content.height() - thumb_->height());
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int candidate_y = point.y() - thumb_y;
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (candidate_y >= 0 && candidate_y < thumb_->height())
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_y(candidate_y);
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    initial_button_offset_.set_y(thumb_->height() / 2);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::MoveButtonTo(const gfx::Point& point) {
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Insets inset = GetInsets();
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Calculate the value.
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL) {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int amount = base::i18n::IsRTL() ?
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        width() - inset.left() - point.x() - initial_button_offset_.x() :
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        point.x() - inset.left() - initial_button_offset_.x();
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetValueInternal(static_cast<float>(amount) /
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         (width() - inset.width() - thumb_->width()),
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     VALUE_CHANGED_BY_USER);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    SetValueInternal(
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        1.0f - static_cast<float>(point.y() - initial_button_offset_.y()) /
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            (height() - thumb_->height()),
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        VALUE_CHANGED_BY_USER);
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::UpdateState(bool control_on) {
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (control_on) {
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thumb_ = rb.GetImageNamed(IDR_SLIDER_ACTIVE_THUMB).ToImageSkia();
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < 4; ++i)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      images_[i] = rb.GetImageNamed(bar_active_images_[i]).ToImageSkia();
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    thumb_ = rb.GetImageNamed(IDR_SLIDER_DISABLED_THUMB).ToImageSkia();
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int i = 0; i < 4; ++i)
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      images_[i] = rb.GetImageNamed(bar_disabled_images_[i]).ToImageSkia();
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bar_height_ = images_[LEFT]->height();
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void Slider::SetAccessibleName(const base::string16& name) {
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  accessible_name_ = name;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void Slider::OnPaintFocus(gfx::Canvas* canvas) {
177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!HasFocus())
178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    return;
179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if (!focus_border_color_) {
181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    canvas->DrawFocusRect(GetLocalBounds());
182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  } else if (HasFocus()) {
183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    canvas->DrawSolidFocusRect(
184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        gfx::Rect(1, 1, width() - 3, height() - 3),
185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        focus_border_color_);
186a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  }
187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
189cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)gfx::Size Slider::GetPreferredSize() const {
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kSizeMajor = 200;
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int kSizeMinor = 40;
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return gfx::Size(std::max(width(), kSizeMajor), kSizeMinor);
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return gfx::Size(kSizeMinor, std::max(height(), kSizeMajor));
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::OnPaint(gfx::Canvas* canvas) {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  gfx::Rect content = GetContentsBounds();
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  float value = move_animation_.get() && move_animation_->is_animating() ?
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      animating_value_ : value_;
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (orientation_ == HORIZONTAL) {
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint slider bar with image resources.
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Inset the slider bar a little bit, so that the left or the right end of
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the slider bar will not be exposed under the thumb button when the thumb
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // button slides to the left most or right most position.
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kBarInsetX = 2;
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int bar_width = content.width() - kBarInsetX * 2;
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int bar_cy = content.height() / 2 - bar_height_ / 2;
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int w = content.width() - thumb_->width();
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int full = value * w;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int middle = std::max(full, images_[LEFT]->width());
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Save();
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Translate(gfx::Vector2d(kBarInsetX, bar_cy));
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*images_[LEFT], 0, 0);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*images_[RIGHT],
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_width - images_[RIGHT]->width(),
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0);
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->TileImageInt(*images_[CENTER_LEFT],
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         images_[LEFT]->width(),
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0,
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         middle - images_[LEFT]->width(),
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_height_);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->TileImageInt(*images_[CENTER_RIGHT],
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         middle,
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         0,
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_width - middle - images_[RIGHT]->width(),
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                         bar_height_);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->Restore();
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Paint slider thumb.
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int button_cx = content.x() + full;
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int thumb_y = content.height() / 2 - thumb_->height() / 2;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*thumb_, button_cx, thumb_y);
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(jennyz): draw vertical slider bar with resources.
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(sad): The painting code should use NativeTheme for various
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // platforms.
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kButtonRadius = thumb_->width() / 2;
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kLineThickness = bar_height_ / 2;
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkColor kFullColor = SkColorSetARGB(125, 0, 0, 0);
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const SkColor kEmptyColor = SkColorSetARGB(50, 0, 0, 0);
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int h = content.height() - thumb_->height();
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int full = value * h;
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int empty = h - full;
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int x = content.width() / 2 - kLineThickness / 2;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->FillRect(gfx::Rect(x, content.y() + kButtonRadius,
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               kLineThickness, empty),
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kEmptyColor);
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->FillRect(gfx::Rect(x, content.y() + empty + 2 * kButtonRadius,
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                               kLineThickness, full),
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     kFullColor);
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(mtomasz): We draw a thumb here because so far it is the same
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // for horizontal and vertical orientations. If it is different, then
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // we will need a separate resource.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int button_cy = content.y() + h - full;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int thumb_x = content.width() / 2 - thumb_->width() / 2;
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    canvas->DrawImageInt(*thumb_, thumb_x, button_cy);
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  View::OnPaint(canvas);
266a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  OnPaintFocus(canvas);
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Slider::OnMousePressed(const ui::MouseEvent& event) {
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!event.IsOnlyLeftMouseButton())
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  OnSliderDragStarted();
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  PrepareForMove(event.location());
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MoveButtonTo(event.location());
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool Slider::OnMouseDragged(const ui::MouseEvent& event) {
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MoveButtonTo(event.location());
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return true;
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void Slider::OnMouseReleased(const ui::MouseEvent& event) {
284f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  OnSliderDragEnded();
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)
308a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochvoid Slider::OnFocus() {
309a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  View::OnFocus();
310a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  SchedulePaint();
311a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch}
312a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
313a02191e04bc25c4935f804f2c080ae28663d096dBen Murdochvoid Slider::OnBlur() {
314a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  View::OnBlur();
315a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch  SchedulePaint();
316a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch}
317a02191e04bc25c4935f804f2c080ae28663d096dBen Murdoch
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void Slider::OnGestureEvent(ui::GestureEvent* event) {
319f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  switch (event->type()) {
320f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // In a multi point gesture only the touch point will generate
321f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // an ET_GESTURE_TAP_DOWN event.
322f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case ui::ET_GESTURE_TAP_DOWN:
323f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      OnSliderDragStarted();
324f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      PrepareForMove(event->location());
325f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      // Intentional fall through to next case.
326f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case ui::ET_GESTURE_SCROLL_BEGIN:
327f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case ui::ET_GESTURE_SCROLL_UPDATE:
328f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      MoveButtonTo(event->location());
329f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      event->SetHandled();
330f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
331f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    case ui::ET_GESTURE_END:
332f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      MoveButtonTo(event->location());
333f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      event->SetHandled();
334f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      if (event->details().touch_points() <= 1)
335f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        OnSliderDragEnded();
336f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
337f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    default:
338f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      break;
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
342d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void Slider::AnimationProgressed(const gfx::Animation* animation) {
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  animating_value_ = animation->CurrentValueBetween(animating_value_, value_);
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SchedulePaint();
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
347a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void Slider::GetAccessibleState(ui::AXViewState* state) {
348a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  state->role = ui::AX_ROLE_SLIDER;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  state->name = accessible_name_;
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  state->value = base::UTF8ToUTF16(
351f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      base::StringPrintf("%d%%", static_cast<int>(value_ * 100 + 0.5)));
352f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
353f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
354f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void Slider::OnSliderDragStarted() {
355f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (listener_)
356f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    listener_->SliderDragStarted(this);
357f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
358f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
359f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void Slider::OnSliderDragEnded() {
360f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (listener_)
361f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    listener_->SliderDragEnded(this);
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace views
365