use rdom_core::NodeId;
use super::TuiAccessorsMut;
use super::helpers::{read_scroll_x, read_scroll_y, set_select_value, write_boolean_attribute};
use crate::node::install_text_content;
use crate::runtime::smooth_scroll::{ScrollIntoViewOptions, ScrollToOptions, perform_scroll};
use crate::{Result, TuiExt};
impl<'a> TuiAccessorsMut<'a> for rdom_core::NodeMut<'a, TuiExt> {
fn set_value(&mut self, value: impl Into<String>) -> Result<()> {
let tag = match self.as_ref().tag_name() {
Some(t) => t.to_string(),
None => return Ok(()),
};
let new_value = value.into();
let id = self.id();
let dom = self.dom_mut();
match tag.as_str() {
"input" => {
if crate::node::is_text_input(dom, id) {
crate::runtime::builtins::input::set_value(dom, id, &new_value);
} else {
crate::runtime::builtins::input::note_default_value(dom, id);
dom.set_attribute(id, "value", &new_value)?;
}
Ok(())
}
"textarea" => {
crate::runtime::builtins::input::note_default_value(dom, id);
crate::runtime::builtins::input::clear_user_edited(dom, id);
install_text_content(dom, id, &new_value)
}
"select" => set_select_value(dom, id, &new_value),
_ => Ok(()),
}
}
fn set_checked(&mut self, value: bool) -> Result<()> {
if self.as_ref().tag_name() != Some("input") {
return Ok(());
}
let id = self.id();
if crate::runtime::builtins::toggle::is_toggle(self.as_ref().dom(), id) {
crate::runtime::builtins::toggle::note_default_checked(self.dom_mut(), id);
}
write_boolean_attribute(self, "checked", value)
}
fn set_default_value(&mut self, value: impl Into<String>) -> Result<()> {
let value = value.into();
let id = self.id();
let dom = self.dom_mut();
if crate::runtime::builtins::input::keeps_default_value(dom, id) {
if let Some(ext) = dom.node_mut(id).ext_mut() {
ext.default_value = Some(value);
}
return Ok(());
}
if dom.node(id).tag_name() == Some("input") {
dom.set_attribute(id, "value", &value)?;
}
Ok(())
}
fn set_default_checked(&mut self, value: bool) -> Result<()> {
let id = self.id();
let dom = self.dom_mut();
if crate::runtime::builtins::toggle::is_toggle(dom, id)
&& let Some(ext) = dom.node_mut(id).ext_mut()
{
ext.default_checked = Some(value);
}
Ok(())
}
fn set_default_selected(&mut self, value: bool) -> Result<()> {
let id = self.id();
let dom = self.dom_mut();
if dom.node(id).tag_name() == Some("option")
&& let Some(ext) = dom.node_mut(id).ext_mut()
{
ext.default_selected = Some(value);
}
Ok(())
}
fn set_indeterminate(&mut self, value: bool) -> Result<()> {
if self.as_ref().tag_name() != Some("input") {
return Ok(());
}
write_boolean_attribute(self, "indeterminate", value)
}
fn set_disabled(&mut self, value: bool) -> Result<()> {
if !matches!(
self.as_ref().tag_name(),
Some("button" | "input" | "select" | "textarea" | "option" | "optgroup" | "fieldset")
) {
return Ok(());
}
write_boolean_attribute(self, "disabled", value)
}
fn set_read_only(&mut self, value: bool) -> Result<()> {
if !matches!(self.as_ref().tag_name(), Some("input" | "textarea")) {
return Ok(());
}
write_boolean_attribute(self, "readonly", value)
}
fn set_inert(&mut self, value: bool) -> Result<()> {
if self.as_ref().tag_name().is_none() {
return Ok(());
}
write_boolean_attribute(self, "inert", value)
}
fn focus(&mut self) {
let id = self.id();
let dom = self.dom_mut();
if !crate::runtime::focus::tabindex::is_focusable(dom, id) {
return;
}
crate::runtime::focus::focus_node(dom, Some(id));
}
fn blur(&mut self) {
let id = self.id();
let dom = self.dom_mut();
if dom.focused() != Some(id) {
return;
}
crate::runtime::focus::focus_node(dom, None);
}
fn click(&mut self) {
use rdom_core::{EventDetail, MouseButton, MouseDetail};
let id = self.id();
let dom = self.dom_mut();
let mut event = rdom_core::Event::new("click").with_synthetic(true);
event.detail = EventDetail::Mouse(MouseDetail::new(MouseButton::Left, 0, 0));
let mut tui = crate::TuiEvent { event };
crate::tui_event::dispatch_to_live(dom, id, &mut tui);
}
fn set_scroll_top(&mut self, value: i32) -> Result<()> {
self.scroll_with(ScrollToOptions {
top: Some(value),
..ScrollToOptions::default()
})
}
fn set_scroll_left(&mut self, value: i32) -> Result<()> {
self.scroll_with(ScrollToOptions {
left: Some(value),
..ScrollToOptions::default()
})
}
fn scroll_to(&mut self, x: i32, y: i32) -> Result<()> {
self.scroll_with(ScrollToOptions {
left: Some(x),
top: Some(y),
..ScrollToOptions::default()
})
}
fn scroll_by(&mut self, dx: i32, dy: i32) -> Result<()> {
self.scroll_by_with(ScrollToOptions {
left: Some(dx),
top: Some(dy),
..ScrollToOptions::default()
})
}
fn scroll_with(&mut self, options: ScrollToOptions) -> Result<()> {
let id = self.id();
let dom = self.dom_mut();
let x = options.left.unwrap_or_else(|| read_scroll_x(dom, id));
let y = options.top.unwrap_or_else(|| read_scroll_y(dom, id));
perform_scroll(dom, id, x, y, options.behavior);
Ok(())
}
fn scroll_by_with(&mut self, options: ScrollToOptions) -> Result<()> {
let id = self.id();
let dom = self.dom_mut();
let x = read_scroll_x(dom, id).saturating_add(options.left.unwrap_or(0));
let y = read_scroll_y(dom, id).saturating_add(options.top.unwrap_or(0));
perform_scroll(dom, id, x, y, options.behavior);
Ok(())
}
fn scroll_into_view(&mut self) -> Result<()> {
self.scroll_into_view_with(ScrollIntoViewOptions::new())
}
fn scroll_into_view_with(&mut self, options: ScrollIntoViewOptions) -> Result<()> {
let id = self.id();
crate::runtime::scrollbar::scroll_element_into_view(self.dom_mut(), id, options);
Ok(())
}
fn style_mut(&mut self) -> Option<crate::cssom::StyleDeclarationMut<'_>> {
self.as_ref().tag_name()?;
let id = self.id();
let dom = self.dom_mut();
Some(crate::cssom::StyleDeclarationMut::new(dom.node_mut(id)))
}
fn set_details_open(&mut self, value: bool) -> Result<()> {
if self.as_ref().tag_name() != Some("details") {
return Ok(());
}
write_boolean_attribute(self, "open", value)
}
fn set_dialog_return_value(&mut self, value: impl Into<String>) -> Result<()> {
if self.as_ref().tag_name() != Some("dialog") {
return Ok(());
}
let id = self.id();
let dom = self.dom_mut();
let s = value.into();
crate::runtime::builtins::dialog::set_return_value(dom, id, &s);
Ok(())
}
fn form_request_submit(
&mut self,
submitter: Option<NodeId>,
) -> Result<crate::runtime::builtins::form::SubmitOutcome> {
if self.as_ref().tag_name() != Some("form") {
return Ok(crate::runtime::builtins::form::SubmitOutcome::NotAForm);
}
let id = self.id();
let dom = self.dom_mut();
crate::runtime::builtins::form::request_submit(dom, id, submitter)
}
fn check_validity(&mut self) -> bool {
let id = self.id();
crate::runtime::builtins::validation::check_validity(self.dom_mut(), id)
}
fn report_validity(&mut self) -> bool {
let id = self.id();
crate::runtime::builtins::validation::report_validity(self.dom_mut(), id)
}
fn set_custom_validity(&mut self, message: &str) -> Result<()> {
if !self
.as_ref()
.tag_name()
.is_some_and(super::read_ref::has_validity)
{
return Ok(());
}
let id = self.id();
crate::runtime::builtins::validation::set_custom_validity(self.dom_mut(), id, message);
Ok(())
}
}