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 ~StickyKeyOverlayLabel() override
;
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::View
{
106 // This object is not owned by the views hiearchy or by the widget. The
107 // StickyKeysOverlay is the only class that should create and destroy this
109 StickyKeysOverlayView();
111 ~StickyKeysOverlayView() override
;
113 // views::View overrides:
114 void OnPaint(gfx::Canvas
* canvas
) override
;
116 void SetKeyState(ui::EventFlags modifier
, StickyKeyState state
);
118 StickyKeyState
GetKeyState(ui::EventFlags modifier
);
120 void SetModifierVisible(ui::EventFlags modifier
, bool visible
);
121 bool GetModifierVisible(ui::EventFlags modifier
);
124 void AddKeyLabel(ui::EventFlags modifier
, const std::string
& key_label
);
126 typedef std::map
<ui::EventFlags
, StickyKeyOverlayLabel
*> ModifierLabelMap
;
127 ModifierLabelMap modifier_label_map_
;
129 DISALLOW_COPY_AND_ASSIGN(StickyKeysOverlayView
);
132 StickyKeysOverlayView::StickyKeysOverlayView() {
133 // The parent StickyKeysOverlay object owns this view.
134 set_owned_by_client();
136 const gfx::Font
& font
=
137 ui::ResourceBundle::GetSharedInstance().GetFont(kKeyLabelFontStyle
);
138 int font_size
= font
.GetFontSize();
139 int font_padding
= font
.GetHeight() - font
.GetBaseline();
141 // Text should have a margin of 0.5 times the font size on each side, so
142 // the spacing between two labels will be the same as the font size.
143 int horizontal_spacing
= font_size
/ 2;
144 int vertical_spacing
= font_size
/ 2 - font_padding
;
145 int child_spacing
= font_size
- 2 * font_padding
;
147 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
,
151 AddKeyLabel(ui::EF_CONTROL_DOWN
,
152 l10n_util::GetStringUTF8(IDS_ASH_CONTROL_KEY
));
153 AddKeyLabel(ui::EF_ALT_DOWN
,
154 l10n_util::GetStringUTF8(IDS_ASH_ALT_KEY
));
155 AddKeyLabel(ui::EF_SHIFT_DOWN
,
156 l10n_util::GetStringUTF8(IDS_ASH_SHIFT_KEY
));
157 AddKeyLabel(ui::EF_COMMAND_DOWN
,
158 l10n_util::GetStringUTF8(IDS_ASH_SEARCH_KEY
));
159 AddKeyLabel(ui::EF_ALTGR_DOWN
,
160 l10n_util::GetStringUTF8(IDS_ASH_ALTGR_KEY
));
161 AddKeyLabel(ui::EF_MOD3_DOWN
,
162 l10n_util::GetStringUTF8(IDS_ASH_MOD3_KEY
));
165 StickyKeysOverlayView::~StickyKeysOverlayView() {}
167 void StickyKeysOverlayView::OnPaint(gfx::Canvas
* canvas
) {
169 paint
.setStyle(SkPaint::kFill_Style
);
170 paint
.setColor(SkColorSetARGB(0xB3, 0x55, 0x55, 0x55));
171 canvas
->DrawRoundRect(GetLocalBounds(), 2, paint
);
172 views::View::OnPaint(canvas
);
175 void StickyKeysOverlayView::SetKeyState(ui::EventFlags modifier
,
176 StickyKeyState state
) {
177 ModifierLabelMap::iterator it
= modifier_label_map_
.find(modifier
);
178 DCHECK(it
!= modifier_label_map_
.end());
179 if (it
!= modifier_label_map_
.end()) {
180 StickyKeyOverlayLabel
* label
= it
->second
;
181 label
->SetKeyState(state
);
185 StickyKeyState
StickyKeysOverlayView::GetKeyState(ui::EventFlags modifier
) {
186 ModifierLabelMap::iterator it
= modifier_label_map_
.find(modifier
);
187 DCHECK(it
!= modifier_label_map_
.end());
188 return it
->second
->state();
191 void StickyKeysOverlayView::SetModifierVisible(ui::EventFlags modifier
,
193 ModifierLabelMap::iterator it
= modifier_label_map_
.find(modifier
);
194 DCHECK(it
!= modifier_label_map_
.end());
195 it
->second
->SetVisible(visible
);
198 bool StickyKeysOverlayView::GetModifierVisible(ui::EventFlags modifier
) {
199 ModifierLabelMap::iterator it
= modifier_label_map_
.find(modifier
);
200 DCHECK(it
!= modifier_label_map_
.end());
201 return it
->second
->visible();
204 void StickyKeysOverlayView::AddKeyLabel(ui::EventFlags modifier
,
205 const std::string
& key_label
) {
206 StickyKeyOverlayLabel
* label
= new StickyKeyOverlayLabel(key_label
);
208 modifier_label_map_
[modifier
] = label
;
211 ///////////////////////////////////////////////////////////////////////////////
213 StickyKeysOverlay::StickyKeysOverlay()
214 : is_visible_(false),
215 overlay_view_(new StickyKeysOverlayView
),
216 widget_size_(overlay_view_
->GetPreferredSize()) {
217 views::Widget::InitParams params
;
218 params
.type
= views::Widget::InitParams::TYPE_POPUP
;
219 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
220 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
221 params
.accept_events
= false;
222 params
.keep_on_top
= true;
223 params
.remove_standard_frame
= true;
224 params
.bounds
= CalculateOverlayBounds();
225 params
.parent
= Shell::GetContainer(Shell::GetTargetRootWindow(),
226 kShellWindowId_OverlayContainer
);
227 overlay_widget_
.reset(new views::Widget
);
228 overlay_widget_
->Init(params
);
229 overlay_widget_
->SetVisibilityChangedAnimationsEnabled(false);
230 overlay_widget_
->SetContentsView(overlay_view_
.get());
231 overlay_widget_
->GetNativeView()->SetName("StickyKeysOverlay");
234 StickyKeysOverlay::~StickyKeysOverlay() {}
236 void StickyKeysOverlay::Show(bool visible
) {
237 if (is_visible_
== visible
)
240 is_visible_
= visible
;
242 overlay_widget_
->Show();
243 overlay_widget_
->SetBounds(CalculateOverlayBounds());
245 ui::LayerAnimator
* animator
= overlay_widget_
->GetLayer()->GetAnimator();
246 animator
->AddObserver(this);
248 // Ensure transform is correct before beginning animation.
249 if (!animator
->is_animating()) {
250 int sign
= is_visible_
? -1 : 1;
251 gfx::Transform transform
;
253 sign
* (widget_size_
.width() + kHorizontalOverlayOffset
), 0);
254 overlay_widget_
->GetLayer()->SetTransform(transform
);
257 ui::ScopedLayerAnimationSettings
settings(animator
);
258 settings
.SetPreemptionStrategy(
259 ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET
);
260 settings
.SetTweenType(visible
? gfx::Tween::EASE_OUT
: gfx::Tween::EASE_IN
);
261 settings
.SetTransitionDuration(
262 base::TimeDelta::FromMilliseconds(kSlideAnimationDurationMs
));
264 overlay_widget_
->GetLayer()->SetTransform(gfx::Transform());
267 void StickyKeysOverlay::SetModifierVisible(ui::EventFlags modifier
,
269 overlay_view_
->SetModifierVisible(modifier
, visible
);
270 widget_size_
= overlay_view_
->GetPreferredSize();
273 bool StickyKeysOverlay::GetModifierVisible(ui::EventFlags modifier
) {
274 return overlay_view_
->GetModifierVisible(modifier
);
277 void StickyKeysOverlay::SetModifierKeyState(ui::EventFlags modifier
,
278 StickyKeyState state
) {
279 overlay_view_
->SetKeyState(modifier
, state
);
282 StickyKeyState
StickyKeysOverlay::GetModifierKeyState(
283 ui::EventFlags modifier
) {
284 return overlay_view_
->GetKeyState(modifier
);
287 views::Widget
* StickyKeysOverlay::GetWidgetForTesting() {
288 return overlay_widget_
.get();
291 gfx::Rect
StickyKeysOverlay::CalculateOverlayBounds() {
292 int x
= is_visible_
? kHorizontalOverlayOffset
: -widget_size_
.width();
293 return gfx::Rect(gfx::Point(x
, kVerticalOverlayOffset
), widget_size_
);
296 void StickyKeysOverlay::OnLayerAnimationEnded(
297 ui::LayerAnimationSequence
* sequence
) {
298 ui::LayerAnimator
* animator
= overlay_widget_
->GetLayer()->GetAnimator();
300 animator
->RemoveObserver(this);
302 overlay_widget_
->Hide();
305 void StickyKeysOverlay::OnLayerAnimationAborted(
306 ui::LayerAnimationSequence
* sequence
) {
307 ui::LayerAnimator
* animator
= overlay_widget_
->GetLayer()->GetAnimator();
309 animator
->RemoveObserver(this);
312 void StickyKeysOverlay::OnLayerAnimationScheduled(
313 ui::LayerAnimationSequence
* sequence
) {