Browse Source

Added theme support.

Kestrel 4 months ago
parent
commit
0f060bb74c
11 changed files with 188 additions and 261 deletions
  1. 11 13
      examples/simplest.rs
  2. 0 104
      src/component.rs
  3. 5 1
      src/layout.rs
  4. 6 0
      src/lib.rs
  5. 43 11
      src/text.rs
  6. 29 0
      src/theme.rs
  7. 42 17
      src/ui.rs
  8. 15 5
      src/widget.rs
  9. 0 80
      src/widget/context.rs
  10. 28 21
      src/widget/label.rs
  11. 9 9
      src/window.rs

+ 11 - 13
examples/simplest.rs

@@ -1,14 +1,15 @@
-use patina::{
-    prelude::*, ui::UIControlMsg, window::WindowComponent
-};
+use patina::{prelude::*, ui::UIControlMsg, window::WindowComponent};
 
 struct SimpleWindow {
     widget: <Self as WindowComponent>::RootWidget,
 }
 
 impl SimpleWindow {
-    fn new(uih: &patina::ui::UIHandle) -> Self {
-        Self { widget: patina::widget::Spacer::new(uih) }
+    fn new(uih: &mut patina::ui::UIHandle) -> Self {
+        Self {
+            // widget: patina::widget::Spacer::new(uih),
+            widget: patina::widget::Label::new(uih),
+        }
     }
 }
 
@@ -30,14 +31,13 @@ impl Component for SimpleWindow {
 impl WindowComponent for SimpleWindow {
     fn map_window_event(&self, we: patina::window::WindowEvent) -> Option<Self::Msg> {
         match we {
-            patina::window::WindowEvent::CloseRequested => {
-                Some(SimpleWindowMsg::Close)
-            },
-            _ => None
+            patina::window::WindowEvent::CloseRequested => Some(SimpleWindowMsg::Close),
+            _ => None,
         }
     }
 
-    type RootWidget = patina::widget::Spacer<Self>;
+    // type RootWidget = patina::widget::Spacer<Self>;
+    type RootWidget = patina::widget::Label<Self>;
     fn root_widget(&self) -> &Self::RootWidget {
         &self.widget
     }
@@ -47,7 +47,5 @@ impl WindowComponent for SimpleWindow {
 }
 
 fn main() {
-    patina::ui::make_opinionated_ui(|uih| {
-        SimpleWindow::new(uih)
-    }).run();
+    patina::ui::make_opinionated_ui(SimpleWindow::new).run();
 }

+ 0 - 104
src/component.rs

@@ -1,23 +1,3 @@
-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 {
     type ParentMsg;
     type Msg;
@@ -28,87 +8,3 @@ pub trait Component {
         msgs.map(|msg| self.process(msg)).flatten().collect()
     }
 }
-
-/*
-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 process(&mut self, msg: Self::Msg) -> Vec<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) -> Vec<PC::Msg> {
-            self.process_all(self.inc.poll(input_state).chain(self.dec.poll(input_state)))
-        }
-    }
-}
-*/

+ 5 - 1
src/layout.rs

@@ -24,7 +24,11 @@ pub struct SizePolicy {
 
 impl SizePolicy {
     pub fn expands(weight: usize) -> Self {
-        Self { minimum: 0, desired: 0, slack_weight: weight }
+        Self {
+            minimum: 0,
+            desired: 0,
+            slack_weight: weight,
+        }
     }
 
     pub fn max(self, rhs: SizePolicy) -> SizePolicy {

+ 6 - 0
src/lib.rs

@@ -2,6 +2,7 @@ pub mod component;
 pub mod input;
 pub mod layout;
 pub mod text;
+pub mod theme;
 pub mod ui;
 pub mod widget;
 pub mod window;
@@ -10,3 +11,8 @@ pub mod prelude {
     pub use crate::component::Component;
     pub use crate::widget::Widget;
 }
+
+pub mod re_exports {
+    pub use fontdue;
+    pub use tiny_skia;
+}

+ 43 - 11
src/text.rs

@@ -1,15 +1,47 @@
-pub struct TextRenderer {
-    ui_font: fontdue::Font,
+pub struct TextLine<'l> {
+    line: &'l str,
+    font: &'l fontdue::Font,
+    size: f32,
 }
 
-impl TextRenderer {
-    pub(crate) fn new() -> Self {
-        let ui_font = fontdue::Font::from_bytes(
-            std::fs::read("/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf")
-                .unwrap(),
-            fontdue::FontSettings::default(),
-        )
-        .unwrap();
-        Self { ui_font: ui_font }
+impl<'l> TextLine<'l> {
+    pub(crate) fn new(font: &'l fontdue::Font, line: &'l str, size: f32) -> Self {
+        Self { line, font, size }
+    }
+
+    pub fn render_line(&self) -> tiny_skia::Pixmap {
+        // pass 1: determine required size
+        let mut total_width = 0;
+        let mut last_char = None;
+        for ch in self.line.chars() {
+            let metrics = self.font.metrics(ch, self.size);
+
+            let glyph_width = if let Some(last) = last_char.take() {
+                metrics.advance_width + self.font.horizontal_kern(last, ch, self.size).unwrap_or(0.)
+            } else {
+                metrics.advance_width
+            };
+
+            total_width += glyph_width.round() as u32;
+
+            last_char = Some(ch);
+        }
+        println!("width: {total_width}");
+
+        let line_metrics = self.font.horizontal_line_metrics(self.size).unwrap();
+        let mut pixmap = tiny_skia::Pixmap::new(total_width, line_metrics.new_line_size.ceil() as u32).unwrap();
+
+        pixmap.fill(tiny_skia::Color::from_rgba8(128, 192, 128, 255));
+
+        // pass 2: generate pixmap
+        // todo!()
+
+        pixmap
     }
 }
+
+pub struct TextBuffer {
+    lines: Vec<String>,
+}
+
+impl TextBuffer {}

+ 29 - 0
src/theme.rs

@@ -0,0 +1,29 @@
+use tiny_skia::Color;
+
+use crate::text::TextLine;
+
+pub struct Theme {
+    pub border: Color,
+    pub background: Color,
+    pub foreground: Color,
+
+    pub ui_font: fontdue::Font,
+    pub ui_font_size: f32,
+}
+
+impl Theme {
+    pub fn make_line<'l>(&'l self, text: &'l str) -> TextLine<'l> {
+        TextLine::new(&self.ui_font, text, self.ui_font_size)
+    }
+
+    pub fn default_with_font(ui_font: fontdue::Font) -> Self {
+        Self {
+            border: Color::from_rgba8(0, 0, 0, 255),
+            background: Color::from_rgba8(0, 0, 0, 255),
+            foreground: Color::from_rgba8(0, 0, 0, 255),
+
+            ui_font,
+            ui_font_size: 12.0,
+        }
+    }
+}

+ 42 - 17
src/ui.rs

@@ -1,7 +1,10 @@
 use std::collections::HashMap;
 
 use crate::{
-    component::Component, layout::{LayoutCache, LayoutNode}, text::TextRenderer, window::{Window, WindowBuilder, WindowComponent, WindowEvent, WindowStateAccess}
+    component::Component,
+    layout::{LayoutCache, LayoutNode},
+    theme::Theme,
+    window::{Window, WindowBuilder, WindowComponent, WindowEvent, WindowStateAccess},
 };
 
 #[derive(Debug)]
@@ -10,15 +13,16 @@ pub enum UIControlMsg {
 }
 
 pub trait UIComponent: Sized + Component<ParentMsg = UIControlMsg> {
-    fn init(&mut self, ui_handle: UIHandle);
-    fn poll(&mut self) -> Vec<UIControlMsg>;
+    fn init(&mut self, ui_handle: &mut UIHandle);
+    fn poll(&mut self, ui_handle: &mut UIHandle) -> Vec<UIControlMsg>;
 }
 
 pub(crate) struct UIState {
     pub(crate) layout_cache: std::rc::Rc<LayoutCache>,
     pub(crate) window_states:
         HashMap<winit::window::WindowId, std::rc::Weak<dyn WindowStateAccess>>,
-    pub(crate) text_renderer: TextRenderer,
+    // pub(crate) text_renderer: TextRenderer,
+    pub(crate) theme: Theme,
 }
 
 pub struct UIHandle<'l> {
@@ -34,6 +38,10 @@ impl<'l> UIHandle<'l> {
     pub fn new_layout_node(&self) -> LayoutNode {
         LayoutNode::new(self.state.layout_cache.clone())
     }
+
+    pub fn theme(&self) -> &Theme {
+        &self.state.theme
+    }
 }
 
 pub struct UI<UIC: UIComponent> {
@@ -55,7 +63,17 @@ impl<UIC: UIComponent> UI<UIC> {
             state: UIState {
                 layout_cache: lcache,
                 window_states: Default::default(),
-                text_renderer: TextRenderer::new(),
+                // text_renderer: TextRenderer::new(),
+                theme: Theme::default_with_font(
+                    fontdue::Font::from_bytes(
+                        std::fs::read(
+                            "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
+                        )
+                        .unwrap(),
+                        Default::default(),
+                    )
+                    .unwrap(),
+                ),
             },
             event_loop: Some(eloop),
             ui_component: uic,
@@ -100,7 +118,10 @@ impl<UIC: UIComponent> UI<UIC> {
     }
 
     fn pump_events(&mut self, eloop: &winit::event_loop::ActiveEventLoop) {
-        let evts = self.ui_component.poll();
+        let evts = self.ui_component.poll(&mut UIHandle {
+            eloop,
+            state: &mut self.state,
+        });
         for evt in evts.into_iter() {
             match evt {
                 UIControlMsg::Terminate => eloop.exit(),
@@ -112,7 +133,7 @@ impl<UIC: UIComponent> UI<UIC> {
 impl<UIC: UIComponent> winit::application::ApplicationHandler<()> for UI<UIC> {
     fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
         if !self.initialized {
-            self.ui_component.init(UIHandle {
+            self.ui_component.init(&mut UIHandle {
                 eloop: event_loop,
                 state: &mut self.state,
             });
@@ -156,14 +177,14 @@ impl<UIC: UIComponent> winit::application::ApplicationHandler<()> for UI<UIC> {
 
 pub struct OpinionatedUIComponent<'l, WC: WindowComponent> {
     window: Option<Window<WC>>,
-    builder: Option<Box<dyn FnOnce(&UIHandle) -> WC + 'l>>,
+    builder: Option<Box<dyn FnOnce(&mut UIHandle) -> WC + 'l>>,
 }
 
-impl<'l, WC: WindowComponent> OpinionatedUIComponent<'l, WC> {
-    
-}
+impl<'l, WC: WindowComponent> OpinionatedUIComponent<'l, WC> {}
 
-impl<'l, WC: WindowComponent<ParentMsg = UIControlMsg>> Component for OpinionatedUIComponent<'l, WC> {
+impl<'l, WC: WindowComponent<ParentMsg = UIControlMsg>> Component
+    for OpinionatedUIComponent<'l, WC>
+{
     type ParentMsg = UIControlMsg;
     type Msg = UIControlMsg;
 
@@ -172,22 +193,26 @@ impl<'l, WC: WindowComponent<ParentMsg = UIControlMsg>> Component for Opinionate
     }
 }
 
-impl<'l, WC: WindowComponent<ParentMsg = UIControlMsg>> UIComponent for OpinionatedUIComponent<'l, WC> {
-    fn init(&mut self, mut ui_handle: UIHandle) {
+impl<'l, WC: WindowComponent<ParentMsg = UIControlMsg>> UIComponent
+    for OpinionatedUIComponent<'l, WC>
+{
+    fn init(&mut self, ui_handle: &mut UIHandle) {
         if let Some(builder) = self.builder.take() {
             self.window = Some(ui_handle.window_builder().build(builder));
         }
     }
-    fn poll(&mut self) -> Vec<UIControlMsg> {
+    fn poll(&mut self, uih: &mut UIHandle) -> Vec<UIControlMsg> {
         if let Some(window) = self.window.as_mut() {
-            window.poll()
+            window.poll(uih)
         } else {
             vec![]
         }
     }
 }
 
-pub fn make_opinionated_ui<'l, WC: WindowComponent<ParentMsg = UIControlMsg>>(wc: impl 'l + FnOnce(&UIHandle) -> WC) -> UI<OpinionatedUIComponent<'l, WC>> {
+pub fn make_opinionated_ui<'l, WC: WindowComponent<ParentMsg = UIControlMsg>>(
+    wc: impl 'l + FnOnce(&mut UIHandle) -> WC,
+) -> UI<OpinionatedUIComponent<'l, WC>> {
     let ouic = OpinionatedUIComponent {
         window: None,
         builder: Some(Box::new(wc)),

+ 15 - 5
src/widget.rs

@@ -4,6 +4,7 @@ use crate::{
     component::Component,
     input::InputState,
     layout::{LayoutCache, LayoutNode, LayoutNodeAccess, LeafLayoutNode, SizePolicy},
+    theme::Theme,
     ui::UIHandle,
 };
 
@@ -16,12 +17,15 @@ pub use frame::Frame;
 pub use group::PlainGroup;
 pub use label::Label;*/
 
+mod label;
+pub use label::Label;
+
 pub trait Widget<C: Component> {
-    fn poll(&mut self, input_state: Option<&InputState>) -> Vec<C::Msg>;
+    fn poll(&mut self, uih: &mut UIHandle, input_state: Option<&InputState>) -> Vec<C::Msg>;
 
     fn layout_node(&self) -> LayoutNodeAccess;
     fn layout_node_mut(&mut self) -> &mut LayoutNode;
-    fn render(&self, target: &mut tiny_skia::Pixmap, ti: &());
+    fn render(&self, theme: &Theme, target: &mut tiny_skia::Pixmap);
 }
 
 pub struct Spacer<C: Component> {
@@ -32,7 +36,9 @@ pub struct Spacer<C: Component> {
 impl<C: Component> Spacer<C> {
     pub fn new(uih: &UIHandle) -> Self {
         let mut lnode = LeafLayoutNode::new(uih.new_layout_node());
-        lnode.set_width_policy(SizePolicy::expands(1)).set_height_policy(SizePolicy::expands(1));
+        lnode
+            .set_width_policy(SizePolicy::expands(1))
+            .set_height_policy(SizePolicy::expands(1));
         Self {
             lnode,
             _ghost: Default::default(),
@@ -41,7 +47,11 @@ impl<C: Component> Spacer<C> {
 }
 
 impl<C: Component> Widget<C> for Spacer<C> {
-    fn poll(&mut self, input_state: Option<&InputState>) -> Vec<<C as Component>::Msg> {
+    fn poll(
+        &mut self,
+        _uih: &mut UIHandle,
+        _input_state: Option<&InputState>,
+    ) -> Vec<<C as Component>::Msg> {
         vec![]
     }
 
@@ -52,7 +62,7 @@ impl<C: Component> Widget<C> for Spacer<C> {
         self.lnode.deref_mut()
     }
 
-    fn render(&self, target: &mut tiny_skia::Pixmap, ti: &()) {
+    fn render(&self, theme: &Theme, target: &mut tiny_skia::Pixmap) {
         let mut path = tiny_skia::PathBuilder::new();
         path.push_rect(self.layout_node().cached_rect().unwrap().to_rect());
         let path = path.finish().unwrap();

+ 0 - 80
src/widget/context.rs

@@ -1,80 +0,0 @@
-use std::collections::HashMap;
-
-use super::{Widget, WidgetID};
-
-#[derive(Default)]
-pub struct WidgetContext<Message> {
-    mappers: HashMap<WidgetID, Box<dyn std::any::Any>>,
-    queue: Vec<Message>,
-    _ghost: std::marker::PhantomData<Message>,
-}
-
-impl<Message: 'static> WidgetContext<Message> {
-    pub fn register<W: Widget>(&mut self, id: WidgetID, cb: impl Fn(W::Event) -> Option<Message> + 'static) {
-        let mapper = Box::new(cb) as Box<dyn Fn(W::Event) -> Option<Message>>;
-        self.mappers.insert(id, Box::new(mapper));
-    }
-    pub fn clear(&mut self, id: WidgetID) {
-        self.mappers.remove(&id);
-    }
-}
-
-pub trait WidgetContextAccess<Event> {
-    fn sink(&mut self, id: WidgetID, event: Event);
-}
-
-impl<Event: 'static, Message: 'static> WidgetContextAccess<Event> for WidgetContext<Message> {
-    fn sink(&mut self, id: WidgetID, event: Event) {
-        let Some(mapper) = self.mappers.get(&id) else {
-            return
-        };
-
-        let mapper = mapper.downcast_ref::<Box<dyn Fn(Event) -> Option<Message>>>().expect("invalid mapper");
-
-        self.queue.extend((*mapper)(event).into_iter());
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::widget::{Widget, WidgetID};
-
-    use super::WidgetContext;
-
-    struct SimpleWidget(WidgetID);
-    impl Widget for SimpleWidget {
-        type Event = usize;
-
-        fn id(&self) -> crate::widget::WidgetID { self.0 }
-        fn layout_node(&self) -> &dyn crate::layout::LayoutNodeAccess {
-            todo!()
-        }
-        fn poll(&self, _input_info: (), comp: &mut dyn super::WidgetContextAccess<Self::Event>) {
-            comp.sink(self.id(), 42);
-        }
-        fn render(&self, _surface: ()) {
-            todo!()
-        }
-    }
-
-    #[test]
-    fn sink_test() {
-        let mut wcon = WidgetContext::<()>::default();
-
-        let id1 = WidgetID::new();
-        let sw1 = SimpleWidget(id1);
-
-        let id2 = WidgetID::new();
-        let sw2 = SimpleWidget(id2);
-
-        wcon.register::<SimpleWidget>(id2, |v| {
-            println!("cb: {v}");
-            Some(())
-        });
-
-        sw1.poll((), &mut wcon);
-        sw2.poll((), &mut wcon);
-
-        assert_eq!(wcon.queue, vec![()]);
-    }
-}

+ 28 - 21
src/widget/label.rs

@@ -1,32 +1,27 @@
-use crate::{
-    geom::{IPoint, IRect}, layout::{LayoutNode, LayoutNodeAccess, LeafLayoutNode}, platform::{
-        render::{Colour, Painter, Surface}, text::TextBuffer, PlatformPainter, PlatformSpec, TextInterface
-    }, Component, Widget
-};
+use crate::{component::Component, layout::{LayoutNode, LayoutNodeAccess, LeafLayoutNode, SizePolicy}, theme::Theme, ui::UIHandle};
 
-use super::BuildContext;
+use super::Widget;
 
-pub struct Label<P: PlatformSpec, C: Component> {
+pub struct Label<C: Component> {
     lnode: LeafLayoutNode,
-    buffer: TextBuffer,
-    _ghost: std::marker::PhantomData<(P, C)>,
+    text: String,
+    _ghost: std::marker::PhantomData<C>,
 }
 
-impl<P: PlatformSpec, C: Component> Label<P, C> {
-    pub fn new(bc: &BuildContext) -> Self {
+impl<C: Component> Label<C> {
+    pub fn new(uih: &UIHandle) -> Self {
+        let mut node = uih.new_layout_node();
+        node.set_width_policy(SizePolicy::expands(1)).set_height_policy(SizePolicy { minimum: 20, desired: 20, slack_weight: 0 });
         Self {
-            lnode: LayoutNode::new(bc.lcache.clone()).into(),
-            buffer: TextBuffer::new("example text here"),
+            lnode: LeafLayoutNode::new(node),
+            text: "example text here".to_string(),
             _ghost: Default::default(),
         }
     }
 }
 
-impl<P: PlatformSpec, C: Component> Widget<P, C> for Label<P, C> {
-    fn poll(
-        &mut self,
-        input_state: Option<&crate::input::InputState>,
-    ) -> Vec<<C as Component>::Msg> {
+impl<C: Component> Widget<C> for Label<C> {
+    fn poll(&mut self, _uih: &mut UIHandle, _input_state: Option<&crate::input::InputState>) -> Vec<<C as Component>::Msg> {
         vec![]
     }
 
@@ -37,12 +32,24 @@ impl<P: PlatformSpec, C: Component> Widget<P, C> for Label<P, C> {
         &mut self.lnode
     }
 
-    fn render(&self, painter: &mut PlatformPainter<P>, ti: &P::Text) {
-        painter.fill(
+    fn render(&self, theme: &Theme, surface: &mut tiny_skia::Pixmap) {
+        let line = theme.make_line(self.text.as_str());
+        let rendered = line.render_line();
+
+        let render_location = self.layout_node().cached_rect().unwrap().to_rect();
+
+        surface.fill_rect(render_location, &tiny_skia::Paint {
+            shader: tiny_skia::Pattern::new(
+                rendered.as_ref(), tiny_skia::SpreadMode::Pad, tiny_skia::FilterQuality::Nearest, 1.0, tiny_skia::Transform::identity()
+            ),
+            blend_mode: tiny_skia::BlendMode::SourceOver,
+            ..Default::default()
+        }, tiny_skia::Transform::identity(), None);
+        /*painter.fill(
             self.layout_node().cached_rect().unwrap(),
             Colour::new_rgb(64, 64, 64),
         );
         let text_surf = ti.render_text(&self.buffer, 0.0);
-        painter.blit(&text_surf, IRect::new_from_size(IPoint::ORIGIN, text_surf.size()), self.layout_node().cached_rect().unwrap().tl());
+        painter.blit(&text_surf, IRect::new_from_size(IPoint::ORIGIN, text_surf.size()), self.layout_node().cached_rect().unwrap().tl());*/
     }
 }

+ 9 - 9
src/window.rs

@@ -33,7 +33,7 @@ pub(crate) struct WindowState<WC: WindowComponent> {
 }
 
 impl<WC: WindowComponent> WindowState<WC> {
-    fn redraw(&mut self) {
+    fn redraw(&mut self, uih: &UIHandle) {
         self.draw_pending = false;
 
         let size = self.window.inner_size();
@@ -53,7 +53,7 @@ impl<WC: WindowComponent> WindowState<WC> {
 
         let mut pixmap = tiny_skia::Pixmap::new(size.width, size.height).unwrap();
         layout::recalculate(LayoutNodeAccess::new(&layout));
-        self.wc.root_widget().render(&mut pixmap, &());
+        self.wc.root_widget().render(uih.theme(), &mut pixmap);
 
         // expensive ABGR -> RGB0 conversion
         let mut pxdata = pixmap.data();
@@ -65,18 +65,18 @@ impl<WC: WindowComponent> WindowState<WC> {
         buf.present().unwrap();
     }
 
-    fn poll(&mut self) -> Vec<WC::ParentMsg> {
+    fn poll(&mut self, uih: &mut UIHandle) -> Vec<WC::ParentMsg> {
         let mut ret = vec![];
         for we in self.events.drain(..) {
             let mapped = self.wc.map_window_event(we);
             ret.extend(self.wc.process_all(mapped.into_iter()).into_iter());
         }
 
-        let evts = self.wc.root_widget_mut().poll(Some(&self.istate));
+        let evts = self.wc.root_widget_mut().poll(uih, Some(&self.istate));
         ret.extend(self.wc.process_all(evts.into_iter()));
 
         if self.draw_pending {
-            self.redraw();
+            self.redraw(uih);
         }
 
         ret
@@ -106,7 +106,7 @@ impl<'r, 'l: 'r> WindowBuilder<'r, 'l> {
         Self { ui_handle }
     }
 
-    pub fn build<WC: WindowComponent>(self, wc: impl FnOnce(&UIHandle) -> WC) -> Window<WC> {
+    pub fn build<WC: WindowComponent>(self, wc: impl FnOnce(&mut UIHandle) -> WC) -> Window<WC> {
         let window = Rc::new(
             self.ui_handle
                 .eloop
@@ -118,7 +118,7 @@ impl<'r, 'l: 'r> WindowBuilder<'r, 'l> {
         let ctx = softbuffer::Context::new(window.clone()).unwrap();
         let surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap();
 
-        let wc = wc(&self.ui_handle);
+        let wc = wc(self.ui_handle);
         let wstate = Rc::new(RefCell::new(WindowState {
             wc,
             root_node: LayoutNode::new(self.ui_handle.state.layout_cache.clone()),
@@ -143,7 +143,7 @@ pub struct Window<WC: WindowComponent> {
 }
 
 impl<WC: WindowComponent> Window<WC> {
-    pub fn poll(&mut self) -> Vec<WC::ParentMsg> {
-        self.state.borrow_mut().poll()
+    pub fn poll(&mut self, uih: &mut UIHandle) -> Vec<WC::ParentMsg> {
+        self.state.borrow_mut().poll(uih)
     }
 }