srctree

Robin Linden parent c2d1cabc e03d3613
style: Wrap UnresolvedValue args that won't change much in a struct

Things like the root font size or the (not-yet-handled) view width are constant within a page layout, so let's wrap them up in a struct to both make extending this easier and to make the API harder to misuse.
layout/layout.cpp added: 69, removed: 60, total 9
@@ -41,12 +41,13 @@ namespace {
 
class Layouter {
public:
Layouter(int root_font_size, type::IType const &type) : root_font_size_{root_font_size}, type_{type} {}
Layouter(int root_font_size, type::IType const &type)
: resolution_context_{.root_font_size = root_font_size}, type_{type} {}
 
void layout(LayoutBox &, geom::Rect const &bounds) const;
 
private:
int root_font_size_;
style::ResolutionInfo resolution_context_;
type::IType const &type_;
 
void layout_inline(LayoutBox &, geom::Rect const &bounds) const;
@@ -434,10 +435,10 @@ void Layouter::calculate_left_and_right_margin(LayoutBox &box,
int margin_px = (parent.width - box.dimensions.border_box().width) / 2;
box.dimensions.margin.left = box.dimensions.margin.right = margin_px;
} else if (margin_left.is_auto() && !margin_right.is_auto()) {
box.dimensions.margin.right = margin_right.resolve(font_size, root_font_size_);
box.dimensions.margin.right = margin_right.resolve(font_size, resolution_context_);
box.dimensions.margin.left = parent.width - box.dimensions.margin_box().width;
} else if (!margin_left.is_auto() && margin_right.is_auto()) {
box.dimensions.margin.left = margin_left.resolve(font_size, root_font_size_);
box.dimensions.margin.left = margin_left.resolve(font_size, resolution_context_);
box.dimensions.margin.right = parent.width - box.dimensions.margin_box().width;
} else {
// TODO(mkiael): Compute margin depending on direction property
@@ -450,13 +451,13 @@ void Layouter::calculate_width_and_margin(LayoutBox &box, geom::Rect const &pare
 
auto &margins = box.dimensions.margin;
if (auto margin_top = box.get_property<css::PropertyId::MarginTop>(); !margin_top.is_auto()) {
margins.top = margin_top.resolve(font_size, root_font_size_);
margins.top = margin_top.resolve(font_size, resolution_context_);
} else {
margins.top = 0;
}
 
if (auto margin_bottom = box.get_property<css::PropertyId::MarginBottom>(); !margin_bottom.is_auto()) {
margins.bottom = margin_bottom.resolve(font_size, root_font_size_);
margins.bottom = margin_bottom.resolve(font_size, resolution_context_);
} else {
margins.bottom = 0;
}
@@ -466,7 +467,7 @@ void Layouter::calculate_width_and_margin(LayoutBox &box, geom::Rect const &pare
auto width = box.get_property<css::PropertyId::Width>();
std::optional<int> resolved_width;
if (!width.is_auto()) {
resolved_width = width.try_resolve(font_size, root_font_size_, parent.width);
resolved_width = width.try_resolve(font_size, resolution_context_, parent.width);
}
 
if (resolved_width) {
@@ -474,16 +475,16 @@ void Layouter::calculate_width_and_margin(LayoutBox &box, geom::Rect const &pare
calculate_left_and_right_margin(box, parent, margin_left, margin_right, font_size);
} else {
if (!margin_left.is_auto()) {
margins.left = margin_left.resolve(font_size, root_font_size_);
margins.left = margin_left.resolve(font_size, resolution_context_);
}
if (!margin_right.is_auto()) {
margins.right = margin_right.resolve(font_size, root_font_size_);
margins.right = margin_right.resolve(font_size, resolution_context_);
}
box.dimensions.content.width = parent.width - box.dimensions.margin_box().width;
}
 
if (auto min = box.get_property<css::PropertyId::MinWidth>(); !min.is_auto()) {
auto resolved = min.resolve(font_size, root_font_size_, parent.width);
auto resolved = min.resolve(font_size, resolution_context_, parent.width);
if (box.dimensions.content.width < resolved) {
box.dimensions.content.width = resolved;
calculate_left_and_right_margin(box, parent, margin_left, margin_right, font_size);
@@ -493,7 +494,7 @@ void Layouter::calculate_width_and_margin(LayoutBox &box, geom::Rect const &pare
auto max = box.get_property<css::PropertyId::MaxWidth>();
std::optional<int> resolved_max;
if (!max.is_none()) {
resolved_max = max.try_resolve(font_size, root_font_size_, parent.width);
resolved_max = max.try_resolve(font_size, resolution_context_, parent.width);
}
 
if (resolved_max) {
@@ -514,45 +515,45 @@ void Layouter::calculate_height(LayoutBox &box, int const font_size) const {
}
 
if (auto height = box.get_property<css::PropertyId::Height>(); !height.is_auto()) {
content.height = height.resolve(font_size, root_font_size_);
content.height = height.resolve(font_size, resolution_context_);
}
 
if (auto min = box.get_property<css::PropertyId::MinHeight>(); !min.is_auto()) {
content.height = std::max(content.height, min.resolve(font_size, root_font_size_));
content.height = std::max(content.height, min.resolve(font_size, resolution_context_));
}
 
if (auto max = box.get_property<css::PropertyId::MaxHeight>(); !max.is_none()) {
content.height = std::min(content.height, max.resolve(font_size, root_font_size_));
content.height = std::min(content.height, max.resolve(font_size, resolution_context_));
}
}
 
void Layouter::calculate_padding(LayoutBox &box, int const font_size) const {
auto &padding = box.dimensions.padding;
padding.left = box.get_property<css::PropertyId::PaddingLeft>().resolve(font_size, root_font_size_);
padding.right = box.get_property<css::PropertyId::PaddingRight>().resolve(font_size, root_font_size_);
padding.top = box.get_property<css::PropertyId::PaddingTop>().resolve(font_size, root_font_size_);
padding.bottom = box.get_property<css::PropertyId::PaddingBottom>().resolve(font_size, root_font_size_);
padding.left = box.get_property<css::PropertyId::PaddingLeft>().resolve(font_size, resolution_context_);
padding.right = box.get_property<css::PropertyId::PaddingRight>().resolve(font_size, resolution_context_);
padding.top = box.get_property<css::PropertyId::PaddingTop>().resolve(font_size, resolution_context_);
padding.bottom = box.get_property<css::PropertyId::PaddingBottom>().resolve(font_size, resolution_context_);
}
 
void Layouter::calculate_border(LayoutBox &box, int const font_size) const {
if (box.get_property<css::PropertyId::BorderLeftStyle>() != style::BorderStyle::None) {
auto border_width = box.get_property<css::PropertyId::BorderLeftWidth>();
box.dimensions.border.left = border_width.resolve(font_size, root_font_size_);
box.dimensions.border.left = border_width.resolve(font_size, resolution_context_);
}
 
if (box.get_property<css::PropertyId::BorderRightStyle>() != style::BorderStyle::None) {
auto border_width = box.get_property<css::PropertyId::BorderRightWidth>();
box.dimensions.border.right = border_width.resolve(font_size, root_font_size_);
box.dimensions.border.right = border_width.resolve(font_size, resolution_context_);
}
 
if (box.get_property<css::PropertyId::BorderTopStyle>() != style::BorderStyle::None) {
auto border_width = box.get_property<css::PropertyId::BorderTopWidth>();
box.dimensions.border.top = border_width.resolve(font_size, root_font_size_);
box.dimensions.border.top = border_width.resolve(font_size, resolution_context_);
}
 
if (box.get_property<css::PropertyId::BorderBottomStyle>() != style::BorderStyle::None) {
auto border_width = box.get_property<css::PropertyId::BorderBottomWidth>();
box.dimensions.border.bottom = border_width.resolve(font_size, root_font_size_);
box.dimensions.border.bottom = border_width.resolve(font_size, resolution_context_);
}
}
 
 
style/styled_node.cpp added: 69, removed: 60, total 9
@@ -206,7 +206,8 @@ constexpr auto kBorderWidthKeywords = std::to_array<std::pair<std::string_view,
 
} // namespace
 
int UnresolvedBorderWidth::resolve(int font_size, int root_font_size, std::optional<int> percent_relative_to) const {
int UnresolvedBorderWidth::resolve(
int font_size, ResolutionInfo context, std::optional<int> percent_relative_to) const {
// NOLINTNEXTLINE(readability-qualified-auto): Not guaranteed to be a ptr.
if (auto it = std::ranges::find(
kBorderWidthKeywords, width.raw, &decltype(kBorderWidthKeywords)::value_type::first);
@@ -214,7 +215,7 @@ int UnresolvedBorderWidth::resolve(int font_size, int root_font_size, std::optio
return it->second;
}
 
return width.resolve(font_size, root_font_size, percent_relative_to);
return width.resolve(font_size, context, percent_relative_to);
}
 
// NOLINTNEXTLINE(misc-no-recursion)
@@ -736,7 +737,10 @@ std::pair<int, int> StyledNode::get_border_radius_property(css::PropertyId id) c
 
int font_size = get_property<css::PropertyId::FontSize>();
int root_font_size = get_root_font_size(*this);
return {horizontal_prop.resolve(font_size, root_font_size), vertical_prop.resolve(font_size, root_font_size)};
return {
horizontal_prop.resolve(font_size, {.root_font_size = root_font_size}),
vertical_prop.resolve(font_size, {.root_font_size = root_font_size}),
};
}
 
} // namespace style
 
style/styled_node.h added: 69, removed: 60, total 9
@@ -108,7 +108,7 @@ enum class WhiteSpace : std::uint8_t {
struct UnresolvedBorderWidth {
UnresolvedValue width{};
 
int resolve(int font_size, int root_font_size, std::optional<int> percent_relative_to = std::nullopt) const;
int resolve(int font_size, ResolutionInfo, std::optional<int> percent_relative_to = std::nullopt) const;
};
 
// NOLINTNEXTLINE(misc-no-recursion)
 
style/unresolved_value.cpp added: 69, removed: 60, total 9
@@ -17,14 +17,14 @@
namespace style {
 
int UnresolvedValue::resolve(int font_size,
int root_font_size,
ResolutionInfo context,
std::optional<int> percent_relative_to,
std::source_location const &caller) const {
return try_resolve(font_size, root_font_size, percent_relative_to, caller).value_or(0);
return try_resolve(font_size, context, percent_relative_to, caller).value_or(0);
}
 
std::optional<int> UnresolvedValue::try_resolve(int font_size,
int root_font_size,
ResolutionInfo context,
std::optional<int> percent_relative_to,
std::source_location const &caller) const {
// Special case for 0 since it won't ever have a unit that needs to be handled.
@@ -68,7 +68,7 @@ std::optional<int> UnresolvedValue::try_resolve(int font_size,
}
 
if (unit == "rem") {
res *= static_cast<float>(root_font_size);
res *= static_cast<float>(context.root_font_size);
return static_cast<int>(res);
}
 
 
style/unresolved_value.h added: 69, removed: 60, total 9
@@ -11,6 +11,10 @@
 
namespace style {
 
struct ResolutionInfo {
int root_font_size{};
};
 
struct UnresolvedValue {
std::string_view raw{};
[[nodiscard]] bool operator==(UnresolvedValue const &) const = default;
@@ -18,11 +22,11 @@ struct UnresolvedValue {
constexpr bool is_auto() const { return raw == "auto"; }
constexpr bool is_none() const { return raw == "none"; }
int resolve(int font_size,
int root_font_size,
ResolutionInfo,
std::optional<int> percent_relative_to = std::nullopt,
std::source_location const &caller = std::source_location::current()) const;
std::optional<int> try_resolve(int font_size,
int root_font_size,
ResolutionInfo,
std::optional<int> percent_relative_to = std::nullopt,
std::source_location const &caller = std::source_location::current()) const;
};
 
style/unresolved_value_test.cpp added: 69, removed: 60, total 9
@@ -16,64 +16,64 @@ int main() {
s.add_test("unit/px", [](etest::IActions &a) {
// Just a raw numeric value.
auto const uv = UnresolvedValue{.raw = "37px"};
a.expect_eq(uv.resolve(100, 100), 37);
a.expect_eq(uv.resolve(123, 456), 37);
a.expect_eq(uv.resolve(0, 0), 37);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}), 37);
a.expect_eq(uv.resolve(123, {.root_font_size = 456}), 37);
a.expect_eq(uv.resolve(0, {.root_font_size = 0}), 37);
});
 
s.add_test("unit/em", [](etest::IActions &a) {
// Based on the first argument, the current element's font-size.
auto const uv = UnresolvedValue{.raw = "2em"};
a.expect_eq(uv.resolve(100, 100), 200);
a.expect_eq(uv.resolve(123, 456), 246);
a.expect_eq(uv.resolve(0, 0), 0);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}), 200);
a.expect_eq(uv.resolve(123, {.root_font_size = 456}), 246);
a.expect_eq(uv.resolve(0, {.root_font_size = 0}), 0);
});
 
s.add_test("unit/ex", [](etest::IActions &a) {
// Based on the first argument, the current element's font-size.
auto const uv = UnresolvedValue{.raw = "1ex"};
a.expect_eq(uv.resolve(100, 100), 50);
a.expect_eq(uv.resolve(123, 456), 61);
a.expect_eq(uv.resolve(0, 0), 0);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}), 50);
a.expect_eq(uv.resolve(123, {.root_font_size = 456}), 61);
a.expect_eq(uv.resolve(0, {.root_font_size = 0}), 0);
});
 
s.add_test("unit/ch", [](etest::IActions &a) {
// Based on the first argument, the current element's font-size.
auto const uv = UnresolvedValue{.raw = "1ch"};
a.expect_eq(uv.resolve(100, 100), 50);
a.expect_eq(uv.resolve(123, 456), 61);
a.expect_eq(uv.resolve(0, 0), 0);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}), 50);
a.expect_eq(uv.resolve(123, {.root_font_size = 456}), 61);
a.expect_eq(uv.resolve(0, {.root_font_size = 0}), 0);
});
 
s.add_test("unit/rem", [](etest::IActions &a) {
// Based on the second argument, the root element's font-size.
auto const uv = UnresolvedValue{.raw = "2rem"};
a.expect_eq(uv.resolve(100, 100), 200);
a.expect_eq(uv.resolve(123, 456), 912);
a.expect_eq(uv.resolve(0, 0), 0);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}), 200);
a.expect_eq(uv.resolve(123, {.root_font_size = 456}), 912);
a.expect_eq(uv.resolve(0, {.root_font_size = 0}), 0);
});
 
