mirror of https://github.com/linebender/xilem
Add documentation
This commit is contained in:
parent
a7cc3a1e4e
commit
cc9e81dbc9
|
@ -57,6 +57,9 @@ macro_rules! impl_view_tuple {
|
|||
#[macro_export]
|
||||
macro_rules! generate_viewsequence_trait {
|
||||
($viewseq:ident, $view:ident, $viewmarker: ident, $bound:ident, $cx:ty, $changeflags:ty, $pod:ty; $( $ss:tt )* ) => {
|
||||
/// This trait represents a (possibly empty) sequence of views.
|
||||
///
|
||||
/// It is up to the parent view how to lay out and display them.
|
||||
pub trait $viewseq<T, A = ()> $( $ss )* {
|
||||
/// Associated states for the views.
|
||||
type State $( $ss )*;
|
||||
|
@ -283,8 +286,8 @@ macro_rules! generate_viewsequence_trait {
|
|||
/// This trait marks a type a
|
||||
#[doc = concat!(stringify!($view), ".")]
|
||||
///
|
||||
/// This trait is a workaround for Rust's orphan rules. It serves as a switch between default"]
|
||||
/// and custom
|
||||
/// This trait is a workaround for Rust's orphan rules. It serves as a switch between
|
||||
/// default and custom
|
||||
#[doc = concat!("`", stringify!($viewseq), "`")]
|
||||
/// implementations. You can't implement
|
||||
#[doc = concat!("`", stringify!($viewseq), "`")]
|
||||
|
|
|
@ -68,6 +68,59 @@ macro_rules! generate_view_trait {
|
|||
) -> $crate::MessageResult<A>;
|
||||
}
|
||||
|
||||
/// A view that wraps a child view and modifies the state that callbacks have access to.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Suppose you have an outer type that looks like
|
||||
///
|
||||
/// ```
|
||||
/// struct State {
|
||||
/// todos: Vec<Todo>
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// and an inner type/view that looks like
|
||||
///
|
||||
/// ```ignore
|
||||
/// struct Todo {
|
||||
/// label: String
|
||||
/// }
|
||||
///
|
||||
/// struct TodoView {
|
||||
/// label: String
|
||||
/// }
|
||||
///
|
||||
/// enum TodoAction {
|
||||
/// Delete
|
||||
/// }
|
||||
///
|
||||
/// impl View<Todo, TodoAction> for TodoView {
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// then your top-level action (`()`) and state type (`State`) don't match `TodoView`'s.
|
||||
/// You can use the `Adapt` view to mediate between them:
|
||||
///
|
||||
/// ```ignore
|
||||
/// state
|
||||
/// .todos
|
||||
/// .enumerate()
|
||||
/// .map(|(idx, todo)| {
|
||||
/// Adapt::new(
|
||||
/// move |data: &mut AppState, thunk| {
|
||||
/// if let MessageResult::Action(action) = thunk.call(&mut data.todos[idx]) {
|
||||
/// match action {
|
||||
/// TodoAction::Delete => data.todos.remove(idx),
|
||||
/// }
|
||||
/// }
|
||||
/// MessageResult::Nop
|
||||
/// },
|
||||
/// TodoView { label: todo.label }
|
||||
/// )
|
||||
/// })
|
||||
/// ```
|
||||
pub struct Adapt<OutData, OutMsg, InData, InMsg, F: Fn(&mut OutData, AdaptThunk<InData, InMsg, V>) -> $crate::MessageResult<OutMsg>, V: View<InData, InMsg>> {
|
||||
f: F,
|
||||
child: V,
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::{
|
|||
};
|
||||
use xilem_core::{Id, MessageResult};
|
||||
|
||||
/// The type responsible for running your app.
|
||||
pub struct App<T, V: View<T>, F: FnMut(&mut T) -> V>(Rc<RefCell<AppInner<T, V, F>>>);
|
||||
|
||||
struct AppInner<T, V: View<T>, F: FnMut(&mut T) -> V> {
|
||||
|
@ -35,6 +36,7 @@ impl<T: 'static, V: View<T> + 'static, F: FnMut(&mut T) -> V + 'static> Clone fo
|
|||
}
|
||||
|
||||
impl<T: 'static, V: View<T> + 'static, F: FnMut(&mut T) -> V + 'static> App<T, V, F> {
|
||||
/// Create an instance of your app with the given logic and initial state.
|
||||
pub fn new(data: T, app_logic: F) -> Self {
|
||||
let inner = AppInner::new(data, app_logic);
|
||||
let app = App(Rc::new(RefCell::new(inner)));
|
||||
|
@ -42,6 +44,10 @@ impl<T: 'static, V: View<T> + 'static, F: FnMut(&mut T) -> V + 'static> App<T, V
|
|||
app
|
||||
}
|
||||
|
||||
/// Run the app.
|
||||
///
|
||||
/// Because we don't want to block the render thread, we return immediately here. The app is
|
||||
/// forgotten, and will continue to respond to events in the background.
|
||||
pub fn run(self, root: &web_sys::HtmlElement) {
|
||||
self.0.borrow_mut().ensure_app(root);
|
||||
// Latter may not be necessary, we have an rc loop.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//! Macros to generate all the different html elements
|
||||
//! Types that wrap [`Element`][super::Element] and represent specific element types.
|
||||
//!
|
||||
macro_rules! elements {
|
||||
() => {};
|
||||
|
@ -10,8 +10,14 @@ macro_rules! elements {
|
|||
|
||||
macro_rules! element {
|
||||
($ty_name:ident, $builder_name:ident, $name:literal, $web_sys_ty:ty) => {
|
||||
/// A view representing a
|
||||
#[doc = concat!("`", $name, "`")]
|
||||
/// element.
|
||||
pub struct $ty_name<ViewSeq>(crate::Element<$web_sys_ty, ViewSeq>);
|
||||
|
||||
/// Builder function for a
|
||||
#[doc = concat!("`", $name, "`")]
|
||||
/// view.
|
||||
pub fn $builder_name<ViewSeq>(children: ViewSeq) -> $ty_name<ViewSeq> {
|
||||
$ty_name(crate::element($name, children))
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ use xilem_core::{Id, MessageResult, VecSplice};
|
|||
pub mod elements;
|
||||
|
||||
/// A view representing a HTML element.
|
||||
///
|
||||
/// If the element has no chilcdren, use the unit type (e.g. `let view = element("div", ())`).
|
||||
pub struct Element<El, Children = ()> {
|
||||
name: Cow<'static, str>,
|
||||
attributes: BTreeMap<Cow<'static, str>, Cow<'static, str>>,
|
||||
|
@ -46,7 +48,9 @@ pub struct ElementState<ViewSeqState> {
|
|||
child_elements: Vec<Pod>,
|
||||
}
|
||||
|
||||
/// Create a new element
|
||||
/// Create a new element view
|
||||
///
|
||||
/// If the element has no chilcdren, use the unit type (e.g. `let view = element("div", ())`).
|
||||
pub fn element<E, ViewSeq>(
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
children: ViewSeq,
|
||||
|
@ -71,10 +75,16 @@ impl<E, ViewSeq> Element<E, ViewSeq> {
|
|||
name: impl Into<Cow<'static, str>>,
|
||||
value: impl Into<Cow<'static, str>>,
|
||||
) -> Self {
|
||||
self.attributes.insert(name.into(), value.into());
|
||||
self.set_attr(name, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set an attribute on this element.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// If the name contains characters that are not valid in an attribute name,
|
||||
/// then the `View::build`/`View::rebuild` functions will panic for this view.
|
||||
pub fn set_attr(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'static, str>>,
|
||||
|
|
|
@ -10,6 +10,9 @@ macro_rules! events {
|
|||
|
||||
macro_rules! event {
|
||||
($ty_name:ident, $builder_name:ident, $name:literal, $web_sys_ty:ty) => {
|
||||
/// A view that listens for the
|
||||
#[doc = concat!("`", $name, "`")]
|
||||
/// event.
|
||||
pub struct $ty_name<T, A, V, F, OA>
|
||||
where
|
||||
V: crate::view::View<T, A>,
|
||||
|
@ -23,6 +26,9 @@ macro_rules! event {
|
|||
optional_action: std::marker::PhantomData<OA>,
|
||||
}
|
||||
|
||||
/// Builder for the
|
||||
#[doc = concat!("`", $name, "`")]
|
||||
/// event listener.
|
||||
pub fn $builder_name<T, A, V, F, OA>(child: V, callback: F) -> $ty_name<T, A, V, F, OA>
|
||||
where
|
||||
V: crate::view::View<T, A>,
|
||||
|
|
|
@ -15,6 +15,10 @@ use crate::{
|
|||
view::{DomNode, View, ViewMarker},
|
||||
};
|
||||
|
||||
/// Wraps a [`View`] `V` and attaches an event listener.
|
||||
///
|
||||
/// The event type `E` contains both the [`web_sys::Event`] subclass for this event and the
|
||||
/// [`web_sys::HtmlElement`] subclass that matches `V::Element`.
|
||||
pub struct OnEvent<E, V, F> {
|
||||
// TODO changing this after creation is unsupported for now,
|
||||
// please create a new view instead.
|
||||
|
@ -107,6 +111,7 @@ pub fn on_event<E, V, F>(name: &'static str, child: V, callback: F) -> OnEvent<E
|
|||
OnEvent::new(name, child, callback)
|
||||
}
|
||||
|
||||
/// State for the `OnEvent` view.
|
||||
pub struct OnEventState<S> {
|
||||
#[allow(unused)]
|
||||
listener: EventListener,
|
||||
|
@ -116,6 +121,7 @@ struct EventMsg<E> {
|
|||
event: E,
|
||||
}
|
||||
|
||||
/// Wraps a `web_sys::Event` and provides auto downcasting for both the event and its target.
|
||||
pub struct Event<Evt, El> {
|
||||
raw: Evt,
|
||||
el: PhantomData<El>,
|
||||
|
@ -135,6 +141,9 @@ where
|
|||
Evt: AsRef<web_sys::Event>,
|
||||
El: JsCast,
|
||||
{
|
||||
/// Get the event target element.
|
||||
///
|
||||
/// Because this type knows its child view's element type, we can downcast to this type here.
|
||||
pub fn target(&self) -> El {
|
||||
let evt: &web_sys::Event = self.raw.as_ref();
|
||||
evt.target().unwrap_throw().dyn_into().unwrap_throw()
|
||||
|
@ -155,7 +164,7 @@ impl<Evt, El> Deref for Event<Evt, El> {
|
|||
pub trait Action {}
|
||||
|
||||
/// Trait that allows callbacks to be polymorphic on return type
|
||||
/// (`Action`, `Option<Action>` or `()`)
|
||||
/// (`Action`, `Option<Action>` or `()`). An implementation detail.
|
||||
pub trait OptionalAction<A>: sealed::Sealed {
|
||||
fn action(self) -> Option<A>;
|
||||
}
|
||||
|
|
|
@ -10,13 +10,20 @@ use xilem_core::{Id, MessageResult};
|
|||
|
||||
use crate::{context::Cx, ChangeFlags};
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
// A possible refinement of xilem_core is to allow a single concrete type
|
||||
// for a view element, rather than an associated type with a bound.
|
||||
pub trait DomNode {
|
||||
/// This trait is implemented for types that implement `AsRef<web_sys::Node>`.
|
||||
/// It is an implementation detail.
|
||||
pub trait DomNode: sealed::Sealed {
|
||||
fn into_pod(self) -> Pod;
|
||||
fn as_node_ref(&self) -> &web_sys::Node;
|
||||
}
|
||||
|
||||
impl<N: AsRef<web_sys::Node> + 'static> sealed::Sealed for N {}
|
||||
impl<N: AsRef<web_sys::Node> + 'static> DomNode for N {
|
||||
fn into_pod(self) -> Pod {
|
||||
Pod(Box::new(self))
|
||||
|
@ -27,6 +34,8 @@ impl<N: AsRef<web_sys::Node> + 'static> DomNode for N {
|
|||
}
|
||||
}
|
||||
|
||||
/// This trait is implemented for types that implement `AsRef<web_sys::Element>`.
|
||||
/// It is an implementation detail.
|
||||
pub trait DomElement: DomNode {
|
||||
fn as_element_ref(&self) -> &web_sys::Element;
|
||||
}
|
||||
|
@ -37,7 +46,9 @@ impl<N: DomNode + AsRef<web_sys::Element>> DomElement for N {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait AnyNode {
|
||||
/// A trait for types that can be type-erased and impl `AsRef<Node>`. It is an
|
||||
/// implementation detail.
|
||||
pub trait AnyNode: sealed::Sealed {
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
fn as_node_ref(&self) -> &web_sys::Node;
|
||||
|
@ -53,6 +64,7 @@ impl<N: AsRef<web_sys::Node> + Any> AnyNode for N {
|
|||
}
|
||||
}
|
||||
|
||||
impl sealed::Sealed for Box<dyn AnyNode> {}
|
||||
impl DomNode for Box<dyn AnyNode> {
|
||||
fn into_pod(self) -> Pod {
|
||||
Pod(self)
|
||||
|
|
|
@ -5,7 +5,10 @@ use std::borrow::Cow;
|
|||
|
||||
use crate::{class::Class, event::OptionalAction, events as e, view::View, Event};
|
||||
|
||||
/// A trait that makes it possible to attach event listeners and more to views
|
||||
/// in the continuation style.
|
||||
pub trait ViewExt<T, A>: View<T, A> + Sized {
|
||||
/// Add an `onclick` event listener.
|
||||
fn on_click<
|
||||
OA: OptionalAction<A>,
|
||||
F: Fn(&mut T, &Event<web_sys::MouseEvent, Self::Element>) -> OA,
|
||||
|
@ -13,6 +16,7 @@ pub trait ViewExt<T, A>: View<T, A> + Sized {
|
|||
self,
|
||||
f: F,
|
||||
) -> e::OnClick<T, A, Self, F, OA>;
|
||||
/// Add an `ondblclick` event listener.
|
||||
fn on_dblclick<
|
||||
OA: OptionalAction<A>,
|
||||
F: Fn(&mut T, &Event<web_sys::MouseEvent, Self::Element>) -> OA,
|
||||
|
@ -20,6 +24,7 @@ pub trait ViewExt<T, A>: View<T, A> + Sized {
|
|||
self,
|
||||
f: F,
|
||||
) -> e::OnDblClick<T, A, Self, F, OA>;
|
||||
/// Add an `oninput` event listener.
|
||||
fn on_input<
|
||||
OA: OptionalAction<A>,
|
||||
F: Fn(&mut T, &Event<web_sys::InputEvent, Self::Element>) -> OA,
|
||||
|
@ -27,6 +32,7 @@ pub trait ViewExt<T, A>: View<T, A> + Sized {
|
|||
self,
|
||||
f: F,
|
||||
) -> e::OnInput<T, A, Self, F, OA>;
|
||||
/// Add an `onkeydown` event listener.
|
||||
fn on_keydown<
|
||||
OA: OptionalAction<A>,
|
||||
F: Fn(&mut T, &Event<web_sys::KeyboardEvent, Self::Element>) -> OA,
|
||||
|
@ -34,6 +40,7 @@ pub trait ViewExt<T, A>: View<T, A> + Sized {
|
|||
self,
|
||||
f: F,
|
||||
) -> e::OnKeyDown<T, A, Self, F, OA>;
|
||||
/// Apply a CSS class to the child view.
|
||||
fn class(self, class: impl Into<Cow<'static, str>>) -> Class<Self> {
|
||||
crate::class::class(self, class)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue