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