Commit d6586907 authored by Tatsuhisa Yamaguchi's avatar Tatsuhisa Yamaguchi Committed by Commit Bot

Extend SideOutContoller to support swipe control on notifications.

This will make notification latched at an in-between position by
partially sliding it left or right, when the notification has either or
both snooze/settings buttons. The position to latch the slider changes
based on the number of those buttons.
This change does not yet show buttons there.

The feature is behind a flag.
To test this, add --enable-features=NotificationSwipeControl flag.

Bug: 840497
Change-Id: I5fce9216ebae79112d66eb289f6e669347c4aba7
Reviewed-on: https://chromium-review.googlesource.com/1177464Reviewed-by: default avatarYoshiki Iguchi <yoshiki@chromium.org>
Reviewed-by: default avatarTetsui Ohkubo <tetsui@chromium.org>
Commit-Queue: Tatsuhisa Yamaguchi <yamaguchi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#585090}
parent db518e6a
...@@ -145,7 +145,16 @@ void ArcNotificationView::OnContainerAnimationStarted() { ...@@ -145,7 +145,16 @@ void ArcNotificationView::OnContainerAnimationStarted() {
content_view_->OnContainerAnimationStarted(); content_view_->OnContainerAnimationStarted();
} }
void ArcNotificationView::OnSettingsButtonPressed(const ui::Event& event) {
// TODO(yamaguchi): remove this line and call CloseSwipeControl() from parent
// view of this view. The parent view should activate the swipe control of
// the slider attached to this view. Same for OnShoozeButtonPressed().
CloseSwipeControl();
MessageView::OnSettingsButtonPressed(event);
}
void ArcNotificationView::OnSnoozeButtonPressed(const ui::Event& event) { void ArcNotificationView::OnSnoozeButtonPressed(const ui::Event& event) {
CloseSwipeControl();
if (item_) if (item_)
return item_->OpenSnooze(); return item_->OpenSnooze();
} }
......
...@@ -52,6 +52,7 @@ class ArcNotificationView : public message_center::MessageView, ...@@ -52,6 +52,7 @@ class ArcNotificationView : public message_center::MessageView,
bool IsManuallyExpandedOrCollapsed() const override; bool IsManuallyExpandedOrCollapsed() const override;
void OnContainerAnimationStarted() override; void OnContainerAnimationStarted() override;
void OnContainerAnimationEnded() override; void OnContainerAnimationEnded() override;
void OnSettingsButtonPressed(const ui::Event& event) override;
void OnSnoozeButtonPressed(const ui::Event& event) override; void OnSnoozeButtonPressed(const ui::Event& event) override;
void UpdateCornerRadius(int top_radius, int bottom_radius) override; void UpdateCornerRadius(int top_radius, int bottom_radius) override;
......
...@@ -15,4 +15,7 @@ const base::Feature kNewStyleNotifications { ...@@ -15,4 +15,7 @@ const base::Feature kNewStyleNotifications {
#endif #endif
}; };
const base::Feature kNotificationSwipeControl{
"NotificationSwipeControl", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace message_center } // namespace message_center
...@@ -14,6 +14,10 @@ namespace message_center { ...@@ -14,6 +14,10 @@ namespace message_center {
// should be used. // should be used.
MESSAGE_CENTER_PUBLIC_EXPORT extern const base::Feature kNewStyleNotifications; MESSAGE_CENTER_PUBLIC_EXPORT extern const base::Feature kNewStyleNotifications;
// Whether the swipe control on notifications should be enabled.
MESSAGE_CENTER_PUBLIC_EXPORT extern const base::Feature
kNotificationSwipeControl;
} // namespace message_center } // namespace message_center
#endif // UI_MESSAGE_CENTER_PUBLIC_CPP_FEATURES_H_ #endif // UI_MESSAGE_CENTER_PUBLIC_CPP_FEATURES_H_
...@@ -142,6 +142,18 @@ constexpr int kMarginBetweenPopups = 10; ...@@ -142,6 +142,18 @@ constexpr int kMarginBetweenPopups = 10;
// The corners are only rounded in Chrome OS. // The corners are only rounded in Chrome OS.
constexpr int kNotificationCornerRadius = 2; constexpr int kNotificationCornerRadius = 2;
// Layout parameters for swipe control of notifications in message center.
constexpr int kSwipeControlButtonImageSize = 20;
constexpr int kSwipeControlButtonSize = 36;
constexpr int kSwipeControlButtonVerticalMargin = 24;
constexpr int kSwipeControlButtonHorizontalMargin = 8;
constexpr SkColor kSwipeControlBackgroundColor =
SkColorSetRGB(0xee, 0xee, 0xee);
// Close if notification is slided more than this amount in addition to the
// width of the buttons and their margins.
constexpr int kSwipeCloseMargin = 64;
} // namespace message_center } // namespace message_center
#endif // UI_MESSAGE_CENTER_PUBLIC_CPP_MESSAGE_CENTER_CONSTANTS_H_ #endif // UI_MESSAGE_CENTER_PUBLIC_CPP_MESSAGE_CENTER_CONSTANTS_H_
...@@ -123,6 +123,23 @@ void MessageView::SetIsNested() { ...@@ -123,6 +123,23 @@ void MessageView::SetIsNested() {
ninebox_insets), ninebox_insets),
-gfx::ShadowValue::GetMargin(shadow.values))); -gfx::ShadowValue::GetMargin(shadow.values)));
} }
if (!base::FeatureList::IsEnabled(message_center::kNotificationSwipeControl))
return;
auto* control_buttons_view = GetControlButtonsView();
if (control_buttons_view) {
int control_button_count =
(control_buttons_view->settings_button() ? 1 : 0) +
(control_buttons_view->snooze_button() ? 1 : 0);
if (control_button_count)
slide_out_controller_.EnableSwipeControl(control_button_count);
// TODO(crbug.com/1177464): support updating the swipe control when
// should_show_setting_buttons is changed after notification creation.
}
}
void MessageView::CloseSwipeControl() {
slide_out_controller_.CloseSwipeControl();
} }
bool MessageView::IsCloseButtonFocused() const { bool MessageView::IsCloseButtonFocused() const {
...@@ -326,6 +343,10 @@ MessageView::Mode MessageView::GetMode() const { ...@@ -326,6 +343,10 @@ MessageView::Mode MessageView::GetMode() const {
return Mode::NORMAL; return Mode::NORMAL;
} }
float MessageView::GetSlideAmount() const {
return slide_out_controller_.gesture_amount();
}
void MessageView::SetSettingMode(bool setting_mode) { void MessageView::SetSettingMode(bool setting_mode) {
setting_mode_ = setting_mode; setting_mode_ = setting_mode;
slide_out_controller_.set_slide_mode(CalculateSlideMode()); slide_out_controller_.set_slide_mode(CalculateSlideMode());
......
...@@ -80,6 +80,7 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView, ...@@ -80,6 +80,7 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView,
virtual bool IsAutoExpandingAllowed() const; virtual bool IsAutoExpandingAllowed() const;
virtual bool IsManuallyExpandedOrCollapsed() const; virtual bool IsManuallyExpandedOrCollapsed() const;
virtual void SetManuallyExpandedOrCollapsed(bool value); virtual void SetManuallyExpandedOrCollapsed(bool value);
virtual void CloseSwipeControl();
// Update corner radii of the notification. Subclasses will override this to // Update corner radii of the notification. Subclasses will override this to
// implement rounded corners if they don't use MessageView's default // implement rounded corners if they don't use MessageView's default
...@@ -118,6 +119,9 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView, ...@@ -118,6 +119,9 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView,
Mode GetMode() const; Mode GetMode() const;
// Gets the current horizontal scroll offset of the view by slide gesture.
float GetSlideAmount() const;
// Set "setting" mode. This overrides "pinned" mode. See the comment of // Set "setting" mode. This overrides "pinned" mode. See the comment of
// MessageView::Mode enum for detail. // MessageView::Mode enum for detail.
void SetSettingMode(bool setting_mode); void SetSettingMode(bool setting_mode);
......
...@@ -1259,6 +1259,11 @@ void NotificationViewMD::SetManuallyExpandedOrCollapsed(bool value) { ...@@ -1259,6 +1259,11 @@ void NotificationViewMD::SetManuallyExpandedOrCollapsed(bool value) {
} }
void NotificationViewMD::OnSettingsButtonPressed(const ui::Event& event) { void NotificationViewMD::OnSettingsButtonPressed(const ui::Event& event) {
// TODO(yamaguchi): remove this line and call CloseSwipeControl() from parent
// view of this view. The parent view should activate the swipe control of
// the slider attached to this view.
CloseSwipeControl();
if (settings_row_) if (settings_row_)
ToggleInlineSettings(event); ToggleInlineSettings(event);
else else
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "ui/compositor/layer.h" #include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/transform.h" #include "ui/gfx/transform.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
namespace message_center { namespace message_center {
...@@ -16,8 +17,25 @@ SlideOutController::SlideOutController(ui::EventTarget* target, ...@@ -16,8 +17,25 @@ SlideOutController::SlideOutController(ui::EventTarget* target,
SlideOutController::~SlideOutController() {} SlideOutController::~SlideOutController() {}
void SlideOutController::CaptureControlOpenState() {
if (!has_swipe_control_)
return;
if (mode_ == SlideMode::FULL &&
fabs(gesture_amount_) >= swipe_control_width_) {
control_open_state_ = gesture_amount_ < 0
? SwipeControlOpenState::OPEN_ON_RIGHT
: SwipeControlOpenState::OPEN_ON_LEFT;
} else {
control_open_state_ = SwipeControlOpenState::CLOSED;
}
}
void SlideOutController::OnGestureEvent(ui::GestureEvent* event) { void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
const float kScrollRatioForClosingNotification = 0.5f; ui::Layer* layer = delegate_->GetSlideOutLayer();
int width = layer->bounds().width();
float scroll_amount_for_closing_notification =
has_swipe_control_ ? swipe_control_width_ + kSwipeCloseMargin
: width * 0.5;
if (event->type() == ui::ET_SCROLL_FLING_START) { if (event->type() == ui::ET_SCROLL_FLING_START) {
// The threshold for the fling velocity is computed empirically. // The threshold for the fling velocity is computed empirically.
...@@ -29,17 +47,29 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) { ...@@ -29,17 +47,29 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
event->StopPropagation(); event->StopPropagation();
return; return;
} }
CaptureControlOpenState();
RestoreVisualState(); RestoreVisualState();
delegate_->OnSlideChanged();
return; return;
} }
if (!event->IsScrollGestureEvent()) if (!event->IsScrollGestureEvent())
return; return;
ui::Layer* layer = delegate_->GetSlideOutLayer();
int width = layer->bounds().width();
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) { if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
switch (control_open_state_) {
case SwipeControlOpenState::CLOSED:
gesture_amount_ = 0.f; gesture_amount_ = 0.f;
break;
case SwipeControlOpenState::OPEN_ON_RIGHT:
gesture_amount_ = -swipe_control_width_;
break;
case SwipeControlOpenState::OPEN_ON_LEFT:
gesture_amount_ = swipe_control_width_;
break;
default:
NOTREACHED();
}
} else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) { } else if (event->type() == ui::ET_GESTURE_SCROLL_UPDATE) {
// The scroll-update events include the incremental scroll amount. // The scroll-update events include the incremental scroll amount.
gesture_amount_ += event->details().scroll_x(); gesture_amount_ += event->details().scroll_x();
...@@ -58,11 +88,11 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) { ...@@ -58,11 +88,11 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
case SlideMode::PARTIALLY: case SlideMode::PARTIALLY:
if (gesture_amount_ >= 0) { if (gesture_amount_ >= 0) {
scroll_amount = std::min(0.5f * gesture_amount_, scroll_amount = std::min(0.5f * gesture_amount_,
width * kScrollRatioForClosingNotification); scroll_amount_for_closing_notification);
} else { } else {
scroll_amount = scroll_amount =
std::max(0.5f * gesture_amount_, std::max(0.5f * gesture_amount_,
-1.f * width * kScrollRatioForClosingNotification); -1.f * scroll_amount_for_closing_notification);
} }
opacity = 1.f; opacity = 1.f;
break; break;
...@@ -75,11 +105,12 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) { ...@@ -75,11 +105,12 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
} else if (event->type() == ui::ET_GESTURE_SCROLL_END) { } else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
float scrolled_ratio = fabsf(gesture_amount_) / width; float scrolled_ratio = fabsf(gesture_amount_) / width;
if (mode_ == SlideMode::FULL && if (mode_ == SlideMode::FULL &&
scrolled_ratio >= kScrollRatioForClosingNotification) { scrolled_ratio >= scroll_amount_for_closing_notification / width) {
SlideOutAndClose(gesture_amount_); SlideOutAndClose(gesture_amount_);
event->StopPropagation(); event->StopPropagation();
return; return;
} }
CaptureControlOpenState();
RestoreVisualState(); RestoreVisualState();
} }
...@@ -94,7 +125,19 @@ void SlideOutController::RestoreVisualState() { ...@@ -94,7 +125,19 @@ void SlideOutController::RestoreVisualState() {
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration( settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS)); base::TimeDelta::FromMilliseconds(kSwipeRestoreDurationMS));
layer->SetTransform(gfx::Transform()); gfx::Transform transform;
switch (control_open_state_) {
case SwipeControlOpenState::CLOSED:
gesture_amount_ = 0.f;
break;
case SwipeControlOpenState::OPEN_ON_RIGHT:
transform.Translate(-swipe_control_width_, 0);
break;
case SwipeControlOpenState::OPEN_ON_LEFT:
transform.Translate(swipe_control_width_, 0);
break;
}
layer->SetTransform(transform);
layer->SetOpacity(1.f); layer->SetOpacity(1.f);
} }
...@@ -116,7 +159,24 @@ void SlideOutController::SlideOutAndClose(int direction) { ...@@ -116,7 +159,24 @@ void SlideOutController::SlideOutAndClose(int direction) {
} }
void SlideOutController::OnImplicitAnimationsCompleted() { void SlideOutController::OnImplicitAnimationsCompleted() {
delegate_->OnSlideChanged();
delegate_->OnSlideOut(); delegate_->OnSlideOut();
} }
void SlideOutController::EnableSwipeControl(int button_count) {
DCHECK(button_count > 0);
swipe_control_width_ =
kSwipeControlButtonSize * button_count +
kSwipeControlButtonHorizontalMargin * (button_count + 1);
has_swipe_control_ = true;
}
void SlideOutController::CloseSwipeControl() {
if (!has_swipe_control_)
return;
gesture_amount_ = 0;
CaptureControlOpenState();
RestoreVisualState();
}
} // namespace message_center } // namespace message_center
...@@ -41,6 +41,7 @@ class MESSAGE_CENTER_EXPORT SlideOutController ...@@ -41,6 +41,7 @@ class MESSAGE_CENTER_EXPORT SlideOutController
~SlideOutController() override; ~SlideOutController() override;
void set_slide_mode(SlideMode mode) { mode_ = mode; } void set_slide_mode(SlideMode mode) { mode_ = mode; }
float gesture_amount() const { return gesture_amount_; }
SlideMode mode() const { return mode_; } SlideMode mode() const { return mode_; }
// ui::EventHandler // ui::EventHandler
...@@ -49,10 +50,24 @@ class MESSAGE_CENTER_EXPORT SlideOutController ...@@ -49,10 +50,24 @@ class MESSAGE_CENTER_EXPORT SlideOutController
// ui::ImplicitAnimationObserver // ui::ImplicitAnimationObserver
void OnImplicitAnimationsCompleted() override; void OnImplicitAnimationsCompleted() override;
// Enables the swipe control. Buttons will appea behind the view as user
// slides it partially and it's kept open after the gesture.
void EnableSwipeControl(int button_count);
// Moves slide back to the center position to closes the swipe control.
// Effective only when swipe control is enabled by EnableSwipeControl().
void CloseSwipeControl();
private: private:
// Positions where the slided view stays after the touch released.
enum class SwipeControlOpenState { CLOSED, OPEN_ON_LEFT, OPEN_ON_RIGHT };
// Restores the transform and opacity of the view. // Restores the transform and opacity of the view.
void RestoreVisualState(); void RestoreVisualState();
// Decides which position the slide should go back after touch is released.
void CaptureControlOpenState();
// Slides the view out and closes it after the animation. The sign of // Slides the view out and closes it after the animation. The sign of
// |direction| indicates which way the slide occurs. // |direction| indicates which way the slide occurs.
void SlideOutAndClose(int direction); void SlideOutAndClose(int direction);
...@@ -60,9 +75,25 @@ class MESSAGE_CENTER_EXPORT SlideOutController ...@@ -60,9 +75,25 @@ class MESSAGE_CENTER_EXPORT SlideOutController
ui::ScopedTargetHandler target_handling_; ui::ScopedTargetHandler target_handling_;
Delegate* delegate_; Delegate* delegate_;
// Cumulative scroll amount since the beginning of current slide gesture.
// Includes the initial shift when swipe control was open at gesture start.
float gesture_amount_ = 0.f; float gesture_amount_ = 0.f;
// Whether or not this view can be slided and/or swiped out.
SlideMode mode_ = SlideMode::FULL; SlideMode mode_ = SlideMode::FULL;
// Whether the swipe control is enabled. See EnableSwipeControl().
// Effective only when |mode_| is FULL.
bool has_swipe_control_ = false;
// The horizontal position offset to for swipe control.
// See |EnableSwipeControl|.
int swipe_control_width_ = 0;
// The position where the slided view stays after the touch released.
// Changed only when |mode_| is FULL and |has_swipe_control_| is true.
SwipeControlOpenState control_open_state_ = SwipeControlOpenState::CLOSED;
DISALLOW_COPY_AND_ASSIGN(SlideOutController); DISALLOW_COPY_AND_ASSIGN(SlideOutController);
}; };
......
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