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"
7 #include "chrome/app/chrome_dll_resource.h"
8 #include "chrome/browser/ui/views/chrome_views_export.h"
9 #include "chrome/grit/generated_resources.h"
10 #include "grit/theme_resources.h"
11 #include "ui/base/hit_test.h"
12 #include "ui/base/l10n/l10n_util.h"
13 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/gfx/screen.h"
15 #include "ui/views/bubble/bubble_border.h"
16 #include "ui/views/bubble/bubble_frame_view.h"
17 #include "ui/views/controls/button/blue_button.h"
18 #include "ui/views/controls/image_view.h"
19 #include "ui/views/controls/link.h"
20 #include "ui/views/controls/link_listener.h"
21 #include "ui/views/view.h"
22 #include "ui/views/widget/widget.h"
23 #include "ui/views/widget/widget_delegate.h"
24 #include "ui/wm/core/shadow_types.h"
27 #include "ui/views/win/hwnd_util.h"
31 #include "ash/shell.h"
36 const int kMinimumWidth
= 460;
37 const int kMaximumWidth
= 1000;
38 const int kHorizontalMargin
= 10;
39 const float kWindowAlphaValue
= 0.85f
;
40 const int kPaddingVertical
= 5;
41 const int kPaddingHorizontal
= 10;
45 // A ClientView that overrides NonClientHitTest() so that the whole window area
46 // acts as a window caption, except a rect specified using set_client_rect().
47 // ScreenCaptureNotificationUIViews uses this class to make the notification bar
49 class NotificationBarClientView
: public views::ClientView
{
51 NotificationBarClientView(views::Widget
* widget
, views::View
* view
)
52 : views::ClientView(widget
, view
) {
54 ~NotificationBarClientView() override
{}
56 void set_client_rect(const gfx::Rect
& rect
) { rect_
= rect
; }
58 // views::ClientView overrides.
59 int NonClientHitTest(const gfx::Point
& point
) override
{
60 if (!bounds().Contains(point
))
62 // The whole window is HTCAPTION, except the |rect_|.
63 if (rect_
.Contains(gfx::PointAtOffsetFromOrigin(point
- bounds().origin())))
72 DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView
);
77 // ScreenCaptureNotificationUI implementation using Views.
78 class ScreenCaptureNotificationUIViews
79 : public ScreenCaptureNotificationUI
,
80 public views::WidgetDelegateView
,
81 public views::ButtonListener
,
82 public views::LinkListener
{
84 explicit ScreenCaptureNotificationUIViews(const base::string16
& text
);
85 ~ScreenCaptureNotificationUIViews() override
;
87 // ScreenCaptureNotificationUI interface.
88 gfx::NativeViewId
OnStarted(const base::Closure
& stop_callback
) override
;
90 // views::View overrides.
91 gfx::Size
GetPreferredSize() const override
;
92 void Layout() override
;
94 // views::WidgetDelegateView overrides.
95 void DeleteDelegate() override
;
96 views::View
* GetContentsView() override
;
97 views::ClientView
* CreateClientView(views::Widget
* widget
) override
;
98 views::NonClientFrameView
* CreateNonClientFrameView(
99 views::Widget
* widget
) override
;
100 base::string16
GetWindowTitle() const override
;
101 bool ShouldShowWindowTitle() const override
;
102 bool ShouldShowCloseButton() const override
;
103 bool CanActivate() const override
;
105 // views::ButtonListener interface.
106 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
108 // views::LinkListener interface.
109 void LinkClicked(views::Link
* source
, int event_flags
) override
;
112 // Helper to call |stop_callback_|.
113 void NotifyStopped();
115 const base::string16 text_
;
116 base::Closure stop_callback_
;
117 NotificationBarClientView
* client_view_
;
118 views::ImageView
* gripper_
;
119 views::Label
* label_
;
120 views::BlueButton
* stop_button_
;
121 views::Link
* hide_link_
;
123 DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews
);
126 ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
127 const base::string16
& text
)
134 set_owned_by_client();
136 gripper_
= new views::ImageView();
138 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
139 IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP
));
140 AddChildView(gripper_
);
142 label_
= new views::Label();
143 AddChildView(label_
);
145 base::string16 stop_text
=
146 l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP
);
147 stop_button_
= new views::BlueButton(this, stop_text
);
148 AddChildView(stop_button_
);
150 // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to
151 // merge to M34. Change it to a new IDS_ after the merge.
152 hide_link_
= new views::Link(
153 l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON
));
154 hide_link_
->set_listener(this);
155 hide_link_
->SetUnderline(false);
156 AddChildView(hide_link_
);
159 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
160 stop_callback_
.Reset();
164 gfx::NativeViewId
ScreenCaptureNotificationUIViews::OnStarted(
165 const base::Closure
& stop_callback
) {
166 stop_callback_
= stop_callback
;
168 label_
->SetElideBehavior(gfx::ELIDE_MIDDLE
);
169 label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
170 label_
->SetText(text_
);
172 views::Widget
* widget
= new views::Widget
;
174 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
175 params
.delegate
= this;
176 params
.ownership
= views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
177 params
.opacity
= views::Widget::InitParams::TRANSLUCENT_WINDOW
;
178 params
.remove_standard_frame
= true;
179 params
.keep_on_top
= 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
));