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