Commit 99b6ee45 authored by tengs@chromium.org's avatar tengs@chromium.org

Show overlay displaying the state of all sticky keys when it is enabled.

BUG=229042
TEST=manual, new unit tests

Review URL: https://codereview.chromium.org/137373003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@245382 0039d316-1c4b-4281-b951-d872f2087c98
parent 8579fe01
...@@ -251,8 +251,11 @@ ...@@ -251,8 +251,11 @@
'shell_delegate.h', 'shell_delegate.h',
'shell_factory.h', 'shell_factory.h',
'shell_window_ids.h', 'shell_window_ids.h',
'sticky_keys/sticky_keys_state.h',
'sticky_keys/sticky_keys_controller.cc', 'sticky_keys/sticky_keys_controller.cc',
'sticky_keys/sticky_keys_controller.h', 'sticky_keys/sticky_keys_controller.h',
'sticky_keys/sticky_keys_overlay.cc',
'sticky_keys/sticky_keys_overlay.h',
'system/bluetooth/bluetooth_observer.h', 'system/bluetooth/bluetooth_observer.h',
'system/bluetooth/tray_bluetooth.cc', 'system/bluetooth/tray_bluetooth.cc',
'system/bluetooth/tray_bluetooth.h', 'system/bluetooth/tray_bluetooth.h',
...@@ -875,6 +878,7 @@ ...@@ -875,6 +878,7 @@
'shell/window_watcher_shelf_item_delegate.h', 'shell/window_watcher_shelf_item_delegate.h',
'shell/window_watcher_unittest.cc', 'shell/window_watcher_unittest.cc',
'shell_unittest.cc', 'shell_unittest.cc',
'sticky_keys/sticky_keys_overlay_unittest.cc',
'sticky_keys/sticky_keys_unittest.cc', 'sticky_keys/sticky_keys_unittest.cc',
'system/chromeos/managed/tray_locally_managed_user_unittest.cc', 'system/chromeos/managed/tray_locally_managed_user_unittest.cc',
'system/chromeos/network/network_state_notifier_unittest.cc', 'system/chromeos/network/network_state_notifier_unittest.cc',
...@@ -951,6 +955,7 @@ ...@@ -951,6 +955,7 @@
['exclude', 'ash_root_window_transformer_unittest.cc'], ['exclude', 'ash_root_window_transformer_unittest.cc'],
['exclude', 'magnifier/magnification_controller_unittest.cc'], ['exclude', 'magnifier/magnification_controller_unittest.cc'],
['exclude', 'wm/workspace/workspace_window_resizer_unittest.cc'], ['exclude', 'wm/workspace/workspace_window_resizer_unittest.cc'],
['exclude', 'sticky_keys/sticky_keys_overlay_unittest.cc'],
['exclude', 'sticky_keys/sticky_keys_unittest.cc'], ['exclude', 'sticky_keys/sticky_keys_unittest.cc'],
['exclude', 'autoclick/autoclick_unittest.cc'], ['exclude', 'autoclick/autoclick_unittest.cc'],
], ],
......
...@@ -598,6 +598,18 @@ Press Shift + Alt to switch. ...@@ -598,6 +598,18 @@ Press Shift + Alt to switch.
<message name="IDS_ASH_LOGOUT_CONFIRMATION_BUTTON" desc="The text for okay button of the logout confirmation dialog."> <message name="IDS_ASH_LOGOUT_CONFIRMATION_BUTTON" desc="The text for okay button of the logout confirmation dialog.">
Sign out now Sign out now
</message> </message>
<message name="IDS_ASH_CONTROL_KEY" desc="Name of [Ctrl] key name. Shouldn't be translated in many languages actually. This name should be lower case.">
ctrl
</message>
<message name="IDS_ASH_SHIFT_KEY" desc="Name of [Shift] key. Shouldn't be translated in many languages actually. This name should be lower case.">
shift
</message>
<message name="IDS_ASH_ALT_KEY" desc="Name of [Alt] key name. Shouldn't be translated in many languages actually. This name should be lower case.">
alt
</message>
<message name="IDS_ASH_SEARCH_KEY" desc="Name of [Search] key name. Shouldn't be translated in many languages actually. This name should be lower case.">
search
</message>
<!-- ChromeOS-specific strings --> <!-- ChromeOS-specific strings -->
<if expr="pp_ifdef('chromeos')"> <if expr="pp_ifdef('chromeos')">
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#undef RootWindow #undef RootWindow
#endif #endif
#include "ash/sticky_keys/sticky_keys_overlay.h"
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/debug/stack_trace.h" #include "base/debug/stack_trace.h"
#include "ui/aura/root_window.h" #include "ui/aura/root_window.h"
...@@ -89,16 +90,7 @@ void StickyKeysHandlerDelegateImpl::DispatchScrollEvent( ...@@ -89,16 +90,7 @@ void StickyKeysHandlerDelegateImpl::DispatchScrollEvent(
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// StickyKeys // StickyKeys
StickyKeysController::StickyKeysController() StickyKeysController::StickyKeysController()
: enabled_(false), : enabled_(false) {
shift_sticky_key_(
new StickyKeysHandler(ui::EF_SHIFT_DOWN,
new StickyKeysHandlerDelegateImpl())),
alt_sticky_key_(
new StickyKeysHandler(ui::EF_ALT_DOWN,
new StickyKeysHandlerDelegateImpl())),
ctrl_sticky_key_(
new StickyKeysHandler(ui::EF_CONTROL_DOWN,
new StickyKeysHandlerDelegateImpl())) {
} }
StickyKeysController::~StickyKeysController() { StickyKeysController::~StickyKeysController() {
...@@ -113,13 +105,17 @@ void StickyKeysController::Enable(bool enabled) { ...@@ -113,13 +105,17 @@ void StickyKeysController::Enable(bool enabled) {
if (enabled_) { if (enabled_) {
shift_sticky_key_.reset( shift_sticky_key_.reset(
new StickyKeysHandler(ui::EF_SHIFT_DOWN, new StickyKeysHandler(ui::EF_SHIFT_DOWN,
new StickyKeysHandlerDelegateImpl())); new StickyKeysHandlerDelegateImpl()));
alt_sticky_key_.reset( alt_sticky_key_.reset(
new StickyKeysHandler(ui::EF_ALT_DOWN, new StickyKeysHandler(ui::EF_ALT_DOWN,
new StickyKeysHandlerDelegateImpl())); new StickyKeysHandlerDelegateImpl()));
ctrl_sticky_key_.reset( ctrl_sticky_key_.reset(
new StickyKeysHandler(ui::EF_CONTROL_DOWN, new StickyKeysHandler(ui::EF_CONTROL_DOWN,
new StickyKeysHandlerDelegateImpl())); new StickyKeysHandlerDelegateImpl()));
overlay_.reset(new StickyKeysOverlay());
} else if (overlay_.get()) {
overlay_->Show(false);
} }
} }
} }
...@@ -149,26 +145,55 @@ void StickyKeysController::OnKeyEvent(ui::KeyEvent* event) { ...@@ -149,26 +145,55 @@ void StickyKeysController::OnKeyEvent(ui::KeyEvent* event) {
return; return;
} }
if (enabled_ && HandleKeyEvent(event)) if (enabled_) {
event->StopPropagation(); if (HandleKeyEvent(event))
event->StopPropagation();
UpdateOverlay();
}
} }
void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) { void StickyKeysController::OnMouseEvent(ui::MouseEvent* event) {
if (enabled_ && HandleMouseEvent(event)) if (enabled_) {
event->StopPropagation(); if (HandleMouseEvent(event))
event->StopPropagation();
UpdateOverlay();
}
} }
void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) { void StickyKeysController::OnScrollEvent(ui::ScrollEvent* event) {
if (enabled_ && HandleScrollEvent(event)) if (enabled_) {
event->StopPropagation(); if (HandleScrollEvent(event))
event->StopPropagation();
UpdateOverlay();
}
}
void StickyKeysController::UpdateOverlay() {
overlay_->SetModifierKeyState(
ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
overlay_->SetModifierKeyState(
ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
overlay_->SetModifierKeyState(
ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
bool key_in_use =
shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
overlay_->Show(enabled_ && key_in_use);
}
StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
return overlay_.get();
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// StickyKeysHandler // StickyKeysHandler
StickyKeysHandler::StickyKeysHandler(ui::EventFlags target_modifier_flag, StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag,
StickyKeysHandlerDelegate* delegate) StickyKeysHandlerDelegate* delegate)
: modifier_flag_(target_modifier_flag), : modifier_flag_(modifier_flag),
current_state_(DISABLED), current_state_(STICKY_KEY_STATE_DISABLED),
event_from_myself_(false), event_from_myself_(false),
preparing_to_enable_(false), preparing_to_enable_(false),
scroll_delta_(0), scroll_delta_(0),
...@@ -188,11 +213,11 @@ bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) { ...@@ -188,11 +213,11 @@ bool StickyKeysHandler::HandleKeyEvent(ui::KeyEvent* event) {
if (event_from_myself_) if (event_from_myself_)
return false; // Do not handle self-generated key event. return false; // Do not handle self-generated key event.
switch (current_state_) { switch (current_state_) {
case DISABLED: case STICKY_KEY_STATE_DISABLED:
return HandleDisabledState(event); return HandleDisabledState(event);
case ENABLED: case STICKY_KEY_STATE_ENABLED:
return HandleEnabledState(event); return HandleEnabledState(event);
case LOCKED: case STICKY_KEY_STATE_LOCKED:
return HandleLockedState(event); return HandleLockedState(event);
} }
NOTREACHED(); NOTREACHED();
...@@ -203,16 +228,18 @@ bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) { ...@@ -203,16 +228,18 @@ bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
if (ShouldModifyMouseEvent(event)) if (ShouldModifyMouseEvent(event))
preparing_to_enable_ = false; preparing_to_enable_ = false;
if (event_from_myself_ || current_state_ == DISABLED if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED
|| !ShouldModifyMouseEvent(event)) { || !ShouldModifyMouseEvent(event)) {
return false; return false;
} }
DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
current_state_ == STICKY_KEY_STATE_LOCKED);
AppendModifier(event); AppendModifier(event);
// Only disable on the mouse released event in normal, non-locked mode. // Only disable on the mouse released event in normal, non-locked mode.
if (current_state_ == ENABLED && event->type() != ui::ET_MOUSE_PRESSED) { if (current_state_ == STICKY_KEY_STATE_ENABLED &&
current_state_ = DISABLED; event->type() != ui::ET_MOUSE_PRESSED) {
current_state_ = STICKY_KEY_STATE_DISABLED;
DispatchEventAndReleaseModifier(event); DispatchEventAndReleaseModifier(event);
return true; return true;
} }
...@@ -222,14 +249,16 @@ bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) { ...@@ -222,14 +249,16 @@ bool StickyKeysHandler::HandleMouseEvent(ui::MouseEvent* event) {
bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
preparing_to_enable_ = false; preparing_to_enable_ = false;
if (event_from_myself_ || current_state_ == DISABLED) if (event_from_myself_ || current_state_ == STICKY_KEY_STATE_DISABLED)
return false; return false;
DCHECK(current_state_ == ENABLED || current_state_ == LOCKED); DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
current_state_ == STICKY_KEY_STATE_LOCKED);
// We detect a direction change if the current |scroll_delta_| is assigned // We detect a direction change if the current |scroll_delta_| is assigned
// and the offset of the current scroll event has the opposing sign. // and the offset of the current scroll event has the opposing sign.
bool direction_changed = false; bool direction_changed = false;
if (current_state_ == ENABLED && event->type() == ui::ET_SCROLL) { if (current_state_ == STICKY_KEY_STATE_ENABLED &&
event->type() == ui::ET_SCROLL) {
int offset = event->y_offset(); int offset = event->y_offset();
if (scroll_delta_) if (scroll_delta_)
direction_changed = offset * scroll_delta_ <= 0; direction_changed = offset * scroll_delta_ <= 0;
...@@ -242,9 +271,9 @@ bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) { ...@@ -242,9 +271,9 @@ bool StickyKeysHandler::HandleScrollEvent(ui::ScrollEvent* event) {
// We want to modify all the scroll events in the scroll sequence, which ends // We want to modify all the scroll events in the scroll sequence, which ends
// with a fling start event. We also stop when the scroll sequence changes // with a fling start event. We also stop when the scroll sequence changes
// direction. // direction.
if (current_state_ == ENABLED && if (current_state_ == STICKY_KEY_STATE_ENABLED &&
(event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) { (event->type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
current_state_ = DISABLED; current_state_ = STICKY_KEY_STATE_DISABLED;
scroll_delta_ = 0; scroll_delta_ = 0;
DispatchEventAndReleaseModifier(event); DispatchEventAndReleaseModifier(event);
return true; return true;
...@@ -287,7 +316,7 @@ bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) { ...@@ -287,7 +316,7 @@ bool StickyKeysHandler::HandleDisabledState(ui::KeyEvent* event) {
if (preparing_to_enable_) { if (preparing_to_enable_) {
preparing_to_enable_ = false; preparing_to_enable_ = false;
scroll_delta_ = 0; scroll_delta_ = 0;
current_state_ = ENABLED; current_state_ = STICKY_KEY_STATE_ENABLED;
modifier_up_event_.reset(new ui::KeyEvent(*event)); modifier_up_event_.reset(new ui::KeyEvent(*event));
return true; return true;
} }
...@@ -313,11 +342,11 @@ bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) { ...@@ -313,11 +342,11 @@ bool StickyKeysHandler::HandleEnabledState(ui::KeyEvent* event) {
case TARGET_MODIFIER_DOWN: case TARGET_MODIFIER_DOWN:
return true; return true;
case TARGET_MODIFIER_UP: case TARGET_MODIFIER_UP:
current_state_ = LOCKED; current_state_ = STICKY_KEY_STATE_LOCKED;
modifier_up_event_.reset(); modifier_up_event_.reset();
return true; return true;
case NORMAL_KEY_DOWN: { case NORMAL_KEY_DOWN: {
current_state_ = DISABLED; current_state_ = STICKY_KEY_STATE_DISABLED;
AppendModifier(event); AppendModifier(event);
DispatchEventAndReleaseModifier(event); DispatchEventAndReleaseModifier(event);
return true; return true;
...@@ -335,7 +364,7 @@ bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) { ...@@ -335,7 +364,7 @@ bool StickyKeysHandler::HandleLockedState(ui::KeyEvent* event) {
case TARGET_MODIFIER_DOWN: case TARGET_MODIFIER_DOWN:
return true; return true;
case TARGET_MODIFIER_UP: case TARGET_MODIFIER_UP:
current_state_ = DISABLED; current_state_ = STICKY_KEY_STATE_DISABLED;
return false; return false;
case NORMAL_KEY_DOWN: case NORMAL_KEY_DOWN:
case NORMAL_KEY_UP: case NORMAL_KEY_UP:
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define ASH_STICKY_KEYS_STICKY_KEYS_CONTROLLER_H_ #define ASH_STICKY_KEYS_STICKY_KEYS_CONTROLLER_H_
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/sticky_keys/sticky_keys_state.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "ui/events/event_constants.h" #include "ui/events/event_constants.h"
#include "ui/events/event_handler.h" #include "ui/events/event_handler.h"
...@@ -22,6 +23,7 @@ class Window; ...@@ -22,6 +23,7 @@ class Window;
namespace ash { namespace ash {
class StickyKeysOverlay;
class StickyKeysHandler; class StickyKeysHandler;
// StickyKeysController is an accessibility feature for users to be able to // StickyKeysController is an accessibility feature for users to be able to
...@@ -65,6 +67,15 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler { ...@@ -65,6 +67,15 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler {
// Activate sticky keys to intercept and modify incoming events. // Activate sticky keys to intercept and modify incoming events.
void Enable(bool enabled); void Enable(bool enabled);
// Overridden from ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
// Returns the StickyKeyOverlay used by the controller. Ownership is not
// passed.
StickyKeysOverlay* GetOverlayForTest();
private: private:
// Handles keyboard event. Returns true if Sticky key consumes keyboard event. // Handles keyboard event. Returns true if Sticky key consumes keyboard event.
bool HandleKeyEvent(ui::KeyEvent* event); bool HandleKeyEvent(ui::KeyEvent* event);
...@@ -75,10 +86,8 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler { ...@@ -75,10 +86,8 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler {
// Handles scroll event. Returns true if sticky key consumes scroll event. // Handles scroll event. Returns true if sticky key consumes scroll event.
bool HandleScrollEvent(ui::ScrollEvent* event); bool HandleScrollEvent(ui::ScrollEvent* event);
// Overridden from ui::EventHandler: // Updates the overlay UI with the current state of the sticky keys.
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; void UpdateOverlay();
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
// Whether sticky keys is activated and modifying events. // Whether sticky keys is activated and modifying events.
bool enabled_; bool enabled_;
...@@ -88,6 +97,8 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler { ...@@ -88,6 +97,8 @@ class ASH_EXPORT StickyKeysController : public ui::EventHandler {
scoped_ptr<StickyKeysHandler> alt_sticky_key_; scoped_ptr<StickyKeysHandler> alt_sticky_key_;
scoped_ptr<StickyKeysHandler> ctrl_sticky_key_; scoped_ptr<StickyKeysHandler> ctrl_sticky_key_;
scoped_ptr<StickyKeysOverlay> overlay_;
DISALLOW_COPY_AND_ASSIGN(StickyKeysController); DISALLOW_COPY_AND_ASSIGN(StickyKeysController);
}; };
...@@ -139,19 +150,6 @@ class ASH_EXPORT StickyKeysHandler { ...@@ -139,19 +150,6 @@ class ASH_EXPORT StickyKeysHandler {
virtual void DispatchScrollEvent(ui::ScrollEvent* event, virtual void DispatchScrollEvent(ui::ScrollEvent* event,
aura::Window* target) = 0; aura::Window* target) = 0;
}; };
// Represents Sticky Key state.
enum StickyKeyState {
// The sticky key is disabled. Incomming non modifier key events are not
// affected.
DISABLED,
// The sticky key is enabled. Incomming non modifier key down events are
// modified with |modifier_flag_|. After that, sticky key state become
// DISABLED.
ENABLED,
// The sticky key is locked. Incomming non modifier key down events are
// modified with |modifier_flag_|.
LOCKED,
};
// This class takes an ownership of |delegate|. // This class takes an ownership of |delegate|.
StickyKeysHandler(ui::EventFlags modifier_flag, StickyKeysHandler(ui::EventFlags modifier_flag,
...@@ -207,7 +205,7 @@ class ASH_EXPORT StickyKeysHandler { ...@@ -207,7 +205,7 @@ class ASH_EXPORT StickyKeysHandler {
void AppendModifier(ui::MouseEvent* event); void AppendModifier(ui::MouseEvent* event);
void AppendModifier(ui::ScrollEvent* event); void AppendModifier(ui::ScrollEvent* event);
// The modifier flag to be monitored and appended. // The modifier flag to be monitored and appended to events.
const ui::EventFlags modifier_flag_; const ui::EventFlags modifier_flag_;
// The current sticky key status. // The current sticky key status.
......
// 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/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;
// Spacing between overlay contents and border.
const int kHorizontalBorderSpacing = 9;
const int kVerticalBorderSpacing = 4;
// Spacing between modifier key labels.
const int kKeyLabelSpacing = 7;
// 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);
virtual ~StickyKeyOverlayLabel();
StickyKeyState state() const { return state_; }
void SetKeyState(StickyKeyState state);
private:
// views::Label overrides:
virtual void PaintText(gfx::Canvas* canvas,
const base::string16& text,
const gfx::Rect& text_bounds,
int flags) OVERRIDE;
StickyKeyState state_;
DISALLOW_COPY_AND_ASSIGN(StickyKeyOverlayLabel);
};
StickyKeyOverlayLabel::StickyKeyOverlayLabel(const std::string& key_name)
: state_(STICKY_KEY_STATE_DISABLED) {
SetText(base::UTF8ToUTF16(key_name));
SetHorizontalAlignment(gfx::ALIGN_LEFT);
SetFontList(
font_list().DeriveFontListWithSize(18));
SetAutoColorReadabilityEnabled(false);
SetFocusable(false);
SetEnabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
SetDisabledColor(SkColorSetARGB(0x80, 0xFF, 0xFF, 0xFF));
}
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().DeriveFontListWithSizeDeltaAndStyle(0, style));
}
void StickyKeyOverlayLabel::PaintText(gfx::Canvas* canvas,
const base::string16& text,
const gfx::Rect& text_bounds,
int flags) {
views::Label::PaintText(canvas,
text,
text_bounds,
flags | gfx::Canvas::NO_SUBPIXEL_RENDERING);
}
///////////////////////////////////////////////////////////////////////////////
// StickyKeyOverlayLabel
class StickyKeysOverlayView : public views::WidgetDelegateView {
public:
StickyKeysOverlayView();
virtual ~StickyKeysOverlayView();
// views::WidgetDelegateView overrides:
virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
void SetKeyState(ui::EventFlags modifier, StickyKeyState state);
StickyKeyState GetKeyState(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() {
SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
kHorizontalBorderSpacing,
kVerticalBorderSpacing,
kKeyLabelSpacing));
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));
}
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::WidgetDelegateView::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::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.can_activate = false;
params.keep_on_top = true;
params.remove_standard_frame = true;
params.delegate = overlay_view_;
params.bounds = CalculateOverlayBounds();
params.parent = Shell::GetContainer(
Shell::GetTargetRootWindow(),
internal::kShellWindowId_OverlayContainer);
overlay_widget_.reset(new views::Widget);
overlay_widget_->Init(params);
overlay_widget_->SetVisibilityChangedAnimationsEnabled(false);
overlay_widget_->SetContentsView(overlay_view_);
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::SetModifierKeyState(ui::EventFlags modifier,
StickyKeyState state) {
overlay_view_->SetKeyState(modifier, state);
}
StickyKeyState StickyKeysOverlay::GetModifierKeyState(
ui::EventFlags modifier) {
return overlay_view_->GetKeyState(modifier);
}
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
// 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.
#ifndef ASH_STICKY_KEYS_STICKY_KEYS_OVERLAY_H_
#define ASH_STICKY_KEYS_STICKY_KEYS_OVERLAY_H_
#include "ash/ash_export.h"
#include "ash/sticky_keys/sticky_keys_state.h"
#include "base/memory/scoped_ptr.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/events/event_constants.h"
#include "ui/gfx/geometry/size.h"
namespace gfx {
class Rect;
}
namespace views {
class Widget;
}
namespace ash {
class StickyKeysOverlayView;
// Controls the overlay UI for sticky keys, an accessibility feature allowing
// use of modifier keys without holding them down. This overlay will appear as
// a transparent window on the top left of the screen, showing the state of
// each sticky key modifier.
class ASH_EXPORT StickyKeysOverlay : public ui::LayerAnimationObserver {
public:
StickyKeysOverlay();
virtual ~StickyKeysOverlay();
// Shows or hides the overlay.
void Show(bool visible);
// Updates the overlay with the current state of a sticky key modifier.
void SetModifierKeyState(ui::EventFlags modifier,
StickyKeyState state);
// Get the current state of the sticky key modifier in the overlay.
StickyKeyState GetModifierKeyState(ui::EventFlags modifier);
// Returns true if the overlay is currently visible. If the overlay is
// animating, the returned value is the target of the animation.
bool is_visible() { return is_visible_; }
private:
// Returns the current bounds of the overlay, which is based on visibility.
gfx::Rect CalculateOverlayBounds();
// gfx::LayerAnimationObserver overrides:
virtual void OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) OVERRIDE;
virtual void OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) OVERRIDE;
virtual void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) OVERRIDE;
bool is_visible_;
scoped_ptr<views::Widget> overlay_widget_;
// Ownership of |overlay_view_| is passed to the view heirarchy.
StickyKeysOverlayView* overlay_view_;
gfx::Size widget_size_;
};
} // namespace ash
#endif // ASH_STICKY_KEYS_STICKY_KEYS_OVERLAY_H_
// 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/sticky_keys/sticky_keys_controller.h"
#include "ash/test/ash_test_base.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window_tree_host_delegate.h"
#include "ui/events/event.h"
namespace ash {
class StickyKeysOverlayTest : public test::AshTestBase {
public:
StickyKeysOverlayTest() :
controller_(NULL),
overlay_(NULL) {}
virtual ~StickyKeysOverlayTest() {}
virtual void SetUp() OVERRIDE {
test::AshTestBase::SetUp();
controller_ = Shell::GetInstance()->sticky_keys_controller();
controller_->Enable(true);
overlay_ = controller_->GetOverlayForTest();
ASSERT_TRUE(overlay_);
}
void PressAndReleaseKey(ui::KeyboardCode code) {
SendKeyEvent(ui::ET_KEY_PRESSED, code);
SendKeyEvent(ui::ET_KEY_RELEASED, code);
}
void SendKeyEvent(ui::EventType type, ui::KeyboardCode code) {
ui::KeyEvent event(type, code, 0, false);
ui::Event::DispatcherApi dispatcher(&event);
dispatcher.set_target(Shell::GetInstance()->GetPrimaryRootWindow());
aura::WindowTreeHostDelegate* delegate = Shell::GetPrimaryRootWindow()
->GetDispatcher()->AsWindowTreeHostDelegate();
delegate->OnHostKeyEvent(&event);
}
StickyKeysController* controller_;
StickyKeysOverlay* overlay_;
};
TEST_F(StickyKeysOverlayTest, OverlayVisibility) {
StickyKeysOverlay overlay;
EXPECT_FALSE(overlay.is_visible());
overlay.Show(true);
EXPECT_TRUE(overlay.is_visible());
}
TEST_F(StickyKeysOverlayTest, ModifierKeyState) {
StickyKeysOverlay overlay;
overlay.SetModifierKeyState(ui::EF_SHIFT_DOWN, STICKY_KEY_STATE_DISABLED);
overlay.SetModifierKeyState(ui::EF_ALT_DOWN, STICKY_KEY_STATE_LOCKED);
overlay.SetModifierKeyState(ui::EF_CONTROL_DOWN, STICKY_KEY_STATE_ENABLED);
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay.GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay.GetModifierKeyState(ui::EF_ALT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay.GetModifierKeyState(ui::EF_CONTROL_DOWN));
}
TEST_F(StickyKeysOverlayTest, OneModifierEnabled) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing modifier key should show overlay.
PressAndReleaseKey(ui::VKEY_CONTROL);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing a normal key should hide overlay.
PressAndReleaseKey(ui::VKEY_T);
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
}
TEST_F(StickyKeysOverlayTest, TwoModifiersEnabled) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
// Pressing two modifiers should show overlay.
PressAndReleaseKey(ui::VKEY_SHIFT);
PressAndReleaseKey(ui::VKEY_CONTROL);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing a normal key should hide overlay.
PressAndReleaseKey(ui::VKEY_N);
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
}
TEST_F(StickyKeysOverlayTest, LockedModifier) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
// Pressing a modifier key twice should lock modifier and show overlay.
PressAndReleaseKey(ui::VKEY_LMENU);
PressAndReleaseKey(ui::VKEY_LMENU);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
// Pressing a normal key should not hide overlay.
PressAndReleaseKey(ui::VKEY_D);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
}
TEST_F(StickyKeysOverlayTest, LockedAndNormalModifier) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
// Pressing a modifier key twice should lock modifier and show overlay.
PressAndReleaseKey(ui::VKEY_CONTROL);
PressAndReleaseKey(ui::VKEY_CONTROL);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
// Pressing another modifier key should still show overlay.
PressAndReleaseKey(ui::VKEY_SHIFT);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
// Pressing a normal key should not hide overlay but disable normal modifier.
PressAndReleaseKey(ui::VKEY_D);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
}
TEST_F(StickyKeysOverlayTest, ModifiersDisabled) {
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
// Enable modifiers.
PressAndReleaseKey(ui::VKEY_CONTROL);
PressAndReleaseKey(ui::VKEY_SHIFT);
PressAndReleaseKey(ui::VKEY_SHIFT);
PressAndReleaseKey(ui::VKEY_LMENU);
EXPECT_TRUE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_LOCKED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_ENABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
// Disable modifiers and overlay should be hidden.
PressAndReleaseKey(ui::VKEY_CONTROL);
PressAndReleaseKey(ui::VKEY_CONTROL);
PressAndReleaseKey(ui::VKEY_SHIFT);
PressAndReleaseKey(ui::VKEY_LMENU);
PressAndReleaseKey(ui::VKEY_LMENU);
EXPECT_FALSE(overlay_->is_visible());
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_CONTROL_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_SHIFT_DOWN));
EXPECT_EQ(STICKY_KEY_STATE_DISABLED,
overlay_->GetModifierKeyState(ui::EF_ALT_DOWN));
}
} // namespace ash
// 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.
#ifndef ASH_STICKY_KEYS_STICKY_KEYS_STATE_H_
#define ASH_STICKY_KEYS_STICKY_KEYS_STATE_H_
namespace ash {
// State of an individual modifier key.
enum StickyKeyState {
// The sticky key is disabled. Incoming non-modifier key events are not
// affected.
STICKY_KEY_STATE_DISABLED,
// The sticky key is enabled. Incoming non-modifier key down events are
// modified. After that, sticky key state becomes DISABLED.
STICKY_KEY_STATE_ENABLED,
// The sticky key is locked. All incoming non modifier key down events are
// modified.
STICKY_KEY_STATE_LOCKED,
};
} // namespace ash
#endif // ASH_STICKY_KEYS_STICKY_KEYS_STATE_H_
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment