1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
|
/*
* Copyright (C) 2018 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.
*/
#pragma once
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
#include "FormattingConstraints.h"
#include "LayoutUnits.h"
#include "RenderStyle.h"
namespace WebCore {
namespace Layout {
class InlineItem;
class InlineTextItem;
struct CandidateTextRunForBreaking;
class InlineContentBreaker {
public:
InlineContentBreaker(std::optional<IntrinsicWidthMode>);
struct PartialRun {
size_t length { 0 };
InlineLayoutUnit logicalWidth { 0 };
std::optional<InlineLayoutUnit> hyphenWidth { };
};
enum class IsEndOfLine { No, Yes };
struct Result {
enum class Action {
Keep, // Keep content on the current line.
Break, // Partial content is on the current line.
Wrap, // Content is wrapped to the next line.
WrapWithHyphen, // Content is wrapped to the next line and the current line ends with a visible hyphen.
// The current content overflows and can't get broken up into smaller bits.
RevertToLastWrapOpportunity, // The content needs to be reverted back to the last wrap opportunity.
RevertToLastNonOverflowingWrapOpportunity // The content needs to be reverted back to a wrap opportunity that still fits the line.
};
struct PartialTrailingContent {
size_t trailingRunIndex { 0 };
std::optional<PartialRun> partialRun; // nullopt partial run means the trailing run is a complete run.
};
Action action { Action::Keep };
IsEndOfLine isEndOfLine { IsEndOfLine::No };
std::optional<PartialTrailingContent> partialTrailingContent { };
const InlineItem* lastWrapOpportunityItem { nullptr };
};
// This struct represents the amount of continuous content committed to content breaking at a time (no in-between wrap opportunities).
// e.g.
// <div>text content <span>span1</span>between<span>span2</span></div>
// [text][ ][content][ ][inline box start][span1][inline box end][between][inline box start][span2][inline box end]
// continuous candidate content at a time:
// 1. [text]
// 2. [ ]
// 3. [content]
// 4. [ ]
// 5. [inline box start][span1][inline box end][between][inline box start][span2][inline box end]
// see https://drafts.csswg.org/css-text-3/#line-break-details
struct ContinuousContent {
InlineLayoutUnit logicalWidth() const { return m_logicalWidth; }
std::optional<InlineLayoutUnit> leadingCollapsibleWidth() const { return m_leadingCollapsibleWidth; }
std::optional<InlineLayoutUnit> trailingCollapsibleWidth() const { return m_trailingCollapsibleWidth; }
bool hasCollapsibleContent() const { return trailingCollapsibleWidth() || leadingCollapsibleWidth(); }
bool isFullyCollapsible() const;
bool isHangingContent() const { return m_trailingHangingContentWidth && logicalWidth() == *m_trailingHangingContentWidth; }
void append(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
void append(const InlineTextItem&, const RenderStyle&, InlineLayoutUnit logicalWidth, std::optional<InlineLayoutUnit> collapsibleWidth);
void append(const InlineTextItem&, const RenderStyle&, InlineLayoutUnit hangingWidth);
void reset();
struct Run {
Run(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
Run(const Run&);
Run& operator=(const Run&);
const InlineItem& inlineItem;
const RenderStyle& style;
InlineLayoutUnit logicalWidth { 0 };
};
using RunList = Vector<Run, 3>;
const RunList& runs() const { return m_runs; }
private:
void appendToRunList(const InlineItem&, const RenderStyle&, InlineLayoutUnit logicalWidth);
void resetTrailingWhitespace();
RunList m_runs;
InlineLayoutUnit m_logicalWidth { 0 };
std::optional<InlineLayoutUnit> m_leadingCollapsibleWidth { };
std::optional<InlineLayoutUnit> m_trailingCollapsibleWidth { };
std::optional<InlineLayoutUnit> m_trailingHangingContentWidth { };
};
struct LineStatus {
InlineLayoutUnit contentLogicalRight { 0 };
InlineLayoutUnit availableWidth { 0 };
// Both of these types of trailing content may be ignored when checking for content fit.
InlineLayoutUnit collapsibleOrHangingWidth { 0 };
std::optional<InlineLayoutUnit> trailingSoftHyphenWidth;
bool hasFullyCollapsibleTrailingContent { false };
bool hasContent { false };
bool hasWrapOpportunityAtPreviousPosition { false };
};
Result processInlineContent(const ContinuousContent&, const LineStatus&);
void setHyphenationDisabled() { n_hyphenationIsDisabled = true; }
static bool isWrappingAllowed(const ContinuousContent::Run&);
private:
Result processOverflowingContent(const ContinuousContent&, const LineStatus&) const;
struct OverflowingTextContent {
size_t runIndex { 0 }; // Overflowing run index. There's always an overflowing run.
struct BreakingPosition {
size_t runIndex { 0 };
struct TrailingContent {
// Trailing content is either the run's left side (when we break the run somewhere in the middle) or the previous run.
// Sometimes the breaking position is at the very beginning of the first run, so there's no trailing run at all.
bool overflows { false };
std::optional<InlineContentBreaker::PartialRun> partialRun { };
};
std::optional<TrailingContent> trailingContent { };
};
std::optional<BreakingPosition> breakingPosition { }; // Where we actually break this overflowing content.
};
OverflowingTextContent processOverflowingContentWithText(const ContinuousContent&, const LineStatus&) const;
std::optional<PartialRun> tryBreakingTextRun(const ContinuousContent::RunList& runs, const CandidateTextRunForBreaking&, InlineLayoutUnit availableWidth, const LineStatus&) const;
std::optional<OverflowingTextContent::BreakingPosition> tryBreakingOverflowingRun(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
std::optional<OverflowingTextContent::BreakingPosition> tryBreakingPreviousNonOverflowingRuns(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
std::optional<OverflowingTextContent::BreakingPosition> tryBreakingNextOverflowingRuns(const LineStatus&, const ContinuousContent::RunList&, size_t overflowingRunIndex, InlineLayoutUnit nonOverflowingContentWidth) const;
enum class WordBreakRule {
AtArbitraryPositionWithinWords = 1 << 0,
AtArbitraryPosition = 1 << 1,
AtHyphenationOpportunities = 1 << 2
};
OptionSet<WordBreakRule> wordBreakBehavior(const RenderStyle&, bool hasWrapOpportunityAtPreviousPosition) const;
bool isInIntrinsicWidthMode() const { return !!m_intrinsicWidthMode; }
std::optional<IntrinsicWidthMode> m_intrinsicWidthMode;
bool n_hyphenationIsDisabled { false };
};
inline InlineContentBreaker::ContinuousContent::Run::Run(const InlineItem& inlineItem, const RenderStyle& style, InlineLayoutUnit logicalWidth)
: inlineItem(inlineItem)
, style(style)
, logicalWidth(logicalWidth)
{
}
inline InlineContentBreaker::ContinuousContent::Run::Run(const Run& other)
: inlineItem(other.inlineItem)
, style(other.style)
, logicalWidth(other.logicalWidth)
{
}
inline bool InlineContentBreaker::ContinuousContent::isFullyCollapsible() const
{
auto collapsibleWidth = std::optional<InlineLayoutUnit> { };
if (m_leadingCollapsibleWidth)
collapsibleWidth = *m_leadingCollapsibleWidth;
if (m_trailingCollapsibleWidth)
collapsibleWidth = collapsibleWidth.value_or(0.f) + *m_trailingCollapsibleWidth;
return collapsibleWidth && *collapsibleWidth == logicalWidth();
}
}
}
#endif
|