Fully support <input type=color> (#36992)

This change adds a shadow-tree widget for `<input type=color>` elements.
It also involves some changes to the way layout interacts with the DOM,
because currently all `input` and `textarea` elements are rendered as
plain text and their descendants are ignored. This obviously doesn't
work for `<input type={color, date, range, etc}>`.


![image](https://github.com/user-attachments/assets/4f16c3b0-1f79-4095-b19d-1153f5853dd5)

<details><summary>HTML used for the screenshot above</summary>

```html
<input type=color>
```

</details>



Testing: I doubt that this affects WPT tests, because the appearance and
behaviour of the widget is almost entirely unspecified.

---------

Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
Simon Wülker 2025-05-15 19:30:38 +02:00 committed by GitHub
parent f9382fcaa0
commit b100a98e1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 469 additions and 100 deletions

View file

@ -12,9 +12,9 @@ use log::warn;
use servo::ipc_channel::ipc::IpcSender;
use servo::servo_geometry::DeviceIndependentPixel;
use servo::{
AlertResponse, AuthenticationRequest, ConfirmResponse, FilterPattern, PermissionRequest,
PromptResponse, SelectElement, SelectElementOption, SelectElementOptionOrOptgroup,
SimpleDialog,
AlertResponse, AuthenticationRequest, ColorPicker, ConfirmResponse, FilterPattern,
PermissionRequest, PromptResponse, RgbColor, SelectElement, SelectElementOption,
SelectElementOptionOrOptgroup, SimpleDialog,
};
pub enum Dialog {
@ -43,6 +43,11 @@ pub enum Dialog {
maybe_prompt: Option<SelectElement>,
toolbar_offset: Length<f32, DeviceIndependentPixel>,
},
ColorPicker {
current_color: egui::Color32,
maybe_prompt: Option<ColorPicker>,
toolbar_offset: Length<f32, DeviceIndependentPixel>,
},
}
impl Dialog {
@ -119,6 +124,22 @@ impl Dialog {
}
}
pub fn new_color_picker_dialog(
prompt: ColorPicker,
toolbar_offset: Length<f32, DeviceIndependentPixel>,
) -> Self {
let current_color = egui::Color32::from_rgb(
prompt.current_color().red,
prompt.current_color().green,
prompt.current_color().blue,
);
Dialog::ColorPicker {
current_color,
maybe_prompt: Some(prompt),
toolbar_offset,
}
}
pub fn update(&mut self, ctx: &egui::Context) -> bool {
match self {
Dialog::File {
@ -485,6 +506,50 @@ impl Dialog {
maybe_prompt.take().unwrap().submit();
}
is_open
},
Dialog::ColorPicker {
current_color,
maybe_prompt,
toolbar_offset,
} => {
let Some(prompt) = maybe_prompt else {
// Prompt was dismissed, so the dialog should be closed too.
return false;
};
let mut is_open = true;
let mut position = prompt.position();
position.min.y += toolbar_offset.0 as i32;
position.max.y += toolbar_offset.0 as i32;
let area = egui::Area::new(egui::Id::new("select-window"))
.fixed_pos(egui::pos2(position.min.x as f32, position.max.y as f32));
let modal = Modal::new("select_element_picker".into()).area(area);
modal.show(ctx, |ui| {
egui::widgets::color_picker::color_picker_color32(
ui,
current_color,
egui::widgets::color_picker::Alpha::Opaque,
);
ui.add_space(10.);
if ui.button("Dismiss").clicked() {
is_open = false;
prompt.select(None);
}
if ui.button("Select").clicked() {
is_open = false;
let selected_color = RgbColor {
red: current_color.r(),
green: current_color.g(),
blue: current_color.b(),
};
prompt.select(Some(selected_color));
}
});
is_open
},
}