godoru 0.1.0

UI Framework for Rust using Godot
use super::*;

pub(crate) struct InteractiveNode<A = crate::NoAction> {
    pub(crate) object: GodotObject,
    pub(crate) component: Component,
    pub(crate) class: Option<String>,
    pub(crate) localStyle: Style,
    pub(crate) interactions: crate::InteractionEvents<A>,
    pub(crate) textValue: Option<String>,
    pub(crate) checkedValue: Option<bool>,
    pub(crate) onChange: Option<std::sync::Arc<dyn Fn(String) -> A>>,
    pub(crate) onSubmit: Option<std::sync::Arc<dyn Fn(String) -> A>>,
    pub(crate) onToggle: Option<std::sync::Arc<dyn Fn(bool) -> A>>,
    pub(crate) pressed: bool,
    pub(crate) hovered: bool,
    pub(crate) focused: bool,
}

impl<A: Clone + 'static> GodotUiTree<A> {
    pub(crate) fn pollActions(&mut self, handler: &mut dyn FnMut(A)) -> GodoruResult<()> {
        for node in &mut self.interactiveNodes {
            match node.component {
                Component::Button => {
                    let pressed = self.bridge.isPressed(node.object)?;
                    let hovered = self.bridge.isHovered(node.object)?;
                    let focused = self.bridge.hasFocus(node.object)?;
                    if pressed && !node.pressed {
                        if let Some(action) = node.interactions.pointerDown.clone() {
                            handler(action);
                        }
                    }
                    if !pressed && node.pressed {
                        if let Some(action) = node.interactions.pointerUp.clone() {
                            handler(action);
                        }
                        if let Some(action) = node.interactions.click.clone() {
                            handler(action);
                        }
                    }
                    if hovered && !node.hovered {
                        if let Some(action) = node.interactions.hover.clone() {
                            handler(action);
                        }
                    }
                    if !hovered && node.hovered {
                        if let Some(action) = node.interactions.unhover.clone() {
                            handler(action);
                        }
                    }
                    if focused && !node.focused {
                        if let Some(action) = node.interactions.focus.clone() {
                            handler(action);
                        }
                    }
                    if !focused && node.focused {
                        if let Some(action) = node.interactions.blur.clone() {
                            handler(action);
                        }
                    }
                    node.pressed = pressed;
                    node.hovered = hovered;
                    node.focused = focused;
                    let _ = (&node.class, &node.localStyle);
                }
                Component::TextInput => {
                    let value = self.bridge.lineEditGetText(node.object)?;
                    if node.textValue.as_deref() != Some(value.as_str()) {
                        node.textValue = Some(value.clone());
                        if let Some(action) = node.onChange.as_ref() {
                            handler(action(value));
                        }
                    }
                    let focused = self.bridge.hasFocus(node.object)?;
                    if focused && !node.focused {
                        if let Some(action) = node.interactions.focus.clone() {
                            handler(action);
                        }
                    }
                    if !focused && node.focused {
                        if let Some(action) = node.interactions.blur.clone() {
                            handler(action);
                        }
                    }
                    node.focused = focused;
                    let _ = (&node.class, &node.localStyle, &node.onSubmit);
                }
                Component::Checkbox => {
                    let checked = self.bridge.isPressed(node.object)?;
                    if node.checkedValue != Some(checked) {
                        node.checkedValue = Some(checked);
                        if let Some(action) = node.onToggle.as_ref() {
                            handler(action(checked));
                        }
                    }
                    let focused = self.bridge.hasFocus(node.object)?;
                    if focused && !node.focused {
                        if let Some(action) = node.interactions.focus.clone() {
                            handler(action);
                        }
                    }
                    if !focused && node.focused {
                        if let Some(action) = node.interactions.blur.clone() {
                            handler(action);
                        }
                    }
                    node.focused = focused;
                    let _ = (&node.class, &node.localStyle);
                }
                _ => {
                    let focused = self.bridge.hasFocus(node.object)?;
                    if focused && !node.focused {
                        if let Some(action) = node.interactions.focus.clone() {
                            handler(action);
                        }
                    }
                    if !focused && node.focused {
                        if let Some(action) = node.interactions.blur.clone() {
                            handler(action);
                        }
                    }
                    node.focused = focused;
                    let _ = (&node.class, &node.localStyle);
                }
            }
        }
        Ok(())
    }
}

impl GodotBridge {
    pub(crate) fn isPressed(&self, object: GodotObject) -> GodoruResult<bool> {
        self.callBool(self.methods.baseButtonIsPressed, object, &[])
    }

    pub(crate) fn isHovered(&self, object: GodotObject) -> GodoruResult<bool> {
        self.callBool(self.methods.baseButtonIsHovered, object, &[])
    }

    pub(crate) fn hasFocus(&self, object: GodotObject) -> GodoruResult<bool> {
        let ignoreHiddenFocus = boolArg(false);
        let args = [&ignoreHiddenFocus as *const u8 as GDExtensionConstTypePtr];
        self.callBool(self.methods.controlHasFocus, object, &args)
    }

    pub(crate) fn enableLocalInput(&self, object: GodotObject) -> GodoruResult<()> {
        let focusMode = FOCUS_ALL;
        let args = [&focusMode as *const i64 as GDExtensionConstTypePtr];
        self.callVoid(self.methods.controlSetFocusMode, object, &args)?;
        let mouseFilter = MOUSE_FILTER_STOP;
        let args = [&mouseFilter as *const i64 as GDExtensionConstTypePtr];
        self.callVoid(self.methods.controlSetMouseFilter, object, &args)
    }

    pub(crate) fn setCursor(&self, object: GodotObject, cursor: &CursorIcon) -> GodoruResult<()> {
        let cursor: i64 = match cursor {
            CursorIcon::Default => 0,
            CursorIcon::Pointer => 2,
            CursorIcon::Text => 1,
            CursorIcon::Grab => 6,
            CursorIcon::Grabbing => 7,
        };
        let args = [&cursor as *const i64 as GDExtensionConstTypePtr];
        self.callVoid(self.methods.controlSetDefaultCursorShape, object, &args)
    }

    pub(crate) fn releaseFocus(&self, object: GodotObject) -> GodoruResult<()> {
        self.callVoid(self.methods.controlReleaseFocus, object, &[])
    }
}