Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibWeb/HTMLInputElement: Improve appearance of color picker #21438

Merged
merged 3 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions Userland/Libraries/LibWeb/CSS/Default.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ label {
}

/* FIXME: This is a temporary hack until we can render a native-looking frame for these. */
input, textarea {
input:not([type=submit], input[type=button], input[type=reset], input[type=color], input[type=checkbox], input[type=radio]), textarea {
border: 1px solid ButtonBorder;
min-width: 80px;
min-height: 16px;
Expand All @@ -44,14 +44,6 @@ textarea {
height: attr(rows lh, 2lh);
}

input[type=submit], input[type=button], input[type=reset], input[type=checkbox], input[type=radio] {
border: none;
min-width: unset;
min-height: unset;
width: unset;
cursor: unset;
}

input::placeholder {
color: GrayText;
}
Expand Down
59 changes: 55 additions & 4 deletions Userland/Libraries/LibWeb/HTML/HTMLInputElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ void HTMLInputElement::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_text_node);
visitor.visit(m_placeholder_element);
visitor.visit(m_placeholder_text_node);
visitor.visit(m_color_well_element);
visitor.visit(m_legacy_pre_activation_behavior_checked_element_in_group.ptr());
visitor.visit(m_selected_files);
}
Expand All @@ -70,7 +71,7 @@ JS::GCPtr<Layout::Node> HTMLInputElement::create_layout_node(NonnullRefPtr<CSS::
if (type_state() == TypeAttributeState::Hidden)
return nullptr;

if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::FileUpload || type_state() == TypeAttributeState::Color)
if (type_state() == TypeAttributeState::SubmitButton || type_state() == TypeAttributeState::Button || type_state() == TypeAttributeState::ResetButton || type_state() == TypeAttributeState::FileUpload)
return heap().allocate_without_realm<Layout::ButtonBox>(document(), *this, move(style));

if (type_state() == TypeAttributeState::Checkbox)
Expand Down Expand Up @@ -317,6 +318,9 @@ void HTMLInputElement::did_pick_color(Optional<Color> picked_color)
m_value = value_sanitization_algorithm(picked_color.value().to_deprecated_string_without_alpha());
m_dirty_value = true;

if (m_color_well_element)
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value));

// the user agent must queue an element task on the user interaction task source
queue_an_element_task(HTML::Task::Source::UserInteraction, [this] {
// given the input element to fire an event named input at the input element, with the bubbles and composed attributes initialized to true
Expand Down Expand Up @@ -387,7 +391,9 @@ WebIDL::ExceptionOr<void> HTMLInputElement::set_value(String const& value)
auto old_value = move(m_value);

// 2. Set the element's value to the new value.
// NOTE: This is done as part of step 4 below.
// NOTE: For the TextNode this is done as part of step 4 below.
if (type_state() == TypeAttributeState::Color && m_color_well_element)
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value));

// 3. Set the element's dirty value flag to true.
m_dirty_value = true;
Expand Down Expand Up @@ -507,20 +513,26 @@ void HTMLInputElement::create_shadow_tree_if_needed()
if (shadow_root_internal())
return;

// FIXME: This could be better factored. Everything except the below types becomes a text input.
switch (type_state()) {
case TypeAttributeState::RadioButton:
case TypeAttributeState::Checkbox:
case TypeAttributeState::Button:
case TypeAttributeState::SubmitButton:
case TypeAttributeState::ResetButton:
case TypeAttributeState::ImageButton:
break;
case TypeAttributeState::Color:
return;
create_color_input_shadow_tree();
break;
// FIXME: This could be better factored. Everything except the above types becomes a text input.
default:
create_text_input_shadow_tree();
break;
}
}

void HTMLInputElement::create_text_input_shadow_tree()
{
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);
auto initial_value = m_value;
auto element = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
Expand Down Expand Up @@ -568,6 +580,35 @@ void HTMLInputElement::create_shadow_tree_if_needed()
set_shadow_root(shadow_root);
}

void HTMLInputElement::create_color_input_shadow_tree()
{
auto shadow_root = heap().allocate<DOM::ShadowRoot>(realm(), document(), *this, Bindings::ShadowRootMode::Closed);

auto color = value_sanitization_algorithm(m_value);

auto border = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
MUST(border->set_attribute(HTML::AttributeNames::style, R"~~~(
width: fit-content;
height: fit-content;
padding: 4px;
border: 1px solid ButtonBorder;
background-color: ButtonFace;
)~~~"_string));

m_color_well_element = DOM::create_element(document(), HTML::TagNames::div, Namespace::HTML).release_value_but_fixme_should_propagate_errors();
MUST(m_color_well_element->set_attribute(HTML::AttributeNames::style, R"~~~(
width: 24px;
height: 24px;
border: 1px solid ButtonBorder;
box-sizing: border-box;
)~~~"_string));
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, color));

MUST(border->append_child(*m_color_well_element));
MUST(shadow_root->append_child(border));
set_shadow_root(shadow_root);
}

void HTMLInputElement::did_receive_focus()
{
auto* browsing_context = document().browsing_context();
Expand Down Expand Up @@ -611,11 +652,17 @@ void HTMLInputElement::attribute_changed(FlyString const& name, Optional<Depreca
if (!m_dirty_value) {
m_value = DeprecatedString::empty();
update_placeholder_visibility();

if (type_state() == TypeAttributeState::Color && m_color_well_element)
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value));
circl-lastname marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
if (!m_dirty_value) {
m_value = value_sanitization_algorithm(*value);
update_placeholder_visibility();

if (type_state() == TypeAttributeState::Color && m_color_well_element)
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value));
circl-lastname marked this conversation as resolved.
Show resolved Hide resolved
}
}
} else if (name == HTML::AttributeNames::placeholder) {
Expand Down Expand Up @@ -950,10 +997,14 @@ void HTMLInputElement::reset_algorithm()

// and then invoke the value sanitization algorithm, if the type attribute's current state defines one.
m_value = value_sanitization_algorithm(m_value);

if (m_text_node) {
m_text_node->set_data(MUST(String::from_deprecated_string(m_value)));
update_placeholder_visibility();
}

if (type_state() == TypeAttributeState::Color && m_color_well_element)
MUST(m_color_well_element->style_for_bindings()->set_property(CSS::PropertyID::BackgroundColor, m_value));
circl-lastname marked this conversation as resolved.
Show resolved Hide resolved
}

void HTMLInputElement::form_associated_element_was_inserted()
Expand Down
3 changes: 3 additions & 0 deletions Userland/Libraries/LibWeb/HTML/HTMLInputElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ class HTMLInputElement final

static TypeAttributeState parse_type_attribute(StringView);
void create_shadow_tree_if_needed();
void create_text_input_shadow_tree();
void create_color_input_shadow_tree();
WebIDL::ExceptionOr<void> run_input_activation_behavior();
void set_checked_within_group();

Expand All @@ -179,6 +181,7 @@ class HTMLInputElement final
JS::GCPtr<DOM::Text> m_placeholder_text_node;

JS::GCPtr<DOM::Element> m_inner_text_element;
JS::GCPtr<DOM::Element> m_color_well_element;
JS::GCPtr<DOM::Text> m_text_node;
bool m_checked { false };

Expand Down
Loading