123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633 |
- // Copyright 2021 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/wm/window_cycle/window_cycle_view.h"
- #include "ash/accessibility/accessibility_controller_impl.h"
- #include "ash/public/cpp/metrics_util.h"
- #include "ash/public/cpp/style/color_provider.h"
- #include "ash/shell.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/style/ash_color_provider.h"
- #include "ash/wm/window_cycle/window_cycle_controller.h"
- #include "ash/wm/window_cycle/window_cycle_item_view.h"
- #include "base/bind.h"
- #include "base/check_op.h"
- #include "base/cxx17_backports.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/time/time.h"
- #include "chromeos/constants/chromeos_features.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #include "third_party/skia/include/core/SkColor.h"
- #include "ui/aura/window.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/base/metadata/metadata_impl_macros.h"
- #include "ui/compositor/animation_throughput_reporter.h"
- #include "ui/compositor/layer.h"
- #include "ui/compositor/layer_animator.h"
- #include "ui/compositor/layer_type.h"
- #include "ui/compositor/scoped_layer_animation_settings.h"
- #include "ui/gfx/animation/tween.h"
- #include "ui/gfx/font.h"
- #include "ui/gfx/geometry/insets.h"
- #include "ui/gfx/geometry/rect_f.h"
- #include "ui/gfx/geometry/rounded_corners_f.h"
- #include "ui/gfx/geometry/size.h"
- #include "ui/gfx/geometry/vector2d.h"
- #include "ui/gfx/text_constants.h"
- #include "ui/views/background.h"
- #include "ui/views/controls/label.h"
- #include "ui/views/highlight_border.h"
- #include "ui/views/layout/box_layout.h"
- #include "ui/views/view.h"
- namespace ash {
- namespace {
- // Shield rounded corner radius.
- constexpr int kBackgroundCornerRadius = 16;
- // Shield horizontal inset.
- constexpr int kBackgroundHorizontalInsetDp = 8;
- // Vertical padding between the alt-tab bandshield and the window previews.
- constexpr int kInsideBorderVerticalPaddingDp = 60;
- // Padding between the alt-tab bandshield and the tab slider container.
- constexpr int kMirrorContainerVerticalPaddingDp = 24;
- // Padding between the window previews within the alt-tab bandshield.
- constexpr int kBetweenChildPaddingDp = 10;
- // Padding between the tab slider button and the tab slider container.
- constexpr int kTabSliderContainerVerticalPaddingDp = 32;
- // The font size of "No recent items" string when there's no window in the
- // window cycle list.
- constexpr int kNoRecentItemsLabelFontSizeDp = 14;
- // The UMA histogram that logs smoothness of the fade-in animation.
- constexpr char kShowAnimationSmoothness[] =
- "Ash.WindowCycleView.AnimationSmoothness.Show";
- // The UMA histogram that logs smoothness of the window container animation.
- constexpr char kContainerAnimationSmoothness[] =
- "Ash.WindowCycleView.AnimationSmoothness.Container";
- // Duration of the window cycle UI fade in animation.
- constexpr base::TimeDelta kFadeInDuration = base::Milliseconds(100);
- // Duration of the window cycle elements slide animation.
- constexpr base::TimeDelta kContainerSlideDuration = base::Milliseconds(120);
- // Duration of the window cycle scale animation when a user toggles alt-tab
- // modes.
- constexpr base::TimeDelta kToggleModeScaleDuration = base::Milliseconds(150);
- } // namespace
- WindowCycleView::WindowCycleView(aura::Window* root_window,
- const WindowList& windows)
- : root_window_(root_window) {
- const bool is_interactive_alt_tab_mode_allowed =
- Shell::Get()->window_cycle_controller()->IsInteractiveAltTabModeAllowed();
- DCHECK(!windows.empty() || is_interactive_alt_tab_mode_allowed);
- // Start the occlusion tracker pauser. It's used to increase smoothness for
- // the fade in but we also create windows here which may occlude other
- // windows.
- occlusion_tracker_pauser_ =
- std::make_unique<aura::WindowOcclusionTracker::ScopedPause>();
- // The layer for |this| is responsible for showing color, background blur
- // and fading in.
- SetPaintToLayer(ui::LAYER_TEXTURED);
- ui::Layer* layer = this->layer();
- layer->SetFillsBoundsOpaquely(false);
- layer->SetBackgroundBlur(ColorProvider::kBackgroundBlurSigma);
- layer->SetBackdropFilterQuality(ColorProvider::kBackgroundBlurQuality);
- layer->SetName("WindowCycleView");
- layer->SetMasksToBounds(true);
- SetBackground(views::CreateRoundedRectBackground(
- AshColorProvider::Get()->GetBaseLayerColor(
- AshColorProvider::BaseLayerType::kTransparent80),
- kBackgroundCornerRadius));
- // |mirror_container_| may be larger than |this|. In this case, it will be
- // shifted along the x-axis when the user tabs through. It is a container
- // for the previews and has no rendered content.
- mirror_container_ = AddChildView(std::make_unique<views::View>());
- mirror_container_->SetPaintToLayer(ui::LAYER_NOT_DRAWN);
- mirror_container_->layer()->SetName("WindowCycleView/MirrorContainer");
- views::BoxLayout* layout =
- mirror_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
- views::BoxLayout::Orientation::kHorizontal,
- gfx::Insets::TLBR(is_interactive_alt_tab_mode_allowed
- ? kMirrorContainerVerticalPaddingDp
- : kInsideBorderVerticalPaddingDp,
- WindowCycleView::kInsideBorderHorizontalPaddingDp,
- kInsideBorderVerticalPaddingDp,
- WindowCycleView::kInsideBorderHorizontalPaddingDp),
- kBetweenChildPaddingDp));
- layout->set_cross_axis_alignment(
- views::BoxLayout::CrossAxisAlignment::kStart);
- if (is_interactive_alt_tab_mode_allowed) {
- tab_slider_container_ =
- AddChildView(std::make_unique<WindowCycleTabSlider>());
- no_recent_items_label_ = AddChildView(std::make_unique<views::Label>(
- l10n_util::GetStringUTF16(IDS_ASH_OVERVIEW_NO_RECENT_ITEMS)));
- no_recent_items_label_->SetPaintToLayer();
- no_recent_items_label_->layer()->SetFillsBoundsOpaquely(false);
- no_recent_items_label_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
- no_recent_items_label_->SetVerticalAlignment(gfx::ALIGN_MIDDLE);
- no_recent_items_label_->SetEnabledColor(
- AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorSecondary));
- no_recent_items_label_->SetFontList(
- no_recent_items_label_->font_list()
- .DeriveWithSizeDelta(
- kNoRecentItemsLabelFontSizeDp -
- no_recent_items_label_->font_list().GetFontSize())
- .DeriveWithWeight(gfx::Font::Weight::NORMAL));
- no_recent_items_label_->SetVisible(windows.empty());
- no_recent_items_label_->SetPreferredSize(
- gfx::Size(tab_slider_container_->GetPreferredSize().width() +
- 2 * WindowCycleView::kInsideBorderHorizontalPaddingDp,
- WindowCycleItemView::kFixedPreviewHeightDp +
- WindowMiniView::kHeaderHeightDp +
- kMirrorContainerVerticalPaddingDp +
- kInsideBorderVerticalPaddingDp + 8));
- }
- for (auto* window : windows) {
- // |mirror_container_| owns |view|. The |preview_view_| in |view| will
- // use trilinear filtering in InitLayerOwner().
- auto* view = mirror_container_->AddChildView(
- std::make_unique<WindowCycleItemView>(window));
- window_view_map_[window] = view;
- no_previews_set_.insert(view);
- }
- // The insets in the WindowCycleItemView are coming from its border, which
- // paints the focus ring around the view when it is highlighted. Exclude the
- // insets such that the spacing between the contents of the views rather
- // than the views themselves is |kBetweenChildPaddingDp|.
- const gfx::Insets cycle_item_insets =
- window_view_map_.empty() ? gfx::Insets()
- : window_view_map_.begin()->second->GetInsets();
- layout->set_between_child_spacing(kBetweenChildPaddingDp -
- cycle_item_insets.width());
- }
- WindowCycleView::~WindowCycleView() = default;
- void WindowCycleView::ScaleCycleView(const gfx::Rect& screen_bounds) {
- auto* layer_animator = layer()->GetAnimator();
- if (layer_animator->is_animating()) {
- // There is an existing scaling animation occurring. To accurately get the
- // new bounds for the next layout, we must abort the ongoing animation so
- // |this| will set the previous bounds of the widget and clear the clip
- // rect.
- layer_animator->AbortAllAnimations();
- }
- // |screen_bounds| is in screen coords so store it in local coordinates in
- // |new_bounds|.
- gfx::Rect old_bounds = GetLocalBounds();
- gfx::Rect new_bounds = gfx::Rect(screen_bounds.size());
- if (old_bounds == new_bounds)
- return;
- if (new_bounds.width() >= old_bounds.width()) {
- // In this case, the cycle view is growing. To achieve the scaling
- // animation we set the widget bounds immediately and scale the clipping
- // rect of |this|'s layer from where the |old_bounds| would be in the
- // new local coordinates.
- GetWidget()->SetBounds(screen_bounds);
- old_bounds +=
- gfx::Vector2d((new_bounds.width() - old_bounds.width()) / 2, 0);
- } else {
- // In this case, the cycle view is shrinking. To achieve the scaling
- // animation, we first scale the clipping rect and defer updating the
- // widget's bounds to when the animation is complete. If we instantly
- // laid out, then it wouldn't appear as though the background is
- // shrinking.
- new_bounds +=
- gfx::Vector2d((old_bounds.width() - new_bounds.width()) / 2, 0);
- defer_widget_bounds_update_ = true;
- }
- layer()->SetClipRect(old_bounds);
- ui::ScopedLayerAnimationSettings settings(layer_animator);
- settings.SetTransitionDuration(kToggleModeScaleDuration);
- settings.SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN_2);
- settings.SetPreemptionStrategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
- settings.AddObserver(this);
- layer()->SetClipRect(new_bounds);
- }
- gfx::Rect WindowCycleView::GetTargetBounds() const {
- // The widget is sized clamped to the screen bounds. Its child, the mirror
- // container which is parent to all the previews may be larger than the
- // widget as some previews will be offscreen. In Layout() of |cycle_view_|
- // the mirror container will be slid back and forth depending on the target
- // window.
- gfx::Rect widget_rect = root_window_->GetBoundsInScreen();
- widget_rect.ClampToCenteredSize(GetPreferredSize());
- return widget_rect;
- }
- void WindowCycleView::UpdateWindows(const WindowList& windows) {
- const bool no_windows = windows.empty();
- const bool is_interactive_alt_tab_mode_allowed =
- Shell::Get()->window_cycle_controller()->IsInteractiveAltTabModeAllowed();
- if (is_interactive_alt_tab_mode_allowed) {
- DCHECK(no_recent_items_label_);
- no_recent_items_label_->SetVisible(no_windows);
- }
- if (no_windows)
- return;
- for (auto* window : windows) {
- auto* view = mirror_container_->AddChildView(
- std::make_unique<WindowCycleItemView>(window));
- window_view_map_[window] = view;
- no_previews_set_.insert(view);
- }
- // If there was an ongoing drag session, it's now been completed so reset
- // |horizontal_distance_dragged_|.
- horizontal_distance_dragged_ = 0.f;
- gfx::Rect widget_rect = GetTargetBounds();
- if (is_interactive_alt_tab_mode_allowed)
- ScaleCycleView(widget_rect);
- else
- GetWidget()->SetBounds(widget_rect);
- SetTargetWindow(windows[0]);
- ScrollToWindow(windows[0]);
- }
- void WindowCycleView::FadeInLayer() {
- DCHECK(GetWidget());
- layer()->SetOpacity(0.f);
- ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator());
- settings.SetTransitionDuration(kFadeInDuration);
- settings.AddObserver(this);
- settings.CacheRenderSurface();
- ui::AnimationThroughputReporter reporter(
- settings.GetAnimator(),
- metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) {
- UMA_HISTOGRAM_PERCENTAGE(kShowAnimationSmoothness, smoothness);
- })));
- layer()->SetOpacity(1.f);
- }
- void WindowCycleView::ScrollToWindow(aura::Window* target) {
- current_window_ = target;
- // If there was an ongoing drag session, it's now been completed so reset
- // |horizontal_distance_dragged_|.
- horizontal_distance_dragged_ = 0.f;
- if (GetWidget())
- Layout();
- }
- void WindowCycleView::SetTargetWindow(aura::Window* target) {
- // Hide the focus border of the previous target window and show the focus
- // border of the new one.
- if (target_window_) {
- auto target_it = window_view_map_.find(target_window_);
- if (target_it != window_view_map_.end())
- target_it->second->UpdateBorderState(/*show=*/false);
- }
- target_window_ = target;
- auto target_it = window_view_map_.find(target_window_);
- if (target_it != window_view_map_.end())
- target_it->second->UpdateBorderState(/*show=*/true);
- // Focus the target window if the user is not currently switching the mode
- // while ChromeVox is on.
- // During the mode switch, we prevent ChromeVox auto-announce the window
- // title from the focus and send our custom string to announce both window
- // title and the selected mode together
- // (see `WindowCycleController::OnModeChanged`).
- auto* a11y_controller = Shell::Get()->accessibility_controller();
- auto* window_cycle_controller = Shell::Get()->window_cycle_controller();
- const bool chromevox_enabled = a11y_controller->spoken_feedback().enabled();
- const bool is_switching_mode = window_cycle_controller->IsSwitchingMode();
- if (!target_window_ || (chromevox_enabled && is_switching_mode))
- return;
- if (GetWidget()) {
- window_view_map_[target_window_]->RequestFocus();
- } else {
- SetInitiallyFocusedView(window_view_map_[target_window_]);
- // When alt-tab mode selection is available, announce via ChromeVox the
- // current mode and the directional cue for mode switching.
- if (window_cycle_controller->IsInteractiveAltTabModeAllowed()) {
- a11y_controller->TriggerAccessibilityAlertWithMessage(
- l10n_util::GetStringUTF8(
- window_cycle_controller->IsAltTabPerActiveDesk()
- ? IDS_ASH_ALT_TAB_FOCUS_CURRENT_DESK_MODE
- : IDS_ASH_ALT_TAB_FOCUS_ALL_DESKS_MODE));
- }
- }
- }
- void WindowCycleView::HandleWindowDestruction(aura::Window* destroying_window,
- aura::Window* new_target) {
- auto view_iter = window_view_map_.find(destroying_window);
- WindowCycleItemView* preview = view_iter->second;
- views::View* parent = preview->parent();
- DCHECK_EQ(mirror_container_, parent);
- window_view_map_.erase(view_iter);
- no_previews_set_.erase(preview);
- delete preview;
- // With one of its children now gone, we must re-layout |mirror_container_|.
- // This must happen before ScrollToWindow() to make sure our own Layout()
- // works correctly when it's calculating highlight bounds.
- parent->Layout();
- SetTargetWindow(new_target);
- ScrollToWindow(new_target);
- }
- void WindowCycleView::DestroyContents() {
- is_destroying_ = true;
- window_view_map_.clear();
- no_previews_set_.clear();
- target_window_ = nullptr;
- current_window_ = nullptr;
- defer_widget_bounds_update_ = false;
- RemoveAllChildViews();
- OnFlingEnd();
- }
- void WindowCycleView::Drag(float delta_x) {
- horizontal_distance_dragged_ += delta_x;
- Layout();
- }
- void WindowCycleView::StartFling(float velocity_x) {
- fling_handler_ = std::make_unique<WmFlingHandler>(
- gfx::Vector2dF(velocity_x, 0),
- GetWidget()->GetNativeWindow()->GetRootWindow(),
- base::BindRepeating(&WindowCycleView::OnFlingStep,
- base::Unretained(this)),
- base::BindRepeating(&WindowCycleView::OnFlingEnd,
- base::Unretained(this)));
- }
- bool WindowCycleView::OnFlingStep(float offset) {
- DCHECK(fling_handler_);
- horizontal_distance_dragged_ += offset;
- Layout();
- return true;
- }
- void WindowCycleView::OnFlingEnd() {
- fling_handler_.reset();
- }
- void WindowCycleView::SetFocusTabSlider(bool focus) {
- tab_slider_container_->SetFocus(focus);
- }
- bool WindowCycleView::IsTabSliderFocused() {
- DCHECK(tab_slider_container_);
- return tab_slider_container_->is_focused();
- }
- aura::Window* WindowCycleView::GetWindowAtPoint(
- const gfx::Point& screen_point) {
- for (const auto& entry : window_view_map_) {
- if (entry.second->GetBoundsInScreen().Contains(screen_point))
- return entry.first;
- }
- return nullptr;
- }
- void WindowCycleView::OnModePrefsChanged() {
- if (tab_slider_container_)
- tab_slider_container_->OnModePrefsChanged();
- }
- gfx::Size WindowCycleView::CalculatePreferredSize() const {
- gfx::Size size = GetContentContainerBounds().size();
- // |mirror_container_| can have window list that overflow out of the
- // screen, but the window cycle view with a bandshield, cropping the
- // overflow window list, should remain within the specified horizontal
- // insets of the screen width.
- const int max_width = CalculateMaxWidth();
- size.set_width(std::min(size.width(), max_width));
- if (Shell::Get()
- ->window_cycle_controller()
- ->IsInteractiveAltTabModeAllowed()) {
- DCHECK(tab_slider_container_);
- // |mirror_container_| can have window list with width smaller the tab
- // slider's width. The padding should be 64px from the tab slider.
- const int min_width = tab_slider_container_->GetPreferredSize().width() +
- 2 * WindowCycleView::kInsideBorderHorizontalPaddingDp;
- size.set_width(std::max(size.width(), min_width));
- size.Enlarge(0, tab_slider_container_->GetPreferredSize().height() +
- kTabSliderContainerVerticalPaddingDp);
- }
- return size;
- }
- void WindowCycleView::Layout() {
- if (is_destroying_)
- return;
- const bool is_interactive_alt_tab_mode_allowed =
- Shell::Get()->window_cycle_controller()->IsInteractiveAltTabModeAllowed();
- if (bounds().IsEmpty() || (!is_interactive_alt_tab_mode_allowed &&
- (!target_window_ || !current_window_))) {
- return;
- }
- const bool first_layout = mirror_container_->bounds().IsEmpty();
- // If |mirror_container_| has not yet been laid out, we must lay it and
- // its descendants out so that the calculations based on |target_view|
- // work properly.
- if (first_layout) {
- mirror_container_->SizeToPreferredSize();
- layer()->SetRoundedCornerRadius(
- gfx::RoundedCornersF{kBackgroundCornerRadius});
- }
- gfx::RectF target_bounds;
- if (current_window_ || !is_interactive_alt_tab_mode_allowed) {
- views::View* target_view = window_view_map_[current_window_];
- target_bounds = gfx::RectF(target_view->GetLocalBounds());
- views::View::ConvertRectToTarget(target_view, mirror_container_,
- &target_bounds);
- } else {
- DCHECK(no_recent_items_label_);
- target_bounds = gfx::RectF(no_recent_items_label_->bounds());
- }
- // Content container represents the mirror container with >=1 windows or
- // no-recent-items label when there is no window to be shown.
- gfx::Rect content_container_bounds = GetContentContainerBounds();
- // Case one: the container is narrower than the screen. Center the
- // container.
- int x_offset = (width() - content_container_bounds.width()) / 2;
- if (x_offset < 0) {
- // Case two: the container is wider than the screen. Center the target
- // view by moving the list just enough to ensure the target view is in
- // the center. Additionally, offset by however much the user has dragged.
- x_offset = width() / 2 - mirror_container_->GetMirroredXInView(
- target_bounds.CenterPoint().x());
- // However, the container must span the screen, i.e. the maximum x is 0
- // and the minimum for its right boundary is the width of the screen.
- int minimum_x = width() - content_container_bounds.width();
- x_offset = base::clamp(x_offset, minimum_x, 0);
- // If the user has dragged, offset the container based on how much they
- // have dragged. Cap |horizontal_distance_dragged_| based on the available
- // distance from the container to the left and right boundaries.
- float clamped_horizontal_distance_dragged = base::clamp(
- horizontal_distance_dragged_, static_cast<float>(minimum_x - x_offset),
- static_cast<float>(-x_offset));
- if (horizontal_distance_dragged_ != clamped_horizontal_distance_dragged)
- OnFlingEnd();
- horizontal_distance_dragged_ = clamped_horizontal_distance_dragged;
- x_offset += horizontal_distance_dragged_;
- }
- content_container_bounds.set_x(x_offset);
- // Layout a tab slider if there is more than one desk.
- if (is_interactive_alt_tab_mode_allowed) {
- // TODO(crbug.com/1216238): Change these back to DCHECKs once the bug is
- // resolved.
- CHECK(tab_slider_container_);
- CHECK(no_recent_items_label_);
- // Layout the tab slider.
- const gfx::Size tab_slider_size = tab_slider_container_->GetPreferredSize();
- const gfx::Rect tab_slider_mirror_container_bounds(
- (width() - tab_slider_size.width()) / 2,
- kTabSliderContainerVerticalPaddingDp, tab_slider_size.width(),
- tab_slider_size.height());
- tab_slider_container_->SetBoundsRect(tab_slider_mirror_container_bounds);
- // Move window cycle container down.
- content_container_bounds.set_y(tab_slider_container_->y() +
- tab_slider_container_->height());
- // Unlike the bounds of scrollable mirror container, the bounds of label
- // should not overflow out of the screen.
- const gfx::Rect no_recent_item_bounds_(
- std::max(0, content_container_bounds.x()), content_container_bounds.y(),
- std::min(width(), content_container_bounds.width()),
- content_container_bounds.height());
- no_recent_items_label_->SetBoundsRect(no_recent_item_bounds_);
- }
- // Enable animations only after the first Layout() pass. If |this| is
- // animating or |defer_widget_bounds_update_|, don't animate as well since
- // the cycle view is already being animated or just finished animating for
- // mode switch.
- std::unique_ptr<ui::ScopedLayerAnimationSettings> settings;
- absl::optional<ui::AnimationThroughputReporter> reporter;
- if (!first_layout && !this->layer()->GetAnimator()->is_animating() &&
- !defer_widget_bounds_update_ &&
- mirror_container_->bounds() != content_container_bounds) {
- settings = std::make_unique<ui::ScopedLayerAnimationSettings>(
- mirror_container_->layer()->GetAnimator());
- settings->SetTransitionDuration(kContainerSlideDuration);
- reporter.emplace(
- settings->GetAnimator(),
- metrics_util::ForSmoothness(base::BindRepeating([](int smoothness) {
- // Reports animation metrics when the mirror container, which holds
- // all the preview views slides along the x-axis. This can happen
- // while tabbing through windows, if the window cycle ui spans the
- // length of the display.
- UMA_HISTOGRAM_PERCENTAGE(kContainerAnimationSmoothness, smoothness);
- })));
- }
- mirror_container_->SetBoundsRect(content_container_bounds);
- // If an element in |no_previews_set_| is no onscreen (its bounds in |this|
- // coordinates intersects |this|), create the rest of its elements and
- // remove it from the set.
- const gfx::RectF local_bounds(GetLocalBounds());
- for (auto it = no_previews_set_.begin(); it != no_previews_set_.end();) {
- WindowCycleItemView* view = *it;
- gfx::RectF bounds(view->GetLocalBounds());
- views::View::ConvertRectToTarget(view, this, &bounds);
- if (bounds.Intersects(local_bounds)) {
- view->ShowPreview();
- it = no_previews_set_.erase(it);
- } else {
- ++it;
- }
- }
- }
- void WindowCycleView::OnImplicitAnimationsCompleted() {
- occlusion_tracker_pauser_.reset();
- layer()->SetClipRect(gfx::Rect());
- if (defer_widget_bounds_update_) {
- // This triggers a Layout() so reset |defer_widget_bounds_update_| after
- // calling SetBounds() to prevent the mirror container from animating.
- GetWidget()->SetBounds(GetTargetBounds());
- defer_widget_bounds_update_ = false;
- }
- }
- void WindowCycleView::OnThemeChanged() {
- views::View::OnThemeChanged();
- background()->SetNativeControlColor(
- AshColorProvider::Get()->GetBaseLayerColor(
- AshColorProvider::BaseLayerType::kTransparent80));
- if (chromeos::features::IsDarkLightModeEnabled()) {
- SetBorder(std::make_unique<views::HighlightBorder>(
- kBackgroundCornerRadius,
- views::HighlightBorder::Type::kHighlightBorder1,
- /*use_light_colors=*/false));
- }
- }
- bool WindowCycleView::IsEventInTabSliderContainer(
- const gfx::Point& screen_point) {
- return tab_slider_container_ &&
- tab_slider_container_->GetBoundsInScreen().Contains(screen_point);
- }
- int WindowCycleView::CalculateMaxWidth() const {
- return root_window_->GetBoundsInScreen().size().width() -
- 2 * kBackgroundHorizontalInsetDp;
- }
- gfx::Rect WindowCycleView::GetContentContainerBounds() const {
- const bool empty_mirror_container = mirror_container_->children().empty();
- if (empty_mirror_container && no_recent_items_label_)
- return gfx::Rect(no_recent_items_label_->GetPreferredSize());
- return gfx::Rect(mirror_container_->GetPreferredSize());
- }
- BEGIN_METADATA(WindowCycleView, views::WidgetDelegateView)
- END_METADATA
- } // namespace ash
|