//! macOS backend — AppKit bindings. //! //! Architecture: correct and complete. AppKit calls are stubs marked TODO. //! //! Each `PlatformNode` maps to an NSView: //! element("div") → NSView //! element("button") → NSButton //! element("input") → NSTextField //! text("...") → NSTextField (label mode) use crate::{EventHandler, PlatformBackend, PlatformError, PlatformNode, PlatformResult}; pub struct MacosBackend; impl MacosBackend { pub fn new() -> Self { Self } fn appkit_class(tag: &str) -> &'static str { match tag { "button" => "NSButton", "input" => "NSTextField", "textarea" => "NSTextView", "img" => "NSImageView", "ul" | "ol" => "NSTableView", "nav" => "NSToolbar", _ => "NSView", } } } impl Default for MacosBackend { fn default() -> Self { Self::new() } } impl PlatformBackend for MacosBackend { fn name(&self) -> &'static str { "macos" } fn create_element(&self, tag: &str) -> PlatformResult { let mut node = PlatformNode::element(tag); // TODO: extern "C" { fn el_macos_create_view(class_name: *const c_char) -> usize; } node.attributes .push(crate::Attribute::new("data-appkit-class", Self::appkit_class(tag))); Ok(node) } fn create_text(&self, content: &str) -> PlatformResult { let mut node = PlatformNode::text(content); // TODO: NSTextField in label mode with stringValue = content node.attributes .push(crate::Attribute::new("data-appkit-class", "NSTextField")); Ok(node) } fn set_attribute( &self, node: &mut PlatformNode, name: &str, value: &str, ) -> PlatformResult<()> { // TODO: map to AppKit property setters 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: [parentView addSubview:childView] 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!( "macos: child index {} out of bounds", child_index ))); } // TODO: [childView removeFromSuperview] 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("macos: replace_child out of bounds".into())); } parent.children[index] = new_child; Ok(()) } fn bind_event( &self, node: &mut PlatformNode, event: &str, _handler: EventHandler, ) -> PlatformResult<()> { // TODO: NSButton.target + NSButton.action pattern via FFI node.attributes .push(crate::Attribute::new(format!("data-appkit-event-{}", event), "bound")); Ok(()) } fn render_to_string(&self, node: &PlatformNode) -> PlatformResult { Ok(node.to_html()) } fn mount(&self, _root: PlatformNode, _container_id: &str) -> PlatformResult<()> { // TODO: find NSWindow or NSViewController and set root as contentView Ok(()) } fn patch(&self, _old: &PlatformNode, _new: &PlatformNode) -> PlatformResult<()> { // TODO: diff and apply AppKit mutations Ok(()) } }