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/root_window.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/corewm/shadow_types.h" 21 #include "ui/views/layout/box_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 { 27 28 const int kMinimumWidth = 460; 29 const int kMaximumWidth = 1000; 30 const int kHorizontalMargin = 10; 31 const int kPadding = 5; 32 const int kPaddingLeft = 10; 33 34 namespace { 35 36 // A ClientView that overrides NonClientHitTest() so that the whole window area 37 // acts as a window caption, except a rect specified using SetClientRect(). 38 // ScreenCaptureNotificationUIViews uses this class to make the notification bar 39 // draggable. 40 class NotificationBarClientView : public views::ClientView { 41 public: 42 NotificationBarClientView(views::Widget* widget, views::View* view) 43 : views::ClientView(widget, view) { 44 } 45 virtual ~NotificationBarClientView() {} 46 47 void SetClientRect(const gfx::Rect& rect) { 48 rect_ = rect; 49 } 50 51 // views::ClientView overrides. 52 virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE { 53 if (!bounds().Contains(point)) 54 return HTNOWHERE; 55 // The whole window is HTCAPTION, except the |rect_|. 56 if (rect_.Contains(gfx::PointAtOffsetFromOrigin(point - bounds().origin()))) 57 return HTCLIENT; 58 59 return HTCAPTION; 60 } 61 62 private: 63 gfx::Rect rect_; 64 65 DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView); 66 }; 67 68 } // namespace 69 70 // ScreenCaptureNotificationUI implementation using Views. 71 class ScreenCaptureNotificationUIViews 72 : public ScreenCaptureNotificationUI, 73 public views::WidgetDelegateView, 74 public views::ButtonListener { 75 public: 76 explicit ScreenCaptureNotificationUIViews(const string16& text); 77 virtual ~ScreenCaptureNotificationUIViews(); 78 79 // ScreenCaptureNotificationUI interface. 80 virtual void OnStarted(const base::Closure& stop_callback) OVERRIDE; 81 82 // views::View overrides. 83 virtual gfx::Size GetPreferredSize() OVERRIDE; 84 virtual void Layout() OVERRIDE; 85 86 // views::WidgetDelegateView overrides. 87 virtual void DeleteDelegate() OVERRIDE; 88 virtual views::View* GetContentsView() OVERRIDE; 89 virtual views::ClientView* CreateClientView(views::Widget* widget) OVERRIDE; 90 virtual views::NonClientFrameView* CreateNonClientFrameView( 91 views::Widget* widget) OVERRIDE; 92 virtual string16 GetWindowTitle() const OVERRIDE; 93 virtual bool ShouldShowWindowTitle() const OVERRIDE; 94 virtual bool ShouldShowCloseButton() const OVERRIDE; 95 96 // views::ButtonListener interface. 97 virtual void ButtonPressed(views::Button* sender, 98 const ui::Event& event) OVERRIDE; 99 100 private: 101 // Helper to call |stop_callback_|. 102 void NotifyStopped(); 103 104 const string16 text_; 105 base::Closure stop_callback_; 106 NotificationBarClientView* client_view_; 107 views::ImageView* gripper_; 108 views::Label* label_; 109 views::BlueButton* stop_button_; 110 111 DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews); 112 }; 113 114 ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews( 115 const string16& text) 116 : text_(text), 117 client_view_(NULL), 118 gripper_(NULL), 119 label_(NULL), 120 stop_button_(NULL) { 121 set_owned_by_client(); 122 123 set_background(views::Background::CreateSolidBackground(GetNativeTheme()-> 124 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground))); 125 126 gripper_ = new views::ImageView(); 127 gripper_->SetImage( 128 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 129 IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP)); 130 AddChildView(gripper_); 131 132 label_ = new views::Label(); 133 AddChildView(label_); 134 135 string16 stop_text = 136 l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP); 137 stop_button_ = new views::BlueButton(this, stop_text); 138 AddChildView(stop_button_); 139 } 140 141 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() { 142 stop_callback_.Reset(); 143 delete GetWidget(); 144 } 145 146 void ScreenCaptureNotificationUIViews::OnStarted( 147 const base::Closure& stop_callback) { 148 stop_callback_ = stop_callback; 149 150 label_->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE); 151 label_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 152 label_->SetText(text_); 153 154 views::Widget* widget = new views::Widget; 155 156 views::Widget::InitParams params; 157 params.delegate = this; 158 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; 159 params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; 160 params.remove_standard_frame = true; 161 params.keep_on_top = true; 162 params.top_level = true; 163 params.can_activate = false; 164 165 #if defined(USE_ASH) 166 // TODO(sergeyu): The notification bar must be shown on the monitor that's 167 // being captured. Make sure it's always the case. Currently we always capture 168 // the primary monitor. 169 if (ash::Shell::HasInstance()) 170 params.context = ash::Shell::GetPrimaryRootWindow(); 171 #endif 172 173 widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM); 174 widget->Init(params); 175 widget->SetAlwaysOnTop(true); 176 177 gfx::Screen* screen = gfx::Screen::GetNativeScreen(); 178 // TODO(sergeyu): Move the notification to the display being captured when 179 // per-display screen capture is supported. 180 gfx::Rect work_area = screen->GetPrimaryDisplay().work_area(); 181 182 // Place the bar in the center of the bottom of the display. 183 gfx::Size size = widget->non_client_view()->GetPreferredSize(); 184 gfx::Rect bounds( 185 work_area.x() + work_area.width() / 2 - size.width() / 2, 186 work_area.y() + work_area.height() - size.height(), 187 size.width(), size.height()); 188 widget->SetBounds(bounds); 189 190 widget->Show(); 191 } 192 193 gfx::Size ScreenCaptureNotificationUIViews::GetPreferredSize() { 194 gfx::Size grip_size = gripper_->GetPreferredSize(); 195 gfx::Size label_size = child_at(1)->GetPreferredSize(); 196 gfx::Size button_size = child_at(2)->GetPreferredSize(); 197 int width = kHorizontalMargin * 2 + grip_size.width() + label_size.width() + 198 button_size.width(); 199 width = std::max(width, kMinimumWidth); 200 width = std::min(width, kMaximumWidth); 201 return gfx::Size(width, std::max(label_size.height(), button_size.height())); 202 } 203 204 void ScreenCaptureNotificationUIViews::Layout() { 205 gfx::Rect grip_rect(gripper_->GetPreferredSize()); 206 grip_rect.set_y(bounds().height() / 2 - grip_rect.height() / 2); 207 gripper_->SetBoundsRect(grip_rect); 208 209 gfx::Rect button_rect(stop_button_->GetPreferredSize()); 210 button_rect.set_x(bounds().width() - button_rect.width()); 211 stop_button_->SetBoundsRect(button_rect); 212 213 gfx::Rect label_rect; 214 label_rect.set_x(grip_rect.right() + kHorizontalMargin); 215 label_rect.set_width(button_rect.x() - kHorizontalMargin - label_rect.x()); 216 label_rect.set_height(bounds().height()); 217 label_->SetBoundsRect(label_rect); 218 219 client_view_->SetClientRect(button_rect); 220 } 221 222 void ScreenCaptureNotificationUIViews::DeleteDelegate() { 223 NotifyStopped(); 224 } 225 226 views::View* ScreenCaptureNotificationUIViews::GetContentsView() { 227 return this; 228 } 229 230 views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView( 231 views::Widget* widget) { 232 DCHECK(!client_view_); 233 client_view_ = new NotificationBarClientView(widget, this); 234 return client_view_; 235 } 236 237 views::NonClientFrameView* 238 ScreenCaptureNotificationUIViews::CreateNonClientFrameView( 239 views::Widget* widget) { 240 views::BubbleFrameView* frame = new views::BubbleFrameView( 241 gfx::Insets(kPadding, kPaddingLeft, kPadding, kPadding)); 242 SkColor color = widget->GetNativeTheme()->GetSystemColor( 243 ui::NativeTheme::kColorId_DialogBackground); 244 views::BubbleBorder* border = new views::BubbleBorder( 245 views::BubbleBorder::NONE, views::BubbleBorder::SMALL_SHADOW, color); 246 frame->SetBubbleBorder(border); 247 return frame; 248 } 249 250 string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const { 251 return text_; 252 } 253 254 bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const { 255 return false; 256 } 257 258 bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const { 259 return false; 260 } 261 262 void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender, 263 const ui::Event& event) { 264 NotifyStopped(); 265 } 266 267 void ScreenCaptureNotificationUIViews::NotifyStopped() { 268 if (!stop_callback_.is_null()) { 269 base::Closure callback = stop_callback_; 270 stop_callback_.Reset(); 271 callback.Run(); 272 } 273 } 274 275 } // namespace 276 277 scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create( 278 const string16& text) { 279 return scoped_ptr<ScreenCaptureNotificationUI>( 280 new ScreenCaptureNotificationUIViews(text)); 281 } 282