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.
5 #include "ash/sticky_keys/sticky_keys_overlay.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"
29 // Horizontal offset of the overlay from the top left of the screen.
30 const int kHorizontalOverlayOffset
= 18;
32 // Vertical offset of the overlay from the top left of the screen.
33 const int kVerticalOverlayOffset
= 18;
35 // Font style used for modifier key labels.
36 const ui::ResourceBundle::FontStyle kKeyLabelFontStyle
=
37 ui::ResourceBundle::LargeFont
;
39 // Duration of slide animation when overlay is shown or hidden.
40 const int kSlideAnimationDurationMs
= 100;
44 ///////////////////////////////////////////////////////////////////////////////
45 // StickyKeyOverlayLabel
46 class StickyKeyOverlayLabel
: public views::Label
{
48 explicit StickyKeyOverlayLabel(const std::string
& key_name
);
50 virtual ~StickyKeyOverlayLabel();
52 StickyKeyState
state() const { return state_
; }
54 void SetKeyState(StickyKeyState state
);
57 StickyKeyState state_
;
59 DISALLOW_COPY_AND_ASSIGN(StickyKeyOverlayLabel
);
62 StickyKeyOverlayLabel::StickyKeyOverlayLabel(const std::string
& key_name
)
63 : state_(STICKY_KEY_STATE_DISABLED
) {
64 ui::ResourceBundle
* rb
= &ui::ResourceBundle::GetSharedInstance();
66 SetText(base::UTF8ToUTF16(key_name
));
67 SetHorizontalAlignment(gfx::ALIGN_LEFT
);
68 SetFontList(rb
->GetFontList(kKeyLabelFontStyle
));
69 SetAutoColorReadabilityEnabled(false);
71 SetEnabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
72 SetDisabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
73 SetSubpixelRenderingEnabled(false);
76 StickyKeyOverlayLabel::~StickyKeyOverlayLabel() {
79 void StickyKeyOverlayLabel::SetKeyState(StickyKeyState state
) {
84 case STICKY_KEY_STATE_ENABLED
:
85 style
= gfx::Font::NORMAL
;
86 label_color
= SkColorSetA(enabled_color(), 0xFF);
88 case STICKY_KEY_STATE_LOCKED
:
89 style
= gfx::Font::UNDERLINE
;
90 label_color
= SkColorSetA(enabled_color(), 0xFF);
93 style
= gfx::Font::NORMAL
;
94 label_color
= SkColorSetA(enabled_color(), 0x80);
97 SetEnabledColor(label_color
);
98 SetDisabledColor(label_color
);
99 SetFontList(font_list().DeriveWithStyle(style
));
102 ///////////////////////////////////////////////////////////////////////////////
103 // StickyKeysOverlayView
104 class StickyKeysOverlayView
: public views::WidgetDelegateView
{
106 StickyKeysOverlayView();
108 virtual ~StickyKeysOverlayView();
110 // views::WidgetDelegateView overrides:
111 virtual void OnPaint(gfx::Canvas
* canvas
) OVERRIDE
;
113 void SetKeyState(ui::EventFlags modifier
, StickyKeyState state
);
115 StickyKeyState
GetKeyState(ui::EventFlags modifier
);
117 void SetModifierVisible(ui::EventFlags modifier
, bool visible
);
118 bool GetModifierVisible(ui::EventFlags modifier
);
121 void AddKeyLabel(ui::EventFlags modifier
, const std::string
& key_label
);
123 typedef std::map
<ui::EventFlags
, StickyKeyOverlayLabel
*> ModifierLabelMap
;
124 ModifierLabelMap modifier_label_map_
;
126 DISALLOW_COPY_AND_ASSIGN(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();
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
;
141 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
,
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
));
157 StickyKeysOverlayView::~StickyKeysOverlayView() {}
159 void StickyKeysOverlayView::OnPaint(gfx::Canvas
* canvas
) {
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
);
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
);
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();
183 void StickyKeysOverlayView::SetModifierVisible(ui::EventFlags modifier
,
185 ModifierLabelMap::iterator it
= modifier_label_map_
.find(modifier
);
186 DCHECK(it
!= modifier_label_map_
.end());
187 it
->second
->SetVisible(visible
);
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();
196 void StickyKeysOverlayView::AddKeyLabel(ui::EventFlags modifier
,
197 const std::string
& key_label
) {
198 StickyKeyOverlayLabel
* label
= new StickyKeyOverlayLabel(key_label
);
200 modifier_label_map_
[modifier
] = label
;
203 ///////////////////////////////////////////////////////////////////////////////
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");
227 StickyKeysOverlay::~StickyKeysOverlay() {}
229 void StickyKeysOverlay::Show(bool visible
) {
230 if (is_visible_
== visible
)
233 is_visible_
= visible
;
235 overlay_widget_
->Show();
236 overlay_widget_
->SetBounds(CalculateOverlayBounds());
238 ui::LayerAnimator
* animator
= overlay_widget_
->GetLayer()->GetAnimator();
239 animator
->AddObserver(this);
241 // Ensure transform is correct before beginning animation.
242 if (!animator
->is_animating()) {
243 int sign
= is_visible_
? -1 : 1;
244 gfx::Transform transform
;
246 sign
* (widget_size_
.width() + kHorizontalOverlayOffset
), 0);
247 overlay_widget_
->GetLayer()->SetTransform(transform
);
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
));
257 overlay_widget_
->GetLayer()->SetTransform(gfx::Transform());
260 void StickyKeysOverlay::SetModifierVisible(ui::EventFlags modifier
,
262 overlay_view_
->SetModifierVisible(modifier
, visible
);
263 widget_size_
= overlay_view_
->GetPreferredSize();
266 bool StickyKeysOverlay::GetModifierVisible(ui::EventFlags modifier
) {
267 return overlay_view_
->GetModifierVisible(modifier
);
270 void StickyKeysOverlay::SetModifierKeyState(ui::EventFlags modifier
,
271 StickyKeyState state
) {
272 overlay_view_
->SetKeyState(modifier
, state
);
275 StickyKeyState
StickyKeysOverlay::GetModifierKeyState(
276 ui::EventFlags modifier
) {
277 return overlay_view_
->GetKeyState(modifier
);
280 gfx::Rect
StickyKeysOverlay::CalculateOverlayBounds() {
281 int x
= is_visible_
? kHorizontalOverlayOffset
: -widget_size_
.width();
282 return gfx::Rect(gfx::Point(x
, kVerticalOverlayOffset
), widget_size_
);
285 void StickyKeysOverlay::OnLayerAnimationEnded(
286 ui::LayerAnimationSequence
* sequence
) {
287 ui::LayerAnimator
* animator
= overlay_widget_
->GetLayer()->GetAnimator();
289 animator
->RemoveObserver(this);
291 overlay_widget_
->Hide();
294 void StickyKeysOverlay::OnLayerAnimationAborted(
295 ui::LayerAnimationSequence
* sequence
) {
296 ui::LayerAnimator
* animator
= overlay_widget_
->GetLayer()->GetAnimator();
298 animator
->RemoveObserver(this);
301 void StickyKeysOverlay::OnLayerAnimationScheduled(
302 ui::LayerAnimationSequence
* sequence
) {