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