Commit 323d2978 authored by Ahmed Mehfooz's avatar Ahmed Mehfooz Committed by Commit Bot

Basic Pagination for System Tray

Add logic to set FeaturePodsContainer Layout according to page numbers
Add PageIndicatorView in UnifiedSystemTrayView

The SystemTray will be limited to show 3x3 FeaturePodButtons at a time.
Additional Buttons will be added to additional pages.
The available pages will be signified by small buttons under the
feature pods container.

Bug: 914077
Change-Id: I8e2e1a51b0503bda251e409f7ce77d81f57f136e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1590610
Commit-Queue: Ahmed Mehfooz <amehfooz@chromium.org>
Reviewed-by: default avatarTim Song <tengs@chromium.org>
Reviewed-by: default avatarJenny Zhang <jennyz@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659728}
parent 322655b4
......@@ -230,14 +230,16 @@ constexpr int kUnifiedFeaturePodSpacing = 6;
constexpr int kUnifiedFeaturePodHoverRadius = 4;
constexpr int kUnifiedFeaturePodVerticalPadding = 28;
constexpr int kUnifiedFeaturePodTopPadding = 24;
constexpr int kUnifiedFeaturePodBottomPadding = 20;
constexpr int kUnifiedFeaturePodBottomPadding = 5;
constexpr int kUnifiedFeaturePodHorizontalSidePadding = 12;
constexpr int kUnifiedFeaturePodHorizontalMiddlePadding = 0;
constexpr int kUnifiedFeaturePodCollapsedVerticalPadding = 16;
constexpr int kUnifiedFeaturePodCollapsedVerticalPadding = 12;
constexpr int kUnifiedFeaturePodCollapsedHorizontalPadding = 24;
constexpr int kUnifiedFeaturePodArrowSpacing = 4;
constexpr int kUnifiedFeaturePodItemsInRow = 3;
constexpr int kUnifiedFeaturePodItemsRows = 3;
constexpr int kUnifiedFeaturePodMaxItemsInCollapsed = 5;
constexpr int kUnifiedFeaturePodsPageSpacing = 48;
constexpr int kUnifiedNotificationSeparatorThickness = 1;
// Constants used in PageIndicatorView of UnifiedSystemTray.
......
......@@ -4,15 +4,26 @@
#include "ash/system/unified/feature_pods_container_view.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/pagination/pagination_controller.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
namespace ash {
FeaturePodsContainerView::FeaturePodsContainerView(bool initially_expanded)
: expanded_amount_(initially_expanded ? 1.0 : 0.0) {}
FeaturePodsContainerView::FeaturePodsContainerView(
PaginationModel* pagination_model,
bool initially_expanded)
: pagination_model_(pagination_model),
expanded_amount_(initially_expanded ? 1.0 : 0.0) {
pagination_model_->AddObserver(this);
}
FeaturePodsContainerView::~FeaturePodsContainerView() = default;
FeaturePodsContainerView::~FeaturePodsContainerView() {
DCHECK(pagination_model_);
pagination_model_->RemoveObserver(this);
}
void FeaturePodsContainerView::SetExpandedAmount(double expanded_amount) {
DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
......@@ -33,6 +44,10 @@ int FeaturePodsContainerView::GetExpandedHeight() const {
// floor(visible_count / kUnifiedFeaturePodItemsInRow)
int number_of_lines = (visible_count + kUnifiedFeaturePodItemsInRow - 1) /
kUnifiedFeaturePodItemsInRow;
if (features::IsSystemTrayFeaturePodsPaginationEnabled())
number_of_lines = std::min(number_of_lines, kUnifiedFeaturePodItemsRows);
return kUnifiedFeaturePodBottomPadding +
(kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
number_of_lines;
......@@ -83,27 +98,10 @@ void FeaturePodsContainerView::ViewHierarchyChanged(
void FeaturePodsContainerView::Layout() {
UpdateCollapsedSidePadding();
int visible_count = 0;
for (auto* child : children()) {
if (!child->visible())
continue;
gfx::Size child_size;
if (expanded_amount_ > 0.0) {
child_size = kUnifiedFeaturePodSize;
// Flexibly give more height if the child view doesn't fit into the
// default height, so that label texts won't be broken up in the middle.
child_size.set_height(std::max(
child_size.height(), child->GetHeightForWidth(child_size.height())));
} else {
child_size = kUnifiedFeaturePodCollapsedSize;
}
child->SetBoundsRect(
gfx::Rect(GetButtonPosition(visible_count++), child_size));
child->Layout();
CalculateIdealBoundsForFeaturePods();
for (int i = 0; i < visible_buttons_.view_size(); ++i) {
auto* button = visible_buttons_.view_at(i);
button->SetBoundsRect(visible_buttons_.ideal_bounds(i));
}
}
......@@ -114,17 +112,28 @@ void FeaturePodsContainerView::UpdateChildVisibility() {
int visible_count = 0;
for (auto* view : children()) {
auto* child = static_cast<FeaturePodButton*>(view);
bool visible = child->visible_preferred() &&
(expanded_amount_ > 0.0 ||
visible_count < kUnifiedFeaturePodMaxItemsInCollapsed);
bool visible = IsButtonVisible(child, visible_count);
child->SetVisibleByContainer(visible);
if (visible)
if (visible) {
if (visible_buttons_.GetIndexOfView(child) < 0)
visible_buttons_.Add(child, visible_count);
++visible_count;
} else {
if (visible_buttons_.GetIndexOfView(child))
visible_buttons_.Remove(visible_buttons_.GetIndexOfView(child));
}
}
UpdateTotalPages();
changing_visibility_ = false;
}
bool FeaturePodsContainerView::IsButtonVisible(FeaturePodButton* button,
int index) {
return button->visible_preferred() &&
(expanded_amount_ > 0.0 ||
index < kUnifiedFeaturePodMaxItemsInCollapsed);
}
int FeaturePodsContainerView::GetVisibleCount() const {
return std::count_if(
children().cbegin(), children().cend(), [](const auto* v) {
......@@ -182,4 +191,106 @@ void FeaturePodsContainerView::UpdateCollapsedSidePadding() {
DCHECK(collapsed_side_padding_ > 0);
}
void FeaturePodsContainerView::AddFeaturePodButton(FeaturePodButton* button) {
int view_size = visible_buttons_.view_size();
if (IsButtonVisible(button, view_size)) {
visible_buttons_.Add(button, view_size);
}
AddChildView(button);
UpdateTotalPages();
}
const gfx::Vector2d FeaturePodsContainerView::CalculateTransitionOffset(
int page_of_view) const {
gfx::Size grid_size = CalculatePreferredSize();
// If there is a transition, calculates offset for current and target page.
const int current_page = pagination_model_->selected_page();
const PaginationModel::Transition& transition =
pagination_model_->transition();
const bool is_valid =
pagination_model_->is_valid_page(transition.target_page);
// Transition to previous page means negative offset.
const int direction = transition.target_page > current_page ? -1 : 1;
int x_offset = 0;
int y_offset = 0;
// Page size including padding pixels. A tile.x + page_width means the same
// tile slot in the next page.
const int page_width = grid_size.width() + kUnifiedFeaturePodsPageSpacing;
if (page_of_view < current_page)
x_offset = -page_width;
else if (page_of_view > current_page)
x_offset = page_width;
if (is_valid) {
if (page_of_view == current_page ||
page_of_view == transition.target_page) {
x_offset += transition.progress * page_width * direction;
}
}
return gfx::Vector2d(x_offset, y_offset);
}
void FeaturePodsContainerView::CalculateIdealBoundsForFeaturePods() {
for (int i = 0; i < visible_buttons_.view_size(); ++i) {
gfx::Rect tile_bounds;
gfx::Size child_size;
if (expanded_amount_ > 0.0) {
child_size = kUnifiedFeaturePodSize;
// Flexibly give more height if the child view doesn't fit into the
// default height, so that label texts won't be broken up in the middle.
child_size.set_height(std::max(
child_size.height(),
visible_buttons_.view_at(i)->GetHeightForWidth(child_size.height())));
tile_bounds =
gfx::Rect(GetButtonPosition(i % GetTilesPerPage()), child_size);
// TODO(amehfooz): refactor this logic so that the ideal_bounds are set
// once when the transition starts and the actual feature pod bounds are
// interpolated using the ideal_bounds as the transition progresses.
tile_bounds.Offset(CalculateTransitionOffset(i / GetTilesPerPage()));
} else {
child_size = kUnifiedFeaturePodCollapsedSize;
tile_bounds = gfx::Rect(GetButtonPosition(i), child_size);
}
visible_buttons_.set_ideal_bounds(i, tile_bounds);
}
}
int FeaturePodsContainerView::GetTilesPerPage() const {
if (features::IsSystemTrayFeaturePodsPaginationEnabled())
return kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows;
else
return children().size();
}
void FeaturePodsContainerView::UpdateTotalPages() {
int total_pages = 0;
int total_visible = visible_buttons_.view_size();
int tiles_per_page = GetTilesPerPage();
if (!visible_buttons_.view_size() || !tiles_per_page) {
total_pages = 0;
} else {
total_pages = (total_visible / tiles_per_page) +
(total_visible % tiles_per_page ? 1 : 0);
}
pagination_model_->SetTotalPages(total_pages);
}
void FeaturePodsContainerView::TransitionChanged() {
const PaginationModel::Transition& transition =
pagination_model_->transition();
if (pagination_model_->is_valid_page(transition.target_page))
Layout();
}
} // namespace ash
......@@ -6,20 +6,31 @@
#define ASH_SYSTEM_UNIFIED_FEATURE_PODS_CONTAINER_VIEW_H_
#include "ash/ash_export.h"
#include "ash/public/cpp/pagination/pagination_model_observer.h"
#include "ui/views/view.h"
#include "ui/views/view_model.h"
namespace ash {
class FeaturePodButton;
class PaginationModel;
// Container of feature pods buttons in the middle of UnifiedSystemTrayView.
// The container has number of buttons placed in 3x3 plane at regular distances.
// FeaturePodButtons implements these individual buttons.
// The container also implements collapsed state where the top 5 buttons are
// horizontally placed and others are hidden.
class ASH_EXPORT FeaturePodsContainerView : public views::View {
class ASH_EXPORT FeaturePodsContainerView : public views::View,
public PaginationModelObserver {
public:
explicit FeaturePodsContainerView(bool initially_expanded);
FeaturePodsContainerView(PaginationModel* pagination_model,
bool initially_expanded);
~FeaturePodsContainerView() override;
// Add a FeaturePodButton as a child view and if it's visible add it to the
// view structure and update the pagination model.
void AddFeaturePodButton(FeaturePodButton* button);
// Change the expanded state. 0.0 if collapsed, and 1.0 if expanded.
// Otherwise, it shows intermediate state. If collapsed, all the buttons are
// horizontally placed.
......@@ -47,15 +58,39 @@ class ASH_EXPORT FeaturePodsContainerView : public views::View {
void Layout() override;
private:
void UpdateChildVisibility();
friend class FeaturePodsContainerViewTest;
// Calculate the current position of the button from |visible_index| and
// |expanded_amount_|.
gfx::Point GetButtonPosition(int visible_index) const;
void UpdateChildVisibility();
// Update |collapsed_state_padding_|.
void UpdateCollapsedSidePadding();
// Calculates the ideal bounds for all feature pods.
void CalculateIdealBoundsForFeaturePods();
// Calculates the offset for |page_of_view| based on current page and
// transition target page.
const gfx::Vector2d CalculateTransitionOffset(int page_of_view) const;
// Returns true if button at provided index is visible.
bool IsButtonVisible(FeaturePodButton* button, int index);
// Returns the number of tiles per page.
int GetTilesPerPage() const;
// Updates page splits for feature pod buttons.
void UpdateTotalPages();
// PaginationModelObserver:
void TransitionChanged() override;
// Owned by UnifiedSystemTrayModel.
PaginationModel* pagination_model_;
// The last |expanded_amount| passed to SetExpandedAmount().
double expanded_amount_;
......@@ -69,6 +104,10 @@ class ASH_EXPORT FeaturePodsContainerView : public views::View {
// A button that had focus at the point SaveButtonFocus is called.
views::View* focused_button_ = nullptr;
// A view model that contains all visible feature pod buttons.
// Used to calculate required number of pages.
views::ViewModelT<FeaturePodButton> visible_buttons_;
DISALLOW_COPY_AND_ASSIGN(FeaturePodsContainerView);
};
......
......@@ -4,10 +4,13 @@
#include "ash/system/unified/feature_pods_container_view.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pod_controller_base.h"
#include "ash/test/ash_test_base.h"
#include "base/test/scoped_feature_list.h"
#include "ui/views/view_observer.h"
namespace ash {
......@@ -22,10 +25,13 @@ class FeaturePodsContainerViewTest : public AshTestBase,
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
pagination_model_ = std::make_unique<PaginationModel>();
container_ = std::make_unique<FeaturePodsContainerView>(
true /* initially_expanded */);
pagination_model_.get(), true /* initially_expanded */);
container_->AddObserver(this);
preferred_size_changed_count_ = 0;
scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
}
// FeaturePodControllerBase:
......@@ -41,6 +47,11 @@ class FeaturePodsContainerViewTest : public AshTestBase,
}
protected:
void EnablePagination() {
scoped_feature_list_->InitAndEnableFeature(
features::kSystemTrayFeaturePodsPagination);
}
void AddButtons(int count) {
for (int i = 0; i < count; ++i) {
buttons_.push_back(new FeaturePodButton(this));
......@@ -52,6 +63,8 @@ class FeaturePodsContainerViewTest : public AshTestBase,
FeaturePodsContainerView* container() { return container_.get(); }
PaginationModel* pagination_model() { return pagination_model_.get(); }
int preferred_size_changed_count() const {
return preferred_size_changed_count_;
}
......@@ -59,7 +72,9 @@ class FeaturePodsContainerViewTest : public AshTestBase,
std::vector<FeaturePodButton*> buttons_;
private:
std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
std::unique_ptr<FeaturePodsContainerView> container_;
std::unique_ptr<PaginationModel> pagination_model_;
int preferred_size_changed_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(FeaturePodsContainerViewTest);
......@@ -164,4 +179,59 @@ TEST_F(FeaturePodsContainerViewTest, SizeChangeByVisibility) {
EXPECT_EQ(2, preferred_size_changed_count());
}
TEST_F(FeaturePodsContainerViewTest, NumberOfPagesChanged) {
const int kNumberOfPages = 8;
EnablePagination();
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
kNumberOfPages);
// Adding buttons to fill kNumberOfPages should cause the the same number of
// pages to be created.
EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
// Adding an additional button causes a new page to be added.
AddButtons(1);
EXPECT_EQ(pagination_model()->total_pages(), kNumberOfPages + 1);
}
TEST_F(FeaturePodsContainerViewTest, PaginationTransition) {
const int kNumberOfPages = 8;
EnablePagination();
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodItemsRows *
kNumberOfPages);
// Position of a button should slide to the left during a page
// transition to the next page.
gfx::Rect current_bounds;
gfx::Rect initial_bounds = buttons_[0]->bounds();
gfx::Rect previous_bounds = initial_bounds;
PaginationModel::Transition transition(
pagination_model()->selected_page() + 1, 0);
for (double i = 0.1; i <= 1.0; i += 0.1) {
transition.progress = i;
pagination_model()->SetTransition(transition);
current_bounds = buttons_[0]->bounds();
EXPECT_LT(current_bounds.x(), previous_bounds.x());
EXPECT_EQ(current_bounds.y(), previous_bounds.y());
previous_bounds = current_bounds;
}
// Button Position after page switch should move to the left by a page offset.
int page_offset = container()->CalculatePreferredSize().width() +
kUnifiedFeaturePodsPageSpacing;
gfx::Rect final_bounds =
gfx::Rect(initial_bounds.x() - page_offset, initial_bounds.y(),
initial_bounds.width(), initial_bounds.height());
pagination_model()->SelectPage(1, false);
container()->Layout();
EXPECT_EQ(final_bounds, buttons_[0]->bounds());
}
} // namespace ash
......@@ -16,6 +16,7 @@
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pods_container_view.h"
#include "ash/system/unified/notification_hidden_view.h"
#include "ash/system/unified/page_indicator_view.h"
#include "ash/system/unified/top_shortcuts_view.h"
#include "ash/system/unified/unified_system_info_view.h"
#include "ash/system/unified/unified_system_tray_controller.h"
......@@ -220,7 +221,11 @@ UnifiedSystemTrayView::UnifiedSystemTrayView(
controller_(controller),
notification_hidden_view_(new NotificationHiddenView()),
top_shortcuts_view_(new TopShortcutsView(controller_)),
feature_pods_container_(new FeaturePodsContainerView(initially_expanded)),
feature_pods_container_(
new FeaturePodsContainerView(controller_->model()->pagination_model(),
initially_expanded)),
page_indicator_view_(
new PageIndicatorView(controller_, initially_expanded)),
sliders_container_(new UnifiedSlidersContainerView(initially_expanded)),
system_info_view_(new UnifiedSystemInfoView(controller_)),
system_tray_container_(new SystemTrayContainer()),
......@@ -255,6 +260,7 @@ UnifiedSystemTrayView::UnifiedSystemTrayView(
system_tray_container_->AddChildView(top_shortcuts_view_);
system_tray_container_->AddChildView(feature_pods_container_);
system_tray_container_->AddChildView(page_indicator_view_);
system_tray_container_->AddChildView(sliders_container_);
system_tray_container_->AddChildView(system_info_view_);
......@@ -288,7 +294,7 @@ void UnifiedSystemTrayView::SetMaxHeight(int max_height) {
}
void UnifiedSystemTrayView::AddFeaturePodButton(FeaturePodButton* button) {
feature_pods_container_->AddChildView(button);
feature_pods_container_->AddFeaturePodButton(button);
}
void UnifiedSystemTrayView::AddSliderView(views::View* slider_view) {
......@@ -335,6 +341,7 @@ void UnifiedSystemTrayView::SetExpandedAmount(double expanded_amount) {
top_shortcuts_view_->SetExpandedAmount(expanded_amount);
feature_pods_container_->SetExpandedAmount(expanded_amount);
page_indicator_view_->SetExpandedAmount(expanded_amount);
sliders_container_->SetExpandedAmount(expanded_amount);
if (!IsTransformEnabled()) {
......@@ -358,6 +365,7 @@ int UnifiedSystemTrayView::GetExpandedSystemTrayHeight() const {
: 0) +
top_shortcuts_view_->GetPreferredSize().height() +
feature_pods_container_->GetExpandedHeight() +
page_indicator_view_->GetPreferredSize().height() +
sliders_container_->GetExpandedHeight() +
system_info_view_->GetPreferredSize().height();
}
......
......@@ -15,6 +15,7 @@ class FeaturePodButton;
class FeaturePodsContainerView;
class TopShortcutsView;
class NotificationHiddenView;
class PageIndicatorView;
class UnifiedMessageCenterView;
class UnifiedSystemInfoView;
class UnifiedSystemTrayController;
......@@ -147,6 +148,7 @@ class ASH_EXPORT UnifiedSystemTrayView : public views::View,
NotificationHiddenView* const notification_hidden_view_;
TopShortcutsView* const top_shortcuts_view_;
FeaturePodsContainerView* const feature_pods_container_;
PageIndicatorView* const page_indicator_view_;
UnifiedSlidersContainerView* const sliders_container_;
UnifiedSystemInfoView* const system_info_view_;
views::View* const system_tray_container_;
......
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