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 "chrome/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 ~NotificationBarClientView() override
{}
53 void set_client_rect(const gfx::Rect
& rect
) { rect_
= rect
; }
55 // views::ClientView overrides.
56 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 ~ScreenCaptureNotificationUIViews() override
;
84 // ScreenCaptureNotificationUI interface.
85 gfx::NativeViewId
OnStarted(const base::Closure
& stop_callback
) override
;
87 // views::View overrides.
88 gfx::Size
GetPreferredSize() const override
;
89 void Layout() override
;
91 // views::WidgetDelegateView overrides.
92 void DeleteDelegate() override
;
93 views::View
* GetContentsView() override
;
94 views::ClientView
* CreateClientView(views::Widget
* widget
) override
;
95 views::NonClientFrameView
* CreateNonClientFrameView(
96 views::Widget
* widget
) override
;
97 base::string16
GetWindowTitle() const override
;
98 bool ShouldShowWindowTitle() const override
;
99 bool ShouldShowCloseButton() const override
;
100 bool CanActivate() const override
;
102 // views::ButtonListener interface.
103 void ButtonPressed(views::Button
* sender
, const ui::Event
& event
) override
;
105 // views::LinkListener interface.
106 void LinkClicked(views::Link
* source
, int event_flags
) override
;
109 // Helper to call |stop_callback_|.
110 void NotifyStopped();
112 const base::string16 text_
;
113 base::Closure stop_callback_
;
114 NotificationBarClientView
* client_view_
;
115 views::ImageView
* gripper_
;
116 views::Label
* label_
;
117 views::BlueButton
* stop_button_
;
118 views::Link
* hide_link_
;
120 DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews
);
123 ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
124 const base::string16
& text
)
131 set_owned_by_client();
133 gripper_
= new views::ImageView();
135 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
136 IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP
));
137 AddChildView(gripper_
);
139 label_
= new views::Label();
140 AddChildView(label_
);
142 base::string16 stop_text
=
143 l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP
);
144 stop_button_
= new views::BlueButton(this, stop_text
);
145 AddChildView(stop_button_
);
147 // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to
148 // merge to M34. Change it to a new IDS_ after the merge.
149 hide_link_
= new views::Link(
150 l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON
));
151 hide_link_
->set_listener(this);
152 hide_link_
->SetUnderline(false);
153 AddChildView(hide_link_
);
156 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
157 stop_callback_
.Reset();
161 gfx::NativeViewId
ScreenCaptureNotificationUIViews::OnStarted(
162 const base::Closure
& stop_callback
) {
163 stop_callback_
= stop_callback
;
165 label_
->SetElideBehavior(gfx::ELIDE_MIDDLE
);
166 label_
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
167 label_
->SetText(text_
);
169 views::Widget
* widget
= new views::Widget
;
171 views::Widget::InitParams
params(views::Widget::InitParams::TYPE_WINDOW
);
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;
178 // TODO(sergeyu): The notification bar must be shown on the monitor that's
179 // being captured. Make sure it's always the case. Currently we always capture
180 // the primary monitor.
181 if (ash::Shell::HasInstance())
182 params
.context
= ash::Shell::GetPrimaryRootWindow();
184 widget
->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM
);
185 widget
->Init(params
);
186 widget
->SetAlwaysOnTop(true);
188 set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
189 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground
)));
191 gfx::Screen
* screen
= gfx::Screen::GetNativeScreen();
192 // TODO(sergeyu): Move the notification to the display being captured when
193 // per-display screen capture is supported.
194 gfx::Rect work_area
= screen
->GetPrimaryDisplay().work_area();
196 // Place the bar in the center of the bottom of the display.
197 gfx::Size size
= widget
->non_client_view()->GetPreferredSize();
199 work_area
.x() + work_area
.width() / 2 - size
.width() / 2,
200 work_area
.y() + work_area
.height() - size
.height(),
201 size
.width(), size
.height());
202 widget
->SetBounds(bounds
);
204 // This has to be called after Show() to have effect.
205 widget
->SetOpacity(0xFF * kWindowAlphaValue
);
208 return gfx::NativeViewId(views::HWNDForWidget(widget
));
214 gfx::Size
ScreenCaptureNotificationUIViews::GetPreferredSize() const {
215 gfx::Size grip_size
= gripper_
->GetPreferredSize();
216 gfx::Size label_size
= label_
->GetPreferredSize();
217 gfx::Size stop_button_size
= stop_button_
->GetPreferredSize();
218 gfx::Size hide_link_size
= hide_link_
->GetPreferredSize();
219 int width
= kHorizontalMargin
* 3 + grip_size
.width() + label_size
.width() +
220 stop_button_size
.width() + hide_link_size
.width();
221 width
= std::max(width
, kMinimumWidth
);
222 width
= std::min(width
, kMaximumWidth
);
223 return gfx::Size(width
, std::max(label_size
.height(),
224 std::max(hide_link_size
.height(),
225 stop_button_size
.height())));
228 void ScreenCaptureNotificationUIViews::Layout() {
229 gfx::Rect
grip_rect(gripper_
->GetPreferredSize());
230 grip_rect
.set_y((bounds().height() - grip_rect
.height()) / 2);
231 gripper_
->SetBoundsRect(grip_rect
);
233 gfx::Rect
stop_button_rect(stop_button_
->GetPreferredSize());
234 gfx::Rect
hide_link_rect(hide_link_
->GetPreferredSize());
236 hide_link_rect
.set_x(bounds().width() - hide_link_rect
.width());
237 hide_link_rect
.set_y((bounds().height() - hide_link_rect
.height()) / 2);
238 hide_link_
->SetBoundsRect(hide_link_rect
);
240 stop_button_rect
.set_x(
241 hide_link_rect
.x() - kHorizontalMargin
- stop_button_rect
.width());
242 stop_button_
->SetBoundsRect(stop_button_rect
);
244 gfx::Rect label_rect
;
245 label_rect
.set_x(grip_rect
.right() + kHorizontalMargin
);
246 label_rect
.set_width(
247 stop_button_rect
.x() - kHorizontalMargin
- label_rect
.x());
248 label_rect
.set_height(bounds().height());
249 label_
->SetBoundsRect(label_rect
);
251 client_view_
->set_client_rect(gfx::Rect(
252 stop_button_rect
.x(), stop_button_rect
.y(),
253 stop_button_rect
.width() + kHorizontalMargin
+ hide_link_rect
.width(),
254 std::max(stop_button_rect
.height(), hide_link_rect
.height())));
257 void ScreenCaptureNotificationUIViews::DeleteDelegate() {
261 views::View
* ScreenCaptureNotificationUIViews::GetContentsView() {
265 views::ClientView
* ScreenCaptureNotificationUIViews::CreateClientView(
266 views::Widget
* widget
) {
267 DCHECK(!client_view_
);
268 client_view_
= new NotificationBarClientView(widget
, this);
272 views::NonClientFrameView
*
273 ScreenCaptureNotificationUIViews::CreateNonClientFrameView(
274 views::Widget
* widget
) {
275 views::BubbleFrameView
* frame
= new views::BubbleFrameView(
276 gfx::Insets(kPaddingVertical
,
279 kPaddingHorizontal
));
280 SkColor color
= widget
->GetNativeTheme()->GetSystemColor(
281 ui::NativeTheme::kColorId_DialogBackground
);
282 frame
->SetBubbleBorder(scoped_ptr
<views::BubbleBorder
>(
283 new views::BubbleBorder(views::BubbleBorder::NONE
,
284 views::BubbleBorder::SMALL_SHADOW
,
289 base::string16
ScreenCaptureNotificationUIViews::GetWindowTitle() const {
293 bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
297 bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
301 bool ScreenCaptureNotificationUIViews::CanActivate() const {
302 // When the window is visible, it can be activated so the mouse clicks
303 // can be sent to the window; when the window is minimized, we don't want it
304 // to activate, otherwise it sometimes does not show properly on Windows.
305 return GetWidget() && GetWidget()->IsVisible();
308 void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button
* sender
,
309 const ui::Event
& event
) {
313 void ScreenCaptureNotificationUIViews::LinkClicked(views::Link
* source
,
315 GetWidget()->Minimize();
318 void ScreenCaptureNotificationUIViews::NotifyStopped() {
319 if (!stop_callback_
.is_null()) {
320 base::Closure callback
= stop_callback_
;
321 stop_callback_
.Reset();
328 scoped_ptr
<ScreenCaptureNotificationUI
> ScreenCaptureNotificationUI::Create(
329 const base::string16
& text
) {
330 return scoped_ptr
<ScreenCaptureNotificationUI
>(
331 new ScreenCaptureNotificationUIViews(text
));