Browse Source

Swapped LayoutNodeContainer for LayoutTreeNode.

Kestrel 11 tháng trước cách đây
mục cha
commit
11e20618f1
14 tập tin đã thay đổi với 234 bổ sung261 xóa
  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
+    }
+}