Home | History | Annotate | Download | only in sticky_keys
      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