blob: 9d6fa0a16cfaf26da711279ac8989f73a3816cb9 [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 "third_party/blink/renderer/core/layout/ng/inline/ng_caret_rect.h"
#include "third_party/blink/renderer/core/editing/local_caret_rect.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_caret_position.h"
#include "third_party/blink/renderer/core/layout/ng/inline/ng_physical_text_fragment.h"
#include "third_party/blink/renderer/core/paint/ng/ng_paint_fragment.h"
namespace blink {
namespace {
NGPhysicalOffsetRect ComputeLocalCaretRectByBoxSide(
const NGPaintFragment& fragment,
NGCaretPositionType position_type) {
const bool is_horizontal = fragment.Style().IsHorizontalWritingMode();
DCHECK(fragment.ContainerLineBox());
const NGPaintFragment& line_box = *fragment.ContainerLineBox();
const NGPhysicalOffset offset_to_line_box =
fragment.InlineOffsetToContainerBox() -
line_box.InlineOffsetToContainerBox();
LayoutUnit caret_height =
is_horizontal ? line_box.Size().height : line_box.Size().width;
LayoutUnit caret_top =
is_horizontal ? -offset_to_line_box.top : -offset_to_line_box.left;
const LocalFrameView* frame_view =
fragment.GetLayoutObject()->GetDocument().View();
LayoutUnit caret_width = frame_view->CaretWidth();
const bool is_ltr = IsLtr(fragment.PhysicalFragment().ResolvedDirection());
LayoutUnit caret_left;
if (is_ltr != (position_type == NGCaretPositionType::kBeforeBox)) {
if (is_horizontal)
caret_left = fragment.Size().width - caret_width;
else
caret_left = fragment.Size().height - caret_width;
}
if (!is_horizontal) {
std::swap(caret_top, caret_left);
std::swap(caret_width, caret_height);
}
const NGPhysicalOffset caret_location(caret_left, caret_top);
const NGPhysicalSize caret_size(caret_width, caret_height);
return NGPhysicalOffsetRect(caret_location, caret_size);
}
NGPhysicalOffsetRect ComputeLocalCaretRectAtTextOffset(
const NGPaintFragment& paint_fragment,
unsigned offset) {
const NGPhysicalTextFragment& fragment =
ToNGPhysicalTextFragment(paint_fragment.PhysicalFragment());
DCHECK_GE(offset, fragment.StartOffset());
DCHECK_LE(offset, fragment.EndOffset());
const LocalFrameView* frame_view =
fragment.GetLayoutObject()->GetDocument().View();
LayoutUnit caret_width = frame_view->CaretWidth();
const bool is_horizontal = fragment.Style().IsHorizontalWritingMode();
LayoutUnit caret_height =
is_horizontal ? fragment.Size().height : fragment.Size().width;
LayoutUnit caret_top;
LayoutUnit caret_left = fragment.InlinePositionForOffset(offset);
if (!fragment.IsLineBreak())
caret_left -= caret_width / 2;
if (!is_horizontal) {
std::swap(caret_top, caret_left);
std::swap(caret_width, caret_height);
}
// Adjust the location to be relative to the inline formatting context.
NGPhysicalOffset caret_location = NGPhysicalOffset(caret_left, caret_top) +
paint_fragment.InlineOffsetToContainerBox();
NGPhysicalSize caret_size(caret_width, caret_height);
const NGPaintFragment& context_fragment =
*NGPaintFragment::GetForInlineContainer(fragment.GetLayoutObject());
const NGPaintFragment* line_box = paint_fragment.ContainerLineBox();
const NGPhysicalOffset line_box_offset =
line_box->InlineOffsetToContainerBox();
const NGPhysicalOffsetRect line_box_rect(line_box_offset, line_box->Size());
// For horizontal text, adjust the location in the x direction to ensure that
// it completely falls in the union of line box and containing block, and
// then round it to the nearest pixel.
if (is_horizontal) {
const LayoutUnit min_x = std::min(LayoutUnit(), line_box_offset.left);
caret_location.left = std::max(caret_location.left, min_x);
const LayoutUnit max_x =
std::max(context_fragment.Size().width, line_box_rect.Right());
caret_location.left = std::min(caret_location.left, max_x - caret_width);
caret_location.left = LayoutUnit(caret_location.left.Round());
return NGPhysicalOffsetRect(caret_location, caret_size);
}
// Similar adjustment and rounding for vertical text.
const LayoutUnit min_y = std::min(LayoutUnit(), line_box_offset.top);
caret_location.top = std::max(caret_location.top, min_y);
const LayoutUnit max_y =
std::max(context_fragment.Size().height, line_box_rect.Bottom());
caret_location.top = std::min(caret_location.top, max_y - caret_height);
caret_location.top = LayoutUnit(caret_location.top.Round());
return NGPhysicalOffsetRect(caret_location, caret_size);
}
LocalCaretRect ComputeLocalCaretRect(const NGCaretPosition& caret_position) {
if (caret_position.IsNull())
return LocalCaretRect();
const NGPaintFragment& fragment = *caret_position.fragment;
const LayoutObject* layout_object = fragment.GetLayoutObject();
switch (caret_position.position_type) {
case NGCaretPositionType::kBeforeBox:
case NGCaretPositionType::kAfterBox: {
DCHECK(fragment.PhysicalFragment().IsBox());
const NGPhysicalOffsetRect fragment_local_rect =
ComputeLocalCaretRectByBoxSide(fragment,
caret_position.position_type);
return {layout_object, fragment_local_rect.ToLayoutRect()};
}
case NGCaretPositionType::kAtTextOffset: {
DCHECK(fragment.PhysicalFragment().IsText());
DCHECK(caret_position.text_offset.has_value());
const NGPhysicalOffsetRect caret_rect = ComputeLocalCaretRectAtTextOffset(
fragment, *caret_position.text_offset);
LayoutRect layout_rect = caret_rect.ToLayoutRect();
// For vertical-rl, convert to "flipped block-flow" coordinates space.
// See core/layout/README.md#coordinate-spaces for details.
if (fragment.Style().IsFlippedBlocksWritingMode()) {
const LayoutBlockFlow* container =
layout_object->ContainingNGBlockFlow();
container->FlipForWritingMode(layout_rect);
}
return {layout_object, layout_rect};
}
}
NOTREACHED();
return {layout_object, LayoutRect()};
}
} // namespace
LocalCaretRect ComputeNGLocalCaretRect(const PositionWithAffinity& position) {
return ComputeLocalCaretRect(ComputeNGCaretPosition(position));
}
} // namespace blink