| // Copyright 2016 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 "ash/display/touch_calibrator_view.h" |
| |
| #include <memory> |
| |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/shell.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| #include "ui/gfx/animation/linear_animation.h" |
| #include "ui/gfx/animation/throb_animation.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/paint_vector_icon.h" |
| #include "ui/strings/grit/ui_strings.h" |
| #include "ui/views/background.h" |
| #include "ui/views/bubble/bubble_border.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| constexpr char kWidgetName[] = "TouchCalibratorOverlay"; |
| |
| constexpr int kAnimationFrameRate = 100; |
| constexpr auto kFadeDuration = base::TimeDelta::FromMilliseconds(150); |
| constexpr auto kPointMoveDuration = base::TimeDelta::FromMilliseconds(400); |
| constexpr auto kPointMoveDurationLong = base::TimeDelta::FromMilliseconds(500); |
| |
| const SkColor kExitLabelColor = SkColorSetARGB(255, 138, 138, 138); |
| constexpr int kExitLabelWidth = 300; |
| constexpr int kExitLabelHeight = 20; |
| |
| const SkColor kTapHereLabelColor = SK_ColorWHITE; |
| |
| constexpr int kHintBoxWidth = 298; |
| constexpr int kHintBoxHeight = 180; |
| constexpr int kHintBoxLabelTextSize = 5; |
| constexpr int kHintBoxSublabelTextSize = 3; |
| |
| constexpr int kThrobberCircleViewWidth = 64; |
| constexpr float kThrobberCircleRadiusFactor = 3.f / 8.f; |
| |
| constexpr auto kFinalMessageTransitionDuration = |
| base::TimeDelta::FromMilliseconds(200); |
| constexpr int kCompleteMessageViewWidth = 427; |
| constexpr int kCompleteMessageViewHeight = kThrobberCircleViewWidth; |
| constexpr int kCompleteMessageTextSize = 16; |
| |
| constexpr int kTouchPointViewOffset = 100; |
| |
| constexpr int kTapLabelHeight = 48; |
| constexpr int kTapLabelWidth = 80; |
| |
| const SkColor kHintLabelTextColor = SK_ColorBLACK; |
| const SkColor kHintSublabelTextColor = SkColorSetARGB(255, 161, 161, 161); |
| |
| const SkColor kInnerCircleColor = SK_ColorWHITE; |
| const SkColor kOuterCircleColor = SkColorSetA(kInnerCircleColor, 255 * 0.2); |
| |
| constexpr int kCircleAnimationDurationMs = 900; |
| |
| constexpr int kHintRectBorderRadius = 4; |
| |
| constexpr float kBackgroundFinalOpacity = 0.75f; |
| |
| constexpr int kTouchTargetWidth = 64; |
| constexpr int kTouchTargetHeight = kTouchTargetWidth + kTouchTargetWidth / 2; |
| |
| constexpr float kTouchTargetVerticalOffsetFactor = 11.f / 24.f; |
| |
| const SkColor kTouchTargetInnerCircleColor = SkColorSetARGB(255, 66, 133, 244); |
| const SkColor kTouchTargetOuterCircleColor = |
| SkColorSetA(kTouchTargetInnerCircleColor, 255 * 0.2); |
| const SkColor kHandIconColor = SkColorSetARGB(255, 201, 201, 201); |
| constexpr float kHandIconHorizontalOffsetFactor = 7.f / 32.f; |
| |
| // Returns the initialization params for the widget that contains the touch |
| // calibrator view. |
| views::Widget::InitParams GetWidgetParams(aura::Window* root_window) { |
| views::Widget::InitParams params; |
| params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
| params.name = kWidgetName; |
| params.keep_on_top = true; |
| params.accept_events = true; |
| params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
| params.parent = |
| Shell::GetContainer(root_window, kShellWindowId_OverlayContainer); |
| return params; |
| } |
| |
| // Returns the size of bounding box required for |text| of given |font_list|. |
| gfx::Size GetSizeForString(const base::string16& text, |
| const gfx::FontList& font_list) { |
| int height = 0, width = 0; |
| gfx::Canvas::SizeStringInt(text, font_list, &width, &height, 0, 0); |
| return gfx::Size(width, height); |
| } |
| |
| void AnimateLayerToPosition(views::View* view, |
| base::TimeDelta duration, |
| gfx::Point end_position, |
| float opacity = 1.f) { |
| ui::ScopedLayerAnimationSettings slide_settings(view->layer()->GetAnimator()); |
| slide_settings.SetPreemptionStrategy( |
| ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| slide_settings.SetTransitionDuration(duration); |
| view->SetBoundsRect(gfx::Rect(end_position, view->size())); |
| view->layer()->SetOpacity(opacity); |
| } |
| |
| } // namespace |
| |
| // Creates a throbbing animated view with two concentric circles. The radius of |
| // the inner circle is fixed while that of the outer circle oscillates between a |
| // min and max radius. The animation takes |animation_duration| milliseconds |
| // to complete. The center of these circles are at the center of the view |
| // element. |
| class CircularThrobberView : public views::View, public gfx::AnimationDelegate { |
| public: |
| CircularThrobberView(int width, |
| const SkColor& inner_circle_color, |
| const SkColor& outer_circle_color, |
| int animation_duration); |
| ~CircularThrobberView() override; |
| |
| // views::View overrides: |
| void OnPaint(gfx::Canvas* canvas) override; |
| |
| // gfx::AnimationDelegate overrides: |
| void AnimationProgressed(const gfx::Animation* animation) override; |
| |
| private: |
| // Radius of the inner circle. |
| const int inner_radius_; |
| |
| // Current radius of the outer circle. |
| int outer_radius_; |
| |
| // Minimum radius for outer animated circle. |
| const int smallest_radius_animated_circle_; |
| |
| // Maximum radius for outer animated circle. |
| const int largest_radius_animated_circle_; |
| |
| cc::PaintFlags inner_circle_flags_; |
| cc::PaintFlags outer_circle_flags_; |
| |
| std::unique_ptr<gfx::ThrobAnimation> animation_; |
| |
| // Center of the concentric circles. |
| const gfx::Point center_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CircularThrobberView); |
| }; |
| |
| CircularThrobberView::CircularThrobberView(int width, |
| const SkColor& inner_circle_color, |
| const SkColor& outer_circle_color, |
| int animation_duration) |
| : inner_radius_(width / 4), |
| outer_radius_(inner_radius_), |
| smallest_radius_animated_circle_(width * kThrobberCircleRadiusFactor), |
| largest_radius_animated_circle_(width / 2), |
| center_(gfx::Point(width / 2, width / 2)) { |
| SetSize(gfx::Size(width, width)); |
| |
| inner_circle_flags_.setColor(inner_circle_color); |
| inner_circle_flags_.setAntiAlias(true); |
| inner_circle_flags_.setStyle(cc::PaintFlags::kFill_Style); |
| |
| outer_circle_flags_.setColor(outer_circle_color); |
| outer_circle_flags_.setAntiAlias(true); |
| outer_circle_flags_.setStyle(cc::PaintFlags::kFill_Style); |
| |
| animation_.reset(new gfx::ThrobAnimation(this)); |
| animation_->SetThrobDuration(animation_duration); |
| animation_->StartThrobbing(-1); |
| |
| SchedulePaint(); |
| } |
| |
| CircularThrobberView::~CircularThrobberView() = default; |
| |
| void CircularThrobberView::OnPaint(gfx::Canvas* canvas) { |
| canvas->DrawCircle(center_, outer_radius_, outer_circle_flags_); |
| canvas->DrawCircle(center_, inner_radius_, inner_circle_flags_); |
| } |
| |
| void CircularThrobberView::AnimationProgressed( |
| const gfx::Animation* animation) { |
| if (animation != animation_.get()) |
| return; |
| outer_radius_ = animation->CurrentValueBetween( |
| smallest_radius_animated_circle_, largest_radius_animated_circle_); |
| SchedulePaint(); |
| } |
| |
| class TouchTargetThrobberView : public CircularThrobberView { |
| public: |
| TouchTargetThrobberView(const gfx::Rect& bounds, |
| const SkColor& inner_circle_color, |
| const SkColor& outer_circle_color, |
| const SkColor& hand_icon_color, |
| int animation_duration); |
| ~TouchTargetThrobberView() override; |
| |
| // views::View overrides: |
| void OnPaint(gfx::Canvas* canvas) override; |
| |
| private: |
| const int horizontal_offset_; |
| |
| const int icon_width_; |
| |
| gfx::ImageSkia hand_icon_; |
| |
| DISALLOW_COPY_AND_ASSIGN(TouchTargetThrobberView); |
| }; |
| |
| TouchTargetThrobberView::TouchTargetThrobberView( |
| const gfx::Rect& bounds, |
| const SkColor& inner_circle_color, |
| const SkColor& outer_circle_color, |
| const SkColor& hand_icon_color, |
| int animation_duration) |
| : CircularThrobberView(bounds.width(), |
| inner_circle_color, |
| outer_circle_color, |
| animation_duration), |
| horizontal_offset_(bounds.width() * kHandIconHorizontalOffsetFactor), |
| icon_width_(bounds.width() * 0.5f) { |
| SetBoundsRect(bounds); |
| |
| hand_icon_ = gfx::CreateVectorIcon(kTouchCalibrationHandIcon, kHandIconColor); |
| } |
| |
| TouchTargetThrobberView::~TouchTargetThrobberView() = default; |
| |
| void TouchTargetThrobberView::OnPaint(gfx::Canvas* canvas) { |
| CircularThrobberView::OnPaint(canvas); |
| canvas->DrawImageInt(hand_icon_, horizontal_offset_, icon_width_); |
| } |
| |
| // Circular _________________________________ |
| // Throbber | | |
| // View | | |
| // ___________ | | |
| // | | | | |
| // | | | | |
| // | . | | Hint Box | |
| // | | | | |
| // |___________| | | |
| // | | |
| // | | |
| // |_________________________________| |
| // |
| // This view is set next to the throbber circle view such that their centers |
| // align. The hint box has a label text and a sublabel text to assist the |
| // user by informing them about the next step in the calibration process. |
| class HintBox : public views::View { |
| public: |
| HintBox(const gfx::Rect& bounds, int border_radius); |
| ~HintBox() override; |
| |
| // views::View overrides: |
| void OnPaint(gfx::Canvas* canvas) override; |
| |
| void SetLabel(const base::string16& text, const SkColor& color); |
| void SetSubLabel(const base::string16& text, const SkColor& color); |
| |
| private: |
| void UpdateWidth(int updated_width); |
| |
| base::string16 label_text_; |
| base::string16 sublabel_text_; |
| |
| SkColor label_color_; |
| SkColor sublabel_color_; |
| |
| const int border_radius_; |
| |
| int base_border_; |
| |
| int arrow_width_; |
| |
| int horizontal_offset_; |
| |
| gfx::Rect rounded_rect_bounds_; |
| |
| gfx::FontList label_font_list_; |
| gfx::FontList sublabel_font_list_; |
| |
| gfx::Rect label_text_bounds_; |
| gfx::Rect sublabel_text_bounds_; |
| |
| cc::PaintFlags flags_; |
| |
| DISALLOW_COPY_AND_ASSIGN(HintBox); |
| }; |
| |
| HintBox::HintBox(const gfx::Rect& bounds, int border_radius) |
| : border_radius_(border_radius) { |
| SetBorder(std::make_unique<views::BubbleBorder>( |
| base::i18n::IsRTL() ? views::BubbleBorder::RIGHT_CENTER |
| : views::BubbleBorder::LEFT_CENTER, |
| views::BubbleBorder::NO_SHADOW_OPAQUE_BORDER, SK_ColorWHITE)); |
| |
| arrow_width_ = (GetInsets().right() - GetInsets().left()) * |
| (base::i18n::IsRTL() ? 1 : -1); |
| |
| // Border on all sides are the same except on the side of the arrow, in which |
| // case the width of the arrow is additional. |
| base_border_ = base::i18n::IsRTL() ? GetInsets().left() : GetInsets().right(); |
| |
| SetBounds(bounds.x(), bounds.y() - base_border_, |
| bounds.width() + 2 * base_border_ + arrow_width_, |
| bounds.height() + 2 * base_border_); |
| |
| rounded_rect_bounds_ = GetLocalBounds(); |
| rounded_rect_bounds_.Inset(GetInsets()); |
| |
| flags_.setColor(SK_ColorWHITE); |
| flags_.setStyle(cc::PaintFlags::kFill_Style); |
| flags_.setAntiAlias(true); |
| |
| horizontal_offset_ = |
| arrow_width_ + base_border_ + rounded_rect_bounds_.width() * 0.08f; |
| int top_offset = horizontal_offset_; |
| int line_gap = rounded_rect_bounds_.height() * 0.018f; |
| int label_height = rounded_rect_bounds_.height() * 0.11f; |
| |
| label_text_bounds_.SetRect(horizontal_offset_, top_offset, 0, label_height); |
| |
| top_offset += label_text_bounds_.height() + line_gap; |
| |
| sublabel_text_bounds_.SetRect(horizontal_offset_, top_offset, 0, |
| label_height); |
| } |
| |
| HintBox::~HintBox() = default; |
| |
| void HintBox::UpdateWidth(int updated_width) { |
| SetSize(gfx::Size(updated_width + 2 * base_border_ + arrow_width_, height())); |
| rounded_rect_bounds_ = GetLocalBounds(); |
| rounded_rect_bounds_.Inset(GetInsets()); |
| } |
| |
| void HintBox::SetLabel(const base::string16& text, const SkColor& color) { |
| label_text_ = text; |
| label_color_ = color; |
| |
| label_font_list_ = |
| ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( |
| kHintBoxLabelTextSize, gfx::Font::FontStyle::NORMAL, |
| gfx::Font::Weight::NORMAL); |
| |
| // Adjust size of label bounds based on text and font. |
| gfx::Size size = GetSizeForString(label_text_, label_font_list_); |
| label_text_bounds_.set_size( |
| gfx::Size(size.width(), label_text_bounds_.height())); |
| |
| // Check if the width of hint box needs to be updated. |
| int minimum_expected_width = |
| size.width() + 2 * horizontal_offset_ - arrow_width_; |
| if (minimum_expected_width > rounded_rect_bounds_.width()) |
| UpdateWidth(minimum_expected_width); |
| } |
| |
| void HintBox::SetSubLabel(const base::string16& text, const SkColor& color) { |
| sublabel_text_ = text; |
| sublabel_color_ = color; |
| |
| sublabel_font_list_ = |
| ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( |
| kHintBoxSublabelTextSize, gfx::Font::FontStyle::NORMAL, |
| gfx::Font::Weight::NORMAL); |
| |
| // Adjust size of sublabel label bounds based on text and font. |
| gfx::Size size = GetSizeForString(sublabel_text_, sublabel_font_list_); |
| sublabel_text_bounds_.set_size( |
| gfx::Size(size.width(), sublabel_text_bounds_.height())); |
| |
| // Check if the width of hint box needs to be updated. |
| int minimum_expected_width = |
| size.width() + 2 * horizontal_offset_ - arrow_width_; |
| if (minimum_expected_width > rounded_rect_bounds_.width()) |
| UpdateWidth(minimum_expected_width); |
| } |
| |
| void HintBox::OnPaint(gfx::Canvas* canvas) { |
| views::View::OnPaint(canvas); |
| canvas->DrawRoundRect(rounded_rect_bounds_, border_radius_, flags_); |
| canvas->DrawStringRectWithFlags(label_text_, label_font_list_, label_color_, |
| label_text_bounds_, gfx::Canvas::NO_ELLIPSIS); |
| canvas->DrawStringRectWithFlags(sublabel_text_, sublabel_font_list_, |
| sublabel_color_, sublabel_text_bounds_, |
| gfx::Canvas::NO_ELLIPSIS); |
| } |
| |
| class CompletionMessageView : public views::View { |
| public: |
| CompletionMessageView(const gfx::Rect& bounds, const base::string16& message); |
| ~CompletionMessageView() override; |
| |
| // views::View overrides: |
| void OnPaint(gfx::Canvas* canvas) override; |
| |
| private: |
| const base::string16 message_; |
| gfx::FontList font_list_; |
| |
| gfx::Rect text_bounds_; |
| |
| gfx::ImageSkia check_icon_; |
| |
| cc::PaintFlags flags_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CompletionMessageView); |
| }; |
| |
| CompletionMessageView::CompletionMessageView(const gfx::Rect& bounds, |
| const base::string16& message) |
| : message_(message) { |
| SetBoundsRect(bounds); |
| |
| int x_offset = height() * 5.f / 4.f; |
| text_bounds_.SetRect(x_offset, 0, width() - x_offset, height()); |
| |
| font_list_ = ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta( |
| kCompleteMessageTextSize, gfx::Font::FontStyle::NORMAL, |
| gfx::Font::Weight::NORMAL); |
| |
| // crbug/676513 moves this file to src/ash which will require an ash icon |
| // file. |
| check_icon_ = |
| gfx::CreateVectorIcon(kTouchCalibrationCompleteCheckIcon, SK_ColorWHITE); |
| |
| flags_.setColor(SK_ColorWHITE); |
| flags_.setStyle(cc::PaintFlags::kFill_Style); |
| flags_.setAntiAlias(true); |
| } |
| |
| CompletionMessageView::~CompletionMessageView() = default; |
| |
| void CompletionMessageView::OnPaint(gfx::Canvas* canvas) { |
| canvas->DrawImageInt(check_icon_, 0, 0); |
| |
| // TODO(malaykeshav): Work with elizabethchiu@ to get better UX for RTL. |
| canvas->DrawStringRectWithFlags( |
| message_, font_list_, flags_.getColor(), text_bounds_, |
| gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::NO_SUBPIXEL_RENDERING); |
| } |
| |
| TouchCalibratorView::TouchCalibratorView(const display::Display& target_display, |
| bool is_primary_view) |
| : display_(target_display), |
| is_primary_view_(is_primary_view), |
| exit_label_(nullptr), |
| tap_label_(nullptr), |
| throbber_circle_(nullptr), |
| hint_box_view_(nullptr), |
| touch_point_view_(nullptr) { |
| aura::Window* root = Shell::GetRootWindowForDisplayId(display_.id()); |
| widget_.reset(new views::Widget); |
| widget_->Init(GetWidgetParams(root)); |
| widget_->SetContentsView(this); |
| widget_->SetBounds(display_.bounds()); |
| widget_->Show(); |
| set_owned_by_client(); |
| |
| animator_.reset( |
| new gfx::LinearAnimation(kFadeDuration, kAnimationFrameRate, this)); |
| |
| InitViewContents(); |
| AdvanceToNextState(); |
| } |
| |
| TouchCalibratorView::~TouchCalibratorView() { |
| state_ = UNKNOWN; |
| widget_->Hide(); |
| animator_->End(); |
| } |
| |
| void TouchCalibratorView::InitViewContents() { |
| // Initialize the background rect. |
| background_rect_ = |
| gfx::RectF(0, 0, display_.bounds().width(), display_.bounds().height()); |
| |
| ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); |
| // Initialize exit label that informs the user how to exit the touch |
| // calibration setup. |
| exit_label_ = new views::Label( |
| rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_EXIT_LABEL), |
| {rb.GetFontListWithDelta(8, gfx::Font::FontStyle::NORMAL, |
| gfx::Font::Weight::NORMAL)}); |
| exit_label_->SetBounds((display_.bounds().width() - kExitLabelWidth) / 2, |
| display_.bounds().height() * 3.f / 4, kExitLabelWidth, |
| kExitLabelHeight); |
| exit_label_->SetAutoColorReadabilityEnabled(false); |
| exit_label_->SetEnabledColor(kExitLabelColor); |
| exit_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| exit_label_->SetSubpixelRenderingEnabled(false); |
| exit_label_->SetVisible(false); |
| |
| AddChildView(exit_label_); |
| |
| // If this is not the screen that is being calibrated, then this is all we |
| // need to display. |
| if (!is_primary_view_) |
| return; |
| |
| // Initialize the touch point view that contains the animated circle that the |
| // user needs to tap. |
| const int kTouchPointViewHeight = kThrobberCircleViewWidth + kTapLabelHeight; |
| const int kThrobberCircleViewHorizontalOffset = |
| (kTapLabelWidth - kThrobberCircleViewWidth) / 2; |
| |
| throbber_circle_ = |
| new CircularThrobberView(kThrobberCircleViewWidth, kInnerCircleColor, |
| kOuterCircleColor, kCircleAnimationDurationMs); |
| throbber_circle_->SetPosition( |
| gfx::Point(kThrobberCircleViewHorizontalOffset, 0)); |
| |
| // Initialize the tap label. |
| tap_label_ = new views::Label( |
| rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_TAP_HERE_LABEL), |
| {rb.GetFontListWithDelta(6, gfx::Font::FontStyle::NORMAL, |
| gfx::Font::Weight::NORMAL)}); |
| tap_label_->SetBounds(0, kThrobberCircleViewWidth, kTapLabelWidth, |
| kTapLabelHeight); |
| tap_label_->SetEnabledColor(kTapHereLabelColor); |
| tap_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER); |
| tap_label_->SetAutoColorReadabilityEnabled(false); |
| tap_label_->SetSubpixelRenderingEnabled(false); |
| tap_label_->SetVisible(false); |
| |
| touch_point_view_ = new views::View; |
| touch_point_view_->SetBounds(kTouchPointViewOffset, kTouchPointViewOffset, |
| kTapLabelWidth, kTouchPointViewHeight); |
| touch_point_view_->SetVisible(false); |
| touch_point_view_->SetPaintToLayer(); |
| touch_point_view_->layer()->SetFillsBoundsOpaquely(false); |
| touch_point_view_->layer()->GetAnimator()->AddObserver(this); |
| touch_point_view_->SetBackground( |
| views::CreateSolidBackground(SK_ColorTRANSPARENT)); |
| |
| touch_point_view_->AddChildView(throbber_circle_); |
| touch_point_view_->AddChildView(tap_label_); |
| |
| AddChildView(touch_point_view_); |
| |
| // Initialize the Hint Box view. |
| base::string16 hint_label_text = |
| rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_LABEL_TEXT); |
| base::string16 hint_sublabel_text = |
| rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_SUBLABEL_TEXT); |
| |
| int tpv_width = touch_point_view_->width(); |
| |
| gfx::Size size(kHintBoxWidth, kHintBoxHeight); |
| |
| gfx::Point position(touch_point_view_->x() + tpv_width * 1.2f, |
| touch_point_view_->y() + |
| (kThrobberCircleViewWidth / 2.f) - |
| (size.height() / 2.f)); |
| |
| HintBox* hint_box = |
| new HintBox(gfx::Rect(position, size), kHintRectBorderRadius); |
| hint_box->SetVisible(false); |
| hint_box->SetLabel(hint_label_text, kHintLabelTextColor); |
| hint_box->SetSubLabel(hint_sublabel_text, kHintSublabelTextColor); |
| hint_box_view_ = hint_box; |
| |
| AddChildView(hint_box_view_); |
| |
| // Initialize the animated hint box throbber view. |
| TouchTargetThrobberView* target_view = new TouchTargetThrobberView( |
| gfx::Rect((hint_box->width() - kTouchTargetWidth) / 2, |
| hint_box->height() * kTouchTargetVerticalOffsetFactor, |
| kTouchTargetWidth, kTouchTargetHeight), |
| kTouchTargetInnerCircleColor, kTouchTargetOuterCircleColor, |
| kHandIconColor, kCircleAnimationDurationMs); |
| target_view->SetVisible(true); |
| |
| hint_box_view_->AddChildView(target_view); |
| |
| // Initialize the view that contains the calibration complete message which |
| // will be displayed at the end. |
| base::string16 finish_msg_text = |
| rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_FINISH_LABEL); |
| |
| gfx::Rect msg_view_bounds( |
| (display_.bounds().width() - kCompleteMessageViewWidth) / 2, |
| display_.bounds().height() / 3, kCompleteMessageViewWidth, |
| kCompleteMessageViewHeight); |
| completion_message_view_ = |
| new CompletionMessageView(msg_view_bounds, finish_msg_text); |
| completion_message_view_->SetVisible(false); |
| completion_message_view_->SetPaintToLayer(); |
| completion_message_view_->layer()->SetFillsBoundsOpaquely(false); |
| completion_message_view_->layer()->GetAnimator()->AddObserver(this); |
| completion_message_view_->SetBackground( |
| views::CreateSolidBackground(SK_ColorTRANSPARENT)); |
| |
| AddChildView(completion_message_view_); |
| } |
| |
| void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) { |
| OnPaintBackground(canvas); |
| } |
| |
| void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) { |
| float opacity; |
| |
| // If current state is a fade in or fade out state then update opacity |
| // based on how far the animation has progressed. |
| if (animator_ && (state_ == TouchCalibratorView::BACKGROUND_FADING_OUT || |
| state_ == TouchCalibratorView::BACKGROUND_FADING_IN)) { |
| opacity = static_cast<float>(animator_->CurrentValueBetween( |
| start_opacity_value_, end_opacity_value_)); |
| } else { |
| opacity = state_ == BACKGROUND_FADING_OUT ? 0.f : kBackgroundFinalOpacity; |
| } |
| |
| flags_.setColor(SkColorSetA(SK_ColorBLACK, |
| std::numeric_limits<uint8_t>::max() * opacity)); |
| canvas->DrawRect(background_rect_, flags_); |
| } |
| |
| void TouchCalibratorView::AnimationProgressed(const gfx::Animation* animation) { |
| if (!is_primary_view_) { |
| SchedulePaint(); |
| return; |
| } |
| } |
| |
| void TouchCalibratorView::AnimationCanceled(const gfx::Animation* animation) { |
| AnimationEnded(animation); |
| } |
| |
| void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) { |
| switch (state_) { |
| case BACKGROUND_FADING_IN: |
| exit_label_->SetVisible(true); |
| state_ = is_primary_view_ ? DISPLAY_POINT_1 : CALIBRATION_COMPLETE; |
| if (is_primary_view_) { |
| touch_point_view_->SetVisible(true); |
| hint_box_view_->SetVisible(true); |
| } |
| break; |
| case BACKGROUND_FADING_OUT: |
| exit_label_->SetVisible(false); |
| if (is_primary_view_) |
| completion_message_view_->SetVisible(false); |
| widget_->Hide(); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void TouchCalibratorView::OnLayerAnimationStarted( |
| ui::LayerAnimationSequence* sequence) {} |
| |
| void TouchCalibratorView::OnLayerAnimationEnded( |
| ui::LayerAnimationSequence* sequence) { |
| switch (state_) { |
| case ANIMATING_1_TO_2: |
| state_ = DISPLAY_POINT_2; |
| tap_label_->SetVisible(true); |
| break; |
| case ANIMATING_2_TO_3: |
| state_ = DISPLAY_POINT_3; |
| break; |
| case ANIMATING_3_TO_4: |
| state_ = DISPLAY_POINT_4; |
| break; |
| case ANIMATING_FINAL_MESSAGE: |
| state_ = CALIBRATION_COMPLETE; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void TouchCalibratorView::OnLayerAnimationAborted( |
| ui::LayerAnimationSequence* sequence) { |
| OnLayerAnimationEnded(sequence); |
| } |
| |
| void TouchCalibratorView::OnLayerAnimationScheduled( |
| ui::LayerAnimationSequence* sequence) {} |
| |
| void TouchCalibratorView::AdvanceToNextState() { |
| // Stop any previous animations and skip them to the end. |
| SkipCurrentAnimation(); |
| |
| switch (state_) { |
| case UNKNOWN: |
| case BACKGROUND_FADING_IN: |
| state_ = BACKGROUND_FADING_IN; |
| start_opacity_value_ = 0.f; |
| end_opacity_value_ = kBackgroundFinalOpacity; |
| |
| flags_.setStyle(cc::PaintFlags::kFill_Style); |
| animator_->SetDuration(kFadeDuration); |
| animator_->Start(); |
| return; |
| case DISPLAY_POINT_1: |
| state_ = ANIMATING_1_TO_2; |
| |
| // The touch point has to be animated from the top left corner of the |
| // screen to the top right corner. |
| AnimateLayerToPosition( |
| touch_point_view_, kPointMoveDuration, |
| gfx::Point(display_.bounds().width() - kTouchPointViewOffset - |
| touch_point_view_->width(), |
| touch_point_view_->y())); |
| hint_box_view_->SetVisible(false); |
| return; |
| case DISPLAY_POINT_2: |
| state_ = ANIMATING_2_TO_3; |
| |
| // The touch point has to be animated from the top right corner of the |
| // screen to the bottom left corner. |
| AnimateLayerToPosition( |
| touch_point_view_, kPointMoveDurationLong, |
| gfx::Point(kTouchPointViewOffset, display_.bounds().height() - |
| kTouchPointViewOffset - |
| touch_point_view_->height())); |
| return; |
| case DISPLAY_POINT_3: |
| state_ = ANIMATING_3_TO_4; |
| |
| // The touch point has to be animated from the bottom left corner of the |
| // screen to the bottom right corner. |
| AnimateLayerToPosition( |
| touch_point_view_, kPointMoveDuration, |
| gfx::Point(display_.bounds().width() - kTouchPointViewOffset - |
| touch_point_view_->width(), |
| touch_point_view_->y())); |
| return; |
| case DISPLAY_POINT_4: |
| state_ = ANIMATING_FINAL_MESSAGE; |
| completion_message_view_->layer()->SetOpacity(0.f); |
| completion_message_view_->SetVisible(true); |
| |
| touch_point_view_->SetVisible(false); |
| |
| AnimateLayerToPosition(completion_message_view_, |
| kFinalMessageTransitionDuration, |
| gfx::Point(completion_message_view_->x(), |
| display_.bounds().height() / 2)); |
| return; |
| case CALIBRATION_COMPLETE: |
| state_ = BACKGROUND_FADING_OUT; |
| if (is_primary_view_) { |
| // In case of primary view, we also need to fade out the calibration |
| // complete message view. |
| AnimateLayerToPosition( |
| completion_message_view_, kFadeDuration, |
| gfx::Point(completion_message_view_->x(), |
| completion_message_view_->y() + |
| 2 * completion_message_view_->height()), |
| 0.f); |
| } |
| |
| start_opacity_value_ = kBackgroundFinalOpacity; |
| end_opacity_value_ = 0.f; |
| |
| flags_.setStyle(cc::PaintFlags::kFill_Style); |
| animator_->SetDuration(kFadeDuration); |
| animator_->Start(); |
| return; |
| default: |
| return; |
| } |
| } |
| |
| bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) { |
| DCHECK(location); |
| if (!is_primary_view_) |
| return false; |
| |
| if (state_ != DISPLAY_POINT_1 && state_ != DISPLAY_POINT_2 && |
| state_ != DISPLAY_POINT_3 && state_ != DISPLAY_POINT_4) { |
| return false; |
| } |
| |
| if (!touch_point_view_ || !throbber_circle_) |
| return false; |
| // TODO(malaykeshav): Can use views::ConvertPointToScreen() |
| location->SetPoint(touch_point_view_->x() + touch_point_view_->width() / 2.f, |
| touch_point_view_->y() + touch_point_view_->width() / 2.f); |
| return true; |
| } |
| |
| void TouchCalibratorView::SkipToFinalState() { |
| state_ = CALIBRATION_COMPLETE; |
| |
| exit_label_->SetVisible(false); |
| |
| if (is_primary_view_) { |
| touch_point_view_->SetVisible(false); |
| hint_box_view_->SetVisible(false); |
| } |
| |
| AdvanceToNextState(); |
| } |
| |
| void TouchCalibratorView::SkipCurrentAnimation() { |
| if (animator_->is_animating()) |
| animator_->End(); |
| if (touch_point_view_ && |
| touch_point_view_->layer()->GetAnimator()->is_animating()) { |
| touch_point_view_->layer()->GetAnimator()->StopAnimating(); |
| } |
| } |
| |
| } // namespace ash |