PaintableFragment.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. /*
  2. * Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/DOM/Range.h>
  7. #include <LibWeb/Layout/Viewport.h>
  8. #include <LibWeb/Painting/PaintableBox.h>
  9. namespace Web::Painting {
  10. PaintableFragment::PaintableFragment(Layout::LineBoxFragment const& fragment)
  11. : m_layout_node(fragment.layout_node())
  12. , m_offset(fragment.offset())
  13. , m_size(fragment.size())
  14. , m_baseline(fragment.baseline())
  15. , m_start(fragment.start())
  16. , m_length(fragment.length())
  17. , m_glyph_run(fragment.glyph_run())
  18. {
  19. }
  20. CSSPixelRect const PaintableFragment::absolute_rect() const
  21. {
  22. CSSPixelRect rect { {}, size() };
  23. auto const* containing_block = paintable().containing_block();
  24. if (containing_block && containing_block->paintable_box())
  25. rect.set_location(containing_block->paintable_box()->absolute_position());
  26. rect.translate_by(offset());
  27. return rect;
  28. }
  29. int PaintableFragment::text_index_at(CSSPixels x) const
  30. {
  31. if (!is<Layout::TextNode>(*m_layout_node))
  32. return 0;
  33. auto& layout_text = verify_cast<Layout::TextNode>(layout_node());
  34. auto& font = layout_text.first_available_font();
  35. Utf8View view(layout_text.text_for_rendering().bytes_as_string_view().substring_view(m_start, m_length));
  36. CSSPixels relative_x = x - absolute_rect().x();
  37. CSSPixels glyph_spacing = font.glyph_spacing();
  38. if (relative_x < 0)
  39. return 0;
  40. CSSPixels width_so_far = 0;
  41. for (auto it = view.begin(); it != view.end(); ++it) {
  42. auto previous_it = it;
  43. CSSPixels glyph_width = CSSPixels::nearest_value_for(font.glyph_or_emoji_width(it));
  44. if ((width_so_far + glyph_width + glyph_spacing / 2) > relative_x)
  45. return m_start + view.byte_offset_of(previous_it);
  46. width_so_far += glyph_width + glyph_spacing;
  47. }
  48. return m_start + m_length;
  49. }
  50. CSSPixelRect PaintableFragment::selection_rect(Gfx::Font const& font) const
  51. {
  52. if (layout_node().selection_state() == Layout::Node::SelectionState::None)
  53. return {};
  54. if (layout_node().selection_state() == Layout::Node::SelectionState::Full)
  55. return absolute_rect();
  56. if (!is<Layout::TextNode>(layout_node()))
  57. return {};
  58. auto selection = layout_node().root().selection();
  59. if (!selection)
  60. return {};
  61. auto range = selection->range();
  62. if (!range)
  63. return {};
  64. // FIXME: m_start and m_length should be unsigned and then we won't need these casts.
  65. auto const start_index = static_cast<unsigned>(m_start);
  66. auto const end_index = static_cast<unsigned>(m_start) + static_cast<unsigned>(m_length);
  67. auto& layout_text = verify_cast<Layout::TextNode>(layout_node());
  68. auto text = layout_text.text_for_rendering().bytes_as_string_view().substring_view(m_start, m_length);
  69. if (layout_node().selection_state() == Layout::Node::SelectionState::StartAndEnd) {
  70. // we are in the start/end node (both the same)
  71. if (start_index > range->end_offset())
  72. return {};
  73. if (end_index < range->start_offset())
  74. return {};
  75. if (range->start_offset() == range->end_offset())
  76. return {};
  77. auto selection_start_in_this_fragment = max(0, range->start_offset() - m_start);
  78. auto selection_end_in_this_fragment = min(m_length, range->end_offset() - m_start);
  79. auto pixel_distance_to_first_selected_character = CSSPixels::nearest_value_for(font.width(text.substring_view(0, selection_start_in_this_fragment)));
  80. auto pixel_width_of_selection = CSSPixels::nearest_value_for(font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment))) + 1;
  81. auto rect = absolute_rect();
  82. rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
  83. rect.set_width(pixel_width_of_selection);
  84. return rect;
  85. }
  86. if (layout_node().selection_state() == Layout::Node::SelectionState::Start) {
  87. // we are in the start node
  88. if (end_index < range->start_offset())
  89. return {};
  90. auto selection_start_in_this_fragment = max(0, range->start_offset() - m_start);
  91. auto selection_end_in_this_fragment = m_length;
  92. auto pixel_distance_to_first_selected_character = CSSPixels::nearest_value_for(font.width(text.substring_view(0, selection_start_in_this_fragment)));
  93. auto pixel_width_of_selection = CSSPixels::nearest_value_for(font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment))) + 1;
  94. auto rect = absolute_rect();
  95. rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
  96. rect.set_width(pixel_width_of_selection);
  97. return rect;
  98. }
  99. if (layout_node().selection_state() == Layout::Node::SelectionState::End) {
  100. // we are in the end node
  101. if (start_index > range->end_offset())
  102. return {};
  103. auto selection_start_in_this_fragment = 0;
  104. auto selection_end_in_this_fragment = min(range->end_offset() - m_start, m_length);
  105. auto pixel_distance_to_first_selected_character = CSSPixels::nearest_value_for(font.width(text.substring_view(0, selection_start_in_this_fragment)));
  106. auto pixel_width_of_selection = CSSPixels::nearest_value_for(font.width(text.substring_view(selection_start_in_this_fragment, selection_end_in_this_fragment - selection_start_in_this_fragment))) + 1;
  107. auto rect = absolute_rect();
  108. rect.set_x(rect.x() + pixel_distance_to_first_selected_character);
  109. rect.set_width(pixel_width_of_selection);
  110. return rect;
  111. }
  112. return {};
  113. }
  114. }