From e55ac637a9c4422b2fccfcc56c25449160e56e02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 23 Jul 2019 10:49:24 +0200 Subject: [PATCH] Add `Runtime` concept to abstract caching --- examples/ggez/main.rs | 9 ++-- src/interface.rs | 77 ------------------------------- src/layout.rs | 19 +++++--- src/lib.rs | 4 +- src/runtime.rs | 103 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+), 89 deletions(-) delete mode 100644 src/interface.rs create mode 100644 src/runtime.rs diff --git a/examples/ggez/main.rs b/examples/ggez/main.rs index 770f79b1..f6d82ad1 100644 --- a/examples/ggez/main.rs +++ b/examples/ggez/main.rs @@ -9,8 +9,6 @@ use ggez::event; use ggez::graphics; use ggez::input::mouse; -use iced::Interface; - pub fn main() -> ggez::GameResult { let cb = ggez::ContextBuilder::new("iced", "ggez"); let (ctx, event_loop) = &mut cb.build()?; @@ -20,6 +18,8 @@ pub fn main() -> ggez::GameResult { struct Game { spritesheet: graphics::Image, + + runtime: iced::Runtime, button: button::State, } @@ -27,6 +27,8 @@ impl Game { fn new(context: &mut ggez::Context) -> ggez::GameResult { Ok(Game { spritesheet: graphics::Image::new(context, "/ui.png").unwrap(), + + runtime: iced::Runtime::new(), button: button::State::new(), }) } @@ -69,8 +71,7 @@ impl event::EventHandler for Game { let renderer = &mut Renderer::new(context, self.spritesheet.clone()); - let ui: Interface = - Interface::compute(content.into(), renderer); + let ui = self.runtime.compute(content.into(), renderer); let cursor = ui.draw(renderer, iced::Point::new(0.0, 0.0)); diff --git a/src/interface.rs b/src/interface.rs deleted file mode 100644 index 6c4982e5..00000000 --- a/src/interface.rs +++ /dev/null @@ -1,77 +0,0 @@ -use std::hash::Hasher; -use stretch::result; - -use crate::{Element, Event, Layout, MouseCursor, Point}; - -pub struct Interface<'a, Message, Renderer> { - hash: u64, - root: Element<'a, Message, Renderer>, - layout: result::Layout, -} - -pub struct Cache { - hash: u64, - layout: result::Layout, -} - -impl<'a, Message, Renderer> Interface<'a, Message, Renderer> { - pub fn compute( - root: Element<'a, Message, Renderer>, - renderer: &Renderer, - ) -> Interface<'a, Message, Renderer> { - let hasher = &mut crate::Hasher::default(); - root.hash(hasher); - - let hash = hasher.finish(); - let layout = root.compute_layout(renderer); - - Interface { hash, root, layout } - } - - pub fn compute_with_cache( - root: Element<'a, Message, Renderer>, - renderer: &Renderer, - cache: Cache, - ) -> Interface<'a, Message, Renderer> { - let hasher = &mut crate::Hasher::default(); - root.hash(hasher); - - let hash = hasher.finish(); - - let layout = if hash == cache.hash { - cache.layout - } else { - root.compute_layout(renderer) - }; - - Interface { hash, root, layout } - } - - pub fn on_event(&mut self, event: Event, cursor_position: Point, messages: &mut Vec) { - let Interface { root, layout, .. } = self; - - root.widget - .on_event(event, Self::layout(layout), cursor_position, messages); - } - - pub fn draw(&self, renderer: &mut Renderer, cursor_position: Point) -> MouseCursor { - let Interface { root, layout, .. } = self; - - let cursor = root - .widget - .draw(renderer, Self::layout(layout), cursor_position); - - cursor - } - - pub fn cache(self) -> Cache { - Cache { - hash: self.hash, - layout: self.layout, - } - } - - fn layout(layout: &result::Layout) -> Layout<'_> { - Layout::new(layout, Point::new(0.0, 0.0)) - } -} diff --git a/src/layout.rs b/src/layout.rs index 011f859a..481b4166 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -19,8 +19,16 @@ pub struct Layout<'a> { } impl<'a> Layout<'a> { - pub(crate) fn new(layout: &'a result::Layout, parent_position: Point) -> Self { - let position = parent_position + Vector::new(layout.location.x, layout.location.y); + pub(crate) fn new(layout: &'a result::Layout) -> Self { + Self::with_parent_position(layout, Point::new(0.0, 0.0)) + } + + fn with_parent_position( + layout: &'a result::Layout, + parent_position: Point, + ) -> Self { + let position = + parent_position + Vector::new(layout.location.x, layout.location.y); Layout { layout, position } } @@ -47,9 +55,8 @@ impl<'a> Layout<'a> { /// [`Layout`]: struct.Layout.html /// [`Node`]: struct.Node.html pub fn children(&'a self) -> impl Iterator> { - self.layout - .children - .iter() - .map(move |layout| Layout::new(layout, self.position)) + self.layout.children.iter().map(move |layout| { + Layout::with_parent_position(layout, self.position) + }) } } diff --git a/src/lib.rs b/src/lib.rs index 9c7eed8d..6ea3e60d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,13 +9,13 @@ pub mod widget; mod element; mod event; mod hasher; -mod interface; mod layout; mod mouse_cursor; mod node; mod point; mod rectangle; mod renderer; +mod runtime; mod style; mod vector; @@ -25,13 +25,13 @@ pub use stretch::{geometry::Size, number::Number}; pub use element::Element; pub use event::Event; pub use hasher::Hasher; -pub use interface::Interface; pub use layout::Layout; pub use mouse_cursor::MouseCursor; pub use node::Node; pub use point::Point; pub use rectangle::Rectangle; pub use renderer::Renderer; +pub use runtime::{Interface, Runtime}; pub use style::{Align, Justify, Style}; pub use vector::Vector; pub use widget::*; diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 00000000..f8a5aaa3 --- /dev/null +++ b/src/runtime.rs @@ -0,0 +1,103 @@ +use crate::{Column, Element, Event, Layout, MouseCursor, Point}; + +use std::hash::Hasher; +use stretch::result; + +pub struct Runtime { + cache: Cache, + events: Vec, + cursor_position: Point, +} + +impl Runtime { + pub fn new() -> Runtime { + // We use this as a placeholder to initialize the cache. + // This way, we can avoid the overhead of using an `Option` + // in `compute`. + let root: Element<'_, (), ()> = Column::new().into(); + + let hasher = &mut crate::Hasher::default(); + root.hash(hasher); + + Runtime { + cache: Cache { + hash: hasher.finish(), + layout: root.compute_layout(&()), + }, + events: Vec::new(), + cursor_position: Point::new(0.0, 0.0), + } + } + + pub fn on_event(&mut self, event: Event) { + self.events.push(event); + } + + pub fn compute<'a, Message, Renderer>( + &'a mut self, + root: Element<'a, Message, Renderer>, + renderer: &Renderer, + ) -> Interface<'a, Message, Renderer> { + let hasher = &mut crate::Hasher::default(); + root.hash(hasher); + + let hash = hasher.finish(); + + if hash != self.cache.hash { + self.cache = Cache { + hash, + layout: root.compute_layout(renderer), + }; + } + + Interface { + root, + layout: &self.cache.layout, + events: &mut self.events, + cursor_position: self.cursor_position, + } + } +} + +struct Cache { + hash: u64, + layout: result::Layout, +} + +pub struct Interface<'a, Message, Renderer> { + root: Element<'a, Message, Renderer>, + layout: &'a result::Layout, + events: &'a mut Vec, + cursor_position: Point, +} + +impl<'a, Message, Renderer> Interface<'a, Message, Renderer> { + pub fn update(&mut self) -> Vec { + let mut messages = Vec::new(); + + for event in self.events.drain(..) { + self.root.widget.on_event( + event, + Layout::new(&self.layout), + self.cursor_position, + &mut messages, + ); + } + + messages + } + + pub fn draw( + &self, + renderer: &mut Renderer, + cursor_position: Point, + ) -> MouseCursor { + let cursor = self.root.widget.draw( + renderer, + Layout::new(self.layout), + cursor_position, + ); + + cursor + } +}