LibWeb: Create BlockContainer layout node for <input type="button">

...and shadow tree with TextNode for "value" attribute is created.
This means InlineFormattingContext is used, and button's text now
respects CSS text-decoration properties and unicode-ranges.

(cherry picked from commit 8feaecd5c8d02a2fdb989a9a9671e008d1c3a7de)
This commit is contained in:
Aliaksandr Kalenik 2024-06-13 20:04:41 +03:00 committed by Nico Weber
parent 7b47c57c54
commit c6e3c0a339
11 changed files with 16 additions and 183 deletions

View file

@ -12,7 +12,6 @@ source_set("Layout") {
"Box.cpp",
"BoxModelMetrics.cpp",
"BreakNode.cpp",
"ButtonBox.cpp",
"CanvasBox.cpp",
"CheckBox.cpp",
"FlexFormattingContext.cpp",

View file

@ -12,7 +12,6 @@ source_set("Painting") {
"BorderRadiiData.cpp",
"BorderRadiusCornerClipper.cpp",
"BordersData.cpp",
"ButtonPaintable.cpp",
"CanvasPaintable.cpp",
"CheckBoxPaintable.cpp",
"ClippableAndScrollable.cpp",

View file

@ -1,4 +1,4 @@
wfh :^) PASS wfh :^) wfh :^) FAIL PASS FAIL wfh :^) FAIL FAIL PASS PASS wfh :^) wfh :^) PASS PASSwfh :^) FAIL wfh :^) FAIL wfh :^) wfh :^) FAIL wfh :^) wfh :^) defaultButton: click button=PASS
wfh :^) PASS wfh :^) PASS wfh :^) FAIL PASS FAIL wfh :^) FAIL FAIL PASS PASS wfh :^) wfh :^) PASS PASSwfh :^) FAIL wfh :^) FAIL wfh :^) wfh :^) FAIL wfh :^) wfh :^) defaultButton: click button=PASS
defaultButton: submit
defaultButton: handledEvent=true
defaultButtonAsInput: click button=PASS

View file

@ -1,4 +1,4 @@
pass Select file...No file selected. text: "pass"
pass pass Select file...No file selected. text: "pass"
hidden: "pass"
button: "pass"
checkbox: "pass"

View file

@ -470,7 +470,6 @@ set(SOURCES
Layout/Box.cpp
Layout/BoxModelMetrics.cpp
Layout/BreakNode.cpp
Layout/ButtonBox.cpp
Layout/CanvasBox.cpp
Layout/CheckBox.cpp
Layout/FlexFormattingContext.cpp
@ -536,7 +535,6 @@ set(SOURCES
Painting/BorderPainting.cpp
Painting/BorderRadiusCornerClipper.cpp
Painting/BordersData.cpp
Painting/ButtonPaintable.cpp
Painting/CanvasPaintable.cpp
Painting/Command.cpp
Painting/CommandExecutorCPU.cpp

View file

@ -37,7 +37,6 @@
#include <LibWeb/Infra/CharacterTypes.h>
#include <LibWeb/Infra/Strings.h>
#include <LibWeb/Layout/BlockContainer.h>
#include <LibWeb/Layout/ButtonBox.h>
#include <LibWeb/Layout/CheckBox.h>
#include <LibWeb/Layout/ImageBox.h>
#include <LibWeb/Layout/RadioButton.h>
@ -100,7 +99,7 @@ JS::GCPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::
return nullptr;
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton)
return heap().allocate_without_realm<Layout::ButtonBox>(document(), *this, move(style));
return heap().allocate_without_realm<Layout::BlockContainer>(document(), this, move(style));
if (type_state() == TypeAttributeState::ImageButton)
return heap().allocate_without_realm<Layout::ImageBox>(document(), *this, move(style), *this);
@ -740,9 +739,12 @@ void HTMLInputElement::create_shadow_tree_if_needed()
case TypeAttributeState::Hidden:
case TypeAttributeState::RadioButton:
case TypeAttributeState::Checkbox:
break;
case TypeAttributeState::Button:
case TypeAttributeState::SubmitButton:
case TypeAttributeState::ResetButton:
create_button_input_shadow_tree();
break;
case TypeAttributeState::ImageButton:
break;
case TypeAttributeState::Color:
@ -779,6 +781,15 @@ void HTMLInputElement::update_shadow_tree()
}
}
void HTMLInputElement::create_button_input_shadow_tree()
{
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
set_shadow_root(shadow_root);
m_text_node = heap().allocate<DOM::Text>(realm(), document(), value());
MUST(shadow_root->append_child(*m_text_node));
}
void HTMLInputElement::create_text_input_shadow_tree()
{
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);

View file

