blob: d3b0532a82f2c5b269801b2fc2697f875d00c0a8 [file] [log] [blame]
// 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