s.add_test("unit/%", [](etest::IActions &a) {
// Based on the third argument, whatever the spec wants the property
// this came from to be resolved against.
auto const uv = UnresolvedValue{.raw = "50%"};
a.expect_eq(uv.resolve(100, 100, 100), 50);
a.expect_eq(uv.resolve(100, 100, 200), 100);
a.expect_eq(uv.resolve(0, 0, 1000), 500);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}, 100), 50);
a.expect_eq(uv.resolve(100, {.root_font_size = 100}, 200), 100);
a.expect_eq(uv.resolve(0, {.root_font_size = 0}, 1000), 500);
 
// If the third argument is not provided, you get nothing.
a.expect_eq(uv.resolve(123, 456), 0);
a.expect_eq(uv.resolve(123, {.root_font_size = 456}), 0);
});
 
s.add_test("try_resolve", [](etest::IActions &a) {
// %, no parent provided.
auto const percent = UnresolvedValue{.raw = "50%"};
a.expect_eq(percent.try_resolve(100, 100), std::nullopt);
a.expect_eq(percent.try_resolve(100, 100, 100), 50);
a.expect_eq(percent.try_resolve(100, {.root_font_size = 100}), std::nullopt);
a.expect_eq(percent.try_resolve(100, {.root_font_size = 100}, 100), 50);
 
// Nonsense.
auto const nonsense = UnresolvedValue{.raw = "foo"};
a.expect_eq(nonsense.try_resolve(100, 100, 100), std::nullopt);
a.expect_eq(nonsense.try_resolve(100, {.root_font_size = 100}, 100), std::nullopt);
});
 
return s.run();