From ccb87b12da9f4c8e65571d31da2952ff1b5b08d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 22 Jul 2019 20:14:23 +0200 Subject: [PATCH] Implement `button::Renderer` in `ggez` example --- examples/ggez/main.rs | 62 ++++++++++--- examples/ggez/renderer.rs | 19 +++- examples/ggez/renderer/button.rs | 144 +++++++++++++++++++++++++++++++ examples/ggez/widget.rs | 2 + resources/ui.png | Bin 0 -> 16691 bytes 5 files changed, 215 insertions(+), 12 deletions(-) create mode 100644 examples/ggez/renderer/button.rs create mode 100644 resources/ui.png diff --git a/examples/ggez/main.rs b/examples/ggez/main.rs index 87949dee..7deef072 100644 --- a/examples/ggez/main.rs +++ b/examples/ggez/main.rs @@ -2,26 +2,33 @@ mod renderer; mod widget; use renderer::Renderer; -use widget::Text; +use widget::{button, Button, Column, Text}; use ggez; 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()?; - let state = &mut Game::new()?; + let state = &mut Game::new(ctx)?; event::run(ctx, event_loop, state) } -struct Game {} +struct Game { + spritesheet: graphics::Image, + button: button::State, +} impl Game { - fn new() -> ggez::GameResult { - Ok(Game {}) + fn new(context: &mut ggez::Context) -> ggez::GameResult { + Ok(Game { + spritesheet: graphics::Image::new(context, "/ui.png").unwrap(), + button: button::State::new(), + }) } } @@ -33,17 +40,50 @@ impl event::EventHandler for Game { 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 screen = graphics::screen_coordinates(context); - let mouse_cursor = ui.draw(renderer, iced::Point::new(0.0, 0.0)); + let cursor = { + let hello = Text::new("Hello, iced!") + .horizontal_alignment(iced::text::HorizontalAlignment::Center); + + let button = Button::new(&mut self.button, "Press me!").width(200); + + let content = Column::new() + .width(screen.w as u32) + .height(screen.h as u32) + .align_items(iced::Align::Center) + .justify_content(iced::Justify::Center) + .spacing(20) + .push(hello) + .push(button); + + let renderer = + &mut Renderer::new(context, self.spritesheet.clone()); + + let ui: Interface<(), Renderer> = + Interface::compute(content.into(), renderer); + + let cursor = ui.draw(renderer, iced::Point::new(0.0, 0.0)); renderer.flush(); - } + + cursor + }; + + mouse::set_cursor_type(context, into_cursor_type(cursor)); graphics::present(context)?; Ok(()) } } + +fn into_cursor_type(cursor: iced::MouseCursor) -> mouse::MouseCursor { + match cursor { + iced::MouseCursor::OutOfBounds => mouse::MouseCursor::Default, + iced::MouseCursor::Idle => mouse::MouseCursor::Default, + iced::MouseCursor::Pointer => mouse::MouseCursor::Hand, + iced::MouseCursor::Working => mouse::MouseCursor::Progress, + iced::MouseCursor::Grab => mouse::MouseCursor::Grab, + iced::MouseCursor::Grabbing => mouse::MouseCursor::Grabbing, + } +} diff --git a/examples/ggez/renderer.rs b/examples/ggez/renderer.rs index ae6e3250..d21c5ec3 100644 --- a/examples/ggez/renderer.rs +++ b/examples/ggez/renderer.rs @@ -1,14 +1,31 @@ +mod button; mod text; -use ggez::graphics::{self, Color}; +use ggez::graphics::{self, spritebatch::SpriteBatch, Color, Image}; use ggez::Context; pub struct Renderer<'a> { pub context: &'a mut Context, + pub sprites: SpriteBatch, + pub spritesheet: Image, } impl Renderer<'_> { + pub fn new(context: &mut Context, spritesheet: Image) -> Renderer { + Renderer { + context, + sprites: SpriteBatch::new(spritesheet.clone()), + spritesheet, + } + } pub fn flush(&mut self) { + graphics::draw( + self.context, + &self.sprites, + graphics::DrawParam::default(), + ) + .expect("Draw sprites"); + graphics::draw_queued_text( self.context, graphics::DrawParam::default(), diff --git a/examples/ggez/renderer/button.rs b/examples/ggez/renderer/button.rs new file mode 100644 index 00000000..2423efe1 --- /dev/null +++ b/examples/ggez/renderer/button.rs @@ -0,0 +1,144 @@ +use super::Renderer; +use ggez::graphics::{ + self, Align, Color, DrawParam, Rect, Scale, Text, TextFragment, WHITE, +}; +use iced::{button, MouseCursor}; + +const LEFT: Rect = Rect { + x: 0.0, + y: 34.0, + w: 6.0, + h: 49.0, +}; + +const BACKGROUND: Rect = Rect { + x: LEFT.w, + y: LEFT.y, + w: 1.0, + h: LEFT.h, +}; + +const RIGHT: Rect = Rect { + x: LEFT.h - LEFT.w, + y: LEFT.y, + w: LEFT.w, + h: LEFT.h, +}; + +impl button::Renderer for Renderer<'_> { + fn draw( + &mut self, + cursor_position: iced::Point, + mut bounds: iced::Rectangle, + state: &button::State, + label: &str, + class: button::Class, + ) -> MouseCursor { + let mouse_over = bounds.contains(cursor_position); + + let mut state_offset = 0.0; + + if mouse_over { + if state.is_pressed() { + bounds.y += 4.0; + state_offset = RIGHT.x + RIGHT.w; + } else { + bounds.y -= 1.0; + } + } + + let class_index = match class { + button::Class::Primary => 0, + button::Class::Secondary => 1, + button::Class::Positive => 2, + }; + + let width = self.spritesheet.width() as f32; + let height = self.spritesheet.height() as f32; + + self.sprites.add(DrawParam { + src: Rect { + x: (LEFT.x + state_offset) / width, + y: (LEFT.y + class_index as f32 * LEFT.h) / height, + w: LEFT.w / width, + h: LEFT.h / height, + }, + dest: ggez::mint::Point2 { + x: bounds.x, + y: bounds.y, + }, + ..DrawParam::default() + }); + + self.sprites.add(DrawParam { + src: Rect { + x: (BACKGROUND.x + state_offset) / width, + y: (BACKGROUND.y + class_index as f32 * BACKGROUND.h) / height, + w: BACKGROUND.w / width, + h: BACKGROUND.h / height, + }, + dest: ggez::mint::Point2 { + x: bounds.x + LEFT.w, + y: bounds.y, + }, + scale: ggez::mint::Vector2 { + x: bounds.width - LEFT.w - RIGHT.w, + y: 1.0, + }, + ..DrawParam::default() + }); + + self.sprites.add(DrawParam { + src: Rect { + x: (RIGHT.x + state_offset) / width, + y: (RIGHT.y + class_index as f32 * RIGHT.h) / height, + w: RIGHT.w / width, + h: RIGHT.h / height, + }, + dest: ggez::mint::Point2 { + x: bounds.x + bounds.width - RIGHT.w, + y: bounds.y, + }, + ..DrawParam::default() + }); + + let mut text = Text::new(TextFragment { + text: String::from(label), + scale: Some(Scale { x: 20.0, y: 20.0 }), + ..Default::default() + }); + + text.set_bounds( + ggez::mint::Point2 { + x: bounds.width, + y: bounds.height, + }, + Align::Center, + ); + + graphics::queue_text( + self.context, + &text, + ggez::mint::Point2 { + x: bounds.x, + y: bounds.y + BACKGROUND.h / 4.0, + }, + Some(if mouse_over { + WHITE + } else { + Color { + r: 0.9, + g: 0.9, + b: 0.9, + a: 1.0, + } + }), + ); + + if mouse_over { + MouseCursor::Pointer + } else { + MouseCursor::OutOfBounds + } + } +} diff --git a/examples/ggez/widget.rs b/examples/ggez/widget.rs index a9e0a7ba..657a3190 100644 --- a/examples/ggez/widget.rs +++ b/examples/ggez/widget.rs @@ -1,3 +1,5 @@ use ggez::graphics::Color; +pub use iced::{button, Button, Column, Row}; + pub type Text = iced::Text; diff --git a/resources/ui.png b/resources/ui.png new file mode 100644 index 0000000000000000000000000000000000000000..4fd3beb3de8034585eb8334c123fb2bbfdefb2e4 GIT binary patch literal 16691 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