windjammer-ui 0.3.6

Cross-platform UI framework for Windjammer (Web, Desktop, Mobile)
Documentation
// VNode - Cross-platform virtual DOM node
// Windjammer wrapper for Rust VNode FFI
//
// This module provides a type-safe way for Windjammer components to construct
// VNodes that can be rendered to:
// - HTML (for web)
// - egui (for desktop)
//
// Usage:
//   use super::vnode::VNode
//   
//   fn render(&self) -> VNode {
//       VNode::div()
//           .add_class("container")
//           .add_style("padding: 16px")
//           .child(VNode::text("Hello, World!".to_string()))
//   }

// Import the Rust FFI functions
use crate::vnode_ffi

/// VNode handle - wraps the Rust VNode registry handle
/// This is an opaque wrapper that tracks the VNode in the Rust registry
@auto
pub struct VNode {
    handle: u64,
}

impl VNode {
    // ========================================================================
    // Element constructors
    // ========================================================================
    
    /// Create a new div element
    pub fn div() -> VNode {
        VNode { handle: vnode_ffi::vnode_element("div") }
    }
    
    /// Create a new span element
    pub fn span() -> VNode {
        VNode { handle: vnode_ffi::vnode_element("span") }
    }
    
    /// Create a new button element
    pub fn button() -> VNode {
        VNode { handle: vnode_ffi::vnode_element("button") }
    }
    
    /// Create a new input element
    pub fn input() -> VNode {
        VNode { handle: vnode_ffi::vnode_element("input") }
    }
    
    /// Create a new text node
    pub fn text(content: string) -> VNode {
        VNode { handle: vnode_ffi::vnode_text(content.as_str()) }
    }
    
    /// Create any HTML element by tag name
    pub fn element(tag: string) -> VNode {
        VNode { handle: vnode_ffi::vnode_element(tag.as_str()) }
    }
    
    // ========================================================================
    // Builder methods (fluent API)
    // Use add_* methods which take owned String for flexibility
    // ========================================================================
    
    /// Add a CSS class (takes owned String)
    pub fn add_class(self, class: string) -> VNode {
        vnode_ffi::vnode_class(self.handle, class.as_str())
        self
    }
    
    /// Add inline style (takes owned String)
    pub fn add_style(self, style: string) -> VNode {
        vnode_ffi::vnode_style(self.handle, style.as_str())
        self
    }
    
    /// Add an attribute (takes owned Strings)
    pub fn add_attr(self, name: string, value: string) -> VNode {
        vnode_ffi::vnode_attr(self.handle, name.as_str(), value.as_str())
        self
    }
    
    /// Add a child VNode
    pub fn child(self, child: VNode) -> VNode {
        vnode_ffi::vnode_child(self.handle, child.handle)
        self
    }
    
    /// Add text content (takes owned String)
    pub fn add_text(self, text: string) -> VNode {
        let text_node = VNode::text(text)
        vnode_ffi::vnode_child(self.handle, text_node.handle)
        self
    }
    
    // ========================================================================
    // Common attribute shortcuts
    // ========================================================================
    
    /// Set id attribute
    pub fn set_id(self, id: string) -> VNode {
        vnode_ffi::vnode_attr(self.handle, "id", id.as_str())
        self
    }
    
    /// Set placeholder (for inputs)
    pub fn set_placeholder(self, text: string) -> VNode {
        vnode_ffi::vnode_attr(self.handle, "placeholder", text.as_str())
        self
    }
    
    /// Set type attribute (for inputs/buttons)
    pub fn set_type(self, t: string) -> VNode {
        vnode_ffi::vnode_attr(self.handle, "type", t.as_str())
        self
    }
    
    /// Set value attribute
    pub fn set_value(self, v: string) -> VNode {
        vnode_ffi::vnode_attr(self.handle, "value", v.as_str())
        self
    }
    
    /// Set disabled attribute
    pub fn set_disabled(self, d: bool) -> VNode {
        if d {
            vnode_ffi::vnode_attr(self.handle, "disabled", "true")
        }
        self
    }
    
    // ========================================================================
    // Access to raw handle (for advanced usage)
    // ========================================================================
    
    /// Get the raw handle (for interop with Rust code)
    pub fn raw_handle(&self) -> u64 {
        self.handle
    }
}

// ============================================================================
// Convenience functions for common UI patterns
// ============================================================================

/// Create a container with padding
pub fn container() -> VNode {
    VNode::div()
        .add_class("wj-container".to_string())
        .add_style("padding: 16px".to_string())
}

/// Create a flex row
pub fn row() -> VNode {
    VNode::div().add_style("display: flex; flex-direction: row; gap: 8px".to_string())
}

/// Create a flex column
pub fn column() -> VNode {
    VNode::div().add_style("display: flex; flex-direction: column; gap: 8px".to_string())
}

/// Create a spacer element
pub fn spacer() -> VNode {
    VNode::div().add_style("flex: 1".to_string())
}

/// Create a horizontal divider
pub fn divider() -> VNode {
    VNode::element("hr".to_string())
        .add_style("border: 0; border-top: 1px solid #333; margin: 8px 0".to_string())
}