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, &[])
}
}