Add "Up next" background using SkPath
This CL adds 3 main things: 1. The background is drawn using a painter to achieve the custom shape 2. A layer mask is added for the same SkPath as the background to the calendar scroll view, so that the calendar underneath doesn't bleed through if the up next background is transparent 3. The "show today's events" button was added that opens the event list for todays date Screenshot of background without fill: http://shortn/_4Y8BnXyDwI Screenshot of background with fill: http://shortn/_FV6qgAuRko Screenshot with button: http://shortn/_N0l5JJlLr5 Screenshot with button (dark mode): http://shortn/_tXciTvw4bw Bug: b:258648730 Change-Id: I5feb164d7b6e794bf9bbbcd82292f6f975cc88dd Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4065972 Auto-Submit: Sam Cackett <samcackett@google.com> Reviewed-by: Jiaming Cheng <jiamingc@chromium.org> Reviewed-by: Evan Stade <estade@chromium.org> Commit-Queue: Sam Cackett <samcackett@google.com> Cr-Commit-Position: refs/heads/main@{#1093942}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
bd32ee9c53
commit
2fbf1a5ff9
ash
BUILD.gnash_strings.grd
ash_strings_grd
resources
system
time
calendar_event_list_item_view_jelly.cccalendar_event_list_view.cccalendar_up_next_pixeltest.cccalendar_up_next_view.cccalendar_up_next_view.hcalendar_up_next_view_background_painter.cccalendar_up_next_view_background_painter.hcalendar_up_next_view_unittest.cccalendar_utils.hcalendar_view.cccalendar_view.hcalendar_view_unittest.cc
@ -1797,6 +1797,8 @@ component("ash") {
|
||||
"system/time/calendar_month_view.h",
|
||||
"system/time/calendar_up_next_view.cc",
|
||||
"system/time/calendar_up_next_view.h",
|
||||
"system/time/calendar_up_next_view_background_painter.cc",
|
||||
"system/time/calendar_up_next_view_background_painter.h",
|
||||
"system/time/calendar_utils.cc",
|
||||
"system/time/calendar_utils.h",
|
||||
"system/time/calendar_view.cc",
|
||||
|
@ -4557,6 +4557,10 @@ Connect your device to power.
|
||||
Scroll right
|
||||
</message>
|
||||
|
||||
<message name="IDS_ASH_CALENDAR_UP_NEXT_TODAYS_EVENTS_BUTTON" desc="The accessible/tooltip description of the calendar up next today's events button.">
|
||||
See all events for today
|
||||
</message>
|
||||
|
||||
<message name="IDS_ASH_STATUS_TRAY_PROGRESS_BAR_ACCESSIBLE_NAME" desc="The accessible name for the progress bar shown in the status tray.">
|
||||
Loading
|
||||
</message>
|
||||
|
@ -0,0 +1 @@
|
||||
c1067ba8021ef5413fc49d18c0648098c5732bfa
|
@ -37,6 +37,7 @@ aggregate_vector_icons("ash_vector_icons") {
|
||||
"autoclick_scroll_up.icon",
|
||||
"battery.icon",
|
||||
"calendar_background.icon",
|
||||
"calendar_up_next_todays_events_button.icon",
|
||||
"cancel.icon",
|
||||
"cancel_circle_outline.icon",
|
||||
"capture_gif.icon",
|
||||
|
@ -0,0 +1,39 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
CANVAS_DIMENSIONS, 16,
|
||||
MOVE_TO, 8, 5.4f,
|
||||
LINE_TO, 4.63f, 8.77f,
|
||||
R_LINE_TO, 1.06f, 1.06f,
|
||||
LINE_TO, 8, 7.52f,
|
||||
R_LINE_TO, 2.31f, 2.31f,
|
||||
R_LINE_TO, 1.06f, -1.06f,
|
||||
LINE_TO, 8, 5.4f,
|
||||
CLOSE,
|
||||
MOVE_TO, 8, 0,
|
||||
R_CUBIC_TO, 1.1f, 0, 2.13f, 0.21f, 3.1f, 0.63f,
|
||||
R_ARC_TO, 8.28f, 8.28f, 0, 0, 1, 2.56f, 1.71f,
|
||||
R_ARC_TO, 8.28f, 8.28f, 0, 0, 1, 1.71f, 2.56f,
|
||||
CUBIC_TO, 15.79f, 5.87f, 16, 6.9f, 16, 8,
|
||||
R_ARC_TO, 7.7f, 7.7f, 0, 0, 1, -0.62f, 3.1f,
|
||||
R_ARC_TO, 8.03f, 8.03f, 0, 0, 1, -4.27f, 4.27f,
|
||||
ARC_TO, 7.8f, 7.8f, 0, 0, 1, 8, 16,
|
||||
R_ARC_TO, 7.7f, 7.7f, 0, 0, 1, -3.1f, -0.62f,
|
||||
R_ARC_TO, 7.86f, 7.86f, 0, 0, 1, -2.54f, -1.73f,
|
||||
R_ARC_TO, 7.86f, 7.86f, 0, 0, 1, -1.73f, -2.54f,
|
||||
ARC_TO, 7.7f, 7.7f, 0, 0, 1, 0, 8,
|
||||
R_CUBIC_TO, 0, -1.1f, 0.21f, -2.13f, 0.63f, -3.1f,
|
||||
R_ARC_TO, 8.03f, 8.03f, 0, 0, 1, 4.27f, -4.27f,
|
||||
ARC_TO, 7.7f, 7.7f, 0, 0, 1, 8, 0,
|
||||
CLOSE,
|
||||
R_MOVE_TO, 0, 1.5f,
|
||||
R_CUBIC_TO, -1.81f, 0, -3.34f, 0.63f, -4.6f, 1.9f,
|
||||
CUBIC_TO, 2.13f, 4.66f, 1.5f, 6.19f, 1.5f, 8,
|
||||
R_CUBIC_TO, 0, 1.81f, 0.63f, 3.34f, 1.9f, 4.6f,
|
||||
CUBIC_TO, 4.66f, 13.87f, 6.19f, 14.5f, 8, 14.5f,
|
||||
R_CUBIC_TO, 1.81f, 0, 3.34f, -0.63f, 4.6f, -1.9f,
|
||||
CUBIC_TO, 13.87f, 11.34f, 14.5f, 9.81f, 14.5f, 8,
|
||||
R_CUBIC_TO, 0, -1.81f, -0.63f, -3.34f, -1.9f, -4.6f,
|
||||
CUBIC_TO, 11.34f, 2.13f, 9.81f, 1.5f, 8, 1.5f,
|
||||
CLOSE
|
@ -163,6 +163,7 @@ CalendarEventListItemViewJelly::CalendarEventListItemViewJelly(
|
||||
const gfx::RoundedCornersF item_corner_radius = gfx::RoundedCornersF(
|
||||
top_radius, top_radius, bottom_radius, bottom_radius);
|
||||
SetPaintToLayer();
|
||||
layer()->SetFillsBoundsOpaquely(false);
|
||||
layer()->SetRoundedCornerRadius(item_corner_radius);
|
||||
|
||||
auto horizontal_layout_manager = std::make_unique<views::BoxLayout>(
|
||||
|
@ -128,6 +128,7 @@ CalendarEventListView::CalendarEventListView(
|
||||
views::BoxLayout::Orientation::kVertical));
|
||||
|
||||
SetPaintToLayer();
|
||||
layer()->SetFillsBoundsOpaquely(false);
|
||||
// Set the bottom corners to be rounded so that `CalendarEventListView` is
|
||||
// contained in `CalendarView`.
|
||||
layer()->SetRoundedCornerRadius(features::IsCalendarJellyEnabled()
|
||||
|
@ -66,8 +66,9 @@ class CalendarUpNextViewPixelTest : public AshTestBase {
|
||||
google_apis::ApiErrorCode::HTTP_SUCCESS,
|
||||
calendar_test_utils::CreateMockEventList(std::move(events)).get());
|
||||
|
||||
up_next_view_ = widget_->SetContentsView(
|
||||
std::make_unique<CalendarUpNextView>(controller_.get()));
|
||||
up_next_view_ =
|
||||
widget_->SetContentsView(std::make_unique<CalendarUpNextView>(
|
||||
controller_.get(), views::Button::PressedCallback()));
|
||||
widget_->SetSize(
|
||||
gfx::Size(kTrayMenuWidth, up_next_view_->GetPreferredSize().height()));
|
||||
}
|
||||
|
@ -12,11 +12,12 @@
|
||||
#include "ash/strings/grit/ash_strings.h"
|
||||
#include "ash/style/icon_button.h"
|
||||
#include "ash/system/time/calendar_event_list_item_view_jelly.h"
|
||||
#include "ash/system/time/calendar_up_next_view_background_painter.h"
|
||||
#include "ash/system/time/calendar_utils.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
|
||||
#include "ui/color/color_provider.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/gfx/text_constants.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
@ -27,10 +28,10 @@
|
||||
namespace ash {
|
||||
namespace {
|
||||
|
||||
constexpr int kContainerInsets = 12;
|
||||
constexpr int kBackgroundRadius = 12;
|
||||
constexpr gfx::Insets kContainerInsets = gfx::Insets::TLBR(4, 14, 12, 14);
|
||||
constexpr int kFullWidth = 0;
|
||||
constexpr int kMaxItemWidth = 160;
|
||||
constexpr gfx::Insets kHeaderInsets = gfx::Insets::TLBR(0, 0, 8, 0);
|
||||
constexpr int kHeaderBetweenChildSpacing = 14;
|
||||
constexpr int kHeaderButtonsBetweenChildSpacing = 28;
|
||||
|
||||
@ -81,12 +82,23 @@ class ScrollingAnimation : public gfx::LinearAnimation,
|
||||
const gfx::Rect end_visible_rect_;
|
||||
};
|
||||
|
||||
views::Builder<views::Label> CreateHeaderLabel() {
|
||||
std::unique_ptr<views::Button> CreateTodaysEventsButton(
|
||||
views::Button::PressedCallback callback) {
|
||||
return views::Builder<views::Button>(
|
||||
std::make_unique<ash::IconButton>(
|
||||
std::move(callback), IconButton::Type::kXSmall,
|
||||
&kCalendarUpNextTodaysEventsButtonIcon,
|
||||
IDS_ASH_CALENDAR_UP_NEXT_TODAYS_EVENTS_BUTTON))
|
||||
.Build();
|
||||
}
|
||||
|
||||
std::unique_ptr<views::Label> CreateHeaderLabel() {
|
||||
return views::Builder<views::Label>(
|
||||
bubble_utils::CreateLabel(
|
||||
bubble_utils::TypographyStyle::kButton2,
|
||||
l10n_util::GetStringUTF16(IDS_ASH_CALENDAR_UP_NEXT)))
|
||||
.SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
|
||||
.SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
|
||||
.Build();
|
||||
}
|
||||
|
||||
bool IsRightScrollButtonEnabled(views::ScrollView* scroll_view) {
|
||||
@ -115,16 +127,20 @@ int GetFirstVisibleChildIndex(std::vector<views::View*> event_views,
|
||||
} // namespace
|
||||
|
||||
CalendarUpNextView::CalendarUpNextView(
|
||||
CalendarViewController* calendar_view_controller)
|
||||
CalendarViewController* calendar_view_controller,
|
||||
views::Button::PressedCallback callback)
|
||||
: calendar_view_controller_(calendar_view_controller),
|
||||
todays_events_button_container_(
|
||||
AddChildView(std::make_unique<views::View>())),
|
||||
header_view_(AddChildView(std::make_unique<views::View>())),
|
||||
scroll_view_(AddChildView(std::make_unique<views::ScrollView>(
|
||||
views::ScrollView::ScrollWithLayers::kEnabled))),
|
||||
content_view_(scroll_view_->SetContents(std::make_unique<views::View>())),
|
||||
bounds_animator_(this) {
|
||||
SetBackground(std::make_unique<CalendarUpNextViewBackground>(
|
||||
cros_tokens::kCrosSysSystemOnBase));
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical, gfx::Insets(kContainerInsets),
|
||||
calendar_utils::kUpNextBetweenChildSpacing));
|
||||
views::BoxLayout::Orientation::kVertical, kContainerInsets, 0));
|
||||
|
||||
if (!gfx::Animation::ShouldRenderRichAnimation())
|
||||
bounds_animator_.SetAnimationDuration(base::TimeDelta());
|
||||
@ -134,15 +150,22 @@ CalendarUpNextView::CalendarUpNextView(
|
||||
base::BindRepeating(&CalendarUpNextView::ToggleScrollButtonState,
|
||||
base::Unretained(this)));
|
||||
|
||||
// Todays events button
|
||||
todays_events_button_container_
|
||||
->SetLayoutManager(std::make_unique<views::BoxLayout>())
|
||||
->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
|
||||
todays_events_button_container_->AddChildView(
|
||||
CreateTodaysEventsButton(callback));
|
||||
|
||||
// Header.
|
||||
auto* header_layout_manager =
|
||||
header_view_->SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
|
||||
views::BoxLayout::Orientation::kHorizontal, kHeaderInsets,
|
||||
kHeaderBetweenChildSpacing));
|
||||
// Header label.
|
||||
auto* header_label = header_view_->AddChildView(CreateHeaderLabel().Build());
|
||||
auto* header_label = header_view_->AddChildView(CreateHeaderLabel());
|
||||
header_layout_manager->SetFlexForView(header_label, 1);
|
||||
// Header buttons.
|
||||
// Header scroll buttons.
|
||||
auto button_container =
|
||||
views::Builder<views::View>()
|
||||
.SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
@ -184,6 +207,10 @@ CalendarUpNextView::CalendarUpNextView(
|
||||
|
||||
CalendarUpNextView::~CalendarUpNextView() = default;
|
||||
|
||||
SkPath CalendarUpNextView::GetClipPath() const {
|
||||
return CalendarUpNextViewBackground::GetPath(size());
|
||||
}
|
||||
|
||||
void CalendarUpNextView::Layout() {
|
||||
// For some reason the `content_view_` is constrained to the
|
||||
// `scroll_view_` width and so it isn't scrollable. This seems to be a
|
||||
@ -204,13 +231,6 @@ void CalendarUpNextView::Layout() {
|
||||
ToggleScrollButtonState();
|
||||
}
|
||||
|
||||
void CalendarUpNextView::OnThemeChanged() {
|
||||
views::View::OnThemeChanged();
|
||||
SetBackground(views::CreateRoundedRectBackground(
|
||||
GetColorProvider()->GetColor(cros_tokens::kCrosSysSystemOnBase),
|
||||
kBackgroundRadius));
|
||||
}
|
||||
|
||||
void CalendarUpNextView::UpdateEvents(
|
||||
const std::list<google_apis::calendar::CalendarEvent>& events,
|
||||
views::BoxLayout* content_layout_manager) {
|
||||
|
@ -24,19 +24,23 @@ class ASH_EXPORT CalendarUpNextView : public views::View {
|
||||
public:
|
||||
METADATA_HEADER(CalendarUpNextView);
|
||||
|
||||
explicit CalendarUpNextView(CalendarViewController* calendar_view_controller);
|
||||
CalendarUpNextView(CalendarViewController* calendar_view_controller,
|
||||
views::Button::PressedCallback callback);
|
||||
CalendarUpNextView(const CalendarUpNextView& other) = delete;
|
||||
CalendarUpNextView& operator=(const CalendarUpNextView& other) = delete;
|
||||
~CalendarUpNextView() override;
|
||||
|
||||
// Returns the `SkPath` for the background of the `CalendarUpNextView`.
|
||||
SkPath GetClipPath() const;
|
||||
|
||||
// views::View
|
||||
void Layout() override;
|
||||
void OnThemeChanged() override;
|
||||
|
||||
private:
|
||||
friend class CalendarUpNextViewTest;
|
||||
friend class CalendarUpNextViewAnimationTest;
|
||||
friend class CalendarUpNextViewPixelTest;
|
||||
friend class CalendarUpNextViewTest;
|
||||
friend class CalendarViewTest;
|
||||
|
||||
// Populates the scroll view with events.
|
||||
void UpdateEvents(
|
||||
@ -63,6 +67,7 @@ class ASH_EXPORT CalendarUpNextView : public views::View {
|
||||
CalendarViewController* calendar_view_controller_;
|
||||
|
||||
// Owned by `CalendarUpNextView`.
|
||||
views::View* const todays_events_button_container_;
|
||||
views::View* const header_view_;
|
||||
views::Button* left_scroll_button_;
|
||||
views::Button* right_scroll_button_;
|
||||
|
97
ash/system/time/calendar_up_next_view_background_painter.cc
Normal file
97
ash/system/time/calendar_up_next_view_background_painter.cc
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/system/time/calendar_up_next_view_background_painter.h"
|
||||
|
||||
#include "cc/paint/paint_flags.h"
|
||||
#include "ui/color/color_provider.h"
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
namespace ash {
|
||||
namespace {
|
||||
|
||||
constexpr int kBackgroundCornerRadius = 24;
|
||||
// Space for the top point of the background.
|
||||
// We start drawing a rectangle 12dip off the top and then eventually curve up
|
||||
// when drawing the circle. The top of the circle will be at y: 0.
|
||||
constexpr float kTopOffset = 12.f;
|
||||
|
||||
} // namespace
|
||||
|
||||
CalendarUpNextViewBackground::CalendarUpNextViewBackground(ui::ColorId color_id)
|
||||
: color_id_(color_id) {}
|
||||
|
||||
CalendarUpNextViewBackground::~CalendarUpNextViewBackground() = default;
|
||||
|
||||
SkPath CalendarUpNextViewBackground::GetPath(const gfx::Size& size) {
|
||||
// First draw a rounded rectangle.
|
||||
SkPath path;
|
||||
gfx::RectF rect_f((gfx::SizeF(size)));
|
||||
rect_f.set_y(kTopOffset);
|
||||
SkRect rect = SkRect{rect_f.x(), rect_f.y(), rect_f.width(), rect_f.height()};
|
||||
path.addRoundRect(
|
||||
rect, (SkScalar[]){kBackgroundCornerRadius, kBackgroundCornerRadius,
|
||||
kBackgroundCornerRadius, kBackgroundCornerRadius, 0.f,
|
||||
0.f, 0.f, 0.f});
|
||||
path.close();
|
||||
|
||||
// Cache center-x for positioning curves when size changes.
|
||||
const float cx = rect_f.CenterPoint().x();
|
||||
|
||||
// y values are shared between both curves.
|
||||
const float curve_bottom_y = kTopOffset;
|
||||
const float curve_top_y = 7;
|
||||
const float curve_control_point_y = 12.84f;
|
||||
|
||||
// Draw left curve.
|
||||
const float left_curve_start_x = cx - 23.f;
|
||||
const float left_curve_end_x = cx - 13.f;
|
||||
const float left_curve_control_point_x = cx - 16.86f;
|
||||
path.moveTo(left_curve_start_x, curve_bottom_y);
|
||||
path.cubicTo(left_curve_start_x, curve_bottom_y, left_curve_control_point_x,
|
||||
curve_control_point_y, left_curve_end_x, curve_top_y);
|
||||
path.lineTo(left_curve_end_x, curve_bottom_y);
|
||||
path.close();
|
||||
|
||||
// Draw right curve.
|
||||
const float right_curve_start_x = cx + 13.f;
|
||||
const float right_curve_end_x = cx + 23.f;
|
||||
const float right_curve_control_point_x = cx + 16.86f;
|
||||
path.moveTo(right_curve_start_x, curve_bottom_y);
|
||||
path.lineTo(right_curve_start_x, curve_top_y);
|
||||
path.cubicTo(right_curve_start_x, curve_top_y, right_curve_control_point_x,
|
||||
curve_control_point_y, right_curve_end_x, curve_bottom_y);
|
||||
path.close();
|
||||
|
||||
// Draw circle in the center.
|
||||
constexpr float kRadius = 16.f;
|
||||
path.addCircle(cx, /*y=*/kRadius, kRadius);
|
||||
path.close();
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
void CalendarUpNextViewBackground::Paint(gfx::Canvas* canvas,
|
||||
views::View* view) const {
|
||||
// Setup paint.
|
||||
cc::PaintFlags flags;
|
||||
flags.setBlendMode(SkBlendMode::kSrcOver);
|
||||
flags.setAntiAlias(true);
|
||||
flags.setStyle(cc::PaintFlags::kFill_Style);
|
||||
flags.setColor(view->GetColorProvider()->GetColor(color_id_));
|
||||
|
||||
// Get the path to draw on the canvas.
|
||||
SkPath path = GetPath(view->GetLocalBounds().size());
|
||||
|
||||
// Draw the path.
|
||||
canvas->DrawPath(path, flags);
|
||||
}
|
||||
|
||||
void CalendarUpNextViewBackground::OnViewThemeChanged(views::View* view) {
|
||||
SetNativeControlColor(view->GetColorProvider()->GetColor(color_id_));
|
||||
view->SchedulePaint();
|
||||
}
|
||||
|
||||
} // namespace ash
|
39
ash/system/time/calendar_up_next_view_background_painter.h
Normal file
39
ash/system/time/calendar_up_next_view_background_painter.h
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2023 The Chromium Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ASH_SYSTEM_TIME_CALENDAR_UP_NEXT_VIEW_BACKGROUND_PAINTER_H_
|
||||
#define ASH_SYSTEM_TIME_CALENDAR_UP_NEXT_VIEW_BACKGROUND_PAINTER_H_
|
||||
|
||||
#include "ui/views/background.h"
|
||||
|
||||
class SkPath;
|
||||
|
||||
namespace gfx {
|
||||
class Canvas;
|
||||
}
|
||||
|
||||
namespace ash {
|
||||
|
||||
class CalendarUpNextViewBackground : public views::Background {
|
||||
public:
|
||||
explicit CalendarUpNextViewBackground(ui::ColorId color_id);
|
||||
CalendarUpNextViewBackground(const CalendarUpNextViewBackground& other) =
|
||||
delete;
|
||||
CalendarUpNextViewBackground& operator=(
|
||||
const CalendarUpNextViewBackground& other) = delete;
|
||||
~CalendarUpNextViewBackground() override;
|
||||
|
||||
static SkPath GetPath(const gfx::Size& size);
|
||||
|
||||
// views::Background:
|
||||
void Paint(gfx::Canvas* canvas, views::View* view) const override;
|
||||
void OnViewThemeChanged(views::View* view) override;
|
||||
|
||||
private:
|
||||
ui::ColorId color_id_;
|
||||
};
|
||||
|
||||
} // namespace ash
|
||||
|
||||
#endif // ASH_SYSTEM_TIME_CALENDAR_UP_NEXT_VIEW_BACKGROUND_PAINTER_H_
|
@ -10,6 +10,7 @@
|
||||
#include "ash/system/time/calendar_view_controller.h"
|
||||
#include "ash/system/tray/tray_constants.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/time/time.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
#include "ui/views/controls/scroll_view.h"
|
||||
@ -49,7 +50,9 @@ class CalendarUpNextViewTest : public AshTestBase {
|
||||
}
|
||||
|
||||
void CreateUpNextView(
|
||||
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events) {
|
||||
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events,
|
||||
views::Button::PressedCallback callback =
|
||||
views::Button::PressedCallback()) {
|
||||
if (!widget_)
|
||||
widget_ = CreateFramelessTestWidget();
|
||||
|
||||
@ -60,7 +63,8 @@ class CalendarUpNextViewTest : public AshTestBase {
|
||||
google_apis::ApiErrorCode::HTTP_SUCCESS,
|
||||
calendar_test_utils::CreateMockEventList(std::move(events)).get());
|
||||
|
||||
auto up_next_view = std::make_unique<CalendarUpNextView>(controller_.get());
|
||||
auto up_next_view =
|
||||
std::make_unique<CalendarUpNextView>(controller_.get(), callback);
|
||||
up_next_view_ = widget_->SetContentsView(std::move(up_next_view));
|
||||
// Set the widget to reflect the CalendarUpNextView size in reality. If we
|
||||
// don't then the view will never be scrollable.
|
||||
@ -88,6 +92,10 @@ class CalendarUpNextViewTest : public AshTestBase {
|
||||
return up_next_view_->right_scroll_button_;
|
||||
}
|
||||
|
||||
const views::View* GetTodaysEventsButton() {
|
||||
return up_next_view_->todays_events_button_container_->children()[0];
|
||||
}
|
||||
|
||||
virtual void PressScrollLeftButton() {
|
||||
PressScrollButton(GetScrollLeftButton());
|
||||
}
|
||||
@ -111,10 +119,8 @@ class CalendarUpNextViewTest : public AshTestBase {
|
||||
CalendarUpNextView* up_next_view() { return up_next_view_; }
|
||||
|
||||
private:
|
||||
virtual void PressScrollButton(const views::View* button) {
|
||||
auto* event_generator = GetEventGenerator();
|
||||
event_generator->MoveMouseTo(button->GetBoundsInScreen().CenterPoint());
|
||||
event_generator->ClickLeftButton();
|
||||
void PressScrollButton(const views::View* button) {
|
||||
LeftClickOn(button);
|
||||
// End the scrolling animation immediately so tests can assert the results
|
||||
// of scrolling. If we don't do this, the test assertions run immediately
|
||||
// (and fail) due to animations concurrently running.
|
||||
@ -411,18 +417,45 @@ TEST_F(
|
||||
EXPECT_EQ(ScrollPosition(), first_event_width);
|
||||
}
|
||||
|
||||
TEST_F(CalendarUpNextViewTest,
|
||||
ShouldInvokeCallback_WhenTodaysEventButtonPressed) {
|
||||
// Set time override.
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
[]() { return base::subtle::TimeNowIgnoringOverride().LocalMidnight(); },
|
||||
nullptr, nullptr);
|
||||
|
||||
// Add event starting in 10 mins.
|
||||
std::list<std::unique_ptr<google_apis::calendar::CalendarEvent>> events;
|
||||
auto event_in_ten_mins_start_time =
|
||||
base::subtle::TimeNowIgnoringOverride().LocalMidnight() +
|
||||
base::Minutes(10);
|
||||
auto event_in_ten_mins_end_time =
|
||||
base::subtle::TimeNowIgnoringOverride().LocalMidnight() + base::Hours(1);
|
||||
events.push_back(
|
||||
CreateEvent(event_in_ten_mins_start_time, event_in_ten_mins_end_time));
|
||||
|
||||
bool called = false;
|
||||
auto callback = base::BindLambdaForTesting(
|
||||
[&called](const ui::Event& event) { called = true; });
|
||||
|
||||
CreateUpNextView(std::move(events), callback);
|
||||
EXPECT_FALSE(called);
|
||||
|
||||
LeftClickOn(GetTodaysEventsButton());
|
||||
|
||||
EXPECT_TRUE(called);
|
||||
}
|
||||
|
||||
class CalendarUpNextViewAnimationTest : public CalendarUpNextViewTest {
|
||||
public:
|
||||
CalendarUpNextViewAnimationTest()
|
||||
: CalendarUpNextViewTest(
|
||||
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
|
||||
|
||||
void PressScrollLeftButton() override {
|
||||
PressScrollButton(GetScrollLeftButton());
|
||||
}
|
||||
void PressScrollLeftButton() override { LeftClickOn(GetScrollLeftButton()); }
|
||||
|
||||
void PressScrollRightButton() override {
|
||||
PressScrollButton(GetScrollRightButton());
|
||||
LeftClickOn(GetScrollRightButton());
|
||||
}
|
||||
|
||||
bool IsAnimating() {
|
||||
@ -432,13 +465,6 @@ class CalendarUpNextViewAnimationTest : public CalendarUpNextViewTest {
|
||||
|
||||
const base::TimeDelta kAnimationStartBufferDuration = base::Milliseconds(50);
|
||||
const base::TimeDelta kAnimationFinishedDuration = base::Seconds(1);
|
||||
|
||||
private:
|
||||
void PressScrollButton(const views::View* button) override {
|
||||
auto* event_generator = GetEventGenerator();
|
||||
event_generator->MoveMouseTo(button->GetBoundsInScreen().CenterPoint());
|
||||
event_generator->ClickLeftButton();
|
||||
}
|
||||
};
|
||||
|
||||
// Flaky: https://crbug.com/1401505
|
||||
|
@ -86,6 +86,12 @@ constexpr int kMaxNumPrunableMonths = 20;
|
||||
// Between child spacing for `CalendarUpNextView`.
|
||||
constexpr int kUpNextBetweenChildSpacing = 8;
|
||||
|
||||
// The `CalendarUpNextView` UI has a rounded 'nub' that sticks up in the middle
|
||||
// of the view. To ensure that the scroll view animates nicely behind the up
|
||||
// next view, we need to forcibly overlap the views slightly for the distance
|
||||
// between the bottom and top of the 'nub'.
|
||||
constexpr int kUpNextOverlapInPx = 12;
|
||||
|
||||
// Checks if the `selected_date` is local time today.
|
||||
bool IsToday(const base::Time selected_date);
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "ui/compositor/animation_throughput_reporter.h"
|
||||
#include "ui/compositor/layer.h"
|
||||
#include "ui/compositor/layer_animator.h"
|
||||
#include "ui/compositor/paint_recorder.h"
|
||||
#include "ui/events/types/event_type.h"
|
||||
#include "ui/gfx/animation/tween.h"
|
||||
#include "ui/gfx/geometry/point.h"
|
||||
@ -186,6 +187,84 @@ void ResetLayer(views::View* view) {
|
||||
view->layer()->SetTransform(gfx::Transform());
|
||||
}
|
||||
|
||||
// Provides a layer mask over the `scroll_view_` that stops the calendar from
|
||||
// showing underneath the `up_next_view_`, if a transparent color is used as
|
||||
// the background color.
|
||||
// TODO: b/265057469 Remove layer mask if the cros.sys.system-on-base dark
|
||||
// theme colour is updated to be opaque.
|
||||
class UpNextViewMask : public ui::LayerOwner,
|
||||
public ui::LayerDelegate,
|
||||
public views::ViewObserver {
|
||||
public:
|
||||
UpNextViewMask(views::ScrollView* scroll_view,
|
||||
CalendarUpNextView* up_next_view)
|
||||
: scroll_view_(scroll_view), up_next_view_(up_next_view) {
|
||||
SetLayer(std::make_unique<ui::Layer>(ui::LAYER_TEXTURED));
|
||||
layer()->SetFillsBoundsOpaquely(false);
|
||||
layer()->set_delegate(this);
|
||||
|
||||
if (!scroll_view_->layer()) {
|
||||
scroll_view_->SetPaintToLayer();
|
||||
scroll_view_->layer()->SetFillsBoundsOpaquely(false);
|
||||
}
|
||||
|
||||
scroll_view_->layer()->SetMaskLayer(layer());
|
||||
|
||||
scroll_view_->AddObserver(this);
|
||||
up_next_view_->AddObserver(this);
|
||||
|
||||
// Up next view is added after the `scroll_view_` already exists so we need
|
||||
// to manually set the layer's bounds initially.
|
||||
if (!scroll_view_->bounds().IsEmpty()) {
|
||||
OnViewBoundsChanged(scroll_view_);
|
||||
}
|
||||
}
|
||||
|
||||
~UpNextViewMask() override {
|
||||
scroll_view_->RemoveObserver(this);
|
||||
up_next_view_->RemoveObserver(this);
|
||||
}
|
||||
|
||||
// ui::LayerDelegate:
|
||||
// We handle the views size dynamically when painting the layer so we don't
|
||||
// need to do anything here.
|
||||
void OnDeviceScaleFactorChanged(float, float) override {}
|
||||
|
||||
void OnPaintLayer(const ui::PaintContext& context) override {
|
||||
ui::PaintRecorder recorder(context, layer()->size());
|
||||
recorder.canvas()->DrawColor(SK_ColorBLACK);
|
||||
|
||||
gfx::Rect up_next_view_bounds(up_next_view_->GetVisibleBounds());
|
||||
views::View::ConvertRectToScreen(up_next_view_, &up_next_view_bounds);
|
||||
|
||||
recorder.canvas()->Translate(gfx::Vector2d(
|
||||
up_next_view_bounds.x() - scroll_view_->GetBoundsInScreen().x(),
|
||||
up_next_view_bounds.y() - scroll_view_->GetBoundsInScreen().y()));
|
||||
|
||||
cc::PaintFlags flags;
|
||||
flags.setBlendMode(SkBlendMode::kClear);
|
||||
flags.setAntiAlias(true);
|
||||
recorder.canvas()->DrawPath(up_next_view_->GetClipPath(), flags);
|
||||
}
|
||||
|
||||
// views::ViewObserver:
|
||||
void OnViewBoundsChanged(views::View* view) override {
|
||||
if (view == scroll_view_) {
|
||||
layer()->SetBounds(scroll_view_->layer()->bounds());
|
||||
return;
|
||||
}
|
||||
|
||||
if (view == up_next_view_) {
|
||||
scroll_view_->layer()->SchedulePaint(
|
||||
gfx::Rect(scroll_view_->layer()->size()));
|
||||
}
|
||||
}
|
||||
|
||||
// Owned by `CalendarView`.
|
||||
views::ScrollView* const scroll_view_;
|
||||
CalendarUpNextView* const up_next_view_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
// The label for each month.
|
||||
@ -378,7 +457,7 @@ CalendarView::CalendarView(DetailedViewDelegate* delegate,
|
||||
calendar_view->set_should_months_animate(true);
|
||||
},
|
||||
base::Unretained(this))) {
|
||||
auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical));
|
||||
SetFocusBehavior(FocusBehavior::ALWAYS);
|
||||
|
||||
@ -453,11 +532,9 @@ CalendarView::CalendarView(DetailedViewDelegate* delegate,
|
||||
|
||||
// Add scroll view.
|
||||
scroll_view_ = AddChildView(std::make_unique<views::ScrollView>());
|
||||
// Flex the scrollview around any sibling views that are added or removed.
|
||||
layout->SetFlexForView(scroll_view_, 1);
|
||||
scroll_view_->SetAllowKeyboardScrolling(false);
|
||||
scroll_view_->SetBackgroundColor(absl::nullopt);
|
||||
scroll_view_->ClipHeightTo(0, INT_MAX);
|
||||
ClipScrollViewHeight(ScrollViewState::FULL_HEIGHT);
|
||||
scroll_view_->SetDrawOverflowIndicator(false);
|
||||
scroll_view_->SetVerticalScrollBarMode(
|
||||
views::ScrollView::ScrollBarMode::kHiddenButEnabled);
|
||||
@ -878,6 +955,14 @@ void CalendarView::OnViewBoundsChanged(views::View* observed_view) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For screen density or orientation changes, we need to redraw the up next
|
||||
// views position and adjust the scroll view height accordingly.
|
||||
if (observed_view == this && up_next_view_) {
|
||||
SetUpNextViewBounds();
|
||||
ClipScrollViewHeight(ScrollViewState::UP_NEXT_SHOWING);
|
||||
return;
|
||||
}
|
||||
|
||||
if (observed_view != scroll_view_)
|
||||
return;
|
||||
|
||||
@ -1085,6 +1170,8 @@ void CalendarView::OpenEventList() {
|
||||
auto event_list_reporter = calendar_metrics::CreateAnimationReporter(
|
||||
event_list_view_, kEventListViewOpenEventListAnimationHistogram);
|
||||
|
||||
// TODO: b/265057469 Fix issue with transparent event list view when animating
|
||||
// in dark mode.
|
||||
views::AnimationBuilder()
|
||||
.SetPreemptionStrategy(
|
||||
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET)
|
||||
@ -1119,7 +1206,9 @@ void CalendarView::CloseEventList() {
|
||||
calendar_view_controller_->currently_shown_date())));
|
||||
scroll_view_->NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
|
||||
/*send_native_event=*/true);
|
||||
scroll_view_->ClipHeightTo(0, INT_MAX);
|
||||
// Increase the scroll height before the animation starts, so that it's
|
||||
// already full height when animating the event list view sliding down.
|
||||
ClipScrollViewHeight(ScrollViewState::FULL_HEIGHT);
|
||||
scroll_view_->SetVerticalScrollBarMode(
|
||||
views::ScrollView::ScrollBarMode::kHiddenButEnabled);
|
||||
|
||||
@ -1618,6 +1707,10 @@ void CalendarView::OnOpenEventListAnimationComplete() {
|
||||
if (is_destroying_)
|
||||
return;
|
||||
|
||||
// Once the event list is open, remove the up next view as it's hidden and can
|
||||
// cause UI issues if it remains there.
|
||||
RemoveUpNextView();
|
||||
|
||||
scroll_view_->SetVerticalScrollBarMode(
|
||||
views::ScrollView::ScrollBarMode::kHiddenButEnabled);
|
||||
// Scrolls to the next month if the selected date is in the `next_month_`, so
|
||||
@ -1635,7 +1728,7 @@ void CalendarView::OnOpenEventListAnimationComplete() {
|
||||
scroll_view_->ScrollToPosition(scroll_view_->vertical_scroll_bar(),
|
||||
PositionOfSelectedDate());
|
||||
// Clip the height to a bit more than the height of a row.
|
||||
scroll_view_->ClipHeightTo(0, calendar_view_controller_->row_height());
|
||||
ClipScrollViewHeight(ScrollViewState::EVENT_LIST_SHOWING);
|
||||
|
||||
if (!should_months_animate_)
|
||||
months_animation_restart_timer_.Reset();
|
||||
@ -1679,6 +1772,10 @@ void CalendarView::OnCloseEventListAnimationComplete() {
|
||||
IDS_ASH_CALENDAR_UP_BUTTON_ACCESSIBLE_DESCRIPTION));
|
||||
down_button_->SetTooltipText(l10n_util::GetStringUTF16(
|
||||
IDS_ASH_CALENDAR_DOWN_BUTTON_ACCESSIBLE_DESCRIPTION));
|
||||
|
||||
// Once the event list view is closed, we might need to show the up next view
|
||||
// if we have upcoming events.
|
||||
MaybeShowUpNextView();
|
||||
}
|
||||
|
||||
void CalendarView::RequestFocusForEventListCloseButton() {
|
||||
@ -1804,26 +1901,78 @@ void CalendarView::MaybeShowUpNextView() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (up_next_view_)
|
||||
if (event_list_view_ || up_next_view_) {
|
||||
return;
|
||||
}
|
||||
|
||||
up_next_view_ = AddChildView(std::make_unique<CalendarUpNextView>(
|
||||
calendar_view_controller_.get(),
|
||||
base::BindRepeating(&CalendarView::OpenEventListForTodaysDate,
|
||||
base::Unretained(this))));
|
||||
up_next_view_->SetProperty(views::kViewIgnoredByLayoutKey, true);
|
||||
SetUpNextViewBounds();
|
||||
ClipScrollViewHeight(ScrollViewState::UP_NEXT_SHOWING);
|
||||
|
||||
up_next_view_ = AddChildView(
|
||||
std::make_unique<CalendarUpNextView>(calendar_view_controller_.get()));
|
||||
InvalidateLayout();
|
||||
|
||||
// TODO: b/265057469 Remove layer mask if the cros.sys.system-on-base dark
|
||||
// theme colour is updated to be opaque.
|
||||
up_next_view_mask_ =
|
||||
std::make_unique<UpNextViewMask>(scroll_view_, up_next_view_);
|
||||
}
|
||||
|
||||
void CalendarView::RemoveUpNextView() {
|
||||
if (!up_next_view_)
|
||||
return;
|
||||
|
||||
up_next_view_mask_.reset();
|
||||
|
||||
RemoveChildViewT(up_next_view_);
|
||||
up_next_view_ = nullptr;
|
||||
|
||||
ClipScrollViewHeight(ScrollViewState::FULL_HEIGHT);
|
||||
// If the up next view is deleted whilst the calendar is still open, e.g.
|
||||
// time has passed and an event no longer meets 'upcoming' criteria, then
|
||||
// the calendar view needs to relayout after removing the upnext view.
|
||||
InvalidateLayout();
|
||||
}
|
||||
|
||||
void CalendarView::SetUpNextViewBounds() {
|
||||
const int up_next_view_preferred_height =
|
||||
up_next_view_->GetPreferredSize().height();
|
||||
up_next_view_->SetBounds(
|
||||
scroll_view_->x(),
|
||||
GetVisibleBounds().bottom() - up_next_view_preferred_height,
|
||||
GetVisibleBounds().width(), up_next_view_preferred_height);
|
||||
}
|
||||
|
||||
// TODO: b/258648728 Handle animating the up next view into the event list view
|
||||
// and use the upcoming event date start time instead of `base::Time::Now()`.
|
||||
void CalendarView::OpenEventListForTodaysDate() {
|
||||
calendar_view_controller_->ShowEventListView(
|
||||
/*selected_calendar_date_cell_view=*/calendar_view_controller_
|
||||
->todays_date_cell_view(),
|
||||
/*selected_date=*/base::Time::Now(),
|
||||
/*row_index=*/calendar_view_controller_->today_row() - 1);
|
||||
}
|
||||
|
||||
void CalendarView::ClipScrollViewHeight(ScrollViewState state_to_change_to) {
|
||||
switch (state_to_change_to) {
|
||||
case ScrollViewState::FULL_HEIGHT:
|
||||
scroll_view_->ClipHeightTo(0, INT_MAX);
|
||||
break;
|
||||
case ScrollViewState::UP_NEXT_SHOWING:
|
||||
scroll_view_->ClipHeightTo(0, GetBoundsInScreen().bottom() -
|
||||
scroll_view_->GetBoundsInScreen().y() -
|
||||
up_next_view_->height() +
|
||||
calendar_utils::kUpNextOverlapInPx);
|
||||
break;
|
||||
case ScrollViewState::EVENT_LIST_SHOWING:
|
||||
scroll_view_->ClipHeightTo(0, calendar_view_controller_->row_height());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_METADATA(CalendarView, views::View)
|
||||
END_METADATA
|
||||
|
||||
|
@ -336,6 +336,23 @@ class ASH_EXPORT CalendarView : public CalendarModel::Observer,
|
||||
// Removes the "Up next" view.
|
||||
void RemoveUpNextView();
|
||||
|
||||
// Sets the bounds of the up next view to be flush with the bottom of the
|
||||
// scrollview. We need to do this manually so it sits over the top of the
|
||||
// scrollview, rather than as an adjacent sibling.
|
||||
void SetUpNextViewBounds();
|
||||
|
||||
// Used by the `CalendarUpNextView` to open the event list for today's date.
|
||||
void OpenEventListForTodaysDate();
|
||||
|
||||
enum class ScrollViewState {
|
||||
FULL_HEIGHT,
|
||||
UP_NEXT_SHOWING,
|
||||
EVENT_LIST_SHOWING
|
||||
};
|
||||
// Used for clipping the calendar scroll view height to the different states
|
||||
// that the calendar view can be in.
|
||||
void ClipScrollViewHeight(ScrollViewState state_to_change_to);
|
||||
|
||||
// Setters for animation flags.
|
||||
void set_should_header_animate(bool should_animate) {
|
||||
should_header_animate_ = should_animate;
|
||||
@ -379,6 +396,8 @@ class ASH_EXPORT CalendarView : public CalendarModel::Observer,
|
||||
CalendarModel* calendar_model_ =
|
||||
Shell::Get()->system_tray_model()->calendar_model();
|
||||
|
||||
std::unique_ptr<ui::LayerOwner> up_next_view_mask_;
|
||||
|
||||
// If it `is_resetting_scroll_`, we don't calculate the scroll position and we
|
||||
// don't need to check if we need to update the month or not.
|
||||
bool is_resetting_scroll_ = false;
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ash/system/time/calendar_view.h"
|
||||
#include <climits>
|
||||
|
||||
#include "ash/calendar/calendar_client.h"
|
||||
#include "ash/calendar/calendar_controller.h"
|
||||
@ -193,6 +194,11 @@ class CalendarViewTest : public AshTestBase {
|
||||
|
||||
views::View* up_next_view() { return calendar_view_->up_next_view_; }
|
||||
|
||||
views::View* up_next_todays_events_button() {
|
||||
return calendar_view_->up_next_view_->todays_events_button_container_
|
||||
->children()[0];
|
||||
}
|
||||
|
||||
void ScrollUpOneMonth() {
|
||||
calendar_view_->ScrollOneMonthAndAutoScroll(/*scroll_up=*/true);
|
||||
}
|
||||
@ -2277,6 +2283,89 @@ TEST_F(
|
||||
EXPECT_TRUE(is_showing_up_next_view);
|
||||
}
|
||||
|
||||
TEST_F(CalendarViewWithJellyEnabledTest,
|
||||
ShouldNotShowUpNextView_WhenEventListViewIsOpen) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
// Open the event list view for any day.
|
||||
GestureTapOn(
|
||||
static_cast<views::LabelButton*>(current_month()->children()[2]));
|
||||
ASSERT_TRUE(event_list_view());
|
||||
// Mock events that start in ten mins coming in.
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
|
||||
// When the event list view is open, then the up next view should not have
|
||||
// been created.
|
||||
bool is_showing_up_next_view = up_next_view();
|
||||
EXPECT_FALSE(is_showing_up_next_view);
|
||||
}
|
||||
|
||||
// If there are upcoming events and the up next view should have been shown but
|
||||
// the event list was open, then when it closes we should show the up next view.
|
||||
TEST_F(CalendarViewWithJellyEnabledTest,
|
||||
ShouldShowUpNextView_WhenEventListViewHasFinishedClosing) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
// Open the event list view for any day.
|
||||
GestureTapOn(
|
||||
static_cast<views::LabelButton*>(current_month()->children()[2]));
|
||||
ASSERT_TRUE(event_list_view());
|
||||
// Mock events that start in ten mins coming in.
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
// Event list view is showing, so up next should not be shown.
|
||||
bool is_showing_up_next_view = up_next_view();
|
||||
ASSERT_FALSE(is_showing_up_next_view);
|
||||
CloseEventList();
|
||||
|
||||
// After closing the event list view, we expect the up next view to now be
|
||||
// shown.
|
||||
is_showing_up_next_view = up_next_view();
|
||||
EXPECT_TRUE(is_showing_up_next_view);
|
||||
}
|
||||
|
||||
TEST_F(CalendarViewWithJellyEnabledTest,
|
||||
ShouldOpenEventListView_WhenUpNextShowTodaysEventsButtonPressed) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
|
||||
// When fetched events are in the next 10 mins, then up next should have been
|
||||
// created.
|
||||
bool is_showing_up_next_view = up_next_view();
|
||||
EXPECT_TRUE(is_showing_up_next_view);
|
||||
bool is_showing_event_list_view = event_list_view();
|
||||
EXPECT_FALSE(is_showing_event_list_view);
|
||||
|
||||
LeftClickOn(up_next_todays_events_button());
|
||||
|
||||
is_showing_event_list_view = event_list_view();
|
||||
EXPECT_TRUE(is_showing_event_list_view);
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CalendarViewWithJellyEnabledTest,
|
||||
GivenUpNextIsShown_WhenNewEventsMoreThanTwoHoursAwayAreFetched_ThenUpNextViewShouldNotBeShown) {
|
||||
@ -2305,4 +2394,73 @@ TEST_F(
|
||||
EXPECT_FALSE(up_next_view());
|
||||
}
|
||||
|
||||
TEST_F(CalendarViewWithJellyEnabledTest,
|
||||
ShouldClipHeightOfScrollView_WhenUpNextIsShown) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
|
||||
// Expect the scrollview to have max int height when neither up next nor the
|
||||
// event list view are showing.
|
||||
EXPECT_EQ(INT_MAX, scroll_view()->GetMaxHeight());
|
||||
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
|
||||
// When fetched events are in the next 10 mins, then up next should have been
|
||||
// created.
|
||||
EXPECT_TRUE(up_next_view());
|
||||
// When up next is showing, the scrollview should be clipped to sit above but
|
||||
// slightly overlapping the up next view bounds.
|
||||
const int expected_max_height = scroll_view()->height() -
|
||||
up_next_view()->height() +
|
||||
calendar_utils::kUpNextOverlapInPx;
|
||||
EXPECT_EQ(expected_max_height, scroll_view()->GetMaxHeight());
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CalendarViewWithJellyEnabledTest,
|
||||
ShouldClipHeightOfScrollView_WhenEventListHasClosed_AndUpNextIsStillShown) {
|
||||
base::Time date;
|
||||
ASSERT_TRUE(base::Time::FromString("18 Nov 2021 10:00 GMT", &date));
|
||||
// Set time override.
|
||||
SetFakeNow(date);
|
||||
base::subtle::ScopedTimeClockOverrides time_override(
|
||||
&CalendarViewTest::FakeTimeNow, /*time_ticks_override=*/nullptr,
|
||||
/*thread_ticks_override=*/nullptr);
|
||||
|
||||
CreateCalendarView();
|
||||
MockEventsFetched(calendar_utils::GetStartOfMonthUTC(date),
|
||||
CreateMockEventListWithEventStartTimeTenMinsAway());
|
||||
|
||||
// When fetched events are in the next 10 mins, then up next should have been
|
||||
// created.
|
||||
EXPECT_TRUE(up_next_view());
|
||||
|
||||
// Open the event list view.
|
||||
ASSERT_EQ(u"2",
|
||||
static_cast<views::LabelButton*>(current_month()->children()[2])
|
||||
->GetText());
|
||||
GestureTapOn(
|
||||
static_cast<views::LabelButton*>(current_month()->children()[2]));
|
||||
ASSERT_TRUE(event_list_view());
|
||||
|
||||
// Close the event list view.
|
||||
GestureTapOn(close_button());
|
||||
ASSERT_FALSE(event_list_view());
|
||||
|
||||
// When the event list view closes, the scrollview max height should have been
|
||||
// clipped back to the right height for the up next view.
|
||||
const int expected_max_height = scroll_view()->height() -
|
||||
up_next_view()->height() +
|
||||
calendar_utils::kUpNextOverlapInPx;
|
||||
EXPECT_EQ(expected_max_height, scroll_view()->GetMaxHeight());
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
Reference in New Issue
Block a user