123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- // Copyright 2020 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/ambient/ui/ambient_background_image_view.h"
- #include <memory>
- #include "ash/ambient/ambient_constants.h"
- #include "ash/ambient/ui/ambient_info_view.h"
- #include "ash/ambient/ui/ambient_shield_view.h"
- #include "ash/ambient/ui/ambient_view_ids.h"
- #include "ash/ambient/ui/jitter_calculator.h"
- #include "ash/ambient/ui/media_string_view.h"
- #include "ash/ambient/util/ambient_util.h"
- #include "ash/shell.h"
- #include "base/no_destructor.h"
- #include "base/rand_util.h"
- #include "ui/base/metadata/metadata_impl_macros.h"
- #include "ui/compositor/layer.h"
- #include "ui/display/display.h"
- #include "ui/display/manager/display_manager.h"
- #include "ui/display/manager/managed_display_info.h"
- #include "ui/display/screen.h"
- #include "ui/events/event.h"
- #include "ui/gfx/geometry/insets.h"
- #include "ui/gfx/geometry/vector2d.h"
- #include "ui/gfx/image/image_skia_operations.h"
- #include "ui/gfx/skbitmap_operations.h"
- #include "ui/views/controls/image_view.h"
- #include "ui/views/layout/box_layout.h"
- #include "ui/views/layout/fill_layout.h"
- #include "ui/views/layout/flex_layout.h"
- #include "ui/views/layout/flex_layout_types.h"
- #include "ui/views/view_class_properties.h"
- namespace ash {
- namespace {
- // Appearance.
- constexpr int kMediaStringMarginDip = 32;
- gfx::ImageSkia ResizeImage(const gfx::ImageSkia& image,
- const gfx::Size& view_size) {
- if (image.isNull())
- return gfx::ImageSkia();
- const double image_width = image.width();
- const double image_height = image.height();
- const double view_width = view_size.width();
- const double view_height = view_size.height();
- const double horizontal_ratio = view_width / image_width;
- const double vertical_ratio = view_height / image_height;
- const double image_ratio = image_height / image_width;
- const double view_ratio = view_height / view_width;
- // If the image and the container view has the same orientation, e.g. both
- // portrait, the |scale| will make the image filled the whole view with
- // possible cropping on one direction. If they are in different orientation,
- // the |scale| will display the image in the view without any cropping, but
- // with empty background.
- const double scale = (image_ratio - 1) * (view_ratio - 1) > 0
- ? std::max(horizontal_ratio, vertical_ratio)
- : std::min(horizontal_ratio, vertical_ratio);
- const gfx::Size& resized = gfx::ScaleToCeiledSize(image.size(), scale);
- return gfx::ImageSkiaOperations::CreateResizedImage(
- image, skia::ImageOperations::RESIZE_BEST, resized);
- }
- gfx::ImageSkia MaybeRotateImage(const gfx::ImageSkia& image,
- const gfx::Size& view_size,
- views::Widget* widget) {
- if (image.isNull())
- return image;
- const double image_width = image.width();
- const double image_height = image.height();
- const double view_width = view_size.width();
- const double view_height = view_size.height();
- const double image_ratio = image_height / image_width;
- const double view_ratio = view_height / view_width;
- // Rotate the image to have the same orientation as the display.
- // Keep the relative orientation between the image and the display in portrait
- // mode.
- if ((image_ratio - 1) * (view_ratio - 1) < 0) {
- bool should_rotate = false;
- SkBitmapOperations::RotationAmount rotation_amount;
- const int64_t display_id =
- display::Screen::GetScreen()
- ->GetDisplayNearestWindow(widget->GetNativeWindow())
- .id();
- const auto active_rotation = Shell::Get()
- ->display_manager()
- ->GetDisplayInfo(display_id)
- .GetActiveRotation();
- switch (active_rotation) {
- case display::Display::ROTATE_90:
- should_rotate = true;
- rotation_amount = SkBitmapOperations::RotationAmount::ROTATION_270_CW;
- break;
- case display::Display::ROTATE_270:
- should_rotate = true;
- rotation_amount = SkBitmapOperations::RotationAmount::ROTATION_90_CW;
- break;
- default:
- NOTREACHED();
- break;
- }
- if (should_rotate) {
- return gfx::ImageSkiaOperations::CreateRotatedImage(image,
- rotation_amount);
- }
- }
- return image;
- }
- } // namespace
- AmbientBackgroundImageView::AmbientBackgroundImageView(
- AmbientViewDelegate* delegate,
- JitterCalculator* glanceable_info_jitter_calculator)
- : delegate_(delegate),
- glanceable_info_jitter_calculator_(glanceable_info_jitter_calculator) {
- DCHECK(delegate_);
- DCHECK(glanceable_info_jitter_calculator_);
- SetID(AmbientViewID::kAmbientBackgroundImageView);
- InitLayout();
- }
- AmbientBackgroundImageView::~AmbientBackgroundImageView() = default;
- void AmbientBackgroundImageView::OnBoundsChanged(
- const gfx::Rect& previous_bounds) {
- if (!GetVisible())
- return;
- if (width() == 0)
- return;
- UpdateLayout();
- // When bounds changes, recalculate the visibility of related image view.
- UpdateRelatedImageViewVisibility();
- UpdateImageDetails(details_, related_details_);
- }
- void AmbientBackgroundImageView::OnViewBoundsChanged(
- views::View* observed_view) {
- if (observed_view == image_view_)
- SetResizedImage(image_view_, image_unscaled_);
- else
- SetResizedImage(related_image_view_, related_image_unscaled_);
- }
- void AmbientBackgroundImageView::UpdateImage(
- const gfx::ImageSkia& image,
- const gfx::ImageSkia& related_image,
- bool is_portrait,
- ::ambient::TopicType type) {
- image_unscaled_ = image;
- related_image_unscaled_ = related_image;
- is_portrait_ = is_portrait;
- topic_type_ = type;
- UpdateGlanceableInfoPosition();
- const bool has_change = UpdateRelatedImageViewVisibility();
- // If there is no change in the visibility of related image view, call
- // SetResizedImages() directly. Otherwise it will be called from
- // OnViewBoundsChanged().
- if (!has_change) {
- SetResizedImage(image_view_, image_unscaled_);
- SetResizedImage(related_image_view_, related_image_unscaled_);
- }
- }
- void AmbientBackgroundImageView::UpdateImageDetails(
- const std::u16string& details,
- const std::u16string& related_details) {
- details_ = details;
- related_details_ = related_details;
- ambient_info_view_->UpdateImageDetails(
- details, MustShowPairs() ? related_details : std::u16string());
- }
- gfx::ImageSkia AmbientBackgroundImageView::GetCurrentImage() {
- return image_view_->GetImage();
- }
- gfx::Rect AmbientBackgroundImageView::GetImageBoundsInScreenForTesting() const {
- gfx::Rect rect = image_view_->GetImageBounds();
- views::View::ConvertRectToScreen(image_view_, &rect);
- return rect;
- }
- gfx::Rect AmbientBackgroundImageView::GetRelatedImageBoundsInScreenForTesting()
- const {
- if (!related_image_view_->GetVisible())
- return gfx::Rect();
- gfx::Rect rect = related_image_view_->GetImageBounds();
- views::View::ConvertRectToScreen(related_image_view_, &rect);
- return rect;
- }
- void AmbientBackgroundImageView::ResetRelatedImageForTesting() {
- related_image_unscaled_ = gfx::ImageSkia();
- UpdateRelatedImageViewVisibility();
- }
- void AmbientBackgroundImageView::InitLayout() {
- static const views::FlexSpecification kUnboundedScaleToZero(
- views::MinimumFlexSizeRule::kScaleToZero,
- views::MaximumFlexSizeRule::kUnbounded);
- SetLayoutManager(std::make_unique<views::FillLayout>());
- // Inits container for images.
- image_container_ = AddChildView(std::make_unique<views::View>());
- image_layout_ =
- image_container_->SetLayoutManager(std::make_unique<views::FlexLayout>());
- image_view_ =
- image_container_->AddChildView(std::make_unique<views::ImageView>());
- // Set a place holder size for Flex layout to assign bounds.
- image_view_->SetPreferredSize(gfx::Size(1, 1));
- image_view_->SetProperty(views::kFlexBehaviorKey, kUnboundedScaleToZero);
- observed_views_.AddObservation(image_view_);
- related_image_view_ =
- image_container_->AddChildView(std::make_unique<views::ImageView>());
- // Set a place holder size for Flex layout to assign bounds.
- related_image_view_->SetPreferredSize(gfx::Size(1, 1));
- related_image_view_->SetProperty(views::kFlexBehaviorKey,
- kUnboundedScaleToZero);
- observed_views_.AddObservation(related_image_view_);
- AddChildView(std::make_unique<AmbientShieldView>());
- ambient_info_view_ =
- AddChildView(std::make_unique<AmbientInfoView>(delegate_));
- gfx::Insets shadow_insets =
- gfx::ShadowValue::GetMargin(ambient::util::GetTextShadowValues(nullptr));
- // Inits the media string view. The media string view is positioned on the
- // right-top corner of the container.
- views::View* media_string_view_container_ =
- AddChildView(std::make_unique<views::View>());
- views::BoxLayout* media_string_layout =
- media_string_view_container_->SetLayoutManager(
- std::make_unique<views::BoxLayout>(
- views::BoxLayout::Orientation::kVertical));
- media_string_layout->set_main_axis_alignment(
- views::BoxLayout::MainAxisAlignment::kStart);
- media_string_layout->set_cross_axis_alignment(
- views::BoxLayout::CrossAxisAlignment::kEnd);
- media_string_layout->set_inside_border_insets(
- gfx::Insets::TLBR(kMediaStringMarginDip + shadow_insets.top(), 0, 0,
- kMediaStringMarginDip + shadow_insets.right()));
- media_string_view_ = media_string_view_container_->AddChildView(
- std::make_unique<MediaStringView>(MediaStringView::Settings(
- {/*icon_light_mode_color=*/ambient::util::GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorPrimary,
- /*dark_mode_enable=*/false),
- /*icon_dark_mode_color=*/
- ambient::util::GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorPrimary,
- /*dark_mode_enable=*/true),
- /*text_light_mode_color=*/
- ambient::util::GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary,
- /*dark_mode_enable=*/false),
- /*text_dark_mode_color=*/
- ambient::util::GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary,
- /*dark_mode_enable=*/true),
- /*text_shadow_elevation=*/
- ambient::util::kDefaultTextShadowElevation})));
- media_string_view_->SetVisible(false);
- }
- void AmbientBackgroundImageView::UpdateGlanceableInfoPosition() {
- gfx::Vector2d jitter = glanceable_info_jitter_calculator_->Calculate();
- gfx::Transform transform;
- transform.Translate(jitter);
- ambient_info_view_->SetTextTransform(transform);
- if (media_string_view_->GetVisible()) {
- gfx::Transform media_string_transform;
- media_string_transform.Translate(-jitter.x(), -jitter.y());
- media_string_view_->layer()->SetTransform(media_string_transform);
- }
- }
- void AmbientBackgroundImageView::UpdateLayout() {
- if (width() > height()) {
- image_layout_->SetOrientation(views::LayoutOrientation::kHorizontal);
- // Set spacing between two images.
- related_image_view_->SetProperty(
- views::kMarginsKey,
- gfx::Insets::TLBR(0, kMarginLeftOfRelatedImageDip, 0, 0));
- } else {
- image_layout_->SetOrientation(views::LayoutOrientation::kVertical);
- // Set spacing between two images.
- related_image_view_->SetProperty(
- views::kMarginsKey,
- gfx::Insets::TLBR(kMarginLeftOfRelatedImageDip, 0, 0, 0));
- }
- image_layout_->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
- image_layout_->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
- }
- bool AmbientBackgroundImageView::UpdateRelatedImageViewVisibility() {
- const bool did_show_pair = related_image_view_->GetVisible();
- const bool show_pair = MustShowPairs() && HasPairedImages();
- related_image_view_->SetVisible(show_pair);
- return did_show_pair != show_pair;
- }
- void AmbientBackgroundImageView::SetResizedImage(
- views::ImageView* image_view,
- const gfx::ImageSkia& image_unscaled) {
- if (!image_view->GetVisible())
- return;
- if (image_unscaled.isNull())
- return;
- gfx::ImageSkia image_rotated =
- topic_type_ == ::ambient::TopicType::kGeo
- ? MaybeRotateImage(image_unscaled, image_view->size(), GetWidget())
- : image_unscaled;
- image_view->SetImage(ResizeImage(image_rotated, image_view->size()));
- // Intend to update the image origin in image view.
- // There is no bounds change or preferred size change when updating image from
- // landscape to portrait when device is in portrait orientation because we
- // only show one photo. Call ResetImageSize() to trigger UpdateImageOrigin().
- image_view->ResetImageSize();
- }
- bool AmbientBackgroundImageView::MustShowPairs() const {
- const bool landscape_mode_portrait_image = width() > height() && is_portrait_;
- const bool portrait_mode_landscape_image =
- width() < height() && !is_portrait_;
- return landscape_mode_portrait_image || portrait_mode_landscape_image;
- }
- bool AmbientBackgroundImageView::HasPairedImages() const {
- return !image_unscaled_.isNull() && !related_image_unscaled_.isNull();
- }
- BEGIN_METADATA(AmbientBackgroundImageView, views::View)
- END_METADATA
- } // namespace ash
|