Home | History | Annotate | Download | only in views
      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 "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"
     26 
     27 #if defined(OS_WIN)
     28 #include "ui/views/win/hwnd_util.h"
     29 #endif
     30 
     31 namespace {
     32 
     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;
     39 
     40 namespace {
     41 
     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
     45 // draggable.
     46 class NotificationBarClientView : public views::ClientView {
     47  public:
     48   NotificationBarClientView(views::Widget* widget, views::View* view)
     49       : views::ClientView(widget, view) {
     50   }
     51   virtual ~NotificationBarClientView() {}
     52 
     53   void set_client_rect(const gfx::Rect& rect) { rect_ = rect; }
     54 
     55   // views::ClientView overrides.
     56   virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE  {
     57     if (!bounds().Contains(point))
     58       return HTNOWHERE;
     59     // The whole window is HTCAPTION, except the |rect_|.
     60     if (rect_.Contains(gfx::PointAtOffsetFromOrigin(point - bounds().origin())))
     61       return HTCLIENT;
     62 
     63     return HTCAPTION;
     64   }
     65 
     66  private:
     67   gfx::Rect rect_;
     68 
     69   DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView);
     70 };
     71 
     72 }  // namespace
     73 
     74 // ScreenCaptureNotificationUI implementation using Views.
     75 class ScreenCaptureNotificationUIViews
     76     : public ScreenCaptureNotificationUI,
     77       public views::WidgetDelegateView,
     78       public views::ButtonListener,
     79       public views::LinkListener {
     80  public:
     81   explicit ScreenCaptureNotificationUIViews(const base::string16& text);
     82   virtual ~ScreenCaptureNotificationUIViews();
     83 
     84   // ScreenCaptureNotificationUI interface.
     85   virtual gfx::NativeViewId OnStarted(const base::Closure& stop_callback)
     86       OVERRIDE;
     87 
     88   // views::View overrides.
     89   virtual gfx::Size GetPreferredSize() const OVERRIDE;
     90   virtual void Layout() OVERRIDE;
     91 
     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;
    102 
    103   // views::ButtonListener interface.
    104   virtual void ButtonPressed(views::Button* sender,
    105                              const ui::Event& event) OVERRIDE;
    106 
    107   // views::LinkListener interface.
    108   virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
    109 
    110  private:
    111   // Helper to call |stop_callback_|.
    112   void NotifyStopped();
    113 
    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_;
    121 
    122   DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews);
    123 };
    124 
    125 ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
    126     const base::string16& text)
    127     : text_(text),
    128       client_view_(NULL),
    129       gripper_(NULL),
    130       label_(NULL),
    131       stop_button_(NULL),
    132       hide_link_(NULL) {
    133   set_owned_by_client();
    134 
    135   gripper_ = new views::ImageView();
    136   gripper_->SetImage(
    137       ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
    138           IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP));
    139   AddChildView(gripper_);
    140 
    141   label_ = new views::Label();
    142   AddChildView(label_);
    143 
    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_);
    148 
    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_);
    156 }
    157 
    158 ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
    159   stop_callback_.Reset();
    160   delete GetWidget();
    161 }
    162 
    163 gfx::NativeViewId ScreenCaptureNotificationUIViews::OnStarted(
    164     const base::Closure& stop_callback) {
    165   stop_callback_ = stop_callback;
    166 
    167   label_->SetElideBehavior(gfx::ELIDE_MIDDLE);
    168   label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    169   label_->SetText(text_);
    170 
    171   views::Widget* widget = new views::Widget;
    172 
    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 
    180   // TODO(sergeyu): The notification bar must be shown on the monitor that's
    181   // being captured. Make sure it's always the case. Currently we always capture
    182   // the primary monitor.
    183   if (ash::Shell::HasInstance())
    184     params.context = ash::Shell::GetPrimaryRootWindow();
    185 
    186   widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
    187   widget->Init(params);
    188   widget->SetAlwaysOnTop(true);
    189 
    190   set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
    191       GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
    192 
    193   gfx::Screen* screen = gfx::Screen::GetNativeScreen();
    194   // TODO(sergeyu): Move the notification to the display being captured when
    195   // per-display screen capture is supported.
    196   gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
    197 
    198   // Place the bar in the center of the bottom of the display.
    199   gfx::Size size = widget->non_client_view()->GetPreferredSize();
    200   gfx::Rect bounds(
    201       work_area.x() + work_area.width() / 2 - size.width() / 2,
    202       work_area.y() + work_area.height() - size.height(),
    203       size.width(), size.height());
    204   widget->SetBounds(bounds);
    205   widget->Show();
    206   // This has to be called after Show() to have effect.
    207   widget->SetOpacity(0xFF * kWindowAlphaValue);
    208 
    209 #if defined(OS_WIN)
    210   return gfx::NativeViewId(views::HWNDForWidget(widget));
    211 #else
    212   return 0;
    213 #endif
    214 }
    215 
    216 gfx::Size ScreenCaptureNotificationUIViews::GetPreferredSize() const {
    217   gfx::Size grip_size = gripper_->GetPreferredSize();
    218   gfx::Size label_size = label_->GetPreferredSize();
    219   gfx::Size stop_button_size = stop_button_->GetPreferredSize();
    220   gfx::Size hide_link_size = hide_link_->GetPreferredSize();
    221   int width = kHorizontalMargin * 3 + grip_size.width() + label_size.width() +
    222       stop_button_size.width() + hide_link_size.width();
    223   width = std::max(width, kMinimumWidth);
    224   width = std::min(width, kMaximumWidth);
    225   return gfx::Size(width, std::max(label_size.height(),
    226                                    std::max(hide_link_size.height(),
    227                                             stop_button_size.height())));
    228 }
    229 
    230 void ScreenCaptureNotificationUIViews::Layout() {
    231   gfx::Rect grip_rect(gripper_->GetPreferredSize());
    232   grip_rect.set_y((bounds().height() - grip_rect.height()) / 2);
    233   gripper_->SetBoundsRect(grip_rect);
    234 
    235   gfx::Rect stop_button_rect(stop_button_->GetPreferredSize());
    236   gfx::Rect hide_link_rect(hide_link_->GetPreferredSize());
    237 
    238   hide_link_rect.set_x(bounds().width() - hide_link_rect.width());
    239   hide_link_rect.set_y((bounds().height() - hide_link_rect.height()) / 2);
    240   hide_link_->SetBoundsRect(hide_link_rect);
    241 
    242   stop_button_rect.set_x(
    243       hide_link_rect.x() - kHorizontalMargin - stop_button_rect.width());
    244   stop_button_->SetBoundsRect(stop_button_rect);
    245 
    246   gfx::Rect label_rect;
    247   label_rect.set_x(grip_rect.right() + kHorizontalMargin);
    248   label_rect.set_width(
    249       stop_button_rect.x() - kHorizontalMargin - label_rect.x());
    250   label_rect.set_height(bounds().height());
    251   label_->SetBoundsRect(label_rect);
    252 
    253   client_view_->set_client_rect(gfx::Rect(
    254       stop_button_rect.x(), stop_button_rect.y(),
    255       stop_button_rect.width() + kHorizontalMargin + hide_link_rect.width(),
    256       std::max(stop_button_rect.height(), hide_link_rect.height())));
    257 }
    258 
    259 void ScreenCaptureNotificationUIViews::DeleteDelegate() {
    260   NotifyStopped();
    261 }
    262 
    263 views::View* ScreenCaptureNotificationUIViews::GetContentsView() {
    264   return this;
    265 }
    266 
    267 views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView(
    268     views::Widget* widget) {
    269   DCHECK(!client_view_);
    270   client_view_ = new NotificationBarClientView(widget, this);
    271   return client_view_;
    272 }
    273 
    274 views::NonClientFrameView*
    275 ScreenCaptureNotificationUIViews::CreateNonClientFrameView(
    276     views::Widget* widget) {
    277   views::BubbleFrameView* frame = new views::BubbleFrameView(
    278       gfx::Insets(kPaddingVertical,
    279                   kPaddingHorizontal,
    280                   kPaddingVertical,
    281                   kPaddingHorizontal));
    282   SkColor color = widget->GetNativeTheme()->GetSystemColor(
    283       ui::NativeTheme::kColorId_DialogBackground);
    284   frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(
    285       new views::BubbleBorder(views::BubbleBorder::NONE,
    286                               views::BubbleBorder::SMALL_SHADOW,
    287                               color)));
    288   return frame;
    289 }
    290 
    291 base::string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const {
    292   return text_;
    293 }
    294 
    295 bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
    296   return false;
    297 }
    298 
    299 bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
    300   return false;
    301 }
    302 
    303 bool ScreenCaptureNotificationUIViews::CanActivate() const {
    304   // When the window is visible, it can be activated so the mouse clicks
    305   // can be sent to the window; when the window is minimized, we don't want it
    306   // to activate, otherwise it sometimes does not show properly on Windows.
    307   return GetWidget() && GetWidget()->IsVisible();
    308 }
    309 
    310 void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender,
    311                                                      const ui::Event& event) {
    312   NotifyStopped();
    313 }
    314 
    315 void ScreenCaptureNotificationUIViews::LinkClicked(views::Link* source,
    316                                                    int event_flags) {
    317   GetWidget()->Minimize();
    318 }
    319 
    320 void ScreenCaptureNotificationUIViews::NotifyStopped() {
    321   if (!stop_callback_.is_null()) {
    322     base::Closure callback = stop_callback_;
    323     stop_callback_.Reset();
    324     callback.Run();
    325   }
    326 }
    327 
    328 }  // namespace
    329 
    330 scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create(
    331     const base::string16& text) {
    332   return scoped_ptr<ScreenCaptureNotificationUI>(
    333       new ScreenCaptureNotificationUIViews(text));
    334 }
    335