Commit 369cf0cd authored by xiyuan's avatar xiyuan Committed by Commit bot

Add a floating close button for arc custom notification

Needed because default notification close button is part of views
hierarchy and covered by the notification surface. This CL adds
a floating version of close button that sits on top of the notification
surface.

BUG=b/25816173
TBR=brettw@chromium.org

Review-Url: https://codereview.chromium.org/2093563007
Cr-Commit-Position: refs/heads/master@{#403196}
parent 03f33b29
......@@ -126,6 +126,7 @@ check_targets = [
"//ui/accessibility/*",
"//ui/android/*",
"//ui/app_list/*",
"//ui/arc/*",
"//ui/aura/*",
"//ui/aura_extra/*",
"//ui/base/*",
......
......@@ -12,6 +12,7 @@
'..',
],
'dependencies': [
'arc_base',
'arc_mojo_bindings',
'components.gyp:exo',
'components.gyp:onc_component',
......@@ -34,18 +35,12 @@
'sources': [
'arc/arc_bridge_bootstrap.cc',
'arc/arc_bridge_bootstrap.h',
'arc/arc_bridge_service.cc',
'arc/arc_bridge_service.h',
'arc/arc_bridge_service_impl.cc',
'arc/arc_bridge_service_impl.h',
'arc/arc_service.cc',
'arc/arc_service.h',
'arc/arc_service_manager.cc',
'arc/arc_service_manager.h',
'arc/audio/arc_audio_bridge.cc',
'arc/audio/arc_audio_bridge.h',
'arc/bitmap/bitmap_type_converters.cc',
'arc/bitmap/bitmap_type_converters.h',
'arc/bluetooth/arc_bluetooth_bridge.cc',
'arc/bluetooth/arc_bluetooth_bridge.h',
'arc/bluetooth/bluetooth_type_converters.cc',
......@@ -89,6 +84,39 @@
'arc/window_manager/arc_window_manager_bridge.h',
],
},
{
# GN version: //components/arc:arc_base
'target_name': 'arc_base',
'type': 'static_library',
'include_dirs': [
'..',
],
'dependencies': [
'../base/base.gyp:base',
'../chromeos/chromeos.gyp:chromeos',
],
'sources': [
'arc/arc_bridge_service.cc',
'arc/arc_bridge_service.h',
'arc/arc_service.cc',
'arc/arc_service.h',
],
},
{
# GN version: //components/arc:arc_bitmap
'target_name': 'arc_bitmap',
'type': 'static_library',
'include_dirs': [
'..',
],
'dependencies': [
'../skia/skia.gyp:skia',
],
'sources': [
'arc/bitmap/bitmap_type_converters.cc',
'arc/bitmap/bitmap_type_converters.h',
],
},
{
# GN version: //components/arc_test_support
'target_name': 'arc_test_support',
......
......@@ -9,18 +9,12 @@ static_library("arc") {
sources = [
"arc_bridge_bootstrap.cc",
"arc_bridge_bootstrap.h",
"arc_bridge_service.cc",
"arc_bridge_service.h",
"arc_bridge_service_impl.cc",
"arc_bridge_service_impl.h",
"arc_service.cc",
"arc_service.h",
"arc_service_manager.cc",
"arc_service_manager.h",
"audio/arc_audio_bridge.cc",
"audio/arc_audio_bridge.h",
"bitmap/bitmap_type_converters.cc",
"bitmap/bitmap_type_converters.h",
"bluetooth/arc_bluetooth_bridge.cc",
"bluetooth/arc_bluetooth_bridge.h",
"bluetooth/bluetooth_type_converters.cc",
......@@ -65,6 +59,7 @@ static_library("arc") {
]
deps = [
":arc_base",
"//ash:ash",
"//base",
"//chromeos",
......@@ -97,6 +92,39 @@ static_library("arc") {
]
}
static_library("arc_base") {
sources = [
"arc_bridge_service.cc",
"arc_bridge_service.h",
"arc_service.cc",
"arc_service.h",
]
deps = [
"//base",
"//chromeos",
]
public_deps = [
":arc_bindings",
]
}
static_library("arc_bitmap") {
sources = [
"bitmap/bitmap_type_converters.cc",
"bitmap/bitmap_type_converters.h",
]
deps = [
"//skia",
]
public_deps = [
":arc_bindings",
]
}
mojom("arc_bindings") {
sources = [
"common/app.mojom",
......@@ -149,6 +177,7 @@ static_library("arc_test_support") {
deps = [
":arc",
":arc_base",
":arc_bindings",
"//base",
"//mojo/common:common_base",
......
......@@ -10,7 +10,6 @@
#include "base/sequenced_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chromeos/chromeos_switches.h"
#include "components/arc/arc_bridge_service_impl.h"
namespace arc {
......
......@@ -20,12 +20,20 @@ static_library("arc") {
]
deps = [
"//ash:ash",
"//base",
"//components/arc:arc_base",
"//components/arc:arc_bindings",
"//components/arc:arc_bitmap",
"//components/exo:exo",
"//components/signin/core/account_id",
"//skia",
"//ui/base",
"//ui/gfx",
"//ui/message_center",
"//ui/resources",
"//ui/strings",
"//ui/views",
]
}
......@@ -42,6 +50,7 @@ test("ui_arc_unittests") {
"//base/test:test_support",
"//components/arc:arc_test_support",
"//mojo/edk/system",
"//testing/gtest",
"//ui/message_center:test_support",
]
}
......@@ -15,12 +15,20 @@
'../..',
],
'dependencies': [
'../base/ui_base.gyp:ui_base',
'../gfx/gfx.gyp:gfx_geometry',
'../message_center/message_center.gyp:message_center',
'../resources/ui_resources.gyp:ui_resources',
'../strings/ui_strings.gyp:ui_strings',
'../views/views.gyp:views',
'../../ash/ash.gyp:ash',
'../../base/base.gyp:base',
'../../url/url.gyp:url_lib',
'../../skia/skia.gyp:skia',
'../../components/components.gyp:arc_base',
'../../components/components.gyp:arc_bitmap',
'../../components/components.gyp:arc_mojo_bindings',
'../../components/components.gyp:exo',
'../../components/components.gyp:signin_core_account_id',
],
'sources': [
......
......@@ -2,7 +2,10 @@ include_rules = [
"+components/exo",
"+components/signin/core/account_id",
"+third_party/skia",
"+ui/base",
"+ui/gfx",
"+ui/message_center",
"+ui/resources",
"+ui/strings",
"+ui/views",
]
......@@ -20,13 +20,14 @@ constexpr char kNotifierId[] = "ARC_NOTIFICATION";
class ArcNotificationDelegate : public message_center::NotificationDelegate {
public:
ArcNotificationDelegate() {}
explicit ArcNotificationDelegate(ArcCustomNotificationItem* item)
: item_(item) {}
std::unique_ptr<views::View> CreateCustomContent() override {
if (!surface_)
return nullptr;
return base::MakeUnique<ArcCustomNotificationView>(surface_);
return base::MakeUnique<ArcCustomNotificationView>(item_, surface_);
}
void set_notification_surface(exo::NotificationSurface* surface) {
......@@ -37,6 +38,7 @@ class ArcNotificationDelegate : public message_center::NotificationDelegate {
// The destructor is private since this class is ref-counted.
~ArcNotificationDelegate() override {}
ArcCustomNotificationItem* const item_;
exo::NotificationSurface* surface_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ArcNotificationDelegate);
......@@ -57,6 +59,8 @@ ArcCustomNotificationItem::ArcCustomNotificationItem(
ArcCustomNotificationItem::~ArcCustomNotificationItem() {
if (ArcNotificationSurfaceManager::Get())
ArcNotificationSurfaceManager::Get()->RemoveObserver(this);
FOR_EACH_OBSERVER(Observer, observers_, OnItemDestroying());
}
void ArcCustomNotificationItem::UpdateWithArcNotificationData(
......@@ -83,7 +87,7 @@ void ArcCustomNotificationItem::UpdateWithArcNotificationData(
base::UTF8ToUTF16(data.message.get()), gfx::Image(),
base::UTF8ToUTF16("arc"), // display source
GURL(), // empty origin url, for system component
notifier_id, rich_data, new ArcNotificationDelegate()));
notifier_id, rich_data, new ArcNotificationDelegate(this)));
exo::NotificationSurface* surface =
ArcNotificationSurfaceManager::Get()->GetSurface(notification_key());
......@@ -91,6 +95,24 @@ void ArcCustomNotificationItem::UpdateWithArcNotificationData(
OnNotificationSurfaceAdded(surface);
else
ArcNotificationSurfaceManager::Get()->AddObserver(this);
pinned_ = rich_data.pinned;
FOR_EACH_OBSERVER(Observer, observers_, OnItemPinnedChanged());
}
void ArcCustomNotificationItem::CloseFromCloseButton() {
// Needs to manually remove notification from MessageCenter because
// the floating close button is not part of MessageCenter.
message_center()->RemoveNotification(notification_id(), true);
Close(true);
}
void ArcCustomNotificationItem::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void ArcCustomNotificationItem::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void ArcCustomNotificationItem::OnNotificationSurfaceAdded(
......
......@@ -6,6 +6,7 @@
#define UI_ARC_NOTIFICATION_ARC_CUSTOM_NOTIFICATION_ITEM_H_
#include "base/macros.h"
#include "base/observer_list.h"
#include "ui/arc/notification/arc_notification_item.h"
#include "ui/arc/notification/arc_notification_surface_manager.h"
......@@ -15,6 +16,18 @@ class ArcCustomNotificationItem
: public ArcNotificationItem,
public ArcNotificationSurfaceManager::Observer {
public:
class Observer {
public:
// Invoked when the notification data for this item has changed.
virtual void OnItemDestroying() = 0;
// Invoked when the pinned stated is changed.
virtual void OnItemPinnedChanged() = 0;
protected:
virtual ~Observer() = default;
};
ArcCustomNotificationItem(ArcNotificationManager* manager,
message_center::MessageCenter* message_center,
const std::string& notification_key,
......@@ -24,11 +37,21 @@ class ArcCustomNotificationItem
void UpdateWithArcNotificationData(
const mojom::ArcNotificationData& data) override;
void CloseFromCloseButton();
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
bool pinned() const { return pinned_; }
private:
// ArcNotificationSurfaceManager::Observer:
void OnNotificationSurfaceAdded(exo::NotificationSurface* surface) override;
void OnNotificationSurfaceRemoved(exo::NotificationSurface* surface) override;
bool pinned_ = false;
base::ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(ArcCustomNotificationItem);
};
......
......@@ -6,15 +6,64 @@
#include "components/exo/notification_surface.h"
#include "components/exo/surface.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/resources/grit/ui_resources.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/widget/widget.h"
namespace arc {
ArcCustomNotificationView::ArcCustomNotificationView(
ArcCustomNotificationItem* item,
exo::NotificationSurface* surface)
: surface_(surface) {}
: item_(item), surface_(surface) {
item_->AddObserver(this);
OnItemPinnedChanged();
}
ArcCustomNotificationView::~ArcCustomNotificationView() {
if (item_)
item_->RemoveObserver(this);
}
void ArcCustomNotificationView::CreateFloatingCloseButton() {
floating_close_button_ = new views::ImageButton(this);
floating_close_button_->set_background(
views::Background::CreateSolidBackground(SK_ColorTRANSPARENT));
floating_close_button_->SetBorder(
views::Border::CreateEmptyBorder(5, 5, 5, 5));
ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
floating_close_button_->SetImage(
views::CustomButton::STATE_NORMAL,
rb.GetImageSkiaNamed(IDR_NOTIFICATION_CLOSE));
floating_close_button_->SetImage(
views::CustomButton::STATE_HOVERED,
rb.GetImageSkiaNamed(IDR_NOTIFICATION_CLOSE_HOVER));
floating_close_button_->SetImage(
views::CustomButton::STATE_PRESSED,
rb.GetImageSkiaNamed(IDR_NOTIFICATION_CLOSE_PRESSED));
floating_close_button_->set_animate_on_state_change(false);
floating_close_button_->SetAccessibleName(l10n_util::GetStringUTF16(
IDS_MESSAGE_CENTER_CLOSE_NOTIFICATION_BUTTON_ACCESSIBLE_NAME));
views::Widget::InitParams params(views::Widget::InitParams::TYPE_CONTROL);
params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.parent = surface_->window();
ArcCustomNotificationView::~ArcCustomNotificationView() {}
floating_close_button_widget_.reset(new views::Widget);
floating_close_button_widget_->Init(params);
floating_close_button_widget_->SetContentsView(floating_close_button_);
floating_close_button_widget_->Show();
Layout();
}
void ArcCustomNotificationView::ViewHierarchyChanged(
const views::View::ViewHierarchyChangedDetails& details) {
......@@ -35,4 +84,38 @@ void ArcCustomNotificationView::ViewHierarchyChanged(
Attach(surface_->window());
}
void ArcCustomNotificationView::Layout() {
views::NativeViewHost::Layout();
if (!floating_close_button_widget_ || !GetWidget())
return;
gfx::Rect surface_local_bounds(surface_->window()->bounds().size());
gfx::Rect close_button_bounds(floating_close_button_->GetPreferredSize());
close_button_bounds.set_x(surface_local_bounds.right() -
close_button_bounds.width());
close_button_bounds.set_y(surface_local_bounds.y());
floating_close_button_widget_->SetBounds(close_button_bounds);
}
void ArcCustomNotificationView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (item_ && !item_->pinned() && sender == floating_close_button_) {
item_->CloseFromCloseButton();
}
}
void ArcCustomNotificationView::OnItemDestroying() {
item_->RemoveObserver(this);
item_ = nullptr;
}
void ArcCustomNotificationView::OnItemPinnedChanged() {
if (item_->pinned() && floating_close_button_widget_) {
floating_close_button_widget_.reset();
} else if (!item_->pinned() && !floating_close_button_widget_) {
CreateFloatingCloseButton();
}
}
} // namespace arc
......@@ -5,29 +5,59 @@
#ifndef UI_ARC_NOTIFICATION_ARC_CUSTOM_NOTIFICATION_VIEW_H_
#define UI_ARC_NOTIFICATION_ARC_CUSTOM_NOTIFICATION_VIEW_H_
#include <memory>
#include <string>
#include "base/macros.h"
#include "ui/arc/notification/arc_custom_notification_item.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/native/native_view_host.h"
namespace exo {
class NotificationSurface;
}
namespace views {
class ImageButton;
class Widget;
}
namespace arc {
class ArcCustomNotificationView : public views::NativeViewHost {
class ArcCustomNotificationView : public views::NativeViewHost,
public views::ButtonListener,
public ArcCustomNotificationItem::Observer {
public:
explicit ArcCustomNotificationView(exo::NotificationSurface* surface);
ArcCustomNotificationView(ArcCustomNotificationItem* item,
exo::NotificationSurface* surface);
~ArcCustomNotificationView() override;
private:
void CreateFloatingCloseButton();
// views::NativeViewHost
void ViewHierarchyChanged(
const ViewHierarchyChangedDetails& details) override;
void Layout() override;
// views::ButtonListener
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
// ArcCustomNotificationItem::Observer
void OnItemDestroying() override;
void OnItemPinnedChanged() override;
ArcCustomNotificationItem* item_;
exo::NotificationSurface* const surface_;
// A close button on top of NotificationSurface. Needed because the
// aura::Window of NotificationSurface is added after hosting widget's
// RootView thus standard notification close button is always below
// it.
std::unique_ptr<views::Widget> floating_close_button_widget_;
views::ImageButton* floating_close_button_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ArcCustomNotificationView);
};
......
......@@ -59,6 +59,7 @@ class ArcNotificationItem {
const AccountId& profile_id() const { return profile_id_; }
const std::string& notification_key() const { return notification_key_; }
const std::string& notification_id() const { return notification_id_; }
message_center::MessageCenter* message_center() { return message_center_; }
message_center::Notification* pending_notification() {
return notification_.get();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment