123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 |
- // Copyright 2018 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/unified/feature_pods_container_view.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"
- #include "ash/system/unified/unified_system_tray_controller.h"
- namespace ash {
- FeaturePodsContainerView::FeaturePodsContainerView(
- UnifiedSystemTrayController* controller,
- bool initially_expanded)
- : controller_(controller),
- pagination_model_(controller->model()->pagination_model()),
- expanded_amount_(initially_expanded ? 1.0 : 0.0),
- feature_pod_rows_(kUnifiedFeaturePodMaxRows) {
- pagination_model_->AddObserver(this);
- }
- FeaturePodsContainerView::~FeaturePodsContainerView() {
- DCHECK(pagination_model_);
- pagination_model_->RemoveObserver(this);
- }
- void FeaturePodsContainerView::SetExpandedAmount(double expanded_amount) {
- DCHECK(0.0 <= expanded_amount && expanded_amount <= 1.0);
- if (expanded_amount_ == expanded_amount)
- return;
- expanded_amount_ = expanded_amount;
- int visible_index = 0;
- for (auto* view : children()) {
- FeaturePodButton* button = static_cast<FeaturePodButton*>(view);
- // When collapsing from page 1, buttons below the second row fade out
- // while the rest move up into a single row for the collapsed state.
- // When collapsing from page > 1, each row of buttons fades out one by one
- // and once expanded_amount is less than kCollapseThreshold we begin to
- // fade in the single row of buttons for the collapsed state.
- if (expanded_amount_ > 0.0 && expanded_amount_ < kCollapseThreshold &&
- pagination_model_->selected_page() > 0) {
- button->SetExpandedAmount(1.0 - expanded_amount,
- true /* fade_icon_button */);
- } else if (visible_index > kUnifiedFeaturePodMaxItemsInCollapsed) {
- int row =
- (visible_index / kUnifiedFeaturePodItemsInRow) % feature_pod_rows_;
- double button_expanded_amount =
- expanded_amount
- ? std::min(1.0, expanded_amount +
- (0.25 * (feature_pod_rows_ - row - 1)))
- : expanded_amount;
- button->SetExpandedAmount(button_expanded_amount,
- true /* fade_icon_button */);
- } else {
- button->SetExpandedAmount(expanded_amount, false /* fade_icon_button */);
- }
- if (view->GetVisible())
- visible_index++;
- }
- UpdateChildVisibility();
- // We have to call Layout() explicitly here.
- Layout();
- }
- int FeaturePodsContainerView::GetExpandedHeight() const {
- const int visible_count = GetVisibleCount();
- // floor(visible_count / kUnifiedFeaturePodItemsInRow)
- int number_of_lines = (visible_count + kUnifiedFeaturePodItemsInRow - 1) /
- kUnifiedFeaturePodItemsInRow;
- number_of_lines = std::min(number_of_lines, feature_pod_rows_);
- return kUnifiedFeaturePodBottomPadding +
- (kUnifiedFeaturePodVerticalPadding + kUnifiedFeaturePodSize.height()) *
- std::max(0, number_of_lines - 1) +
- kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodTopPadding;
- }
- int FeaturePodsContainerView::GetCollapsedHeight() const {
- return 2 * kUnifiedFeaturePodCollapsedVerticalPadding +
- kUnifiedFeaturePodCollapsedSize.height();
- }
- gfx::Size FeaturePodsContainerView::CalculatePreferredSize() const {
- return gfx::Size(
- kTrayMenuWidth,
- static_cast<int>(GetCollapsedHeight() * (1.0 - expanded_amount_) +
- GetExpandedHeight() * expanded_amount_));
- }
- void FeaturePodsContainerView::ChildVisibilityChanged(View* child) {
- // ChildVisibilityChanged can change child visibility using
- // SetVisibleByContainer() in UpdateChildVisibility(), so we have to prevent
- // reentrancy.
- if (changing_visibility_)
- return;
- // Visibility change is caused by the child's SetVisible(), so update actual
- // visibility and propagate the container size change to the parent.
- UpdateChildVisibility();
- PreferredSizeChanged();
- Layout();
- SchedulePaint();
- }
- void FeaturePodsContainerView::ViewHierarchyChanged(
- const views::ViewHierarchyChangedDetails& details) {
- UpdateChildVisibility();
- }
- void FeaturePodsContainerView::Layout() {
- UpdateCollapsedSidePadding();
- CalculateIdealBoundsForFeaturePods();
- for (size_t i = 0; i < visible_buttons_.view_size(); ++i) {
- auto* button = visible_buttons_.view_at(i);
- button->SetBoundsRect(visible_buttons_.ideal_bounds(i));
- }
- }
- const char* FeaturePodsContainerView::GetClassName() const {
- return "FeaturePodsContainerView";
- }
- void FeaturePodsContainerView::UpdateChildVisibility() {
- DCHECK(!changing_visibility_);
- changing_visibility_ = true;
- int visible_count = 0;
- for (auto* view : children()) {
- auto* child = static_cast<FeaturePodButton*>(view);
- bool visible = IsButtonVisible(child, visible_count);
- child->SetVisibleByContainer(visible);
- if (visible) {
- if (!visible_buttons_.GetIndexOfView(child).has_value())
- visible_buttons_.Add(child, visible_count);
- ++visible_count;
- } else {
- if (auto index = visible_buttons_.GetIndexOfView(child);
- index.has_value()) {
- visible_buttons_.Remove(index.value());
- }
- }
- }
- 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) {
- return static_cast<const FeaturePodButton*>(v)->visible_preferred();
- });
- }
- void FeaturePodsContainerView::EnsurePageWithButton(views::View* button) {
- auto index = visible_buttons_.GetIndexOfView(button->parent());
- if (!index.has_value())
- return;
- int tiles_per_page = GetTilesPerPage();
- size_t first_index = pagination_model_->selected_page() * tiles_per_page;
- size_t last_index =
- ((pagination_model_->selected_page() + 1) * tiles_per_page) - 1;
- if (index.value() < first_index || index.value() > last_index) {
- int page = ((index.value() + 1) / tiles_per_page) +
- ((index.value() + 1) % tiles_per_page ? 1 : 0) - 1;
- pagination_model_->SelectPage(page, true /*animate*/);
- }
- }
- gfx::Point FeaturePodsContainerView::GetButtonPosition(
- int visible_index) const {
- int row = visible_index / kUnifiedFeaturePodItemsInRow;
- int column = visible_index % kUnifiedFeaturePodItemsInRow;
- int x = kUnifiedFeaturePodHorizontalSidePadding +
- (kUnifiedFeaturePodSize.width() +
- kUnifiedFeaturePodHorizontalMiddlePadding) *
- column;
- int y = kUnifiedFeaturePodTopPadding + (kUnifiedFeaturePodSize.height() +
- kUnifiedFeaturePodVerticalPadding) *
- row;
- // Only feature pods visible in the collapsed state (i.e. the first 5 pods)
- // move during expansion/collapse. Otherwise, the button position will always
- // be constant.
- if (expanded_amount_ == 1.0 ||
- visible_index > kUnifiedFeaturePodMaxItemsInCollapsed ||
- (pagination_model_->selected_page() > 0 &&
- expanded_amount_ >= kCollapseThreshold)) {
- return gfx::Point(x, y);
- }
- int collapsed_x =
- collapsed_side_padding_ + (kUnifiedFeaturePodCollapsedSize.width() +
- kUnifiedFeaturePodCollapsedHorizontalPadding) *
- visible_index;
- int collapsed_y = kUnifiedFeaturePodCollapsedVerticalPadding;
- // When fully collapsed or collapsing from a different page to the first
- // page, just return the collapsed position.
- if (expanded_amount_ == 0.0 || (expanded_amount_ < kCollapseThreshold &&
- pagination_model_->selected_page() > 0))
- return gfx::Point(collapsed_x, collapsed_y);
- // Button width is different between expanded and collapsed states.
- // During the transition, expanded width is used, so it should be adjusted.
- collapsed_x -= (kUnifiedFeaturePodSize.width() -
- kUnifiedFeaturePodCollapsedSize.width()) /
- 2;
- return gfx::Point(
- x * expanded_amount_ + collapsed_x * (1.0 - expanded_amount_),
- y * expanded_amount_ + collapsed_y * (1.0 - expanded_amount_));
- }
- int FeaturePodsContainerView::CalculateRowsFromHeight(int height) {
- int available_height =
- height - kUnifiedFeaturePodBottomPadding - kUnifiedFeaturePodTopPadding;
- int row_height =
- kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding;
- // Only use the max number of rows when there is enough space
- // to show the fully expanded message center and quick settings.
- if (available_height > (kUnifiedFeaturePodMaxRows * row_height) &&
- available_height - (kUnifiedFeaturePodMaxRows * row_height) >
- kMessageCenterCollapseThreshold) {
- return kUnifiedFeaturePodMaxRows;
- }
- // Use 1 less than the max number of rows when there is enough
- // space to show the message center in the collapsed state along
- // with the expanded quick settings.
- int feature_pod_rows = kUnifiedFeaturePodMaxRows - 1;
- if (available_height > (feature_pod_rows * row_height) &&
- available_height - (feature_pod_rows * row_height) >
- kStackedNotificationBarHeight) {
- return feature_pod_rows;
- }
- return kUnifiedFeaturePodMinRows;
- }
- void FeaturePodsContainerView::SetMaxHeight(int max_height) {
- int feature_pod_rows = CalculateRowsFromHeight(max_height);
- if (feature_pod_rows_ != feature_pod_rows) {
- feature_pod_rows_ = feature_pod_rows;
- UpdateTotalPages();
- }
- }
- void FeaturePodsContainerView::UpdateCollapsedSidePadding() {
- const int visible_count =
- std::min(GetVisibleCount(), kUnifiedFeaturePodMaxItemsInCollapsed);
- int contents_width =
- visible_count * kUnifiedFeaturePodCollapsedSize.width() +
- (visible_count - 1) * kUnifiedFeaturePodCollapsedHorizontalPadding;
- collapsed_side_padding_ = (kTrayMenuWidth - contents_width) / 2;
- DCHECK(collapsed_side_padding_ > 0);
- }
- void FeaturePodsContainerView::AddFeaturePodButton(FeaturePodButton* button) {
- size_t 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 (size_t i = 0; i < visible_buttons_.view_size(); ++i) {
- gfx::Rect tile_bounds;
- gfx::Size child_size;
- // When we are on the first page we calculate bounds for an expanded tray
- // when expanded_amount is greater than zero. However, when not on the first
- // page, we only calculate bounds for an expanded tray until expanded_amount
- // is above kCollapseThreshold. Below kCollapseThreshold we return collapsed
- // bounds.
- if ((expanded_amount_ > 0.0 && pagination_model_->selected_page() == 0) ||
- expanded_amount_ >= kCollapseThreshold) {
- 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 {
- return kUnifiedFeaturePodItemsInRow * feature_pod_rows_;
- }
- void FeaturePodsContainerView::UpdateTotalPages() {
- int total_pages = 0;
- size_t 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();
- }
- void FeaturePodsContainerView::OnGestureEvent(ui::GestureEvent* event) {
- if (controller_->pagination_controller()->OnGestureEvent(*event,
- GetContentsBounds()))
- event->SetHandled();
- }
- void FeaturePodsContainerView::OnScrollEvent(ui::ScrollEvent* event) {
- controller_->pagination_controller()->OnScroll(
- gfx::Vector2d(event->x_offset(), event->y_offset()), event->type());
- event->SetHandled();
- }
- bool FeaturePodsContainerView::OnMouseWheel(const ui::MouseWheelEvent& event) {
- return controller_->pagination_controller()->OnScroll(event.offset(),
- event.type());
- }
- } // namespace ash
|