소스 검색

Reorganization and more documentation.

Kestrel 6 달 전
부모
커밋
9954f78bc9
18개의 변경된 파일308개의 추가작업 그리고 159개의 파일을 삭제
  1. 14 0
      README.md
  2. 0 2
      examples/text_edits.rs
  3. 0 10
      src/component.rs
  4. 129 113
      src/layout.rs
  5. 7 0
      src/layout/cache.rs
  6. 9 5
      src/layout/calc.rs
  7. 109 0
      src/layout/dump.rs
  8. 24 2
      src/lib.rs
  9. 2 7
      src/render.rs
  10. 4 3
      src/ui.rs
  11. 1 1
      src/widget.rs
  12. 2 2
      src/widget/button.rs
  13. 1 1
      src/widget/frame.rs
  14. 1 2
      src/widget/group.rs
  15. 2 2
      src/widget/label.rs
  16. 1 4
      src/widget/spacer.rs
  17. 1 4
      src/widget/text_edit.rs
  18. 1 1
      src/window.rs

+ 14 - 0
README.md

@@ -0,0 +1,14 @@
+patina is a lightweight component-based GUI library for the Rust programming language. 
+
+Inspired by libraries such as [relm](https://lib.rs/crates/relm) and
+[druid](https://lib.rs/crates/druid), patina aims to provide a more usable and
+friendly experience for building simple-to-moderately-complex UIs.
+
+### Rendering backends
+
+Currently the only supported rendering backend is
+[`kahlo`](https://lib.rs/crates/kahlo), a lightweight SIMD-accelerated software
+rendering library. Other backends, such as rendering via Vulkan or OpenGL,
+are planned, as is integration to coexist nicely within a rendering context.
+
+

+ 0 - 2
examples/text_edits.rs

@@ -83,5 +83,3 @@ impl WindowComponent for TextEditWindow {
 fn main() {
     patina::ui::make_opinionated_ui(TextEditWindow::new).run();
 }
-
-fn main() {}

+ 0 - 10
src/component.rs

@@ -1,10 +0,0 @@
-pub trait Component {
-    type ParentMsg;
-    type Msg;
-
-    fn process(&mut self, msg: Self::Msg) -> Vec<Self::ParentMsg>;
-
-    fn process_all(&mut self, msgs: impl Iterator<Item = Self::Msg>) -> Vec<Self::ParentMsg> {
-        msgs.flat_map(|msg| self.process(msg)).collect()
-    }
-}

+ 129 - 113
src/layout.rs

@@ -1,10 +1,102 @@
-//! Layout calculations for the widget tree.
+//! Layout calculations for the widget tree. All layout calculations are performed on a tree of
+//! [`LayoutNode`]s, which are stored in various widget structs.
 //!
 //! Layout calculations are done in two passes:
 //! - **Arrangement**: computes minimum sizes, net size policies, and records parent/child relationships between
-//! nodes.
+//! nodes for optimization purposes.
 //! - **Layout**: given minimum sizes and net size policies from the arrangement step, computes
-//! exact pixel values for positions and sizes.
+//! exact pixel values for positions and sizes by distributing the available space appropriately.
+//!
+//! Collectively, layout nodes form a tree structure with _externally_-stored edges. This is a
+//! slightly odd data structure, so it deserves some elaboration. In order to play nicely with
+//! Rust's borrow checker without using interior mutability (which comes with a large set of
+//! gotchas and a performance hit), when using internally-stored edges, layout nodes must be
+//! traversed from the root node to acquire a mutable reference to an interior node.
+//!
+//! To make this less awkward, the tree structure of the layout nodes -- and indeed the ownership
+//! of nodes -- is not stored directly in layout nodes themselves, but instead in a separate widget
+//! tree structure. At first this seems an unnecessary complication and major source of
+//! awkwardness, but it allows for a few very useful tricks:
+//! - Mutable references can be created for any number of nodes,
+//! - The entire layout tree need not be traversed to find a specific child,
+//! - There is no 1:1 requirement between widgets and layout nodes, so layout nodes can be used for
+//! internal alignment,
+//! - Layout is significantly easier to debug as it is by design entirely agnostic to details of
+//! the widgets involved,
+//! - Rather than numerical indices or similar awkward indexing schemes, nodes are simply stored as
+//! members of a struct and benefit from all the usual borrow checker behaviour.
+//!
+//! The tree structure is created by implementations of the [`LayoutTreeNode`] trait. Consider the
+//! following tree:
+//! ```text
+//!     root --+-- child1
+//!            |
+//!            \-- child2 --+-- child2a
+//!                         |
+//!                         \-- child2b
+//! ```
+//!
+//! The following is one simplified way the structure can be imposed externally:
+//! ```
+//! # use patina::layout::*;
+//! trait Widget {
+//!     fn lnode(&self) -> LayoutNodeAccess<'_>;
+//! }
+//! struct RootWidget {
+//!     node: LayoutNode,
+//!     label: LabelWidget,
+//!     select: ChoiceWidget,
+//! }
+//! impl LayoutTreeNode<()> for RootWidget {
+//!     fn current_node(&self) -> &LayoutNode { &self.node }
+//!     fn child_count(&self) -> usize { 2 }
+//!     fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+//!         match ndx {
+//!             0 => Some(self.label.lnode()),  // root -> child1 edge
+//!             1 => Some(self.select.lnode()), // root -> child2 edge
+//!             _ => None
+//!         }
+//!     }
+//! }
+//! impl Widget for RootWidget {
+//!     fn lnode(&self) -> LayoutNodeAccess<'_> {
+//!         LayoutNodeAccess::new::<()>(self)
+//!     }
+//! }
+//! struct LabelWidget {
+//!     // see documentation on LeafLayoutNode for more details
+//!     // this is basically a shortcut to avoid reimplementing LayoutTreeNode
+//!     // to store a node with no children
+//!     node: LeafLayoutNode
+//! }
+//! impl Widget for LabelWidget {
+//!     fn lnode(&self) -> LayoutNodeAccess<'_> {
+//!         self.node.access()
+//!     }
+//! }
+//! struct ChoiceWidget {
+//!     node: LayoutNode,
+//!     choice1: LabelWidget,
+//!     choice2: LabelWidget,
+//! }
+//! impl LayoutTreeNode<()> for ChoiceWidget {
+//!     // elided for brevity
+//! #   fn current_node(&self) -> &LayoutNode { &self.node }
+//! #   fn child_count(&self) -> usize { 2 }
+//! #   fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+//! #       match ndx {
+//! #           0 => Some(self.choice1.lnode()),
+//! #           1 => Some(self.choice2.lnode()),
+//! #           _ => None
+//! #       }
+//! #   }
+//! }
+//! impl Widget for ChoiceWidget {
+//!     fn lnode(&self) -> LayoutNodeAccess<'_> {
+//!         LayoutNodeAccess::new::<()>(self)
+//!     }
+//! }
+//! ```
 
 use std::{ops::Deref, rc::Rc};
 
@@ -14,12 +106,18 @@ use kahlo::math::{PixelBox, PixelSideOffsets};
 mod arr;
 mod cache;
 mod calc;
+mod dump;
 
 pub use cache::{LayoutCache, LayoutNodeID};
 pub use calc::recalculate;
 
+pub use dump::*;
+
+#[doc(hidden)]
 pub struct CellMarker;
+/// Type representing a single cell's coordinates in a table.
 pub type TableCell = euclid::Point2D<usize, CellMarker>;
+/// Type representing the total size of a table.
 pub type TableSize = euclid::Size2D<usize, CellMarker>;
 
 /// Sizing policy for a layout dimension. Defaults to no minimum or desired size and a low-weighted
@@ -102,7 +200,9 @@ impl SizePolicy {
     }
 }
 
+#[doc(hidden)]
 pub struct SizePolicyTag;
+/// A 2D size policy type using [`euclid`].
 pub type SizePolicy2D = euclid::Size2D<SizePolicy, SizePolicyTag>;
 
 impl std::ops::Add<Self> for SizePolicy {
@@ -130,6 +230,7 @@ impl Default for HorizontalAlignment {
     }
 }
 
+/// How to vertically align a smaller node inside a larger node.
 #[derive(Clone, Copy, PartialEq, Debug)]
 pub enum VerticalAlignment {
     Top,
@@ -155,6 +256,7 @@ pub enum NodeBehaviour {
     Anchored,
 }
 
+/// Represents a layout style for a layout node to arrange its children with.
 #[derive(Clone)]
 pub enum ChildArrangement {
     Custom(std::rc::Rc<dyn arr::ArrangementCalculator>),
@@ -189,6 +291,10 @@ impl Deref for ChildArrangement {
     }
 }
 
+/// Type representing a single node in the layout tree.
+///
+/// Notably, this struct is unaware of any edges it may participate in; see the module
+/// documentation for more information.
 pub struct LayoutNode {
     /// Human-readable label for making layout tree dumps easier to read.
     label: Option<String>,
@@ -210,7 +316,7 @@ pub struct LayoutNode {
     valign: VerticalAlignment,
     /// User-exposed margins, or spacing between the parent and the render area of this node.
     margin: PixelSideOffsets,
-    /// Table row/column assignment for this node insite its parent.
+    /// Table row/column assignment for this node inside its parent.
     table_cell: Option<TableCell>,
 }
 
@@ -402,114 +508,6 @@ impl Drop for LayoutNode {
     }
 }
 
-fn dump_node_tree_helper(lna: LayoutNodeAccess, indent: usize, out: &mut String) {
-    let ind = "    ".repeat(indent);
-    out.push_str(ind.as_str());
-    out.push_str("Node (");
-    out.push_str(match lna.label() {
-        Some(v) => v,
-        None => "<anon>",
-    });
-    out.push_str(
-        format!(
-            ") [wpol: {}/{}/{} hpol: {}/{}/{} behaviour: {:?} upd: {:?} rend: {:?}]\n",
-            lna.width_policy.minimum,
-            lna.width_policy.desired,
-            lna.width_policy.slack_weight,
-            lna.height_policy.minimum,
-            lna.height_policy.desired,
-            lna.height_policy.slack_weight,
-            lna.behaviour,
-            lna.with_state(|v| v.needs_update),
-            lna.with_state(|v| v.needs_render),
-        )
-        .as_str(),
-    );
-    out.push_str(ind.as_str());
-    out.push_str("        ");
-    out.push_str(format!("net policy:  {:?}\n", lna.with_state(|v| v.net_policy)).as_str());
-    out.push_str(ind.as_str());
-    out.push_str("        ");
-    out.push_str(format!("render area: {:?}\n", lna.render_area()).as_str());
-
-    for child in lna.child_iter() {
-        dump_node_tree_helper(child, indent + 1, out);
-    }
-}
-
-pub fn dump_node_tree(lna: LayoutNodeAccess, out: &mut String) {
-    dump_node_tree_helper(lna, 0, out);
-}
-
-pub fn dump_tree_json<W: ?Sized + std::io::Write>(
-    lna: LayoutNodeAccess,
-    out: &mut std::io::BufWriter<W>,
-) -> std::io::Result<()> {
-    use std::io::Write;
-
-    write!(out, "{{")?;
-    write!(out, "\"label\": \"{}\" ", lna.label().unwrap_or("null"))?;
-    write!(
-        out,
-        ",\"id\": {} ",
-        <LayoutNodeID as Into<usize>>::into(lna.id())
-    )?;
-    write!(out, ",\"behaviour\": \"{:?}\"", lna.behaviour())?;
-    write!(out, ",\"child_arrangement\": \"{:?}\"", lna.arrangement())?;
-    write!(
-        out,
-        ",\"width_policy\": [{},{},{}]",
-        lna.width_policy().minimum,
-        lna.width_policy().desired,
-        lna.width_policy().slack_weight
-    )?;
-    write!(
-        out,
-        ",\"height_policy\": [{},{},{}]",
-        lna.height_policy().minimum,
-        lna.height_policy().desired,
-        lna.height_policy().slack_weight
-    )?;
-    write!(out, ",\"halign\": \"{:?}\"", lna.halign())?;
-    write!(out, ",\"valign\": \"{:?}\"", lna.valign())?;
-    write!(
-        out,
-        ",\"margin\": {{\"top\": {}, \"bottom\": {}, \"left\": {}, \"right\": {}}}",
-        lna.margin().top,
-        lna.margin().bottom,
-        lna.margin().left,
-        lna.margin().right,
-    )?;
-    match lna.table_cell() {
-        None => write!(out, ",\"table_cell\": null")?,
-        Some(c) => write!(out, ",\"table_cell\": [{},{}]", c.x, c.y)?,
-    }
-    match lna.render_area() {
-        None => write!(out, ",\"render_area\": null")?,
-        Some(a) => write!(
-            out,
-            ",\"render_area\": [[{},{}],[{},{}]]",
-            a.min.x, a.min.y, a.max.x, a.max.y
-        )?,
-    }
-
-    write!(out, ",\"children\": [")?;
-
-    let mut needs_sep = false;
-    for child in lna.child_iter() {
-        if needs_sep {
-            out.write(",".as_bytes())?;
-        } else {
-            needs_sep = true;
-        }
-        dump_tree_json(child, out)?;
-    }
-
-    write!(out, r#"]}}"#)?;
-
-    Ok(())
-}
-
 /// Iterator implementation to iterate across children of a LayoutNodeContainer
 #[derive(Clone)]
 struct LayoutChildIter<'l> {
@@ -537,6 +535,7 @@ impl<'l> Iterator for LayoutChildIter<'l> {
     }
 }
 
+/// Accessor trait for a struct that stores edge information for the layout tree.
 pub trait LayoutTreeNode<Tag: 'static> {
     fn current_node(&self) -> &LayoutNode;
     fn child_count(&self) -> usize;
@@ -559,6 +558,10 @@ fn child_dispatch<'l, Tag: 'static>(dynptr: DynPtr, ndx: usize) -> Option<Layout
     unsafe { std::mem::transmute::<_, &'l dyn LayoutTreeNode<Tag>>(dynptr).child(ndx) }
 }
 
+/// Edge-aware accessor for a [`LayoutNode`].
+///
+/// Use this struct if you wish to accept arbitrary [`LayoutNode`]s and require information about
+/// the layout tree as well.
 #[derive(Clone, Copy)]
 pub struct LayoutNodeAccess<'l> {
     current_node_func: fn(DynPtr) -> &'l LayoutNode,
@@ -609,7 +612,14 @@ impl<'l> Deref for LayoutNodeAccess<'l> {
     }
 }
 
-/// Helper struct to store a leaf LayoutNode and automatically provide a LayoutTreeNode impl
+/// Helper struct to store a leaf LayoutNode and automatically provide a [`LayoutTreeNode`]
+/// implementation.
+///
+/// This helps avoid repetitive reimplementations of [`LayoutTreeNode`] when a container stores a
+/// node with no children.
+///
+/// If you're reading this documentation, you probably want the [`LeafLayoutNode::access`]
+/// function, which gives a [`LayoutNodeAccess`].
 pub struct LeafLayoutNode(LayoutNode);
 
 impl LeafLayoutNode {
@@ -652,3 +662,9 @@ impl LayoutTreeNode<()> for LeafLayoutNode {
         0
     }
 }
+
+/*macro_rules! layout_tree {
+    ($ctype:ident, $($tag:ident => $nname:ident | [$($ctag:ident),*]),*) => {
+
+    };
+}*/

+ 7 - 0
src/layout/cache.rs

@@ -2,6 +2,10 @@ use std::{cell::RefCell, collections::HashMap, rc::Rc};
 
 use super::SizePolicy2D;
 
+/// Unique per-node ID value.
+///
+/// This struct is a thin wrapper around a `usize`. Generally, you should never have to interact
+/// with this type unless you are working on patina itself.
 #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
 pub struct LayoutNodeID(usize);
 static NEXT_NODE_ID: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(1);
@@ -27,6 +31,9 @@ pub struct NodeState {
     pub(super) children: Vec<LayoutNodeID>,
 }
 
+/// This struct stores meta-information about each node, such as its calculated render area.
+///
+/// All nodes have a refcounted reference to an instance of this struct.
 #[derive(Debug, Default)]
 pub struct LayoutCache {
     pub(super) update_queue: RefCell<Vec<LayoutNodeID>>,

+ 9 - 5
src/layout/calc.rs

@@ -1,5 +1,9 @@
 use super::{cache::LayoutNodeID, Layer, LayoutNodeAccess, NodeBehaviour, NodeState};
 
+/// Main entry point into layout calculations.
+///
+/// Given a node (which must be [`NodeBehaviour::Fixed`], this function correctly performs the
+/// arrangement and layout passes, reusing cached values whenever possible.
 pub fn recalculate(node: LayoutNodeAccess) {
     if let NodeBehaviour::Fixed { area } = node.behaviour() {
         let layer = Layer::default();
@@ -58,7 +62,7 @@ mod test {
 
     use crate::layout::{
         cache::LayoutCache, dump_node_tree, ChildArrangement, LayoutNode, LayoutNodeAccess,
-        LayoutNodeContainer, NodeBehaviour, SizePolicy, TableCell,
+        LayoutTreeNode, NodeBehaviour, SizePolicy, TableCell,
     };
 
     use super::recalculate;
@@ -68,14 +72,14 @@ mod test {
         node: LayoutNode,
     }
 
-    impl LayoutNodeContainer for LayoutTree {
-        fn layout_node(&self) -> &LayoutNode {
+    impl LayoutTreeNode<()> for LayoutTree {
+        fn current_node(&self) -> &LayoutNode {
             &self.node
         }
-        fn layout_child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
+        fn child(&self, ndx: usize) -> Option<LayoutNodeAccess<'_>> {
             self.children.get(ndx).map(|t| LayoutNodeAccess::new(t))
         }
-        fn layout_child_count(&self) -> usize {
+        fn child_count(&self) -> usize {
             self.children.len()
         }
     }

+ 109 - 0
src/layout/dump.rs

@@ -0,0 +1,109 @@
+use super::*;
+
+fn dump_node_tree_helper(lna: LayoutNodeAccess, indent: usize, out: &mut String) {
+    let ind = "    ".repeat(indent);
+    out.push_str(ind.as_str());
+    out.push_str("Node (");
+    out.push_str(match lna.label() {
+        Some(v) => v,
+        None => "<anon>",
+    });
+    out.push_str(
+        format!(
+            ") [wpol: {}/{}/{} hpol: {}/{}/{} behaviour: {:?} upd: {:?} rend: {:?}]\n",
+            lna.width_policy.minimum,
+            lna.width_policy.desired,
+            lna.width_policy.slack_weight,
+            lna.height_policy.minimum,
+            lna.height_policy.desired,
+            lna.height_policy.slack_weight,
+            lna.behaviour,
+            lna.with_state(|v| v.needs_update),
+            lna.with_state(|v| v.needs_render),
+        )
+        .as_str(),
+    );
+    out.push_str(ind.as_str());
+    out.push_str("        ");
+    out.push_str(format!("net policy:  {:?}\n", lna.with_state(|v| v.net_policy)).as_str());
+    out.push_str(ind.as_str());
+    out.push_str("        ");
+    out.push_str(format!("render area: {:?}\n", lna.render_area()).as_str());
+
+    for child in lna.child_iter() {
+        dump_node_tree_helper(child, indent + 1, out);
+    }
+}
+
+pub fn dump_node_tree(lna: LayoutNodeAccess, out: &mut String) {
+    dump_node_tree_helper(lna, 0, out);
+}
+
+pub fn dump_tree_json<W: ?Sized + std::io::Write>(
+    lna: LayoutNodeAccess,
+    out: &mut std::io::BufWriter<W>,
+) -> std::io::Result<()> {
+    use std::io::Write;
+
+    write!(out, "{{")?;
+    write!(out, "\"label\": \"{}\" ", lna.label().unwrap_or("null"))?;
+    write!(
+        out,
+        ",\"id\": {} ",
+        <LayoutNodeID as Into<usize>>::into(lna.id())
+    )?;
+    write!(out, ",\"behaviour\": \"{:?}\"", lna.behaviour())?;
+    write!(out, ",\"child_arrangement\": \"{:?}\"", lna.arrangement())?;
+    write!(
+        out,
+        ",\"width_policy\": [{},{},{}]",
+        lna.width_policy().minimum,
+        lna.width_policy().desired,
+        lna.width_policy().slack_weight
+    )?;
+    write!(
+        out,
+        ",\"height_policy\": [{},{},{}]",
+        lna.height_policy().minimum,
+        lna.height_policy().desired,
+        lna.height_policy().slack_weight
+    )?;
+    write!(out, ",\"halign\": \"{:?}\"", lna.halign())?;
+    write!(out, ",\"valign\": \"{:?}\"", lna.valign())?;
+    write!(
+        out,
+        ",\"margin\": {{\"top\": {}, \"bottom\": {}, \"left\": {}, \"right\": {}}}",
+        lna.margin().top,
+        lna.margin().bottom,
+        lna.margin().left,
+        lna.margin().right,
+    )?;
+    match lna.table_cell() {
+        None => write!(out, ",\"table_cell\": null")?,
+        Some(c) => write!(out, ",\"table_cell\": [{},{}]", c.x, c.y)?,
+    }
+    match lna.render_area() {
+        None => write!(out, ",\"render_area\": null")?,
+        Some(a) => write!(
+            out,
+            ",\"render_area\": [[{},{}],[{},{}]]",
+            a.min.x, a.min.y, a.max.x, a.max.y
+        )?,
+    }
+
+    write!(out, ",\"children\": [")?;
+
+    let mut needs_sep = false;
+    for child in lna.child_iter() {
+        if needs_sep {
+            out.write(",".as_bytes())?;
+        } else {
+            needs_sep = true;
+        }
+        dump_tree_json(child, out)?;
+    }
+
+    write!(out, r#"]}}"#)?;
+
+    Ok(())
+}

+ 24 - 2
src/lib.rs

@@ -1,6 +1,7 @@
+#![doc = include_str!("../README.md")]
+
 pub(crate) mod render;
 
-pub mod component;
 pub mod font;
 pub mod input;
 pub mod layout;
@@ -9,17 +10,38 @@ pub mod ui;
 pub mod widget;
 pub mod window;
 
+/// Commonly-used traits collected in one place for easier imports.
 pub mod prelude {
-    pub use crate::component::Component;
     pub use crate::widget::{Widget, WidgetExt};
     pub use crate::window::WindowComponent;
+    pub use crate::Component;
 }
 
+/// Crates with types exposed in the patina API.
 pub mod re_exports {
     pub use fontdue;
     pub use kahlo;
 }
 
+/// Math types for representing points, sizes, and rectangular regions.
 pub mod math {
     pub use kahlo::math::*;
 }
+
+/// Trait for any object that participates in the event handling tree.
+pub trait Component {
+    /// What type of message to pass up the tree?
+    type ParentMsg;
+    /// What type of message do we know how to process?
+    type Msg;
+
+    /// Process a single incoming message and return the resulting messages for the parent
+    /// component.
+    fn process(&mut self, msg: Self::Msg) -> Vec<Self::ParentMsg>;
+
+    /// Process multiple incoming messages and return the resulting messages for the parent
+    /// component.
+    fn process_all(&mut self, msgs: impl Iterator<Item = Self::Msg>) -> Vec<Self::ParentMsg> {
+        msgs.flat_map(|msg| self.process(msg)).collect()
+    }
+}

+ 2 - 7
src/render.rs

@@ -1,15 +1,10 @@
-use std::{
-    cell::{Cell, RefCell},
-    rc::Rc,
-};
-
 use kahlo::{
     colour::Colour,
-    math::{PixelBox, PixelSize},
+    math::PixelBox,
     prelude::{BitmapAccess, KahloOps},
 };
 
-use crate::{layout::LayoutNode, theme::Theme, ui::UIHandle, window::RenderFormat};
+use crate::{layout::LayoutNode, theme::Theme, window::RenderFormat};
 
 mod text;
 pub use text::TextCache;

+ 4 - 3
src/ui.rs

@@ -3,12 +3,12 @@ use std::{collections::HashMap, rc::Rc};
 use kahlo::math::{PixelPoint, PixelSize};
 
 use crate::{
-    component::Component,
     font::FontStore,
     input::MouseButton,
     layout::{LayoutCache, LayoutNode},
     theme::Theme,
     window::{Window, WindowBuilder, WindowComponent, WindowEvent, WindowStateAccess},
+    Component,
 };
 
 #[derive(Debug)]
@@ -30,6 +30,7 @@ pub(crate) struct UIState {
     pub(crate) theme: Theme,
 }
 
+/// Reference to the global state of the currently-active UI.
 pub struct UIHandle<'l> {
     pub(crate) state: &'l mut UIState,
     pub(crate) eloop: &'l winit::event_loop::ActiveEventLoop,
@@ -195,9 +196,9 @@ impl<UIC: UIComponent> winit::application::ApplicationHandler<()> for UI<UIC> {
                 wsa.request_redraw();
             }
             winit::event::WindowEvent::KeyboardInput {
-                device_id,
+                device_id: _,
                 event,
-                is_synthetic,
+                is_synthetic: _,
             } => {
                 if event.state == winit::event::ElementState::Pressed {
                     if let Some(text) = event.text {

+ 1 - 1
src/widget.rs

@@ -1,9 +1,9 @@
 use crate::{
-    component::Component,
     input::InputState,
     layout::{HorizontalAlignment, LayoutNode, LayoutNodeAccess, TableCell, VerticalAlignment},
     render::RenderTarget,
     ui::UIHandle,
+    Component,
 };
 use kahlo::math::PixelSideOffsets;
 

+ 2 - 2
src/widget/button.rs

@@ -1,7 +1,6 @@
 use kahlo::math::PixelSideOffsets;
 
 use crate::{
-    component::Component,
     input::{MouseButton, MouseCheckStatus},
     layout::{
         HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutTreeNode, LeafLayoutNode,
@@ -9,6 +8,7 @@ use crate::{
     },
     render::{ColourChoice, RenderTarget, TextCache},
     ui::UIHandle,
+    Component,
 };
 
 use super::Widget;
@@ -108,7 +108,7 @@ impl<C: Component> LayoutNodeContainer for Button<C> {
 impl<C: Component> Widget<C> for Button<C> {
     fn poll(
         &mut self,
-        uih: &mut UIHandle,
+        _uih: &mut UIHandle,
         input_state: Option<&crate::input::InputState>,
     ) -> Vec<<C as Component>::Msg> {
         let mut result: Vec<<C as Component>::Msg> = vec![];

+ 1 - 1
src/widget/frame.rs

@@ -1,10 +1,10 @@
 use kahlo::math::PixelSideOffsets;
 
 use crate::{
-    component::Component,
     layout::{ChildArrangement, LayoutNode, LayoutNodeAccess, LayoutTreeNode, SizePolicy},
     render::{ColourChoice, RenderTarget},
     ui::UIHandle,
+    Component,
 };
 
 use super::Widget;

+ 1 - 2
src/widget/group.rs

@@ -3,16 +3,15 @@ use std::rc::Rc;
 use kahlo::math::PixelSideOffsets;
 
 use crate::{
-    component::Component,
     input::InputState,
     layout::{
         ChildArrangement, HorizontalAlignment, LayoutNode, LayoutNodeAccess, LayoutTreeNode,
         SizePolicy, TableCell,
     },
     render::RenderTarget,
-    theme::Theme,
     ui::UIHandle,
     widget::Widget,
+    Component,
 };
 
 pub struct PlainGroup<C: Component> {

+ 2 - 2
src/widget/label.rs

@@ -1,10 +1,10 @@
 use std::rc::Rc;
 
 use crate::{
-    component::Component,
     layout::{LayoutNode, LayoutNodeAccess, LeafLayoutNode, SizePolicy},
     render::{RenderTarget, TextCache},
     ui::UIHandle,
+    Component,
 };
 
 use super::Widget;
@@ -68,7 +68,7 @@ impl<C: Component> Label<C> {
 impl<C: Component> Widget<C> for Label<C> {
     fn poll(
         &mut self,
-        uih: &mut UIHandle,
+        _uih: &mut UIHandle,
         _input_state: Option<&crate::input::InputState>,
     ) -> Vec<<C as Component>::Msg> {
         self.cache.update_node_size_hint(&mut *self.lnode);

+ 1 - 4
src/widget/spacer.rs

@@ -1,15 +1,12 @@
 use std::ops::DerefMut;
 
-use kahlo::prelude::*;
-
 use crate::{
-    component::Component,
     input::InputState,
     layout::{LayoutNode, LayoutNodeAccess, LeafLayoutNode, SizePolicy},
     render::RenderTarget,
-    theme::Theme,
     ui::UIHandle,
     widget::Widget,
+    Component,
 };
 
 pub struct Spacer<C: Component> {

+ 1 - 4
src/widget/text_edit.rs

@@ -1,5 +1,3 @@
-use std::cell::RefCell;
-
 use crate::{
     input::{MouseButton, MouseCheckStatus},
     layout::{LayoutNode, LayoutNodeAccess, LeafLayoutNode, SizePolicy},
@@ -7,7 +5,6 @@ use crate::{
     render::{ColourChoice, RenderTarget, TextCache},
     ui::UIHandle,
 };
-use kahlo::{math::PixelBox, prelude::*};
 use winit::keyboard::{Key, NamedKey};
 
 pub struct TextEdit<C: Component> {
@@ -57,7 +54,7 @@ impl<C: Component> TextEdit<C> {
 impl<C: Component> Widget<C> for TextEdit<C> {
     fn poll(
         &mut self,
-        uih: &mut UIHandle,
+        _uih: &mut UIHandle,
         input_state: Option<&crate::input::InputState>,
     ) -> Vec<<C as Component>::Msg> {
         let Some(istate) = input_state else {

+ 1 - 1
src/window.rs

@@ -8,11 +8,11 @@ use kahlo::{
 
 use crate::layout::{self, LayoutNode, LayoutNodeAccess, LayoutTreeNode};
 use crate::widget::Widget;
-use crate::{component::Component, ui::UIHandle};
 use crate::{
     input::{InputState, MouseButton},
     render::RenderTarget,
 };
+use crate::{ui::UIHandle, Component};
 
 pub type RenderFormat = kahlo::formats::Bgr32;