Skip to content

Commit

Permalink
feat: lay groundwork for deferred elements
Browse files Browse the repository at this point in the history
  • Loading branch information
Stumblinbear committed Jan 28, 2024
1 parent 47d7a4c commit 39a825a
Show file tree
Hide file tree
Showing 31 changed files with 482 additions and 128 deletions.
25 changes: 17 additions & 8 deletions crates/agui_core/src/callback/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::{
sync::Arc,
};

use crate::{element::ElementId, unit::AsAny, util::ptr_eq::PtrEqual};
use crate::{element::ElementId, util::ptr_eq::PtrEqual};

mod queue;

Expand All @@ -29,7 +29,7 @@ pub enum Callback<A: ?Sized> {

impl<A> Callback<A>
where
A: AsAny,
A: Any,
{
pub fn call(&self, arg: A) {
match self {
Expand Down Expand Up @@ -69,7 +69,10 @@ impl<A> Clone for Callback<A> {
}
}

impl<A: 'static> std::fmt::Debug for Callback<A> {
impl<A> std::fmt::Debug for Callback<A>
where
A: Any,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Callback::Widget(cb) => f.debug_tuple("Widget").field(cb).finish(),
Expand All @@ -88,9 +91,12 @@ pub struct WidgetCallback<A: ?Sized> {

impl<A> WidgetCallback<A>
where
A: AsAny,
A: Any,
{
pub fn new<F: 'static>(element_id: ElementId, callback_queue: CallbackQueue) -> Self {
pub fn new<F>(element_id: ElementId, callback_queue: CallbackQueue) -> Self
where
F: Any,
{
Self::new_unchecked(element_id, TypeId::of::<F>(), callback_queue)
}

Expand Down Expand Up @@ -163,7 +169,7 @@ pub struct FuncCallback<A: ?Sized> {

impl<A> FuncCallback<A>
where
A: AsAny,
A: Any,
{
pub fn call(&self, arg: A) {
(self.func)(arg)
Expand Down Expand Up @@ -196,7 +202,10 @@ impl<A: ?Sized> Clone for FuncCallback<A> {
}
}

impl<A: 'static> std::fmt::Debug for FuncCallback<A> {
impl<A> std::fmt::Debug for FuncCallback<A>
where
A: Any,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FuncCallback")
.field("func", &TypeId::of::<A>())
Expand All @@ -206,7 +215,7 @@ impl<A: 'static> std::fmt::Debug for FuncCallback<A> {

impl<A, F> From<F> for Callback<A>
where
A: AsAny,
A: Any,
F: Fn(A) + Send + Sync + 'static,
{
fn from(value: F) -> Self {
Expand Down
2 changes: 1 addition & 1 deletion crates/agui_core/src/element/comparison.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#[derive(Debug, PartialEq, Eq)]
pub enum ElementComparison {
/// The widgets were of the same type and instance.
/// The widgets were of the same type and the element can be reused.
Identical,

/// The widgets was changed and the element must be rebuilt to reflect the changes.
Expand Down
7 changes: 0 additions & 7 deletions crates/agui_core/src/element/deferred.rs

This file was deleted.

76 changes: 76 additions & 0 deletions crates/agui_core/src/element/deferred/erased.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use std::{any::Any, sync::Arc};

use crate::{
element::{
deferred::{resolver::DeferredResolver, ElementDeferred},
ElementLifecycle,
},
unit::Constraints,
widget::Widget,
};

pub trait ErasedElementDeferred: ElementLifecycle {
#[allow(private_interfaces)]
fn create_resolver(&self) -> Arc<dyn DeferredResolver>;

#[allow(private_interfaces)]
fn build(&self, resolver: &dyn DeferredResolver) -> Widget;
}

impl<T> ErasedElementDeferred for T
where
T: ElementDeferred,
{
#[allow(private_interfaces)]
fn create_resolver(&self) -> Arc<dyn DeferredResolver> {
Arc::new(ErasedDeferredResolver {
resolver: self.create_resolver(),

current_param: None,
})
}

#[allow(private_interfaces)]
fn build(&self, resolver: &dyn DeferredResolver) -> Widget {
let param = resolver
.param()
.expect("deferred elements must be resolved before being built")
.downcast_ref::<T::Param>()
.expect("failed to downcast deferred element param");

ElementDeferred::build(self, param)
}
}

pub(crate) struct ErasedDeferredResolver<ResolverFn, Param> {
resolver: ResolverFn,

current_param: Option<Param>,
}

impl<ResolverFn, Param> DeferredResolver for ErasedDeferredResolver<ResolverFn, Param>
where
ResolverFn: Fn(Constraints) -> Param + Send + Sync + 'static,
Param: Any + PartialEq + Send + Sync,
{
fn resolve(&mut self, constraints: Constraints) -> bool {
let param = (self.resolver)(constraints);

if self
.current_param
.as_ref()
.map(|p| p == &param)
.unwrap_or(false)
{
return false;
}

self.current_param = Some(param);

true
}

fn param(&self) -> Option<&(dyn Any + Send)> {
self.current_param.as_ref().map(|p| p as &(dyn Any + Send))
}
}
24 changes: 24 additions & 0 deletions crates/agui_core/src/element/deferred/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use crate::{unit::Constraints, widget::Widget};

use super::lifecycle::ElementLifecycle;

pub mod erased;
pub mod resolver;

/// A deferred element is an element that is not immediately build its children,
/// but instead waits until the layout phase to do so.
///
/// It creates a resolver that is passed into the layout phase, which is used to
/// determine if the element needs to be rebuilt. If the resolver returns a value
/// that is not equal to the previous value, the element will be rebuilt.
///
/// Resolver functions should be cheap to call, and should _only_ return a different
/// value if it is absolutely necessary, as it will stall the rendering phase in
/// order to rebuild the element leading to a poor user experience.
pub trait ElementDeferred: ElementLifecycle {
type Param: PartialEq + Send + Sync;

fn create_resolver(&self) -> impl Fn(Constraints) -> Self::Param + Send + Sync + 'static;

fn build(&self, param: &Self::Param) -> Widget;
}
9 changes: 9 additions & 0 deletions crates/agui_core/src/element/deferred/resolver.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use std::any::Any;

use crate::unit::Constraints;

pub(crate) trait DeferredResolver: Any + Send + Sync {
fn resolve(&mut self, constraints: Constraints) -> bool;

fn param(&self) -> Option<&(dyn Any + Send)>;
}
32 changes: 29 additions & 3 deletions crates/agui_core/src/element/inherited.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ pub trait ElementInherited: ElementLifecycle {
where
Self: Sized;

/// Returns the [`TypeId`] of the data provided by this element.
fn inherited_type_id(&self) -> TypeId;

fn inherited_data(&self) -> &Self::Data
where
Self: Sized;
Expand All @@ -22,3 +19,32 @@ pub trait ElementInherited: ElementLifecycle {
/// this element need to be rebuilt.
fn needs_notify(&mut self) -> bool;
}

pub trait ErasedElementInherited: ElementLifecycle {
/// Returns the [`TypeId`] of the data provided by this element.
fn inherited_type_id(&self) -> TypeId;

fn child(&self) -> Widget;

/// Called during the build phase to determine if elements listening to
/// this element need to be rebuilt.
fn needs_notify(&mut self) -> bool;
}

impl<T> ErasedElementInherited for T
where
T: ElementInherited,
T::Data: Any,
{
fn inherited_type_id(&self) -> TypeId {
TypeId::of::<T::Data>()
}

fn child(&self) -> Widget {
self.child()
}

fn needs_notify(&mut self) -> bool {
self.needs_notify()
}
}
1 change: 0 additions & 1 deletion crates/agui_core/src/element/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,5 @@ pub trait ElementLifecycle: AsAny {
#[allow(unused_variables)]
fn unmount(&mut self, ctx: &mut ElementUnmountContext) {}

/// Returns true if the widget is of the same type as the other widget.
fn update(&mut self, new_widget: &Widget) -> ElementComparison;
}
6 changes: 1 addition & 5 deletions crates/agui_core/src/element/mock/inherited.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{any::TypeId, cell::RefCell, rc::Rc};
use std::{cell::RefCell, rc::Rc};

use crate::{
element::{
Expand Down Expand Up @@ -76,10 +76,6 @@ impl ElementWidget for MockedElementInherited {
impl ElementInherited for MockedElementInherited {
type Data = Rc<MockInheritedWidget>;

fn inherited_type_id(&self) -> TypeId {
TypeId::of::<Self::Data>()
}

fn inherited_data(&self) -> &Self::Data {
&self.widget
}
Expand Down
Loading

0 comments on commit 39a825a

Please sign in to comment.