mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Parse and propagate extended text-indent property values
CSS Text 3 gives `text-indent` a couple of optional keywords to control which lines are affected. This commit parses them, but doesn't yet do anything with them.
This commit is contained in:
parent
eea1d4e1d2
commit
c4b9e7eadf
Notes:
github-actions[bot]
2025-11-20 15:03:53 +00:00
Author: https://github.com/AtkinsSJ
Commit: c4b9e7eadf
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6855
Reviewed-by: https://github.com/gmta ✅
16 changed files with 199 additions and 9 deletions
|
|
@ -273,6 +273,7 @@ set(SOURCES
|
|||
CSS/StyleValues/StyleValue.cpp
|
||||
CSS/StyleValues/StyleValueList.cpp
|
||||
CSS/StyleValues/SuperellipseStyleValue.cpp
|
||||
CSS/StyleValues/TextIndentStyleValue.cpp
|
||||
CSS/StyleValues/TextUnderlinePositionStyleValue.cpp
|
||||
CSS/StyleValues/TransformationStyleValue.cpp
|
||||
CSS/StyleValues/TreeCountingFunctionStyleValue.cpp
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/ShadowStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextIndentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
|
||||
|
|
@ -1369,6 +1370,17 @@ Vector<ShadowData> ComputedProperties::text_shadow(Layout::Node const& layout_no
|
|||
return shadow(PropertyID::TextShadow, layout_node);
|
||||
}
|
||||
|
||||
TextIndentData ComputedProperties::text_indent() const
|
||||
{
|
||||
auto const& value = property(PropertyID::TextIndent).as_text_indent();
|
||||
|
||||
return TextIndentData {
|
||||
.length_percentage = LengthPercentage::from_style_value(value.length_percentage()),
|
||||
.each_line = value.each_line(),
|
||||
.hanging = value.hanging(),
|
||||
};
|
||||
}
|
||||
|
||||
TextWrapMode ComputedProperties::text_wrap_mode() const
|
||||
{
|
||||
auto const& value = property(PropertyID::TextWrapMode);
|
||||
|
|
|
|||
|
|
@ -122,6 +122,7 @@ public:
|
|||
TextDecorationThickness text_decoration_thickness() const;
|
||||
TextTransform text_transform() const;
|
||||
Vector<ShadowData> text_shadow(Layout::Node const&) const;
|
||||
TextIndentData text_indent() const;
|
||||
TextWrapMode text_wrap_mode() const;
|
||||
ListStyleType list_style_type() const;
|
||||
ListStylePosition list_style_position() const;
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ struct ScrollbarColorData {
|
|||
Color track_color { Color::Transparent };
|
||||
};
|
||||
|
||||
struct TextIndentData {
|
||||
LengthPercentage length_percentage;
|
||||
bool each_line { false };
|
||||
bool hanging { false };
|
||||
};
|
||||
|
||||
struct TextUnderlinePosition {
|
||||
TextUnderlinePositionHorizontal horizontal { TextUnderlinePositionHorizontal::Auto };
|
||||
TextUnderlinePositionVertical vertical { TextUnderlinePositionVertical::Auto };
|
||||
|
|
@ -159,7 +165,7 @@ public:
|
|||
static TextDecorationStyle text_decoration_style() { return TextDecorationStyle::Solid; }
|
||||
static TextTransform text_transform() { return TextTransform::None; }
|
||||
static TextOverflow text_overflow() { return TextOverflow::Clip; }
|
||||
static LengthPercentage text_indent() { return Length::make_px(0); }
|
||||
static TextIndentData text_indent() { return { Length::make_px(0) }; }
|
||||
static TextWrapMode text_wrap_mode() { return TextWrapMode::Wrap; }
|
||||
static CSSPixels text_underline_offset() { return 2; }
|
||||
static TextUnderlinePosition text_underline_position() { return { .horizontal = TextUnderlinePositionHorizontal::Auto, .vertical = TextUnderlinePositionVertical::Auto }; }
|
||||
|
|
@ -497,7 +503,7 @@ public:
|
|||
Variant<Length, double> tab_size() const { return m_inherited.tab_size; }
|
||||
TextAlign text_align() const { return m_inherited.text_align; }
|
||||
TextJustify text_justify() const { return m_inherited.text_justify; }
|
||||
LengthPercentage const& text_indent() const { return m_inherited.text_indent; }
|
||||
TextIndentData const& text_indent() const { return m_inherited.text_indent; }
|
||||
TextWrapMode text_wrap_mode() const { return m_inherited.text_wrap_mode; }
|
||||
CSSPixels text_underline_offset() const { return m_inherited.text_underline_offset; }
|
||||
TextUnderlinePosition text_underline_position() const { return m_inherited.text_underline_position; }
|
||||
|
|
@ -702,7 +708,7 @@ protected:
|
|||
TextAlign text_align { InitialValues::text_align() };
|
||||
TextJustify text_justify { InitialValues::text_justify() };
|
||||
TextTransform text_transform { InitialValues::text_transform() };
|
||||
LengthPercentage text_indent { InitialValues::text_indent() };
|
||||
TextIndentData text_indent { InitialValues::text_indent() };
|
||||
TextWrapMode text_wrap_mode { InitialValues::text_wrap_mode() };
|
||||
CSSPixels text_underline_offset { InitialValues::text_underline_offset() };
|
||||
TextUnderlinePosition text_underline_position { InitialValues::text_underline_position() };
|
||||
|
|
@ -915,7 +921,7 @@ public:
|
|||
void set_text_decoration_color(Color value) { m_noninherited.text_decoration_color = value; }
|
||||
void set_text_transform(TextTransform value) { m_inherited.text_transform = value; }
|
||||
void set_text_shadow(Vector<ShadowData>&& value) { m_inherited.text_shadow = move(value); }
|
||||
void set_text_indent(LengthPercentage value) { m_inherited.text_indent = move(value); }
|
||||
void set_text_indent(TextIndentData value) { m_inherited.text_indent = move(value); }
|
||||
void set_text_wrap_mode(TextWrapMode value) { m_inherited.text_wrap_mode = value; }
|
||||
void set_text_overflow(TextOverflow value) { m_noninherited.text_overflow = value; }
|
||||
void set_text_underline_offset(CSSPixels value) { m_inherited.text_underline_offset = value; }
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/RectStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/StyleValues/SuperellipseStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextIndentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
|
||||
#include <LibWeb/CSS/Transformation.h>
|
||||
|
|
@ -1877,6 +1878,22 @@ static RefPtr<StyleValue const> interpolate_value_impl(DOM::Element& element, Ca
|
|||
interpolate_length_or_auto(from_rect.left_edge, to_rect.left_edge, calculation_context, delta),
|
||||
});
|
||||
}
|
||||
case StyleValue::Type::TextIndent: {
|
||||
auto& from_text_indent = from.as_text_indent();
|
||||
auto& to_text_indent = to.as_text_indent();
|
||||
|
||||
if (from_text_indent.each_line() != to_text_indent.each_line()
|
||||
|| from_text_indent.hanging() != to_text_indent.hanging())
|
||||
return {};
|
||||
|
||||
auto interpolated_length_percentage = interpolate_value(element, calculation_context, from_text_indent.length_percentage(), to_text_indent.length_percentage(), delta, allow_discrete);
|
||||
if (!interpolated_length_percentage)
|
||||
return {};
|
||||
|
||||
return TextIndentStyleValue::create(interpolated_length_percentage.release_nonnull(),
|
||||
from_text_indent.hanging() ? TextIndentStyleValue::Hanging::Yes : TextIndentStyleValue::Hanging::No,
|
||||
from_text_indent.each_line() ? TextIndentStyleValue::EachLine::Yes : TextIndentStyleValue::EachLine::No);
|
||||
}
|
||||
case StyleValue::Type::Superellipse: {
|
||||
// https://drafts.csswg.org/css-borders-4/#corner-shape-interpolation
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@
|
|||
"down",
|
||||
"e",
|
||||
"e-resize",
|
||||
"each-line",
|
||||
"ease",
|
||||
"ease-in",
|
||||
"ease-in-out",
|
||||
|
|
@ -237,6 +238,7 @@
|
|||
"graytext",
|
||||
"grid",
|
||||
"groove",
|
||||
"hanging",
|
||||
"hard-light",
|
||||
"height",
|
||||
"help",
|
||||
|
|
|
|||
|
|
@ -506,6 +506,7 @@ private:
|
|||
RefPtr<StyleValue const> parse_shape_outside_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_text_decoration_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_text_decoration_line_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_text_indent_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_text_underline_position_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_rotate_value(TokenStream<ComponentValue>&);
|
||||
RefPtr<StyleValue const> parse_stroke_dasharray_value(TokenStream<ComponentValue>&);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/StringStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextIndentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
|
||||
|
|
@ -721,6 +722,8 @@ Parser::ParseErrorOr<NonnullRefPtr<StyleValue const>> Parser::parse_css_value(Pr
|
|||
return parse_all_as(tokens, [this](auto& tokens) { return parse_text_decoration_value(tokens); });
|
||||
case PropertyID::TextDecorationLine:
|
||||
return parse_all_as(tokens, [this](auto& tokens) { return parse_text_decoration_line_value(tokens); });
|
||||
case PropertyID::TextIndent:
|
||||
return parse_all_as(tokens, [this](auto& tokens) { return parse_text_indent_value(tokens); });
|
||||
case PropertyID::TextShadow:
|
||||
return parse_all_as(tokens, [this](auto& tokens) { return parse_shadow_value(tokens, ShadowStyleValue::ShadowType::Text); });
|
||||
case PropertyID::TextUnderlinePosition:
|
||||
|
|
@ -4731,6 +4734,51 @@ RefPtr<StyleValue const> Parser::parse_text_decoration_line_value(TokenStream<Co
|
|||
return StyleValueList::create(move(style_values), StyleValueList::Separator::Space);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-text-3/#text-indent-property
|
||||
RefPtr<StyleValue const> Parser::parse_text_indent_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
// [ <length-percentage> ] && hanging? && each-line?
|
||||
auto transaction = tokens.begin_transaction();
|
||||
|
||||
RefPtr<StyleValue const> length_percentage;
|
||||
bool has_hanging = false;
|
||||
bool has_each_line = false;
|
||||
|
||||
tokens.discard_whitespace();
|
||||
|
||||
while (tokens.has_next_token()) {
|
||||
if (!length_percentage) {
|
||||
if (auto parsed = parse_length_percentage_value(tokens)) {
|
||||
length_percentage = parsed.release_nonnull();
|
||||
tokens.discard_whitespace();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (auto keyword = parse_keyword_value(tokens)) {
|
||||
if (!has_hanging && keyword->to_keyword() == Keyword::Hanging) {
|
||||
has_hanging = true;
|
||||
continue;
|
||||
}
|
||||
if (!has_each_line && keyword->to_keyword() == Keyword::EachLine) {
|
||||
has_each_line = true;
|
||||
continue;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!length_percentage)
|
||||
return nullptr;
|
||||
|
||||
transaction.commit();
|
||||
return TextIndentStyleValue::create(length_percentage.release_nonnull(),
|
||||
has_hanging ? TextIndentStyleValue::Hanging::Yes : TextIndentStyleValue::Hanging::No,
|
||||
has_each_line ? TextIndentStyleValue::EachLine::Yes : TextIndentStyleValue::EachLine::No);
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-text-decor-4/#text-underline-position-property
|
||||
RefPtr<StyleValue const> Parser::parse_text_underline_position_value(TokenStream<ComponentValue>& tokens)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3680,6 +3680,10 @@
|
|||
"length [-∞,∞]",
|
||||
"percentage [-∞,∞]"
|
||||
],
|
||||
"valid-identifiers": [
|
||||
"each-line",
|
||||
"hanging"
|
||||
],
|
||||
"percentages-resolve-to": "length",
|
||||
"quirks": [
|
||||
"unitless-length"
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/Font/FontStyleMapping.h>
|
||||
#include <LibGfx/Font/FontWeight.h>
|
||||
#include <LibWeb/CSS/CSSStyleValue.h>
|
||||
#include <LibWeb/CSS/ComputedProperties.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
|
|
@ -68,6 +67,7 @@
|
|||
#include <LibWeb/CSS/StyleValues/StyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/StyleValueList.h>
|
||||
#include <LibWeb/CSS/StyleValues/SuperellipseStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextIndentStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TextUnderlinePositionStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TimeStyleValue.h>
|
||||
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ namespace Web::CSS {
|
|||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Shorthand, shorthand, ShorthandStyleValue) \
|
||||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(String, string, StringStyleValue) \
|
||||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Superellipse, superellipse, SuperellipseStyleValue) \
|
||||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(TextIndent, text_indent, TextIndentStyleValue) \
|
||||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(TextUnderlinePosition, text_underline_position, TextUnderlinePositionStyleValue) \
|
||||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Time, time, TimeStyleValue) \
|
||||
__ENUMERATE_CSS_STYLE_VALUE_TYPE(Transformation, transformation, TransformationStyleValue) \
|
||||
|
|
|
|||
54
Libraries/LibWeb/CSS/StyleValues/TextIndentStyleValue.cpp
Normal file
54
Libraries/LibWeb/CSS/StyleValues/TextIndentStyleValue.cpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "TextIndentStyleValue.h"
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
ValueComparingNonnullRefPtr<TextIndentStyleValue const> TextIndentStyleValue::create(NonnullRefPtr<StyleValue const> length_percentage, Hanging hanging, EachLine each_line)
|
||||
{
|
||||
return adopt_ref(*new (nothrow) TextIndentStyleValue(move(length_percentage), hanging, each_line));
|
||||
}
|
||||
|
||||
TextIndentStyleValue::TextIndentStyleValue(NonnullRefPtr<StyleValue const> length_percentage, Hanging hanging, EachLine each_line)
|
||||
: StyleValueWithDefaultOperators(Type::TextIndent)
|
||||
, m_length_percentage(move(length_percentage))
|
||||
, m_hanging(hanging == Hanging::Yes)
|
||||
, m_each_line(each_line == EachLine::Yes)
|
||||
{
|
||||
}
|
||||
|
||||
TextIndentStyleValue::~TextIndentStyleValue() = default;
|
||||
|
||||
String TextIndentStyleValue::to_string(SerializationMode mode) const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append(m_length_percentage->to_string(mode));
|
||||
if (m_each_line)
|
||||
builder.append(" each-line"sv);
|
||||
if (m_hanging)
|
||||
builder.append(" hanging"sv);
|
||||
return builder.to_string_without_validation();
|
||||
}
|
||||
|
||||
ValueComparingNonnullRefPtr<StyleValue const> TextIndentStyleValue::absolutized(ComputationContext const& context) const
|
||||
{
|
||||
auto new_length_percentage = m_length_percentage->absolutized(context);
|
||||
if (new_length_percentage->equals(m_length_percentage))
|
||||
return *this;
|
||||
return create(move(new_length_percentage),
|
||||
m_hanging ? Hanging::Yes : Hanging::No,
|
||||
m_each_line ? EachLine::Yes : EachLine::No);
|
||||
}
|
||||
|
||||
bool TextIndentStyleValue::properties_equal(TextIndentStyleValue const& other) const
|
||||
{
|
||||
return m_length_percentage == other.m_length_percentage
|
||||
&& m_each_line == other.m_each_line
|
||||
&& m_hanging == other.m_hanging;
|
||||
}
|
||||
|
||||
}
|
||||
43
Libraries/LibWeb/CSS/StyleValues/TextIndentStyleValue.h
Normal file
43
Libraries/LibWeb/CSS/StyleValues/TextIndentStyleValue.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/StyleValues/StyleValue.h>
|
||||
|
||||
namespace Web::CSS {
|
||||
|
||||
class TextIndentStyleValue : public StyleValueWithDefaultOperators<TextIndentStyleValue> {
|
||||
public:
|
||||
enum class Hanging : u8 {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
enum class EachLine : u8 {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
static ValueComparingNonnullRefPtr<TextIndentStyleValue const> create(NonnullRefPtr<StyleValue const> length_percentage, Hanging hanging, EachLine each_line);
|
||||
virtual ~TextIndentStyleValue() override;
|
||||
|
||||
StyleValue const& length_percentage() const { return m_length_percentage; }
|
||||
bool hanging() const { return m_hanging; }
|
||||
bool each_line() const { return m_each_line; }
|
||||
|
||||
virtual String to_string(SerializationMode) const override;
|
||||
virtual ValueComparingNonnullRefPtr<StyleValue const> absolutized(ComputationContext const&) const override;
|
||||
bool properties_equal(TextIndentStyleValue const&) const;
|
||||
|
||||
private:
|
||||
TextIndentStyleValue(NonnullRefPtr<StyleValue const> length_percentage, Hanging hanging, EachLine each_line);
|
||||
|
||||
NonnullRefPtr<StyleValue const> m_length_percentage;
|
||||
bool m_hanging;
|
||||
bool m_each_line;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -378,6 +378,7 @@ class StyleValueList;
|
|||
class SuperellipseStyleValue;
|
||||
class Supports;
|
||||
class SVGPaint;
|
||||
class TextIndentStyleValue;
|
||||
class TextUnderlinePositionStyleValue;
|
||||
class Time;
|
||||
class TimeOrCalculated;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ LineBuilder::LineBuilder(InlineFormattingContext& context, LayoutState& layout_s
|
|||
, m_direction(direction)
|
||||
, m_writing_mode(writing_mode)
|
||||
{
|
||||
m_text_indent = m_context.containing_block().computed_values().text_indent().to_px(m_context.containing_block(), m_containing_block_used_values.content_width());
|
||||
auto text_indent = m_context.containing_block().computed_values().text_indent();
|
||||
m_text_indent = text_indent.length_percentage.to_px(m_context.containing_block(), m_containing_block_used_values.content_width());
|
||||
begin_new_line(false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -622,9 +622,7 @@ void NodeWithStyle::apply_style(CSS::ComputedProperties const& computed_style)
|
|||
computed_values.set_text_underline_offset(computed_style.text_underline_offset());
|
||||
computed_values.set_text_underline_position(computed_style.text_underline_position());
|
||||
|
||||
if (auto text_indent = computed_style.length_percentage(CSS::PropertyID::TextIndent, *this, CSS::ComputedProperties::ClampNegativeLengths::No); text_indent.has_value())
|
||||
computed_values.set_text_indent(text_indent.release_value());
|
||||
|
||||
computed_values.set_text_indent(computed_style.text_indent());
|
||||
computed_values.set_text_wrap_mode(computed_style.text_wrap_mode());
|
||||
computed_values.set_tab_size(computed_style.tab_size());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue