|
@@ -0,0 +1,123 @@
|
|
|
+use std::{cell::RefCell, rc::Rc};
|
|
|
+
|
|
|
+pub struct ComponentStorage<Msg> {
|
|
|
+ queue: RefCell<Vec<Msg>>,
|
|
|
+}
|
|
|
+
|
|
|
+impl<Msg> Default for ComponentStorage<Msg> {
|
|
|
+ fn default() -> Self {
|
|
|
+ Self {
|
|
|
+ queue: vec![].into()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+impl<Msg> ComponentStorage<Msg> {
|
|
|
+ pub fn enqueue(&self, msg: Msg) {
|
|
|
+ self.queue.borrow_mut().push(msg);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub trait Component: 'static {
|
|
|
+ type ParentMsg;
|
|
|
+ type Msg;
|
|
|
+
|
|
|
+ fn storage(&self) -> Rc<ComponentStorage<Self::Msg>>;
|
|
|
+ fn process(&mut self, msg: Self::Msg) -> Option<Self::ParentMsg>;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+pub mod counter_component_example {
|
|
|
+ use std::rc::Weak;
|
|
|
+
|
|
|
+ use super::*;
|
|
|
+ use crate::widget::Widget;
|
|
|
+
|
|
|
+ pub enum CounterMsg {
|
|
|
+ Increase,
|
|
|
+ Decrease,
|
|
|
+ Reset
|
|
|
+ }
|
|
|
+
|
|
|
+ struct ButtonWidget<C: Component> {
|
|
|
+ click_mapper: Option<Box<dyn Fn() -> Option<C::Msg>>>,
|
|
|
+ _ghost: std::marker::PhantomData<C>,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<C: Component> ButtonWidget<C> {
|
|
|
+ pub fn new(_cstore: Weak<ComponentStorage<C::Msg>>, click_mapper: impl Fn() -> Option<C::Msg> + 'static) -> Self {
|
|
|
+ Self {
|
|
|
+ click_mapper: Some(Box::new(click_mapper)),
|
|
|
+ _ghost: Default::default(),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<C: Component> Widget<C> for ButtonWidget<C> {
|
|
|
+ fn render(&self, surface: ()) { todo!() }
|
|
|
+ fn layout_node(&self) -> &dyn crate::layout::LayoutNodeAccess { todo!() }
|
|
|
+
|
|
|
+ fn poll(&mut self, input_state: &crate::input::InputState) -> impl Iterator<Item = <C as Component>::Msg> {
|
|
|
+ // for now, simulate a click every time
|
|
|
+ self.click_mapper.as_ref().map(|v| (v)()).flatten().into_iter()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ pub struct CounterComponent<PC: Component> {
|
|
|
+ val: usize,
|
|
|
+ inc: ButtonWidget<Self>,
|
|
|
+ dec: ButtonWidget<Self>,
|
|
|
+ cstore: Rc<ComponentStorage<CounterMsg>>,
|
|
|
+ upd_mapper: Box<dyn Fn(usize) -> Option<PC::Msg>>,
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<PC: Component> CounterComponent<PC> {
|
|
|
+ pub fn new(_pc: &PC, upd_mapper: impl Fn(usize) -> Option<PC::Msg> + 'static) -> Self {
|
|
|
+ // let pcstore = Rc::downgrade(&pc.storage());
|
|
|
+ let cstore = Default::default();
|
|
|
+ Self {
|
|
|
+ val: 0,
|
|
|
+ inc: ButtonWidget::new(Rc::downgrade(&cstore), || Some(CounterMsg::Increase)),
|
|
|
+ dec: ButtonWidget::new(Rc::downgrade(&cstore), || Some(CounterMsg::Decrease)),
|
|
|
+ cstore,
|
|
|
+ upd_mapper: Box::new(upd_mapper),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<PC: Component> Component for CounterComponent<PC> {
|
|
|
+ type ParentMsg = PC::Msg;
|
|
|
+ type Msg = CounterMsg;
|
|
|
+
|
|
|
+ fn storage(&self) -> Rc<ComponentStorage<Self::Msg>> { self.cstore.clone() }
|
|
|
+
|
|
|
+ fn process(&mut self, msg: Self::Msg) -> Option<Self::ParentMsg> {
|
|
|
+ match msg {
|
|
|
+ CounterMsg::Increase => self.val += 1,
|
|
|
+ CounterMsg::Decrease => self.val -= 1,
|
|
|
+ CounterMsg::Reset => self.val = 0,
|
|
|
+ }
|
|
|
+ (self.upd_mapper)(self.val)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ impl<PC: Component> Widget<PC> for CounterComponent<PC> {
|
|
|
+ fn render(&self, surface: ()) { todo!() }
|
|
|
+ fn layout_node(&self) -> &dyn crate::layout::LayoutNodeAccess { todo!() }
|
|
|
+
|
|
|
+ fn poll(&mut self, input_state: &crate::input::InputState) -> impl Iterator<Item = <PC as Component>::Msg> {
|
|
|
+ self.cstore.queue.borrow_mut().extend(self.inc.poll(input_state).chain(self.dec.poll(input_state)));
|
|
|
+
|
|
|
+ let mut pb = vec![];
|
|
|
+
|
|
|
+ loop {
|
|
|
+ let Some(v) = self.cstore.queue.borrow_mut().pop() else { break };
|
|
|
+
|
|
|
+ pb.extend(self.process(v).into_iter());
|
|
|
+ }
|
|
|
+
|
|
|
+ pb.into_iter()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+*/
|