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.
5 #include "chrome/browser/ui/screen_capture_notification_ui.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"
28 #include "ui/views/win/hwnd_util.h"
33 const int kMinimumWidth
= 460;
34 const int kMaximumWidth
= 1000;
35 const int kHorizontalMargin
= 10;
36 const float kWindowAlphaValue
= 0.85f
;
37 const int kPaddingVertical
= 5;
38 const int kPaddingHorizontal
= 10;
42 // A ClientView that overrides NonClientHitTest() so that the whole window area
43 // acts as a window caption, except a rect specified using set_client_rect().
44 // ScreenCaptureNotificationUIViews uses this class to make the notification bar
46 class NotificationBarClientView
: public views::ClientView
{
48 NotificationBarClientView(views::Widget
* widget
, views::View
* view
)
49 : views::ClientView(widget
, view
) {
51 virtual ~NotificationBarClientView() {}
53 void set_client_rect(const gfx::Rect
& rect
) { rect_
= rect
; }
55 // views::ClientView overrides.
56 virtual int NonClientHitTest(const gfx::Point
& point
) OVERRIDE
{
57 if (!bounds().Contains(point
))
59 // The whole window is HTCAPTION, except the |rect_|.
60 if (rect_
.Contains(gfx::PointAtOffsetFromOrigin(point
- bounds().origin())))
69 DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView
);
74 // ScreenCaptureNotificationUI implementation using Views.
75 class ScreenCaptureNotificationUIViews
76 : public ScreenCaptureNotificationUI
,
77 public views::WidgetDelegateView
,
78 public views::ButtonListener
,
79 public views::LinkListener
{
81 explicit ScreenCaptureNotificationUIViews(const base::string16
& text
);
82 virtual ~ScreenCaptureNotificationUIViews();
84 // ScreenCaptureNotificationUI interface.
85 virtual gfx::NativeViewId
OnStarted(const base::Closure
& stop_callback
)
88 // views::View overrides.
89 virtual gfx::Size
GetPreferredSize() const OVERRIDE
;
90 virtual void Layout() OVERRIDE
;
92 // views::WidgetDelegateView overrides.
93 virtual void DeleteDelegate() OVERRIDE
;
94 virtual views::View
* GetContentsView() OVERRIDE
;
95 virtual views::ClientView
* CreateClientView(views::Widget
* widget
) OVERRIDE
;
96 virtual views::NonClientFrameView
* CreateNonClientFrameView(
97 views::Widget
* widget
) OVERRIDE
;
98 virtual base::string16
GetWindowTitle() const OVERRIDE
;
99 virtual bool ShouldShowWindowTitle() const OVERRIDE
;
100 virtual bool ShouldShowCloseButton() const OVERRIDE
;
101 virtual bool CanActivate() const OVERRIDE
;
103 // views::ButtonListener interface.
104 virtual void ButtonPressed(views::Button
* sender
,
105 const ui::Event
& event
) OVERRIDE
;
107 // views::LinkListener interface.
108 virtual void LinkClicked(views::Link
* source
, int event_flags
) OVERRIDE
;
111 // Helper to call |stop_callback_|.
112 void NotifyStopped();
114 const base::string16 text_
;
115 base::Closure stop_callback_
;
116 NotificationBarClientView
* client_view_
;
117 views::ImageView
* gripper_
;
118 views::Label
* label_
;
119 views::BlueButton
* stop_button_
;
120 views::Link
* hide_link_
;
122 DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews
);
125 ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
126 const base::string16
& text
)
133 set_owned_by_client();
135 gripper_
= new views::ImageView();
137 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
138 IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP
));
139 AddChildView(gripper_
);
141 label_
= new views::Label();
142 AddChildView(label_
);
144 base::string16 stop_text
=
145 l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP
);
146 stop_button_
= new views::BlueButton(this, stop_text
);
147 AddChildView(stop_button_
);
149 // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to
150 // merge to M34. Change it to a new IDS_ after the merge.
151 hide_link_
= new views::Link(
152 l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON
));
153 hide_link_
->set_listener(this);
154 hide_link_
->SetUnderline(false);
155 AddChildView(hide_link_
);
158 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
159 stop_callback_
.Reset();
163 gfx::NativeViewId
ScreenCaptureNotificationUIViews::OnStarted(
164 const base::Closure
& stop_callback
) {
165 stop_callback_
= stop_callback
;
167 label_
->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE
);
168 label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
169 label_
->SetText(text_
);
171 views::Widget
* widget
= new views::Widget
;
173 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
174 params
.delegate
= this;
175 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
176 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
177 params
.remove_standard_frame
= true;
178 params
.keep_on_top
= true;
179 params
.top_level
= true;
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();
189 widget
->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM
);
190 widget
->Init(params
);
191 widget
->SetAlwaysOnTop(true);
193 set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
194 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground
)));
196 gfx::Screen
* screen
= gfx::Screen::GetNativeScreen();
197 // TODO(sergeyu): Move the notification to the display being captured when
198 // per-display screen capture is supported.
199 gfx::Rect work_area
= screen
->GetPrimaryDisplay().work_area();
201 // Place the bar in the center of the bottom of the display.
202 gfx::Size size
= widget
->non_client_view()->GetPreferredSize();
204 work_area
.x() + work_area
.width() / 2 - size
.width() / 2,
205 work_area
.y() + work_area
.height() - size
.height(),
206 size
.width(), size
.height());
207 widget
->SetBounds(bounds
);
209 // This has to be called after Show() to have effect.
210 widget
->SetOpacity(0xFF * kWindowAlphaValue
);
213 return gfx::NativeViewId(views::HWNDForWidget(widget
));
219 gfx::Size
ScreenCaptureNotificationUIViews::GetPreferredSize() const {
220 gfx::Size grip_size
= gripper_
->GetPreferredSize();
221 gfx::Size label_size
= label_
->GetPreferredSize();
222 gfx::Size stop_button_size
= stop_button_
->GetPreferredSize();
223 gfx::Size hide_link_size
= hide_link_
->GetPreferredSize();
224 int width
= kHorizontalMargin
* 3 + grip_size
.width() + label_size
.width() +
225 stop_button_size
.width() + hide_link_size
.width();
226 width
= std::max(width
, kMinimumWidth
);
227 width
= std::min(width
, kMaximumWidth
);
228 return gfx::Size(width
, std::max(label_size
.height(),
229 std::max(hide_link_size
.height(),
230 stop_button_size
.height())));
233 void ScreenCaptureNotificationUIViews::Layout() {
234 gfx::Rect
grip_rect(gripper_
->GetPreferredSize());
235 grip_rect
.set_y((bounds().height() - grip_rect
.height()) / 2);
236 gripper_
->SetBoundsRect(grip_rect
);
238 gfx::Rect
stop_button_rect(stop_button_
->GetPreferredSize());
239 gfx::Rect
hide_link_rect(hide_link_
->GetPreferredSize());
241 hide_link_rect
.set_x(bounds().width() - hide_link_rect
.width());
242 hide_link_rect
.set_y((bounds().height() - hide_link_rect
.height()) / 2);
243 hide_link_
->SetBoundsRect(hide_link_rect
);
245 stop_button_rect
.set_x(
246 hide_link_rect
.x() - kHorizontalMargin
- stop_button_rect
.width());
247 stop_button_
->SetBoundsRect(stop_button_rect
);
249 gfx::Rect label_rect
;
250 label_rect
.set_x(grip_rect
.right() + kHorizontalMargin
);
251 label_rect
.set_width(
252 stop_button_rect
.x() - kHorizontalMargin
- label_rect
.x());
253 label_rect
.set_height(bounds().height());
254 label_
->SetBoundsRect(label_rect
);
256 client_view_
->set_client_rect(gfx::Rect(
257 stop_button_rect
.x(), stop_button_rect
.y(),
258 stop_button_rect
.width() + kHorizontalMargin
+ hide_link_rect
.width(),
259 std::max(stop_button_rect
.height(), hide_link_rect
.height())));
262 void ScreenCaptureNotificationUIViews::DeleteDelegate() {
266 views::View
* ScreenCaptureNotificationUIViews::GetContentsView() {
270 views::ClientView
* ScreenCaptureNotificationUIViews::CreateClientView(
271 views::Widget
* widget
) {
272 DCHECK(!client_view_
);
273 client_view_
= new NotificationBarClientView(widget
, this);
277 views::NonClientFrameView
*
278 ScreenCaptureNotificationUIViews::CreateNonClientFrameView(
279 views::Widget
* widget
) {
280 views::BubbleFrameView
* frame
= new views::BubbleFrameView(
281 gfx::Insets(kPaddingVertical
,
284 kPaddingHorizontal
));
285 SkColor color
= widget
->GetNativeTheme()->GetSystemColor(
286 ui::NativeTheme::kColorId_DialogBackground
);
287 frame
->SetBubbleBorder(scoped_ptr
<views::BubbleBorder
>(
288 new views::BubbleBorder(views::BubbleBorder::NONE
,
289 views::BubbleBorder::SMALL_SHADOW
,
294 base::string16
ScreenCaptureNotificationUIViews::GetWindowTitle() const {
298 bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
302 bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
306 bool ScreenCaptureNotificationUIViews::CanActivate() const {
307 // When the window is visible, it can be activated so the mouse clicks
308 // can be sent to the window; when the window is minimized, we don't want it
309 // to activate, otherwise it sometimes does not show properly on Windows.
310 return GetWidget() && GetWidget()->IsVisible();
313 void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button
* sender
,
314 const ui::Event
& event
) {
318 void ScreenCaptureNotificationUIViews::LinkClicked(views::Link
* source
,
320 GetWidget()->Minimize();
323 void ScreenCaptureNotificationUIViews::NotifyStopped() {
324 if (!stop_callback_
.is_null()) {
325 base::Closure callback
= stop_callback_
;
326 stop_callback_
.Reset();
333 scoped_ptr
<ScreenCaptureNotificationUI
> ScreenCaptureNotificationUI::Create(
334 const base::string16
& text
) {
335 return scoped_ptr
<ScreenCaptureNotificationUI
>(
336 new ScreenCaptureNotificationUIViews(text
));