ListItemMarkerBox.cpp 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/StringBuilder.h>
  8. #include <LibGfx/Painter.h>
  9. #include <LibWeb/Layout/ListItemMarkerBox.h>
  10. namespace Web::Layout {
  11. ListItemMarkerBox::ListItemMarkerBox(DOM::Document& document, CSS::ListStyleType style_type, size_t index, NonnullRefPtr<CSS::StyleProperties> style)
  12. : Box(document, nullptr, move(style))
  13. , m_list_style_type(style_type)
  14. , m_index(index)
  15. {
  16. switch (m_list_style_type) {
  17. case CSS::ListStyleType::Square:
  18. case CSS::ListStyleType::Circle:
  19. case CSS::ListStyleType::Disc:
  20. break;
  21. case CSS::ListStyleType::Decimal:
  22. m_text = String::formatted("{}.", m_index);
  23. break;
  24. case CSS::ListStyleType::DecimalLeadingZero:
  25. // This is weird, but in accordance to spec.
  26. m_text = m_index < 10 ? String::formatted("0{}.", m_index) : String::formatted("{}.", m_index);
  27. break;
  28. case CSS::ListStyleType::LowerAlpha:
  29. case CSS::ListStyleType::LowerLatin:
  30. m_text = String::bijective_base_from(m_index - 1).to_lowercase();
  31. break;
  32. case CSS::ListStyleType::UpperAlpha:
  33. case CSS::ListStyleType::UpperLatin:
  34. m_text = String::bijective_base_from(m_index - 1);
  35. break;
  36. case CSS::ListStyleType::LowerRoman:
  37. m_text = String::roman_number_from(m_index).to_lowercase();
  38. break;
  39. case CSS::ListStyleType::UpperRoman:
  40. m_text = String::roman_number_from(m_index);
  41. break;
  42. case CSS::ListStyleType::None:
  43. break;
  44. default:
  45. VERIFY_NOT_REACHED();
  46. }
  47. int image_width = 0;
  48. int image_height = 0;
  49. if (auto const* list_style_image = list_style_image_bitmap()) {
  50. image_width = list_style_image->rect().width();
  51. image_height = list_style_image->rect().height();
  52. }
  53. if (m_text.is_null()) {
  54. set_width(image_width + 4);
  55. } else {
  56. auto text_width = font().width(m_text);
  57. set_width(image_width + text_width);
  58. }
  59. set_height(max(image_height, line_height()));
  60. }
  61. ListItemMarkerBox::~ListItemMarkerBox()
  62. {
  63. }
  64. void ListItemMarkerBox::paint(PaintContext& context, PaintPhase phase)
  65. {
  66. if (phase != PaintPhase::Foreground)
  67. return;
  68. auto enclosing = enclosing_int_rect(absolute_rect());
  69. if (auto const* list_style_image = list_style_image_bitmap()) {
  70. context.painter().blit(enclosing.location(), *list_style_image, list_style_image->rect());
  71. return;
  72. }
  73. // FIXME: It would be nicer to not have to go via the parent here to get our inherited style.
  74. auto color = parent()->computed_values().color();
  75. int marker_width = (int)enclosing.height() / 2;
  76. Gfx::IntRect marker_rect { 0, 0, marker_width, marker_width };
  77. marker_rect.center_within(enclosing);
  78. switch (m_list_style_type) {
  79. case CSS::ListStyleType::Square:
  80. context.painter().fill_rect(marker_rect, color);
  81. break;
  82. case CSS::ListStyleType::Circle:
  83. // For some reason for draw_ellipse() the ellipse is outside of the rect while for fill_ellipse() the ellipse is inside.
  84. // Scale the marker_rect with sqrt(2) to get an ellipse arc (circle) that appears as if it was inside of the marker_rect.
  85. marker_rect.set_height(marker_rect.height() / 1.41);
  86. marker_rect.set_width(marker_rect.width() / 1.41);
  87. marker_rect.center_within(enclosing);
  88. context.painter().draw_ellipse_intersecting(marker_rect, color);
  89. break;
  90. case CSS::ListStyleType::Disc:
  91. context.painter().fill_ellipse(marker_rect, color);
  92. break;
  93. case CSS::ListStyleType::Decimal:
  94. case CSS::ListStyleType::DecimalLeadingZero:
  95. case CSS::ListStyleType::LowerAlpha:
  96. case CSS::ListStyleType::LowerLatin:
  97. case CSS::ListStyleType::LowerRoman:
  98. case CSS::ListStyleType::UpperAlpha:
  99. case CSS::ListStyleType::UpperLatin:
  100. case CSS::ListStyleType::UpperRoman:
  101. if (m_text.is_null())
  102. break;
  103. context.painter().draw_text(enclosing, m_text, Gfx::TextAlignment::Center);
  104. break;
  105. case CSS::ListStyleType::None:
  106. return;
  107. default:
  108. VERIFY_NOT_REACHED();
  109. }
  110. }
  111. Gfx::Bitmap const* ListItemMarkerBox::list_style_image_bitmap() const
  112. {
  113. return list_style_image() ? list_style_image()->bitmap() : nullptr;
  114. }
  115. }