Parcourir la source

LibWeb: Add input number up down UI buttons

Bastiaan van der Plaat il y a 1 an
Parent
commit
f8509e2183

+ 2 - 2
Tests/LibWeb/Layout/expected/input-element-with-display-inline.txt

@@ -5,7 +5,7 @@ Viewport <#document> at (0,0) content-size 800x600 children: not-inline
         frag 0 from BlockContainer start: 0, length: 0, rect: [11,11 200x25.84375]
       BlockContainer <input> at (11,11) content-size 200x25.84375 inline-block [BFC] children: not-inline
         Box <div> at (13,12) content-size 196x23.84375 flex-container(row) [FFC] children: not-inline
-          BlockContainer <div> at (14,13) content-size 0x21.84375 flex-item [BFC] children: inline
+          BlockContainer <div> at (14,13) content-size 194x21.84375 flex-item [BFC] children: inline
             TextNode <#text>
 
 ViewportPaintable (Viewport<#document>) [0,0 800x600]
@@ -13,4 +13,4 @@ ViewportPaintable (Viewport<#document>) [0,0 800x600]
     PaintableWithLines (BlockContainer<BODY>) [9,9 782x29.84375]
       PaintableWithLines (BlockContainer<INPUT>) [10,10 202x27.84375]
         PaintableBox (Box<DIV>) [11,11 200x25.84375]
-          PaintableWithLines (BlockContainer<DIV>) [13,12 2x23.84375]
+          PaintableWithLines (BlockContainer<DIV>) [13,12 196x23.84375]

+ 14 - 14
Tests/LibWeb/Layout/expected/input-text-node-invalidation-on-value-change.txt

@@ -1,22 +1,22 @@
 Viewport <#document> at (0,0) content-size 800x600 children: not-inline
-  BlockContainer <html> at (0,0) content-size 800x600 [BFC] children: not-inline
-    BlockContainer <body> at (8,8) content-size 784x21.46875 children: inline
-      line 0 width: 191.875, height: 21.46875, bottom: 21.46875, baseline: 13.53125
-        frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 189.875x19.46875]
-      BlockContainer <input#foo> at (9,9) content-size 189.875x19.46875 inline-block [BFC] children: not-inline
-        Box <div> at (11,10) content-size 185.875x17.46875 flex-container(row) [FFC] children: not-inline
-          BlockContainer <div> at (11,10) content-size 49.734375x17.46875 flex-item [BFC] children: inline
-            line 0 width: 49.734375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
-              frag 0 from TextNode start: 0, length: 4, rect: [11,10 49.734375x17.46875]
+  BlockContainer <html> at (0,0) content-size 800x41.84375 [BFC] children: not-inline
+    BlockContainer <body> at (8,8) content-size 784x25.84375 children: inline
+      line 0 width: 202, height: 25.84375, bottom: 25.84375, baseline: 16.921875
+        frag 0 from BlockContainer start: 0, length: 0, rect: [9,9 200x23.84375]
+      BlockContainer <input#foo> at (9,9) content-size 200x23.84375 inline-block [BFC] children: not-inline
+        Box <div> at (11,10) content-size 196x21.84375 flex-container(row) [FFC] children: not-inline
+          BlockContainer <div> at (11,10) content-size 196x21.84375 flex-item [BFC] children: inline
+            line 0 width: 62.171875, height: 21.84375, bottom: 21.84375, baseline: 16.921875
+              frag 0 from TextNode start: 0, length: 4, rect: [11,10 62.171875x21.84375]
                 "PASS"
             TextNode <#text>
       TextNode <#text>
       TextNode <#text>
 
 ViewportPaintable (Viewport<#document>) [0,0 800x600]
-  PaintableWithLines (BlockContainer<HTML>) [0,0 800x600]
-    PaintableWithLines (BlockContainer<BODY>) [8,8 784x21.46875]
-      PaintableWithLines (BlockContainer<INPUT>#foo) [8,8 191.875x21.46875]
-        PaintableBox (Box<DIV>) [9,9 189.875x19.46875]
-          PaintableWithLines (BlockContainer<DIV>) [11,10 49.734375x17.46875]
+  PaintableWithLines (BlockContainer<HTML>) [0,0 800x41.84375]
+    PaintableWithLines (BlockContainer<BODY>) [8,8 784x25.84375]
+      PaintableWithLines (BlockContainer<INPUT>#foo) [8,8 202x25.84375]
+        PaintableBox (Box<DIV>) [9,9 200x23.84375]
+          PaintableWithLines (BlockContainer<DIV>) [11,10 196x21.84375]
             TextPaintable (TextNode<#text>)

+ 8 - 1
Tests/LibWeb/Layout/input/input-text-node-invalidation-on-value-change.html

@@ -1,4 +1,11 @@
-<input id="foo">
+<!DOCTYPE html><html><head><style>
+* {
+    font: 20px 'SerenitySans';
+}
+input {
+    width: 200px;
+}
+</style></head><body><input id="foo">
 <script>
     let foo = document.getElementById("foo");
     foo.value = "FAIL";

+ 59 - 14
Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp

@@ -7,11 +7,13 @@
  * SPDX-License-Identifier: BSD-2-Clause
  */
 
+#include <LibJS/Runtime/NativeFunction.h>
 #include <LibWeb/CSS/StyleValues/DisplayStyleValue.h>
 #include <LibWeb/CSS/StyleValues/IdentifierStyleValue.h>
 #include <LibWeb/DOM/Document.h>
 #include <LibWeb/DOM/ElementFactory.h>
 #include <LibWeb/DOM/Event.h>
+#include <LibWeb/DOM/IDLEventListener.h>
 #include <LibWeb/DOM/ShadowRoot.h>
 #include <LibWeb/HTML/BrowsingContext.h>
 #include <LibWeb/HTML/EventNames.h>
@@ -563,8 +565,10 @@ void HTMLInputElement::create_shadow_tree_if_needed()
 void HTMLInputElement::create_text_input_shadow_tree()
 {
     auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
+    set_shadow_root(shadow_root);
+
     auto initial_value = m_value;
-    auto element = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
+    auto element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
     MUST(element->set_attribute(HTML::AttributeNames::style, R"~~~(
         display: flex;
         height: 100%;
@@ -572,37 +576,78 @@ void HTMLInputElement::create_text_input_shadow_tree()
         white-space: pre;
         border: none;
         padding: 1px 2px;
-)~~~"_string));
+    )~~~"_string));
+    MUST(shadow_root->append_child(element));
 
     m_placeholder_element = heap().allocate<PlaceholderElement>(realm(), document());
-    MUST(m_placeholder_element->style_for_bindings()->set_property(CSS::PropertyID::Height, "1lh"sv));
+    MUST(m_placeholder_element->set_attribute(HTML::AttributeNames::style, R"~~~(
+        flex: 1;
+        height: 1lh;
+    )~~~"_string));
+    MUST(element->append_child(*m_placeholder_element));
 
     m_placeholder_text_node = heap().allocate<DOM::Text>(realm(), document(), initial_value);
     m_placeholder_text_node->set_data(attribute(HTML::AttributeNames::placeholder).value_or(String {}));
     m_placeholder_text_node->set_editable_text_node_owner(Badge<HTMLInputElement> {}, *this);
     MUST(m_placeholder_element->append_child(*m_placeholder_text_node));
-    MUST(element->append_child(*m_placeholder_element));
 
-    m_inner_text_element = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
-    MUST(m_inner_text_element->style_for_bindings()->set_property(CSS::PropertyID::Height, "1lh"sv));
+    m_inner_text_element = MUST(DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML));
+    MUST(m_inner_text_element->set_attribute(HTML::AttributeNames::style, R"~~~(
+        flex: 1;
+        height: 1lh;
+    )~~~"_string));
+    MUST(element->append_child(*m_inner_text_element));
 
     m_text_node = heap().allocate<DOM::Text>(realm(), document(), move(initial_value));
-    if (m_type == TypeAttributeState::FileUpload) {
+    if (type_state() == TypeAttributeState::FileUpload) {
         // NOTE: file upload state is mutable, but we don't allow the text node to be modifed
         m_text_node->set_always_editable(false);
     } else {
         handle_readonly_attribute(attribute(HTML::AttributeNames::readonly));
     }
-
     m_text_node->set_editable_text_node_owner(Badge<HTMLInputElement> {}, *this);
-
-    if (m_type == TypeAttributeState::Password)
+    if (type_state() == TypeAttributeState::Password)
         m_text_node->set_is_password_input({}, true);
-
     MUST(m_inner_text_element->append_child(*m_text_node));
-    MUST(element->append_child(*m_inner_text_element));
-    MUST(shadow_root->append_child(element));
-    set_shadow_root(shadow_root);
+
+    if (type_state() == TypeAttributeState::Number) {
+        // Up button
+        auto up_button = MUST(DOM::create_element(document(), HTML::TagNames::button, Namespace::HTML));
+        // FIXME: This cursor property doesn't work
+        MUST(up_button->set_attribute(HTML::AttributeNames::style, R"~~~(
+            padding: 0;
+            cursor: default;
+        )~~~"_string));
+        MUST(up_button->set_inner_html("<svg style=\"width: 1em; height: 1em;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z\" /></svg>"sv));
+        MUST(element->append_child(up_button));
+
+        auto up_callback_function = JS::NativeFunction::create(
+            realm(), [this](JS::VM&) {
+                (void)step_up();
+                return JS::js_undefined();
+            },
+            0, "", &realm());
+        auto up_callback = realm().heap().allocate_without_realm<WebIDL::CallbackType>(*up_callback_function, Bindings::host_defined_environment_settings_object(realm()));
+        up_button->add_event_listener_without_options("click"_fly_string, DOM::IDLEventListener::create(realm(), up_callback));
+
+        // Down button
+        auto down_button = MUST(DOM::create_element(document(), HTML::TagNames::button, Namespace::HTML));
+        MUST(down_button->set_attribute(HTML::AttributeNames::style, R"~~~(
+            padding: 0;
+            cursor: default;
+        )~~~"_string));
+        MUST(down_button->set_inner_html("<svg style=\"width: 1em; height: 1em;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z\" /></svg>"sv));
+        MUST(element->append_child(down_button));
+
+        auto down_callback_function = JS::NativeFunction::create(
+            realm(), [this](JS::VM&) {
+                (void)step_down();
+                return JS::js_undefined();
+            },
+            0, "", &realm());
+        auto down_callback = realm().heap().allocate_without_realm<WebIDL::CallbackType>(*down_callback_function, Bindings::host_defined_environment_settings_object(realm()));
+        down_button->add_event_listener_without_options("click"_fly_string, DOM::IDLEventListener::create(realm(), down_callback));
+    }
 }
 
 void HTMLInputElement::create_color_input_shadow_tree()