1 // Copyright 2013 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/accelerators/exit_warning_handler.h" 6 7 #include "ash/shell.h" 8 #include "ash/shell_delegate.h" 9 #include "ash/shell_window_ids.h" 10 #include "base/strings/utf_string_conversions.h" 11 #include "base/time/time.h" 12 #include "base/timer/timer.h" 13 #include "grit/ash_strings.h" 14 #include "ui/aura/root_window.h" 15 #include "ui/base/accessibility/accessible_view_state.h" 16 #include "ui/base/l10n/l10n_util.h" 17 #include "ui/base/resource/resource_bundle.h" 18 #include "ui/gfx/canvas.h" 19 #include "ui/gfx/font.h" 20 #include "ui/views/controls/label.h" 21 #include "ui/views/layout/fill_layout.h" 22 #include "ui/views/view.h" 23 #include "ui/views/widget/widget.h" 24 #include "ui/views/widget/widget_delegate.h" 25 26 namespace ash { 27 namespace { 28 29 const int64 kTimeOutMilliseconds = 2000; 30 const SkColor kForegroundColor = 0xFFFFFFFF; 31 const SkColor kBackgroundColor = 0xE0808080; 32 const int kHorizontalMarginAroundText = 100; 33 const int kVerticalMarginAroundText = 100; 34 35 class ExitWarningLabel : public views::Label { 36 public: 37 ExitWarningLabel() {} 38 39 virtual ~ExitWarningLabel() {} 40 41 private: 42 virtual void PaintText(gfx::Canvas* canvas, 43 const string16& text, 44 const gfx::Rect& text_bounds, 45 int flags) OVERRIDE { 46 // Turn off subpixel rendering. 47 views::Label::PaintText(canvas, 48 text, 49 text_bounds, 50 flags | gfx::Canvas::NO_SUBPIXEL_RENDERING); 51 } 52 53 DISALLOW_COPY_AND_ASSIGN(ExitWarningLabel); 54 }; 55 56 class ExitWarningWidgetDelegateView : public views::WidgetDelegateView { 57 public: 58 ExitWarningWidgetDelegateView() : text_width_(0), width_(0), height_(0) { 59 text_ = l10n_util::GetStringUTF16(IDS_ASH_EXIT_WARNING_POPUP_TEXT); 60 accessible_name_ = 61 l10n_util::GetStringUTF16(IDS_ASH_EXIT_WARNING_POPUP_TEXT_ACCESSIBLE); 62 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 63 font_ = rb.GetFont(ui::ResourceBundle::LargeFont); 64 text_width_ = font_.GetStringWidth(text_); 65 width_ = text_width_ + kHorizontalMarginAroundText; 66 height_ = font_.GetHeight() + kVerticalMarginAroundText; 67 views::Label* label = new ExitWarningLabel; 68 label->SetText(text_); 69 label->SetHorizontalAlignment(gfx::ALIGN_CENTER); 70 label->SetFont(font_); 71 label->SetEnabledColor(kForegroundColor); 72 label->SetDisabledColor(kForegroundColor); 73 label->SetAutoColorReadabilityEnabled(false); 74 AddChildView(label); 75 SetLayoutManager(new views::FillLayout); 76 } 77 78 virtual gfx::Size GetPreferredSize() OVERRIDE { 79 return gfx::Size(width_, height_); 80 } 81 82 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { 83 canvas->FillRect(GetLocalBounds(), kBackgroundColor); 84 views::WidgetDelegateView::OnPaint(canvas); 85 } 86 87 virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE { 88 state->name = accessible_name_; 89 state->role = ui::AccessibilityTypes::ROLE_ALERT; 90 } 91 92 private: 93 base::string16 text_; 94 base::string16 accessible_name_; 95 gfx::Font font_; 96 int text_width_; 97 int width_; 98 int height_; 99 100 DISALLOW_COPY_AND_ASSIGN(ExitWarningWidgetDelegateView); 101 }; 102 103 } // namespace 104 105 ExitWarningHandler::ExitWarningHandler() 106 : state_(IDLE), 107 stub_timer_for_test_(false) { 108 } 109 110 ExitWarningHandler::~ExitWarningHandler() { 111 // Note: If a timer is outstanding, it is stopped in its destructor. 112 Hide(); 113 } 114 115 void ExitWarningHandler::HandleAccelerator() { 116 ShellDelegate* shell_delegate = Shell::GetInstance()->delegate(); 117 switch (state_) { 118 case IDLE: 119 state_ = WAIT_FOR_DOUBLE_PRESS; 120 Show(); 121 StartTimer(); 122 shell_delegate->RecordUserMetricsAction(UMA_ACCEL_EXIT_FIRST_Q); 123 break; 124 case WAIT_FOR_DOUBLE_PRESS: 125 state_ = EXITING; 126 CancelTimer(); 127 Hide(); 128 shell_delegate->RecordUserMetricsAction(UMA_ACCEL_EXIT_SECOND_Q); 129 shell_delegate->Exit(); 130 break; 131 case EXITING: 132 break; 133 default: 134 NOTREACHED(); 135 break; 136 } 137 } 138 139 void ExitWarningHandler::TimerAction() { 140 Hide(); 141 if (state_ == WAIT_FOR_DOUBLE_PRESS) 142 state_ = IDLE; 143 } 144 145 void ExitWarningHandler::StartTimer() { 146 if (stub_timer_for_test_) 147 return; 148 timer_.Start(FROM_HERE, 149 base::TimeDelta::FromMilliseconds(kTimeOutMilliseconds), 150 this, 151 &ExitWarningHandler::TimerAction); 152 } 153 154 void ExitWarningHandler::CancelTimer() { 155 timer_.Stop(); 156 } 157 158 void ExitWarningHandler::Show() { 159 if (widget_) 160 return; 161 aura::RootWindow* root_window = Shell::GetActiveRootWindow(); 162 ExitWarningWidgetDelegateView* delegate = new ExitWarningWidgetDelegateView; 163 gfx::Size rs = root_window->bounds().size(); 164 gfx::Size ps = delegate->GetPreferredSize(); 165 gfx::Rect bounds((rs.width() - ps.width()) / 2, 166 (rs.height() - ps.height()) / 3, 167 ps.width(), ps.height()); 168 views::Widget::InitParams params; 169 params.type = views::Widget::InitParams::TYPE_POPUP; 170 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 171 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 172 params.accept_events = false; 173 params.can_activate = false; 174 params.keep_on_top = true; 175 params.remove_standard_frame = true; 176 params.delegate = delegate; 177 params.bounds = bounds; 178 params.parent = Shell::GetContainer( 179 root_window, 180 internal::kShellWindowId_SettingBubbleContainer); 181 widget_.reset(new views::Widget); 182 widget_->Init(params); 183 widget_->SetContentsView(delegate); 184 widget_->GetNativeView()->SetName("ExitWarningWindow"); 185 widget_->Show(); 186 187 delegate->NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_ALERT, true); 188 } 189 190 void ExitWarningHandler::Hide() { 191 widget_.reset(); 192 } 193 194 } // namespace ash 195