Quellcode durchsuchen

Swapped LayoutNodeContainer for LayoutTreeNode.

Kestrel vor 6 Monaten
Ursprung
Commit
11e20618f1
14 geänderte Dateien mit 234 neuen und 261 gelöschten Zeilen
  1. 71 76
      src/layout.rs
  2. 2 2
      src/layout/arr/line.rs
  3. 0 1
      src/lib.rs
  4. 7 5
      src/render.rs
  5. 8 4
      src/render/text.rs
  6. 0 86
      src/text.rs
  7. 7 12
      src/theme.rs
  8. 8 1
      src/ui.rs
  9. 36 9
      src/widget/button.rs
  10. 6 8
      src/widget/frame.rs
  11. 22 20
      src/widget/group.rs
  12. 27 31
      src/widget/label.rs
  13. 4 1
      src/widget/text_edit.rs
  14. 36 5
      src/window.rs

+ 71 - 76
src/layout.rs

@@ -512,14 +512,14 @@ pub fn dump_tree_json<W: ?Sized + std::io::Write>(
 
 /// Iterator implementation to iterate across children of a LayoutNodeContainer
 #[derive(Clone)]
-pub struct LayoutChildIter<'l> {
-    lnc: &'l dyn LayoutNodeContainer,
+struct LayoutChildIter<'l> {
+    lna: &'l LayoutNodeAccess<'l>,
     next_index: usize,
 }
 
 impl<'l> LayoutChildIter<'l> {
-    fn new(lnc: &'l dyn LayoutNodeContainer) -> Self {
-        Self { lnc, next_index: 0 }
+    fn new(lna: &'l LayoutNodeAccess) -> Self {
+        Self { lna, next_index: 0 }
     }
 }
 
@@ -528,80 +528,98 @@ impl<'l> Iterator for LayoutChildIter<'l> {
 
     fn next(&mut self) -> Option<Self::Item> {
         let index = self.next_index;
-        if index >= self.lnc.layout_child_count() {
+        if index >= self.lna.child_count() {
             None
         } else {
             self.next_index += 1;
-            self.lnc.layout_child(index)
+            self.lna.child(index)
         }
     }
 }
 
-/// Wrapper struct to access a [`LayoutNodeContainer`].
+pub trait LayoutTreeNode<Tag: 'static> {
+    fn current_node(&self) -> &LayoutNode;
+    fn child_count(&self) -> usize;
+    fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>>;
+}
+
+const DYNPTR_WIDTH: usize =
+    std::mem::size_of::<&'static dyn LayoutTreeNode<()>>() / std::mem::size_of::<*const ()>();
+type DynPtr = [*const (); DYNPTR_WIDTH];
+
+fn current_node_dispatch<'l, Tag: 'static>(dynptr: DynPtr) -> &'l LayoutNode {
+    unsafe { std::mem::transmute::<_, &'l dyn LayoutTreeNode<Tag>>(dynptr).current_node() }
+}
+
+fn child_count_dispatch<'l, Tag: 'static>(dynptr: DynPtr) -> usize {
+    unsafe { std::mem::transmute::<_, &'l dyn LayoutTreeNode<Tag>>(dynptr).child_count() }
+}
+
+fn child_dispatch<'l, Tag: 'static>(dynptr: DynPtr, ndx: usize) -> Option<LayoutNodeAccess<'l>> {
+    unsafe { std::mem::transmute::<_, &'l dyn LayoutTreeNode<Tag>>(dynptr).child(ndx) }
+}
+
 #[derive(Clone, Copy)]
 pub struct LayoutNodeAccess<'l> {
-    // general problem here:
-    // - this needs to be a reference to make the LNA clone/copy easily,
-    // - the reference means there needs to be a persistent object,
-    // - using the widget as a persistent object means only one level of heirarchy is supported,
-    // - so either:
-    //      a) nodes are stored in some sort of container in the widget, which makes access annoying, or
-    //      b) LayoutNodeContainer is given a tag type parameter to allow multiple implementations on a single type? this somewhat complicates the LayoutNodeAccess type
-    // this could *also* be changed to store a Box<dyn LayoutNodeContainer>, which would allow for a different set of ownership semantics; the result is significantly less efficient as it requires allocations to occur during tree traversal
-    // could require each external node linkage source to return a slice of all child nodes?
-    // - still runs into boxing requirements
-    // could also accept closures that are passed reference to slice of child nodes
-    //
-    // another possible solution here is to simply return an iterator across children
-    // - this does not require an object in quite the same way?
-    // - ExactSizeIterator handles most of the functionality nicely
-    // - and this way there need not be a single trait impl on the widget type; this allows for a
-    // full hierarchy to be specified
-    //
-    // alternatively, simply provide better mechanism for tree storage / easy node access
-    // - simple macro that generates types and impl automatically?
-    lnc: &'l dyn LayoutNodeContainer,
+    current_node_func: fn(DynPtr) -> &'l LayoutNode,
+    child_count_func: fn(DynPtr) -> usize,
+    child_func: fn(DynPtr, ndx: usize) -> Option<LayoutNodeAccess<'l>>,
+    dynptr: DynPtr,
 }
 
 impl<'l> LayoutNodeAccess<'l> {
-    pub fn new(lnc: &'l dyn LayoutNodeContainer) -> Self {
-        Self { lnc }
+    pub fn new<Tag: 'static>(from: &'l dyn LayoutTreeNode<Tag>) -> Self {
+        assert_eq!(
+            std::mem::size_of::<&dyn LayoutTreeNode<Tag>>(),
+            std::mem::size_of::<DynPtr>()
+        );
+        assert_eq!(
+            std::mem::align_of::<&dyn LayoutTreeNode<Tag>>(),
+            std::mem::align_of::<DynPtr>()
+        );
+        Self {
+            current_node_func: current_node_dispatch::<Tag>,
+            child_count_func: child_count_dispatch::<Tag>,
+            child_func: child_dispatch::<Tag>,
+            dynptr: unsafe { std::mem::transmute(from) },
+        }
     }
-}
 
-impl<'l> Deref for LayoutNodeAccess<'l> {
-    type Target = LayoutNode;
-    fn deref(&self) -> &Self::Target {
-        self.lnc.layout_node()
+    pub fn current_node(&self) -> &'l LayoutNode {
+        (self.current_node_func)(self.dynptr)
     }
-}
 
-impl<'l> LayoutNodeAccess<'l> {
-    pub fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'l>> {
-        self.lnc.layout_child(ndx)
+    pub fn child_count(&self) -> usize {
+        (self.child_count_func)(self.dynptr)
     }
-    pub fn child_len(&self) -> usize {
-        self.lnc.layout_child_count()
+
+    pub fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'l>> {
+        (self.child_func)(self.dynptr, ndx)
     }
-    pub fn child_iter(&self) -> LayoutChildIter {
-        LayoutChildIter::new(self.lnc)
+
+    pub fn child_iter(&self) -> impl Iterator<Item = LayoutNodeAccess> + Clone {
+        LayoutChildIter::new(&self)
     }
 }
 
-/// Data source trait for LayoutNodeAccess.
-pub trait LayoutNodeContainer {
-    fn layout_node(&self) -> &LayoutNode;
-    fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>>;
-    fn layout_child_count(&self) -> usize;
+impl<'l> Deref for LayoutNodeAccess<'l> {
+    type Target = LayoutNode;
+    fn deref(&self) -> &Self::Target {
+        (self.current_node_func)(self.dynptr)
+    }
 }
 
-/// Helper struct to store a leaf LayoutNode and automatically provide a LayoutNodeContainer impl
+/// Helper struct to store a leaf LayoutNode and automatically provide a LayoutTreeNode impl
 pub struct LeafLayoutNode(LayoutNode);
 
 impl LeafLayoutNode {
     pub fn new(ln: LayoutNode) -> Self {
         Self(ln)
     }
+
+    pub fn access(&self) -> LayoutNodeAccess {
+        LayoutNodeAccess::new(self)
+    }
 }
 
 impl From<LayoutNode> for LeafLayoutNode {
@@ -623,37 +641,14 @@ impl std::ops::DerefMut for LeafLayoutNode {
     }
 }
 
-impl LayoutNodeContainer for LeafLayoutNode {
-    fn layout_node(&self) -> &LayoutNode {
+impl LayoutTreeNode<()> for LeafLayoutNode {
+    fn current_node(&self) -> &LayoutNode {
         &self.0
     }
-    fn layout_child(&self, _ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+    fn child(&self, _ndx: usize) -> Option<LayoutNodeAccess<'_>> {
         None
     }
-    fn layout_child_count(&self) -> usize {
+    fn child_count(&self) -> usize {
         0
     }
 }
-
-/// Helper LayoutNodeContainer implementation for LayoutNodes with one child
-pub struct LinearAccess<'l>(&'l LayoutNode, LayoutNodeAccess<'l>);
-impl<'l> LinearAccess<'l> {
-    pub fn new(parent: &'l LayoutNode, child: LayoutNodeAccess<'l>) -> Self {
-        Self(parent, child)
-    }
-}
-impl<'l> LayoutNodeContainer for LinearAccess<'l> {
-    fn layout_node(&self) -> &LayoutNode {
-        self.0
-    }
-    fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess> {
-        if ndx == 0 {
-            Some(self.1)
-        } else {
-            None
-        }
-    }
-    fn layout_child_count(&self) -> usize {
-        1
-    }
-}

+ 2 - 2
src/layout/arr/line.rs

@@ -22,7 +22,7 @@ impl LineArrangement {
 
 impl ArrangementCalculator for LineArrangement {
     fn arrange_step(&self, node: LayoutNodeAccess) -> SizePolicy2D {
-        if node.child_len() == 0 {
+        if node.child_count() == 0 {
             return SizePolicy2D::new(node.width_policy, node.height_policy)
                 + node.margin_as_policy();
         }
@@ -66,7 +66,7 @@ impl ArrangementCalculator for LineArrangement {
             }
         });
 
-        if node.child_len() == 0 {
+        if node.child_count() == 0 {
             return;
         }
 

+ 0 - 1
src/lib.rs

@@ -4,7 +4,6 @@ pub mod component;
 pub mod font;
 pub mod input;
 pub mod layout;
-pub mod text;
 pub mod theme;
 pub mod ui;
 pub mod widget;

+ 7 - 5
src/render.rs

@@ -1,12 +1,15 @@
-use std::{cell::{RefCell, Cell}, rc::Rc};
+use std::{
+    cell::{Cell, RefCell},
+    rc::Rc,
+};
 
 use kahlo::{
     colour::Colour,
     math::{PixelBox, PixelSize},
-    prelude::{KahloOps, BitmapAccess},
+    prelude::{BitmapAccess, KahloOps},
 };
 
-use crate::{layout::LayoutNode, theme::Theme, window::RenderFormat, ui::UIHandle};
+use crate::{layout::LayoutNode, theme::Theme, ui::UIHandle, window::RenderFormat};
 
 mod text;
 pub use text::TextCache;
@@ -81,7 +84,7 @@ impl<'l, 'r: 'l, 'data: 'r> NodeRenderTarget<'l, 'r, 'data> {
     pub fn is_skipped(&self) -> bool {
         match self {
             Self::Render { .. } => false,
-            Self::SkipRender(_) => true
+            Self::SkipRender(_) => true,
         }
     }
 }
@@ -89,7 +92,6 @@ impl<'l, 'r: 'l, 'data: 'r> NodeRenderTarget<'l, 'r, 'data> {
 impl<'l, 'r: 'l, 'data: 'r> NodeRenderTarget<'l, 'r, 'data> {
     pub fn finish(self) {}
 
-
     pub fn fill(mut self, cc: ColourChoice) -> Self {
         if let Self::Render { rt, area, .. } = &mut self {
             rt.target

+ 8 - 4
src/render/text.rs

@@ -1,8 +1,8 @@
 use std::{cell::RefCell, rc::Rc};
 
 use kahlo::{
-    math::{PixelBox, PixelSize, PixelPoint},
-    prelude::{KahloOps, BitmapAccess},
+    math::{PixelBox, PixelPoint, PixelSize},
+    prelude::{BitmapAccess, KahloOps},
 };
 
 use crate::layout::LayoutNode;
@@ -60,7 +60,7 @@ impl TextCache {
     pub(crate) fn with_rendered<R>(&self, func: impl FnOnce(&kahlo::Alphamap) -> R) -> Option<R> {
         let mut rendered = self.rendered.borrow_mut();
         if let Some(amap) = rendered.as_ref() {
-            return Some(func(amap))
+            return Some(func(amap));
         }
 
         *rendered = self.render(self.pxsize);
@@ -74,7 +74,11 @@ impl TextCache {
         Some(amap)
     }
 
-    fn generate_baseline_offsets<'l, 's: 'l>(&'s self, pxsize: f32, text: &'l str) -> impl 'l + Iterator<Item = (u32, u32, char)> {
+    fn generate_baseline_offsets<'l, 's: 'l>(
+        &'s self,
+        pxsize: f32,
+        text: &'l str,
+    ) -> impl 'l + Iterator<Item = (u32, u32, char)> {
         let mut running_offset = 0;
         let mut last_char = None;
 

+ 0 - 86
src/text.rs

@@ -1,86 +0,0 @@
-use kahlo::{
-    math::{PixelBox, PixelPoint, PixelSize},
-    prelude::*,
-};
-
-pub struct TextLine<'l> {
-    line: &'l str,
-    font: &'l fontdue::Font,
-    size: f32,
-}
-
-impl<'l> TextLine<'l> {
-    pub(crate) fn new(font: &'l fontdue::Font, line: &'l str, size: f32) -> Self {
-        Self { line, font, size }
-    }
-
-    fn generate_baseline_offsets(&self) -> impl '_ + Iterator<Item = (u32, u32, char)> {
-        let mut running_offset = 0;
-        let mut last_char = None;
-
-        self.line.chars().map(move |ch| {
-            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
-            };
-
-            last_char = Some(ch);
-
-            let expose = (running_offset, glyph_width.round() as u32, ch);
-            running_offset += glyph_width.round() as u32;
-            expose
-        })
-    }
-
-    pub fn calculate_size(&self) -> PixelSize {
-        // pass: determine required size
-        let total_width = match self.generate_baseline_offsets().last() {
-            Some(ofs) => ofs.0 + ofs.1,
-            None => 0,
-        };
-        let line_metrics = self.font.horizontal_line_metrics(self.size).unwrap();
-        let line_height = line_metrics.new_line_size.ceil() as i32;
-
-        PixelSize::new(total_width as i32, line_height)
-    }
-
-    pub fn render_line(&self) -> (kahlo::Alphamap, i32) {
-        // pass 1: determine required size
-        let alphamap_size = self.calculate_size();
-
-        let line_metrics = self.font.horizontal_line_metrics(self.size).unwrap();
-        let baseline = line_metrics.ascent.ceil() as usize;
-        let mut alphamap =
-            kahlo::Alphamap::new(alphamap_size.width as usize, alphamap_size.height as usize);
-
-        // pass: generate alphamap
-        for (offset, _width, ch) in self.generate_baseline_offsets() {
-            let (metrics, raster) = self.font.rasterize(ch, self.size);
-            let character_alphamap = kahlo::BitmapRef::<kahlo::formats::A8>::new(
-                raster.as_slice(),
-                metrics.width,
-                metrics.height,
-            );
-
-            alphamap.copy_from(
-                &character_alphamap,
-                PixelBox::from_size(character_alphamap.size()),
-                PixelPoint::new(
-                    metrics.xmin + offset as i32,
-                    baseline as i32 - (metrics.height as i32 + metrics.ymin),
-                ),
-            );
-        }
-
-        (alphamap, 0)
-    }
-}
-
-pub struct TextBuffer {
-    lines: Vec<String>,
-}
-
-impl TextBuffer {}

+ 7 - 12
src/theme.rs

@@ -1,9 +1,6 @@
 use std::rc::Rc;
 
-use crate::{
-    font::{fontdb as fdb, FontStore},
-    text::TextLine,
-};
+use crate::font::{fontdb as fdb, FontStore};
 
 use kahlo::colour::Colour;
 
@@ -20,15 +17,13 @@ pub struct Theme {
 }
 
 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 build_default(fs: &FontStore) -> Self {
-        let ui_font = fs.query_for_font(&fdb::Query {
-            families: &[fdb::Family::SansSerif],
-            ..Default::default()
-        }).expect("couldn't load sans-serif font!");
+        let ui_font = fs
+            .query_for_font(&fdb::Query {
+                families: &[fdb::Family::SansSerif],
+                ..Default::default()
+            })
+            .expect("couldn't load sans-serif font!");
 
         Self {
             active: Colour::hex_rgb("#c16e70").unwrap(),

+ 8 - 1
src/ui.rs

@@ -26,7 +26,7 @@ pub(crate) struct UIState {
     pub(crate) window_states:
         HashMap<winit::window::WindowId, std::rc::Weak<dyn WindowStateAccess>>,
 
-    pub(crate) fontstore: crate::font::FontStore,
+    pub(crate) fontstore: FontStore,
     pub(crate) theme: Theme,
 }
 
@@ -47,6 +47,13 @@ impl<'l> UIHandle<'l> {
     pub fn theme(&self) -> &Theme {
         &self.state.theme
     }
+
+    pub fn lookup_font(&self, q: &fontdb::Query) -> Rc<fontdue::Font> {
+        self.state
+            .fontstore
+            .query_for_font(q)
+            .expect("no matching font found")
+    }
 }
 
 pub struct UI<UIC: UIComponent> {

+ 36 - 9
src/widget/button.rs

@@ -4,8 +4,8 @@ use crate::{
     component::Component,
     input::{MouseButton, MouseCheckStatus},
     layout::{
-        HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy,
-        VerticalAlignment,
+        HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutTreeNode, LeafLayoutNode,
+        SizePolicy, VerticalAlignment,
     },
     render::{ColourChoice, RenderTarget, TextCache},
     ui::UIHandle,
@@ -21,13 +21,16 @@ enum ButtonState {
 }
 
 pub struct Button<C: Component> {
-    layout: LayoutNode,
+    layout_root: LayoutNode,
+    layout_label: LeafLayoutNode,
     text_cache: TextCache,
     label: String,
     state: ButtonState,
     hook: Option<Box<dyn Fn() -> Option<C::Msg>>>,
 }
 
+struct RootTag;
+
 impl<C: Component> Button<C> {
     pub fn new(uih: &UIHandle) -> Self {
         let mut layout = uih.new_layout_node();
@@ -42,8 +45,12 @@ impl<C: Component> Button<C> {
             .set_halign(HorizontalAlignment::Centre);
         *layout.margin_mut() = PixelSideOffsets::new_all_same(uih.theme().border_width as i32);
         Self {
-            layout,
-            text_cache: TextCache::new_with_font_and_size(uih.theme().ui_font.clone(), uih.theme().ui_font_size),
+            layout_root: layout,
+            layout_label: uih.new_layout_node().into(),
+            text_cache: TextCache::new_with_font_and_size(
+                uih.theme().ui_font.clone(),
+                uih.theme().ui_font_size,
+            ),
             label: "".to_string(),
             state: ButtonState::Idle,
             hook: None,
@@ -66,6 +73,25 @@ impl<C: Component> Button<C> {
     }
 }
 
+impl<C: Component> LayoutTreeNode<RootTag> for Button<C> {
+    fn current_node(&self) -> &LayoutNode {
+        &self.layout_root
+    }
+
+    fn child_count(&self) -> usize {
+        1
+    }
+
+    fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+        if ndx == 0 {
+            Some(self.layout_label.access())
+        } else {
+            None
+        }
+    }
+}
+
+/*
 impl<C: Component> LayoutNodeContainer for Button<C> {
     fn layout_node(&self) -> &LayoutNode {
         &self.layout
@@ -77,6 +103,7 @@ impl<C: Component> LayoutNodeContainer for Button<C> {
         0
     }
 }
+*/
 
 impl<C: Component> Widget<C> for Button<C> {
     fn poll(
@@ -85,7 +112,7 @@ impl<C: Component> Widget<C> for Button<C> {
         input_state: Option<&crate::input::InputState>,
     ) -> Vec<<C as Component>::Msg> {
         let mut result: Vec<<C as Component>::Msg> = vec![];
-        if let Some((istate, area)) = input_state.zip(self.layout.render_area()) {
+        if let Some((istate, area)) = input_state.zip(self.layout_root.render_area()) {
             let saved = self.state;
             match istate.mouse_status_in(area, MouseButton::Left) {
                 MouseCheckStatus::Idle | MouseCheckStatus::Leave => {
@@ -103,7 +130,7 @@ impl<C: Component> Widget<C> for Button<C> {
                 }
             }
             if self.state != saved {
-                self.layout.render_needed();
+                self.layout_root.render_needed();
             }
         }
 
@@ -116,12 +143,12 @@ impl<C: Component> Widget<C> for Button<C> {
         LayoutNodeAccess::new(self)
     }
     fn layout_node_mut(&mut self) -> &mut LayoutNode {
-        &mut self.layout
+        &mut self.layout_root
     }
 
     fn render<'r, 'data: 'r>(&self, target: &mut RenderTarget<'r, 'data>) {
         target
-            .with_node(&self.layout)
+            .with_node(&self.layout_root)
             .fill(match self.state {
                 ButtonState::Idle => ColourChoice::Background,
                 ButtonState::Hovered => ColourChoice::Panel,

+ 6 - 8
src/widget/frame.rs

@@ -1,12 +1,10 @@
-use kahlo::{math::PixelSideOffsets, prelude::*};
+use kahlo::math::PixelSideOffsets;
 
 use crate::{
     component::Component,
-    layout::{ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutNodeContainer, SizePolicy},
+    layout::{ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutTreeNode, SizePolicy},
     render::{ColourChoice, RenderTarget},
-    theme::Theme,
     ui::UIHandle,
-    window::RenderFormat,
 };
 
 use super::Widget;
@@ -54,18 +52,18 @@ impl<C: Component> Frame<C> {
     }
 }
 
-impl<C: Component> LayoutNodeContainer for Frame<C> {
-    fn layout_node(&self) -> &LayoutNode {
+impl<C: Component> LayoutTreeNode<()> for Frame<C> {
+    fn current_node(&self) -> &LayoutNode {
         &self.layout
     }
-    fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess> {
+    fn child(&self, ndx: usize) -> Option<LayoutNodeAccess> {
         if ndx == 0 {
             Some(self.child.as_ref()?.layout_node())
         } else {
             None
         }
     }
-    fn layout_child_count(&self) -> usize {
+    fn child_count(&self) -> usize {
         if self.child.is_some() {
             1
         } else {

+ 22 - 20
src/widget/group.rs

@@ -6,14 +6,13 @@ use crate::{
     component::Component,
     input::InputState,
     layout::{
-        ChildArrangement, HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutNodeContainer,
+        ChildArrangement, HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutTreeNode,
         SizePolicy, TableCell,
     },
     render::RenderTarget,
     theme::Theme,
     ui::UIHandle,
     widget::Widget,
-    window::RenderFormat,
 };
 
 pub struct PlainGroup<C: Component> {
@@ -61,23 +60,21 @@ impl<C: Component> PlainGroup<C> {
     }
 }
 
-impl<C: Component> LayoutNodeContainer for PlainGroup<C> {
-    fn layout_node(&self) -> &LayoutNode {
+impl<C: Component> LayoutTreeNode<()> for PlainGroup<C> {
+    fn current_node(&self) -> &LayoutNode {
         &self.lnode
     }
-
-    fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess> {
-        self.children.get(ndx).map(|w| w.layout_node())
-    }
-
-    fn layout_child_count(&self) -> usize {
+    fn child_count(&self) -> usize {
         self.children.len()
     }
+    fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+        self.children.get(ndx).map(|w| w.layout_node())
+    }
 }
 
 impl<C: Component> Widget<C> for PlainGroup<C> {
     fn layout_node(&self) -> LayoutNodeAccess {
-        LayoutNodeAccess::new(self)
+        LayoutNodeAccess::new::<()>(self)
     }
     fn layout_node_mut(&mut self) -> &mut LayoutNode {
         &mut self.lnode
@@ -135,19 +132,24 @@ impl<C: Component> FormGroup<C> {
         widget.set_table_cell(TableCell::new(1, self.labels.len()));
         self.widgets.push(widget);
         self.labels.push(
-            Label::new_with_node_and_text(self.label_font.clone(), self.label_font_size, self.lnode.make_new_node(), label)
-                .with_table_cell(TableCell::new(0, self.labels.len()))
-                .with_margins(self.label_margin)
-                .with_halign(HorizontalAlignment::Right),
+            Label::new_with_node_and_text(
+                self.label_font.clone(),
+                self.label_font_size,
+                self.lnode.make_new_node(),
+                label,
+            )
+            .with_table_cell(TableCell::new(0, self.labels.len()))
+            .with_margins(self.label_margin)
+            .with_halign(HorizontalAlignment::Right),
         );
     }
 }
 
-impl<C: Component> LayoutNodeContainer for FormGroup<C> {
-    fn layout_node(&self) -> &LayoutNode {
+impl<C: Component> LayoutTreeNode<()> for FormGroup<C> {
+    fn current_node(&self) -> &LayoutNode {
         &self.lnode
     }
-    fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+    fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
         if ndx < self.labels.len() {
             Some(self.labels[ndx].layout_node())
         } else if ndx < (self.labels.len() + self.widgets.len()) {
@@ -156,14 +158,14 @@ impl<C: Component> LayoutNodeContainer for FormGroup<C> {
             None
         }
     }
-    fn layout_child_count(&self) -> usize {
+    fn child_count(&self) -> usize {
         self.labels.len() + self.widgets.len()
     }
 }
 
 impl<C: Component> Widget<C> for FormGroup<C> {
     fn layout_node(&self) -> LayoutNodeAccess {
-        LayoutNodeAccess::new(self)
+        LayoutNodeAccess::new::<()>(self)
     }
 
     fn layout_node_mut(&mut self) -> &mut LayoutNode {

+ 27 - 31
src/widget/label.rs

@@ -1,6 +1,4 @@
-use std::{cell::RefCell, rc::Rc, ops::DerefMut};
-
-use kahlo::{math::PixelBox, prelude::*};
+use std::rc::Rc;
 
 use crate::{
     component::Component,
@@ -18,7 +16,12 @@ pub struct Label<C: Component> {
 }
 
 impl<C: Component> Label<C> {
-    fn construct(font: Rc<fontdue::Font>, pxsize: f32, mut node: LayoutNode, text: Option<&str>) -> Self {
+    fn construct(
+        font: Rc<fontdue::Font>,
+        pxsize: f32,
+        mut node: LayoutNode,
+        text: Option<&str>,
+    ) -> Self {
         node.set_width_policy(SizePolicy::fixed(0))
             .set_height_policy(SizePolicy::fixed(0));
         let mut cache = TextCache::new_with_font_and_size(font, pxsize);
@@ -31,14 +34,29 @@ impl<C: Component> Label<C> {
     }
 
     pub fn new(uih: &UIHandle) -> Self {
-        Self::construct(uih.theme().ui_font.clone(), uih.theme().ui_font_size, uih.new_layout_node(), None)
+        Self::construct(
+            uih.theme().ui_font.clone(),
+            uih.theme().ui_font_size,
+            uih.new_layout_node(),
+            None,
+        )
     }
 
     pub fn new_with_text(uih: &UIHandle, text: &str) -> Self {
-        Self::construct(uih.theme().ui_font.clone(), uih.theme().ui_font_size, uih.new_layout_node(), Some(text))
+        Self::construct(
+            uih.theme().ui_font.clone(),
+            uih.theme().ui_font_size,
+            uih.new_layout_node(),
+            Some(text),
+        )
     }
 
-    pub fn new_with_node_and_text(font: Rc<fontdue::Font>, pxsize: f32, node: LayoutNode, text: &str) -> Self {
+    pub fn new_with_node_and_text(
+        font: Rc<fontdue::Font>,
+        pxsize: f32,
+        node: LayoutNode,
+        text: &str,
+    ) -> Self {
         Self::construct(font, pxsize, node, Some(text))
     }
 
@@ -47,34 +65,12 @@ impl<C: Component> Label<C> {
     }
 }
 
-impl<C: Component> Label<C> {
-    fn update_hints(&mut self, uih: &UIHandle) {
-        /*let Some(text) = self.text.as_ref() else {
-            return;
-        };*/
-        /*let line = uih.theme().make_line(text.as_str());
-        let (rendered, offset) = line.render_line();
-        let sz = rendered.size();
-        *self.rendered.borrow_mut() = Some(rendered);
-        self.lnode.render_needed();
-
-        let wp = self.lnode.width_policy().with_minimum(sz.width as usize);
-        self.lnode.set_width_policy(wp);
-        let hp = self.lnode.height_policy().with_minimum(sz.height as usize);
-        self.lnode.set_height_policy(hp);*/
-    }
-}
-
 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> {
-        // self.cache.size_hint();
-        /*if self.rendered.borrow().is_none() {
-            self.update_hints(uih);
-        }*/
         self.cache.update_node_size_hint(&mut *self.lnode);
 
         vec![]
@@ -88,8 +84,8 @@ impl<C: Component> Widget<C> for Label<C> {
     }
 
     fn render<'r, 'data: 'r>(&self, target: &mut RenderTarget<'r, 'data>) {
-
-        target.with_node(&self.lnode)
+        target
+            .with_node(&self.lnode)
             .simple_text(&self.cache, crate::render::ColourChoice::Foreground);
 
         /*if !self.lnode.render_check() {

+ 4 - 1
src/widget/text_edit.rs

@@ -27,7 +27,10 @@ impl<C: Component> TextEdit<C> {
 
         Self {
             lnode: LeafLayoutNode::new(lnode),
-            text: TextCache::new_with_font_and_size(uih.theme().ui_font.clone(), uih.theme().ui_font_size),
+            text: TextCache::new_with_font_and_size(
+                uih.theme().ui_font.clone(),
+                uih.theme().ui_font_size,
+            ),
             cursor_pos: None,
             onchange: None,
         }

+ 36 - 5
src/window.rs

@@ -6,7 +6,7 @@ use kahlo::{
     prelude::*,
 };
 
-use crate::layout::{self, LayoutNode, LayoutNodeAccess, LinearAccess};
+use crate::layout::{self, LayoutNode, LayoutNodeAccess, LayoutTreeNode};
 use crate::widget::Widget;
 use crate::{component::Component, ui::UIHandle};
 use crate::{
@@ -57,7 +57,7 @@ impl<WC: WindowComponent> WindowState<WC> {
             area: PixelBox::from_size(PixelSize::new(size.width as i32, size.height as i32)),
         });
 
-        let layout = LinearAccess::new(&self.root_node, self.wc.root_widget().layout_node());
+        let layout = WindowLayoutHelper::new(&self.root_node, self.wc.root_widget().layout_node());
 
         let mut bitmap = self.bitmap.borrow_mut();
         if bitmap.as_ref().map(|v| v.size())
@@ -78,7 +78,7 @@ impl<WC: WindowComponent> WindowState<WC> {
         );
 
         layout::recalculate(LayoutNodeAccess::new(&layout));
-        let before = std::time::Instant::now();
+        // let before = std::time::Instant::now();
 
         // save the layout tree for later dumping
         let mut pre_render_dump = format!("render generation: {}\n", self.render_generation);
@@ -96,8 +96,9 @@ impl<WC: WindowComponent> WindowState<WC> {
             PixelBox::from_size(bitmap.size()),
             PixelPoint::zero(),
         );
-        let after = std::time::Instant::now();
+        // let after = std::time::Instant::now();
 
+        /*
         // put the render time on the screen
         // we're going to do this the very dumb way for now
         let render_time = format!(
@@ -115,7 +116,7 @@ impl<WC: WindowComponent> WindowState<WC> {
             ),
             uih.theme().foreground,
             kahlo::colour::BlendMode::Simple,
-        );
+        );*/
 
         // now put the pre-render layout dump on the window
         // again, we're doing this the dumb way
@@ -260,3 +261,33 @@ impl<WC: WindowComponent> Window<WC> {
         self.state.borrow_mut().poll(uih)
     }
 }
+
+struct WindowLayoutHelper<'l> {
+    window_node: &'l LayoutNode,
+    widget_node: LayoutNodeAccess<'l>,
+}
+
+impl<'l> WindowLayoutHelper<'l> {
+    fn new(window: &'l LayoutNode, widget: LayoutNodeAccess<'l>) -> Self {
+        Self {
+            window_node: window,
+            widget_node: widget,
+        }
+    }
+}
+
+impl<'l> LayoutTreeNode<()> for WindowLayoutHelper<'l> {
+    fn current_node(&self) -> &LayoutNode {
+        self.window_node
+    }
+    fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+        if ndx == 0 {
+            Some(self.widget_node)
+        } else {
+            None
+        }
+    }
+    fn child_count(&self) -> usize {
+        1
+    }
+}