use crate::{
events::focus_mgr::{FocusHandle, FocusType},
prelude::*,
};
#[derive(Default, Query, Declare)]
pub struct FocusNode {
#[declare(default, builtin)]
pub tab_index: i16,
#[declare(default, builtin)]
pub auto_focus: bool,
}
impl ComposeChild for FocusNode {
type Child = Widget;
fn compose_child(
this: impl StateWriter<Value = Self>,
mut child: Self::Child,
) -> impl WidgetBuilder {
fn_widget! {
let tree = ctx!().tree.borrow();
let node = child.id().assert_get(&tree.arena);
let has_focus_node = node.contain_type::<FocusNode>();
if !has_focus_node {
let subject = node.query_most_outside(|l: &LifecycleListener| l.lifecycle_stream());
drop(tree);
let subject = if let Some(subject) = subject {
subject
} else {
let listener = LifecycleListener::default();
let subject = listener.lifecycle_stream();
child = child.attach_data(listener, ctx!());
subject
};
fn subscribe_fn(this: impl StateReader<Value = FocusNode>)
-> impl FnMut(&'_ mut AllLifecycle) + 'static
{
move |e| match e {
AllLifecycle::Mounted(e) => {
let auto_focus = this.read().auto_focus;
e.window().add_focus_node(e.id, auto_focus, FocusType::Node)
}
AllLifecycle::PerformedLayout(_) => {}
AllLifecycle::Disposed(e) => e.window().remove_focus_node(e.id, FocusType::Node),
}
}
let h = subject
.subscribe(subscribe_fn(this.clone_reader()))
.unsubscribe_when_dropped();
child = child.attach_state_data(this, ctx!()).attach_anonymous_data(h, ctx!());
}
child
}
}
}
#[derive(Declare, Query)]
pub struct RequestFocus {
#[declare(default)]
handle: Option<FocusHandle>,
}
impl ComposeChild for RequestFocus {
type Child = Widget;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> impl WidgetBuilder {
fn_widget! {
@$child {
on_mounted: move |e| {
let handle = e.window().focus_mgr.borrow().focus_handle(e.id);
$this.silent().handle = Some(handle);
}
}
.widget_build(ctx!())
.attach_state_data(this, ctx!())
}
}
}
impl RequestFocus {
pub fn request_focus(&self) {
if let Some(h) = self.handle.as_ref() {
h.request_focus();
}
}
pub fn unfocus(&self) {
if let Some(h) = self.handle.as_ref() {
h.unfocus();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{reset_test_env, test_helper::*};
#[test]
fn dynamic_focus_node() {
reset_test_env!();
let widget = fn_widget! {
@FocusNode {
tab_index: 0i16, auto_focus: false,
@FocusNode {
tab_index: 0i16, auto_focus: false,
@FocusNode {
tab_index: 0i16, auto_focus: false,
@MockBox {
size: Size::default(),
}
}
}
}
};
let wnd = TestWindow::new(widget);
let tree = wnd.widget_tree.borrow();
let id = tree.root();
let node = id.get(&tree.arena).unwrap();
let mut cnt = 0;
node.query_type_inside_first(|_: &FocusNode| {
cnt += 1;
true
});
assert!(cnt == 1);
}
}