root/chrome/browser/ui/views/screen_capture_notification_ui_views.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_client_rect
  2. NonClientHitTest
  3. hide_link_
  4. OnStarted
  5. GetPreferredSize
  6. Layout
  7. DeleteDelegate
  8. GetContentsView
  9. CreateClientView
  10. CreateNonClientFrameView
  11. GetWindowTitle
  12. ShouldShowWindowTitle
  13. ShouldShowCloseButton
  14. CanActivate
  15. ButtonPressed
  16. LinkClicked
  17. NotifyStopped
  18. Create

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/screen_capture_notification_ui.h"

#include "ash/shell.h"
#include "chrome/app/chrome_dll_resource.h"
#include "chrome/browser/ui/views/chrome_views_export.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/hit_test.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/views/bubble/bubble_border.h"
#include "ui/views/bubble/bubble_frame_view.h"
#include "ui/views/controls/button/blue_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/link.h"
#include "ui/views/controls/link_listener.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/wm/core/shadow_types.h"

#if defined(OS_WIN)
#include "ui/views/win/hwnd_util.h"
#endif

namespace {

const int kMinimumWidth = 460;
const int kMaximumWidth = 1000;
const int kHorizontalMargin = 10;
const float kWindowAlphaValue = 0.85f;
const int kPaddingVertical = 5;
const int kPaddingHorizontal = 10;

namespace {

// A ClientView that overrides NonClientHitTest() so that the whole window area
// acts as a window caption, except a rect specified using set_client_rect().
// ScreenCaptureNotificationUIViews uses this class to make the notification bar
// draggable.
class NotificationBarClientView : public views::ClientView {
 public:
  NotificationBarClientView(views::Widget* widget, views::View* view)
      : views::ClientView(widget, view) {
  }
  virtual ~NotificationBarClientView() {}

  void set_client_rect(const gfx::Rect& rect) { rect_ = rect; }

  // views::ClientView overrides.
  virtual int NonClientHitTest(const gfx::Point& point) OVERRIDE  {
    if (!bounds().Contains(point))
      return HTNOWHERE;
    // The whole window is HTCAPTION, except the |rect_|.
    if (rect_.Contains(gfx::PointAtOffsetFromOrigin(point - bounds().origin())))
      return HTCLIENT;

    return HTCAPTION;
  }

 private:
  gfx::Rect rect_;

  DISALLOW_COPY_AND_ASSIGN(NotificationBarClientView);
};

}  // namespace

// ScreenCaptureNotificationUI implementation using Views.
class ScreenCaptureNotificationUIViews
    : public ScreenCaptureNotificationUI,
      public views::WidgetDelegateView,
      public views::ButtonListener,
      public views::LinkListener {
 public:
  explicit ScreenCaptureNotificationUIViews(const base::string16& text);
  virtual ~ScreenCaptureNotificationUIViews();

  // ScreenCaptureNotificationUI interface.
  virtual gfx::NativeViewId OnStarted(const base::Closure& stop_callback)
      OVERRIDE;

  // views::View overrides.
  virtual gfx::Size GetPreferredSize() OVERRIDE;
  virtual void Layout() OVERRIDE;

  // views::WidgetDelegateView overrides.
  virtual void DeleteDelegate() OVERRIDE;
  virtual views::View* GetContentsView() OVERRIDE;
  virtual views::ClientView* CreateClientView(views::Widget* widget) OVERRIDE;
  virtual views::NonClientFrameView* CreateNonClientFrameView(
      views::Widget* widget) OVERRIDE;
  virtual base::string16 GetWindowTitle() const OVERRIDE;
  virtual bool ShouldShowWindowTitle() const OVERRIDE;
  virtual bool ShouldShowCloseButton() const OVERRIDE;
  virtual bool CanActivate() const OVERRIDE;

  // views::ButtonListener interface.
  virtual void ButtonPressed(views::Button* sender,
                             const ui::Event& event) OVERRIDE;

  // views::LinkListener interface.
  virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;

 private:
  // Helper to call |stop_callback_|.
  void NotifyStopped();

  const base::string16 text_;
  base::Closure stop_callback_;
  NotificationBarClientView* client_view_;
  views::ImageView* gripper_;
  views::Label* label_;
  views::BlueButton* stop_button_;
  views::Link* hide_link_;

  DISALLOW_COPY_AND_ASSIGN(ScreenCaptureNotificationUIViews);
};

ScreenCaptureNotificationUIViews::ScreenCaptureNotificationUIViews(
    const base::string16& text)
    : text_(text),
      client_view_(NULL),
      gripper_(NULL),
      label_(NULL),
      stop_button_(NULL),
      hide_link_(NULL) {
  set_owned_by_client();

  gripper_ = new views::ImageView();
  gripper_->SetImage(
      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
          IDR_SCREEN_CAPTURE_NOTIFICATION_GRIP));
  AddChildView(gripper_);

