sticky_keys_overlay.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // Copyright 2014 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/accessibility/sticky_keys/sticky_keys_overlay.h"
  5. #include <memory>
  6. #include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
  7. #include "ash/public/cpp/shell_window_ids.h"
  8. #include "ash/shell.h"
  9. #include "ash/strings/grit/ash_strings.h"
  10. #include "ash/wm/work_area_insets.h"
  11. #include "base/strings/string_util.h"
  12. #include "base/strings/utf_string_conversions.h"
  13. #include "ui/base/l10n/l10n_util.h"
  14. #include "ui/base/resource/resource_bundle.h"
  15. #include "ui/compositor/layer.h"
  16. #include "ui/compositor/scoped_layer_animation_settings.h"
  17. #include "ui/gfx/canvas.h"
  18. #include "ui/gfx/font_list.h"
  19. #include "ui/gfx/geometry/insets.h"
  20. #include "ui/views/border.h"
  21. #include "ui/views/controls/label.h"
  22. #include "ui/views/layout/box_layout.h"
  23. #include "ui/views/view.h"
  24. #include "ui/views/widget/widget.h"
  25. #include "ui/views/widget/widget_delegate.h"
  26. namespace ash {
  27. namespace {
  28. // Horizontal offset of the overlay from the top left of the screen.
  29. const int kHorizontalOverlayOffset = 18;
  30. // Vertical offset of the overlay from the top left of the screen.
  31. const int kVerticalOverlayOffset = 18;
  32. // Font style used for modifier key labels.
  33. const ui::ResourceBundle::FontStyle kKeyLabelFontStyle =
  34. ui::ResourceBundle::LargeFont;
  35. // Duration of slide animation when overlay is shown or hidden.
  36. const int kSlideAnimationDurationMs = 100;
  37. ///////////////////////////////////////////////////////////////////////////////
  38. // StickyKeyOverlayLabel
  39. class StickyKeyOverlayLabel : public views::Label {
  40. public:
  41. explicit StickyKeyOverlayLabel(const std::string& key_name);
  42. StickyKeyOverlayLabel(const StickyKeyOverlayLabel&) = delete;
  43. StickyKeyOverlayLabel& operator=(const StickyKeyOverlayLabel&) = delete;
  44. ~StickyKeyOverlayLabel() override;
  45. StickyKeyState state() const { return state_; }
  46. void SetKeyState(StickyKeyState state);
  47. private:
  48. StickyKeyState state_;
  49. };
  50. StickyKeyOverlayLabel::StickyKeyOverlayLabel(const std::string& key_name)
  51. : state_(STICKY_KEY_STATE_DISABLED) {
  52. ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
  53. SetText(base::UTF8ToUTF16(key_name));
  54. SetHorizontalAlignment(gfx::ALIGN_LEFT);
  55. SetFontList(rb->GetFontList(kKeyLabelFontStyle));
  56. SetAutoColorReadabilityEnabled(false);
  57. SetEnabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
  58. SetSubpixelRenderingEnabled(false);
  59. }
  60. StickyKeyOverlayLabel::~StickyKeyOverlayLabel() = default;
  61. void StickyKeyOverlayLabel::SetKeyState(StickyKeyState state) {
  62. state_ = state;
  63. SkColor label_color;
  64. int style;
  65. switch (state) {
  66. case STICKY_KEY_STATE_ENABLED:
  67. style = gfx::Font::NORMAL;
  68. label_color = SkColorSetA(GetEnabledColor(), 0xFF);
  69. break;
  70. case STICKY_KEY_STATE_LOCKED:
  71. style = gfx::Font::UNDERLINE;
  72. label_color = SkColorSetA(GetEnabledColor(), 0xFF);
  73. break;
  74. default:
  75. style = gfx::Font::NORMAL;
  76. label_color = SkColorSetA(GetEnabledColor(), 0x80);
  77. }
  78. SetEnabledColor(label_color);
  79. SetFontList(font_list().DeriveWithStyle(style));
  80. }
  81. } // namespace
  82. ///////////////////////////////////////////////////////////////////////////////
  83. // StickyKeysOverlayView
  84. class StickyKeysOverlayView : public views::View {
  85. public:
  86. StickyKeysOverlayView();
  87. StickyKeysOverlayView(const StickyKeysOverlayView&) = delete;
  88. StickyKeysOverlayView& operator=(const StickyKeysOverlayView&) = delete;
  89. ~StickyKeysOverlayView() override;
  90. // views::View overrides:
  91. void OnPaint(gfx::Canvas* canvas) override;
  92. void SetKeyState(ui::EventFlags modifier, StickyKeyState state);
  93. StickyKeyState GetKeyState(ui::EventFlags modifier);
  94. void SetModifierVisible(ui::EventFlags modifier, bool visible);
  95. bool GetModifierVisible(ui::EventFlags modifier);
  96. private:
  97. void AddKeyLabel(ui::EventFlags modifier, const std::string& key_label);
  98. typedef std::map<ui::EventFlags, StickyKeyOverlayLabel*> ModifierLabelMap;
  99. ModifierLabelMap modifier_label_map_;
  100. };
  101. StickyKeysOverlayView::StickyKeysOverlayView() {
  102. const gfx::Font& font =
  103. ui::ResourceBundle::GetSharedInstance().GetFont(kKeyLabelFontStyle);
  104. int font_size = font.GetFontSize();
  105. int font_padding = font.GetHeight() - font.GetBaseline();
  106. // Text should have a margin of 0.5 times the font size on each side, so
  107. // the spacing between two labels will be the same as the font size.
  108. int horizontal_spacing = font_size / 2;
  109. int vertical_spacing = font_size / 2 - font_padding;
  110. int child_spacing = font_size - 2 * font_padding;
  111. SetLayoutManager(std::make_unique<views::BoxLayout>(
  112. views::BoxLayout::Orientation::kVertical,
  113. gfx::Insets::VH(vertical_spacing, horizontal_spacing), child_spacing));
  114. AddKeyLabel(ui::EF_CONTROL_DOWN,
  115. l10n_util::GetStringUTF8(IDS_ASH_CONTROL_KEY));
  116. AddKeyLabel(ui::EF_ALT_DOWN, l10n_util::GetStringUTF8(IDS_ASH_ALT_KEY));
  117. AddKeyLabel(ui::EF_SHIFT_DOWN, l10n_util::GetStringUTF8(IDS_ASH_SHIFT_KEY));
  118. AddKeyLabel(ui::EF_COMMAND_DOWN,
  119. l10n_util::GetStringUTF8(IDS_ASH_SEARCH_KEY));
  120. AddKeyLabel(ui::EF_ALTGR_DOWN, l10n_util::GetStringUTF8(IDS_ASH_ALTGR_KEY));
  121. AddKeyLabel(ui::EF_MOD3_DOWN, l10n_util::GetStringUTF8(IDS_ASH_MOD3_KEY));
  122. }
  123. StickyKeysOverlayView::~StickyKeysOverlayView() = default;
  124. void StickyKeysOverlayView::OnPaint(gfx::Canvas* canvas) {
  125. cc::PaintFlags flags;
  126. flags.setStyle(cc::PaintFlags::kFill_Style);
  127. flags.setColor(SkColorSetARGB(0xB3, 0x55, 0x55, 0x55));
  128. canvas->DrawRoundRect(GetLocalBounds(), 2, flags);
  129. views::View::OnPaint(canvas);
  130. }
  131. void StickyKeysOverlayView::SetKeyState(ui::EventFlags modifier,
  132. StickyKeyState state) {
  133. ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
  134. DCHECK(it != modifier_label_map_.end());
  135. if (it != modifier_label_map_.end()) {
  136. StickyKeyOverlayLabel* label = it->second;
  137. label->SetKeyState(state);
  138. }
  139. }
  140. StickyKeyState StickyKeysOverlayView::GetKeyState(ui::EventFlags modifier) {
  141. ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
  142. DCHECK(it != modifier_label_map_.end());
  143. return it->second->state();
  144. }
  145. void StickyKeysOverlayView::SetModifierVisible(ui::EventFlags modifier,
  146. bool visible) {
  147. ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
  148. DCHECK(it != modifier_label_map_.end());
  149. it->second->SetVisible(visible);
  150. }
  151. bool StickyKeysOverlayView::GetModifierVisible(ui::EventFlags modifier) {
  152. ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
  153. DCHECK(it != modifier_label_map_.end());
  154. return it->second->GetVisible();
  155. }
  156. void StickyKeysOverlayView::AddKeyLabel(ui::EventFlags modifier,
  157. const std::string& key_label) {
  158. StickyKeyOverlayLabel* label = new StickyKeyOverlayLabel(key_label);
  159. AddChildView(label);
  160. modifier_label_map_[modifier] = label;
  161. }
  162. ///////////////////////////////////////////////////////////////////////////////
  163. // StickyKeysOverlay
  164. StickyKeysOverlay::StickyKeysOverlay() {
  165. auto overlay_view = std::make_unique<StickyKeysOverlayView>();
  166. widget_size_ = overlay_view->GetPreferredSize();
  167. views::Widget::InitParams params;
  168. params.type = views::Widget::InitParams::TYPE_POPUP;
  169. params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
  170. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  171. params.accept_events = false;
  172. params.z_order = ui::ZOrderLevel::kFloatingUIElement;
  173. params.bounds = CalculateOverlayBounds();
  174. params.parent = Shell::GetContainer(Shell::GetRootWindowForNewWindows(),
  175. kShellWindowId_OverlayContainer);
  176. overlay_widget_ = std::make_unique<views::Widget>();
  177. overlay_widget_->Init(std::move(params));
  178. overlay_widget_->SetVisibilityChangedAnimationsEnabled(false);
  179. overlay_view_ = overlay_widget_->SetContentsView(std::move(overlay_view));
  180. overlay_widget_->GetNativeView()->SetName("StickyKeysOverlay");
  181. }
  182. StickyKeysOverlay::~StickyKeysOverlay() {
  183. // Remove ourself from the animator to avoid being re-entrantly called in
  184. // |overlay_widget_|'s destructor.
  185. StopObservingImplicitAnimations();
  186. }
  187. void StickyKeysOverlay::Show(bool visible) {
  188. if (is_visible_ == visible)
  189. return;
  190. is_visible_ = visible;
  191. if (is_visible_)
  192. overlay_widget_->Show();
  193. overlay_widget_->SetBounds(CalculateOverlayBounds());
  194. ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
  195. // Ensure transform is correct before beginning animation.
  196. if (!animator->is_animating()) {
  197. int sign = is_visible_ ? -1 : 1;
  198. gfx::Transform transform;
  199. transform.Translate(
  200. sign * (widget_size_.width() + kHorizontalOverlayOffset), 0);
  201. overlay_widget_->GetLayer()->SetTransform(transform);
  202. }
  203. ui::ScopedLayerAnimationSettings settings(animator);
  204. settings.AddObserver(this);
  205. settings.SetPreemptionStrategy(
  206. ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
  207. settings.SetTweenType(visible ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN);
  208. settings.SetTransitionDuration(base::Milliseconds(kSlideAnimationDurationMs));
  209. overlay_widget_->GetLayer()->SetTransform(gfx::Transform());
  210. }
  211. void StickyKeysOverlay::UpdateBoundsIfVisible() {
  212. if (!is_visible_)
  213. return;
  214. overlay_widget_->SetBounds(CalculateOverlayBounds());
  215. }
  216. void StickyKeysOverlay::SetModifierVisible(ui::EventFlags modifier,
  217. bool visible) {
  218. overlay_view_->SetModifierVisible(modifier, visible);
  219. widget_size_ = overlay_view_->GetPreferredSize();
  220. }
  221. bool StickyKeysOverlay::GetModifierVisible(ui::EventFlags modifier) {
  222. return overlay_view_->GetModifierVisible(modifier);
  223. }
  224. void StickyKeysOverlay::SetModifierKeyState(ui::EventFlags modifier,
  225. StickyKeyState state) {
  226. overlay_view_->SetKeyState(modifier, state);
  227. }
  228. StickyKeyState StickyKeysOverlay::GetModifierKeyState(ui::EventFlags modifier) {
  229. return overlay_view_->GetKeyState(modifier);
  230. }
  231. views::Widget* StickyKeysOverlay::GetWidgetForTesting() {
  232. return overlay_widget_.get();
  233. }
  234. gfx::Rect StickyKeysOverlay::CalculateOverlayBounds() {
  235. // Get work area to inset overlay (below Docked Magnifier or ChromeVox).
  236. gfx::Rect work_area = WorkAreaInsets::ForWindow(Shell::GetPrimaryRootWindow())
  237. ->user_work_area_bounds();
  238. int x = is_visible_ ? kHorizontalOverlayOffset : -widget_size_.width();
  239. return gfx::Rect(
  240. gfx::Point(work_area.x() + x, work_area.y() + kVerticalOverlayOffset),
  241. widget_size_);
  242. }
  243. void StickyKeysOverlay::OnImplicitAnimationsCompleted() {
  244. if (WasAnimationAbortedForProperty(ui::LayerAnimationElement::TRANSFORM))
  245. return;
  246. DCHECK(
  247. WasAnimationCompletedForProperty(ui::LayerAnimationElement::TRANSFORM));
  248. if (!is_visible_)
  249. overlay_widget_->Hide();
  250. }
  251. } // namespace ash