Viewport.cpp 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. /*
  2. * Copyright (c) 2018-2023, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibWeb/DOM/Range.h>
  7. #include <LibWeb/Dump.h>
  8. #include <LibWeb/Layout/Viewport.h>
  9. #include <LibWeb/Painting/PaintableBox.h>
  10. #include <LibWeb/Painting/StackingContext.h>
  11. #include <LibWeb/Painting/ViewportPaintable.h>
  12. namespace Web::Layout {
  13. Viewport::Viewport(DOM::Document& document, NonnullRefPtr<CSS::StyleProperties> style)
  14. : BlockContainer(document, &document, move(style))
  15. {
  16. }
  17. Viewport::~Viewport() = default;
  18. JS::GCPtr<Selection::Selection> Viewport::selection() const
  19. {
  20. return const_cast<DOM::Document&>(document()).get_selection();
  21. }
  22. void Viewport::recompute_selection_states()
  23. {
  24. // 1. Start by resetting the selection state of all layout nodes to None.
  25. for_each_in_inclusive_subtree([&](auto& layout_node) {
  26. layout_node.set_selection_state(SelectionState::None);
  27. return IterationDecision::Continue;
  28. });
  29. // 2. If there is no active Selection or selected Range, return.
  30. auto selection = document().get_selection();
  31. if (!selection)
  32. return;
  33. auto range = selection->range();
  34. if (!range)
  35. return;
  36. auto* start_container = range->start_container();
  37. auto* end_container = range->end_container();
  38. // 3. If the selection starts and ends in the same node:
  39. if (start_container == end_container) {
  40. // 1. If the selection starts and ends at the same offset, return.
  41. if (range->start_offset() == range->end_offset()) {
  42. // NOTE: A zero-length selection should not be visible.
  43. return;
  44. }
  45. // 2. If it's a text node, mark it as StartAndEnd and return.
  46. if (is<DOM::Text>(*start_container)) {
  47. if (auto* layout_node = start_container->layout_node()) {
  48. layout_node->set_selection_state(SelectionState::StartAndEnd);
  49. }
  50. return;
  51. }
  52. }
  53. if (start_container == end_container && is<DOM::Text>(*start_container)) {
  54. if (auto* layout_node = start_container->layout_node()) {
  55. layout_node->set_selection_state(SelectionState::StartAndEnd);
  56. }
  57. return;
  58. }
  59. // 4. Mark the selection start node as Start (if text) or Full (if anything else).
  60. if (auto* layout_node = start_container->layout_node()) {
  61. if (is<DOM::Text>(*start_container))
  62. layout_node->set_selection_state(SelectionState::Start);
  63. else
  64. layout_node->set_selection_state(SelectionState::Full);
  65. }
  66. // 5. Mark the selection end node as End (if text) or Full (if anything else).
  67. if (auto* layout_node = end_container->layout_node()) {
  68. if (is<DOM::Text>(*end_container))
  69. layout_node->set_selection_state(SelectionState::End);
  70. else
  71. layout_node->set_selection_state(SelectionState::Full);
  72. }
  73. // 6. Mark the nodes between start node and end node (in tree order) as Full.
  74. for (auto* node = start_container->next_in_pre_order(); node && node != end_container; node = node->next_in_pre_order()) {
  75. if (auto* layout_node = node->layout_node())
  76. layout_node->set_selection_state(SelectionState::Full);
  77. }
  78. }
  79. JS::GCPtr<Painting::Paintable> Viewport::create_paintable() const
  80. {
  81. return Painting::ViewportPaintable::create(*this);
  82. }
  83. }