  label_ = new views::Label();
  AddChildView(label_);

  base::string16 stop_text =
      l10n_util::GetStringUTF16(IDS_MEDIA_SCREEN_CAPTURE_NOTIFICATION_STOP);
  stop_button_ = new views::BlueButton(this, stop_text);
  AddChildView(stop_button_);

  // TODO(jiayl): IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON is used for the need to
  // merge to M34. Change it to a new IDS_ after the merge.
  hide_link_ = new views::Link(
      l10n_util::GetStringUTF16(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON));
  hide_link_->set_listener(this);
  hide_link_->SetUnderline(false);
  AddChildView(hide_link_);
}

ScreenCaptureNotificationUIViews::~ScreenCaptureNotificationUIViews() {
  stop_callback_.Reset();
  delete GetWidget();
}

gfx::NativeViewId ScreenCaptureNotificationUIViews::OnStarted(
    const base::Closure& stop_callback) {
  stop_callback_ = stop_callback;

  label_->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE);
  label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
  label_->SetText(text_);

  views::Widget* widget = new views::Widget;

  views::Widget::InitParams params;
  params.delegate = this;
  params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
  params.remove_standard_frame = true;
  params.keep_on_top = true;
  params.top_level = true;
  // Make sure can_activate is true so the window icon will show in the taskbar.
  params.can_activate = true;

#if defined(USE_ASH)
  // TODO(sergeyu): The notification bar must be shown on the monitor that's
  // being captured. Make sure it's always the case. Currently we always capture
  // the primary monitor.
  if (ash::Shell::HasInstance())
    params.context = ash::Shell::GetPrimaryRootWindow();
#endif

  widget->set_frame_type(views::Widget::FRAME_TYPE_FORCE_CUSTOM);
  widget->Init(params);
  widget->SetAlwaysOnTop(true);

  set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
      GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));

  gfx::Screen* screen = gfx::Screen::GetNativeScreen();
  // TODO(sergeyu): Move the notification to the display being captured when
  // per-display screen capture is supported.
  gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();

  // Place the bar in the center of the bottom of the display.
  gfx::Size size = widget->non_client_view()->GetPreferredSize();
  gfx::Rect bounds(
      work_area.x() + work_area.width() / 2 - size.width() / 2,
      work_area.y() + work_area.height() - size.height(),
      size.width(), size.height());
  widget->SetBounds(bounds);
  widget->SetOpacity(0xFF * kWindowAlphaValue);
  widget->Show();

#if defined(OS_WIN)
  return gfx::NativeViewId(views::HWNDForWidget(widget));
#else
  return 0;
#endif
}

gfx::Size ScreenCaptureNotificationUIViews::GetPreferredSize() {
  gfx::Size grip_size = gripper_->GetPreferredSize();
  gfx::Size label_size = label_->GetPreferredSize();
  gfx::Size stop_button_size = stop_button_->GetPreferredSize();
  gfx::Size hide_link_size = hide_link_->GetPreferredSize();
  int width = kHorizontalMargin * 3 + grip_size.width() + label_size.width() +
      stop_button_size.width() + hide_link_size.width();
  width = std::max(width, kMinimumWidth);
  width = std::min(width, kMaximumWidth);
  return gfx::Size(width, std::max(label_size.height(),
                                   std::max(hide_link_size.height(),
                                            stop_button_size.height())));
}

void ScreenCaptureNotificationUIViews::Layout() {
  gfx::Rect grip_rect(gripper_->GetPreferredSize());
  grip_rect.set_y((bounds().height() - grip_rect.height()) / 2);
  gripper_->SetBoundsRect(grip_rect);

  gfx::Rect stop_button_rect(stop_button_->GetPreferredSize());
  gfx::Rect hide_link_rect(hide_link_->GetPreferredSize());

  hide_link_rect.set_x(bounds().width() - hide_link_rect.width());
  hide_link_rect.set_y((bounds().height() - hide_link_rect.height()) / 2);
  hide_link_->SetBoundsRect(hide_link_rect);

  stop_button_rect.set_x(
      hide_link_rect.x() - kHorizontalMargin - stop_button_rect.width());
  stop_button_->SetBoundsRect(stop_button_rect);

  gfx::Rect label_rect;
  label_rect.set_x(grip_rect.right() + kHorizontalMargin);
  label_rect.set_width(
      stop_button_rect.x() - kHorizontalMargin - label_rect.x());
  label_rect.set_height(bounds().height());
  label_->SetBoundsRect(label_rect);

  client_view_->set_client_rect(gfx::Rect(
      stop_button_rect.x(), stop_button_rect.y(),
      stop_button_rect.width() + kHorizontalMargin + hide_link_rect.width(),
      std::max(stop_button_rect.height(), hide_link_rect.height())));
}

void ScreenCaptureNotificationUIViews::DeleteDelegate() {
  NotifyStopped();
}

views::View* ScreenCaptureNotificationUIViews::GetContentsView() {
  return this;
}

views::ClientView* ScreenCaptureNotificationUIViews::CreateClientView(
    views::Widget* widget) {
  DCHECK(!client_view_);
  client_view_ = new NotificationBarClientView(widget, this);
  return client_view_;
}

views::NonClientFrameView*
ScreenCaptureNotificationUIViews::CreateNonClientFrameView(
    views::Widget* widget) {
  views::BubbleFrameView* frame = new views::BubbleFrameView(
      gfx::Insets(kPaddingVertical,
                  kPaddingHorizontal,
                  kPaddingVertical,
                  kPaddingHorizontal));
  SkColor color = widget->GetNativeTheme()->GetSystemColor(
      ui::NativeTheme::kColorId_DialogBackground);
  frame->SetBubbleBorder(scoped_ptr<views::BubbleBorder>(
      new views::BubbleBorder(views::BubbleBorder::NONE,
                              views::BubbleBorder::SMALL_SHADOW,
                              color)));
  return frame;
}

base::string16 ScreenCaptureNotificationUIViews::GetWindowTitle() const {
  return text_;
}

bool ScreenCaptureNotificationUIViews::ShouldShowWindowTitle() const {
  return false;
}

bool ScreenCaptureNotificationUIViews::ShouldShowCloseButton() const {
  return false;
}

bool ScreenCaptureNotificationUIViews::CanActivate() const {
  // When the window is visible, it can be activated so the mouse clicks
  // can be sent to the window; when the window is minimized, we don't want it
  // to activate, otherwise it sometimes does not show properly on Windows.
  return GetWidget() && GetWidget()->IsVisible();
}

void ScreenCaptureNotificationUIViews::ButtonPressed(views::Button* sender,
                                                     const ui::Event& event) {
  NotifyStopped();
}

void ScreenCaptureNotificationUIViews::LinkClicked(views::Link* source,
                                                   int event_flags) {
  GetWidget()->Minimize();
}

void ScreenCaptureNotificationUIViews::NotifyStopped() {
  if (!stop_callback_.is_null()) {
    base::Closure callback = stop_callback_;
    stop_callback_.Reset();
    callback.Run();
  }
}

}  // namespace

scoped_ptr<ScreenCaptureNotificationUI> ScreenCaptureNotificationUI::Create(
    const base::string16& text) {
  return scoped_ptr<ScreenCaptureNotificationUI>(
      new ScreenCaptureNotificationUIViews(text));
}

/* [<][>][^][v][top][bottom][index][help] */