| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/sticky_keys/sticky_keys_overlay.h" |
| |
| #include "ash/shell.h" |
| #include "ash/shell_window_ids.h" |
| #include "ash/sticky_keys/sticky_keys_controller.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "grit/ash_strings.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| #include "ui/gfx/canvas.h" |
| #include "ui/gfx/font_list.h" |
| #include "ui/views/border.h" |
| #include "ui/views/controls/label.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/view.h" |
| #include "ui/views/widget/widget.h" |
| #include "ui/views/widget/widget_delegate.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Horizontal offset of the overlay from the top left of the screen. |
| const int kHorizontalOverlayOffset = 18; |
| |
| // Vertical offset of the overlay from the top left of the screen. |
| const int kVerticalOverlayOffset = 18; |
| |
| // Font style used for modifier key labels. |
| const ui::ResourceBundle::FontStyle kKeyLabelFontStyle = |
| ui::ResourceBundle::LargeFont; |
| |
| // Duration of slide animation when overlay is shown or hidden. |
| const int kSlideAnimationDurationMs = 100; |
| |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // StickyKeyOverlayLabel |
| class StickyKeyOverlayLabel : public views::Label { |
| public: |
| explicit StickyKeyOverlayLabel(const std::string& key_name); |
| |
| ~StickyKeyOverlayLabel() override; |
| |
| StickyKeyState state() const { return state_; } |
| |
| void SetKeyState(StickyKeyState state); |
| |
| private: |
| StickyKeyState state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StickyKeyOverlayLabel); |
| }; |
| |
| StickyKeyOverlayLabel::StickyKeyOverlayLabel(const std::string& key_name) |
| : state_(STICKY_KEY_STATE_DISABLED) { |
| ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance(); |
| |
| SetText(base::UTF8ToUTF16(key_name)); |
| SetHorizontalAlignment(gfx::ALIGN_LEFT); |
| SetFontList(rb->GetFontList(kKeyLabelFontStyle)); |
| SetAutoColorReadabilityEnabled(false); |
| SetFocusable(false); |
| SetEnabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF)); |
| SetDisabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF)); |
| SetSubpixelRenderingEnabled(false); |
| } |
| |
| StickyKeyOverlayLabel::~StickyKeyOverlayLabel() { |
| } |
| |
| void StickyKeyOverlayLabel::SetKeyState(StickyKeyState state) { |
| state_ = state; |
| SkColor label_color; |
| int style; |
| switch (state) { |
| case STICKY_KEY_STATE_ENABLED: |
| style = gfx::Font::NORMAL; |
| label_color = SkColorSetA(enabled_color(), 0xFF); |
| break; |
| case STICKY_KEY_STATE_LOCKED: |
| style = gfx::Font::UNDERLINE; |
| label_color = SkColorSetA(enabled_color(), 0xFF); |
| break; |
| default: |
| style = gfx::Font::NORMAL; |
| label_color = SkColorSetA(enabled_color(), 0x80); |
| } |
| |
| SetEnabledColor(label_color); |
| SetDisabledColor(label_color); |
| SetFontList(font_list().DeriveWithStyle(style)); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // StickyKeysOverlayView |
| class StickyKeysOverlayView : public views::View { |
| public: |
| // This object is not owned by the views hiearchy or by the widget. The |
| // StickyKeysOverlay is the only class that should create and destroy this |
| // view. |
| StickyKeysOverlayView(); |
| |
| ~StickyKeysOverlayView() override; |
| |
| // views::View overrides: |
| void OnPaint(gfx::Canvas* canvas) override; |
| |
| void SetKeyState(ui::EventFlags modifier, StickyKeyState state); |
| |
| StickyKeyState GetKeyState(ui::EventFlags modifier); |
| |
| void SetModifierVisible(ui::EventFlags modifier, bool visible); |
| bool GetModifierVisible(ui::EventFlags modifier); |
| |
| private: |
| void AddKeyLabel(ui::EventFlags modifier, const std::string& key_label); |
| |
| typedef std::map<ui::EventFlags, StickyKeyOverlayLabel*> ModifierLabelMap; |
| ModifierLabelMap modifier_label_map_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StickyKeysOverlayView); |
| }; |
| |
| StickyKeysOverlayView::StickyKeysOverlayView() { |
| // The parent StickyKeysOverlay object owns this view. |
| set_owned_by_client(); |
| |
| const gfx::Font& font = |
| ui::ResourceBundle::GetSharedInstance().GetFont(kKeyLabelFontStyle); |
| int font_size = font.GetFontSize(); |
| int font_padding = font.GetHeight() - font.GetBaseline(); |
| |
| // Text should have a margin of 0.5 times the font size on each side, so |
| // the spacing between two labels will be the same as the font size. |
| int horizontal_spacing = font_size / 2; |
| int vertical_spacing = font_size / 2 - font_padding; |
| int child_spacing = font_size - 2 * font_padding; |
| |
| SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, |
| horizontal_spacing, |
| vertical_spacing, |
| child_spacing)); |
| AddKeyLabel(ui::EF_CONTROL_DOWN, |
| l10n_util::GetStringUTF8(IDS_ASH_CONTROL_KEY)); |
| AddKeyLabel(ui::EF_ALT_DOWN, |
| l10n_util::GetStringUTF8(IDS_ASH_ALT_KEY)); |
| AddKeyLabel(ui::EF_SHIFT_DOWN, |
| l10n_util::GetStringUTF8(IDS_ASH_SHIFT_KEY)); |
| AddKeyLabel(ui::EF_COMMAND_DOWN, |
| l10n_util::GetStringUTF8(IDS_ASH_SEARCH_KEY)); |
| AddKeyLabel(ui::EF_ALTGR_DOWN, |
| l10n_util::GetStringUTF8(IDS_ASH_ALTGR_KEY)); |
| AddKeyLabel(ui::EF_MOD3_DOWN, |
| l10n_util::GetStringUTF8(IDS_ASH_MOD3_KEY)); |
| } |
| |
| StickyKeysOverlayView::~StickyKeysOverlayView() {} |
| |
| void StickyKeysOverlayView::OnPaint(gfx::Canvas* canvas) { |
| SkPaint paint; |
| paint.setStyle(SkPaint::kFill_Style); |
| paint.setColor(SkColorSetARGB(0xB3, 0x55, 0x55, 0x55)); |
| canvas->DrawRoundRect(GetLocalBounds(), 2, paint); |
| views::View::OnPaint(canvas); |
| } |
| |
| void StickyKeysOverlayView::SetKeyState(ui::EventFlags modifier, |
| StickyKeyState state) { |
| ModifierLabelMap::iterator it = modifier_label_map_.find(modifier); |
| DCHECK(it != modifier_label_map_.end()); |
| if (it != modifier_label_map_.end()) { |
| StickyKeyOverlayLabel* label = it->second; |
| label->SetKeyState(state); |
| } |
| } |
| |
| StickyKeyState StickyKeysOverlayView::GetKeyState(ui::EventFlags modifier) { |
| ModifierLabelMap::iterator it = modifier_label_map_.find(modifier); |
| DCHECK(it != modifier_label_map_.end()); |
| return it->second->state(); |
| } |
| |
| void StickyKeysOverlayView::SetModifierVisible(ui::EventFlags modifier, |
| bool visible) { |
| ModifierLabelMap::iterator it = modifier_label_map_.find(modifier); |
| DCHECK(it != modifier_label_map_.end()); |
| it->second->SetVisible(visible); |
| } |
| |
| bool StickyKeysOverlayView::GetModifierVisible(ui::EventFlags modifier) { |
| ModifierLabelMap::iterator it = modifier_label_map_.find(modifier); |
| DCHECK(it != modifier_label_map_.end()); |
| return it->second->visible(); |
| } |
| |
| void StickyKeysOverlayView::AddKeyLabel(ui::EventFlags modifier, |
| const std::string& key_label) { |
| StickyKeyOverlayLabel* label = new StickyKeyOverlayLabel(key_label); |
| AddChildView(label); |
| modifier_label_map_[modifier] = label; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // StickyKeysOverlay |
| StickyKeysOverlay::StickyKeysOverlay() |
| : is_visible_(false), |
| overlay_view_(new StickyKeysOverlayView), |
| widget_size_(overlay_view_->GetPreferredSize()) { |
| views::Widget::InitParams params; |
| params.type = views::Widget::InitParams::TYPE_POPUP; |
| params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.accept_events = false; |
| params.keep_on_top = true; |
| params.remove_standard_frame = true; |
| params.bounds = CalculateOverlayBounds(); |
| params.parent = Shell::GetContainer(Shell::GetTargetRootWindow(), |
| kShellWindowId_OverlayContainer); |
| overlay_widget_.reset(new views::Widget); |
| overlay_widget_->Init(params); |
| overlay_widget_->SetVisibilityChangedAnimationsEnabled(false); |
| overlay_widget_->SetContentsView(overlay_view_.get()); |
| overlay_widget_->GetNativeView()->SetName("StickyKeysOverlay"); |
| } |
| |
| StickyKeysOverlay::~StickyKeysOverlay() {} |
| |
| void StickyKeysOverlay::Show(bool visible) { |
| if (is_visible_ == visible) |
| return; |
| |
| is_visible_ = visible; |
| if (is_visible_) |
| overlay_widget_->Show(); |
| overlay_widget_->SetBounds(CalculateOverlayBounds()); |
| |
| ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator(); |
| animator->AddObserver(this); |
| |
| // Ensure transform is correct before beginning animation. |
| if (!animator->is_animating()) { |
| int sign = is_visible_ ? -1 : 1; |
| gfx::Transform transform; |
| transform.Translate( |
| sign * (widget_size_.width() + kHorizontalOverlayOffset), 0); |
| overlay_widget_->GetLayer()->SetTransform(transform); |
| } |
| |
| ui::ScopedLayerAnimationSettings settings(animator); |
| settings.SetPreemptionStrategy( |
| ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
| settings.SetTweenType(visible ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN); |
| settings.SetTransitionDuration( |
| base::TimeDelta::FromMilliseconds(kSlideAnimationDurationMs)); |
| |
| overlay_widget_->GetLayer()->SetTransform(gfx::Transform()); |
| } |
| |
| void StickyKeysOverlay::SetModifierVisible(ui::EventFlags modifier, |
| bool visible) { |
| overlay_view_->SetModifierVisible(modifier, visible); |
| widget_size_ = overlay_view_->GetPreferredSize(); |
| } |
| |
| bool StickyKeysOverlay::GetModifierVisible(ui::EventFlags modifier) { |
| return overlay_view_->GetModifierVisible(modifier); |
| } |
| |
| void StickyKeysOverlay::SetModifierKeyState(ui::EventFlags modifier, |
| StickyKeyState state) { |
| overlay_view_->SetKeyState(modifier, state); |
| } |
| |
| StickyKeyState StickyKeysOverlay::GetModifierKeyState( |
| ui::EventFlags modifier) { |
| return overlay_view_->GetKeyState(modifier); |
| } |
| |
| views::Widget* StickyKeysOverlay::GetWidgetForTesting() { |
| return overlay_widget_.get(); |
| } |
| |
| gfx::Rect StickyKeysOverlay::CalculateOverlayBounds() { |
| int x = is_visible_ ? kHorizontalOverlayOffset : -widget_size_.width(); |
| return gfx::Rect(gfx::Point(x, kVerticalOverlayOffset), widget_size_); |
| } |
| |
| void StickyKeysOverlay::OnLayerAnimationEnded( |
| ui::LayerAnimationSequence* sequence) { |
| ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator(); |
| if (animator) |
| animator->RemoveObserver(this); |
| if (!is_visible_) |
| overlay_widget_->Hide(); |
| } |
| |
| void StickyKeysOverlay::OnLayerAnimationAborted( |
| ui::LayerAnimationSequence* sequence) { |
| ui::LayerAnimator* animator = overlay_widget_->GetLayer()->GetAnimator(); |
| if (animator) |
| animator->RemoveObserver(this); |
| } |
| |
| void StickyKeysOverlay::OnLayerAnimationScheduled( |
| ui::LayerAnimationSequence* sequence) { |
| } |
| |
| } // namespace ash |