2020-01-18 09:38:21 +01:00
|
|
|
|
/*
|
2024-10-04 13:19:50 +02:00
|
|
|
|
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
|
2021-08-09 21:28:56 +02:00
|
|
|
|
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
|
2020-01-18 09:38:21 +01:00
|
|
|
|
*
|
2021-04-22 01:24:48 -07:00
|
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 09:38:21 +01:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-02-09 21:44:19 +13:00
|
|
|
|
#include <LibWeb/DOM/Document.h>
|
2026-02-08 16:42:02 +01:00
|
|
|
|
#include <LibWeb/DOM/Element.h>
|
2020-11-22 15:53:01 +01:00
|
|
|
|
#include <LibWeb/Layout/ListItemMarkerBox.h>
|
2022-03-10 14:02:25 +01:00
|
|
|
|
#include <LibWeb/Painting/MarkerPaintable.h>
|
2019-10-11 23:16:53 +02:00
|
|
|
|
|
2020-11-22 15:53:01 +01:00
|
|
|
|
namespace Web::Layout {
|
2020-03-07 10:27:02 +01:00
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
|
GC_DEFINE_ALLOCATOR(ListItemMarkerBox);
|
2024-04-06 10:16:04 -07:00
|
|
|
|
|
2025-01-27 18:50:27 +01:00
|
|
|
|
ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document, CSS::ListStyleType style_type, CSS::ListStylePosition style_position, GC::Ref<DOM::Element> list_item_element, GC::Ref<CSS::ComputedProperties> style)
|
2021-09-23 19:47:53 +02:00
|
|
|
|
: Box(document, nullptr, move(style))
|
2021-04-17 23:10:10 +02:00
|
|
|
|
, m_list_style_type(style_type)
|
2023-06-02 23:05:15 +02:00
|
|
|
|
, m_list_style_position(style_position)
|
2025-01-27 18:50:27 +01:00
|
|
|
|
, m_list_item_element(list_item_element)
|
2019-10-11 23:16:53 +02:00
|
|
|
|
{
|
2025-01-27 18:50:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ListItemMarkerBox::~ListItemMarkerBox() = default;
|
|
|
|
|
|
|
2026-02-27 13:11:38 +13:00
|
|
|
|
bool ListItemMarkerBox::counter_style_is_rendered_with_custom_image(RefPtr<CSS::CounterStyle const> const& counter_style)
|
2026-02-09 21:44:19 +13:00
|
|
|
|
{
|
|
|
|
|
|
// https://drafts.csswg.org/css-counter-styles-3/#simple-symbolic
|
|
|
|
|
|
// When used in list-style-type, a UA may instead render these styles using a UA-generated image or a UA-chosen font
|
|
|
|
|
|
// instead of rendering the specified character in the element’s own font. If using an image, it must look similar
|
|
|
|
|
|
// to the character, and must be sized to attractively fill a 1em by 1em square.
|
|
|
|
|
|
|
2026-02-27 13:11:38 +13:00
|
|
|
|
if (!counter_style)
|
2026-02-09 21:44:19 +13:00
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
auto const& counter_style_name = counter_style->name();
|
|
|
|
|
|
|
|
|
|
|
|
return first_is_one_of(counter_style_name, "square"_fly_string, "circle"_fly_string, "disc"_fly_string, "disclosure-closed"_fly_string, "disclosure-open"_fly_string);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-27 18:50:27 +01:00
|
|
|
|
Optional<String> ListItemMarkerBox::text() const
|
|
|
|
|
|
{
|
2026-02-09 21:44:19 +13:00
|
|
|
|
// https://drafts.csswg.org/css-lists-3/#text-markers
|
2025-01-27 18:50:27 +01:00
|
|
|
|
auto index = m_list_item_element->ordinal_value();
|
|
|
|
|
|
|
|
|
|
|
|
return m_list_style_type.visit(
|
2026-01-29 23:50:16 +13:00
|
|
|
|
[](Empty const&) -> Optional<String> {
|
2026-02-09 21:44:19 +13:00
|
|
|
|
// none
|
|
|
|
|
|
// The element has no marker string.
|
2026-01-29 23:50:16 +13:00
|
|
|
|
return {};
|
|
|
|
|
|
},
|
2026-02-27 13:11:38 +13:00
|
|
|
|
[&](RefPtr<CSS::CounterStyle const> const& counter_style) -> Optional<String> {
|
2026-02-09 21:44:19 +13:00
|
|
|
|
// <counter-style>
|
|
|
|
|
|
// Specifies the element’s marker string as the value of the list-item counter represented using the
|
|
|
|
|
|
// specified <counter-style>. Specifically, the marker string is the result of generating a counter
|
|
|
|
|
|
// representation of the list-item counter value using the specified <counter-style>, prefixed by the prefix
|
|
|
|
|
|
// of the <counter-style>, and followed by the suffix of the <counter-style>. If the specified
|
|
|
|
|
|
// <counter-style> does not exist, decimal is assumed.
|
|
|
|
|
|
if (counter_style_is_rendered_with_custom_image(counter_style))
|
2025-01-27 18:50:27 +01:00
|
|
|
|
return {};
|
2026-02-09 21:44:19 +13:00
|
|
|
|
|
|
|
|
|
|
// NB: Fallback to decimal if the counter style does not exist is handled within generate_a_counter_representation()
|
2026-04-06 11:23:33 +12:00
|
|
|
|
auto counter_representation = CSS::generate_a_counter_representation(counter_style, DOM::AbstractElement { m_list_item_element }.style_scope(), index);
|
2026-02-09 21:44:19 +13:00
|
|
|
|
|
2026-02-27 13:11:38 +13:00
|
|
|
|
if (!counter_style)
|
2026-02-09 21:44:19 +13:00
|
|
|
|
return MUST(String::formatted("{}. ", counter_representation));
|
|
|
|
|
|
|
|
|
|
|
|
return MUST(String::formatted("{}{}{}", counter_style->prefix(), counter_representation, counter_style->suffix()));
|
2025-02-10 12:48:40 +00:00
|
|
|
|
},
|
2025-01-27 18:50:27 +01:00
|
|
|
|
[](String const& string) -> Optional<String> {
|
2026-02-09 21:44:19 +13:00
|
|
|
|
// <string>
|
|
|
|
|
|
// The element’s marker string is the specified <string>.
|
2025-01-27 18:50:27 +01:00
|
|
|
|
return string;
|
2025-02-10 12:48:40 +00:00
|
|
|
|
});
|
2019-10-11 23:16:53 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-15 04:01:23 +13:00
|
|
|
|
GC::Ptr<Painting::Paintable> ListItemMarkerBox::create_paintable() const
|
2021-10-29 09:00:30 -04:00
|
|
|
|
{
|
2022-03-10 14:02:25 +01:00
|
|
|
|
return Painting::MarkerPaintable::create(*this);
|
2021-10-29 09:00:30 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-16 21:35:09 +02:00
|
|
|
|
CSSPixels ListItemMarkerBox::relative_size() const
|
|
|
|
|
|
{
|
2026-01-29 23:50:16 +13:00
|
|
|
|
VERIFY(!m_list_style_type.has<Empty>());
|
|
|
|
|
|
|
2025-07-16 21:35:09 +02:00
|
|
|
|
auto font_size = first_available_font().pixel_size();
|
|
|
|
|
|
auto marker_text = text();
|
|
|
|
|
|
if (marker_text.has_value())
|
|
|
|
|
|
return CSSPixels::nearest_value_for(font_size);
|
|
|
|
|
|
|
2026-02-11 23:33:46 +13:00
|
|
|
|
// https://drafts.csswg.org/css-counter-styles-3/#simple-symbolic
|
|
|
|
|
|
// NB: The spec allows us to render some predefined symbol counter styles using a UA-generated image instead of
|
|
|
|
|
|
// text, it instructs us to size these in order to attractively fit within a 1em x 1em square. We mimic Firefox
|
|
|
|
|
|
// and generally use a size of 0.35em, except for disclosure open/closed styles which use a size of 0.5em.
|
|
|
|
|
|
static constexpr float marker_image_size_factor = 0.35f;
|
|
|
|
|
|
static constexpr float disclosure_marker_image_size_factor = 0.5f;
|
|
|
|
|
|
|
2026-02-27 13:11:38 +13:00
|
|
|
|
auto const& counter_style = m_list_style_type.get<RefPtr<CSS::CounterStyle const>>();
|
2026-02-09 21:44:19 +13:00
|
|
|
|
|
2026-02-27 13:11:38 +13:00
|
|
|
|
VERIFY(counter_style);
|
2026-02-09 21:44:19 +13:00
|
|
|
|
|
|
|
|
|
|
if (counter_style->name() == "square"_fly_string || counter_style->name() == "circle"_fly_string || counter_style->name() == "disc"_fly_string)
|
2026-02-11 23:33:46 +13:00
|
|
|
|
return CSSPixels::nearest_value_for(ceilf(font_size * marker_image_size_factor));
|
2026-02-09 21:44:19 +13:00
|
|
|
|
|
|
|
|
|
|
if (counter_style->name() == "disclosure-closed"_fly_string || counter_style->name() == "disclosure-open"_fly_string)
|
|
|
|
|
|
return CSSPixels::nearest_value_for(ceilf(font_size * disclosure_marker_image_size_factor));
|
|
|
|
|
|
|
|
|
|
|
|
VERIFY_NOT_REACHED();
|
2025-07-16 21:35:09 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-01-27 18:50:27 +01:00
|
|
|
|
void ListItemMarkerBox::visit_edges(Cell::Visitor& visitor)
|
|
|
|
|
|
{
|
|
|
|
|
|
Base::visit_edges(visitor);
|
|
|
|
|
|
visitor.visit(m_list_item_element);
|
|
|
|
|
|
}
|
2025-05-13 07:06:33 -04:00
|
|
|
|
|
2020-03-07 10:27:02 +01:00
|
|
|
|
}
|