blob: 260cd998d52d2aaf4d92552f0c2e6d22a6147800 [file] [log] [blame]
/*
* Copyright (C) 2019-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "InlineIteratorTextBox.h"
#include "InlineIteratorLine.h"
#include "LayoutIntegrationLineLayout.h"
#include "RenderCombineText.h"
namespace WebCore {
namespace InlineIterator {
TextBoxIterator TextBox::nextTextBox() const
{
return TextBoxIterator(*this).traverseNextTextBox();
}
LayoutRect TextBox::selectionRect(unsigned rangeStart, unsigned rangeEnd) const
{
auto [clampedStart, clampedEnd] = selectableRange().clamp(rangeStart, rangeEnd);
if (clampedStart >= clampedEnd && !(rangeStart == rangeEnd && rangeStart >= start() && rangeStart <= end()))
return { };
auto selectionTop = line()->selectionTop();
auto selectionHeight = line()->selectionHeight();
LayoutRect selectionRect { logicalLeft(), selectionTop, logicalWidth(), selectionHeight };
TextRun textRun = createTextRun();
if (clampedStart || clampedEnd != textRun.length())
fontCascade().adjustSelectionRectForText(textRun, selectionRect, clampedStart, clampedEnd);
return snappedSelectionRect(selectionRect, logicalRight(), selectionTop, selectionHeight, isHorizontal());
}
unsigned TextBox::offsetForPosition(float x, bool includePartialGlyphs) const
{
if (isLineBreak())
return 0;
if (x - logicalLeft() > logicalWidth())
return isLeftToRightDirection() ? length() : 0;
if (x - logicalLeft() < 0)
return isLeftToRightDirection() ? 0 : length();
return fontCascade().offsetForPosition(createTextRun(CreateTextRunMode::Editing), x - logicalLeft(), includePartialGlyphs);
}
float TextBox::positionForOffset(unsigned offset) const
{
ASSERT(offset >= start());
ASSERT(offset <= end());
if (isLineBreak())
return logicalLeft();
auto [startOffset, endOffset] = [&] {
if (direction() == TextDirection::RTL)
return std::pair { selectableRange().clamp(offset), length() };
return std::pair { 0u, selectableRange().clamp(offset) };
}();
auto selectionRect = LayoutRect(logicalLeft(), 0, 0, 0);
auto textRun = createTextRun(CreateTextRunMode::Editing);
fontCascade().adjustSelectionRectForText(textRun, selectionRect, startOffset, endOffset);
return snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()).maxX();
}
bool TextBox::isCombinedText() const
{
auto& renderer = this->renderer();
return is<RenderCombineText>(renderer) && downcast<RenderCombineText>(renderer).isCombined();
}
const FontCascade& TextBox::fontCascade() const
{
if (isCombinedText())
return downcast<RenderCombineText>(renderer()).textCombineFont();
return style().fontCascade();
}
TextBoxIterator::TextBoxIterator(Box::PathVariant&& pathVariant)
: LeafBoxIterator(WTFMove(pathVariant))
{
}
TextBoxIterator::TextBoxIterator(const Box& box)
: LeafBoxIterator(box)
{
}
TextBoxIterator& TextBoxIterator::traverseNextTextBox()
{
WTF::switchOn(m_box.m_pathVariant, [](auto& path) {
path.traverseNextTextBox();
});
return *this;
}
TextBoxIterator firstTextBoxFor(const RenderText& text)
{
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
if (auto* lineLayout = LayoutIntegration::LineLayout::containing(text))
return lineLayout->textBoxesFor(text);
#endif
return { BoxLegacyPath { text.firstTextBox() } };
}
TextBoxIterator textBoxFor(const LegacyInlineTextBox* legacyInlineTextBox)
{
return { BoxLegacyPath { legacyInlineTextBox } };
}
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
TextBoxIterator textBoxFor(const LayoutIntegration::InlineContent& content, const InlineDisplay::Box& box)
{
return textBoxFor(content, content.indexForBox(box));
}
TextBoxIterator textBoxFor(const LayoutIntegration::InlineContent& content, size_t boxIndex)
{
ASSERT(content.boxes[boxIndex].text());
return { BoxModernPath { content, boxIndex } };
}
#endif
TextBoxRange textBoxesFor(const RenderText& text)
{
return { firstTextBoxFor(text) };
}
}
}