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() {
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) {
CloseSwipeControl();
if (item_)
return item_->OpenSnooze();
}
......
......@@ -52,6 +52,7 @@ class ArcNotificationView : public message_center::MessageView,
bool IsManuallyExpandedOrCollapsed() const override;
void OnContainerAnimationStarted() override;
void OnContainerAnimationEnded() override;
void OnSettingsButtonPressed(const ui::Event& event) override;
void OnSnoozeButtonPressed(const ui::Event& event) override;
void UpdateCornerRadius(int top_radius, int bottom_radius) override;
......
......@@ -15,4 +15,7 @@ const base::Feature kNewStyleNotifications {
#endif
};
const base::Feature kNotificationSwipeControl{
"NotificationSwipeControl", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace message_center
......@@ -14,6 +14,10 @@ namespace message_center {
// should be used.
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
#endif // UI_MESSAGE_CENTER_PUBLIC_CPP_FEATURES_H_
......@@ -142,6 +142,18 @@ constexpr int kMarginBetweenPopups = 10;
// The corners are only rounded in Chrome OS.
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
#endif // UI_MESSAGE_CENTER_PUBLIC_CPP_MESSAGE_CENTER_CONSTANTS_H_
......@@ -123,6 +123,23 @@ void MessageView::SetIsNested() {
ninebox_insets),
-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 {
......@@ -326,6 +343,10 @@ MessageView::Mode MessageView::GetMode() const {
return Mode::NORMAL;
}
float MessageView::GetSlideAmount() const {
return slide_out_controller_.gesture_amount();
}
void MessageView::SetSettingMode(bool setting_mode) {
setting_mode_ = setting_mode;
slide_out_controller_.set_slide_mode(CalculateSlideMode());
......
......@@ -80,6 +80,7 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView,
virtual bool IsAutoExpandingAllowed() const;
virtual bool IsManuallyExpandedOrCollapsed() const;
virtual void SetManuallyExpandedOrCollapsed(bool value);
virtual void CloseSwipeControl();
// Update corner radii of the notification. Subclasses will override this to
// implement rounded corners if they don't use MessageView's default
......@@ -118,6 +119,9 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView,
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
// MessageView::Mode enum for detail.
void SetSettingMode(bool setting_mode);
......
......@@ -1259,6 +1259,11 @@ void NotificationViewMD::SetManuallyExpandedOrCollapsed(bool value) {
}
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_)
ToggleInlineSettings(event);
else
......
......@@ -7,6 +7,7 @@
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/transform.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
namespace message_center {
......@@ -16,8 +17,25 @@ SlideOutController::SlideOutController(ui::EventTarget* target,
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) {
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) {
// The threshold for the fling velocity is computed empirically.
......@@ -29,17 +47,29 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
event->StopPropagation();
return;
}
CaptureControlOpenState();
RestoreVisualState();
delegate_->OnSlideChanged();
return;
}
if (!event->IsScrollGestureEvent())
return;
ui::Layer* layer = delegate_->GetSlideOutLayer();
int width = layer->bounds().width();
if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
gesture_amount_ = 0.f;
switch (control_open_state_) {
case SwipeControlOpenState::CLOSED:
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) {
// The scroll-update events include the incremental scroll amount.
gesture_amount_ += event->details().scroll_x();
......@@ -58,11 +88,11 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
case SlideMode::PARTIALLY:
if (gesture_amount_ >= 0) {
scroll_amount = std::min(0.5f * gesture_amount_,
width * kScrollRatioForClosingNotification);
scroll_amount_for_closing_notification);
} else {
scroll_amount =
std::max(0.5f * gesture_amount_,
-1.f * width * kScrollRatioForClosingNotification);
-1.f * scroll_amount_for_closing_notification);
}
opacity = 1.f;
break;
......@@ -75,11 +105,12 @@ void SlideOutController::OnGestureEvent(ui::GestureEvent* event) {
} else if (event->type() == ui::ET_GESTURE_SCROLL_END) {
float scrolled_ratio = fabsf(gesture_amount_) / width;
if (mode_ == SlideMode::FULL &&
scrolled_ratio >= kScrollRatioForClosingNotification) {
scrolled_ratio >= scroll_amount_for_closing_notification / width) {
SlideOutAndClose(gesture_amount_);
event->StopPropagation();
return;
}
CaptureControlOpenState();
RestoreVisualState();
}
......@@ -94,7 +125,19 @@ void SlideOutController::RestoreVisualState() {
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration(
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);
}
......@@ -116,7 +159,24 @@ void SlideOutController::SlideOutAndClose(int direction) {
}
void SlideOutController::OnImplicitAnimationsCompleted() {
delegate_->OnSlideChanged();
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
......@@ -41,6 +41,7 @@ class MESSAGE_CENTER_EXPORT SlideOutController
~SlideOutController() override;
void set_slide_mode(SlideMode mode) { mode_ = mode; }
float gesture_amount() const { return gesture_amount_; }
SlideMode mode() const { return mode_; }
// ui::EventHandler
......@@ -49,10 +50,24 @@ class MESSAGE_CENTER_EXPORT SlideOutController
// ui::ImplicitAnimationObserver
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:
// 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.
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
// |direction| indicates which way the slide occurs.
void SlideOutAndClose(int direction);
......@@ -60,9 +75,25 @@ class MESSAGE_CENTER_EXPORT SlideOutController
ui::ScopedTargetHandler target_handling_;
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;
// Whether or not this view can be slided and/or swiped out.
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);
};
......
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