Skip to content

Commit

Permalink
feat(core): 🎸 support query WriteRef
Browse files Browse the repository at this point in the history
  • Loading branch information
M-Adoo committed Jun 12, 2024
1 parent ccd3f97 commit d98aa6e
Show file tree
Hide file tree
Showing 21 changed files with 499 additions and 398 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ Please only add new entries below the [Unreleased](#unreleased---releasedate) he

## [@Unreleased] - @ReleaseDate

### Features

- **core**: Added support to query a `WriteRef` from a state, enabling users to modify the state after attaching it to a widget. (#pr @M-Adoo)

### Changed

- **core**: Render widgets no longer need to implement the `Query` trait. Data can only be queried if it's a state or wrapped with `Queryable`. (#pr @M-Adoo)
Expand Down
4 changes: 1 addition & 3 deletions core/src/builtin_widgets/focus_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,11 @@ mod tests {
let id = tree.content_root();
let node = id.get(&tree.arena).unwrap();
let mut cnt = 0;
node.query_type_inside_first(|b: &MixBuiltin| {
node.query_all_iter::<MixBuiltin>().for_each(|b| {
if b.contain_flag(BuiltinFlags::Focus) {
cnt += 1;
}
true
});

assert_eq!(cnt, 1);
}
}
27 changes: 15 additions & 12 deletions core/src/builtin_widgets/mix_builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,28 +349,31 @@ fn life_fn_once_to_fn_mut(
impl ComposeChild for MixBuiltin {
type Child = Widget;
#[inline]
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> impl WidgetBuilder {
fn compose_child(
this: impl StateWriter<Value = Self>, mut child: Self::Child,
) -> impl WidgetBuilder {
move |ctx: &BuildCtx| match this.try_into_value() {
Ok(this) => {
let mut this = Some(this);
child
if let Some(m) = child
.id()
.assert_get(&ctx.tree.borrow().arena)
.query_most_outside(|m: &MixBuiltin| {
let this = this.take().unwrap();
if !m.contain_flag(BuiltinFlags::Focus) && this.contain_flag(BuiltinFlags::Focus) {
this.callbacks_for_focus_node();
}
m.merge(this)
});
.query_ref::<MixBuiltin>()
{
let this = unsafe { this.take().unwrap_unchecked() };
if !m.contain_flag(BuiltinFlags::Focus) && this.contain_flag(BuiltinFlags::Focus) {
this.callbacks_for_focus_node();
}
m.merge(this);
}
// We do not use an else branch here, due to the borrow conflict of the `ctx`.
if let Some(this) = this {
if this.contain_flag(BuiltinFlags::Focus) {
this.callbacks_for_focus_node();
}
child.attach_data(Queryable(this), ctx)
} else {
child
child = child.attach_data(Queryable(this), ctx);
}
child
}
Err(this) => {
if this.read().contain_flag(BuiltinFlags::Focus) {
Expand Down
12 changes: 7 additions & 5 deletions core/src/context/build_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,13 @@ impl<'a> BuildCtx<'a> {

let arena = &self.tree.borrow().arena;
p.ancestors(arena).any(|p| {
p.assert_get(arena)
.query_type_inside_first(|t: &Sc<Theme>| {
themes.push(t.clone());
matches!(t.deref(), Theme::Inherit(_))
});
for t in p.assert_get(arena).query_all_iter::<Sc<Theme>>() {
themes.push(t.clone());
if matches!(&**t, Theme::Full(_)) {
break;
}
}

matches!(themes.last().map(Sc::deref), Some(Theme::Full(_)))
});
themes
Expand Down
4 changes: 2 additions & 2 deletions core/src/context/widget_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ pub trait WidgetCtx {
pub(crate) trait WidgetCtxImpl {
fn id(&self) -> WidgetId;

// todo: return sc instead of rc
fn current_wnd(&self) -> Rc<Window>;

#[inline]
Expand Down Expand Up @@ -201,7 +200,8 @@ impl<T: WidgetCtxImpl> WidgetCtx for T {
) -> Option<R> {
self.with_tree(|tree| {
id.assert_get(&tree.arena)
.query_most_outside(callback)
.query_ref::<W>()
.map(|r| callback(&r))
})
}

Expand Down
53 changes: 21 additions & 32 deletions core/src/data_widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Widget {
///
/// User can query the state or its value type.
pub fn try_unwrap_state_and_attach<D: Any>(
self, data: impl StateReader<Value = D>, ctx: &BuildCtx,
self, data: impl StateWriter<Value = D>, ctx: &BuildCtx,
) -> Widget {
match data.try_into_value() {
Ok(data) => self.attach_data(Queryable(data), ctx),
Expand All @@ -70,37 +70,32 @@ impl<D: Query> RenderProxy for DataAttacher<D> {
where
Self: 'r;

fn proxy<'r>(&'r self) -> Self::Target<'r> { self.render.as_ref() }
fn proxy(&self) -> Self::Target<'_> { self.render.as_ref() }
}

impl<D: Query> Query for DataAttacher<D> {
fn query_inside_first(
&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool,
) -> bool {
self.render.query_inside_first(type_id, callback)
&& self.data.query_inside_first(type_id, callback)
fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> {
let mut types = self.render.query_all(type_id);
types.extend(self.data.query_all(type_id));
types
}

fn query_outside_first(
&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool,
) -> bool {
self.data.query_outside_first(type_id, callback)
&& self.render.query_outside_first(type_id, callback)
fn query(&self, type_id: TypeId) -> Option<QueryHandle> {
self
.data
.query(type_id)
.or_else(|| self.render.query(type_id))
}
}

impl Query for AnonymousAttacher {
fn query_inside_first(
&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool,
) -> bool {
self.render.query_inside_first(type_id, callback)
#[inline]
fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> {
self.render.query_all(type_id)
}

fn query_outside_first(
&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool,
) -> bool {
self.render.query_outside_first(type_id, callback)
}
#[inline]
fn query(&self, type_id: TypeId) -> Option<QueryHandle> { self.render.query(type_id) }
}

impl RenderProxy for AnonymousAttacher {
Expand All @@ -110,21 +105,15 @@ impl RenderProxy for AnonymousAttacher {
where
Self: 'r;

fn proxy<'r>(&'r self) -> Self::Target<'r> { self.render.as_ref() }
fn proxy(&self) -> Self::Target<'_> { self.render.as_ref() }
}

impl<T: Any> Query for Queryable<T> {
#[inline]
fn query_inside_first(
&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool,
) -> bool {
self.query_outside_first(type_id, callback)
fn query_all(&self, type_id: TypeId) -> smallvec::SmallVec<[QueryHandle; 1]> {
self.query(type_id).into_iter().collect()
}

#[inline]
fn query_outside_first(
&self, type_id: TypeId, callback: &mut dyn FnMut(&dyn Any) -> bool,
) -> bool {
if type_id == TypeId::of::<T>() { callback(&self.0) } else { true }
fn query(&self, type_id: TypeId) -> Option<QueryHandle> {
(type_id == self.0.type_id()).then(|| QueryHandle::new(&self.0))
}
}
11 changes: 3 additions & 8 deletions core/src/events/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,9 @@ impl Dispatcher {

let nearest_focus = self.pointer_down_uid.and_then(|wid| {
wid.ancestors(&tree.arena).find(|id| {
let mut is_focus_node = false;
if let Some(w) = id.get(&tree.arena) {
w.query_type_outside_first(|m: &MixBuiltin| {
is_focus_node |= m.contain_flag(BuiltinFlags::Focus);
!is_focus_node
});
}
is_focus_node
id.get(&tree.arena)
.and_then(|w| w.query_ref::<MixBuiltin>())
.map_or(false, |m| m.contain_flag(BuiltinFlags::Focus))
})
});
if let Some(focus_id) = nearest_focus {
Expand Down
34 changes: 15 additions & 19 deletions core/src/events/focus_mgr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,16 +339,11 @@ impl FocusManager {
.find_map(|wid| self.node_ids.get(&wid).copied())?;

self.scope_list(node_id).find(|id| {
let mut has_ignore = false;
self.get(*id).and_then(|n| n.wid).map(|wid| {
wid
.get(arena)?
.query_most_inside(|s: &FocusScope| {
has_ignore = s.skip_descendants;
!has_ignore
})
});
has_ignore
self
.get(*id)
.and_then(|n| n.wid)
.and_then(|wid| wid.get(arena)?.query_ref::<FocusScope>())
.map_or(false, |s| s.skip_descendants)
})
}

Expand All @@ -357,21 +352,22 @@ impl FocusManager {
let tree = wnd.widget_tree.borrow();
scope_id
.and_then(|id| id.get(&tree.arena))
.and_then(|r| r.query_most_inside(|s: &FocusScope| s.clone()))
.and_then(|r| r.query_ref::<FocusScope>().map(|s| s.clone()))
.unwrap_or_default()
}

fn tab_index(&self, node_id: NodeId) -> i16 {
let wnd = self.window();

let get_index = || {
let wid = self.get(node_id)?.wid?;
let tree = wnd.widget_tree.borrow();
let r = wid.get(&tree.arena)?;
r.query_most_outside(|s: &MixBuiltin| s.get_tab_index())
};

get_index().unwrap_or(0)
self
.get(node_id)
.and_then(|n| n.wid)
.and_then(|wid| {
let tree = wnd.widget_tree.borrow();
let m = wid.get(&tree.arena)?.query_ref::<MixBuiltin>()?;
Some(m.get_tab_index())
})
.unwrap_or_default()
}

fn insert_node(&mut self, parent: NodeId, node_id: NodeId, wid: WidgetId, arena: &TreeArena) {
Expand Down
3 changes: 3 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod widget_children;
pub mod window;
pub use rxrust;
pub mod overlay;
pub mod query;

pub mod prelude {
pub use log;
Expand All @@ -34,6 +35,7 @@ pub mod prelude {
pub use ribir_text::*;
#[doc(hidden)]
pub use rxrust::prelude::*;
pub use smallvec;

#[doc(no_inline)]
pub use crate::builtin_widgets::*;
Expand Down Expand Up @@ -61,6 +63,7 @@ pub mod prelude {
pub use crate::window::Window;
pub use crate::{
animation::*,
query::*,
ticker::{Duration, Instant},
};
}
Expand Down
Loading

0 comments on commit d98aa6e

Please sign in to comment.