touch_calibrator_view.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. // Copyright 2016 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/display/touch_calibrator_view.h"
  5. #include <memory>
  6. #include "ash/public/cpp/shell_window_ids.h"
  7. #include "ash/resources/vector_icons/vector_icons.h"
  8. #include "ash/shell.h"
  9. #include "base/memory/ptr_util.h"
  10. #include "ui/aura/window.h"
  11. #include "ui/base/resource/resource_bundle.h"
  12. #include "ui/compositor/layer.h"
  13. #include "ui/compositor/scoped_layer_animation_settings.h"
  14. #include "ui/gfx/animation/throb_animation.h"
  15. #include "ui/gfx/canvas.h"
  16. #include "ui/gfx/paint_vector_icon.h"
  17. #include "ui/strings/grit/ui_strings.h"
  18. #include "ui/views/background.h"
  19. #include "ui/views/bubble/bubble_border.h"
  20. #include "ui/views/controls/label.h"
  21. #include "ui/views/widget/widget.h"
  22. namespace ash {
  23. namespace {
  24. constexpr char kWidgetName[] = "TouchCalibratorOverlay";
  25. constexpr int kAnimationFrameRate = 100;
  26. constexpr auto kFadeDuration = base::Milliseconds(150);
  27. constexpr auto kPointMoveDuration = base::Milliseconds(400);
  28. constexpr auto kPointMoveDurationLong = base::Milliseconds(500);
  29. const SkColor kExitLabelColor = SkColorSetARGB(255, 138, 138, 138);
  30. constexpr int kExitLabelWidth = 300;
  31. constexpr int kExitLabelHeight = 20;
  32. const SkColor kTapHereLabelColor = SK_ColorWHITE;
  33. constexpr int kHintBoxWidth = 298;
  34. constexpr int kHintBoxHeight = 180;
  35. constexpr int kHintBoxLabelTextSize = 5;
  36. constexpr int kHintBoxSublabelTextSize = 3;
  37. constexpr int kThrobberCircleViewWidth = 64;
  38. constexpr float kThrobberCircleRadiusFactor = 3.f / 8.f;
  39. constexpr auto kFinalMessageTransitionDuration = base::Milliseconds(200);
  40. constexpr int kCompleteMessageViewWidth = 427;
  41. constexpr int kCompleteMessageViewHeight = kThrobberCircleViewWidth;
  42. constexpr int kCompleteMessageTextSize = 16;
  43. constexpr int kTouchPointViewOffset = 100;
  44. constexpr int kTapLabelHeight = 48;
  45. constexpr int kTapLabelWidth = 80;
  46. const SkColor kHintLabelTextColor = SK_ColorBLACK;
  47. const SkColor kHintSublabelTextColor = SkColorSetARGB(255, 161, 161, 161);
  48. const SkColor kInnerCircleColor = SK_ColorWHITE;
  49. const SkColor kOuterCircleColor = SkColorSetA(kInnerCircleColor, 255 * 0.2);
  50. constexpr auto kCircleAnimationDuration = base::Milliseconds(900);
  51. constexpr int kHintRectBorderRadius = 4;
  52. constexpr float kBackgroundFinalOpacity = 0.75f;
  53. constexpr int kTouchTargetWidth = 64;
  54. constexpr int kTouchTargetHeight = kTouchTargetWidth + kTouchTargetWidth / 2;
  55. constexpr float kTouchTargetVerticalOffsetFactor = 11.f / 24.f;
  56. const SkColor kTouchTargetInnerCircleColor = SkColorSetARGB(255, 66, 133, 244);
  57. const SkColor kTouchTargetOuterCircleColor =
  58. SkColorSetA(kTouchTargetInnerCircleColor, 255 * 0.2);
  59. const SkColor kHandIconColor = SkColorSetARGB(255, 201, 201, 201);
  60. constexpr float kHandIconHorizontalOffsetFactor = 7.f / 32.f;
  61. // Returns the initialization params for the widget that contains the touch
  62. // calibrator view.
  63. views::Widget::InitParams GetWidgetParams(aura::Window* root_window) {
  64. views::Widget::InitParams params;
  65. params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
  66. params.name = kWidgetName;
  67. params.z_order = ui::ZOrderLevel::kFloatingWindow;
  68. params.accept_events = true;
  69. params.activatable = views::Widget::InitParams::Activatable::kNo;
  70. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  71. params.parent =
  72. Shell::GetContainer(root_window, kShellWindowId_OverlayContainer);
  73. return params;
  74. }
  75. // Returns the size of bounding box required for |text| of given |font_list|.
  76. gfx::Size GetSizeForString(const std::u16string& text,
  77. const gfx::FontList& font_list) {
  78. int height = 0, width = 0;
  79. gfx::Canvas::SizeStringInt(text, font_list, &width, &height, 0, 0);
  80. return gfx::Size(width, height);
  81. }
  82. void AnimateLayerToPosition(views::View* view,
  83. base::TimeDelta duration,
  84. gfx::Point end_position,
  85. float opacity = 1.f) {
  86. ui::ScopedLayerAnimationSettings slide_settings(view->layer()->GetAnimator());
  87. slide_settings.SetPreemptionStrategy(
  88. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  89. slide_settings.SetTransitionDuration(duration);
  90. view->SetBoundsRect(gfx::Rect(end_position, view->size()));
  91. view->layer()->SetOpacity(opacity);
  92. }
  93. } // namespace
  94. // Creates a throbbing animated view with two concentric circles. The radius of
  95. // the inner circle is fixed while that of the outer circle oscillates between a
  96. // min and max radius. The animation takes |animation_duration| milliseconds
  97. // to complete. The center of these circles are at the center of the view
  98. // element.
  99. class CircularThrobberView : public views::View,
  100. public views::AnimationDelegateViews {
  101. public:
  102. CircularThrobberView(int width,
  103. const SkColor& inner_circle_color,
  104. const SkColor& outer_circle_color,
  105. base::TimeDelta animation_duration);
  106. CircularThrobberView(const CircularThrobberView&) = delete;
  107. CircularThrobberView& operator=(const CircularThrobberView&) = delete;
  108. ~CircularThrobberView() override;
  109. // views::View:
  110. void OnPaint(gfx::Canvas* canvas) override;
  111. // views::AnimationDelegateViews:
  112. void AnimationProgressed(const gfx::Animation* animation) override;
  113. private:
  114. // Radius of the inner circle.
  115. const int inner_radius_;
  116. // Current radius of the outer circle.
  117. int outer_radius_;
  118. // Minimum radius for outer animated circle.
  119. const int smallest_radius_animated_circle_;
  120. // Maximum radius for outer animated circle.
  121. const int largest_radius_animated_circle_;
  122. cc::PaintFlags inner_circle_flags_;
  123. cc::PaintFlags outer_circle_flags_;
  124. std::unique_ptr<gfx::ThrobAnimation> animation_;
  125. // Center of the concentric circles.
  126. const gfx::Point center_;
  127. };
  128. CircularThrobberView::CircularThrobberView(int width,
  129. const SkColor& inner_circle_color,
  130. const SkColor& outer_circle_color,
  131. base::TimeDelta animation_duration)
  132. : views::AnimationDelegateViews(this),
  133. inner_radius_(width / 4),
  134. outer_radius_(inner_radius_),
  135. smallest_radius_animated_circle_(width * kThrobberCircleRadiusFactor),
  136. largest_radius_animated_circle_(width / 2),
  137. center_(gfx::Point(width / 2, width / 2)) {
  138. SetSize(gfx::Size(width, width));
  139. inner_circle_flags_.setColor(inner_circle_color);
  140. inner_circle_flags_.setAntiAlias(true);
  141. inner_circle_flags_.setStyle(cc::PaintFlags::kFill_Style);
  142. outer_circle_flags_.setColor(outer_circle_color);
  143. outer_circle_flags_.setAntiAlias(true);
  144. outer_circle_flags_.setStyle(cc::PaintFlags::kFill_Style);
  145. animation_ = std::make_unique<gfx::ThrobAnimation>(this);
  146. animation_->SetThrobDuration(animation_duration);
  147. animation_->StartThrobbing(-1);
  148. SchedulePaint();
  149. }
  150. CircularThrobberView::~CircularThrobberView() = default;
  151. void CircularThrobberView::OnPaint(gfx::Canvas* canvas) {
  152. canvas->DrawCircle(center_, outer_radius_, outer_circle_flags_);
  153. canvas->DrawCircle(center_, inner_radius_, inner_circle_flags_);
  154. }
  155. void CircularThrobberView::AnimationProgressed(
  156. const gfx::Animation* animation) {
  157. if (animation != animation_.get())
  158. return;
  159. outer_radius_ = animation->CurrentValueBetween(
  160. smallest_radius_animated_circle_, largest_radius_animated_circle_);
  161. SchedulePaint();
  162. }
  163. class TouchTargetThrobberView : public CircularThrobberView {
  164. public:
  165. TouchTargetThrobberView(const gfx::Rect& bounds,
  166. const SkColor& inner_circle_color,
  167. const SkColor& outer_circle_color,
  168. const SkColor& hand_icon_color,
  169. base::TimeDelta animation_duration);
  170. TouchTargetThrobberView(const TouchTargetThrobberView&) = delete;
  171. TouchTargetThrobberView& operator=(const TouchTargetThrobberView&) = delete;
  172. ~TouchTargetThrobberView() override;
  173. // views::View:
  174. void OnPaint(gfx::Canvas* canvas) override;
  175. private:
  176. const int horizontal_offset_;
  177. const int icon_width_;
  178. gfx::ImageSkia hand_icon_;
  179. };
  180. TouchTargetThrobberView::TouchTargetThrobberView(
  181. const gfx::Rect& bounds,
  182. const SkColor& inner_circle_color,
  183. const SkColor& outer_circle_color,
  184. const SkColor& hand_icon_color,
  185. base::TimeDelta animation_duration)
  186. : CircularThrobberView(bounds.width(),
  187. inner_circle_color,
  188. outer_circle_color,
  189. animation_duration),
  190. horizontal_offset_(bounds.width() * kHandIconHorizontalOffsetFactor),
  191. icon_width_(bounds.width() * 0.5f) {
  192. SetBoundsRect(bounds);
  193. hand_icon_ = gfx::CreateVectorIcon(kTouchCalibrationHandIcon, kHandIconColor);
  194. }
  195. TouchTargetThrobberView::~TouchTargetThrobberView() = default;
  196. void TouchTargetThrobberView::OnPaint(gfx::Canvas* canvas) {
  197. CircularThrobberView::OnPaint(canvas);
  198. canvas->DrawImageInt(hand_icon_, horizontal_offset_, icon_width_);
  199. }
  200. // Circular _________________________________
  201. // Throbber | |
  202. // View | |
  203. // ___________ | |
  204. // | | | |
  205. // | | | |
  206. // | . | | Hint Box |
  207. // | | | |
  208. // |___________| | |
  209. // | |
  210. // | |
  211. // |_________________________________|
  212. //
  213. // This view is set next to the throbber circle view such that their centers
  214. // align. The hint box has a label text and a sublabel text to assist the
  215. // user by informing them about the next step in the calibration process.
  216. class HintBox : public views::View {
  217. public:
  218. HintBox(const gfx::Rect& bounds, int border_radius);
  219. HintBox(const HintBox&) = delete;
  220. HintBox& operator=(const HintBox&) = delete;
  221. ~HintBox() override;
  222. // views::View:
  223. void OnPaint(gfx::Canvas* canvas) override;
  224. void SetLabel(const std::u16string& text, const SkColor& color);
  225. void SetSubLabel(const std::u16string& text, const SkColor& color);
  226. private:
  227. void UpdateWidth(int updated_width);
  228. std::u16string label_text_;
  229. std::u16string sublabel_text_;
  230. SkColor label_color_;
  231. SkColor sublabel_color_;
  232. const int border_radius_;
  233. int base_border_;
  234. int arrow_width_;
  235. int horizontal_offset_;
  236. gfx::Rect rounded_rect_bounds_;
  237. gfx::FontList label_font_list_;
  238. gfx::FontList sublabel_font_list_;
  239. gfx::Rect label_text_bounds_;
  240. gfx::Rect sublabel_text_bounds_;
  241. cc::PaintFlags flags_;
  242. };
  243. HintBox::HintBox(const gfx::Rect& bounds, int border_radius)
  244. : border_radius_(border_radius) {
  245. auto border = std::make_unique<views::BubbleBorder>(
  246. base::i18n::IsRTL() ? views::BubbleBorder::RIGHT_CENTER
  247. : views::BubbleBorder::LEFT_CENTER,
  248. views::BubbleBorder::NO_SHADOW);
  249. border->SetColor(SK_ColorWHITE);
  250. SetBorder(std::move(border));
  251. arrow_width_ = (GetInsets().right() - GetInsets().left()) *
  252. (base::i18n::IsRTL() ? 1 : -1);
  253. // Border on all sides are the same except on the side of the arrow, in which
  254. // case the width of the arrow is additional.
  255. base_border_ = base::i18n::IsRTL() ? GetInsets().left() : GetInsets().right();
  256. SetBounds(bounds.x(), bounds.y() - base_border_,
  257. bounds.width() + 2 * base_border_ + arrow_width_,
  258. bounds.height() + 2 * base_border_);
  259. rounded_rect_bounds_ = GetContentsBounds();
  260. flags_.setColor(SK_ColorWHITE);
  261. flags_.setStyle(cc::PaintFlags::kFill_Style);
  262. flags_.setAntiAlias(true);
  263. horizontal_offset_ =
  264. arrow_width_ + base_border_ + rounded_rect_bounds_.width() * 0.08f;
  265. int top_offset = horizontal_offset_;
  266. int line_gap = rounded_rect_bounds_.height() * 0.018f;
  267. int label_height = rounded_rect_bounds_.height() * 0.11f;
  268. label_text_bounds_.SetRect(horizontal_offset_, top_offset, 0, label_height);
  269. top_offset += label_text_bounds_.height() + line_gap;
  270. sublabel_text_bounds_.SetRect(horizontal_offset_, top_offset, 0,
  271. label_height);
  272. }
  273. HintBox::~HintBox() = default;
  274. void HintBox::UpdateWidth(int updated_width) {
  275. SetSize(gfx::Size(updated_width + 2 * base_border_ + arrow_width_, height()));
  276. rounded_rect_bounds_ = GetContentsBounds();
  277. }
  278. void HintBox::SetLabel(const std::u16string& text, const SkColor& color) {
  279. label_text_ = text;
  280. label_color_ = color;
  281. label_font_list_ =
  282. ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
  283. kHintBoxLabelTextSize);
  284. // Adjust size of label bounds based on text and font.
  285. gfx::Size size = GetSizeForString(label_text_, label_font_list_);
  286. label_text_bounds_.set_size(
  287. gfx::Size(size.width(), label_text_bounds_.height()));
  288. // Check if the width of hint box needs to be updated.
  289. int minimum_expected_width =
  290. size.width() + 2 * horizontal_offset_ - arrow_width_;
  291. if (minimum_expected_width > rounded_rect_bounds_.width())
  292. UpdateWidth(minimum_expected_width);
  293. }
  294. void HintBox::SetSubLabel(const std::u16string& text, const SkColor& color) {
  295. sublabel_text_ = text;
  296. sublabel_color_ = color;
  297. sublabel_font_list_ =
  298. ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
  299. kHintBoxSublabelTextSize);
  300. // Adjust size of sublabel label bounds based on text and font.
  301. gfx::Size size = GetSizeForString(sublabel_text_, sublabel_font_list_);
  302. sublabel_text_bounds_.set_size(
  303. gfx::Size(size.width(), sublabel_text_bounds_.height()));
  304. // Check if the width of hint box needs to be updated.
  305. int minimum_expected_width =
  306. size.width() + 2 * horizontal_offset_ - arrow_width_;
  307. if (minimum_expected_width > rounded_rect_bounds_.width())
  308. UpdateWidth(minimum_expected_width);
  309. }
  310. void HintBox::OnPaint(gfx::Canvas* canvas) {
  311. views::View::OnPaint(canvas);
  312. canvas->DrawRoundRect(rounded_rect_bounds_, border_radius_, flags_);
  313. canvas->DrawStringRectWithFlags(label_text_, label_font_list_, label_color_,
  314. label_text_bounds_, gfx::Canvas::NO_ELLIPSIS);
  315. canvas->DrawStringRectWithFlags(sublabel_text_, sublabel_font_list_,
  316. sublabel_color_, sublabel_text_bounds_,
  317. gfx::Canvas::NO_ELLIPSIS);
  318. }
  319. class CompletionMessageView : public views::View {
  320. public:
  321. CompletionMessageView(const gfx::Rect& bounds, const std::u16string& message);
  322. CompletionMessageView(const CompletionMessageView&) = delete;
  323. CompletionMessageView& operator=(const CompletionMessageView&) = delete;
  324. ~CompletionMessageView() override;
  325. // views::View:
  326. void OnPaint(gfx::Canvas* canvas) override;
  327. private:
  328. const std::u16string message_;
  329. gfx::FontList font_list_;
  330. gfx::Rect text_bounds_;
  331. gfx::ImageSkia check_icon_;
  332. cc::PaintFlags flags_;
  333. };
  334. CompletionMessageView::CompletionMessageView(const gfx::Rect& bounds,
  335. const std::u16string& message)
  336. : message_(message) {
  337. SetBoundsRect(bounds);
  338. int x_offset = height() * 5.f / 4.f;
  339. text_bounds_.SetRect(x_offset, 0, width() - x_offset, height());
  340. font_list_ = ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
  341. kCompleteMessageTextSize);
  342. // crbug/676513 moves this file to src/ash which will require an ash icon
  343. // file.
  344. check_icon_ =
  345. gfx::CreateVectorIcon(kTouchCalibrationCompleteCheckIcon, SK_ColorWHITE);
  346. flags_.setColor(SK_ColorWHITE);
  347. flags_.setStyle(cc::PaintFlags::kFill_Style);
  348. flags_.setAntiAlias(true);
  349. }
  350. CompletionMessageView::~CompletionMessageView() = default;
  351. void CompletionMessageView::OnPaint(gfx::Canvas* canvas) {
  352. canvas->DrawImageInt(check_icon_, 0, 0);
  353. // TODO(malaykeshav): Work with elizabethchiu@ to get better UX for RTL.
  354. canvas->DrawStringRectWithFlags(
  355. message_, font_list_, flags_.getColor(), text_bounds_,
  356. gfx::Canvas::TEXT_ALIGN_LEFT | gfx::Canvas::NO_SUBPIXEL_RENDERING);
  357. }
  358. // static
  359. views::UniqueWidgetPtr TouchCalibratorView::Create(
  360. const display::Display& target_display,
  361. bool is_primary_view) {
  362. aura::Window* root = Shell::GetRootWindowForDisplayId(target_display.id());
  363. views::UniqueWidgetPtr widget(
  364. std::make_unique<views::Widget>(GetWidgetParams(root)));
  365. widget->SetContentsView(base::WrapUnique(
  366. new TouchCalibratorView(target_display, is_primary_view)));
  367. widget->SetBounds(target_display.bounds());
  368. widget->Show();
  369. return widget;
  370. }
  371. TouchCalibratorView::TouchCalibratorView(const display::Display& target_display,
  372. bool is_primary_view)
  373. : views::AnimationDelegateViews(this),
  374. display_(target_display),
  375. is_primary_view_(is_primary_view),
  376. animator_(std::make_unique<gfx::LinearAnimation>(kFadeDuration,
  377. kAnimationFrameRate,
  378. this)) {
  379. InitViewContents();
  380. AdvanceToNextState();
  381. }
  382. TouchCalibratorView::~TouchCalibratorView() {
  383. state_ = UNKNOWN;
  384. animator_->End();
  385. }
  386. void TouchCalibratorView::InitViewContents() {
  387. // Initialize the background rect.
  388. background_rect_ =
  389. gfx::RectF(0, 0, display_.bounds().width(), display_.bounds().height());
  390. ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  391. // Initialize exit label that informs the user how to exit the touch
  392. // calibration setup.
  393. exit_label_ = AddChildView(std::make_unique<views::Label>(
  394. rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_EXIT_LABEL),
  395. views::Label::CustomFont{rb.GetFontListWithDelta(8)}));
  396. exit_label_->SetBounds((display_.bounds().width() - kExitLabelWidth) / 2,
  397. display_.bounds().height() * 3.f / 4, kExitLabelWidth,
  398. kExitLabelHeight);
  399. exit_label_->SetAutoColorReadabilityEnabled(false);
  400. exit_label_->SetEnabledColor(kExitLabelColor);
  401. exit_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
  402. exit_label_->SetSubpixelRenderingEnabled(false);
  403. exit_label_->SetVisible(false);
  404. // If this is not the screen that is being calibrated, then this is all we
  405. // need to display.
  406. if (!is_primary_view_)
  407. return;
  408. // Initialize the touch point view that contains the animated circle that the
  409. // user needs to tap.
  410. const int kTouchPointViewHeight = kThrobberCircleViewWidth + kTapLabelHeight;
  411. const int kThrobberCircleViewHorizontalOffset =
  412. (kTapLabelWidth - kThrobberCircleViewWidth) / 2;
  413. touch_point_view_ = AddChildView(std::make_unique<views::View>());
  414. touch_point_view_->SetBounds(kTouchPointViewOffset, kTouchPointViewOffset,
  415. kTapLabelWidth, kTouchPointViewHeight);
  416. touch_point_view_->SetVisible(false);
  417. touch_point_view_->SetPaintToLayer();
  418. touch_point_view_->layer()->SetFillsBoundsOpaquely(false);
  419. touch_point_view_->layer()->GetAnimator()->AddObserver(this);
  420. touch_point_view_->SetBackground(
  421. views::CreateSolidBackground(SK_ColorTRANSPARENT));
  422. throbber_circle_ =
  423. touch_point_view_->AddChildView(std::make_unique<CircularThrobberView>(
  424. kThrobberCircleViewWidth, kInnerCircleColor, kOuterCircleColor,
  425. kCircleAnimationDuration));
  426. throbber_circle_->SetPosition(
  427. gfx::Point(kThrobberCircleViewHorizontalOffset, 0));
  428. // Initialize the tap label.
  429. tap_label_ = touch_point_view_->AddChildView(std::make_unique<views::Label>(
  430. rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_TAP_HERE_LABEL),
  431. views::Label::CustomFont{rb.GetFontListWithDelta(6)}));
  432. tap_label_->SetBounds(0, kThrobberCircleViewWidth, kTapLabelWidth,
  433. kTapLabelHeight);
  434. tap_label_->SetEnabledColor(kTapHereLabelColor);
  435. tap_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
  436. tap_label_->SetAutoColorReadabilityEnabled(false);
  437. tap_label_->SetSubpixelRenderingEnabled(false);
  438. tap_label_->SetVisible(false);
  439. // Initialize the Hint Box view.
  440. std::u16string hint_label_text =
  441. rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_LABEL_TEXT);
  442. std::u16string hint_sublabel_text =
  443. rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_HINT_SUBLABEL_TEXT);
  444. int tpv_width = touch_point_view_->width();
  445. gfx::Size size(kHintBoxWidth, kHintBoxHeight);
  446. gfx::Point position(touch_point_view_->x() + tpv_width * 1.2f,
  447. touch_point_view_->y() +
  448. (kThrobberCircleViewWidth / 2.f) -
  449. (size.height() / 2.f));
  450. hint_box_view_ = AddChildView(std::make_unique<HintBox>(
  451. gfx::Rect(position, size), kHintRectBorderRadius));
  452. hint_box_view_->SetVisible(false);
  453. hint_box_view_->SetLabel(hint_label_text, kHintLabelTextColor);
  454. hint_box_view_->SetSubLabel(hint_sublabel_text, kHintSublabelTextColor);
  455. // Initialize the animated hint box throbber view.
  456. auto* target_view =
  457. hint_box_view_->AddChildView(std::make_unique<TouchTargetThrobberView>(
  458. gfx::Rect((hint_box_view_->width() - kTouchTargetWidth) / 2,
  459. hint_box_view_->height() * kTouchTargetVerticalOffsetFactor,
  460. kTouchTargetWidth, kTouchTargetHeight),
  461. kTouchTargetInnerCircleColor, kTouchTargetOuterCircleColor,
  462. kHandIconColor, kCircleAnimationDuration));
  463. target_view->SetVisible(true);
  464. // Initialize the view that contains the calibration complete message which
  465. // will be displayed at the end.
  466. std::u16string finish_msg_text =
  467. rb.GetLocalizedString(IDS_DISPLAY_TOUCH_CALIBRATION_FINISH_LABEL);
  468. gfx::Rect msg_view_bounds(
  469. (display_.bounds().width() - kCompleteMessageViewWidth) / 2,
  470. display_.bounds().height() / 3, kCompleteMessageViewWidth,
  471. kCompleteMessageViewHeight);
  472. completion_message_view_ =
  473. AddChildView(std::make_unique<CompletionMessageView>(msg_view_bounds,
  474. finish_msg_text));
  475. completion_message_view_->SetVisible(false);
  476. completion_message_view_->SetPaintToLayer();
  477. completion_message_view_->layer()->SetFillsBoundsOpaquely(false);
  478. completion_message_view_->layer()->GetAnimator()->AddObserver(this);
  479. completion_message_view_->SetBackground(
  480. views::CreateSolidBackground(SK_ColorTRANSPARENT));
  481. }
  482. void TouchCalibratorView::OnPaint(gfx::Canvas* canvas) {
  483. OnPaintBackground(canvas);
  484. }
  485. void TouchCalibratorView::OnPaintBackground(gfx::Canvas* canvas) {
  486. float opacity;
  487. // If current state is a fade in or fade out state then update opacity
  488. // based on how far the animation has progressed.
  489. if (animator_ && (state_ == TouchCalibratorView::BACKGROUND_FADING_OUT ||
  490. state_ == TouchCalibratorView::BACKGROUND_FADING_IN)) {
  491. opacity = static_cast<float>(animator_->CurrentValueBetween(
  492. start_opacity_value_, end_opacity_value_));
  493. } else {
  494. opacity = state_ == BACKGROUND_FADING_OUT ? 0.0f : kBackgroundFinalOpacity;
  495. }
  496. flags_.setColor(SkColorSetA(SK_ColorBLACK,
  497. std::numeric_limits<uint8_t>::max() * opacity));
  498. canvas->DrawRect(background_rect_, flags_);
  499. }
  500. void TouchCalibratorView::AnimationProgressed(const gfx::Animation* animation) {
  501. if (!is_primary_view_) {
  502. SchedulePaint();
  503. return;
  504. }
  505. }
  506. void TouchCalibratorView::AnimationCanceled(const gfx::Animation* animation) {
  507. AnimationEnded(animation);
  508. }
  509. void TouchCalibratorView::AnimationEnded(const gfx::Animation* animation) {
  510. switch (state_) {
  511. case BACKGROUND_FADING_IN:
  512. exit_label_->SetVisible(true);
  513. state_ = is_primary_view_ ? DISPLAY_POINT_1 : CALIBRATION_COMPLETE;
  514. if (is_primary_view_) {
  515. touch_point_view_->SetVisible(true);
  516. hint_box_view_->SetVisible(true);
  517. }
  518. break;
  519. case BACKGROUND_FADING_OUT:
  520. exit_label_->SetVisible(false);
  521. if (is_primary_view_)
  522. completion_message_view_->SetVisible(false);
  523. GetWidget()->Hide();
  524. break;
  525. default:
  526. break;
  527. }
  528. }
  529. void TouchCalibratorView::OnLayerAnimationStarted(
  530. ui::LayerAnimationSequence* sequence) {}
  531. void TouchCalibratorView::OnLayerAnimationEnded(
  532. ui::LayerAnimationSequence* sequence) {
  533. switch (state_) {
  534. case ANIMATING_1_TO_2:
  535. state_ = DISPLAY_POINT_2;
  536. tap_label_->SetVisible(true);
  537. break;
  538. case ANIMATING_2_TO_3:
  539. state_ = DISPLAY_POINT_3;
  540. break;
  541. case ANIMATING_3_TO_4:
  542. state_ = DISPLAY_POINT_4;
  543. break;
  544. case ANIMATING_FINAL_MESSAGE:
  545. state_ = CALIBRATION_COMPLETE;
  546. break;
  547. default:
  548. break;
  549. }
  550. }
  551. void TouchCalibratorView::OnLayerAnimationAborted(
  552. ui::LayerAnimationSequence* sequence) {
  553. OnLayerAnimationEnded(sequence);
  554. }
  555. void TouchCalibratorView::OnLayerAnimationScheduled(
  556. ui::LayerAnimationSequence* sequence) {}
  557. void TouchCalibratorView::AdvanceToNextState() {
  558. // Stop any previous animations and skip them to the end.
  559. SkipCurrentAnimation();
  560. switch (state_) {
  561. case UNKNOWN:
  562. case BACKGROUND_FADING_IN:
  563. state_ = BACKGROUND_FADING_IN;
  564. start_opacity_value_ = 0.0f;
  565. end_opacity_value_ = kBackgroundFinalOpacity;
  566. flags_.setStyle(cc::PaintFlags::kFill_Style);
  567. animator_->SetDuration(kFadeDuration);
  568. animator_->Start();
  569. return;
  570. case DISPLAY_POINT_1:
  571. state_ = ANIMATING_1_TO_2;
  572. // The touch point has to be animated from the top left corner of the
  573. // screen to the top right corner.
  574. AnimateLayerToPosition(
  575. touch_point_view_, kPointMoveDuration,
  576. gfx::Point(display_.bounds().width() - kTouchPointViewOffset -
  577. touch_point_view_->width(),
  578. touch_point_view_->y()));
  579. hint_box_view_->SetVisible(false);
  580. return;
  581. case DISPLAY_POINT_2:
  582. state_ = ANIMATING_2_TO_3;
  583. // The touch point has to be animated from the top right corner of the
  584. // screen to the bottom left corner.
  585. AnimateLayerToPosition(
  586. touch_point_view_, kPointMoveDurationLong,
  587. gfx::Point(kTouchPointViewOffset, display_.bounds().height() -
  588. kTouchPointViewOffset -
  589. touch_point_view_->height()));
  590. return;
  591. case DISPLAY_POINT_3:
  592. state_ = ANIMATING_3_TO_4;
  593. // The touch point has to be animated from the bottom left corner of the
  594. // screen to the bottom right corner.
  595. AnimateLayerToPosition(
  596. touch_point_view_, kPointMoveDuration,
  597. gfx::Point(display_.bounds().width() - kTouchPointViewOffset -
  598. touch_point_view_->width(),
  599. touch_point_view_->y()));
  600. return;
  601. case DISPLAY_POINT_4:
  602. state_ = ANIMATING_FINAL_MESSAGE;
  603. completion_message_view_->layer()->SetOpacity(0.0f);
  604. completion_message_view_->SetVisible(true);
  605. touch_point_view_->SetVisible(false);
  606. AnimateLayerToPosition(completion_message_view_,
  607. kFinalMessageTransitionDuration,
  608. gfx::Point(completion_message_view_->x(),
  609. display_.bounds().height() / 2));
  610. return;
  611. case CALIBRATION_COMPLETE:
  612. state_ = BACKGROUND_FADING_OUT;
  613. if (is_primary_view_) {
  614. // In case of primary view, we also need to fade out the calibration
  615. // complete message view.
  616. AnimateLayerToPosition(
  617. completion_message_view_, kFadeDuration,
  618. gfx::Point(completion_message_view_->x(),
  619. completion_message_view_->y() +
  620. 2 * completion_message_view_->height()),
  621. 0.0f);
  622. }
  623. start_opacity_value_ = kBackgroundFinalOpacity;
  624. end_opacity_value_ = 0.0f;
  625. flags_.setStyle(cc::PaintFlags::kFill_Style);
  626. animator_->SetDuration(kFadeDuration);
  627. animator_->Start();
  628. return;
  629. default:
  630. return;
  631. }
  632. }
  633. bool TouchCalibratorView::GetDisplayPointLocation(gfx::Point* location) {
  634. DCHECK(location);
  635. if (!is_primary_view_)
  636. return false;
  637. if (state_ != DISPLAY_POINT_1 && state_ != DISPLAY_POINT_2 &&
  638. state_ != DISPLAY_POINT_3 && state_ != DISPLAY_POINT_4) {
  639. return false;
  640. }
  641. if (!touch_point_view_ || !throbber_circle_)
  642. return false;
  643. // TODO(malaykeshav): Can use views::ConvertPointToScreen()
  644. location->SetPoint(touch_point_view_->x() + touch_point_view_->width() / 2.f,
  645. touch_point_view_->y() + touch_point_view_->width() / 2.f);
  646. return true;
  647. }
  648. void TouchCalibratorView::SkipToFinalState() {
  649. state_ = CALIBRATION_COMPLETE;
  650. exit_label_->SetVisible(false);
  651. if (is_primary_view_) {
  652. touch_point_view_->SetVisible(false);
  653. hint_box_view_->SetVisible(false);
  654. }
  655. AdvanceToNextState();
  656. }
  657. void TouchCalibratorView::SkipCurrentAnimation() {
  658. if (animator_->is_animating())
  659. animator_->End();
  660. if (touch_point_view_ &&
  661. touch_point_view_->layer()->GetAnimator()->is_animating()) {
  662. touch_point_view_->layer()->GetAnimator()->StopAnimating();
  663. }
  664. }
  665. } // namespace ash