blob: d2f524e81012ff3e766debc69070baa8b2b49f59 [file] [log] [blame]
// 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 "components/ui_devtools/views/overlay_agent_views.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "components/ui_devtools/ui_element.h"
#include "components/ui_devtools/views/view_element.h"
#include "components/ui_devtools/views/widget_element.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/effects/SkDashPathEffect.h"
#include "ui/compositor/paint_recorder.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/events/event.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/render_text.h"
#include "ui/views/background.h"
#include "ui/views/border.h"
#if defined(USE_AURA)
#include "ui/aura/window.h"
#include "ui/wm/core/window_util.h"
#endif
namespace ui_devtools {
namespace {
using namespace ui_devtools::protocol;
void DrawRulerText(const base::string16& utf16_text,
const gfx::Point& p,
gfx::Canvas* canvas,
gfx::RenderText* render_text_) {
render_text_->SetText(utf16_text);
render_text_->SetColor(SK_ColorRED);
const gfx::Rect text_rect(gfx::Rect(p, render_text_->GetStringSize()));
canvas->FillRect(text_rect, SK_ColorWHITE, SkBlendMode::kColor);
render_text_->SetDisplayRect(text_rect);
render_text_->Draw(canvas);
}
void DrawRulers(const gfx::Rect& screen_bounds,
gfx::Canvas* canvas,
gfx::RenderText* render_text_) {
// Top horizontal ruler from left to right.
canvas->Draw1pxLine(gfx::PointF(0.0f, 0.0f),
gfx::PointF(screen_bounds.right(), 0.0f),
SK_ColorMAGENTA);
// Left vertical ruler from top to bottom.
canvas->Draw1pxLine(gfx::PointF(0.0f, 0.0f),
gfx::PointF(0.0f, screen_bounds.bottom()),
SK_ColorMAGENTA);
int short_stroke = 5;
int long_stroke = 10;
int gap_between_strokes = 4;
int gap_between_long_stroke = 100;
// Draw top horizontal ruler.
for (int x = gap_between_strokes; x < screen_bounds.right();
x += gap_between_strokes) {
if (x % gap_between_long_stroke == 0) {
canvas->Draw1pxLine(gfx::PointF(x, 0.0f), gfx::PointF(x, long_stroke),
SK_ColorMAGENTA);
// Draw ruler marks.
base::string16 utf16_text = base::UTF8ToUTF16(std::to_string(x));
DrawRulerText(utf16_text, gfx::Point(x + 2, long_stroke), canvas,
render_text_);
} else {
canvas->Draw1pxLine(gfx::PointF(x, 0.0f), gfx::PointF(x, short_stroke),
SK_ColorMAGENTA);
}
}
// Draw left vertical ruler.
for (int y = 0; y < screen_bounds.bottom(); y += gap_between_strokes) {
if (y % gap_between_long_stroke == 0) {
canvas->Draw1pxLine(gfx::PointF(0.0f, y), gfx::PointF(long_stroke, y),
SK_ColorMAGENTA);
// Draw ruler marks.
base::string16 utf16_text = base::UTF8ToUTF16(std::to_string(y));
DrawRulerText(utf16_text, gfx::Point(short_stroke + 1, y + 2), canvas,
render_text_);
} else {
canvas->Draw1pxLine(gfx::PointF(0.0f, y), gfx::PointF(short_stroke, y),
SK_ColorMAGENTA);
}
}
}
// Draw width() x height() of a rectangle if not empty. Otherwise, draw either
// width() or height() if any of them is not empty.
void DrawSizeOfRectangle(const gfx::Rect& hovered_rect,
const RectSide drawing_side,
gfx::Canvas* canvas,
gfx::RenderText* render_text_) {
base::string16 utf16_text;
const std::string unit = "dp";
if (!hovered_rect.IsEmpty()) {
utf16_text = base::UTF8ToUTF16(hovered_rect.size().ToString() + unit);
} else if (hovered_rect.height()) {
// Draw only height() if height() is not empty.
utf16_text =
base::UTF8ToUTF16(std::to_string(hovered_rect.height()) + unit);
} else if (hovered_rect.width()) {
// Draw only width() if width() is not empty.
utf16_text = base::UTF8ToUTF16(std::to_string(hovered_rect.width()) + unit);
} else {
// If both width() and height() are empty, canvas won't draw size.
return;
}
render_text_->SetText(utf16_text);
render_text_->SetColor(SK_ColorRED);
const gfx::Size& text_size = render_text_->GetStringSize();
gfx::Rect text_rect;
if (drawing_side == RectSide::LEFT_SIDE) {
const gfx::Point text_left_side(
hovered_rect.x() + 1,
hovered_rect.height() / 2 - text_size.height() / 2 + hovered_rect.y());
text_rect = gfx::Rect(text_left_side,
gfx::Size(text_size.width(), text_size.height()));
} else if (drawing_side == RectSide::RIGHT_SIDE) {
const gfx::Point text_right_side(
hovered_rect.right() - 1 - text_size.width(),
hovered_rect.height() / 2 - text_size.height() / 2 + hovered_rect.y());
text_rect = gfx::Rect(text_right_side,
gfx::Size(text_size.width(), text_size.height()));
} else if (drawing_side == RectSide::TOP_SIDE) {
const gfx::Point text_top_side(
hovered_rect.x() + hovered_rect.width() / 2 - text_size.width() / 2,
hovered_rect.y() + 1);
text_rect = gfx::Rect(text_top_side,
gfx::Size(text_size.width(), text_size.height()));
} else if (drawing_side == RectSide::BOTTOM_SIDE) {
const gfx::Point text_top_side(
hovered_rect.x() + hovered_rect.width() / 2 - text_size.width() / 2,
hovered_rect.bottom() - 1 - text_size.height());
text_rect = gfx::Rect(text_top_side,
gfx::Size(text_size.width(), text_size.height()));
}
canvas->FillRect(text_rect, SK_ColorWHITE, SkBlendMode::kColor);
render_text_->SetDisplayRect(text_rect);
render_text_->Draw(canvas);
}
void DrawRectGuideLinesOnCanvas(const gfx::Rect& screen_bounds,
const gfx::RectF& rect_f,
cc::PaintFlags flags,
gfx::Canvas* canvas) {
// Top horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, rect_f.y()),
gfx::PointF(screen_bounds.right(), rect_f.y()), flags);
// Bottom horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, rect_f.bottom()),
gfx::PointF(screen_bounds.right(), rect_f.bottom()), flags);
// Left vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(rect_f.x(), 0.0f),
gfx::PointF(rect_f.x(), screen_bounds.bottom()), flags);
// Right vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(rect_f.right(), 0.0f),
gfx::PointF(rect_f.right(), screen_bounds.bottom()), flags);
}
void DrawSizeWithAnyBounds(float x1,
float y1,
float x2,
float y2,
RectSide side,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
if (x2 > x1 || y2 > y1) {
DrawSizeOfRectangle(gfx::Rect(x1, y1, x2 - x1, y2 - y1), side, canvas,
render_text);
} else {
DrawSizeOfRectangle(gfx::Rect(x2, y2, x1 - x2, y1 - y2), side, canvas,
render_text);
}
}
void DrawR1ContainsR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
// Horizontal left distance line.
float x1 = pinned_rect_f.x();
float y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
float x2 = hovered_rect_f.x();
float y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
// Horizontal right distance line.
x1 = hovered_rect_f.right();
y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
x2 = pinned_rect_f.right();
y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
// Vertical top distance line.
x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
y1 = pinned_rect_f.y();
x2 = x1;
y2 = hovered_rect_f.y();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
// Vertical bottom distance line.
x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
y1 = hovered_rect_f.bottom();
x2 = x1;
y2 = pinned_rect_f.bottom();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1HorizontalFullLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
// Horizontal left distance line.
float x1 = hovered_rect_f.right();
float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
float x2 = pinned_rect_f.x();
float y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
}
void DrawR1TopFullLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas_,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.x() + hovered_rect_f.width();
float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
float x2 = pinned_rect_f.x();
float y2 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
// Horizontal left dotted line.
canvas_->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas_,
render_text);
x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
y1 = hovered_rect_f.y() + hovered_rect_f.height();
x2 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
y2 = pinned_rect_f.y();
// Vertical left dotted line.
canvas_->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas_,
render_text);
}
void DrawR1BottomFullLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.right();
float y1 = hovered_rect_f.y() + hovered_rect_f.height() / 2;
float x2 = pinned_rect_f.x();
float y2 = y1;
// Horizontal left distance line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
y1 = pinned_rect_f.bottom();
x2 = x1;
y2 = hovered_rect_f.y();
// Vertical left distance line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1TopPartialLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
float y1 = hovered_rect_f.bottom();
float x2 = x1;
float y2 = pinned_rect_f.y();
// Vertical left dotted line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1BottomPartialLeftR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
float x1 = hovered_rect_f.x() + hovered_rect_f.width() / 2;
float y1 = pinned_rect_f.bottom();
float x2 = x1;
float y2 = hovered_rect_f.y();
// Vertical left dotted line.
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
}
void DrawR1IntersectsR2(const gfx::RectF& pinned_rect_f,
const gfx::RectF& hovered_rect_f,
const cc::PaintFlags& flags,
gfx::Canvas* canvas,
gfx::RenderText* render_text) {
// Vertical dotted line for the top side of the pinned rectangle
float x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
float y1 = pinned_rect_f.y();
float x2 = x1;
float y2 = hovered_rect_f.y();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
// Vertical dotted line for the bottom side of the pinned rectangle
x1 = pinned_rect_f.x() + pinned_rect_f.width() / 2;
y1 = pinned_rect_f.bottom();
x2 = x1;
y2 = hovered_rect_f.bottom();
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::LEFT_SIDE, canvas,
render_text);
// Horizontal dotted line for the left side of the pinned rectangle
x1 = pinned_rect_f.x();
y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
x2 = hovered_rect_f.x();
y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
// Horizontal dotted line for the right side of the pinned rectangle
x1 = pinned_rect_f.right();
y1 = pinned_rect_f.y() + pinned_rect_f.height() / 2;
x2 = hovered_rect_f.right();
y2 = y1;
canvas->DrawLine(gfx::PointF(x1, y1), gfx::PointF(x2, y2), flags);
DrawSizeWithAnyBounds(x1, y1, x2, y2, RectSide::BOTTOM_SIDE, canvas,
render_text);
}
} // namespace
OverlayAgentViews::OverlayAgentViews(DOMAgent* dom_agent)
: OverlayAgent(dom_agent),
show_size_on_canvas_(false),
highlight_rect_config_(HighlightRectsConfiguration::NO_DRAW) {}
OverlayAgentViews::~OverlayAgentViews() {}
void OverlayAgentViews::SetPinnedNodeId(int node_id) {
pinned_id_ = node_id;
frontend()->nodeHighlightRequested(pinned_id_);
HighlightNode(pinned_id_, true /* show_size */);
}
protocol::Response OverlayAgentViews::setInspectMode(
const String& in_mode,
protocol::Maybe<protocol::Overlay::HighlightConfig> in_highlightConfig) {
pinned_id_ = 0;
if (in_mode.compare("searchForNode") == 0) {
InstallPreTargetHandler();
} else if (in_mode.compare("none") == 0) {
RemovePreTargetHandler();
}
return protocol::Response::OK();
}
protocol::Response OverlayAgentViews::highlightNode(
std::unique_ptr<protocol::Overlay::HighlightConfig> highlight_config,
protocol::Maybe<int> node_id) {
return HighlightNode(node_id.fromJust());
}
protocol::Response OverlayAgentViews::hideHighlight() {
if (layer_for_highlighting_ && layer_for_highlighting_->visible())
layer_for_highlighting_->SetVisible(false);
return Response::OK();
}
void OverlayAgentViews::ShowDistancesInHighlightOverlay(int pinned_id,
int element_id) {
const std::pair<gfx::NativeWindow, gfx::Rect> pair_r2(
dom_agent()
->GetElementFromNodeId(element_id)
->GetNodeWindowAndScreenBounds());
const std::pair<gfx::NativeWindow, gfx::Rect> pair_r1(
dom_agent()
->GetElementFromNodeId(pinned_id)
->GetNodeWindowAndScreenBounds());
#if defined(OS_MACOSX)
// TODO(lgrey): Explain this
if (pair_r1.first != pair_r2.first) {
pinned_id_ = 0;
return;
}
#endif
gfx::Rect r2(pair_r2.second);
gfx::Rect r1(pair_r1.second);
pinned_rect_ = r1;
is_swap_ = false;
if (r1.x() > r2.x()) {
is_swap_ = true;
std::swap(r1, r2);
}
if (r1.Contains(r2)) {
highlight_rect_config_ = HighlightRectsConfiguration::R1_CONTAINS_R2;
} else if (r1.right() <= r2.x()) {
if ((r1.y() <= r2.y() && r2.y() <= r1.bottom()) ||
(r1.y() <= r2.bottom() && r2.bottom() <= r1.bottom()) ||
(r2.y() <= r1.y() && r1.y() <= r2.bottom()) ||
(r2.y() <= r1.bottom() && r1.bottom() <= r2.bottom())) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_HORIZONTAL_FULL_LEFT_R2;
} else if (r1.bottom() <= r2.y()) {
highlight_rect_config_ = HighlightRectsConfiguration::R1_TOP_FULL_LEFT_R2;
} else if (r1.y() >= r2.bottom()) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2;
}
} else if (r1.x() <= r2.x() && r2.x() <= r1.right()) {
if (r1.bottom() <= r2.y()) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2;
} else if (r1.y() >= r2.bottom()) {
highlight_rect_config_ =
HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2;
} else if (r1.Intersects(r2)) {
highlight_rect_config_ = HighlightRectsConfiguration::R1_INTERSECTS_R2;
} else {
NOTREACHED();
}
} else {
highlight_rect_config_ = HighlightRectsConfiguration::NO_DRAW;
}
}
Response OverlayAgentViews::HighlightNode(int node_id, bool show_size) {
UIElement* element = dom_agent()->GetElementFromNodeId(node_id);
if (!element)
return Response::Error("No node found with that id");
if (!layer_for_highlighting_) {
layer_for_highlighting_.reset(new ui::Layer(ui::LayerType::LAYER_TEXTURED));
layer_for_highlighting_->set_name("HighlightingLayer");
layer_for_highlighting_->set_delegate(this);
layer_for_highlighting_->SetFillsBoundsOpaquely(false);
}
highlight_rect_config_ = HighlightRectsConfiguration::NO_DRAW;
show_size_on_canvas_ = show_size;
layer_for_highlighting_->SetVisible(
UpdateHighlight(element->GetNodeWindowAndScreenBounds()));
return Response::OK();
}
void OverlayAgentViews::OnMouseEvent(ui::MouseEvent* event) {
// Make sure the element tree has been populated before processing
// mouse events.
if (!dom_agent()->element_root())
return;
// Show parent of the pinned element with id |pinned_id_| when mouse scrolls
// up. If parent exists, highlight and re-pin parent element.
if (event->type() == ui::ET_MOUSEWHEEL && pinned_id_) {
const ui::MouseWheelEvent* mouse_event =
static_cast<ui::MouseWheelEvent*>(event);
DCHECK(mouse_event);
if (mouse_event->y_offset() > 0) {
const int parent_node_id = dom_agent()->GetParentIdOfNodeId(pinned_id_);
if (parent_node_id)
SetPinnedNodeId(parent_node_id);
event->SetHandled();
} else if (mouse_event->y_offset() < 0) {
// TODO(thanhph): discuss behaviours when mouse scrolls down.
}
return;
}
// Find node id of element whose bounds contain the mouse pointer location.
int element_id = FindElementIdTargetedByPoint(event);
if (!element_id)
return;
#if defined(USE_AURA)
aura::Window* target = static_cast<aura::Window*>(event->target());
bool active_window = ::wm::IsActiveWindow(
target->GetRootWindow()->GetEventHandlerForPoint(event->root_location()));
#else
bool active_window = true;
#endif
if (pinned_id_ == element_id && active_window) {
event->SetHandled();
return;
}
// Pin the hover element on click.
if (event->type() == ui::ET_MOUSE_PRESSED) {
if (active_window)
event->SetHandled();
SetPinnedNodeId(element_id);
} else if (pinned_id_) {
// If hovering with a pinned element, then show distances between the pinned
// element and the hover element.
HighlightNode(element_id, false /* show_size */);
ShowDistancesInHighlightOverlay(pinned_id_, element_id);
} else {
// Display only guidelines if hovering without a pinned element.
frontend()->nodeHighlightRequested(element_id);
HighlightNode(element_id, false /* show_size */);
}
}
void OverlayAgentViews::OnKeyEvent(ui::KeyEvent* event) {
if (!dom_agent()->element_root())
return;
// Exit inspect mode by pressing ESC key.
if (event->key_code() == ui::KeyboardCode::VKEY_ESCAPE) {
RemovePreTargetHandler();
if (pinned_id_) {
frontend()->inspectNodeRequested(pinned_id_);
HighlightNode(pinned_id_, true /* show_size */);
}
// Unpin element.
pinned_id_ = 0;
}
}
void OverlayAgentViews::OnPaintLayer(const ui::PaintContext& context) {
const gfx::Rect& screen_bounds(layer_for_highlighting_->bounds());
ui::PaintRecorder recorder(context, screen_bounds.size());
gfx::Canvas* canvas = recorder.canvas();
// Convert the hovered rect from screen coordinates to layer coordinates.
gfx::RectF hovered_rect_f(hovered_rect_);
hovered_rect_f.Offset(-layer_for_highlighting_screen_offset_);
cc::PaintFlags flags;
flags.setStrokeWidth(1.0f);
flags.setColor(SK_ColorBLUE);
flags.setStyle(cc::PaintFlags::kStroke_Style);
constexpr SkScalar intervals[] = {1.f, 4.f};
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
if (!render_text_)
render_text_ = gfx::RenderText::CreateHarfBuzzInstance();
DrawRulers(screen_bounds, canvas, render_text_.get());
// Display guide lines if |highlight_rect_config_| is NO_DRAW.
if (highlight_rect_config_ == HighlightRectsConfiguration::NO_DRAW) {
hovered_rect_f.Inset(gfx::InsetsF(-1));
DrawRectGuideLinesOnCanvas(screen_bounds, hovered_rect_f, flags, canvas);
// Draw |hovered_rect_f| bounds.
flags.setPathEffect(nullptr);
canvas->DrawRect(hovered_rect_f, flags);
// Display size of the rectangle after mouse click.
if (show_size_on_canvas_) {
DrawSizeOfRectangle(gfx::ToNearestRect(hovered_rect_f),
RectSide::BOTTOM_SIDE, canvas, render_text_.get());
}
return;
}
flags.setPathEffect(nullptr);
flags.setColor(SK_ColorBLUE);
// Convert the pinned rect from screen coordinates to layer coordinates.
gfx::RectF pinned_rect_f(pinned_rect_);
pinned_rect_f.Offset(-layer_for_highlighting_screen_offset_);
// Draw |pinned_rect_f| bounds in blue.
canvas->DrawRect(pinned_rect_f, flags);
// Draw |hovered_rect_f| bounds in green.
flags.setColor(SK_ColorGREEN);
canvas->DrawRect(hovered_rect_f, flags);
// Draw distances in red colour.
flags.setPathEffect(nullptr);
flags.setColor(SK_ColorRED);
// Make sure |pinned_rect_f| stays on the right or below of |hovered_rect_f|.
if (pinned_rect_f.x() < hovered_rect_f.x() ||
(pinned_rect_f.x() == hovered_rect_f.x() &&
pinned_rect_f.y() < hovered_rect_f.y())) {
std::swap(pinned_rect_f, hovered_rect_f);
}
switch (highlight_rect_config_) {
case HighlightRectsConfiguration::R1_CONTAINS_R2:
DrawR1ContainsR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
return;
case HighlightRectsConfiguration::R1_HORIZONTAL_FULL_LEFT_R2:
DrawR1HorizontalFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
return;
case HighlightRectsConfiguration::R1_TOP_FULL_LEFT_R2:
DrawR1TopFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 4 guide lines along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
// Bottom horizontal dotted line from left to right.
canvas->DrawLine(
gfx::PointF(0.0f, hovered_rect_f.bottom()),
gfx::PointF(screen_bounds.right(), hovered_rect_f.bottom()), flags);
// Right vertical dotted line from top to bottom.
canvas->DrawLine(
gfx::PointF(hovered_rect_f.right(), 0.0f),
gfx::PointF(hovered_rect_f.right(), screen_bounds.bottom()), flags);
// Top horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
flags);
// Left vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(pinned_rect_f.x(), 0.0f),
gfx::PointF(pinned_rect_f.x(), screen_bounds.bottom()),
flags);
return;
case HighlightRectsConfiguration::R1_BOTTOM_FULL_LEFT_R2:
DrawR1BottomFullLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 2 guide lines along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
// Top horizontal dotted line from left to right.
canvas->DrawLine(
gfx::PointF(0.0f, pinned_rect_f.bottom()),
gfx::PointF(screen_bounds.right(), pinned_rect_f.bottom()), flags);
// Left vertical dotted line from top to bottom.
canvas->DrawLine(gfx::PointF(pinned_rect_f.x(), 0.0f),
gfx::PointF(pinned_rect_f.x(), screen_bounds.bottom()),
flags);
return;
case HighlightRectsConfiguration::R1_TOP_PARTIAL_LEFT_R2:
DrawR1TopPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 1 guide line along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
// Top horizontal dotted line from left to right.
canvas->DrawLine(gfx::PointF(0.0f, pinned_rect_f.y()),
gfx::PointF(screen_bounds.right(), pinned_rect_f.y()),
flags);
return;
case HighlightRectsConfiguration::R1_BOTTOM_PARTIAL_LEFT_R2:
DrawR1BottomPartialLeftR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
return;
case HighlightRectsConfiguration::R1_INTERSECTS_R2:
DrawR1IntersectsR2(pinned_rect_f, hovered_rect_f, flags, canvas,
render_text_.get());
// Draw 4 guide line along distance lines.
flags.setPathEffect(SkDashPathEffect::Make(intervals, 2, 0));
DrawRectGuideLinesOnCanvas(screen_bounds, hovered_rect_f, flags, canvas);
return;
default:
NOTREACHED();
return;
}
}
bool OverlayAgentViews::UpdateHighlight(
const std::pair<gfx::NativeWindow, gfx::Rect>& window_and_bounds) {
if (window_and_bounds.second.IsEmpty()) {
hovered_rect_.SetRect(0, 0, 0, 0);
return false;
}
ui::Layer* root_layer = nullptr;
#if defined(OS_MACOSX)
views::Widget* widget =
views::Widget::GetWidgetForNativeWindow(window_and_bounds.first);
root_layer = widget->GetLayer();
layer_for_highlighting_screen_offset_ =
widget->GetContentsView()->GetBoundsInScreen().OffsetFromOrigin();
#else
gfx::NativeWindow root = window_and_bounds.first->GetRootWindow();
root_layer = root->layer();
#if defined(OS_CHROMEOS)
// Get the screen's display-root window; otherwise, if the window belongs to
// a window service client, |root| will only be a client-root window.
aura::Window* window = display::Screen::GetScreen()->GetWindowAtScreenPoint(
root->GetBoundsInScreen().origin());
if (window) // May be null in unit tests.
root = window->GetRootWindow();
#endif // OS_CHROMEOS
layer_for_highlighting_screen_offset_ =
root->GetBoundsInScreen().OffsetFromOrigin();
#endif // defined(OS_MACOSX)
DCHECK(root_layer);
layer_for_highlighting_->SetBounds(root_layer->bounds());
layer_for_highlighting_->SchedulePaint(root_layer->bounds());
if (root_layer != layer_for_highlighting_->parent())
root_layer->Add(layer_for_highlighting_.get());
else
root_layer->StackAtTop(layer_for_highlighting_.get());
hovered_rect_ = window_and_bounds.second;
return true;
}
} // namespace ui_devtools