@ -243,6 +243,7 @@ private:
static TypeAttributeState parse_type_attribute(StringView);
void create_shadow_tree_if_needed();
void update_shadow_tree();
void create_button_input_shadow_tree();
void create_text_input_shadow_tree();
void create_color_input_shadow_tree();
void create_file_input_shadow_tree();

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Font/Font.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/Layout/ButtonBox.h>
#include <LibWeb/Painting/ButtonPaintable.h>
namespace Web::Layout {
JS_DEFINE_ALLOCATOR(ButtonBox);
ButtonBox::ButtonBox(DOM::Document& document, HTML::HTMLInputElement& element, NonnullRefPtr<CSS::StyleProperties> style)
: FormAssociatedLabelableNode(document, element, move(style))
{
}
ButtonBox::~ButtonBox() = default;
void ButtonBox::prepare_for_replaced_layout()
{
// For <input type="submit" /> and <input type="button" />, the contents of
// the button does not appear as the contents of the element but as the
// value attribute. This is not the case with <button />, which contains
// its contents normally.
if (is<HTML::HTMLInputElement>(dom_node())) {
set_natural_width(CSSPixels::nearest_value_for(first_available_font().width(static_cast<HTML::HTMLInputElement&>(dom_node()).value())));
set_natural_height(first_available_font().pixel_size_rounded_up());
}
}
JS::GCPtr<Painting::Paintable> ButtonBox::create_paintable() const
{
return Painting::ButtonPaintable::create(*this);
}
}

View file

@ -1,28 +0,0 @@
/*
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/HTML/HTMLInputElement.h>
#include <LibWeb/Layout/FormAssociatedLabelableNode.h>
namespace Web::Layout {
class ButtonBox final : public FormAssociatedLabelableNode {
JS_CELL(ButtonBox, FormAssociatedLabelableNode);
JS_DECLARE_ALLOCATOR(ButtonBox);
public:
ButtonBox(DOM::Document&, HTML::HTMLInputElement&, NonnullRefPtr<CSS::StyleProperties>);
virtual ~ButtonBox() override;
virtual void prepare_for_replaced_layout() override;
private:
virtual JS::GCPtr<Painting::Paintable> create_paintable() const override;
};
}

View file

@ -1,77 +0,0 @@
/*
* Copyright (c) 2018-2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/HTMLImageElement.h>
#include <LibWeb/Layout/ButtonBox.h>
#include <LibWeb/Layout/Label.h>
#include <LibWeb/Painting/ButtonPaintable.h>
namespace Web::Painting {
JS_DEFINE_ALLOCATOR(ButtonPaintable);
JS::NonnullGCPtr<ButtonPaintable> ButtonPaintable::create(Layout::ButtonBox const& layout_box)
{
return layout_box.heap().allocate_without_realm<ButtonPaintable>(layout_box);
}
ButtonPaintable::ButtonPaintable(Layout::ButtonBox const& layout_box)
: LabelablePaintable(layout_box)
{
}
Layout::ButtonBox const& ButtonPaintable::layout_box() const
{
return static_cast<Layout::ButtonBox const&>(layout_node());
}
Layout::ButtonBox& ButtonPaintable::layout_box()
{
return static_cast<Layout::ButtonBox&>(layout_node());
}
void ButtonPaintable::paint(PaintContext& context, PaintPhase phase) const
{
if (!is_visible())
return;
PaintableBox::paint(context, phase);
auto const& dom_node = layout_box().dom_node();
if (is<HTML::HTMLInputElement>(dom_node) && phase == PaintPhase::Foreground) {
auto button_rect = context.enclosing_device_rect(absolute_rect());
auto text_rect = button_rect;
// Apply CSS text-indent property to text rect
// FIXME: The second parameter to to_px() needs to be the block containers own inline-axis inner size:
// https://drafts.csswg.org/css-text-3/#propdef-text-indent
auto text_indent = computed_values().text_indent().to_px(layout_box(), CSSPixels());
text_rect.translate_by(context.rounded_device_pixels(text_indent), 0);
// Apply button pressed state offset
if (being_pressed()) {
auto offset = context.rounded_device_pixels(1);
text_rect.translate_by(offset, offset);
}
// Paint button text clipped to button rect
auto& painter = context.recording_painter();
painter.save();
auto clip_rect = absolute_rect();
clip_rect.translate_by(enclosing_scroll_frame_offset().value_or({}));
painter.add_clip_rect(context.enclosing_device_rect(clip_rect).to_type<int>());
painter.draw_text(
text_rect.to_type<int>(),
static_cast<HTML::HTMLInputElement const&>(dom_node).value(),
layout_box().scaled_font(context.device_pixels_per_css_pixel()),
Gfx::TextAlignment::Center,
computed_values().color());
painter.restore();
}
}
}

View file

@ -1,30 +0,0 @@
/*
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibWeb/Layout/ButtonBox.h>
#include <LibWeb/Painting/LabelablePaintable.h>
namespace Web::Painting {
class ButtonPaintable final : public LabelablePaintable {
JS_CELL(ButtonPaintable, LabelablePaintable);
JS_DECLARE_ALLOCATOR(ButtonPaintable);
public:
static JS::NonnullGCPtr<ButtonPaintable> create(Layout::ButtonBox const&);
virtual void paint(PaintContext&, PaintPhase) const override;
Layout::ButtonBox const& layout_box() const;
Layout::ButtonBox& layout_box();
private:
ButtonPaintable(Layout::ButtonBox const&);
};
}