Chromium Code Reviews| Index: ui/arc/notification/arc_custom_notification_view.cc |
| diff --git a/ui/arc/notification/arc_custom_notification_view.cc b/ui/arc/notification/arc_custom_notification_view.cc |
| index 95a6c88e8e2021542f38ac3a36a8507e94a01974..2ec4b491e572c0c720b694520fcbb6f56114433f 100644 |
| --- a/ui/arc/notification/arc_custom_notification_view.cc |
| +++ b/ui/arc/notification/arc_custom_notification_view.cc |
| @@ -9,6 +9,10 @@ |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/resource/resource_bundle.h" |
| +#include "ui/compositor/layer_animation_observer.h" |
| +#include "ui/display/screen.h" |
| +#include "ui/events/event_handler.h" |
| +#include "ui/gfx/transform.h" |
| #include "ui/message_center/message_center_style.h" |
| #include "ui/resources/grit/ui_resources.h" |
| #include "ui/strings/grit/ui_strings.h" |
| @@ -16,23 +20,111 @@ |
| #include "ui/views/border.h" |
| #include "ui/views/controls/button/image_button.h" |
| #include "ui/views/widget/widget.h" |
| +#include "ui/wm/core/window_util.h" |
| namespace arc { |
| +class ArcCustomNotificationView::EventForwarder : public ui::EventHandler { |
| + public: |
| + explicit EventForwarder(ArcCustomNotificationView* owner) : owner_(owner) {} |
| + ~EventForwarder() override = default; |
| + |
| + private: |
| + // ui::EventHandler |
| + void OnKeyEvent(ui::KeyEvent* event) override { owner_->OnKeyEvent(event); } |
| + void OnMouseEvent(ui::MouseEvent* event) override { |
| + owner_->OnMouseEvent(event); |
| + } |
| + void OnGestureEvent(ui::GestureEvent* event) override { |
| + owner_->OnGestureEvent(event); |
| + } |
|
sadrul
2016/08/10 03:26:39
Do you only care about these events, or do you wan
xiyuan
2016/08/10 16:28:07
OnEvent makes more sense. Replace individual handl
|
| + |
| + ArcCustomNotificationView* const owner_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(EventForwarder); |
| +}; |
| + |
| +class ArcCustomNotificationView::SlideHelper |
| + : public ui::LayerAnimationObserver { |
| + public: |
| + SlideHelper(ArcCustomNotificationView* owner) : owner_(owner) { |
|
sadrul
2016/08/10 03:26:39
explicit
xiyuan
2016/08/10 16:28:07
Done.
|
| + owner_->parent()->layer()->GetAnimator()->AddObserver(this); |
| + |
| + // Reset opacity to 1 to handle to case when the surface is sliding before |
| + // getting managed by this class, e.g. sliding in a popup before showing |
| + // in a message center view. |
| + if (owner_->surface_ && owner_->surface_->window()) |
| + owner_->surface_->window()->layer()->SetOpacity(1.0f); |
| + } |
| + ~SlideHelper() override { |
| + owner_->parent()->layer()->GetAnimator()->RemoveObserver(this); |
| + } |
| + |
| + void Update() { |
| + const bool has_animation = |
| + owner_->parent()->layer()->GetAnimator()->is_animating(); |
| + const bool has_transform = !owner_->parent()->GetTransform().IsIdentity(); |
| + const bool sliding = has_transform || has_animation; |
| + if (sliding_ == sliding) |
| + return; |
| + |
| + sliding_ = sliding; |
| + |
| + if (sliding_) |
| + OnSlideStart(); |
| + else |
| + OnSlideEnd(); |
| + } |
| + |
| + private: |
| + void OnSlideStart() { |
| + if (!owner_->surface_ || !owner_->surface_->window()) |
| + return; |
| + surface_copy_ = ::wm::RecreateLayers(owner_->surface_->window(), nullptr); |
| + owner_->layer()->Add(surface_copy_->root()); |
| + owner_->surface_->window()->layer()->SetOpacity(0.0f); |
| + } |
| + |
| + void OnSlideEnd() { |
| + if (!owner_->surface_ || !owner_->surface_->window()) |
| + return; |
| + owner_->surface_->window()->layer()->SetOpacity(1.0f); |
| + owner_->Layout(); |
| + surface_copy_.reset(); |
| + } |
| + |
| + // ui::LayerAnimationObserver |
| + void OnLayerAnimationEnded(ui::LayerAnimationSequence* seq) override { |
| + Update(); |
| + } |
| + void OnLayerAnimationAborted(ui::LayerAnimationSequence* seq) override { |
| + Update(); |
| + } |
| + void OnLayerAnimationScheduled(ui::LayerAnimationSequence* seq) override {} |
| + |
| + ArcCustomNotificationView* const owner_; |
| + bool sliding_ = false; |
| + std::unique_ptr<ui::LayerTreeOwner> surface_copy_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(SlideHelper); |
| +}; |
| + |
| ArcCustomNotificationView::ArcCustomNotificationView( |
| ArcCustomNotificationItem* item, |
| exo::NotificationSurface* surface) |
| - : item_(item), surface_(surface) { |
| + : item_(item), event_forwarder_(new EventForwarder(this)) { |
| + SetSurface(surface); |
| item_->AddObserver(this); |
| OnItemPinnedChanged(); |
| - surface_->window()->AddObserver(this); |
| + |
| + // Create a layer as an anchor to insert surface copy during a slide. |
| + SetPaintToLayer(true); |
| } |
| ArcCustomNotificationView::~ArcCustomNotificationView() { |
| + SetSurface(nullptr); |
| if (item_) |
| item_->RemoveObserver(this); |
| - if (surface_ && surface_->window()) |
| - surface_->window()->RemoveObserver(this); |
| } |
| void ArcCustomNotificationView::CreateFloatingCloseButton() { |
| @@ -64,11 +156,27 @@ void ArcCustomNotificationView::CreateFloatingCloseButton() { |
| 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::SetSurface(exo::NotificationSurface* surface) { |
| + if (surface_ == surface) |
| + return; |
| + |
| + if (surface_ && surface_->window()) { |
| + surface_->window()->RemoveObserver(this); |
| + surface_->window()->RemovePreTargetHandler(event_forwarder_.get()); |
| + } |
| + |
| + surface_ = surface; |
| + |
| + if (surface_ && surface_->window()) { |
| + surface_->window()->AddObserver(this); |
| + surface_->window()->AddPreTargetHandler(event_forwarder_.get()); |
|
sadrul
2016/08/10 03:26:39
Can you clarify why we need a pre-target handler f
xiyuan
2016/08/10 16:28:07
This is because |surface_| has children aura::Wind
|
| + } |
| +} |
| + |
| void ArcCustomNotificationView::UpdatePreferredSize() { |
| gfx::Size preferred_size = surface_->GetSize(); |
| if (preferred_size.width() != message_center::kNotificationWidth) { |
| @@ -81,10 +189,31 @@ void ArcCustomNotificationView::UpdatePreferredSize() { |
| SetPreferredSize(preferred_size); |
| } |
| +void ArcCustomNotificationView::UpdateCloseButtonVisiblity() { |
| + if (!surface_ || !floating_close_button_widget_) |
| + return; |
| + |
| + const bool target_visiblity = |
| + surface_->window()->GetBoundsInScreen().Contains( |
| + display::Screen::GetScreen()->GetCursorScreenPoint()); |
| + if (target_visiblity == floating_close_button_widget_->IsVisible()) |
| + return; |
| + |
| + if (target_visiblity) |
| + floating_close_button_widget_->Show(); |
| + else |
| + floating_close_button_widget_->Hide(); |
| +} |
|
sadrul
2016/08/10 03:26:39
Consider using views::MouseWatcher (with MouseWatc
xiyuan
2016/08/10 16:28:07
Contains() is needed for determining the visibilit
|
| + |
| void ArcCustomNotificationView::ViewHierarchyChanged( |
| const views::View::ViewHierarchyChangedDetails& details) { |
| views::Widget* widget = GetWidget(); |
| + if (!details.is_add) { |
| + // Resets slide helper when this view is removed from its parent. |
| + slide_helper_.reset(); |
| + } |
| + |
| // Bail if native_view() has attached to a different widget. |
| if (widget && native_view() && |
| views::Widget::GetTopLevelWidgetForNativeView(native_view()) != widget) { |
| @@ -98,6 +227,9 @@ void ArcCustomNotificationView::ViewHierarchyChanged( |
| UpdatePreferredSize(); |
| Attach(surface_->window()); |
| + |
| + // Creates slide helper after this view is added to its parent. |
| + slide_helper_.reset(new SlideHelper(this)); |
| } |
| void ArcCustomNotificationView::Layout() { |
| @@ -126,6 +258,27 @@ void ArcCustomNotificationView::Layout() { |
| close_button_bounds.width()); |
| close_button_bounds.set_y(surface_local_bounds.y()); |
| floating_close_button_widget_->SetBounds(close_button_bounds); |
| + |
| + UpdateCloseButtonVisiblity(); |
| +} |
| + |
| +void ArcCustomNotificationView::OnKeyEvent(ui::KeyEvent* event) { |
| + // Forward to parent CustomNotificationView to handle keyboard dismissal. |
| + parent()->OnKeyEvent(event); |
| +} |
| + |
| +void ArcCustomNotificationView::OnGestureEvent(ui::GestureEvent* event) { |
| + // Forward to parent CustomNotificationView to handle sliding out. |
| + parent()->OnGestureEvent(event); |
| + slide_helper_->Update(); |
| +} |
| + |
| +void ArcCustomNotificationView::OnMouseEntered(const ui::MouseEvent&) { |
| + UpdateCloseButtonVisiblity(); |
| +} |
| + |
| +void ArcCustomNotificationView::OnMouseExited(const ui::MouseEvent&) { |
| + UpdateCloseButtonVisiblity(); |
| } |
| void ArcCustomNotificationView::ButtonPressed(views::Button* sender, |
| @@ -135,9 +288,10 @@ void ArcCustomNotificationView::ButtonPressed(views::Button* sender, |
| } |
| } |
| -void ArcCustomNotificationView::OnWindowBoundsChanged(aura::Window* window, |
| - const gfx::Rect& old_bounds, |
| - const gfx::Rect& new_bounds) { |
| +void ArcCustomNotificationView::OnWindowBoundsChanged( |
| + aura::Window* window, |
| + const gfx::Rect& old_bounds, |
| + const gfx::Rect& new_bounds) { |
| UpdatePreferredSize(); |
| } |
| @@ -151,7 +305,7 @@ void ArcCustomNotificationView::OnItemDestroying() { |
| // Reset |surface_| with |item_| since no one is observing the |surface_| |
| // after |item_| is gone and this view should be removed soon. |
| - surface_ = nullptr; |
| + SetSurface(nullptr); |
| } |
| void ArcCustomNotificationView::OnItemPinnedChanged() { |
| @@ -163,7 +317,7 @@ void ArcCustomNotificationView::OnItemPinnedChanged() { |
| } |
| void ArcCustomNotificationView::OnItemNotificationSurfaceRemoved() { |
| - surface_ = nullptr; |
| + SetSurface(nullptr); |
| } |
| } // namespace arc |