Index: chrome/browser/chromeos/status/caps_lock_menu_button.cc |
diff --git a/chrome/browser/chromeos/status/caps_lock_menu_button.cc b/chrome/browser/chromeos/status/caps_lock_menu_button.cc |
index 2b0bc760c0f02f37e8851e84f2601f06e026a871..565079cca09b9de74a2195e5860844b6641c4bfb 100644 |
--- a/chrome/browser/chromeos/status/caps_lock_menu_button.cc |
+++ b/chrome/browser/chromeos/status/caps_lock_menu_button.cc |
@@ -8,8 +8,10 @@ |
#include "base/utf_string_conversions.h" |
#include "chrome/browser/chromeos/input_method/xkeyboard.h" |
+#include "chrome/browser/chromeos/system/runtime_environment.h" |
#include "chrome/browser/prefs/pref_service.h" |
#include "chrome/browser/profiles/profile.h" |
+#include "chrome/browser/ui/views/bubble/bubble.h" |
#include "chrome/common/chrome_notification_types.h" |
#include "chrome/common/pref_names.h" |
#include "grit/generated_resources.h" |
@@ -36,6 +38,9 @@ const int kPadLeftX = 10, kPadRightX = 10, kPadY = 5; |
// Padding between image and text. |
const int kTextPadX = 10; |
+const size_t kMaxBubbleCount = 3; |
+const size_t kCloseBubbleTimerInSec = 5; |
+ |
// Returns PrefService object associated with |host|. |
PrefService* GetPrefService(chromeos::StatusAreaHost* host) { |
if (host->GetProfile()) |
@@ -58,7 +63,7 @@ class CapsLockMenuButton::StatusView : public View { |
virtual ~StatusView() { |
} |
- gfx::Size GetPreferredSize() { |
+ virtual gfx::Size GetPreferredSize() OVERRIDE { |
// TODO(yusukes): For better string localization, we should use either |
// IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SHIFT_KEYS or |
// IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SEARCH here instead of just |
@@ -122,10 +127,12 @@ class CapsLockMenuButton::StatusView : public View { |
} |
void OnMouseReleased(const views::MouseEvent& event) { |
- if (event.IsLeftMouseButton()) { |
- DCHECK(menu_button_->menu_runner_.get()); |
- menu_button_->menu_runner_->Cancel(); |
- } |
+ if (!event.IsLeftMouseButton()) |
+ return; |
+ if (menu_button_->IsMenuShown()) |
+ menu_button_->HideMenu(); |
+ if (menu_button_->IsBubbleShown()) |
+ menu_button_->HideBubble(); |
} |
private: |
@@ -141,7 +148,10 @@ class CapsLockMenuButton::StatusView : public View { |
CapsLockMenuButton::CapsLockMenuButton(StatusAreaHost* host) |
: StatusAreaButton(host, this), |
prefs_(GetPrefService(host)), |
- status_(NULL) { |
+ status_(NULL), |
+ bubble_(NULL), |
+ should_show_bubble_(true), |
+ bubble_count_(0) { |
if (prefs_) |
remap_search_key_to_.Init( |
prefs::kLanguageXkbRemapSearchKeyTo, prefs_, this); |
@@ -180,6 +190,9 @@ bool CapsLockMenuButton::IsCommandEnabled(int id) const { |
void CapsLockMenuButton::RunMenu(views::View* source, const gfx::Point& pt) { |
static const int kDummyCommandId = 1000; |
+ if (IsBubbleShown()) |
+ HideBubble(); |
+ |
views::MenuItemView* menu = new views::MenuItemView(this); |
// MenuRunner takes ownership of |menu|. |
menu_runner_.reset(new views::MenuRunner(menu)); |
@@ -210,7 +223,25 @@ void CapsLockMenuButton::RunMenu(views::View* source, const gfx::Point& pt) { |
// SystemKeyEventListener::CapsLockObserver implementation |
void CapsLockMenuButton::OnCapsLockChange(bool enabled) { |
+ if (!enabled && !HasCapsLock() && bubble_count_ > 0) { |
+ // Both shift keys are pressed. We can assume that the user now recognizes |
+ // how to turn off Caps Lock. |
+ should_show_bubble_ = false; |
+ } |
+ |
+ // Update the indicator. |
UpdateUIFromCurrentCapsLock(enabled); |
+ |
+ // Update the drop-down menu and bubble. Since the constructor also calls |
+ // UpdateUIFromCurrentCapsLock, we shouldn't do this in the function. |
+ if (enabled && IsMenuShown()) |
+ status_->Update(); // Update the drop-down menu if it's already shown. |
+ else if (!enabled && IsMenuShown()) |
+ HideMenu(); |
+ if (enabled) |
+ MaybeShowBubble(); |
+ else if (!enabled && IsBubbleShown()) |
+ HideBubble(); |
} |
//////////////////////////////////////////////////////////////////////////////// |
@@ -225,14 +256,14 @@ void CapsLockMenuButton::Observe(int type, |
void CapsLockMenuButton::UpdateAccessibleName() { |
int id = IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SHIFT_KEYS; |
- if (prefs_ && (remap_search_key_to_.GetValue() == input_method::kCapsLockKey)) |
+ if (HasCapsLock()) |
id = IDS_STATUSBAR_CAPS_LOCK_ENABLED_PRESS_SEARCH; |
SetAccessibleName(l10n_util::GetStringUTF16(id)); |
} |
string16 CapsLockMenuButton::GetText() const { |
int id = IDS_STATUSBAR_PRESS_SHIFT_KEYS; |
- if (prefs_ && (remap_search_key_to_.GetValue() == input_method::kCapsLockKey)) |
+ if (HasCapsLock()) |
id = IDS_STATUSBAR_PRESS_SEARCH; |
return l10n_util::GetStringUTF16(id); |
} |
@@ -240,10 +271,72 @@ string16 CapsLockMenuButton::GetText() const { |
void CapsLockMenuButton::UpdateUIFromCurrentCapsLock(bool enabled) { |
SetVisible(enabled); |
SchedulePaint(); |
- if (enabled && status_) |
- status_->Update(); |
- if (!enabled && menu_runner_.get()) |
- menu_runner_->Cancel(); |
+} |
+ |
+bool CapsLockMenuButton::IsMenuShown() const { |
+ return menu_runner_.get() && status_; |
+} |
+ |
+void CapsLockMenuButton::HideMenu() { |
+ if (!IsMenuShown()) |
+ return; |
+ menu_runner_->Cancel(); |
+} |
+ |
+bool CapsLockMenuButton::IsBubbleShown() const { |
+ return bubble_; |
+} |
+ |
+void CapsLockMenuButton::MaybeShowBubble() { |
+ if (IsBubbleShown() || |
+ // We've already shown the bubble |kMaxBubbleCount| times. |
+ !should_show_bubble_ || |
+ // Don't show the bubble when Caps Lock key is available. |
+ HasCapsLock()) |
+ return; |
+ |
+ ++bubble_count_; |
+ if (bubble_count_ > kMaxBubbleCount) { |
+ should_show_bubble_ = false; |
+ } else { |
+ CreateAndShowBubble(); |
+ bubble_timer_.Start(FROM_HERE, |
+ base::TimeDelta::FromSeconds(kCloseBubbleTimerInSec), |
+ this, |
+ &CapsLockMenuButton::HideBubble); |
+ } |
+} |
+ |
+void CapsLockMenuButton::CreateAndShowBubble() { |
+ if (IsBubbleShown()) { |
+ NOTREACHED(); |
+ return; |
+ } |
+ |
+ gfx::Rect button_bounds = GetScreenBounds(); |
+ button_bounds.set_y(button_bounds.y() + 1); // See login/message_bubble.cc. |
+ |
+ bubble_ = Bubble::ShowFocusless(GetWidget(), |
+ button_bounds, |
+ views::BubbleBorder::TOP_RIGHT, |
+ new CapsLockMenuButton::StatusView(this), |
+ NULL /* no delegate */, |
+ true /* show_while_screen_is_locked */); |
+} |
+ |
+void CapsLockMenuButton::HideBubble() { |
+ if (!IsBubbleShown()) |
+ return; |
+ bubble_timer_.Stop(); // no-op if it's not running. |
+ bubble_->Close(); |
+ bubble_ = NULL; |
+} |
+ |
+bool CapsLockMenuButton::HasCapsLock() const { |
+ return (prefs_ && |
+ (remap_search_key_to_.GetValue() == input_method::kCapsLockKey)) || |
+ // A keyboard for Linux usually has Caps Lock. |
+ !system::runtime_environment::IsRunningOnChromeOS(); |
} |
} // namespace chromeos |