Commit 317adc6a authored by Vladislav Kaznacheev's avatar Vladislav Kaznacheev Committed by Commit Bot

Implement metalayer palette tool in ash.

Add a new "highlighter" tool using the same fast
rendering method as laser pointer (CL extracting
the common base class is to follow).

Bug: b:63142451
Change-Id: Id848b77f08ae95c167c527a07aec6ccdf79f49f4
Reviewed-on: https://chromium-review.googlesource.com/558467
Commit-Queue: Vladislav Kaznacheev <kaznacheev@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Reviewed-by: default avatarLuis Hector Chavez <lhchavez@chromium.org>
Reviewed-by: default avatarDavid Reveman <reveman@chromium.org>
Reviewed-by: default avatarJacob Dufault <jdufault@chromium.org>
Cr-Commit-Position: refs/heads/master@{#488688}
parent 71aac2d2
......@@ -159,6 +159,15 @@ component("ash") {
"gpu_support_stub.h",
"high_contrast/high_contrast_controller.cc",
"high_contrast/high_contrast_controller.h",
"highlighter/highlighter_controller.cc",
"highlighter/highlighter_controller.h",
"highlighter/highlighter_gesture_util.cc",
"highlighter/highlighter_gesture_util.h",
"highlighter/highlighter_result_view.cc",
"highlighter/highlighter_result_view.h",
"highlighter/highlighter_selection_observer.h",
"highlighter/highlighter_view.cc",
"highlighter/highlighter_view.h",
"host/ash_window_tree_host.cc",
"host/ash_window_tree_host.h",
"host/ash_window_tree_host_init_params.h",
......@@ -1164,6 +1173,7 @@ source_set("common_unittests") {
"frame/caption_buttons/frame_size_button_unittest.cc",
"frame/custom_frame_view_ash_unittest.cc",
"frame/default_header_painter_unittest.cc",
"highlighter/highlighter_gesture_util_unittest.cc",
"ime/ime_controller_unittest.cc",
"laser/laser_pointer_controller_unittest.cc",
"laser/laser_pointer_points_unittest.cc",
......
include_rules = [
"+ash/fast_ink",
]
// 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_controller.h"
#include "ash/ash_switches.h"
#include "ash/highlighter/highlighter_gesture_util.h"
#include "ash/highlighter/highlighter_result_view.h"
#include "ash/highlighter/highlighter_selection_observer.h"
#include "ash/highlighter/highlighter_view.h"
#include "ash/shell.h"
#include "ash/system/palette/palette_utils.h"
#include "base/command_line.h"
#include "base/strings/string_number_conversions.h"
#include "ui/display/screen.h"
#include "ui/events/base_event_utils.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// Adjust the height of the bounding box to match the pen tip height,
// while keeping the same vertical center line. Adjust the width to
// account for the pen tip width.
gfx::RectF AdjustHorizontalStroke(const gfx::RectF& box,
const gfx::SizeF& pen_tip_size) {
return gfx::RectF(box.x() - pen_tip_size.width() / 2,
box.CenterPoint().y() - pen_tip_size.height() / 2,
box.width() + pen_tip_size.width(), pen_tip_size.height());
}
} // namespace
HighlighterController::HighlighterController() {
Shell::Get()->AddPreTargetHandler(this);
}
HighlighterController::~HighlighterController() {
Shell::Get()->RemovePreTargetHandler(this);
}
void HighlighterController::EnableHighlighter(
HighlighterSelectionObserver* observer) {
observer_ = observer;
}
void HighlighterController::DisableHighlighter() {
observer_ = nullptr;
}
void HighlighterController::OnTouchEvent(ui::TouchEvent* event) {
if (!observer_)
return;
if (event->pointer_details().pointer_type !=
ui::EventPointerType::POINTER_TYPE_PEN)
return;
if (event->type() != ui::ET_TOUCH_MOVED &&
event->type() != ui::ET_TOUCH_PRESSED &&
event->type() != ui::ET_TOUCH_RELEASED)
return;
// Find the root window that the event was captured on. We never need to
// switch between different root windows because it is not physically possible
// to seamlessly drag a finger between two displays like it is with a mouse.
gfx::Point event_location = event->root_location();
aura::Window* current_window =
static_cast<aura::Window*>(event->target())->GetRootWindow();
// Start a new highlighter session if the stylus is pressed but not
// pressed over the palette.
if (event->type() == ui::ET_TOUCH_PRESSED &&
!palette_utils::PaletteContainsPointInScreen(event_location)) {
DestroyHighlighterView();
UpdateHighlighterView(current_window, event->root_location_f(), event);
}
if (event->type() == ui::ET_TOUCH_MOVED && highlighter_view_)
UpdateHighlighterView(current_window, event->root_location_f(), event);
if (event->type() == ui::ET_TOUCH_RELEASED && highlighter_view_) {
const gfx::RectF box = GetBoundingBox(highlighter_view_->points());
const gfx::PointF pivot(box.CenterPoint());
const float scale_factor = current_window->layer()->device_scale_factor();
if (DetectHorizontalStroke(box, HighlighterView::kPenTipSize)) {
const gfx::Rect box_adjusted = gfx::ToEnclosingRect(
AdjustHorizontalStroke(box, HighlighterView::kPenTipSize));
observer_->HandleSelection(
gfx::ScaleToEnclosingRect(box_adjusted, scale_factor));
highlighter_view_->Animate(pivot,
HighlighterView::AnimationMode::kFadeout);
result_view_ = base::MakeUnique<HighlighterResultView>(current_window);
result_view_->AnimateInPlace(box_adjusted, HighlighterView::kPenColor);
} else if (DetectClosedShape(box, highlighter_view_->points())) {
const gfx::Rect box_enclosing = gfx::ToEnclosingRect(box);
observer_->HandleSelection(
gfx::ScaleToEnclosingRect(box_enclosing, scale_factor));
highlighter_view_->Animate(pivot,
HighlighterView::AnimationMode::kInflate);
result_view_ = base::MakeUnique<HighlighterResultView>(current_window);
result_view_->AnimateDeflate(box_enclosing);
} else {
highlighter_view_->Animate(pivot,
HighlighterView::AnimationMode::kDeflate);
}
}
}
void HighlighterController::OnWindowDestroying(aura::Window* window) {
SwitchTargetRootWindowIfNeeded(window);
}
void HighlighterController::SwitchTargetRootWindowIfNeeded(
aura::Window* root_window) {
if (!root_window) {
DestroyHighlighterView();
}
if (root_window && observer_ && !highlighter_view_) {
highlighter_view_ = base::MakeUnique<HighlighterView>(root_window);
}
}
void HighlighterController::UpdateHighlighterView(
aura::Window* current_window,
const gfx::PointF& event_location,
ui::Event* event) {
SwitchTargetRootWindowIfNeeded(current_window);
highlighter_view_->AddNewPoint(event_location);
event->StopPropagation();
}
void HighlighterController::DestroyHighlighterView() {
highlighter_view_.reset();
result_view_.reset();
}
} // namespace ash
// 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.
#ifndef ASH_HIGHLIGHTER_HIGHLIGHTER_CONTROLLER_H_
#define ASH_HIGHLIGHTER_HIGHLIGHTER_CONTROLLER_H_
#include <memory>
#include "ash/ash_export.h"
#include "base/macros.h"
#include "base/time/time.h"
#include "ui/aura/window_observer.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/point_f.h"
namespace ash {
class HighlighterResultView;
class HighlighterSelectionObserver;
class HighlighterView;
// Controller for the highlighter functionality.
// Enables/disables highlighter as well as receives points
// and passes them off to be rendered.
class ASH_EXPORT HighlighterController : public ui::EventHandler,
public aura::WindowObserver {
public:
HighlighterController();
~HighlighterController() override;
// Turns the highlighter feature on. The user still has to press and
// drag to see the highlighter.
void EnableHighlighter(HighlighterSelectionObserver* observer);
void DisableHighlighter();
private:
// ui::EventHandler:
void OnTouchEvent(ui::TouchEvent* event) override;
// aura::WindowObserver:
void OnWindowDestroying(aura::Window* window) override;
// Handles monitor disconnect/swap primary.
void SwitchTargetRootWindowIfNeeded(aura::Window* root_window);
// Updates |highlighter_view_| by changing its root window or creating it if
// needed. Also adds the latest point at |event_location| and stops
// propagation of |event|.
void UpdateHighlighterView(aura::Window* current_window,
const gfx::PointF& event_location,
ui::Event* event);
// Destroys |highlighter_view_|, if it exists.
void DestroyHighlighterView();
// |highlighter_view_| will only hold an instance when the highlighter is
// enabled and activated (pressed or dragged).
std::unique_ptr<HighlighterView> highlighter_view_;
std::unique_ptr<HighlighterResultView> result_view_;
// |observer_| in non-null when the highlighter is enabled,
// null when disabled.
HighlighterSelectionObserver* observer_;
DISALLOW_COPY_AND_ASSIGN(HighlighterController);
};
} // namespace ash
#endif // ASH_HIGHLIGHTER_HIGHLIGHTER_CONTROLLER_H_
// 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_gesture_util.h"
#include <cmath>
namespace ash {
const float kHorizontalStrokeLengthThreshold = 20;
const float kHorizontalStrokeThicknessThreshold = 2;
const float kHorizontalStrokeFlatnessThreshold = 0.1;
const double kClosedShapeWrapThreshold = M_PI * 2 * 0.95;
const double kClosedShapeSweepThreshold = M_PI * 2 * 0.8;
const double kClosedShapeJiggleThreshold = 0.1;
gfx::RectF GetBoundingBox(const std::vector<gfx::PointF>& points) {
if (points.empty())
return gfx::RectF();
gfx::PointF min_point = points[0];
gfx::PointF max_point = points[0];
for (size_t i = 1; i < points.size(); ++i) {
min_point.SetToMin(points[i]);
max_point.SetToMax(points[i]);
}
return gfx::BoundingRect(min_point, max_point);
}
bool DetectHorizontalStroke(const gfx::RectF& box,
const gfx::SizeF& pen_tip_size) {
return box.width() > kHorizontalStrokeLengthThreshold &&
box.height() <
pen_tip_size.height() * kHorizontalStrokeThicknessThreshold &&
box.height() < box.width() * kHorizontalStrokeFlatnessThreshold;
}
bool DetectClosedShape(const gfx::RectF& box,
const std::vector<gfx::PointF>& points) {
if (points.size() < 3)
return false;
const gfx::PointF center = box.CenterPoint();
// Analyze vectors pointing from the center to each point.
// Compute the cumulative swept angle and count positive
// and negative angles separately.
double swept_angle = 0.0;
int positive = 0;
int negative = 0;
double prev_angle = 0.0;
bool has_prev_angle = false;
for (const auto& point : points) {
const double angle = atan2(point.y() - center.y(), point.x() - center.x());
if (has_prev_angle) {
double diff_angle = angle - prev_angle;
if (diff_angle > kClosedShapeWrapThreshold) {
diff_angle -= M_PI * 2;
} else if (diff_angle < -kClosedShapeWrapThreshold) {
diff_angle += M_PI * 2;
}
swept_angle += diff_angle;
if (diff_angle > 0)
positive++;
if (diff_angle < 0)
negative++;
} else {
has_prev_angle = true;
}
prev_angle = angle;
}
if (std::abs(swept_angle) < kClosedShapeSweepThreshold) {
// Has not swept enough of the full circle.
return false;
}
if (swept_angle > 0 && (static_cast<double>(negative) / positive) >
kClosedShapeJiggleThreshold) {
// Main direction is positive, but went too often in the negative direction.
return false;
}
if (swept_angle < 0 && (static_cast<double>(positive) / negative) >
kClosedShapeJiggleThreshold) {
// Main direction is negative, but went too often in the positive direction.
return false;
}
return true;
}
} // namespace ash
// 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.
#ifndef ASH_HIGHLIGHTER_HIGHLIGHTER_GESTURE_UTIL_H_
#define ASH_HIGHLIGHTER_HIGHLIGHTER_GESTURE_UTIL_H_
#include <vector>
#include "ash/ash_export.h"
#include "ui/gfx/geometry/rect_f.h"
namespace ash {
// Returns the bounding box of |points|
gfx::RectF ASH_EXPORT GetBoundingBox(const std::vector<gfx::PointF>& points);
// Returns true if |box| is sufficiently long horizontally and short vertically.
bool ASH_EXPORT DetectHorizontalStroke(const gfx::RectF& box,
const gfx::SizeF& pen_tip_size);
// Returns true if |points| is forming a "closed shape" which is defined as a
// sequence of points sweeping at least of 80% of a full circle around the
// center of |box|, and going in one direction (clockwise or counterclockwise),
// with a little noise tolerated).
bool ASH_EXPORT DetectClosedShape(const gfx::RectF& box,
const std::vector<gfx::PointF>& points);
} // namespace ash
#endif // ASH_HIGHLIGHTER_HIGHLIGHTER_GESTURE_UTIL_H_
// 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_gesture_util.h"
#include "ash/test/ash_test_base.h"
#include "ui/gfx/geometry/rect_conversions.h"
namespace ash {
namespace {
constexpr float kLength = 400;
constexpr gfx::RectF kFrame(50, 50, 300, 200);
constexpr gfx::SizeF kPenTipSize(4.0, 14);
constexpr int kPointsPerLine = 10;
float LinearInterpolate(float from, float to, int position, int range) {
return from + ((to - from) * position) / range;
}
class HighlighterGestureUtilTest : public AshTestBase {
public:
HighlighterGestureUtilTest() {}
~HighlighterGestureUtilTest() override {}
protected:
std::vector<gfx::PointF> points_;
void MoveTo(float x, float y) { AddPoint(x, y); }
void LineTo(float x, float y) {
const gfx::PointF origin = points_.back();
for (int i = 1; i <= kPointsPerLine; i++) {
AddPoint(LinearInterpolate(origin.x(), x, i, kPointsPerLine),
LinearInterpolate(origin.y(), y, i, kPointsPerLine));
}
}
void TraceLine(float x0, float y0, float x1, float y1) {
MoveTo(x0, y0);
LineTo(x1, y1);
}
void TraceRect(const gfx::RectF& rect, float tilt) {
MoveTo(rect.x() + tilt, rect.y());
LineTo(rect.right(), rect.y() + tilt);
LineTo(rect.right() - tilt, rect.bottom());
LineTo(rect.x(), rect.bottom() - tilt);
LineTo(rect.x() + tilt, rect.y());
}
void TraceZigZag(float w, float h) {
MoveTo(0, 0);
LineTo(w / 4, h);
LineTo(w / 2, 0);
LineTo(w * 3 / 4, h);
LineTo(w, 0);
}
bool DetectHorizontalStroke() {
gfx::RectF box = GetBoundingBox(points_);
return ash::DetectHorizontalStroke(box, kPenTipSize);
}
bool DetectClosedShape() {
gfx::RectF box = GetBoundingBox(points_);
return ash::DetectClosedShape(box, points_);
}
private:
void AddPoint(float x, float y) { points_.push_back(gfx::PointF(x, y)); }
DISALLOW_COPY_AND_ASSIGN(HighlighterGestureUtilTest);
};
} // namespace
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeTooShort) {
TraceLine(0, 0, 1, 0);
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeFlat) {
TraceLine(0, 0, kLength, 0);
EXPECT_TRUE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeTiltedLeftSlightly) {
const float kTilt = kLength / 20;
TraceLine(0, kTilt, kLength, 0);
EXPECT_TRUE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeTiltedRightSlightly) {
const float kTilt = kLength / 20;
TraceLine(0, 0, kLength, kTilt);
EXPECT_TRUE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeTiltedLeftTooMuch) {
const float kTilt = kLength / 5;
TraceLine(0, kTilt, kLength, 0);
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeTiltedRightTooMuch) {
const float kTilt = kLength / 5;
TraceLine(0, 0, kLength, kTilt);
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeZigZagSlightly) {
const float kHeight = kLength / 20;
TraceZigZag(kLength, kHeight);
EXPECT_TRUE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, HorizontalStrokeZigZagTooMuch) {
const float kHeight = kLength / 5;
TraceZigZag(kLength, kHeight);
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeRectangle) {
TraceRect(kFrame, 0);
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_TRUE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeRectangleThin) {
TraceRect(gfx::RectF(0, 0, kLength, kLength / 20), 0);
EXPECT_TRUE(DetectHorizontalStroke());
EXPECT_TRUE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeRectangleTilted) {
TraceRect(kFrame, 10.0);
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_TRUE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeWithOverlap) {
// 1/4 overlap
MoveTo(kFrame.right(), kFrame.y());
LineTo(kFrame.x(), kFrame.y());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.y());
LineTo(kFrame.x(), kFrame.y());
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_TRUE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeLikeU) {
// 1/4 open shape
MoveTo(kFrame.x(), kFrame.y());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.y());
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeLikeG) {
// 1/8 open shape
MoveTo(kFrame.right(), kFrame.y());
LineTo(kFrame.x(), kFrame.y());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.CenterPoint().y());
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_TRUE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeWithLittleBackAndForth) {
MoveTo(kFrame.right(), kFrame.y());
LineTo(kFrame.x(), kFrame.y());
// Go back one pixel, then go forward again.
MoveTo(kFrame.x() + 1, kFrame.y());
MoveTo(kFrame.x(), kFrame.y());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.CenterPoint().y());
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_TRUE(DetectClosedShape());
}
TEST_F(HighlighterGestureUtilTest, ClosedShapeWithTooMuchBackAndForth) {
MoveTo(kFrame.right(), kFrame.y());
LineTo(kFrame.x(), kFrame.y());
LineTo(kFrame.right(), kFrame.y());
LineTo(kFrame.x(), kFrame.y());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.x(), kFrame.y());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.bottom());
LineTo(kFrame.x(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.bottom());
LineTo(kFrame.right(), kFrame.CenterPoint().y());
EXPECT_FALSE(DetectHorizontalStroke());
EXPECT_FALSE(DetectClosedShape());
}
} // namespace ash
// 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 "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/compositor/scoped_layer_animation_settings.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 = 600;
constexpr int kResultFadeinDurationMs = 400;
constexpr int kResultFadeoutDelayMs = 500;
constexpr int kResultFadeoutDurationMs = 200;
constexpr int kResultInPlaceFadeinDelayMs = 500;
constexpr int kResultInPlaceFadeinDurationMs = 500;
constexpr float kInitialScale = 1.2;
class ResultLayer : public ui::Layer, public ui::LayerDelegate {
public:
ResultLayer(const gfx::Rect& bounds);
private:
void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
void OnDeviceScaleFactorChanged(float 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_ = base::MakeUnique<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() {}
void HighlighterResultView::AnimateInPlace(const gfx::Rect& bounds,
SkColor color) {
ui::Layer* layer = widget_->GetLayer();
// A solid transparent rectangle.
result_layer_ = base::MakeUnique<ui::Layer>(ui::LAYER_SOLID_COLOR);
result_layer_->set_name("HighlighterResultView:SOLID_LAYER");
result_layer_->SetBounds(bounds);
result_layer_->SetFillsBoundsOpaquely(false);
result_layer_->SetMasksToBounds(false);
result_layer_->SetColor(color);
layer->Add(result_layer_.get());
ScheduleFadeIn(
base::TimeDelta::FromMilliseconds(kResultInPlaceFadeinDelayMs),
base::TimeDelta::FromMilliseconds(kResultInPlaceFadeinDurationMs));
}
void HighlighterResultView::AnimateDeflate(const gfx::Rect& bounds) {
ui::Layer* layer = widget_->GetLayer();
result_layer_ = base::MakeUnique<ResultLayer>(bounds);
layer->Add(result_layer_.get());
gfx::Transform transform;
const gfx::Point pivot = bounds.CenterPoint();
transform.Translate(pivot.x() * (1 - kInitialScale),
pivot.y() * (1 - kInitialScale));
transform.Scale(kInitialScale, kInitialScale);
layer->SetTransform(transform);
ScheduleFadeIn(base::TimeDelta::FromMilliseconds(kResultFadeinDelayMs),
base::TimeDelta::FromMilliseconds(kResultFadeinDurationMs));
}
void HighlighterResultView::ScheduleFadeIn(const base::TimeDelta& delay,
const base::TimeDelta& duration) {
ui::Layer* layer = widget_->GetLayer();
layer->SetOpacity(0);
animation_timer_.reset(
new base::Timer(FROM_HERE, delay,
base::Bind(&HighlighterResultView::FadeIn,
base::Unretained(this), duration),
false));
animation_timer_->Reset();
}
void HighlighterResultView::FadeIn(const base::TimeDelta& duration) {
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_.reset(new base::Timer(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kResultFadeinDurationMs +
kResultFadeoutDelayMs),
base::Bind(&HighlighterResultView::FadeOut, base::Unretained(this)),
false));
animation_timer_->Reset();
}
void HighlighterResultView::FadeOut() {
ui::Layer* layer = widget_->GetLayer();
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kResultFadeoutDurationMs));
settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
layer->SetOpacity(0);
}
} // namespace ash
// 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.
#ifndef ASH_HIGHLIGHTER_HIGHLIGHTER_RESULT_VIEW_H_
#define ASH_HIGHLIGHTER_HIGHLIGHTER_RESULT_VIEW_H_
#include <memory>
#include "base/macros.h"
#include "ui/views/view.h"
namespace aura {
class Window;
}
namespace base {
class Timer;
}
namespace ui {
class Layer;
}
namespace views {
class Widget;
}
namespace ash {
// HighlighterResultView displays an animated shape that represents
// the result of the selection.
class HighlighterResultView : public views::View {
public:
HighlighterResultView(aura::Window* root_window);
~HighlighterResultView() override;
void AnimateInPlace(const gfx::Rect& bounds, SkColor color);
void AnimateDeflate(const gfx::Rect& bounds);
private:
void ScheduleFadeIn(const base::TimeDelta& delay,
const base::TimeDelta& duration);
void FadeIn(const base::TimeDelta& duration);
void FadeOut();
std::unique_ptr<views::Widget> widget_;
std::unique_ptr<ui::Layer> result_layer_;
std::unique_ptr<base::Timer> animation_timer_;
DISALLOW_COPY_AND_ASSIGN(HighlighterResultView);
};
} // namespace ash
#endif // ASH_HIGHLIGHTER_HIGHLIGHTER_RESULT_VIEW_H_
// 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.
#ifndef ASH_HIGHLIGHTER_HIGHLIGHTER_SELECTION_OBSERVER_H_
#define ASH_HIGHLIGHTER_HIGHLIGHTER_SELECTION_OBSERVER_H_
namespace gfx {
class Rect;
} // namespace gfx
namespace ash {
// Observer for handling highlighter selection.
class HighlighterSelectionObserver {
public:
virtual ~HighlighterSelectionObserver() {}
virtual void HandleSelection(const gfx::Rect& rect) = 0;
};
} // namespace ash
#endif // ASH_HIGHLIGHTER_HIGHLIGHTER_SELECTION_OBSERVER_H_
// 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_view.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkTypes.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/canvas.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// Variables for rendering the highlighter. Sizes in DIP.
const SkColor kHighlighterColor = SkColorSetRGB(0x42, 0x85, 0xF4);
constexpr int kHighlighterOpacity = 0xCC;
constexpr float kPenTipWidth = 4;
constexpr float kPenTipHeight = 14;
constexpr int kOutsetForAntialiasing = 1;
constexpr float kStrokeScale = 1.2;
constexpr int kStrokeFadeoutDelayMs = 500;
constexpr int kStrokeFadeoutDurationMs = 500;
constexpr int kStrokeScaleDurationMs = 300;
gfx::RectF GetPenTipRect(const gfx::PointF& p) {
return gfx::RectF(p.x() - kPenTipWidth / 2, p.y() - kPenTipHeight / 2,
kPenTipWidth, kPenTipHeight);
}
gfx::Rect GetSegmentDamageRect(const gfx::RectF& r1, const gfx::RectF& r2) {
gfx::RectF rect = r1;
rect.Union(r2);
rect.Inset(-kOutsetForAntialiasing, -kOutsetForAntialiasing);
return gfx::ToEnclosingRect(rect);
}
// A highlighter segment is best imagined as a result of a rectangle
// being dragged along a straight line, while keeping its orientation.
// r1 is the start position of the rectangle and r2 is the final position.
void DrawSegment(gfx::Canvas& canvas,
const gfx::RectF& r1,
const gfx::RectF& r2,
const cc::PaintFlags& flags) {
if (r1.x() > r2.x()) {
DrawSegment(canvas, r2, r1, flags);
return;
}
SkPath path;
path.moveTo(r1.x(), r1.y());
if (r1.y() < r2.y())
path.lineTo(r1.right(), r1.y());
else
path.lineTo(r2.x(), r2.y());
path.lineTo(r2.right(), r2.y());
path.lineTo(r2.right(), r2.bottom());
if (r1.y() < r2.y())
path.lineTo(r2.x(), r2.bottom());
else
path.lineTo(r1.right(), r1.bottom());
path.lineTo(r1.x(), r1.bottom());
path.lineTo(r1.x(), r1.y());
canvas.DrawPath(path, flags);
}
} // namespace
const SkColor HighlighterView::kPenColor =
SkColorSetA(kHighlighterColor, kHighlighterOpacity);
const gfx::SizeF HighlighterView::kPenTipSize(kPenTipWidth, kPenTipHeight);
HighlighterView::HighlighterView(aura::Window* root_window)
: FastInkView(root_window) {}
HighlighterView::~HighlighterView() {}
void HighlighterView::AddNewPoint(const gfx::PointF& point) {
TRACE_EVENT1("ui", "HighlighterView::AddNewPoint", "point", point.ToString());
if (!points_.empty()) {
UpdateDamageRect(GetSegmentDamageRect(GetPenTipRect(points_.back()),
GetPenTipRect(point)));
}
points_.push_back(point);
RequestRedraw();
}
void HighlighterView::Animate(const gfx::PointF& pivot,
AnimationMode animation_mode) {
animation_timer_.reset(new base::Timer(
FROM_HERE, base::TimeDelta::FromMilliseconds(kStrokeFadeoutDelayMs),
base::Bind(&HighlighterView::FadeOut, base::Unretained(this), pivot,
animation_mode),
false));
animation_timer_->Reset();
}
void HighlighterView::FadeOut(const gfx::PointF& pivot,
AnimationMode animation_mode) {
ui::Layer* layer = GetWidget()->GetLayer();
{
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kStrokeFadeoutDurationMs));
settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
layer->SetOpacity(0);
}
if (animation_mode != AnimationMode::kFadeout) {
ui::ScopedLayerAnimationSettings settings(layer->GetAnimator());
settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(kStrokeScaleDurationMs));
settings.SetTweenType(gfx::Tween::LINEAR_OUT_SLOW_IN);
const float scale = animation_mode == AnimationMode::kInflate
? kStrokeScale
: (1 / kStrokeScale);
gfx::Transform transform;
transform.Translate(pivot.x() * (1 - scale), pivot.y() * (1 - scale));
transform.Scale(scale, scale);
layer->SetTransform(transform);
}
}
void HighlighterView::OnRedraw(gfx::Canvas& canvas,
const gfx::Vector2d& offset) {
if (points_.size() < 2)
return;
gfx::Rect clip_rect;
if (!canvas.GetClipBounds(&clip_rect))
return;
cc::PaintFlags flags;
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setAntiAlias(true);
flags.setColor(kPenColor);
flags.setBlendMode(SkBlendMode::kSrc);
for (size_t i = 1; i < points_.size(); ++i) {
const gfx::RectF tip1 = GetPenTipRect(points_[i - 1]) - offset;
const gfx::RectF tip2 = GetPenTipRect(points_[i]) - offset;
// Only draw the segment if it is touching the clip rect.
if (clip_rect.Intersects(GetSegmentDamageRect(tip1, tip2)))
DrawSegment(canvas, tip1, tip2, flags);
}
}
} // namespace ash
// 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.
#ifndef ASH_HIGHLIGHTER_HIGHLIGHTER_VIEW_H_
#define ASH_HIGHLIGHTER_HIGHLIGHTER_VIEW_H_
#include <vector>
#include "ash/fast_ink/fast_ink_view.h"
#include "base/time/time.h"
namespace aura {
class Window;
}
namespace base {
class Timer;
}
namespace gfx {
class PointF;
}
namespace ash {
// HighlighterView displays the highlighter palette tool. It draws the
// highlighter stroke which consists of a series of thick lines connecting
// touch points.
class HighlighterView : public FastInkView {
public:
enum class AnimationMode {
kFadeout,
kInflate,
kDeflate,
};
static const SkColor kPenColor;
static const gfx::SizeF kPenTipSize;
explicit HighlighterView(aura::Window* root_window);
~HighlighterView() override;
const std::vector<gfx::PointF>& points() const { return points_; }
void AddNewPoint(const gfx::PointF& new_point);
void Animate(const gfx::PointF& pivot, AnimationMode animation_mode);
private:
void OnRedraw(gfx::Canvas& canvas, const gfx::Vector2d& offset) override;
void FadeOut(const gfx::PointF& pivot, AnimationMode animation_mode);
std::vector<gfx::PointF> points_;
std::unique_ptr<base::Timer> animation_timer_;
DISALLOW_COPY_AND_ASSIGN(HighlighterView);
};
} // namespace ash
#endif // ASH_HIGHLIGHTER_HIGHLIGHTER_VIEW_H_
......@@ -39,6 +39,7 @@
#include "ash/frame/custom_frame_view_ash.h"
#include "ash/gpu_support.h"
#include "ash/high_contrast/high_contrast_controller.h"
#include "ash/highlighter/highlighter_controller.h"
#include "ash/host/ash_window_tree_host_init_params.h"
#include "ash/ime/ime_controller.h"
#include "ash/keyboard/keyboard_ui.h"
......@@ -752,6 +753,7 @@ Shell::~Shell() {
audio_a11y_controller_.reset();
laser_pointer_controller_.reset();
partial_magnification_controller_.reset();
highlighter_controller_.reset();
// This also deletes all RootWindows. Note that we invoke Shutdown() on
// WindowTreeHostManager before resetting |window_tree_host_manager_|, since
......@@ -1053,6 +1055,7 @@ void Shell::Init(const ShellInitParams& init_params) {
laser_pointer_controller_.reset(new LaserPointerController());
partial_magnification_controller_.reset(new PartialMagnificationController());
highlighter_controller_.reset(new HighlighterController());
magnification_controller_.reset(MagnificationController::CreateInstance());
mru_window_tracker_ = base::MakeUnique<MruWindowTracker>();
......
......@@ -102,6 +102,7 @@ class FirstRunHelper;
class FocusCycler;
class GPUSupport;
class HighContrastController;
class HighlighterController;
class ImeController;
class ImmersiveContextAsh;
class ImmersiveHandlerFactoryAsh;
......@@ -388,6 +389,9 @@ class ASH_EXPORT Shell : public SessionObserver,
PartialMagnificationController* partial_magnification_controller() {
return partial_magnification_controller_.get();
}
HighlighterController* highlighter_controller() {
return highlighter_controller_.get();
}
ScreenshotController* screenshot_controller() {
return screenshot_controller_.get();
}
......@@ -795,6 +799,7 @@ class ASH_EXPORT Shell : public SessionObserver,
std::unique_ptr<LaserPointerController> laser_pointer_controller_;
std::unique_ptr<PartialMagnificationController>
partial_magnification_controller_;
std::unique_ptr<HighlighterController> highlighter_controller_;
// The split view controller for Chrome OS in tablet mode.
std::unique_ptr<SplitViewController> split_view_controller_;
......
......@@ -53,6 +53,8 @@ void TestPaletteDelegate::ShowMetalayer(const base::Closure& closed) {
metalayer_closed_ = closed;
}
void TestPaletteDelegate::HideMetalayer() {}
void TestPaletteDelegate::HideMetalayer() {
++hide_metalayer_count_;
}
} // namespace ash
......@@ -46,6 +46,8 @@ class TestPaletteDelegate : public PaletteDelegate {
int show_metalayer_count() const { return show_metalayer_count_; }
int hide_metalayer_count() const { return hide_metalayer_count_; }
base::Closure metalayer_closed() const { return metalayer_closed_; }
private:
......@@ -73,6 +75,7 @@ class TestPaletteDelegate : public PaletteDelegate {
bool should_show_palette_ = false;
bool is_metalayer_supported_ = false;
int show_metalayer_count_ = 0;
int hide_metalayer_count_ = 0;
base::Closure metalayer_closed_;
DISALLOW_COPY_AND_ASSIGN(TestPaletteDelegate);
......
......@@ -59,16 +59,26 @@ TEST_F(MetalayerToolTest, ViewOnlyCreatedWhenMetalayerIsSupported) {
tool_->OnViewDestroyed();
}
// Verifies that enabling the tool triggers the metalayer in the delegate.
// Invoking the callback passed to the delegate disables the tool.
TEST_F(MetalayerToolTest, EnablingMetalayerCallsDelegateAndDisablesTool) {
// Showing a metalayer calls the palette delegate to show
// the metalayer and hides the palette.
// Verifies that enabling/disabling the metalayer tool invokes the delegate.
TEST_F(MetalayerToolTest, EnablingDisablingMetalayerCallsDelegate) {
// Enabling the metalayer tool calls the delegate to show the metalayer.
// It should also hide the palette.
EXPECT_CALL(*palette_tool_delegate_.get(), HidePalette());
tool_->OnEnable();
EXPECT_EQ(1, test_palette_delegate()->show_metalayer_count());
EXPECT_EQ(0, test_palette_delegate()->hide_metalayer_count());
testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
// Enabling the metalayer tool calls the delegate to hide the metalayer.
tool_->OnDisable();
EXPECT_EQ(1, test_palette_delegate()->show_metalayer_count());
EXPECT_EQ(1, test_palette_delegate()->hide_metalayer_count());
testing::Mock::VerifyAndClearExpectations(palette_tool_delegate_.get());
}
// Verifies that invoking the callback passed to the delegate disables the tool.
TEST_F(MetalayerToolTest, MetalayerCallbackDisablesPaletteTool) {
tool_->OnEnable();
// Calling the associated callback (metalayer closed) will disable the tool.
EXPECT_CALL(*palette_tool_delegate_.get(),
DisableTool(PaletteToolId::METALAYER));
......
......@@ -35,8 +35,6 @@
#include "components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "components/arc/arc_util.h"
#include "components/arc/instance_holder.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "content/public/browser/browser_thread.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
......@@ -81,24 +79,6 @@ void ScreenshotCallback(
callback.Run(result);
}
bool IsMetalayerWindow(aura::Window* window) {
exo::Surface* surface = exo::Surface::AsSurface(window);
return surface != nullptr && surface->IsStylusOnly();
}
void CollectLayers(LayerSet& layers,
aura::Window* root_window,
base::Callback<bool(aura::Window*)> matcher_func) {
if (matcher_func.Run(root_window))
layers.insert(root_window->layer());
aura::Window::Windows children = root_window->children();
for (aura::Window::Windows::iterator iter = children.begin();
iter != children.end(); ++iter) {
CollectLayers(layers, *iter, matcher_func);
}
}
std::unique_ptr<ui::LayerTreeOwner> CreateLayerTreeForSnapshot(
aura::Window* root_window) {
LayerSet blocked_layers;
......@@ -108,7 +88,12 @@ std::unique_ptr<ui::LayerTreeOwner> CreateLayerTreeForSnapshot(
}
LayerSet excluded_layers;
CollectLayers(excluded_layers, root_window, base::Bind(IsMetalayerWindow));
// Exclude metalayer-related layers. This will also include other layers
// under kShellWindowId_OverlayContainer which is fine.
aura::Window* overlay_container = ash::Shell::GetContainer(
root_window, ash::kShellWindowId_OverlayContainer);
if (overlay_container != nullptr)
excluded_layers.insert(overlay_container->layer());
auto layer_tree_owner = ::wm::RecreateLayersWithClosure(
root_window, base::BindRepeating(
......@@ -237,11 +222,6 @@ void ArcVoiceInteractionFrameworkService::OnInstanceReady() {
binding_.Bind(mojo::MakeRequest(&host_proxy));
framework_instance->Init(std::move(host_proxy));
// Temporary shortcut added to enable the metalayer experiment.
ash::Shell::Get()->accelerator_controller()->Register(
{ui::Accelerator(ui::VKEY_A, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN)},
this);
if (is_request_pending_) {
is_request_pending_ = false;
framework_instance->StartVoiceInteractionSession();
......@@ -250,43 +230,10 @@ void ArcVoiceInteractionFrameworkService::OnInstanceReady() {
void ArcVoiceInteractionFrameworkService::OnInstanceClosed() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ash::Shell::Get()->accelerator_controller()->UnregisterAll(this);
CallAndResetMetalayerCallback();
metalayer_enabled_ = false;
}
bool ArcVoiceInteractionFrameworkService::AcceleratorPressed(
const ui::Accelerator& accelerator) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::RecordAction(base::UserMetricsAction(
"VoiceInteraction.MetalayerStarted.Search_Shift_A"));
// Temporary, used for debugging.
// Does not take into account or update the palette state.
// Explicitly call ShowMetalayer() to take into account user interaction
// initiations.
ShowMetalayer(base::Bind([]() {}));
return true;
}
bool ArcVoiceInteractionFrameworkService::CanHandleAccelerators() const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
return true;
}
void ArcVoiceInteractionFrameworkService::OnTouchEvent(ui::TouchEvent* event) {
if (event->pointer_details().pointer_type !=
ui::EventPointerType::POINTER_TYPE_PEN)
return;
if (event->type() == ui::ET_TOUCH_RELEASED) {
// Stylus gesture has just completed. This handler is only active while
// the metalayer is on. Looks like a legitimate metalayer action.
InitiateUserInteraction();
}
}
void ArcVoiceInteractionFrameworkService::CaptureFocusedWindow(
const CaptureFocusedWindowCallback& callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
......@@ -361,9 +308,6 @@ void ArcVoiceInteractionFrameworkService::ShowMetalayer(
return;
}
metalayer_closed_callback_ = closed;
// Hanlde all stylus events while the metalayer is on.
exo::WMHelper::GetInstance()->AddPreTargetHandler(this);
SetMetalayerVisibility(true);
}
void ArcVoiceInteractionFrameworkService::HideMetalayer() {
......@@ -373,8 +317,6 @@ void ArcVoiceInteractionFrameworkService::HideMetalayer() {
return;
}
metalayer_closed_callback_ = base::Closure();
exo::WMHelper::GetInstance()->RemovePreTargetHandler(this);
SetMetalayerVisibility(false);
}
void ArcVoiceInteractionFrameworkService::OnArcPlayStoreEnabledChanged(
......@@ -400,25 +342,11 @@ void ArcVoiceInteractionFrameworkService::StartVoiceInteractionSetupWizard() {
framework_instance->StartVoiceInteractionSetupWizard();
}
void ArcVoiceInteractionFrameworkService::SetMetalayerVisibility(bool visible) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
mojom::VoiceInteractionFrameworkInstance* framework_instance =
ARC_GET_INSTANCE_FOR_METHOD(
arc_bridge_service_->voice_interaction_framework(),
SetMetalayerVisibility);
if (!framework_instance) {
CallAndResetMetalayerCallback();
return;
}
framework_instance->SetMetalayerVisibility(visible);
}
void ArcVoiceInteractionFrameworkService::CallAndResetMetalayerCallback() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (metalayer_closed_callback_.is_null())
return;
base::ResetAndReturn(&metalayer_closed_callback_).Run();
exo::WMHelper::GetInstance()->RemovePreTargetHandler(this);
}
void ArcVoiceInteractionFrameworkService::SetVoiceInteractionEnabled(
......
......@@ -14,8 +14,6 @@
#include "components/arc/instance_holder.h"
#include "components/keyed_service/core/keyed_service.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/events/event_handler.h"
class KeyedServiceBaseFactory;
......@@ -37,8 +35,6 @@ class ArcBridgeService;
class ArcVoiceInteractionFrameworkService
: public KeyedService,
public mojom::VoiceInteractionFrameworkHost,
public ui::AcceleratorTarget,
public ui::EventHandler,
public InstanceHolder<mojom::VoiceInteractionFrameworkInstance>::Observer,
public ArcSessionManager::Observer {
public:
......@@ -58,17 +54,11 @@ class ArcVoiceInteractionFrameworkService
void OnInstanceReady() override;
void OnInstanceClosed() override;
// ui::AcceleratorTarget overrides.
bool AcceleratorPressed(const ui::Accelerator& accelerator) override;
bool CanHandleAccelerators() const override;
// ui::EventHandler overrides.
void OnTouchEvent(ui::TouchEvent* event) override;
// mojom::VoiceInteractionFrameworkHost overrides.
void CaptureFocusedWindow(
const CaptureFocusedWindowCallback& callback) override;
void CaptureFullscreen(const CaptureFullscreenCallback& callback) override;
// TODO(kaznacheev) remove usages of this obsolete method from the container.
void OnMetalayerClosed() override;
void SetMetalayerEnabled(bool enabled) override;
void SetVoiceInteractionRunning(bool running) override;
......@@ -114,8 +104,6 @@ class ArcVoiceInteractionFrameworkService
static const char kArcServiceName[];
private:
void SetMetalayerVisibility(bool visible);
void CallAndResetMetalayerCallback();
bool InitiateUserInteraction();
......
......@@ -5,6 +5,8 @@
#include "chrome/browser/ui/ash/palette_delegate_chromeos.h"
#include "ash/accelerators/accelerator_controller_delegate_classic.h"
#include "ash/highlighter/highlighter_controller.h"
#include "ash/highlighter/highlighter_selection_observer.h"
#include "ash/screenshot_delegate.h"
#include "ash/shell.h"
#include "ash/shell_port_classic.h"
......@@ -26,36 +28,26 @@
namespace chromeos {
class VoiceInteractionScreenshotDelegate : public ash::ScreenshotDelegate {
class VoiceInteractionSelectionObserver
: public ash::HighlighterSelectionObserver {
public:
explicit VoiceInteractionScreenshotDelegate(Profile* profile)
explicit VoiceInteractionSelectionObserver(Profile* profile)
: profile_(profile) {}
~VoiceInteractionScreenshotDelegate() override = default;
~VoiceInteractionSelectionObserver() override = default;
private:
void HandleTakeScreenshotForAllRootWindows() override { NOTIMPLEMENTED(); }
void HandleTakePartialScreenshot(aura::Window* window,
const gfx::Rect& rect) override {
void HandleSelection(const gfx::Rect& rect) override {
auto* framework =
arc::ArcVoiceInteractionFrameworkService::GetForBrowserContext(
profile_);
if (!framework)
return;
double device_scale_factor = window->layer()->device_scale_factor();
framework->StartSessionFromUserInteraction(
gfx::ScaleToEnclosingRect(rect, device_scale_factor));
}
void HandleTakeWindowScreenshot(aura::Window* window) override {
NOTIMPLEMENTED();
framework->StartSessionFromUserInteraction(rect);
}
bool CanTakeScreenshot() override { return true; }
Profile* const profile_; // Owned by ProfileManager.
DISALLOW_COPY_AND_ASSIGN(VoiceInteractionScreenshotDelegate);
DISALLOW_COPY_AND_ASSIGN(VoiceInteractionSelectionObserver);
};
PaletteDelegateChromeOS::PaletteDelegateChromeOS() : weak_factory_(this) {
......@@ -174,22 +166,9 @@ void PaletteDelegateChromeOS::TakeScreenshot() {
void PaletteDelegateChromeOS::TakePartialScreenshot(const base::Closure& done) {
auto* screenshot_controller = ash::Shell::Get()->screenshot_controller();
ash::ScreenshotDelegate* screenshot_delegate;
if (IsMetalayerSupported()) {
// This is an experimental mode. It will be either taken out or grow
// into a separate tool next to "Capture region".
if (!voice_interaction_screenshot_delegate_) {
voice_interaction_screenshot_delegate_ =
base::MakeUnique<VoiceInteractionScreenshotDelegate>(profile_);
}
screenshot_delegate = voice_interaction_screenshot_delegate_.get();
} else {
screenshot_delegate = ash::ShellPortClassic::Get()
->accelerator_controller_delegate()
->screenshot_delegate();
}
auto* screenshot_delegate = ash::ShellPortClassic::Get()
->accelerator_controller_delegate()
->screenshot_delegate();
screenshot_controller->set_pen_events_only(true);
screenshot_controller->StartPartialScreenshotSession(
screenshot_delegate, false /* draw_overlay_immediately */);
......@@ -217,6 +196,13 @@ void PaletteDelegateChromeOS::ShowMetalayer(const base::Closure& closed) {
return;
}
service->ShowMetalayer(closed);
if (!highlighter_selection_observer) {
highlighter_selection_observer =
base::MakeUnique<VoiceInteractionSelectionObserver>(profile_);
}
ash::Shell::Get()->highlighter_controller()->EnableHighlighter(
highlighter_selection_observer.get());
}
void PaletteDelegateChromeOS::HideMetalayer() {
......@@ -225,6 +211,8 @@ void PaletteDelegateChromeOS::HideMetalayer() {
if (!service)
return;
service->HideMetalayer();
ash::Shell::Get()->highlighter_controller()->DisableHighlighter();
}
} // namespace chromeos
......@@ -20,7 +20,7 @@ class PrefChangeRegistrar;
class Profile;
namespace ash {
class ScreenshotDelegate;
class HighlighterSelectionObserver;
}
namespace chromeos {
......@@ -72,8 +72,8 @@ class PaletteDelegateChromeOS
session_state_observer_;
content::NotificationRegistrar registrar_;
std::unique_ptr<ash::ScreenshotDelegate>
voice_interaction_screenshot_delegate_;
std::unique_ptr<ash::HighlighterSelectionObserver>
highlighter_selection_observer;
base::WeakPtrFactory<PaletteDelegateChromeOS> weak_factory_;
......
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