Label.cpp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibGUI/Event.h>
  7. #include <LibWeb/DOM/Document.h>
  8. #include <LibWeb/DOM/Element.h>
  9. #include <LibWeb/Layout/InitialContainingBlock.h>
  10. #include <LibWeb/Layout/Label.h>
  11. #include <LibWeb/Layout/LabelableNode.h>
  12. #include <LibWeb/Layout/TextNode.h>
  13. #include <LibWeb/Painting/LabelablePaintable.h>
  14. namespace Web::Layout {
  15. Label::Label(DOM::Document& document, HTML::HTMLLabelElement* element, NonnullRefPtr<CSS::StyleProperties> style)
  16. : BlockContainer(document, element, move(style))
  17. {
  18. }
  19. Label::~Label() = default;
  20. void Label::handle_mousedown_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoint const&, unsigned button)
  21. {
  22. if (button != GUI::MouseButton::Primary)
  23. return;
  24. if (auto* control = labeled_control(); control)
  25. control->paintable()->handle_associated_label_mousedown({});
  26. m_tracking_mouse = true;
  27. }
  28. void Label::handle_mouseup_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoint const& position, unsigned button)
  29. {
  30. if (!m_tracking_mouse || button != GUI::MouseButton::Primary)
  31. return;
  32. // NOTE: Changing the checked state of the DOM node may run arbitrary JS, which could disappear this node.
  33. NonnullRefPtr protect = *this;
  34. if (auto* control = labeled_control(); control) {
  35. bool is_inside_control = enclosing_int_rect(control->paint_box()->absolute_rect()).contains(position);
  36. bool is_inside_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position);
  37. if (is_inside_control || is_inside_label)
  38. control->paintable()->handle_associated_label_mouseup({});
  39. }
  40. m_tracking_mouse = false;
  41. }
  42. void Label::handle_mousemove_on_label(Badge<Painting::TextPaintable>, Gfx::IntPoint const& position, unsigned)
  43. {
  44. if (!m_tracking_mouse)
  45. return;
  46. if (auto* control = labeled_control(); control) {
  47. bool is_inside_control = enclosing_int_rect(control->paint_box()->absolute_rect()).contains(position);
  48. bool is_inside_label = enclosing_int_rect(paint_box()->absolute_rect()).contains(position);
  49. control->paintable()->handle_associated_label_mousemove({}, is_inside_control || is_inside_label);
  50. }
  51. }
  52. bool Label::is_inside_associated_label(LabelableNode const& control, const Gfx::IntPoint& position)
  53. {
  54. if (auto* label = label_for_control_node(control); label)
  55. return enclosing_int_rect(label->paint_box()->absolute_rect()).contains(position);
  56. return false;
  57. }
  58. bool Label::is_associated_label_hovered(LabelableNode const& control)
  59. {
  60. if (auto* label = label_for_control_node(control); label) {
  61. if (label->document().hovered_node() == &label->dom_node())
  62. return true;
  63. if (auto* child = label->first_child_of_type<TextNode>(); child)
  64. return label->document().hovered_node() == &child->dom_node();
  65. }
  66. return false;
  67. }
  68. // https://html.spec.whatwg.org/multipage/forms.html#labeled-control
  69. Label const* Label::label_for_control_node(LabelableNode const& control)
  70. {
  71. if (!control.document().layout_node())
  72. return nullptr;
  73. // The for attribute may be specified to indicate a form control with which the caption is to be associated.
  74. // If the attribute is specified, the attribute's value must be the ID of a labelable element in the
  75. // same tree as the label element. If the attribute is specified and there is an element in the tree
  76. // whose ID is equal to the value of the for attribute, and the first such element in tree order is
  77. // a labelable element, then that element is the label element's labeled control.
  78. if (auto id = control.dom_node().attribute(HTML::AttributeNames::id); !id.is_empty()) {
  79. Label const* label = nullptr;
  80. control.document().layout_node()->for_each_in_inclusive_subtree_of_type<Label>([&](auto& node) {
  81. if (node.dom_node().for_() == id) {
  82. label = &node;
  83. return IterationDecision::Break;
  84. }
  85. return IterationDecision::Continue;
  86. });
  87. if (label)
  88. return label;
  89. }
  90. // If the for attribute is not specified, but the label element has a labelable element descendant,
  91. // then the first such descendant in tree order is the label element's labeled control.
  92. return control.first_ancestor_of_type<Label>();
  93. }
  94. // https://html.spec.whatwg.org/multipage/forms.html#labeled-control
  95. LabelableNode* Label::labeled_control()
  96. {
  97. if (!document().layout_node())
  98. return nullptr;
  99. LabelableNode* control = nullptr;
  100. // The for attribute may be specified to indicate a form control with which the caption is to be associated.
  101. // If the attribute is specified, the attribute's value must be the ID of a labelable element in the
  102. // same tree as the label element. If the attribute is specified and there is an element in the tree
  103. // whose ID is equal to the value of the for attribute, and the first such element in tree order is
  104. // a labelable element, then that element is the label element's labeled control.
  105. if (auto for_ = dom_node().for_(); !for_.is_null()) {
  106. document().layout_node()->for_each_in_inclusive_subtree_of_type<LabelableNode>([&](auto& node) {
  107. if (node.dom_node().attribute(HTML::AttributeNames::id) == for_) {
  108. control = &node;
  109. return IterationDecision::Break;
  110. }
  111. return IterationDecision::Continue;
  112. });
  113. return control;
  114. }
  115. // If the for attribute is not specified, but the label element has a labelable element descendant,
  116. // then the first such descendant in tree order is the label element's labeled control.
  117. for_each_in_subtree_of_type<LabelableNode>([&](auto& labelable_node) {
  118. control = &labelable_node;
  119. return IterationDecision::Break;
  120. });
  121. return control;
  122. }
  123. }