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 52 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 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 SetSubpixelRenderingEnabled(false); 74 } 75 76 StickyKeyOverlayLabel::~StickyKeyOverlayLabel() { 77 } 78 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 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 157 StickyKeysOverlayView::~StickyKeysOverlayView() {} 158 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 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 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 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 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 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 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 227 StickyKeysOverlay::~StickyKeysOverlay() {} 228 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 260 void StickyKeysOverlay::SetModifierVisible(ui::EventFlags modifier, 261 bool visible) { 262 overlay_view_->SetModifierVisible(modifier, visible); 263 widget_size_ = overlay_view_->GetPreferredSize(); 264 } 265 266 bool StickyKeysOverlay::GetModifierVisible(ui::EventFlags modifier) { 267 return overlay_view_->GetModifierVisible(modifier); 268 } 269 270 void StickyKeysOverlay::SetModifierKeyState(ui::EventFlags modifier, 271 StickyKeyState state) { 272 overlay_view_->SetKeyState(modifier, state); 273 } 274 275 StickyKeyState StickyKeysOverlay::GetModifierKeyState( 276 ui::EventFlags modifier) { 277 return overlay_view_->GetKeyState(modifier); 278 } 279 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 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 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 301 void StickyKeysOverlay::OnLayerAnimationScheduled( 302 ui::LayerAnimationSequence* sequence) { 303 } 304 305 } // namespace ash 306