| // Copyright 2017 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/highlighter/highlighter_result_view.h" |
| |
| #include <memory> |
| |
| #include "ash/highlighter/highlighter_gesture_util.h" |
| #include "ash/highlighter/highlighter_view.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/shell.h" |
| #include "base/bind.h" |
| #include "base/timer/timer.h" |
| #include "ui/compositor/paint_recorder.h" |
| #include "ui/compositor/scoped_layer_animation_settings.h" |
| #include "ui/gfx/geometry/rect_conversions.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Variables for rendering the highlight result. Sizes in DIP. |
| constexpr float kCornerCircleRadius = 6; |
| constexpr float kStrokeFillWidth = 2; |
| constexpr float kStrokeOutlineWidth = 1; |
| constexpr float kOutsetForAntialiasing = 1; |
| constexpr float kResultLayerMargin = |
| kOutsetForAntialiasing + kCornerCircleRadius; |
| |
| constexpr int kInnerFillOpacity = 0x0D; |
| const SkColor kInnerFillColor = SkColorSetRGB(0x00, 0x00, 0x00); |
| |
| constexpr int kStrokeFillOpacity = 0xFF; |
| const SkColor kStrokeFillColor = SkColorSetRGB(0xFF, 0xFF, 0xFF); |
| |
| constexpr int kStrokeOutlineOpacity = 0x14; |
| const SkColor kStrokeOutlineColor = SkColorSetRGB(0x00, 0x00, 0x00); |
| |
| constexpr int kCornerCircleOpacity = 0xFF; |
| const SkColor kCornerCircleColorLT = SkColorSetRGB(0x42, 0x85, 0xF4); |
| const SkColor kCornerCircleColorRT = SkColorSetRGB(0xEA, 0x43, 0x35); |
| const SkColor kCornerCircleColorLB = SkColorSetRGB(0x34, 0xA8, 0x53); |
| const SkColor kCornerCircleColorRB = SkColorSetRGB(0xFB, 0xBC, 0x05); |
| |
| constexpr int kResultFadeinDelayMs = 200; |
| constexpr int kResultFadeinDurationMs = 400; |
| constexpr int kResultFadeoutDelayMs = 500; |
| constexpr int kResultFadeoutDurationMs = 200; |
| |
| constexpr int kResultInPlaceFadeinDelayMs = 100; |
| constexpr int kResultInPlaceFadeinDurationMs = 500; |
| |
| constexpr float kInitialScale = 1.2; |
| |
| class ResultLayer : public ui::Layer, public ui::LayerDelegate { |
| public: |
| ResultLayer(const gfx::Rect& bounds); |
| |
| private: |
| // ui::LayerDelegate: |
| void OnDeviceScaleFactorChanged(float old_device_scale_factor, |
| float new_device_scale_factor) override {} |
| void OnPaintLayer(const ui::PaintContext& context) override; |
| |
| void DrawVerticalBar(gfx::Canvas& canvas, |
| float x, |
| float y, |
| float height, |
| cc::PaintFlags& flags); |
| void DrawHorizontalBar(gfx::Canvas& canvas, |
| float x, |
| float y, |
| float width, |
| cc::PaintFlags& flags); |
| |
| DISALLOW_COPY_AND_ASSIGN(ResultLayer); |
| }; |
| |
| ResultLayer::ResultLayer(const gfx::Rect& box) { |
| set_name("HighlighterResultView:ResultLayer"); |
| gfx::Rect bounds = box; |
| bounds.Inset(-kResultLayerMargin, -kResultLayerMargin); |
| SetBounds(bounds); |
| SetFillsBoundsOpaquely(false); |
| SetMasksToBounds(false); |
| set_delegate(this); |
| } |
| |
| void ResultLayer::OnPaintLayer(const ui::PaintContext& context) { |
| ui::PaintRecorder recorder(context, size()); |
| gfx::Canvas& canvas = *recorder.canvas(); |
| |
| cc::PaintFlags flags; |
| flags.setStyle(cc::PaintFlags::kFill_Style); |
| flags.setAntiAlias(true); |
| |
| const float left = kResultLayerMargin; |
| const float right = size().width() - kResultLayerMargin; |
| const float width = right - left; |
| |
| const float top = kResultLayerMargin; |
| const float bottom = size().height() - kResultLayerMargin; |
| const float height = bottom - top; |
| |
| flags.setColor(SkColorSetA(kInnerFillColor, kInnerFillOpacity)); |
| canvas.DrawRect(gfx::RectF(left, top, width, height), flags); |
| |
| DrawVerticalBar(canvas, left, top, height, flags); |
| DrawVerticalBar(canvas, right, top, height, flags); |
| DrawHorizontalBar(canvas, left, top, width, flags); |
| DrawHorizontalBar(canvas, left, bottom, width, flags); |
| |
| flags.setColor(SkColorSetA(kCornerCircleColorLT, kCornerCircleOpacity)); |
| canvas.DrawCircle(gfx::PointF(left, top), kCornerCircleRadius, flags); |
| |
| flags.setColor(SkColorSetA(kCornerCircleColorRT, kCornerCircleOpacity)); |
| canvas.DrawCircle(gfx::PointF(right, top), kCornerCircleRadius, flags); |
| |
| flags.setColor(SkColorSetA(kCornerCircleColorLB, kCornerCircleOpacity)); |
| canvas.DrawCircle(gfx::PointF(right, bottom), kCornerCircleRadius, flags); |
| |
| flags.setColor(SkColorSetA(kCornerCircleColorRB, kCornerCircleOpacity)); |
| canvas.DrawCircle(gfx::PointF(left, bottom), kCornerCircleRadius, flags); |
| } |
| |
| void ResultLayer::DrawVerticalBar(gfx::Canvas& canvas, |
| float x, |
| float y, |
| float height, |
| cc::PaintFlags& flags) { |
| const float x_fill = x - kStrokeFillWidth / 2; |
| const float x_outline_left = x_fill - kStrokeOutlineWidth; |
| const float x_outline_right = x_fill + kStrokeFillWidth; |
| |
| flags.setColor(SkColorSetA(kStrokeFillColor, kStrokeFillOpacity)); |
| canvas.DrawRect(gfx::RectF(x_fill, y, kStrokeFillWidth, height), flags); |
| |
| flags.setColor(SkColorSetA(kStrokeOutlineColor, kStrokeOutlineOpacity)); |
| canvas.DrawRect(gfx::RectF(x_outline_left, y, kStrokeOutlineWidth, height), |
| flags); |
| canvas.DrawRect(gfx::RectF(x_outline_right, y, kStrokeOutlineWidth, height), |
| flags); |
| } |
| |
| void ResultLayer::DrawHorizontalBar(gfx::Canvas& canvas, |
| float x, |
| float y, |
| float width, |
| cc::PaintFlags& flags) { |
| const float y_fill = y - kStrokeFillWidth / 2; |
| const float y_outline_left = y_fill - kStrokeOutlineWidth; |
| const float y_outline_right = y_fill + kStrokeFillWidth; |
| |
| flags.setColor(SkColorSetA(kStrokeFillColor, kStrokeFillOpacity)); |
| canvas.DrawRect(gfx::RectF(x, y_fill, width, kStrokeFillWidth), flags); |
| |
| flags.setColor(SkColorSetA(kStrokeOutlineColor, kStrokeOutlineOpacity)); |
| canvas.DrawRect(gfx::RectF(x, y_outline_left, width, kStrokeOutlineWidth), |
| flags); |
| canvas.DrawRect(gfx::RectF(x, y_outline_right, width, kStrokeOutlineWidth), |
| flags); |
| } |
| |
| } // namespace |
| |
| HighlighterResultView::HighlighterResultView(aura::Window* root_window) { |
| widget_ = std::make_unique<views::Widget>(); |
| |
| views::Widget::InitParams params; |
| params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; |
| params.name = "HighlighterResult"; |
| params.accept_events = false; |
| params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; |
| params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
| params.parent = |
| Shell::GetContainer(root_window, kShellWindowId_OverlayContainer); |
| params.layer_type = ui::LAYER_SOLID_COLOR; |
| |
| widget_->Init(params); |
| widget_->Show(); |
| widget_->SetContentsView(this); |
| widget_->SetFullscreen(true); |
| set_owned_by_client(); |
| } |
| |
| HighlighterResultView::~HighlighterResultView() = default; |
| |
| void HighlighterResultView::Animate(const gfx::RectF& bounds, |
| HighlighterGestureType gesture_type, |
| const base::Closure& done) { |
| ui::Layer* layer = widget_->GetLayer(); |
| |
| base::TimeDelta delay; |
| base::TimeDelta duration; |
| |
| if (gesture_type == HighlighterGestureType::kHorizontalStroke) { |
| // The original stroke is fading out in place. |
| // Fade in a solid transparent rectangle. |
| result_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR); |
| result_layer_->set_name("HighlighterResultView:SOLID_LAYER"); |
| result_layer_->SetBounds(gfx::ToEnclosingRect(bounds)); |
| result_layer_->SetFillsBoundsOpaquely(false); |
| result_layer_->SetMasksToBounds(false); |
| result_layer_->SetColor(HighlighterView::kPenColor); |
| |
| layer->Add(result_layer_.get()); |
| |
| delay = base::TimeDelta::FromMilliseconds(kResultInPlaceFadeinDelayMs); |
| duration = |
| base::TimeDelta::FromMilliseconds(kResultInPlaceFadeinDurationMs); |
| } else { |
| DCHECK(gesture_type == HighlighterGestureType::kClosedShape); |
| // The original stroke is fading out and inflating. |
| // Fade in the deflating lens overlay. |
| result_layer_ = std::make_unique<ResultLayer>(gfx::ToEnclosingRect(bounds)); |
| layer->Add(result_layer_.get()); |
| |
| gfx::Transform transform; |
| const gfx::PointF pivot = bounds.CenterPoint(); |
| transform.Translate(pivot.x() * (1 - kInitialScale), |
| pivot.y() * (1 - kInitialScale)); |
| transform.Scale(kInitialScale, kInitialScale); |
| layer->SetTransform(transform); |
| |
| delay = base::TimeDelta::FromMilliseconds(kResultFadeinDelayMs); |
| duration = base::TimeDelta::FromMilliseconds(kResultFadeinDurationMs); |
| } |
| |
| layer->SetOpacity(0); |
| |
| animation_timer_ = std::make_unique<base::OneShotTimer>(); |
| animation_timer_->Start(FROM_HERE, delay, |
| base::Bind(&HighlighterResultView::FadeIn, |
| base::Unretained(this), duration, done)); |
| } |
| |
| void HighlighterResultView::FadeIn(const base::TimeDelta& duration, |
| const base::Closure& done) { |
| ui::Layer* layer = widget_->GetLayer(); |
| |
| { |
| ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); |
| settings.SetTransitionDuration(duration); |
| settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN); |
| layer->SetOpacity(1); |
| |
| gfx::Transform transform; |
| transform.Scale(1, 1); |
| transform.Translate(0, 0); |
| layer->SetTransform(transform); |
| } |
| |
| animation_timer_ = std::make_unique<base::OneShotTimer>(); |
| animation_timer_->Start( |
| FROM_HERE, |
| duration + base::TimeDelta::FromMilliseconds(kResultFadeoutDelayMs), |
| base::Bind(&HighlighterResultView::FadeOut, base::Unretained(this), |
| done)); |
| } |
| |
| void HighlighterResultView::FadeOut(const base::Closure& done) { |
| ui::Layer* layer = widget_->GetLayer(); |
| |
| base::TimeDelta duration = |
| base::TimeDelta::FromMilliseconds(kResultFadeoutDurationMs); |
| |
| ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); |
| settings.SetTransitionDuration(duration); |
| settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN); |
| layer->SetOpacity(0); |
| |
| animation_timer_ = std::make_unique<base::OneShotTimer>(); |
| animation_timer_->Start(FROM_HERE, duration, done); |
| } |
| |
| } // namespace ash |