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