diff --git a/Cargo.toml b/Cargo.toml index 7f6b67c1..61995a58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,3 +15,6 @@ categories = ["gui"] stretch = "0.2" nalgebra = "0.18" twox-hash = "1.3" + +[dev-dependencies] +ggez = { version = "0.5", git = "https://github.com/hecrj/ggez" } diff --git a/examples/ggez/main.rs b/examples/ggez/main.rs new file mode 100644 index 00000000..87949dee --- /dev/null +++ b/examples/ggez/main.rs @@ -0,0 +1,49 @@ +mod renderer; +mod widget; + +use renderer::Renderer; +use widget::Text; + +use ggez; +use ggez::event; +use ggez::graphics; + +use iced::Interface; + +pub fn main() -> ggez::GameResult { + let cb = ggez::ContextBuilder::new("iced", "ggez"); + let (ctx, event_loop) = &mut cb.build()?; + let state = &mut Game::new()?; + event::run(ctx, event_loop, state) +} + +struct Game {} + +impl Game { + fn new() -> ggez::GameResult { + Ok(Game {}) + } +} + +impl event::EventHandler for Game { + fn update(&mut self, _ctx: &mut ggez::Context) -> ggez::GameResult { + Ok(()) + } + + fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult { + graphics::clear(context, [0.1, 0.2, 0.3, 1.0].into()); + + { + let renderer = &mut Renderer { context }; + let ui: Interface<(), Renderer> = + Interface::compute(Text::new("Hello, iced!").into(), renderer); + + let mouse_cursor = ui.draw(renderer, iced::Point::new(0.0, 0.0)); + + renderer.flush(); + } + + graphics::present(context)?; + Ok(()) + } +} diff --git a/examples/ggez/renderer.rs b/examples/ggez/renderer.rs new file mode 100644 index 00000000..ae6e3250 --- /dev/null +++ b/examples/ggez/renderer.rs @@ -0,0 +1,26 @@ +mod text; + +use ggez::graphics::{self, Color}; +use ggez::Context; + +pub struct Renderer<'a> { + pub context: &'a mut Context, +} + +impl Renderer<'_> { + pub fn flush(&mut self) { + graphics::draw_queued_text( + self.context, + graphics::DrawParam::default(), + Default::default(), + graphics::FilterMode::Linear, + ) + .expect("Draw text"); + } +} + +impl iced::Renderer for Renderer<'_> { + type Color = Color; + + fn explain(&mut self, layout: &iced::Layout<'_>, color: Color) {} +} diff --git a/examples/ggez/renderer/text.rs b/examples/ggez/renderer/text.rs new file mode 100644 index 00000000..bfdedf74 --- /dev/null +++ b/examples/ggez/renderer/text.rs @@ -0,0 +1,107 @@ +use super::Renderer; +use ggez::graphics::{self, mint, Align, Color, Scale, Text, TextFragment}; + +use iced::text; +use std::cell::RefCell; +use std::f32; + +impl text::Renderer for Renderer<'_> { + fn node(&self, style: iced::Style, content: &str, size: f32) -> iced::Node { + let font_cache = graphics::font_cache(self.context); + let content = String::from(content); + let measure = RefCell::new(None); + + iced::Node::with_measure(style, move |bounds| { + // TODO: Investigate why stretch tries to measure this MANY times + // with every ancestor's bounds. + // Bug? Using the library wrong? I should probably open an issue on + // the stretch repository. + // I noticed that the first measure is the one that matters in + // practice. Here, we use a RefCell to store the cached + // measurement. + let mut measure = measure.borrow_mut(); + + if measure.is_none() { + let bounds = ( + match bounds.width { + iced::Number::Undefined => f32::INFINITY, + iced::Number::Defined(w) => w, + }, + match bounds.height { + iced::Number::Undefined => f32::INFINITY, + iced::Number::Defined(h) => h, + }, + ); + + let mut text = Text::new(TextFragment { + text: content.clone(), + scale: Some(Scale { x: size, y: size }), + ..Default::default() + }); + + text.set_bounds( + mint::Point2 { + x: bounds.0, + y: bounds.1, + }, + Align::Left, + ); + + let (width, height) = text.dimensions(&font_cache); + + let size = iced::Size { + width: width as f32, + height: height as f32, + }; + + // If the text has no width boundary we avoid caching as the + // layout engine may just be measuring text in a row. + if bounds.0 == f32::INFINITY { + return size; + } else { + *measure = Some(size); + } + } + + measure.unwrap() + }) + } + + fn draw( + &mut self, + bounds: iced::Rectangle, + content: &str, + size: f32, + color: Color, + horizontal_alignment: text::HorizontalAlignment, + _vertical_alignment: text::VerticalAlignment, + ) { + let mut text = Text::new(TextFragment { + text: String::from(content), + scale: Some(Scale { x: size, y: size }), + ..Default::default() + }); + + text.set_bounds( + mint::Point2 { + x: bounds.width, + y: bounds.height, + }, + match horizontal_alignment { + text::HorizontalAlignment::Left => graphics::Align::Left, + text::HorizontalAlignment::Center => graphics::Align::Center, + text::HorizontalAlignment::Right => graphics::Align::Right, + }, + ); + + graphics::queue_text( + self.context, + &text, + mint::Point2 { + x: bounds.x, + y: bounds.y, + }, + Some(color), + ); + } +} diff --git a/examples/ggez/widget.rs b/examples/ggez/widget.rs new file mode 100644 index 00000000..a9e0a7ba --- /dev/null +++ b/examples/ggez/widget.rs @@ -0,0 +1,3 @@ +use ggez::graphics::Color; + +pub type Text = iced::Text;