2022-03-10 14:02:25 +01:00
/*
2024-10-04 13:19:50 +02:00
* Copyright ( c ) 2018 - 2022 , Andreas Kling < andreas @ ladybird . org >
2022-03-10 14:02:25 +01:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2024-11-15 04:01:23 +13:00
# include <LibGC/Heap.h>
2022-03-10 14:02:25 +01:00
# include <LibWeb/Layout/ListItemMarkerBox.h>
2025-02-02 15:55:26 +01:00
# include <LibWeb/Painting/DisplayListRecorder.h>
2022-03-10 14:02:25 +01:00
# include <LibWeb/Painting/MarkerPaintable.h>
namespace Web : : Painting {
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( MarkerPaintable ) ;
2024-04-06 10:16:04 -07:00
2024-11-15 04:01:23 +13:00
GC : : Ref < MarkerPaintable > MarkerPaintable : : create ( Layout : : ListItemMarkerBox const & layout_box )
2022-03-10 14:02:25 +01:00
{
2024-11-14 06:13:46 +13:00
return layout_box . heap ( ) . allocate < MarkerPaintable > ( layout_box ) ;
2022-03-10 14:02:25 +01:00
}
MarkerPaintable : : MarkerPaintable ( Layout : : ListItemMarkerBox const & layout_box )
2022-03-10 15:50:57 +01:00
: PaintableBox ( layout_box )
2022-03-10 14:02:25 +01:00
{
}
Layout : : ListItemMarkerBox const & MarkerPaintable : : layout_box ( ) const
{
2022-03-10 15:50:57 +01:00
return static_cast < Layout : : ListItemMarkerBox const & > ( layout_node ( ) ) ;
2022-03-10 14:02:25 +01:00
}
2023-05-30 22:50:29 +02:00
constexpr float sin_60_deg = 0.866025403f ;
2022-03-10 14:02:25 +01:00
void MarkerPaintable : : paint ( PaintContext & context , PaintPhase phase ) const
{
2023-06-02 22:50:11 +02:00
if ( phase = = PaintPhase : : Overlay )
PaintableBox : : paint ( context , phase ) ;
2022-03-10 14:02:25 +01:00
if ( phase ! = PaintPhase : : Foreground )
return ;
2023-08-08 15:59:54 +02:00
CSSPixelRect enclosing = absolute_rect ( ) . to_rounded < CSSPixels > ( ) ;
2022-10-27 16:23:39 +01:00
auto device_enclosing = context . enclosing_device_rect ( enclosing ) ;
2022-03-10 14:02:25 +01:00
2023-08-26 15:03:04 +01:00
CSSPixels marker_width = enclosing . height ( ) / 2 ;
2022-07-31 01:11:59 +01:00
if ( auto const * list_style_image = layout_box ( ) . list_style_image ( ) ) {
2022-10-27 16:23:39 +01:00
CSSPixelRect image_rect {
2022-07-31 01:11:59 +01:00
0 , 0 ,
2023-06-12 21:37:35 +03:00
list_style_image - > natural_width ( ) . value_or ( marker_width ) ,
list_style_image - > natural_height ( ) . value_or ( marker_width )
2022-07-31 01:11:59 +01:00
} ;
image_rect . center_within ( enclosing ) ;
2022-10-27 16:23:39 +01:00
auto device_image_rect = context . enclosing_device_rect ( image_rect ) ;
2022-11-08 16:31:01 +00:00
list_style_image - > resolve_for_size ( layout_box ( ) , image_rect . size ( ) ) ;
list_style_image - > paint ( context , device_image_rect , computed_values ( ) . image_rendering ( ) ) ;
2022-03-10 14:02:25 +01:00
return ;
}
2022-10-27 16:23:39 +01:00
CSSPixelRect marker_rect { 0 , 0 , marker_width , marker_width } ;
2022-03-10 14:02:25 +01:00
marker_rect . center_within ( enclosing ) ;
2022-10-27 16:23:39 +01:00
auto device_marker_rect = context . enclosing_device_rect ( marker_rect ) ;
2022-03-10 14:02:25 +01:00
2023-05-30 22:50:29 +02:00
float left = device_marker_rect . x ( ) . value ( ) ;
float right = left + device_marker_rect . width ( ) . value ( ) ;
float top = device_marker_rect . y ( ) . value ( ) ;
float bottom = top + device_marker_rect . height ( ) . value ( ) ;
2022-07-31 01:11:59 +01:00
auto color = computed_values ( ) . color ( ) ;
2025-01-27 18:50:27 +01:00
if ( auto text = layout_box ( ) . text ( ) ; text . has_value ( ) ) {
2023-04-01 21:14:21 +01:00
// FIXME: This should use proper text layout logic!
// This does not line up with the text in the <li> element which looks very sad :(
2025-04-19 19:10:01 +02:00
context . display_list_recorder ( ) . draw_text ( device_enclosing . to_type < int > ( ) , * text , layout_box ( ) . font ( context ) , Gfx : : TextAlignment : : Center , color ) ;
2025-02-10 12:48:40 +00:00
} else if ( auto const * counter_style = layout_box ( ) . list_style_type ( ) . get_pointer < CSS : : CounterStyleNameKeyword > ( ) ) {
switch ( * counter_style ) {
case CSS : : CounterStyleNameKeyword : : Square :
context . display_list_recorder ( ) . fill_rect ( device_marker_rect . to_type < int > ( ) , color ) ;
break ;
case CSS : : CounterStyleNameKeyword : : Circle :
context . display_list_recorder ( ) . draw_ellipse ( device_marker_rect . to_type < int > ( ) , color , 1 ) ;
break ;
case CSS : : CounterStyleNameKeyword : : Disc :
context . display_list_recorder ( ) . fill_ellipse ( device_marker_rect . to_type < int > ( ) , color ) ;
break ;
case CSS : : CounterStyleNameKeyword : : DisclosureClosed : {
// https://drafts.csswg.org/css-counter-styles-3/#disclosure-closed
// For the disclosure-open and disclosure-closed counter styles, the marker must be an image or character suitable for indicating the open and closed states of a disclosure widget, such as HTML’ s details element.
// FIXME: If the image is directional, it must respond to the writing mode of the element, similar to the bidi-sensitive images feature of the Images 4 module.
// Draw an equilateral triangle pointing right.
auto path = Gfx : : Path ( ) ;
path . move_to ( { left , top } ) ;
path . line_to ( { left + sin_60_deg * ( right - left ) , ( top + bottom ) / 2 } ) ;
path . line_to ( { left , bottom } ) ;
path . close ( ) ;
context . display_list_recorder ( ) . fill_path ( { . path = path , . color = color , . winding_rule = Gfx : : WindingRule : : EvenOdd } ) ;
break ;
}
case CSS : : CounterStyleNameKeyword : : DisclosureOpen : {
// https://drafts.csswg.org/css-counter-styles-3/#disclosure-open
// For the disclosure-open and disclosure-closed counter styles, the marker must be an image or character suitable for indicating the open and closed states of a disclosure widget, such as HTML’ s details element.
// FIXME: If the image is directional, it must respond to the writing mode of the element, similar to the bidi-sensitive images feature of the Images 4 module.
// Draw an equilateral triangle pointing down.
auto path = Gfx : : Path ( ) ;
path . move_to ( { left , top } ) ;
path . line_to ( { right , top } ) ;
path . line_to ( { ( left + right ) / 2 , top + sin_60_deg * ( bottom - top ) } ) ;
path . close ( ) ;
context . display_list_recorder ( ) . fill_path ( { . path = path , . color = color , . winding_rule = Gfx : : WindingRule : : EvenOdd } ) ;
break ;
}
case CSS : : CounterStyleNameKeyword : : None :
return ;
case CSS : : CounterStyleNameKeyword : : Decimal :
case CSS : : CounterStyleNameKeyword : : DecimalLeadingZero :
case CSS : : CounterStyleNameKeyword : : LowerAlpha :
case CSS : : CounterStyleNameKeyword : : LowerLatin :
case CSS : : CounterStyleNameKeyword : : LowerRoman :
case CSS : : CounterStyleNameKeyword : : UpperAlpha :
case CSS : : CounterStyleNameKeyword : : UpperLatin :
case CSS : : CounterStyleNameKeyword : : UpperRoman :
// These are handled by text() already.
default :
VERIFY_NOT_REACHED ( ) ;
}
2022-03-10 14:02:25 +01:00
}
}
}