Commit 53c1dd90 authored by Malay Keshav's avatar Malay Keshav Committed by Commit Bot

Integrates SkiaVectorAnimation with views

This patch integrates SkiaVectorAnimation class with the views
framework. Since most of the code is shared with ImageView, a new
virtual super class is introduced, ImageViewBase. AnimatedImageView is
added as a subclass of this virtual class.

For now the integration class is a very basic view that can play and
stop a given Skia vector animation at a custom size.

This patch also adds an example to views/examples to load animations
and play.

Design doc: go/cros-skottie

Bug: 890221
Change-Id: I55d7310ef74cdfb825cce441c8e61eadff9c5616
Component: Views, examples, skottie, SkiaVectorAnimation
Reviewed-on: https://chromium-review.googlesource.com/c/1262967
Commit-Queue: Malay Keshav <malaykeshav@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597646}
parent d7db601c
......@@ -95,6 +95,7 @@ jumbo_component("views") {
"color_chooser/color_chooser_view.h",
"context_menu_controller.h",
"controls/animated_icon_view.h",
"controls/animated_image_view.h",
"controls/button/blue_button.h",
"controls/button/button.h",
"controls/button/checkbox.h",
......@@ -112,6 +113,7 @@ jumbo_component("views") {
"controls/focus_ring.h",
"controls/focusable_border.h",
"controls/image_view.h",
"controls/image_view_base.h",
"controls/label.h",
"controls/link.h",
"controls/link_listener.h",
......@@ -292,6 +294,7 @@ jumbo_component("views") {
"button_drag_utils.cc",
"color_chooser/color_chooser_view.cc",
"controls/animated_icon_view.cc",
"controls/animated_image_view.cc",
"controls/button/blue_button.cc",
"controls/button/button.cc",
"controls/button/checkbox.cc",
......@@ -307,6 +310,7 @@ jumbo_component("views") {
"controls/focus_ring.cc",
"controls/focusable_border.cc",
"controls/image_view.cc",
"controls/image_view_base.cc",
"controls/label.cc",
"controls/link.cc",
"controls/menu/display_change_listener_mac.cc",
......
// 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 "ui/views/controls/animated_image_view.h"
#include <utility>
#include "base/logging.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/skottie_wrapper.h"
#include "ui/views/widget/widget.h"
namespace views {
namespace {
bool AreAnimatedImagesEqual(const gfx::SkiaVectorAnimation& animation_1,
const gfx::SkiaVectorAnimation& animation_2) {
// In rare cases this may return false, even if the animated images are backed
// by the same resource file.
return animation_1.skottie() == animation_2.skottie();
}
} // namespace
AnimatedImageView::AnimatedImageView() = default;
AnimatedImageView::~AnimatedImageView() = default;
void AnimatedImageView::SetAnimatedImage(
std::unique_ptr<gfx::SkiaVectorAnimation> animated_image) {
if (animated_image_ &&
AreAnimatedImagesEqual(*animated_image, *animated_image_)) {
Stop();
return;
}
gfx::Size preferred_size(GetPreferredSize());
animated_image_ = std::move(animated_image);
// Stop the animation to reset it.
Stop();
if (preferred_size != GetPreferredSize())
PreferredSizeChanged();
SchedulePaint();
}
void AnimatedImageView::Play() {
DCHECK(animated_image_);
DCHECK_EQ(state_, State::kStopped);
state_ = State::kPlaying;
// We cannot play the animation unless we have a valid compositor.
if (!compositor_)
return;
// Ensure the class is added as an observer to receive clock ticks.
if (!compositor_->HasAnimationObserver(this))
compositor_->AddAnimationObserver(this);
animated_image_->Start();
}
void AnimatedImageView::Stop() {
DCHECK(animated_image_);
if (compositor_)
compositor_->RemoveAnimationObserver(this);
animated_image_->Stop();
state_ = State::kStopped;
}
gfx::Size AnimatedImageView::GetImageSize() const {
return image_size_.value_or(
animated_image_ ? animated_image_->GetOriginalSize() : gfx::Size());
}
void AnimatedImageView::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas);
if (!animated_image_)
return;
canvas->Save();
canvas->Translate(GetImageBounds().origin().OffsetFromOrigin());
// OnPaint may be called before clock tick was received; in that case just
// paint the first frame.
if (!previous_timestamp_.is_null() && state_ != State::kStopped)
animated_image_->Paint(canvas, previous_timestamp_, GetImageSize());
else
animated_image_->PaintFrame(canvas, 0, GetImageSize());
canvas->Restore();
}
const char* AnimatedImageView::GetClassName() const {
return "AnimatedImageView";
}
void AnimatedImageView::NativeViewHierarchyChanged() {
// When switching a window from one display to another, the compositor
// associated with the widget changes.
AddedToWidget();
}
void AnimatedImageView::AddedToWidget() {
ui::Compositor* compositor = GetWidget()->GetCompositor();
DCHECK(compositor);
if (compositor_ != compositor) {
if (compositor_ && compositor_->HasAnimationObserver(this))
compositor_->RemoveAnimationObserver(this);
compositor_ = compositor;
}
}
void AnimatedImageView::RemovedFromWidget() {
if (compositor_) {
Stop();
if (compositor_->HasAnimationObserver(this))
compositor_->RemoveAnimationObserver(this);
compositor_ = nullptr;
}
}
void AnimatedImageView::OnAnimationStep(base::TimeTicks timestamp) {
previous_timestamp_ = timestamp;
SchedulePaint();
}
void AnimatedImageView::OnCompositingShuttingDown(ui::Compositor* compositor) {
if (compositor_ == compositor) {
Stop();
compositor_->RemoveAnimationObserver(this);
compositor_ = nullptr;
}
}
} // namespace views
// 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.
#ifndef UI_VIEWS_CONTROLS_ANIMATED_IMAGE_VIEW_H_
#define UI_VIEWS_CONTROLS_ANIMATED_IMAGE_VIEW_H_
#include <memory>
#include "base/macros.h"
#include "ui/gfx/skia_vector_animation.h"
#include "ui/views/controls/image_view_base.h"
namespace gfx {
class SkiaVectorAnimation;
class Canvas;
} // namespace gfx
namespace ui {
class Compositor;
}
namespace views {
/////////////////////////////////////////////////////////////////////////////
//
// AnimatedImageView class.
//
// An AnimatedImageView can display a skia vector animation. The animation paint
// size can be set via SetImageSize. The animation is stopped by default.
// Use this over AnimatedIconView if you want to play a skottie animation file.
//
/////////////////////////////////////////////////////////////////////////////
class VIEWS_EXPORT AnimatedImageView : public ImageViewBase,
public ui::CompositorAnimationObserver {
public:
enum class State {
kPlaying, // The animation is currently playing.
kStopped // The animation is stopped and paint will raster the first
// frame.
};
AnimatedImageView();
~AnimatedImageView() override;
// Set the animated image that should be displayed. Setting an animated image
// will result in stopping the current animation.
void SetAnimatedImage(
std::unique_ptr<gfx::SkiaVectorAnimation> animated_image);
// Plays the animation in loop.
void Play();
// Stops any animation and resets it to the start frame.
void Stop();
private:
friend class AnimatedImageViewTest;
// Overridden from View:
void OnPaint(gfx::Canvas* canvas) override;
const char* GetClassName() const override;
void NativeViewHierarchyChanged() override;
void AddedToWidget() override;
void RemovedFromWidget() override;
// Overridden from ui::CompositorAnimationObserver:
void OnAnimationStep(base::TimeTicks timestamp) override;
void OnCompositingShuttingDown(ui::Compositor* compositor) override;
// Overridden from ImageViewBase:
gfx::Size GetImageSize() const override;
// The current state of the animation.
State state_ = State::kStopped;
// The compositor associated with the widget of this view.
ui::Compositor* compositor_ = nullptr;
// The most recent timestamp at which a paint was scheduled for this view.
base::TimeTicks previous_timestamp_;
// The underlying skia vector animation.
std::unique_ptr<gfx::SkiaVectorAnimation> animated_image_;
DISALLOW_COPY_AND_ASSIGN(AnimatedImageView);
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_ANIMATED_IMAGE_VIEW_H_
......@@ -7,12 +7,9 @@
#include <utility>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "cc/paint/paint_flags.h"
#include "skia/ext/image_operations.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
namespace views {
......@@ -29,13 +26,9 @@ void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) {
// static
const char ImageView::kViewClassName[] = "ImageView";
ImageView::ImageView()
: horizontal_alignment_(CENTER),
vertical_alignment_(CENTER),
last_paint_scale_(0.f),
last_painted_bitmap_pixels_(nullptr) {}
ImageView::ImageView() = default;
ImageView::~ImageView() {}
ImageView::~ImageView() = default;
void ImageView::SetImage(const gfx::ImageSkia& img) {
if (IsImageEqual(img))
......@@ -63,20 +56,6 @@ const gfx::ImageSkia& ImageView::GetImage() const {
return image_;
}
void ImageView::SetImageSize(const gfx::Size& image_size) {
image_size_ = image_size;
PreferredSizeChanged();
}
gfx::Rect ImageView::GetImageBounds() const {
gfx::Size image_size = GetImageSize();
return gfx::Rect(ComputeImageOrigin(image_size), image_size);
}
void ImageView::ResetImageSize() {
image_size_.reset();
}
bool ImageView::IsImageEqual(const gfx::ImageSkia& img) const {
// Even though we copy ImageSkia in SetImage() the backing store
// (ImageSkiaStorage) is not copied and may have changed since the last call
......@@ -92,130 +71,15 @@ gfx::Size ImageView::GetImageSize() const {
return image_size_.value_or(image_.size());
}
gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const {
gfx::Insets insets = GetInsets();
int x = 0;
// In order to properly handle alignment of images in RTL locales, we need
// to flip the meaning of trailing and leading. For example, if the
// horizontal alignment is set to trailing, then we'll use left alignment for
// the image instead of right alignment if the UI layout is RTL.
Alignment actual_horizontal_alignment = horizontal_alignment_;
if (base::i18n::IsRTL() && (horizontal_alignment_ != CENTER)) {
actual_horizontal_alignment =
(horizontal_alignment_ == LEADING) ? TRAILING : LEADING;
}
switch (actual_horizontal_alignment) {
case LEADING:
x = insets.left();
break;
case TRAILING:
x = width() - insets.right() - image_size.width();
break;
case CENTER:
x = (width() - insets.width() - image_size.width()) / 2 + insets.left();
break;
}
int y = 0;
switch (vertical_alignment_) {
case LEADING:
y = insets.top();
break;
case TRAILING:
y = height() - insets.bottom() - image_size.height();
break;
case CENTER:
y = (height() - insets.height() - image_size.height()) / 2 + insets.top();
break;
}
return gfx::Point(x, y);
}
void ImageView::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas);
OnPaintImage(canvas);
}
void ImageView::SetAccessibleName(const base::string16& accessible_name) {
accessible_name_ = accessible_name;
}
base::string16 ImageView::GetAccessibleName() const {
return accessible_name_;
}
void ImageView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ax::mojom::Role::kImage;
node_data->SetName(accessible_name_);
}
const char* ImageView::GetClassName() const {
return kViewClassName;
}
void ImageView::SetHorizontalAlignment(Alignment alignment) {
if (alignment != horizontal_alignment_) {
horizontal_alignment_ = alignment;
SchedulePaint();
}
}
ImageView::Alignment ImageView::GetHorizontalAlignment() const {
return horizontal_alignment_;
}
void ImageView::SetVerticalAlignment(Alignment alignment) {
if (alignment != vertical_alignment_) {
vertical_alignment_ = alignment;
SchedulePaint();
}
}
ImageView::Alignment ImageView::GetVerticalAlignment() const {
return vertical_alignment_;
}
// TODO(crbug.com/890465): Update the duplicate code here and in views::Button.
void ImageView::SetTooltipText(const base::string16& tooltip) {
tooltip_text_ = tooltip;
if (accessible_name_.empty())
accessible_name_ = tooltip_text_;
}
base::string16 ImageView::GetTooltipText() const {
return tooltip_text_;
}
bool ImageView::GetTooltipText(const gfx::Point& p,
base::string16* tooltip) const {
if (tooltip_text_.empty())
return false;
*tooltip = GetTooltipText();
return true;
}
gfx::Size ImageView::CalculatePreferredSize() const {
gfx::Size size = GetImageSize();
size.Enlarge(GetInsets().width(), GetInsets().height());
return size;
}
views::PaintInfo::ScaleType ImageView::GetPaintScaleType() const {
// ImageView contains an image which is rastered at the device scale factor.
// By default, the paint commands are recorded at a scale factor slightly
// different from the device scale factor. Re-rastering the image at this
// paint recording scale will result in a distorted image. Paint recording
// scale might also not be uniform along the x & y axis, thus resulting in
// further distortion in the aspect ratio of the final image.
// |kUniformScaling| ensures that the paint recording scale is uniform along
// the x & y axis and keeps the scale equal to the device scale factor.
// See http://crbug.com/754010 for more details.
return views::PaintInfo::ScaleType::kUniformScaling;
}
void ImageView::OnPaintImage(gfx::Canvas* canvas) {
last_paint_scale_ = canvas->image_scale();
last_painted_bitmap_pixels_ = nullptr;
......
......@@ -6,9 +6,8 @@
#define UI_VIEWS_CONTROLS_IMAGE_VIEW_H_
#include "base/macros.h"
#include "base/optional.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/view.h"
#include "ui/views/controls/image_view_base.h"
namespace gfx {
class Canvas;
......@@ -26,17 +25,11 @@ namespace views {
// provided image size.
//
/////////////////////////////////////////////////////////////////////////////
class VIEWS_EXPORT ImageView : public View {
class VIEWS_EXPORT ImageView : public ImageViewBase {
public:
// Internal class name.
static const char kViewClassName[];
enum Alignment {
LEADING = 0,
CENTER,
TRAILING
};
ImageView();
~ImageView() override;
......@@ -52,39 +45,13 @@ class VIEWS_EXPORT ImageView : public View {
// The returned image is still owned by the ImageView.
const gfx::ImageSkia& GetImage() const;
// Set the desired image size for the receiving ImageView.
void SetImageSize(const gfx::Size& image_size);
// Returns the actual bounds of the visible image inside the view.
gfx::Rect GetImageBounds() const;
// Reset the image size to the current image dimensions.
void ResetImageSize();
// Set / Get the horizontal alignment.
void SetHorizontalAlignment(Alignment ha);
Alignment GetHorizontalAlignment() const;
// Set / Get the vertical alignment.
void SetVerticalAlignment(Alignment va);
Alignment GetVerticalAlignment() const;
// Set / Get the tooltip text.
void SetTooltipText(const base::string16& tooltip);
base::string16 GetTooltipText() const;
// Set / Get the accessible name text.
void SetAccessibleName(const base::string16& name);
base::string16 GetAccessibleName() const;
// Overriden from View:
// Overridden from View:
void OnPaint(gfx::Canvas* canvas) override;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
const char* GetClassName() const override;
bool GetTooltipText(const gfx::Point& p,
base::string16* tooltip) const override;
gfx::Size CalculatePreferredSize() const override;
views::PaintInfo::ScaleType GetPaintScaleType() const override;
protected:
// Overridden from ImageViewBase:
gfx::Size GetImageSize() const override;
private:
friend class ImageViewTest;
......@@ -99,40 +66,18 @@ class VIEWS_EXPORT ImageView : public View {
// for this to return false even though the images are in fact equal.
bool IsImageEqual(const gfx::ImageSkia& img) const;
// Returns the size the image will be painted.
gfx::Size GetImageSize() const;
// Compute the image origin given the desired size and the receiver alignment
// properties.
gfx::Point ComputeImageOrigin(const gfx::Size& image_size) const;
// The actual image size.
base::Optional<gfx::Size> image_size_;
// The underlying image.
gfx::ImageSkia image_;
// Caches the scaled image reps.
gfx::ImageSkia scaled_image_;
// Horizontal alignment.
Alignment horizontal_alignment_;
// Vertical alignment.
Alignment vertical_alignment_;
// The current tooltip text.
base::string16 tooltip_text_;
// The current accessible name text.
base::string16 accessible_name_;
// Scale last painted at.
float last_paint_scale_;
float last_paint_scale_ = 0.f;
// Address of bytes we last painted. This is used only for comparison, so its
// safe to cache.
void* last_painted_bitmap_pixels_;
void* last_painted_bitmap_pixels_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(ImageView);
};
......
// 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 "ui/views/controls/image_view_base.h"
#include <utility>
#include "base/logging.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/gfx/geometry/insets.h"
namespace views {
ImageViewBase::ImageViewBase() = default;
ImageViewBase::~ImageViewBase() = default;
void ImageViewBase::SetImageSize(const gfx::Size& image_size) {
image_size_ = image_size;
image_origin_ = ComputeImageOrigin(GetImageSize());
PreferredSizeChanged();
}
gfx::Rect ImageViewBase::GetImageBounds() const {
return gfx::Rect(image_origin_, GetImageSize());
}
void ImageViewBase::ResetImageSize() {
image_size_.reset();
PreferredSizeChanged();
image_origin_ = ComputeImageOrigin(GetImageSize());
}
gfx::Point ImageViewBase::ComputeImageOrigin(
const gfx::Size& image_size) const {
gfx::Insets insets = GetInsets();
int x = 0;
// In order to properly handle alignment of images in RTL locales, we need
// to flip the meaning of trailing and leading. For example, if the
// horizontal alignment is set to trailing, then we'll use left alignment for
// the image instead of right alignment if the UI layout is RTL.
Alignment actual_horizontal_alignment = horizontal_alignment_;
if (base::i18n::IsRTL() && (horizontal_alignment_ != CENTER)) {
actual_horizontal_alignment =
(horizontal_alignment_ == LEADING) ? TRAILING : LEADING;
}
switch (actual_horizontal_alignment) {
case LEADING:
x = insets.left();
break;
case TRAILING:
x = width() - insets.right() - image_size.width();
break;
case CENTER:
x = (width() - insets.width() - image_size.width()) / 2 + insets.left();
break;
}
int y = 0;
switch (vertical_alignment_) {
case LEADING:
y = insets.top();
break;
case TRAILING:
y = height() - insets.bottom() - image_size.height();
break;
case CENTER:
y = (height() - insets.height() - image_size.height()) / 2 + insets.top();
break;
}
return gfx::Point(x, y);
}
void ImageViewBase::GetAccessibleNodeData(ui::AXNodeData* node_data) {
node_data->role = ax::mojom::Role::kImage;
node_data->SetName(tooltip_text_);
}
void ImageViewBase::SetHorizontalAlignment(Alignment alignment) {
if (alignment != horizontal_alignment_) {
horizontal_alignment_ = alignment;
image_origin_ = ComputeImageOrigin(GetImageSize());
SchedulePaint();
}
}
ImageViewBase::Alignment ImageViewBase::GetHorizontalAlignment() const {
return horizontal_alignment_;
}
void ImageViewBase::SetVerticalAlignment(Alignment alignment) {
if (alignment != vertical_alignment_) {
vertical_alignment_ = alignment;
image_origin_ = ComputeImageOrigin(GetImageSize());
SchedulePaint();
}
}
ImageViewBase::Alignment ImageViewBase::GetVerticalAlignment() const {
return vertical_alignment_;
}
void ImageViewBase::SetAccessibleName(const base::string16& accessible_name) {
accessible_name_ = accessible_name;
}
base::string16 ImageViewBase::GetAccessibleName() const {
return accessible_name_;
}
void ImageViewBase::SetTooltipText(const base::string16& tooltip) {
tooltip_text_ = tooltip;
}
base::string16 ImageViewBase::GetTooltipText() const {
return tooltip_text_;
}
bool ImageViewBase::GetTooltipText(const gfx::Point& p,
base::string16* tooltip) const {
if (tooltip_text_.empty())
return false;
*tooltip = GetTooltipText();
return true;
}
gfx::Size ImageViewBase::CalculatePreferredSize() const {
gfx::Size size = GetImageSize();
size.Enlarge(GetInsets().width(), GetInsets().height());
return size;
}
views::PaintInfo::ScaleType ImageViewBase::GetPaintScaleType() const {
// ImageViewBase contains an image which is rastered at the device scale
// factor. By default, the paint commands are recorded at a scale factor
// slightly different from the device scale factor. Re-rastering the image at
// this paint recording scale will result in a distorted image. Paint
// recording scale might also not be uniform along the x & y axis, thus
// resulting in further distortion in the aspect ratio of the final image.
// |kUniformScaling| ensures that the paint recording scale is uniform along
// the x & y axis and keeps the scale equal to the device scale factor.
// See http://crbug.com/754010 for more details.
return views::PaintInfo::ScaleType::kUniformScaling;
}
void ImageViewBase::OnBoundsChanged(const gfx::Rect& previous_bounds) {
image_origin_ = ComputeImageOrigin(GetImageSize());
}
} // namespace views
// 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.
#ifndef UI_VIEWS_CONTROLS_IMAGE_VIEW_BASE_H_
#define UI_VIEWS_CONTROLS_IMAGE_VIEW_BASE_H_
#include "base/macros.h"
#include "base/optional.h"
#include "ui/views/view.h"
namespace gfx {
class Canvas;
}
namespace views {
class VIEWS_EXPORT ImageViewBase : public View {
public:
enum Alignment { LEADING, CENTER, TRAILING };
ImageViewBase();
~ImageViewBase() override;
// Set the desired image size for the receiving ImageView.
void SetImageSize(const gfx::Size& image_size);
// Returns the actual bounds of the visible image inside the view.
gfx::Rect GetImageBounds() const;
// Reset the image size to the current image dimensions.
void ResetImageSize();
// Set / Get the horizontal alignment.
void SetHorizontalAlignment(Alignment ha);
Alignment GetHorizontalAlignment() const;
// Set / Get the vertical alignment.
void SetVerticalAlignment(Alignment va);
Alignment GetVerticalAlignment() const;
// Set / Get the tooltip text.
void SetTooltipText(const base::string16& tooltip);
base::string16 GetTooltipText() const;
// Set / Get the accessible name text.
void SetAccessibleName(const base::string16& name);
base::string16 GetAccessibleName() const;
// Overridden from View:
void OnPaint(gfx::Canvas* canvas) override = 0;
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
const char* GetClassName() const override = 0;
bool GetTooltipText(const gfx::Point& p,
base::string16* tooltip) const override;
gfx::Size CalculatePreferredSize() const override;
views::PaintInfo::ScaleType GetPaintScaleType() const override;
void OnBoundsChanged(const gfx::Rect& previous_bounds) override;
protected:
// Returns the size the image will be painted.
virtual gfx::Size GetImageSize() const = 0;
// The requested image size.
base::Optional<gfx::Size> image_size_;
// The origin of the image.
gfx::Point image_origin_;
private:
friend class ImageViewTest;
// Compute the image origin given the desired size and the receiver alignment
// properties.
gfx::Point ComputeImageOrigin(const gfx::Size& image_size) const;
// Horizontal alignment.
Alignment horizontal_alignment_ = Alignment::CENTER;
// Vertical alignment.
Alignment vertical_alignment_ = Alignment::CENTER;
// The current tooltip text.
base::string16 tooltip_text_;
// The current accessible name text.
base::string16 accessible_name_;
DISALLOW_COPY_AND_ASSIGN(ImageViewBase);
};
} // namespace views
#endif // UI_VIEWS_CONTROLS_IMAGE_VIEW_BASE_H_
......@@ -9,6 +9,8 @@ jumbo_component("views_examples_lib") {
testonly = true
sources = [
"animated_image_view_example.cc",
"animated_image_view_example.h",
"box_layout_example.cc",
"box_layout_example.h",
"bubble_example.cc",
......
// 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 "ui/views/examples/animated_image_view_example.h"
#include <memory>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/skottie_wrapper.h"
#include "ui/views/border.h"
#include "ui/views/controls/animated_image_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/md_text_button.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view.h"
namespace views {
namespace examples {
namespace {
// This class can load a skottie(and lottie) animation file from disk and play
// it in a view as AnimatedImageView.
// See https://skia.org/user/modules/skottie for more info on skottie.
class AnimationGallery : public View,
public TextfieldController,
public ButtonListener {
public:
AnimationGallery()
: animated_image_view_(new AnimatedImageView()),
image_view_container_(new views::View()),
size_input_(new Textfield()),
file_chooser_(new Textfield()),
file_go_button_(
MdTextButton::Create(this, base::ASCIIToUTF16("Render"))) {
AddChildView(size_input_);
image_view_container_->AddChildView(animated_image_view_);
image_view_container_->SetLayoutManager(std::make_unique<FillLayout>());
animated_image_view_->SetBorder(
CreateSolidSidedBorder(1, 1, 1, 1, SK_ColorBLACK));
AddChildView(image_view_container_);
BoxLayout* box = SetLayoutManager(
std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10));
box->SetFlexForView(image_view_container_, 1);
file_chooser_->set_placeholder_text(
base::ASCIIToUTF16("Enter path to lottie JSON file"));
View* file_container = new View();
BoxLayout* file_box =
file_container->SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::kHorizontal, gfx::Insets(10), 10));
file_container->AddChildView(file_chooser_);
file_container->AddChildView(file_go_button_);
file_box->SetFlexForView(file_chooser_, 1);
AddChildView(file_container);
size_input_->set_placeholder_text(
base::ASCIIToUTF16("Size in dip (Empty for default)"));
size_input_->set_controller(this);
}
~AnimationGallery() override = default;
// TextfieldController:
void ContentsChanged(Textfield* sender,
const base::string16& new_contents) override {
if (sender == size_input_) {
if (!base::StringToInt(new_contents, &size_) && (size_ > 0)) {
size_ = 0;
size_input_->SetText(base::string16());
}
Update();
}
}
// ButtonListener:
void ButtonPressed(Button* sender, const ui::Event& event) override {
DCHECK_EQ(file_go_button_, sender);
std::string json;
base::ScopedAllowBlockingForTesting allow_blocking;
#if defined(OS_POSIX)
base::FilePath path(base::UTF16ToUTF8(file_chooser_->text()));
#else
base::FilePath path(file_chooser_->text());
#endif // defined(OS_POSIX)
base::ReadFileToString(path, &json);
auto skottie = base::MakeRefCounted<gfx::SkottieWrapper>(
base::RefCountedString::TakeString(&json));
animated_image_view_->SetAnimatedImage(
std::make_unique<gfx::SkiaVectorAnimation>(skottie));
animated_image_view_->Play();
Update();
}
private:
void Update() {
if (size_ > 24)
animated_image_view_->SetImageSize(gfx::Size(size_, size_));
else
animated_image_view_->ResetImageSize();
Layout();
}
AnimatedImageView* animated_image_view_;
View* image_view_container_;
Textfield* size_input_;
Textfield* file_chooser_;
Button* file_go_button_;
int size_ = 0;
DISALLOW_COPY_AND_ASSIGN(AnimationGallery);
};
} // namespace
AnimatedImageViewExample::AnimatedImageViewExample()
: ExampleBase("Animated Image View") {}
AnimatedImageViewExample::~AnimatedImageViewExample() {}
void AnimatedImageViewExample::CreateExampleView(View* container) {
container->SetLayoutManager(std::make_unique<FillLayout>());
container->AddChildView(new AnimationGallery());
}
} // namespace examples
} // namespace views
// 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.
#ifndef UI_VIEWS_EXAMPLES_ANIMATED_IMAGE_VIEW_EXAMPLE_H_
#define UI_VIEWS_EXAMPLES_ANIMATED_IMAGE_VIEW_EXAMPLE_H_
#include "base/macros.h"
#include "ui/views/examples/example_base.h"
namespace views {
namespace examples {
class VIEWS_EXAMPLES_EXPORT AnimatedImageViewExample : public ExampleBase {
public:
AnimatedImageViewExample();
~AnimatedImageViewExample() override;
// ExampleBase:
void CreateExampleView(View* container) override;
private:
DISALLOW_COPY_AND_ASSIGN(AnimatedImageViewExample);
};
} // namespace examples
} // namespace views
#endif // UI_VIEWS_EXAMPLES_ANIMATED_IMAGE_VIEW_EXAMPLE_H_
......@@ -18,6 +18,7 @@
#include "ui/views/background.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/label.h"
#include "ui/views/examples/animated_image_view_example.h"
#include "ui/views/examples/box_layout_example.h"
#include "ui/views/examples/bubble_example.h"
#include "ui/views/examples/button_example.h"
......@@ -58,6 +59,7 @@ namespace {
// Creates the default set of examples.
ExampleVector CreateExamples() {
ExampleVector examples;
examples.push_back(std::make_unique<AnimatedImageViewExample>());
examples.push_back(std::make_unique<BoxLayoutExample>());
examples.push_back(std::make_unique<BubbleExample>());
examples.push_back(std::make_unique<ButtonExample>());
......
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