• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 #include "ash/sticky_keys/sticky_keys_overlay.h"
6 
7 #include "ash/shell.h"
8 #include "ash/shell_window_ids.h"
9 #include "ash/sticky_keys/sticky_keys_controller.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "grit/ash_strings.h"
13 #include "ui/base/l10n/l10n_util.h"
14 #include "ui/base/resource/resource_bundle.h"
15 #include "ui/compositor/scoped_layer_animation_settings.h"
16 #include "ui/gfx/canvas.h"
17 #include "ui/gfx/font_list.h"
18 #include "ui/views/border.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/layout/box_layout.h"
21 #include "ui/views/view.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/views/widget/widget_delegate.h"
24 
25 namespace ash {
26 
27 namespace {
28 
29 // Horizontal offset of the overlay from the top left of the screen.
30 const int kHorizontalOverlayOffset = 18;
31 
32 // Vertical offset of the overlay from the top left of the screen.
33 const int kVerticalOverlayOffset = 18;
34 
35 // Font style used for modifier key labels.
36 const ui::ResourceBundle::FontStyle kKeyLabelFontStyle =
37     ui::ResourceBundle::LargeFont;
38 
39 // Duration of slide animation when overlay is shown or hidden.
40 const int kSlideAnimationDurationMs = 100;
41 
42 }
43 
44 ///////////////////////////////////////////////////////////////////////////////
45 //  StickyKeyOverlayLabel
46 class StickyKeyOverlayLabel : public views::Label {
47  public:
48   explicit StickyKeyOverlayLabel(const std::string& key_name);
49 
50   virtual ~StickyKeyOverlayLabel();
51 
state() const52   StickyKeyState state() const { return state_; }
53 
54   void SetKeyState(StickyKeyState state);
55 
56  private:
57   StickyKeyState state_;
58 
59   DISALLOW_COPY_AND_ASSIGN(StickyKeyOverlayLabel);
60 };
61 
StickyKeyOverlayLabel(const std::string & key_name)62 StickyKeyOverlayLabel::StickyKeyOverlayLabel(const std::string& key_name)
63     : state_(STICKY_KEY_STATE_DISABLED) {
64   ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
65 
66   SetText(base::UTF8ToUTF16(key_name));
67   SetHorizontalAlignment(gfx::ALIGN_LEFT);
68   SetFontList(rb->GetFontList(kKeyLabelFontStyle));
69   SetAutoColorReadabilityEnabled(false);
70   SetFocusable(false);
71   SetEnabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
72   SetDisabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
73   set_subpixel_rendering_enabled(false);
74 }
75 
~StickyKeyOverlayLabel()76 StickyKeyOverlayLabel::~StickyKeyOverlayLabel() {
77 }
78 
SetKeyState(StickyKeyState state)79 void StickyKeyOverlayLabel::SetKeyState(StickyKeyState state) {
80   state_ = state;
81   SkColor label_color;
82   int style;
83   switch (state) {
84     case STICKY_KEY_STATE_ENABLED:
85       style = gfx::Font::NORMAL;
86       label_color = SkColorSetA(enabled_color(), 0xFF);
87       break;
88     case STICKY_KEY_STATE_LOCKED:
89       style = gfx::Font::UNDERLINE;
90       label_color = SkColorSetA(enabled_color(), 0xFF);
91       break;
92     default:
93       style = gfx::Font::NORMAL;
94       label_color = SkColorSetA(enabled_color(), 0x80);
95   }
96 
97   SetEnabledColor(label_color);
98   SetDisabledColor(label_color);
99   SetFontList(font_list().DeriveWithStyle(style));
100 }
101 
102 ///////////////////////////////////////////////////////////////////////////////
103 //  StickyKeysOverlayView
104 class StickyKeysOverlayView : public views::WidgetDelegateView {
105  public:
106   StickyKeysOverlayView();
107 
108   virtual ~StickyKeysOverlayView();
109 
110   // views::WidgetDelegateView overrides:
111   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
112 
113   void SetKeyState(ui::EventFlags modifier, StickyKeyState state);
114 
115   StickyKeyState GetKeyState(ui::EventFlags modifier);
116 
117   void SetModifierVisible(ui::EventFlags modifier, bool visible);
118   bool GetModifierVisible(ui::EventFlags modifier);
119 
120  private:
121   void AddKeyLabel(ui::EventFlags modifier, const std::string& key_label);
122 
123   typedef std::map<ui::EventFlags, StickyKeyOverlayLabel*> ModifierLabelMap;
124   ModifierLabelMap modifier_label_map_;
125 
126   DISALLOW_COPY_AND_ASSIGN(StickyKeysOverlayView);
127 };
128 
StickyKeysOverlayView()129 StickyKeysOverlayView::StickyKeysOverlayView() {
130   const gfx::Font& font =
131       ui::ResourceBundle::GetSharedInstance().GetFont(kKeyLabelFontStyle);
132   int font_size = font.GetFontSize();
133   int font_padding = font.GetHeight() - font.GetBaseline();
134 
135   // Text should have a margin of 0.5 times the font size on each side, so
136   // the spacing between two labels will be the same as the font size.
137   int horizontal_spacing = font_size / 2;
138   int vertical_spacing = font_size / 2 - font_padding;
139   int child_spacing = font_size - 2 * font_padding;
140 
141   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
142                                         horizontal_spacing,
143                                         vertical_spacing,
144                                         child_spacing));
145   AddKeyLabel(ui::EF_CONTROL_DOWN,
146               l10n_util::GetStringUTF8(IDS_ASH_CONTROL_KEY));
147   AddKeyLabel(ui::EF_ALT_DOWN,
148               l10n_util::GetStringUTF8(IDS_ASH_ALT_KEY));
149   AddKeyLabel(ui::EF_SHIFT_DOWN,
150               l10n_util::GetStringUTF8(IDS_ASH_SHIFT_KEY));
151   AddKeyLabel(ui::EF_ALTGR_DOWN,
152               l10n_util::GetStringUTF8(IDS_ASH_ALTGR_KEY));
153   AddKeyLabel(ui::EF_MOD3_DOWN,
154               l10n_util::GetStringUTF8(IDS_ASH_MOD3_KEY));
155 }
156 
~StickyKeysOverlayView()157 StickyKeysOverlayView::~StickyKeysOverlayView() {}
158 
OnPaint(gfx::Canvas * canvas)159 void StickyKeysOverlayView::OnPaint(gfx::Canvas* canvas) {
160   SkPaint paint;
161   paint.setStyle(SkPaint::kFill_Style);
162   paint.setColor(SkColorSetARGB(0xB3, 0x55, 0x55, 0x55));
163   canvas->DrawRoundRect(GetLocalBounds(), 2, paint);
164   views::WidgetDelegateView::OnPaint(canvas);
165 }
166 
SetKeyState(ui::EventFlags modifier,StickyKeyState state)167 void StickyKeysOverlayView::SetKeyState(ui::EventFlags modifier,
168                                         StickyKeyState state) {
169   ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
170   DCHECK(it != modifier_label_map_.end());
171   if (it != modifier_label_map_.end()) {
172     StickyKeyOverlayLabel* label = it->second;
173     label->SetKeyState(state);
174   }
175 }
176 
GetKeyState(ui::EventFlags modifier)177 StickyKeyState StickyKeysOverlayView::GetKeyState(ui::EventFlags modifier) {
178   ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
179   DCHECK(it != modifier_label_map_.end());
180   return it->second->state();
181 }
182 
SetModifierVisible(ui::EventFlags modifier,bool visible)183 void StickyKeysOverlayView::SetModifierVisible(ui::EventFlags modifier,
184                                                bool visible) {
185   ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
186   DCHECK(it != modifier_label_map_.end());
187   it->second->SetVisible(visible);
188 }
189 
GetModifierVisible(ui::EventFlags modifier)190 bool StickyKeysOverlayView::GetModifierVisible(ui::EventFlags modifier) {
191   ModifierLabelMap::iterator it = modifier_label_map_.find(modifier);
192   DCHECK(it != modifier_label_map_.end());
193   return it->second->visible();
194 }
195 
AddKeyLabel(ui::EventFlags modifier,const std::string & key_label)196 void StickyKeysOverlayView::AddKeyLabel(ui::EventFlags modifier,
197                                         const std::string& key_label) {
198   StickyKeyOverlayLabel* label = new StickyKeyOverlayLabel(key_label);
199   AddChildView(label);
200   modifier_label_map_[modifier] = label;
201 }
202 
203 ///////////////////////////////////////////////////////////////////////////////
204 //  StickyKeysOverlay
StickyKeysOverlay()205 StickyKeysOverlay::StickyKeysOverlay()
206     : is_visible_(false),
207       overlay_view_(new StickyKeysOverlayView),
208       widget_size_(overlay_view_->GetPreferredSize()) {
209   views::Widget::InitParams params;
210   params.type = views::Widget::InitParams::TYPE_POPUP;
211   params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
212   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
213   params.accept_events = false;
214   params.keep_on_top = true;
215   params.remove_standard_frame = true;
216   params.delegate = overlay_view_;
217   params.bounds = CalculateOverlayBounds();
218   params.parent = Shell::GetContainer(Shell::GetTargetRootWindow(),
219                                       kShellWindowId_OverlayContainer);
220   overlay_widget_.reset(new views::Widget);
221   overlay_widget_->Init(params);
222   overlay_widget_->SetVisibilityChangedAnimationsEnabled(false);
223   overlay_widget_->SetContentsView(overlay_view_);
224   overlay_widget_->GetNativeView()->SetName("StickyKeysOverlay");
225 }
226 
~StickyKeysOverlay()227 StickyKeysOverlay::~StickyKeysOverlay() {}
228 
Show(bool visible)229 void StickyKeysOverlay::Show(bool visible) {
230   if (is_visible_ == visible)
231     return;
232 
233   is_visible_ = visible;
234   if (is_visible_)
235     overlay_widget_->Show();
236   overlay_widget_->SetBounds(CalculateOverlayBounds());
237 
238   ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
239   animator->AddObserver(this);
240 
241   // Ensure transform is correct before beginning animation.
242   if (!animator->is_animating()) {
243     int sign = is_visible_ ? -1 : 1;
244     gfx::Transform transform;
245     transform.Translate(
246         sign * (widget_size_.width() + kHorizontalOverlayOffset), 0);
247     overlay_widget_->GetLayer()->SetTransform(transform);
248   }
249 
250   ui::ScopedLayerAnimationSettings settings(animator);
251   settings.SetPreemptionStrategy(
252       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
253   settings.SetTweenType(visible ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN);
254   settings.SetTransitionDuration(
255       base::TimeDelta::FromMilliseconds(kSlideAnimationDurationMs));
256 
257   overlay_widget_->GetLayer()->SetTransform(gfx::Transform());
258 }
259 
SetModifierVisible(ui::EventFlags modifier,bool visible)260 void StickyKeysOverlay::SetModifierVisible(ui::EventFlags modifier,
261                                            bool visible) {
262   overlay_view_->SetModifierVisible(modifier, visible);
263   widget_size_ = overlay_view_->GetPreferredSize();
264 }
265 
GetModifierVisible(ui::EventFlags modifier)266 bool StickyKeysOverlay::GetModifierVisible(ui::EventFlags modifier) {
267   return overlay_view_->GetModifierVisible(modifier);
268 }
269 
SetModifierKeyState(ui::EventFlags modifier,StickyKeyState state)270 void StickyKeysOverlay::SetModifierKeyState(ui::EventFlags modifier,
271                                             StickyKeyState state) {
272   overlay_view_->SetKeyState(modifier, state);
273 }
274 
GetModifierKeyState(ui::EventFlags modifier)275 StickyKeyState StickyKeysOverlay::GetModifierKeyState(
276     ui::EventFlags modifier) {
277   return overlay_view_->GetKeyState(modifier);
278 }
279 
CalculateOverlayBounds()280 gfx::Rect StickyKeysOverlay::CalculateOverlayBounds() {
281   int x = is_visible_ ? kHorizontalOverlayOffset : -widget_size_.width();
282   return gfx::Rect(gfx::Point(x, kVerticalOverlayOffset), widget_size_);
283 }
284 
OnLayerAnimationEnded(ui::LayerAnimationSequence * sequence)285 void StickyKeysOverlay::OnLayerAnimationEnded(
286     ui::LayerAnimationSequence* sequence) {
287   ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
288   if (animator)
289     animator->RemoveObserver(this);
290   if (!is_visible_)
291     overlay_widget_->Hide();
292 }
293 
OnLayerAnimationAborted(ui::LayerAnimationSequence * sequence)294 void StickyKeysOverlay::OnLayerAnimationAborted(
295     ui::LayerAnimationSequence* sequence) {
296   ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator();
297   if (animator)
298     animator->RemoveObserver(this);
299 }
300 
OnLayerAnimationScheduled(ui::LayerAnimationSequence * sequence)301 void StickyKeysOverlay::OnLayerAnimationScheduled(
302     ui::LayerAnimationSequence* sequence) {
303 }
304 
305 }  // namespace ash
306