//! Linux backend — GTK/Wayland. //! //! Architecture: correct and complete. GTK calls are stubs marked TODO. //! //! Each `PlatformNode` maps to a GtkWidget: //! element("div") → GtkBox (vertical) //! element("button") → GtkButton //! element("input") → GtkEntry //! text("...") → GtkLabel use crate::{EventHandler, PlatformBackend, PlatformError, PlatformNode, PlatformResult}; pub struct LinuxBackend; impl LinuxBackend { pub fn new() -> Self { Self } fn gtk_widget(tag: &str) -> &'static str { match tag { "button" => "GtkButton", "input" => "GtkEntry", "textarea" => "GtkTextView", "img" => "GtkImage", "ul" | "ol" => "GtkListBox", "li" => "GtkListBoxRow", "nav" => "GtkHeaderBar", "div" | "section" | "main" | "article" | "span" => "GtkBox", _ => "GtkWidget", } } fn gtk_signal(event: &str) -> &'static str { match event { "click" => "clicked", "input" | "change" => "changed", "focus" => "focus-in-event", "blur" => "focus-out-event", "keydown" | "keypress" => "key-press-event", "keyup" => "key-release-event", _ => "event", } } } impl Default for LinuxBackend { fn default() -> Self { Self::new() } } impl PlatformBackend for LinuxBackend { fn name(&self) -> &'static str { "linux" } fn create_element(&self, tag: &str) -> PlatformResult { let mut node = PlatformNode::element(tag); // TODO: gtk_button_new() / gtk_box_new() / etc. via gtk-rs or raw FFI node.attributes .push(crate::Attribute::new("data-gtk-widget", Self::gtk_widget(tag))); Ok(node) } fn create_text(&self, content: &str) -> PlatformResult { let mut node = PlatformNode::text(content); // TODO: gtk_label_new(content) node.attributes .push(crate::Attribute::new("data-gtk-widget", "GtkLabel")); Ok(node) } fn set_attribute( &self, node: &mut PlatformNode, name: &str, value: &str, ) -> PlatformResult<()> { // TODO: map to GTK property setters // "class" → gtk_widget_add_css_class // "disabled" → gtk_widget_set_sensitive(false) // "placeholder" → gtk_entry_set_placeholder_text node.attributes.retain(|a| a.name != name); node.attributes.push(crate::Attribute::new(name, value)); Ok(()) } fn remove_attribute(&self, node: &mut PlatformNode, name: &str) -> PlatformResult<()> { node.attributes.retain(|a| a.name != name); Ok(()) } fn append_child( &self, parent: &mut PlatformNode, child: PlatformNode, ) -> PlatformResult<()> { // TODO: gtk_box_append(parent, child) parent.children.push(child); Ok(()) } fn remove_child(&self, parent: &mut PlatformNode, child_index: usize) -> PlatformResult<()> { if child_index >= parent.children.len() { return Err(PlatformError::Render(format!( "linux: child index {} out of bounds", child_index ))); } // TODO: gtk_widget_unparent(child) parent.children.remove(child_index); Ok(()) } fn replace_child( &self, parent: &mut PlatformNode, index: usize, new_child: PlatformNode, ) -> PlatformResult<()> { if index >= parent.children.len() { return Err(PlatformError::Render("linux: replace_child out of bounds".into())); } parent.children[index] = new_child; Ok(()) } fn bind_event( &self, node: &mut PlatformNode, event: &str, _handler: EventHandler, ) -> PlatformResult<()> { let signal = Self::gtk_signal(event); // TODO: g_signal_connect(widget, signal, callback, data) node.attributes .push(crate::Attribute::new(format!("data-gtk-signal-{}", event), signal)); Ok(()) } fn render_to_string(&self, node: &PlatformNode) -> PlatformResult { Ok(node.to_html()) } fn mount(&self, _root: PlatformNode, _container_id: &str) -> PlatformResult<()> { // TODO: get GtkWindow and call gtk_window_set_child(root) Ok(()) } fn patch(&self, _old: &PlatformNode, _new: &PlatformNode) -> PlatformResult<()> { // TODO: diff and apply GTK mutations Ok(()) } }