2022-03-10 14:02:25 +01:00
/*
* Copyright ( c ) 2018 - 2022 , Andreas Kling < kling @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-06-05 14:45:24 +01:00
# include <LibGfx/AntiAliasingPainter.h>
2022-03-10 14:02:25 +01:00
# include <LibGfx/StylePainter.h>
2023-07-09 11:40:17 +03:30
# include <LibJS/Heap/Heap.h>
2022-03-10 14:02:25 +01:00
# include <LibWeb/Layout/ListItemMarkerBox.h>
# include <LibWeb/Painting/MarkerPaintable.h>
namespace Web : : Painting {
2023-01-11 12:51:49 +01:00
JS : : NonnullGCPtr < MarkerPaintable > MarkerPaintable : : create ( Layout : : ListItemMarkerBox const & layout_box )
2022-03-10 14:02:25 +01:00
{
2023-01-11 12:51:49 +01:00
return layout_box . heap ( ) . allocate_without_realm < 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 ;
2022-10-31 19:46:55 +00:00
// FIXME: All this does is round to the nearest whole CSS pixel, but it's goofy.
2023-08-07 22:12:21 +01:00
CSSPixelRect enclosing = absolute_rect ( ) . to_type < float > ( ) . to_rounded < float > ( ) . to_type < 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-05-24 10:50:57 +02:00
CSSPixels marker_width = enclosing . height ( ) / 2.0 ;
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 ( ) ;
2022-06-05 14:45:24 +01:00
Gfx : : AntiAliasingPainter aa_painter { context . painter ( ) } ;
2022-03-10 14:02:25 +01:00
switch ( layout_box ( ) . list_style_type ( ) ) {
case CSS : : ListStyleType : : Square :
2022-10-27 16:23:39 +01:00
context . painter ( ) . fill_rect ( device_marker_rect . to_type < int > ( ) , color ) ;
2022-03-10 14:02:25 +01:00
break ;
case CSS : : ListStyleType : : Circle :
2022-10-27 16:23:39 +01:00
aa_painter . draw_ellipse ( device_marker_rect . to_type < int > ( ) , color , 1 ) ;
2022-03-10 14:02:25 +01:00
break ;
case CSS : : ListStyleType : : Disc :
2022-10-27 16:23:39 +01:00
aa_painter . fill_ellipse ( device_marker_rect . to_type < int > ( ) , color ) ;
2022-03-10 14:02:25 +01:00
break ;
2023-05-30 22:50:29 +02:00
case CSS : : ListStyleType : : 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 ( ) ;
aa_painter . fill_path ( path , color ) ;
break ;
}
case CSS : : ListStyleType : : 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 ( ) ;
aa_painter . fill_path ( path , color ) ;
break ;
}
2022-03-10 14:02:25 +01:00
case CSS : : ListStyleType : : Decimal :
case CSS : : ListStyleType : : DecimalLeadingZero :
case CSS : : ListStyleType : : LowerAlpha :
case CSS : : ListStyleType : : LowerLatin :
case CSS : : ListStyleType : : LowerRoman :
case CSS : : ListStyleType : : UpperAlpha :
case CSS : : ListStyleType : : UpperLatin :
case CSS : : ListStyleType : : UpperRoman :
if ( layout_box ( ) . text ( ) . is_null ( ) )
break ;
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 :(
2023-07-23 20:05:47 +02:00
context . painter ( ) . draw_text ( device_enclosing . to_type < int > ( ) , layout_box ( ) . text ( ) , layout_box ( ) . scaled_font ( context ) , Gfx : : TextAlignment : : Center , color ) ;
2022-03-10 14:02:25 +01:00
break ;
case CSS : : ListStyleType : : None :
return ;
default :
VERIFY_NOT_REACHED ( ) ;
}
}
}