screen_capture_notification_ui_views.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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 "chrome/browser/ui/screen_capture_notification_ui.h" 6 7#include "ash/shell.h" 8#include "chrome/app/chrome_dll_resource.h" 9#include "chrome/browser/ui/views/chrome_views_export.h" 10#include "grit/generated_resources.h" 11#include "grit/theme_resources.h" 12#include "ui/aura/window_event_dispatcher.h" 13#include "ui/base/hit_test.h" 14#include "ui/base/l10n/l10n_util.h" 15#include "ui/base/resource/resource_bundle.h" 16#include "ui/views/bubble/bubble_border.h" 17#include "ui/views/bubble/bubble_frame_view.h" 18#include "ui/views/controls/button/blue_button.h" 19#include "ui/views/controls/image_view.h" 20#include "ui/views/controls/link.h" 21#include "ui/views/controls/link_listener.h" 22#include "ui/views/view.h" 23#include "ui/views/widget/widget.h" 24#include "ui/views/widget/widget_delegate.h" 25#include "ui/wm/core/shadow_types.h" 26 27namespace { 28 29const int kMinimumWidth = 460; 30const int kMaximumWidth = 1000; 31const int kHorizontalMargin = 10; 32const int kPadding = 5; 33const int kPaddingLeft = 10; 34 35namespace { 36 37// A ClientView that overrides NonClientHitTest() so that the whole window area 38// acts as a window caption, except a rect specified using SetClientRect(). 39// ScreenCaptureNotificationUIViews uses this class to make the notification bar 40// draggable. 41class NotificationBarClientView : public views::ClientView { 42 public: 43 NotificationBarClientView(views::Widget* widget, views::View* view) 44 : views::ClientView(widget, view) { 45 } 46 virtual ~NotificationBarClientView() {} 47 48 void SetClientRect(const gfx::Rect& rect) { 49 rect_ = rect; 50 } 51 52 // views::ClientView overrides. 53 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { 54 if (!bounds().Contains(point)) 55 return HTNOWHERE; 56 // The whole window is HTCAPTION, except the |rect_|. 57 if (rect_.Contains(gfx::PointAtOffsetFromOrigin(point - bounds().origin()))) 58 return HTCLIENT; 59 60 return HTCAPTION; 61 } 62 63 private: 64 gfx::Rect rect_; 65 66 DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView); 67}; 68 69} // namespace 70 71// ScreenCaptureNotificationUI implementation using Views. 72class ScreenCaptureNotificationUIViews 73 : public ScreenCaptureNotificationUI, 74 public views::WidgetDelegateView, 75 public views::ButtonListener, 76 public views::LinkListener { 77 public: 78 explicit ScreenCaptureNotificationUIViews(const base::string16& text); 79 virtual ~ScreenCaptureNotificationUIViews(); 80 81 // ScreenCaptureNotificationUI interface. 82 virtual void OnStarted(const base::Closure& stop_callback) OVERRIDE; 83 84 // views::View overrides. 85 virtual gfx::Size GetPreferredSize() OVERRIDE; 86 virtual void Layout() OVERRIDE; 87 88 // views::WidgetDelegateView overrides. 89 virtual void DeleteDelegate() OVERRIDE; 90 virtual views::View* GetContentsView() OVERRIDE; 91 virtual views::ClientView* CreateClientView(views::Widget* widget) OVERRIDE; 92 virtual views::NonClientFrameView* CreateNonClientFrameView( 93 views::Widget* widget) OVERRIDE; 94 virtual base::string16 GetWindowTitle() const OVERRIDE; 95 virtual bool ShouldShowWindowTitle() const OVERRIDE; 96 virtual bool ShouldShowCloseButton() const OVERRIDE; 97 virtual bool CanActivate() const OVERRIDE; 98 99 // views::ButtonListener interface. 100 virtual void ButtonPressed(views::Button* sender, 101 const ui::Event& event) OVERRIDE; 102 103 // views::LinkListener interface. 104 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; 105 106 private: 107 // Helper to call |stop_callback_|. 108 void NotifyStopped(); 109 110 const base::string16 text_; 111 base::Closure stop_callback_; 112 NotificationBarClientView* client_view_; 113 views::ImageView* gripper_; 114 views::Label* label_; 115 views::BlueButton* stop_button_; 116 views::Link* hide_link_; 117 118 DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews); 119}; 120 121ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews( 122 const base::string16& text) 123 : text_(text), 124 client_view_(NULL), 125 gripper_(NULL), 126 label_(NULL), 127 stop_button_(NULL), 128 hide_link_(NULL) { 129 set_owned_by_client(); 130 131 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> 132 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); 133 134 gripper_ = new views::ImageView(); 135 gripper_->SetImage( 136 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 137 IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP)); 138 AddChildView(gripper_); 139 140 label_ = new views::Label(); 141 AddChildView(label_); 142 143 base::string16 stop_text = 144 l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP); 145 stop_button_ = new views::BlueButton(this, stop_text); 146 AddChildView(stop_button_); 147 148 // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to 149 // merge to M34. Change it to a new IDS_ after the merge. 150 hide_link_ = new views::Link( 151 l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON)); 152 hide_link_->set_listener(this); 153 AddChildView(hide_link_); 154} 155 156ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() { 157 stop_callback_.Reset(); 158 delete GetWidget(); 159} 160 161void ScreenCaptureNotificationUIViews::OnStarted( 162 const base::Closure& stop_callback) { 163 stop_callback_ = stop_callback; 164 165 label_->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE); 166 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 167 label_->SetText(text_); 168 169 views::Widget* widget = new views::Widget; 170 171 views::Widget::InitParams params; 172 params.delegate = this; 173 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 174 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 175 params.remove_standard_frame = true; 176 params.keep_on_top = true; 177 params.top_level = true; 178 // Make sure can_activate is true so the window icon will show in the taskbar. 179 params.can_activate = true; 180 181#if defined(USE_ASH) 182 // TODO(sergeyu): The notification bar must be shown on the monitor that's 183 // being captured. Make sure it's always the case. Currently we always capture 184 // the primary monitor. 185 if (ash::Shell::HasInstance()) 186 params.context = ash::Shell::GetPrimaryRootWindow(); 187#endif 188 189 widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); 190 widget->Init(params); 191 widget->SetAlwaysOnTop(true); 192 193 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); 194 // TODO(sergeyu): Move the notification to the display being captured when 195 // per-display screen capture is supported. 196 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); 197 198 // Place the bar in the center of the bottom of the display. 199 gfx::Size size = widget->non_client_view()->GetPreferredSize(); 200 gfx::Rect bounds( 201 work_area.x() + work_area.width() / 2 - size.width() / 2, 202 work_area.y() + work_area.height() - size.height(), 203 size.width(), size.height()); 204 widget->SetBounds(bounds); 205 206 widget->Show(); 207} 208 209gfx::Size ScreenCaptureNotificationUIViews::GetPreferredSize() { 210 gfx::Size grip_size = gripper_->GetPreferredSize(); 211 gfx::Size label_size = label_->GetPreferredSize(); 212 gfx::Size stop_button_size = stop_button_->GetPreferredSize(); 213 gfx::Size hide_link_size = hide_link_->GetPreferredSize(); 214 int width = kHorizontalMargin * 3 + grip_size.width() + label_size.width() + 215 stop_button_size.width() + hide_link_size.width(); 216 width = std::max(width, kMinimumWidth); 217 width = std::min(width, kMaximumWidth); 218 return gfx::Size(width, std::max(label_size.height(), 219 std::max(hide_link_size.height(), 220 stop_button_size.height()))); 221} 222 223void ScreenCaptureNotificationUIViews::Layout() { 224 gfx::Rect grip_rect(gripper_->GetPreferredSize()); 225 grip_rect.set_y((bounds().height() - grip_rect.height()) / 2); 226 gripper_->SetBoundsRect(grip_rect); 227 228 gfx::Rect stop_button_rect(stop_button_->GetPreferredSize()); 229 gfx::Rect hide_link_rect(hide_link_->GetPreferredSize()); 230 231 hide_link_rect.set_x(bounds().width() - hide_link_rect.width()); 232 hide_link_rect.set_y((bounds().height() - hide_link_rect.height()) / 2); 233 hide_link_->SetBoundsRect(hide_link_rect); 234 235 stop_button_rect.set_x( 236 hide_link_rect.x() - kHorizontalMargin - stop_button_rect.width()); 237 stop_button_->SetBoundsRect(stop_button_rect); 238 239 gfx::Rect label_rect; 240 label_rect.set_x(grip_rect.right() + kHorizontalMargin); 241 label_rect.set_width( 242 stop_button_rect.x() - kHorizontalMargin - label_rect.x()); 243 label_rect.set_height(bounds().height()); 244 label_->SetBoundsRect(label_rect); 245 246 client_view_->SetClientRect(gfx::Rect( 247 stop_button_rect.x(), stop_button_rect.y(), 248 stop_button_rect.width() + kHorizontalMargin + hide_link_rect.width(), 249 std::max(stop_button_rect.height(), hide_link_rect.height()))); 250} 251 252void ScreenCaptureNotificationUIViews::DeleteDelegate() { 253 NotifyStopped(); 254} 255 256views::View* ScreenCaptureNotificationUIViews::GetContentsView() { 257 return this; 258} 259 260views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView( 261 views::Widget* widget) { 262 DCHECK(!client_view_); 263 client_view_ = new NotificationBarClientView(widget, this); 264 return client_view_; 265} 266 267views::NonClientFrameView* 268ScreenCaptureNotificationUIViews::CreateNonClientFrameView( 269 views::Widget* widget) { 270 views::BubbleFrameView* frame = new views::BubbleFrameView( 271 gfx::Insets(kPadding, kPaddingLeft, kPadding, kPadding)); 272 SkColor color = widget->GetNativeTheme()->GetSystemColor( 273 ui::NativeTheme::kColorId_DialogBackground); 274 frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>( 275 new views::BubbleBorder(views::BubbleBorder::NONE, 276 views::BubbleBorder::SMALL_SHADOW, 277 color))); 278 return frame; 279} 280 281base::string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const { 282 return text_; 283} 284 285bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const { 286 return false; 287} 288 289bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const { 290 return false; 291} 292 293bool ScreenCaptureNotificationUIViews::CanActivate() const { 294 // When the window is visible, it can be activated so the mouse clicks 295 // can be sent to the window; when the window is minimized, we don't want it 296 // to activate, otherwise it sometimes does not show properly on Windows. 297 return GetWidget() && GetWidget()->IsVisible(); 298} 299 300void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender, 301 const ui::Event& event) { 302 NotifyStopped(); 303} 304 305void ScreenCaptureNotificationUIViews::LinkClicked(views::Link* source, 306 int event_flags) { 307 GetWidget()->Minimize(); 308} 309 310void ScreenCaptureNotificationUIViews::NotifyStopped() { 311 if (!stop_callback_.is_null()) { 312 base::Closure callback = stop_callback_; 313 stop_callback_.Reset(); 314 callback.Run(); 315 } 316} 317 318} // namespace 319 320scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create( 321 const base::string16& text) { 322 return scoped_ptr<ScreenCaptureNotificationUI>( 323 new ScreenCaptureNotificationUIViews(text)); 324} 325