ladybird/Libraries/LibWeb/Layout/ListItemMarkerBox.cpp

119 lines
5.1 KiB
C++
Raw Permalink Normal View History

/*
* Copyright (c) 2018-2022, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/Layout/ListItemMarkerBox.h>
#include <LibWeb/Painting/MarkerPaintable.h>
namespace Web::Layout {
GC_DEFINE_ALLOCATOR(ListItemMarkerBox);
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)
: Box(document, nullptr, move(style))
, m_list_style_type(style_type)
, m_list_style_position(style_position)
, m_list_item_element(list_item_element)
{
}
ListItemMarkerBox::~ListItemMarkerBox() = default;
bool ListItemMarkerBox::counter_style_is_rendered_with_custom_image(RefPtr<CSS::CounterStyle const> const& counter_style)
{
// 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.
if (!counter_style)
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);
}
Optional<String> ListItemMarkerBox::text() const
{
// https://drafts.csswg.org/css-lists-3/#text-markers
auto index = m_list_item_element->ordinal_value();
return m_list_style_type.visit(
[](Empty const&) -> Optional<String> {
// none
// The element has no marker string.
return {};
},
[&](RefPtr<CSS::CounterStyle const> const& counter_style) -> Optional<String> {
// <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))
return {};
// NB: Fallback to decimal if the counter style does not exist is handled within generate_a_counter_representation()
auto counter_representation = CSS::generate_a_counter_representation(counter_style, DOM::AbstractElement { m_list_item_element }.style_scope(), index);
if (!counter_style)
return MUST(String::formatted("{}. ", counter_representation));
return MUST(String::formatted("{}{}{}", counter_style->prefix(), counter_representation, counter_style->suffix()));
},
[](String const& string) -> Optional<String> {
// <string>
// The element’s marker string is the specified <string>.
return string;
});
}
GC::Ptr<Painting::Paintable> ListItemMarkerBox::create_paintable() const
{
return Painting::MarkerPaintable::create(*this);
}
CSSPixels ListItemMarkerBox::relative_size() const
{
VERIFY(!m_list_style_type.has<Empty>());
auto font_size = first_available_font().pixel_size();
auto marker_text = text();
if (marker_text.has_value())
return CSSPixels::nearest_value_for(font_size);
// 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;
auto const& counter_style = m_list_style_type.get<RefPtr<CSS::CounterStyle const>>();
VERIFY(counter_style);
if (counter_style->name() == "square"_fly_string || counter_style->name() == "circle"_fly_string || counter_style->name() == "disc"_fly_string)
return CSSPixels::nearest_value_for(ceilf(font_size * marker_image_size_factor));
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();
}
void ListItemMarkerBox::visit_edges(Cell::Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_list_item_element);
}
}