Adding in signal_vec::unsync::mutable, and also support for using SignalVec with dominator

This commit is contained in:
Pauan 2018-03-01 03:39:58 -10:00
parent a7a29befd2
commit d99dbbbee8
5 changed files with 351 additions and 26 deletions

View File

@ -16,7 +16,9 @@ use stdweb::web::event::ClickEvent;
use stdweb::web::IParentNode;
use signals::signal;
use signals::signal_vec;
use signals::signal::Signal;
use signals::signal_vec::SignalVec;
use dominator::traits::*;
use dominator::{Dom, text};
@ -36,7 +38,7 @@ fn main() {
let mut count = 0;
let (sender_elements, receiver_elements) = signal::unsync::mutable(count);
let (sender_count, receiver_count) = signal::unsync::mutable(count);
let mut width: u32 = 10;
@ -46,6 +48,8 @@ fn main() {
let (sender3, receiver3) = signal::unsync::mutable(vec![width]);
let (text_sender, text_receiver) = signal::unsync::mutable(format!("{}", width));
let (mut sender_elements, receiver_elements) = signal_vec::unsync::mutable();
/*let style_width = receiver1.switch(move |x| {
receiver2.clone().switch(move |y| {
receiver3.clone().map(move |z| {
@ -62,6 +66,22 @@ fn main() {
};
let mut elements_index = 0;
let mut increment = move || {
elements_index += 1;
elements_index
};
sender_elements.push((increment(), 1));
sender_elements.push((increment(), 2));
sender_elements.push((increment(), 3));
sender_elements.push((increment(), 4));
sender_elements.push((increment(), 5));
sender_elements.push((increment(), 6));
sender_elements.push((increment(), 7));
dominator::append_dom(&document().query_selector("body").unwrap().unwrap(),
html!("div", {
style("border", "10px solid blue");
@ -70,6 +90,8 @@ fn main() {
text(text_receiver.dynamic()),
text(receiver_count.map(|x| format!(" - {}", x)).dynamic()),
html!("div", {
style("width", style_width.dynamic());
style("height", "50px");
@ -84,30 +106,36 @@ fn main() {
sender2.set(vec![width]).unwrap();
sender3.set(vec![width]).unwrap();
text_sender.set(format!("{}", width)).unwrap();
sender_elements.set(count).unwrap();
sender_count.set(count).unwrap();
sender_elements.push((increment(), 8));
sender_elements.push((increment(), 0));
sender_elements.push((increment(), 5));
sender_elements.push((increment(), 9));
});
children(receiver_elements.map(|count| {
(0..count).map(|_| {
html!("div", {
style("border", "5px solid red");
style("width", "50px");
style("height", "50px");
children(
receiver_elements
.filter_map(|(x, y)| {
if y > 2 {
Some((x, y + 100))
} else {
None
}
})
})
}).dynamic());
}),
html!("div", {
style("width", "50px");
style("height", "50px");
style("background-color", "red");
children(&mut [
html!("div", {
style("width", "10px");
style("height", "10px");
style("background-color", "orange");
})
]);
.sort_by(|&(_, a), &(_, b)| {
a.cmp(&b).reverse()
})
.map(|(x, y)| {
html!("div", {
style("border", "5px solid red");
style("width", "100px");
style("height", "50px");
children(&mut [
text(format!("({}, {})", x, y))
]);
})
})
.dynamic()
);
}),
html!("div", {

View File

@ -1,5 +1,7 @@
use std::cmp::Ordering;
use futures::{Stream, Poll, Async};
use futures::stream::ForEach;
use futures::future::IntoFuture;
#[derive(Debug, Clone, PartialEq, Eq)]
@ -82,7 +84,7 @@ pub trait SignalVec {
}
#[inline]
fn sort_by<A, F>(self, compare: F) -> SortBy<Self, F>
fn sort_by<F>(self, compare: F) -> SortBy<Self, F>
where F: FnMut(&Self::Item, &Self::Item) -> Ordering,
Self: Sized {
SortBy {
@ -101,6 +103,17 @@ pub trait SignalVec {
}
}
#[inline]
// TODO file Rust bug about bad error message when `callback` isn't marked as `mut`
fn for_each<F, U>(self, callback: F) -> ForEach<SignalVecStream<Self>, F, U>
where F: FnMut(VecChange<Self::Item>) -> U,
// TODO allow for errors ?
U: IntoFuture<Item = (), Error = ()>,
Self:Sized {
self.to_stream().for_each(callback)
}
#[inline]
fn by_ref(&mut self) -> &mut Self {
self
@ -516,6 +529,112 @@ impl<A, F> SignalVec for SortBy<A, F>
}
// TODO verify that this is correct
pub mod unsync {
use super::{SignalVec, VecChange};
use futures::unsync::mpsc;
use futures::{Async, Stream};
pub struct Sender<A> {
values: Vec<A>,
sender: mpsc::UnboundedSender<VecChange<A>>,
}
impl<A: Clone> Sender<A> {
pub fn push(&mut self, value: A) {
let clone = value.clone();
self.values.push(value);
self.sender.unbounded_send(VecChange::Push { value: clone }).unwrap();
}
pub fn insert(&mut self, index: usize, value: A) {
let clone = value.clone();
if index == self.values.len() {
self.values.push(value);
self.sender.unbounded_send(VecChange::Push { value: clone }).unwrap();
} else {
self.values.insert(index, value);
self.sender.unbounded_send(VecChange::InsertAt { index, value: clone }).unwrap();
}
}
// TODO replace this with something else, like entry or IndexMut or whatever
pub fn update(&mut self, index: usize, value: A) {
let clone = value.clone();
self.values[index] = value;
self.sender.unbounded_send(VecChange::UpdateAt { index, value: clone }).unwrap();
}
}
impl<A> Sender<A> {
pub fn pop(&mut self) -> Option<A> {
let value = self.values.pop();
if let Some(_) = value {
self.sender.unbounded_send(VecChange::Pop {}).unwrap();
}
value
}
pub fn remove(&mut self, index: usize) -> A {
let len = self.values.len();
let value = self.values.remove(index);
if index == (len - 1) {
self.sender.unbounded_send(VecChange::Pop {}).unwrap();
} else {
self.sender.unbounded_send(VecChange::RemoveAt { index }).unwrap();
}
value
}
pub fn clear(&mut self) {
let len = self.values.len();
self.values.clear();
if len > 0 {
self.sender.unbounded_send(VecChange::Clear {}).unwrap();
}
}
}
pub struct Receiver<A> {
receiver: mpsc::UnboundedReceiver<VecChange<A>>,
}
// TODO have it send a Replace at the beginning
impl<A> SignalVec for Receiver<A> {
type Item = A;
#[inline]
fn poll(&mut self) -> Async<Option<VecChange<Self::Item>>> {
self.receiver.poll().unwrap()
}
}
#[inline]
pub fn mutable<A>() -> (Sender<A>, Receiver<A>) {
let (sender, receiver) = mpsc::unbounded();
let sender = Sender { values: vec![], sender };
let receiver = Receiver { receiver };
(sender, receiver)
}
}
#[cfg(test)]
mod tests {
use futures::{Future, Poll, task};

View File

@ -10,6 +10,30 @@ pub fn create_element_ns<A: IElement>(name: &str, namespace: &str) -> A
js!( return document.createElementNS(@{namespace}, @{name}); ).try_into().unwrap()
}
#[inline]
pub fn insert_at<A: INode, B: INode>(parent: &A, index: u32, child: &B) {
js! { @(no_return)
var parent = @{parent.as_ref()};
parent.insertBefore(@{child.as_ref()}, parent.childNodes[@{index}]);
}
}
#[inline]
pub fn update_at<A: INode, B: INode>(parent: &A, index: u32, child: &B) {
js! { @(no_return)
var parent = @{parent.as_ref()};
parent.replaceChild(@{child.as_ref()}, parent.childNodes[@{index}]);
}
}
#[inline]
pub fn remove_at<A: INode>(parent: &A, index: u32) {
js! { @(no_return)
var parent = @{parent.as_ref()};
parent.removeChild(parent.childNodes[@{index}]);
}
}
// TODO this should be in stdweb
#[inline]
pub fn set_text(element: &TextNode, value: &str) {

View File

@ -1,8 +1,11 @@
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use stdweb::PromiseFuture;
use discard::{Discard, DiscardOnDrop};
use stdweb::Reference;
use stdweb::web::TextNode;
use signals::signal::{Signal, cancelable_future, CancelableFutureHandle};
use signals::signal_vec::{VecChange, SignalVec};
use dom_operations;
use dom::{Dom, IStyle};
use callbacks::Callbacks;
@ -29,6 +32,24 @@ fn for_each<A, B>(signal: A, mut callback: B) -> CancelableFutureHandle
}
fn for_each_vec<A, B>(signal: A, mut callback: B) -> CancelableFutureHandle
where A: SignalVec + 'static,
B: FnMut(VecChange<A::Item>) + 'static {
let future = signal.for_each(move |value| {
callback(value);
Ok(())
});
// TODO make this more efficient ?
let (handle, future) = cancelable_future(future, |_| ());
PromiseFuture::spawn(future);
DiscardOnDrop::leak(handle)
}
// TODO inline this ?
pub fn set_text_signal<A>(element: &TextNode, callbacks: &mut Callbacks, signal: A)
where A: Signal<Item = String> + 'static {
@ -166,6 +187,7 @@ pub fn set_focused_bool<A: IHtmlElement + Clone + 'static>(element: &A, callback
}
/*
// TODO inline this ?
pub fn insert_children_signal<A, B, C>(element: &A, callbacks: &mut Callbacks, signal: C)
where A: INode + Clone + 'static,
@ -191,7 +213,7 @@ pub fn insert_children_signal<A, B, C>(element: &A, callbacks: &mut Callbacks, s
// TODO verify that this will drop `old_children`
callbacks.after_remove(move || handle.discard());
}
}*/
#[inline]
pub fn insert_children_iter<'a, A: INode, B: IntoIterator<Item = &'a mut Dom>>(element: &A, callbacks: &mut Callbacks, value: B) {
@ -202,3 +224,120 @@ pub fn insert_children_iter<'a, A: INode, B: IntoIterator<Item = &'a mut Dom>>(e
element.append_child(&dom.element);
}
}
pub fn insert_children_signal_vec<A, B>(element: &A, callbacks: &mut Callbacks, signal: B)
where A: INode + Clone + 'static,
B: SignalVec<Item = Dom> + 'static {
let element = element.clone();
// TODO does this create a new struct type every time ?
struct State {
is_inserted: Cell<bool>,
children: RefCell<Vec<Dom>>,
}
let state = Rc::new(State {
is_inserted: Cell::new(false),
children: RefCell::new(vec![]),
});
{
let state = state.clone();
callbacks.after_insert(move |_| {
if !state.is_inserted.replace(true) {
let mut children = state.children.borrow_mut();
// TODO figure out a better way to iterate
for mut dom in &mut children[..] {
dom.callbacks.trigger_after_insert();
}
}
});
}
let handle = for_each_vec(signal, move |change| {
match change {
VecChange::Replace { values } => {
dom_operations::remove_all_children(&element);
let mut children = state.children.borrow_mut();
*children = values;
// TODO use document fragment ?
// TODO figure out a better way to iterate
for dom in &mut children[..] {
element.append_child(&dom.element);
if state.is_inserted.get() {
dom.callbacks.trigger_after_insert();
}
}
},
VecChange::InsertAt { index, mut value } => {
// TODO better usize -> u32 conversion
dom_operations::insert_at(&element, index as u32, &value.element);
if state.is_inserted.get() {
value.callbacks.trigger_after_insert();
}
// TODO figure out a way to move this to the top
state.children.borrow_mut().insert(index, value);
},
VecChange::UpdateAt { index, mut value } => {
// TODO better usize -> u32 conversion
dom_operations::update_at(&element, index as u32, &value.element);
if state.is_inserted.get() {
value.callbacks.trigger_after_insert();
}
// TODO figure out a way to move this to the top
state.children.borrow_mut()[index] = value;
},
VecChange::RemoveAt { index } => {
// TODO better usize -> u32 conversion
dom_operations::remove_at(&element, index as u32);
state.children.borrow_mut().remove(index);
},
VecChange::Push { mut value } => {
element.append_child(&value.element);
if state.is_inserted.get() {
value.callbacks.trigger_after_insert();
}
// TODO figure out a way to move this to the top
state.children.borrow_mut().push(value);
},
VecChange::Pop {} => {
let mut children = state.children.borrow_mut();
let index = children.len() - 1;
// TODO better usize -> u32 conversion
dom_operations::remove_at(&element, index as u32);
children.pop();
},
VecChange::Clear {} => {
dom_operations::remove_all_children(&element);
*state.children.borrow_mut() = vec![];
},
}
});
// TODO verify that this will drop `old_children`
callbacks.after_remove(move || handle.discard());
}

View File

@ -5,6 +5,7 @@ use stdweb::unstable::TryInto;
use dom::{Dom, IStyle, Dynamic};
use callbacks::Callbacks;
use signals::signal::Signal;
use signals::signal_vec::SignalVec;
use operations;
@ -56,6 +57,13 @@ impl<'a> Text for &'a str {
}
}
impl<'a> Text for String {
#[inline]
fn into_dom(self) -> Dom {
self.as_str().into_dom()
}
}
impl<'a> Property for &'a str {
#[inline]
fn set_property<A: AsRef<Reference>>(self, element: &A, _callbacks: &mut Callbacks, name: &str) {
@ -151,11 +159,18 @@ impl<A: Signal<Item = bool> + 'static> Focused for Dynamic<A> {
}
}
impl<A, B> Children for Dynamic<B>
/*impl<A, B> Children for Dynamic<B>
where A: IntoIterator<Item = Dom>,
B: Signal<Item = A> + 'static {
#[inline]
fn insert_children<C: INode + Clone + 'static>(self, element: &C, callbacks: &mut Callbacks) {
operations::insert_children_signal(element, callbacks, self.0)
}
}*/
impl<A> Children for Dynamic<A> where A: SignalVec<Item = Dom> + 'static {
#[inline]
fn insert_children<C: INode + Clone + 'static>(self, element: &C, callbacks: &mut Callbacks) {
operations::insert_children_signal_vec(element, callbacks, self.0)
}
}