InlineNode.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. * Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibGfx/Painter.h>
  8. #include <LibWeb/DOM/Document.h>
  9. #include <LibWeb/DOM/Element.h>
  10. #include <LibWeb/Layout/BlockContainer.h>
  11. #include <LibWeb/Layout/InlineFormattingContext.h>
  12. #include <LibWeb/Layout/InlineNode.h>
  13. #include <LibWeb/Painting/BackgroundPainting.h>
  14. #include <LibWeb/Painting/BorderPainting.h>
  15. #include <LibWeb/Painting/ShadowPainting.h>
  16. namespace Web::Layout {
  17. InlineNode::InlineNode(DOM::Document& document, DOM::Element* element, NonnullRefPtr<CSS::StyleProperties> style)
  18. : Layout::NodeWithStyleAndBoxModelMetrics(document, element, move(style))
  19. {
  20. set_inline(true);
  21. }
  22. InlineNode::~InlineNode()
  23. {
  24. }
  25. void InlineNode::paint(PaintContext& context, PaintPhase phase)
  26. {
  27. auto& painter = context.painter();
  28. if (phase == PaintPhase::Background) {
  29. auto top_left_border_radius = computed_values().border_top_left_radius();
  30. auto top_right_border_radius = computed_values().border_top_right_radius();
  31. auto bottom_right_border_radius = computed_values().border_bottom_right_radius();
  32. auto bottom_left_border_radius = computed_values().border_bottom_left_radius();
  33. auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position();
  34. for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) {
  35. Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() };
  36. if (is_first_fragment) {
  37. float extra_start_width = box_model().padding.left;
  38. absolute_fragment_rect.translate_by(-extra_start_width, 0);
  39. absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_start_width);
  40. }
  41. if (is_last_fragment) {
  42. float extra_end_width = box_model().padding.right;
  43. absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width);
  44. }
  45. auto border_radius_data = Painting::normalized_border_radius_data(*this, absolute_fragment_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
  46. Painting::paint_background(context, *this, enclosing_int_rect(absolute_fragment_rect), computed_values().background_color(), &computed_values().background_layers(), border_radius_data);
  47. if (auto computed_box_shadow = computed_values().box_shadow(); !computed_box_shadow.is_empty()) {
  48. Vector<Painting::BoxShadowData> resolved_box_shadow_data;
  49. resolved_box_shadow_data.ensure_capacity(computed_box_shadow.size());
  50. for (auto const& layer : computed_box_shadow) {
  51. resolved_box_shadow_data.empend(
  52. layer.color,
  53. static_cast<int>(layer.offset_x.to_px(*this)),
  54. static_cast<int>(layer.offset_y.to_px(*this)),
  55. static_cast<int>(layer.blur_radius.to_px(*this)),
  56. static_cast<int>(layer.spread_distance.to_px(*this)),
  57. layer.placement == CSS::BoxShadowPlacement::Outer ? Painting::BoxShadowPlacement::Outer : Painting::BoxShadowPlacement::Inner);
  58. }
  59. Painting::paint_box_shadow(context, enclosing_int_rect(absolute_fragment_rect), resolved_box_shadow_data);
  60. }
  61. return IterationDecision::Continue;
  62. });
  63. }
  64. if (phase == PaintPhase::Border) {
  65. auto top_left_border_radius = computed_values().border_top_left_radius();
  66. auto top_right_border_radius = computed_values().border_top_right_radius();
  67. auto bottom_right_border_radius = computed_values().border_bottom_right_radius();
  68. auto bottom_left_border_radius = computed_values().border_bottom_left_radius();
  69. auto borders_data = Painting::BordersData {
  70. .top = computed_values().border_top(),
  71. .right = computed_values().border_right(),
  72. .bottom = computed_values().border_bottom(),
  73. .left = computed_values().border_left(),
  74. };
  75. auto containing_block_position_in_absolute_coordinates = containing_block()->absolute_position();
  76. for_each_fragment([&](auto const& fragment, bool is_first_fragment, bool is_last_fragment) {
  77. Gfx::FloatRect absolute_fragment_rect { containing_block_position_in_absolute_coordinates.translated(fragment.offset()), fragment.size() };
  78. if (is_first_fragment) {
  79. float extra_start_width = box_model().padding.left;
  80. absolute_fragment_rect.translate_by(-extra_start_width, 0);
  81. absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_start_width);
  82. }
  83. if (is_last_fragment) {
  84. float extra_end_width = box_model().padding.right;
  85. absolute_fragment_rect.set_width(absolute_fragment_rect.width() + extra_end_width);
  86. }
  87. auto bordered_rect = absolute_fragment_rect.inflated(borders_data.top.width, borders_data.right.width, borders_data.bottom.width, borders_data.left.width);
  88. auto border_radius_data = Painting::normalized_border_radius_data(*this, bordered_rect, top_left_border_radius, top_right_border_radius, bottom_right_border_radius, bottom_left_border_radius);
  89. Painting::paint_all_borders(context, bordered_rect, border_radius_data, borders_data);
  90. return IterationDecision::Continue;
  91. });
  92. }
  93. if (phase == PaintPhase::Foreground && document().inspected_node() == dom_node()) {
  94. // FIXME: This paints a double-thick border between adjacent fragments, where ideally there
  95. // would be none. Once we implement non-rectangular outlines for the `outline` CSS
  96. // property, we can use that here instead.
  97. for_each_fragment([&](auto& fragment, bool, bool) {
  98. painter.draw_rect(enclosing_int_rect(fragment.absolute_rect()), Color::Magenta);
  99. return IterationDecision::Continue;
  100. });
  101. }
  102. }
  103. template<typename Callback>
  104. void InlineNode::for_each_fragment(Callback callback)
  105. {
  106. // FIXME: This will be slow if the containing block has a lot of fragments!
  107. Vector<LineBoxFragment const&> fragments;
  108. containing_block()->for_each_fragment([&](auto& fragment) {
  109. if (is_inclusive_ancestor_of(fragment.layout_node()))
  110. fragments.append(fragment);
  111. return IterationDecision::Continue;
  112. });
  113. for (size_t i = 0; i < fragments.size(); ++i) {
  114. auto const& fragment = fragments[i];
  115. callback(fragment, i == 0, i == fragments.size() - 1);
  116. }
  117. }
  118. }