summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-10-03 00:01:45 +0200
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2019-10-03 00:01:45 +0200
commite1b9d42bf1443ae4958aa9303255ef19c635debb (patch)
treea7b7615dabc328a90300488ab8623740417277c8 /examples
parent67d3fe67f312c4dfe9fe4af0f0cbc7cb23c30072 (diff)
downloadiced-e1b9d42bf1443ae4958aa9303255ef19c635debb.tar.gz
iced-e1b9d42bf1443ae4958aa9303255ef19c635debb.tar.bz2
iced-e1b9d42bf1443ae4958aa9303255ef19c635debb.zip
Start `iced_winit` and `iced_wgpu`
Diffstat (limited to 'examples')
-rw-r--r--examples/tour/Cargo.toml23
-rw-r--r--examples/tour/src/iced_ggez.rs6
-rw-r--r--examples/tour/src/iced_ggez/renderer.rs77
-rw-r--r--examples/tour/src/iced_ggez/renderer/button.rs154
-rw-r--r--examples/tour/src/iced_ggez/renderer/checkbox.rs94
-rw-r--r--examples/tour/src/iced_ggez/renderer/debugger.rs32
-rw-r--r--examples/tour/src/iced_ggez/renderer/image.rs76
-rw-r--r--examples/tour/src/iced_ggez/renderer/radio.rs92
-rw-r--r--examples/tour/src/iced_ggez/renderer/slider.rs93
-rw-r--r--examples/tour/src/iced_ggez/renderer/text.rs110
-rw-r--r--examples/tour/src/iced_ggez/widget.rs12
-rw-r--r--examples/tour/src/lib.rs11
-rw-r--r--examples/tour/src/main.rs712
-rw-r--r--examples/tour/src/tour.rs563
-rw-r--r--examples/tour/src/web.rs33
-rw-r--r--examples/tour/src/widget.rs5
16 files changed, 548 insertions, 1545 deletions
diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml
index 2c79cbf7..8b3d7765 100644
--- a/examples/tour/Cargo.toml
+++ b/examples/tour/Cargo.toml
@@ -8,26 +8,5 @@ repository = "https://github.com/hecrj/iced"
edition = "2018"
publish = false
-[lib]
-crate-type = ["cdylib", "rlib"]
-
-[[bin]]
-name = "main"
-path = "src/main.rs"
-
[dependencies]
-futures-preview = "=0.3.0-alpha.18"
-
-[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
-iced_native = { version = "0.1.0-alpha", path = "../../native" }
-# A personal `ggez` fork that introduces a `FontCache` type to measure text
-# efficiently and fixes HiDPI issues.
-ggez = { version = "0.5", git = "https://github.com/hecrj/ggez.git" }
-env_logger = "0.6"
-
-[target.'cfg(target_arch = "wasm32")'.dependencies]
-iced_web = { path = "../../web" }
-wasm-bindgen = "0.2.50"
-log = "0.4"
-console_error_panic_hook = "0.1.6"
-console_log = "0.1.2"
+iced = { version = "0.1.0-alpha.1", path = "../.." }
diff --git a/examples/tour/src/iced_ggez.rs b/examples/tour/src/iced_ggez.rs
deleted file mode 100644
index 4a9c0ef4..00000000
--- a/examples/tour/src/iced_ggez.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-mod renderer;
-mod widget;
-
-pub use renderer::Cache as ImageCache;
-pub use renderer::Renderer;
-pub use widget::*;
diff --git a/examples/tour/src/iced_ggez/renderer.rs b/examples/tour/src/iced_ggez/renderer.rs
deleted file mode 100644
index c0e6d559..00000000
--- a/examples/tour/src/iced_ggez/renderer.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-mod button;
-mod checkbox;
-mod debugger;
-mod image;
-mod radio;
-mod slider;
-mod text;
-
-use ggez::graphics::{
- self, spritebatch::SpriteBatch, Font, Image, MeshBuilder,
-};
-use ggez::Context;
-
-pub use image::Cache;
-
-pub struct Renderer<'a> {
- pub context: &'a mut Context,
- pub images: &'a mut image::Cache,
- pub sprites: SpriteBatch,
- pub spritesheet: Image,
- pub font: Font,
- font_size: f32,
- debug_mesh: Option<MeshBuilder>,
-}
-
-impl<'a> Renderer<'a> {
- pub fn new(
- context: &'a mut Context,
- images: &'a mut image::Cache,
- spritesheet: Image,
- font: Font,
- ) -> Renderer<'a> {
- Renderer {
- context,
- images,
- sprites: SpriteBatch::new(spritesheet.clone()),
- spritesheet,
- font,
- font_size: 20.0,
- debug_mesh: None,
- }
- }
-
- 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(),
- Default::default(),
- graphics::FilterMode::Linear,
- )
- .expect("Draw text");
-
- if let Some(debug_mesh) = self.debug_mesh.take() {
- let mesh =
- debug_mesh.build(self.context).expect("Build debug mesh");
-
- graphics::draw(self.context, &mesh, graphics::DrawParam::default())
- .expect("Draw debug mesh");
- }
- }
-}
-
-pub fn into_color(color: iced_native::Color) -> graphics::Color {
- graphics::Color {
- r: color.r,
- g: color.g,
- b: color.b,
- a: color.a,
- }
-}
diff --git a/examples/tour/src/iced_ggez/renderer/button.rs b/examples/tour/src/iced_ggez/renderer/button.rs
deleted file mode 100644
index 78a5de07..00000000
--- a/examples/tour/src/iced_ggez/renderer/button.rs
+++ /dev/null
@@ -1,154 +0,0 @@
-use super::Renderer;
-use ggez::graphics::{
- self, Align, Color, DrawParam, Rect, Scale, Text, TextFragment, WHITE,
-};
-use iced_native::{button, Button, Layout, Length, MouseCursor, Node, Style};
-
-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 node<Message>(&self, button: &Button<'_, Message>) -> Node {
- let style = Style::default()
- .width(button.width)
- .height(Length::Units(LEFT.h as u16))
- .min_width(Length::Units(100))
- .align_self(button.align_self);
-
- Node::new(style)
- }
-
- fn draw<Message>(
- &mut self,
- button: &Button<'_, Message>,
- layout: Layout<'_>,
- cursor_position: iced_native::Point,
- ) -> MouseCursor {
- let mut bounds = layout.bounds();
- let mouse_over = bounds.contains(cursor_position);
-
- let mut state_offset = 0.0;
-
- if mouse_over {
- if button.state.is_pressed() {
- bounds.y += 4.0;
- state_offset = RIGHT.x + RIGHT.w;
- } else {
- bounds.y -= 1.0;
- }
- }
-
- let class_index = match button.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: button.label.clone(),
- font: Some(self.font),
- 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/tour/src/iced_ggez/renderer/checkbox.rs b/examples/tour/src/iced_ggez/renderer/checkbox.rs
deleted file mode 100644
index 807185d9..00000000
--- a/examples/tour/src/iced_ggez/renderer/checkbox.rs
+++ /dev/null
@@ -1,94 +0,0 @@
-use super::Renderer;
-
-use ggez::graphics::{DrawParam, Rect};
-use iced_native::{
- checkbox, text, Align, Checkbox, Column, Layout, Length, MouseCursor, Node,
- Row, Text, Widget,
-};
-
-const SPRITE: Rect = Rect {
- x: 98.0,
- y: 0.0,
- w: 28.0,
- h: 28.0,
-};
-
-impl checkbox::Renderer for Renderer<'_>
-where
- Self: text::Renderer,
-{
- fn node<Message>(&mut self, checkbox: &Checkbox<Message>) -> Node {
- Row::<(), Self>::new()
- .spacing(15)
- .align_items(Align::Center)
- .push(
- Column::new()
- .width(Length::Units(SPRITE.w as u16))
- .height(Length::Units(SPRITE.h as u16)),
- )
- .push(Text::new(&checkbox.label))
- .node(self)
- }
-
- fn draw<Message>(
- &mut self,
- checkbox: &Checkbox<Message>,
- layout: Layout<'_>,
- cursor_position: iced_native::Point,
- ) -> MouseCursor {
- let bounds = layout.bounds();
- let children: Vec<_> = layout.children().collect();
- let text_bounds = children[1].bounds();
-
- let mut text = Text::new(&checkbox.label);
-
- if let Some(label_color) = checkbox.label_color {
- text = text.color(label_color);
- }
-
- text::Renderer::draw(self, &text, children[1]);
-
- let mouse_over = bounds.contains(cursor_position)
- || text_bounds.contains(cursor_position);
-
- let width = self.spritesheet.width() as f32;
- let height = self.spritesheet.height() as f32;
-
- self.sprites.add(DrawParam {
- src: Rect {
- x: (SPRITE.x + (if mouse_over { SPRITE.w } else { 0.0 }))
- / width,
- y: SPRITE.y / height,
- w: SPRITE.w / width,
- h: SPRITE.h / height,
- },
- dest: ggez::mint::Point2 {
- x: bounds.x,
- y: bounds.y,
- },
- ..DrawParam::default()
- });
-
- if checkbox.is_checked {
- self.sprites.add(DrawParam {
- src: Rect {
- x: (SPRITE.x + SPRITE.w * 2.0) / width,
- y: SPRITE.y / height,
- w: SPRITE.w / width,
- h: SPRITE.h / height,
- },
- dest: ggez::mint::Point2 {
- x: bounds.x,
- y: bounds.y,
- },
- ..DrawParam::default()
- });
- }
-
- if mouse_over {
- MouseCursor::Pointer
- } else {
- MouseCursor::OutOfBounds
- }
- }
-}
diff --git a/examples/tour/src/iced_ggez/renderer/debugger.rs b/examples/tour/src/iced_ggez/renderer/debugger.rs
deleted file mode 100644
index ffb658af..00000000
--- a/examples/tour/src/iced_ggez/renderer/debugger.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use super::{into_color, Renderer};
-use ggez::graphics::{DrawMode, MeshBuilder, Rect};
-
-impl iced_native::renderer::Debugger for Renderer<'_> {
- fn explain(
- &mut self,
- layout: &iced_native::Layout<'_>,
- color: iced_native::Color,
- ) {
- let bounds = layout.bounds();
-
- let mut debug_mesh =
- self.debug_mesh.take().unwrap_or(MeshBuilder::new());
-
- debug_mesh.rectangle(
- DrawMode::stroke(1.0),
- Rect {
- x: bounds.x,
- y: bounds.y,
- w: bounds.width,
- h: bounds.height,
- },
- into_color(color),
- );
-
- self.debug_mesh = Some(debug_mesh);
-
- for child in layout.children() {
- self.explain(&child, color);
- }
- }
-}
diff --git a/examples/tour/src/iced_ggez/renderer/image.rs b/examples/tour/src/iced_ggez/renderer/image.rs
deleted file mode 100644
index b12b65c3..00000000
--- a/examples/tour/src/iced_ggez/renderer/image.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use super::Renderer;
-
-use ggez::{graphics, nalgebra};
-use iced_native::{image, Image, Layout, Length, Style};
-
-pub struct Cache {
- images: std::collections::HashMap<String, graphics::Image>,
-}
-
-impl Cache {
- pub fn new() -> Self {
- Self {
- images: std::collections::HashMap::new(),
- }
- }
-
- fn get<'a>(
- &mut self,
- name: &'a str,
- context: &mut ggez::Context,
- ) -> graphics::Image {
- if let Some(image) = self.images.get(name) {
- return image.clone();
- }
-
- let mut image = graphics::Image::new(context, &format!("/{}", name))
- .expect("Load ferris image");
-
- image.set_filter(graphics::FilterMode::Linear);
-
- self.images.insert(name.to_string(), image.clone());
-
- image
- }
-}
-
-impl<'a> image::Renderer<&'a str> for Renderer<'_> {
- fn node(&mut self, image: &Image<&'a str>) -> iced_native::Node {
- let ggez_image = self.images.get(image.handle, self.context);
-
- let aspect_ratio =
- ggez_image.width() as f32 / ggez_image.height() as f32;
-
- let mut style = Style::default().align_self(image.align_self);
-
- style = match (image.width, image.height) {
- (Length::Units(width), _) => style.width(image.width).height(
- Length::Units((width as f32 / aspect_ratio).round() as u16),
- ),
- (_, _) => style
- .width(Length::Units(ggez_image.width()))
- .height(Length::Units(ggez_image.height())),
- };
-
- iced_native::Node::new(style)
- }
-
- fn draw(&mut self, image: &Image<&'a str>, layout: Layout<'_>) {
- let image = self.images.get(image.handle, self.context);
- let bounds = layout.bounds();
-
- // We should probably use batches to draw images efficiently and keep
- // draw side-effect free, but this is good enough for the example.
- graphics::draw(
- self.context,
- &image,
- graphics::DrawParam::new()
- .dest(nalgebra::Point2::new(bounds.x, bounds.y))
- .scale(nalgebra::Vector2::new(
- bounds.width / image.width() as f32,
- bounds.height / image.height() as f32,
- )),
- )
- .expect("Draw image");
- }
-}
diff --git a/examples/tour/src/iced_ggez/renderer/radio.rs b/examples/tour/src/iced_ggez/renderer/radio.rs
deleted file mode 100644
index dbd29ecd..00000000
--- a/examples/tour/src/iced_ggez/renderer/radio.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-use super::Renderer;
-
-use ggez::graphics::{DrawParam, Rect};
-use iced_native::{
- radio, text, Align, Column, Layout, Length, MouseCursor, Node, Point,
- Radio, Row, Text, Widget,
-};
-
-const SPRITE: Rect = Rect {
- x: 98.0,
- y: 28.0,
- w: 28.0,
- h: 28.0,
-};
-
-impl radio::Renderer for Renderer<'_>
-where
- Self: text::Renderer,
-{
- fn node<Message>(&mut self, radio: &Radio<Message>) -> Node {
- Row::<(), Self>::new()
- .spacing(15)
- .align_items(Align::Center)
- .push(
- Column::new()
- .width(Length::Units(SPRITE.w as u16))
- .height(Length::Units(SPRITE.h as u16)),
- )
- .push(Text::new(&radio.label))
- .node(self)
- }
-
- fn draw<Message>(
- &mut self,
- radio: &Radio<Message>,
- layout: Layout<'_>,
- cursor_position: Point,
- ) -> MouseCursor {
- let children: Vec<_> = layout.children().collect();
-
- let mut text = Text::new(&radio.label);
-
- if let Some(label_color) = radio.label_color {
- text = text.color(label_color);
- }
-
- text::Renderer::draw(self, &text, children[1]);
-
- let bounds = layout.bounds();
- let mouse_over = bounds.contains(cursor_position);
-
- let width = self.spritesheet.width() as f32;
- let height = self.spritesheet.height() as f32;
-
- self.sprites.add(DrawParam {
- src: Rect {
- x: (SPRITE.x + (if mouse_over { SPRITE.w } else { 0.0 }))
- / width,
- y: SPRITE.y / height,
- w: SPRITE.w / width,
- h: SPRITE.h / height,
- },
- dest: ggez::mint::Point2 {
- x: bounds.x,
- y: bounds.y,
- },
- ..DrawParam::default()
- });
-
- if radio.is_selected {
- self.sprites.add(DrawParam {
- src: Rect {
- x: (SPRITE.x + SPRITE.w * 2.0) / width,
- y: SPRITE.y / height,
- w: SPRITE.w / width,
- h: SPRITE.h / height,
- },
- dest: ggez::mint::Point2 {
- x: bounds.x,
- y: bounds.y,
- },
- ..DrawParam::default()
- });
- }
-
- if mouse_over {
- MouseCursor::Pointer
- } else {
- MouseCursor::OutOfBounds
- }
- }
-}
diff --git a/examples/tour/src/iced_ggez/renderer/slider.rs b/examples/tour/src/iced_ggez/renderer/slider.rs
deleted file mode 100644
index 60c40c55..00000000
--- a/examples/tour/src/iced_ggez/renderer/slider.rs
+++ /dev/null
@@ -1,93 +0,0 @@
-use super::Renderer;
-
-use ggez::graphics::{DrawParam, Rect};
-use iced_native::{
- slider, Layout, Length, MouseCursor, Node, Point, Slider, Style,
-};
-
-const RAIL: Rect = Rect {
- x: 98.0,
- y: 56.0,
- w: 1.0,
- h: 4.0,
-};
-
-const MARKER: Rect = Rect {
- x: RAIL.x + 28.0,
- y: RAIL.y,
- w: 16.0,
- h: 24.0,
-};
-
-impl slider::Renderer for Renderer<'_> {
- fn node<Message>(&self, slider: &Slider<'_, Message>) -> Node {
- let style = Style::default()
- .width(slider.width)
- .height(Length::Units(25))
- .min_width(Length::Units(100));
-
- Node::new(style)
- }
-
- fn draw<Message>(
- &mut self,
- slider: &Slider<'_, Message>,
- layout: Layout<'_>,
- cursor_position: Point,
- ) -> MouseCursor {
- let bounds = layout.bounds();
- let width = self.spritesheet.width() as f32;
- let height = self.spritesheet.height() as f32;
-
- self.sprites.add(DrawParam {
- src: Rect {
- x: RAIL.x / width,
- y: RAIL.y / height,
- w: RAIL.w / width,
- h: RAIL.h / height,
- },
- dest: ggez::mint::Point2 {
- x: bounds.x + MARKER.w as f32 / 2.0,
- y: bounds.y + 12.5,
- },
- scale: ggez::mint::Vector2 {
- x: bounds.width - MARKER.w as f32,
- y: 1.0,
- },
- ..DrawParam::default()
- });
-
- let (range_start, range_end) = slider.range.clone().into_inner();
-
- let marker_offset = (bounds.width - MARKER.w as f32)
- * ((slider.value - range_start)
- / (range_end - range_start).max(1.0));
-
- let mouse_over = bounds.contains(cursor_position);
- let is_active = slider.state.is_dragging() || mouse_over;
-
- self.sprites.add(DrawParam {
- src: Rect {
- x: (MARKER.x + (if is_active { MARKER.w } else { 0.0 }))
- / width,
- y: MARKER.y / height,
- w: MARKER.w / width,
- h: MARKER.h / height,
- },
- dest: ggez::mint::Point2 {
- x: bounds.x + marker_offset.round(),
- y: bounds.y
- + (if slider.state.is_dragging() { 2.0 } else { 0.0 }),
- },
- ..DrawParam::default()
- });
-
- if slider.state.is_dragging() {
- MouseCursor::Grabbing
- } else if mouse_over {
- MouseCursor::Grab
- } else {
- MouseCursor::OutOfBounds
- }
- }
-}
diff --git a/examples/tour/src/iced_ggez/renderer/text.rs b/examples/tour/src/iced_ggez/renderer/text.rs
deleted file mode 100644
index b51cc220..00000000
--- a/examples/tour/src/iced_ggez/renderer/text.rs
+++ /dev/null
@@ -1,110 +0,0 @@
-use super::{into_color, Renderer};
-use ggez::graphics::{self, mint, Align, Scale, Text, TextFragment};
-
-use iced_native::{text, Layout, Node, Style};
-use std::cell::RefCell;
-use std::f32;
-
-impl text::Renderer for Renderer<'_> {
- fn node(&self, text: &iced_native::Text) -> Node {
- let font = self.font;
- let font_cache = graphics::font_cache(self.context);
- let content = String::from(&text.content);
-
- // 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 measure = RefCell::new(None);
- let size = text.size.map(f32::from).unwrap_or(self.font_size);
-
- let style = Style::default().width(text.width);
-
- iced_native::Node::with_measure(style, move |bounds| {
- let mut measure = measure.borrow_mut();
-
- if measure.is_none() {
- let bounds = (
- match bounds.width {
- iced_native::Number::Undefined => f32::INFINITY,
- iced_native::Number::Defined(w) => w,
- },
- match bounds.height {
- iced_native::Number::Undefined => f32::INFINITY,
- iced_native::Number::Defined(h) => h,
- },
- );
-
- let mut text = Text::new(TextFragment {
- text: content.clone(),
- font: Some(font),
- 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) = font_cache.dimensions(&text);
-
- let size = iced_native::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, text: &iced_native::Text, layout: Layout<'_>) {
- let size = text.size.map(f32::from).unwrap_or(self.font_size);
- let bounds = layout.bounds();
-
- let mut ggez_text = Text::new(TextFragment {
- text: text.content.clone(),
- font: Some(self.font),
- scale: Some(Scale { x: size, y: size }),
- ..Default::default()
- });
-
- ggez_text.set_bounds(
- mint::Point2 {
- x: bounds.width,
- y: bounds.height,
- },
- match text.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,
- &ggez_text,
- mint::Point2 {
- x: bounds.x,
- y: bounds.y,
- },
- text.color
- .or(Some(iced_native::Color::BLACK))
- .map(into_color),
- );
- }
-}
diff --git a/examples/tour/src/iced_ggez/widget.rs b/examples/tour/src/iced_ggez/widget.rs
deleted file mode 100644
index 948f9fc6..00000000
--- a/examples/tour/src/iced_ggez/widget.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use super::Renderer;
-
-pub use iced_native::{
- button, slider, text, Align, Button, Checkbox, Color, Length, Radio,
- Slider, Text,
-};
-
-pub type Image<'a> = iced_native::Image<&'a str>;
-
-pub type Column<'a, Message> = iced_native::Column<'a, Message, Renderer<'a>>;
-pub type Row<'a, Message> = iced_native::Row<'a, Message, Renderer<'a>>;
-pub type Element<'a, Message> = iced_native::Element<'a, Message, Renderer<'a>>;
diff --git a/examples/tour/src/lib.rs b/examples/tour/src/lib.rs
deleted file mode 100644
index eb41fcd9..00000000
--- a/examples/tour/src/lib.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-pub mod tour;
-
-pub use tour::{Message, Tour};
-
-mod widget;
-
-#[cfg(target_arch = "wasm32")]
-mod web;
-
-#[cfg(not(target_arch = "wasm32"))]
-pub mod iced_ggez;
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index a34d3298..5017041a 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -1,191 +1,573 @@
-use iced_tour::{iced_ggez, Tour};
-
-use ggez;
-use ggez::event;
-use ggez::filesystem;
-use ggez::graphics;
-use ggez::input::mouse;
-
-pub fn main() -> ggez::GameResult {
- env_logger::init();
-
- let (context, event_loop) = {
- &mut ggez::ContextBuilder::new("iced", "ggez")
- .window_mode(ggez::conf::WindowMode {
- width: 1280.0,
- height: 1024.0,
- resizable: true,
- ..ggez::conf::WindowMode::default()
- })
- .build()?
- };
-
- filesystem::mount(
- context,
- std::path::Path::new(env!("CARGO_MANIFEST_DIR")),
- true,
- );
-
- let state = &mut Game::new(context)?;
-
- event::run(context, event_loop, state)
-}
+use iced::{
+ button, slider, text::HorizontalAlignment, Align, Button, Checkbox, Color,
+ Column, Element, Image, Length, Radio, Row, Slider, Text, UserInterface,
+};
-struct Game {
- spritesheet: graphics::Image,
- font: graphics::Font,
- images: iced_ggez::ImageCache,
- tour: Tour,
+pub fn main() {
+ let tour = Tour::new();
- events: Vec<iced_native::Event>,
- cache: Option<iced_native::Cache>,
+ tour.run();
}
-impl Game {
- fn new(context: &mut ggez::Context) -> ggez::GameResult<Game> {
- graphics::set_default_filter(context, graphics::FilterMode::Nearest);
-
- Ok(Game {
- spritesheet: graphics::Image::new(context, "/resources/ui.png")
- .unwrap(),
- font: graphics::Font::new(context, "/resources/Roboto-Regular.ttf")
- .unwrap(),
- images: iced_ggez::ImageCache::new(),
- tour: Tour::new(),
+pub struct Tour {
+ steps: Steps,
+ back_button: button::State,
+ next_button: button::State,
+ debug: bool,
+}
- events: Vec::new(),
- cache: Some(iced_native::Cache::default()),
- })
+impl Tour {
+ pub fn new() -> Tour {
+ Tour {
+ steps: Steps::new(),
+ back_button: button::State::new(),
+ next_button: button::State::new(),
+ debug: false,
+ }
}
}
-impl event::EventHandler for Game {
- fn update(&mut self, _ctx: &mut ggez::Context) -> ggez::GameResult {
- Ok(())
- }
-
- fn mouse_button_down_event(
- &mut self,
- _context: &mut ggez::Context,
- _button: mouse::MouseButton,
- _x: f32,
- _y: f32,
- ) {
- self.events.push(iced_native::Event::Mouse(
- iced_native::input::mouse::Event::Input {
- state: iced_native::input::ButtonState::Pressed,
- button: iced_native::input::mouse::Button::Left, // TODO: Map `button`
- },
- ));
- }
-
- fn mouse_button_up_event(
- &mut self,
- _context: &mut ggez::Context,
- _button: mouse::MouseButton,
- _x: f32,
- _y: f32,
- ) {
- self.events.push(iced_native::Event::Mouse(
- iced_native::input::mouse::Event::Input {
- state: iced_native::input::ButtonState::Released,
- button: iced_native::input::mouse::Button::Left, // TODO: Map `button`
- },
- ));
- }
-
- fn mouse_motion_event(
- &mut self,
- _context: &mut ggez::Context,
- x: f32,
- y: f32,
- _dx: f32,
- _dy: f32,
- ) {
- self.events.push(iced_native::Event::Mouse(
- iced_native::input::mouse::Event::CursorMoved { x, y },
- ));
- }
-
- fn resize_event(
- &mut self,
- context: &mut ggez::Context,
- width: f32,
- height: f32,
- ) {
- graphics::set_screen_coordinates(
- context,
- graphics::Rect {
- x: 0.0,
- y: 0.0,
- w: width,
- h: height,
- },
- )
- .expect("Set screen coordinates");
- }
-
- fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult {
- graphics::clear(context, graphics::WHITE);
-
- let screen = graphics::screen_coordinates(context);
-
- let (messages, cursor) = {
- let view = self.tour.view();
-
- let content = iced_ggez::Column::new()
- .width(iced_native::Length::Units(screen.w as u16))
- .height(iced_native::Length::Units(screen.h as u16))
- .padding(20)
- .align_items(iced_native::Align::Center)
- .justify_content(iced_native::Justify::Center)
- .push(view);
-
- let renderer = &mut iced_ggez::Renderer::new(
- context,
- &mut self.images,
- self.spritesheet.clone(),
- self.font,
+impl UserInterface for Tour {
+ type Message = Message;
+
+ fn update(&mut self, event: Message) {
+ match event {
+ Message::BackPressed => {
+ self.steps.go_back();
+ }
+ Message::NextPressed => {
+ self.steps.advance();
+ }
+ Message::StepMessage(step_msg) => {
+ self.steps.update(step_msg, &mut self.debug);
+ }
+ }
+ }
+
+ fn view(&mut self) -> Element<Message> {
+ let Tour {
+ steps,
+ back_button,
+ next_button,
+ ..
+ } = self;
+
+ let mut controls = Row::new();
+
+ if steps.has_previous() {
+ controls = controls.push(
+ Button::new(back_button, "Back")
+ .on_press(Message::BackPressed)
+ .class(button::Class::Secondary),
);
+ }
+
+ controls = controls.push(Column::new());
- let mut ui = iced_native::UserInterface::build(
- content,
- self.cache.take().unwrap(),
- renderer,
+ if steps.can_continue() {
+ controls = controls.push(
+ Button::new(next_button, "Next").on_press(Message::NextPressed),
);
+ }
- let messages = ui.update(self.events.drain(..));
- let cursor = ui.draw(renderer);
+ let element: Element<_> = Column::new()
+ .max_width(Length::Units(500))
+ .spacing(20)
+ .push(steps.view(self.debug).map(Message::StepMessage))
+ .push(controls)
+ .into();
- self.cache = Some(ui.into_cache());
+ if self.debug {
+ element.explain(Color::BLACK)
+ } else {
+ element
+ }
+ }
+}
- renderer.flush();
+#[derive(Debug, Clone, Copy)]
+pub enum Message {
+ BackPressed,
+ NextPressed,
+ StepMessage(StepMessage),
+}
- (messages, cursor)
- };
+struct Steps {
+ steps: Vec<Step>,
+ current: usize,
+}
- for message in messages {
- self.tour.update(message);
+impl Steps {
+ fn new() -> Steps {
+ Steps {
+ steps: vec![
+ Step::Welcome,
+ Step::Slider {
+ state: slider::State::new(),
+ value: 50,
+ },
+ Step::RowsAndColumns {
+ layout: Layout::Row,
+ spacing_slider: slider::State::new(),
+ spacing: 20,
+ },
+ Step::Text {
+ size_slider: slider::State::new(),
+ size: 30,
+ color_sliders: [slider::State::new(); 3],
+ color: Color::BLACK,
+ },
+ Step::Radio { selection: None },
+ Step::Image {
+ width: 300,
+ slider: slider::State::new(),
+ },
+ Step::Debugger,
+ Step::End,
+ ],
+ current: 0,
}
+ }
- let cursor_type = into_cursor_type(cursor);
+ fn update(&mut self, msg: StepMessage, debug: &mut bool) {
+ self.steps[self.current].update(msg, debug);
+ }
+
+ fn view(&mut self, debug: bool) -> Element<StepMessage> {
+ self.steps[self.current].view(debug)
+ }
+
+ fn advance(&mut self) {
+ if self.can_continue() {
+ self.current += 1;
+ }
+ }
- if mouse::cursor_type(context) != cursor_type {
- mouse::set_cursor_type(context, cursor_type);
+ fn go_back(&mut self) {
+ if self.has_previous() {
+ self.current -= 1;
}
+ }
- graphics::present(context)?;
- Ok(())
+ fn has_previous(&self) -> bool {
+ self.current > 0
}
+
+ fn can_continue(&self) -> bool {
+ self.current + 1 < self.steps.len()
+ && self.steps[self.current].can_continue()
+ }
+}
+
+enum Step {
+ Welcome,
+ Slider {
+ state: slider::State,
+ value: u16,
+ },
+ RowsAndColumns {
+ layout: Layout,
+ spacing_slider: slider::State,
+ spacing: u16,
+ },
+ Text {
+ size_slider: slider::State,
+ size: u16,
+ color_sliders: [slider::State; 3],
+ color: Color,
+ },
+ Radio {
+ selection: Option<Language>,
+ },
+ Image {
+ width: u16,
+ slider: slider::State,
+ },
+ Debugger,
+ End,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum StepMessage {
+ SliderChanged(f32),
+ LayoutChanged(Layout),
+ SpacingChanged(f32),
+ TextSizeChanged(f32),
+ TextColorChanged(Color),
+ LanguageSelected(Language),
+ ImageWidthChanged(f32),
+ DebugToggled(bool),
}
-fn into_cursor_type(cursor: iced_native::MouseCursor) -> mouse::MouseCursor {
- match cursor {
- iced_native::MouseCursor::OutOfBounds => mouse::MouseCursor::Default,
- iced_native::MouseCursor::Idle => mouse::MouseCursor::Default,
- iced_native::MouseCursor::Pointer => mouse::MouseCursor::Hand,
- iced_native::MouseCursor::Working => mouse::MouseCursor::Progress,
- iced_native::MouseCursor::Grab => mouse::MouseCursor::Grab,
- iced_native::MouseCursor::Grabbing => mouse::MouseCursor::Grabbing,
+impl<'a> Step {
+ fn update(&mut self, msg: StepMessage, debug: &mut bool) {
+ match msg {
+ StepMessage::DebugToggled(value) => {
+ if let Step::Debugger = self {
+ *debug = value;
+ }
+ }
+ StepMessage::LanguageSelected(language) => {
+ if let Step::Radio { selection } = self {
+ *selection = Some(language);
+ }
+ }
+ StepMessage::SliderChanged(new_value) => {
+ if let Step::Slider { value, .. } = self {
+ *value = new_value.round() as u16;
+ }
+ }
+ StepMessage::TextSizeChanged(new_size) => {
+ if let Step::Text { size, .. } = self {
+ *size = new_size.round() as u16;
+ }
+ }
+ StepMessage::TextColorChanged(new_color) => {
+ if let Step::Text { color, .. } = self {
+ *color = new_color;
+ }
+ }
+ StepMessage::LayoutChanged(new_layout) => {
+ if let Step::RowsAndColumns { layout, .. } = self {
+ *layout = new_layout;
+ }
+ }
+ StepMessage::SpacingChanged(new_spacing) => {
+ if let Step::RowsAndColumns { spacing, .. } = self {
+ *spacing = new_spacing.round() as u16;
+ }
+ }
+ StepMessage::ImageWidthChanged(new_width) => {
+ if let Step::Image { width, .. } = self {
+ *width = new_width.round() as u16;
+ }
+ }
+ };
+ }
+
+ fn can_continue(&self) -> bool {
+ match self {
+ Step::Welcome => true,
+ Step::Radio { selection } => *selection == Some(Language::Rust),
+ Step::Slider { .. } => true,
+ Step::Text { .. } => true,
+ Step::Image { .. } => true,
+ Step::RowsAndColumns { .. } => true,
+ Step::Debugger => true,
+ Step::End => false,
+ }
+ }
+
+ fn view(&mut self, debug: bool) -> Element<StepMessage> {
+ match self {
+ Step::Welcome => Self::welcome().into(),
+ Step::Radio { selection } => Self::radio(*selection).into(),
+ Step::Slider { state, value } => Self::slider(state, *value).into(),
+ Step::Text {
+ size_slider,
+ size,
+ color_sliders,
+ color,
+ } => Self::text(size_slider, *size, color_sliders, *color).into(),
+ Step::Image { width, slider } => Self::image(*width, slider).into(),
+ Step::RowsAndColumns {
+ layout,
+ spacing_slider,
+ spacing,
+ } => {
+ Self::rows_and_columns(*layout, spacing_slider, *spacing).into()
+ }
+ Step::Debugger => Self::debugger(debug).into(),
+ Step::End => Self::end().into(),
+ }
}
+
+ fn container(title: &str) -> Column<'a, StepMessage> {
+ Column::new()
+ .spacing(20)
+ .align_items(Align::Stretch)
+ .push(Text::new(title).size(50))
+ }
+
+ fn welcome() -> Column<'a, StepMessage> {
+ Self::container("Welcome!")
+ .push(Text::new(
+ "This a simple tour meant to showcase a bunch of widgets that \
+ can be easily implemented on top of Iced.",
+ ))
+ .push(Text::new(
+ "Iced is a renderer-agnostic GUI library for Rust focused on \
+ simplicity and type-safety. It is heavily inspired by Elm.",
+ ))
+ .push(Text::new(
+ "It was originally born as part of Coffee, an opinionated \
+ 2D game engine for Rust.",
+ ))
+ .push(Text::new(
+ "Iced does not provide a built-in renderer. This example runs \
+ on WebAssembly using dodrio, an experimental VDOM library \
+ for Rust.",
+ ))
+ .push(Text::new(
+ "You will need to interact with the UI in order to reach the \
+ end!",
+ ))
+ }
+
+ fn slider(
+ state: &'a mut slider::State,
+ value: u16,
+ ) -> Column<'a, StepMessage> {
+ Self::container("Slider")
+ .push(Text::new(
+ "A slider allows you to smoothly select a value from a range \
+ of values.",
+ ))
+ .push(Text::new(
+ "The following slider lets you choose an integer from \
+ 0 to 100:",
+ ))
+ .push(Slider::new(
+ state,
+ 0.0..=100.0,
+ value as f32,
+ StepMessage::SliderChanged,
+ ))
+ .push(
+ Text::new(&value.to_string())
+ .horizontal_alignment(HorizontalAlignment::Center),
+ )
+ }
+
+ fn rows_and_columns(
+ layout: Layout,
+ spacing_slider: &'a mut slider::State,
+ spacing: u16,
+ ) -> Column<'a, StepMessage> {
+ let row_radio = Radio::new(
+ Layout::Row,
+ "Row",
+ Some(layout),
+ StepMessage::LayoutChanged,
+ );
+
+ let column_radio = Radio::new(
+ Layout::Column,
+ "Column",
+ Some(layout),
+ StepMessage::LayoutChanged,
+ );
+
+ let layout_section: Element<_> = match layout {
+ Layout::Row => Row::new()
+ .spacing(spacing)
+ .push(row_radio)
+ .push(column_radio)
+ .into(),
+ Layout::Column => Column::new()
+ .spacing(spacing)
+ .push(row_radio)
+ .push(column_radio)
+ .into(),
+ };
+
+ let spacing_section = Column::new()
+ .spacing(10)
+ .push(Slider::new(
+ spacing_slider,
+ 0.0..=80.0,
+ spacing as f32,
+ StepMessage::SpacingChanged,
+ ))
+ .push(
+ Text::new(&format!("{} px", spacing))
+ .horizontal_alignment(HorizontalAlignment::Center),
+ );
+
+ Self::container("Rows and columns")
+ .spacing(spacing)
+ .push(Text::new(
+ "Iced uses a layout model based on flexbox to position UI \
+ elements.",
+ ))
+ .push(Text::new(
+ "Rows and columns can be used to distribute content \
+ horizontally or vertically, respectively.",
+ ))
+ .push(layout_section)
+ .push(Text::new(
+ "You can also easily change the spacing between elements:",
+ ))
+ .push(spacing_section)
+ }
+
+ fn text(
+ size_slider: &'a mut slider::State,
+ size: u16,
+ color_sliders: &'a mut [slider::State; 3],
+ color: Color,
+ ) -> Column<'a, StepMessage> {
+ let size_section = Column::new()
+ .padding(20)
+ .spacing(20)
+ .push(Text::new("You can change its size:"))
+ .push(
+ Text::new(&format!("This text is {} pixels", size)).size(size),
+ )
+ .push(Slider::new(
+ size_slider,
+ 10.0..=70.0,
+ size as f32,
+ StepMessage::TextSizeChanged,
+ ));
+
+ let [red, green, blue] = color_sliders;
+ let color_section = Column::new()
+ .padding(20)
+ .spacing(20)
+ .push(Text::new("And its color:"))
+ .push(Text::new(&format!("{:?}", color)).color(color))
+ .push(
+ Row::new()
+ .spacing(10)
+ .push(Slider::new(red, 0.0..=1.0, color.r, move |r| {
+ StepMessage::TextColorChanged(Color { r, ..color })
+ }))
+ .push(Slider::new(green, 0.0..=1.0, color.g, move |g| {
+ StepMessage::TextColorChanged(Color { g, ..color })
+ }))
+ .push(Slider::new(blue, 0.0..=1.0, color.b, move |b| {
+ StepMessage::TextColorChanged(Color { b, ..color })
+ })),
+ );
+
+ Self::container("Text")
+ .push(Text::new(
+ "Text is probably the most essential widget for your UI. \
+ It will try to adapt to the dimensions of its container.",
+ ))
+ .push(size_section)
+ .push(color_section)
+ }
+
+ fn radio(selection: Option<Language>) -> Column<'a, StepMessage> {
+ let question = Column::new()
+ .padding(20)
+ .spacing(10)
+ .push(Text::new("Iced is written in...").size(24))
+ .push(Language::all().iter().cloned().fold(
+ Column::new().padding(10).spacing(20),
+ |choices, language| {
+ choices.push(Radio::new(
+ language,
+ language.into(),
+ selection,
+ StepMessage::LanguageSelected,
+ ))
+ },
+ ));
+
+ Self::container("Radio button")
+ .push(Text::new(
+ "A radio button is normally used to represent a choice... \
+ Surprise test!",
+ ))
+ .push(question)
+ .push(Text::new(
+ "Iced works very well with iterators! The list above is \
+ basically created by folding a column over the different \
+ choices, creating a radio button for each one of them!",
+ ))
+ }
+
+ fn image(
+ width: u16,
+ slider: &'a mut slider::State,
+ ) -> Column<'a, StepMessage> {
+ Self::container("Image")
+ .push(Text::new("An image that tries to keep its aspect ratio."))
+ .push(
+ Image::new("resources/ferris.png")
+ .width(Length::Units(width))
+ .align_self(Align::Center),
+ )
+ .push(Slider::new(
+ slider,
+ 100.0..=500.0,
+ width as f32,
+ StepMessage::ImageWidthChanged,
+ ))
+ .push(
+ Text::new(&format!("Width: {} px", width.to_string()))
+ .horizontal_alignment(HorizontalAlignment::Center),
+ )
+ }
+
+ fn debugger(debug: bool) -> Column<'a, StepMessage> {
+ Self::container("Debugger")
+ .push(Text::new(
+ "You can ask Iced to visually explain the layouting of the \
+ different elements comprising your UI!",
+ ))
+ .push(Text::new(
+ "Give it a shot! Check the following checkbox to be able to \
+ see element boundaries.",
+ ))
+ .push(Checkbox::new(
+ debug,
+ "Explain layout",
+ StepMessage::DebugToggled,
+ ))
+ .push(Text::new("Feel free to go back and take a look."))
+ }
+
+ fn end() -> Column<'a, StepMessage> {
+ Self::container("You reached the end!")
+ .push(Text::new(
+ "This tour will be updated as more features are added.",
+ ))
+ .push(Text::new("Make sure to keep an eye on it!"))
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Language {
+ Rust,
+ Elm,
+ Ruby,
+ Haskell,
+ C,
+ Other,
+}
+
+impl Language {
+ fn all() -> [Language; 6] {
+ [
+ Language::C,
+ Language::Elm,
+ Language::Ruby,
+ Language::Haskell,
+ Language::Rust,
+ Language::Other,
+ ]
+ }
+}
+
+impl From<Language> for &str {
+ fn from(language: Language) -> &'static str {
+ match language {
+ Language::Rust => "Rust",
+ Language::Elm => "Elm",
+ Language::Ruby => "Ruby",
+ Language::Haskell => "Haskell",
+ Language::C => "C",
+ Language::Other => "Other",
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub enum Layout {
+ Row,
+ Column,
}
diff --git a/examples/tour/src/tour.rs b/examples/tour/src/tour.rs
deleted file mode 100644
index 04740fce..00000000
--- a/examples/tour/src/tour.rs
+++ /dev/null
@@ -1,563 +0,0 @@
-use crate::widget::{
- button, slider, text::HorizontalAlignment, Align, Button, Checkbox, Color,
- Column, Element, Image, Length, Radio, Row, Slider, Text,
-};
-
-pub struct Tour {
- steps: Steps,
- back_button: button::State,
- next_button: button::State,
- debug: bool,
-}
-
-impl Tour {
- pub fn new() -> Tour {
- Tour {
- steps: Steps::new(),
- back_button: button::State::new(),
- next_button: button::State::new(),
- debug: false,
- }
- }
-
- pub fn update(&mut self, event: Message) {
- match event {
- Message::BackPressed => {
- self.steps.go_back();
- }
- Message::NextPressed => {
- self.steps.advance();
- }
- Message::StepMessage(step_msg) => {
- self.steps.update(step_msg, &mut self.debug);
- }
- }
- }
-
- pub fn view(&mut self) -> Element<Message> {
- let Tour {
- steps,
- back_button,
- next_button,
- ..
- } = self;
-
- let mut controls = Row::new();
-
- if steps.has_previous() {
- controls = controls.push(
- Button::new(back_button, "Back")
- .on_press(Message::BackPressed)
- .class(button::Class::Secondary),
- );
- }
-
- controls = controls.push(Column::new());
-
- if steps.can_continue() {
- controls = controls.push(
- Button::new(next_button, "Next").on_press(Message::NextPressed),
- );
- }
-
- let element: Element<_> = Column::new()
- .max_width(Length::Units(500))
- .spacing(20)
- .push(steps.view(self.debug).map(Message::StepMessage))
- .push(controls)
- .into();
-
- if self.debug {
- element.explain(Color::BLACK)
- } else {
- element
- }
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum Message {
- BackPressed,
- NextPressed,
- StepMessage(StepMessage),
-}
-
-struct Steps {
- steps: Vec<Step>,
- current: usize,
-}
-
-impl Steps {
- fn new() -> Steps {
- Steps {
- steps: vec![
- Step::Welcome,
- Step::Slider {
- state: slider::State::new(),
- value: 50,
- },
- Step::RowsAndColumns {
- layout: Layout::Row,
- spacing_slider: slider::State::new(),
- spacing: 20,
- },
- Step::Text {
- size_slider: slider::State::new(),
- size: 30,
- color_sliders: [slider::State::new(); 3],
- color: Color::BLACK,
- },
- Step::Radio { selection: None },
- Step::Image {
- width: 300,
- slider: slider::State::new(),
- },
- Step::Debugger,
- Step::End,
- ],
- current: 0,
- }
- }
-
- fn update(&mut self, msg: StepMessage, debug: &mut bool) {
- self.steps[self.current].update(msg, debug);
- }
-
- fn view(&mut self, debug: bool) -> Element<StepMessage> {
- self.steps[self.current].view(debug)
- }
-
- fn advance(&mut self) {
- if self.can_continue() {
- self.current += 1;
- }
- }
-
- fn go_back(&mut self) {
- if self.has_previous() {
- self.current -= 1;
- }
- }
-
- fn has_previous(&self) -> bool {
- self.current > 0
- }
-
- fn can_continue(&self) -> bool {
- self.current + 1 < self.steps.len()
- && self.steps[self.current].can_continue()
- }
-}
-
-enum Step {
- Welcome,
- Slider {
- state: slider::State,
- value: u16,
- },
- RowsAndColumns {
- layout: Layout,
- spacing_slider: slider::State,
- spacing: u16,
- },
- Text {
- size_slider: slider::State,
- size: u16,
- color_sliders: [slider::State; 3],
- color: Color,
- },
- Radio {
- selection: Option<Language>,
- },
- Image {
- width: u16,
- slider: slider::State,
- },
- Debugger,
- End,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum StepMessage {
- SliderChanged(f32),
- LayoutChanged(Layout),
- SpacingChanged(f32),
- TextSizeChanged(f32),
- TextColorChanged(Color),
- LanguageSelected(Language),
- ImageWidthChanged(f32),
- DebugToggled(bool),
-}
-
-impl<'a> Step {
- fn update(&mut self, msg: StepMessage, debug: &mut bool) {
- match msg {
- StepMessage::DebugToggled(value) => {
- if let Step::Debugger = self {
- *debug = value;
- }
- }
- StepMessage::LanguageSelected(language) => {
- if let Step::Radio { selection } = self {
- *selection = Some(language);
- }
- }
- StepMessage::SliderChanged(new_value) => {
- if let Step::Slider { value, .. } = self {
- *value = new_value.round() as u16;
- }
- }
- StepMessage::TextSizeChanged(new_size) => {
- if let Step::Text { size, .. } = self {
- *size = new_size.round() as u16;
- }
- }
- StepMessage::TextColorChanged(new_color) => {
- if let Step::Text { color, .. } = self {
- *color = new_color;
- }
- }
- StepMessage::LayoutChanged(new_layout) => {
- if let Step::RowsAndColumns { layout, .. } = self {
- *layout = new_layout;
- }
- }
- StepMessage::SpacingChanged(new_spacing) => {
- if let Step::RowsAndColumns { spacing, .. } = self {
- *spacing = new_spacing.round() as u16;
- }
- }
- StepMessage::ImageWidthChanged(new_width) => {
- if let Step::Image { width, .. } = self {
- *width = new_width.round() as u16;
- }
- }
- };
- }
-
- fn can_continue(&self) -> bool {
- match self {
- Step::Welcome => true,
- Step::Radio { selection } => *selection == Some(Language::Rust),
- Step::Slider { .. } => true,
- Step::Text { .. } => true,
- Step::Image { .. } => true,
- Step::RowsAndColumns { .. } => true,
- Step::Debugger => true,
- Step::End => false,
- }
- }
-
- fn view(&mut self, debug: bool) -> Element<StepMessage> {
- match self {
- Step::Welcome => Self::welcome().into(),
- Step::Radio { selection } => Self::radio(*selection).into(),
- Step::Slider { state, value } => Self::slider(state, *value).into(),
- Step::Text {
- size_slider,
- size,
- color_sliders,
- color,
- } => Self::text(size_slider, *size, color_sliders, *color).into(),
- Step::Image { width, slider } => Self::image(*width, slider).into(),
- Step::RowsAndColumns {
- layout,
- spacing_slider,
- spacing,
- } => {
- Self::rows_and_columns(*layout, spacing_slider, *spacing).into()
- }
- Step::Debugger => Self::debugger(debug).into(),
- Step::End => Self::end().into(),
- }
- }
-
- fn container(title: &str) -> Column<'a, StepMessage> {
- Column::new()
- .spacing(20)
- .align_items(Align::Stretch)
- .push(Text::new(title).size(50))
- }
-
- fn welcome() -> Column<'a, StepMessage> {
- Self::container("Welcome!")
- .push(Text::new(
- "This a simple tour meant to showcase a bunch of widgets that \
- can be easily implemented on top of Iced.",
- ))
- .push(Text::new(
- "Iced is a renderer-agnostic GUI library for Rust focused on \
- simplicity and type-safety. It is heavily inspired by Elm.",
- ))
- .push(Text::new(
- "It was originally born as part of Coffee, an opinionated \
- 2D game engine for Rust.",
- ))
- .push(Text::new(
- "Iced does not provide a built-in renderer. This example runs \
- on WebAssembly using dodrio, an experimental VDOM library \
- for Rust.",
- ))
- .push(Text::new(
- "You will need to interact with the UI in order to reach the \
- end!",
- ))
- }
-
- fn slider(
- state: &'a mut slider::State,
- value: u16,
- ) -> Column<'a, StepMessage> {
- Self::container("Slider")
- .push(Text::new(
- "A slider allows you to smoothly select a value from a range \
- of values.",
- ))
- .push(Text::new(
- "The following slider lets you choose an integer from \
- 0 to 100:",
- ))
- .push(Slider::new(
- state,
- 0.0..=100.0,
- value as f32,
- StepMessage::SliderChanged,
- ))
- .push(
- Text::new(&value.to_string())
- .horizontal_alignment(HorizontalAlignment::Center),
- )
- }
-
- fn rows_and_columns(
- layout: Layout,
- spacing_slider: &'a mut slider::State,
- spacing: u16,
- ) -> Column<'a, StepMessage> {
- let row_radio = Radio::new(
- Layout::Row,
- "Row",
- Some(layout),
- StepMessage::LayoutChanged,
- );
-
- let column_radio = Radio::new(
- Layout::Column,
- "Column",
- Some(layout),
- StepMessage::LayoutChanged,
- );
-
- let layout_section: Element<_> = match layout {
- Layout::Row => Row::new()
- .spacing(spacing)
- .push(row_radio)
- .push(column_radio)
- .into(),
- Layout::Column => Column::new()
- .spacing(spacing)
- .push(row_radio)
- .push(column_radio)
- .into(),
- };
-
- let spacing_section = Column::new()
- .spacing(10)
- .push(Slider::new(
- spacing_slider,
- 0.0..=80.0,
- spacing as f32,
- StepMessage::SpacingChanged,
- ))
- .push(
- Text::new(&format!("{} px", spacing))
- .horizontal_alignment(HorizontalAlignment::Center),
- );
-
- Self::container("Rows and columns")
- .spacing(spacing)
- .push(Text::new(
- "Iced uses a layout model based on flexbox to position UI \
- elements.",
- ))
- .push(Text::new(
- "Rows and columns can be used to distribute content \
- horizontally or vertically, respectively.",
- ))
- .push(layout_section)
- .push(Text::new(
- "You can also easily change the spacing between elements:",
- ))
- .push(spacing_section)
- }
-
- fn text(
- size_slider: &'a mut slider::State,
- size: u16,
- color_sliders: &'a mut [slider::State; 3],
- color: Color,
- ) -> Column<'a, StepMessage> {
- let size_section = Column::new()
- .padding(20)
- .spacing(20)
- .push(Text::new("You can change its size:"))
- .push(
- Text::new(&format!("This text is {} pixels", size)).size(size),
- )
- .push(Slider::new(
- size_slider,
- 10.0..=70.0,
- size as f32,
- StepMessage::TextSizeChanged,
- ));
-
- let [red, green, blue] = color_sliders;
- let color_section = Column::new()
- .padding(20)
- .spacing(20)
- .push(Text::new("And its color:"))
- .push(Text::new(&format!("{:?}", color)).color(color))
- .push(
- Row::new()
- .spacing(10)
- .push(Slider::new(red, 0.0..=1.0, color.r, move |r| {
- StepMessage::TextColorChanged(Color { r, ..color })
- }))
- .push(Slider::new(green, 0.0..=1.0, color.g, move |g| {
- StepMessage::TextColorChanged(Color { g, ..color })
- }))
- .push(Slider::new(blue, 0.0..=1.0, color.b, move |b| {
- StepMessage::TextColorChanged(Color { b, ..color })
- })),
- );
-
- Self::container("Text")
- .push(Text::new(
- "Text is probably the most essential widget for your UI. \
- It will try to adapt to the dimensions of its container.",
- ))
- .push(size_section)
- .push(color_section)
- }
-
- fn radio(selection: Option<Language>) -> Column<'a, StepMessage> {
- let question = Column::new()
- .padding(20)
- .spacing(10)
- .push(Text::new("Iced is written in...").size(24))
- .push(Language::all().iter().cloned().fold(
- Column::new().padding(10).spacing(20),
- |choices, language| {
- choices.push(Radio::new(
- language,
- language.into(),
- selection,
- StepMessage::LanguageSelected,
- ))
- },
- ));
-
- Self::container("Radio button")
- .push(Text::new(
- "A radio button is normally used to represent a choice... \
- Surprise test!",
- ))
- .push(question)
- .push(Text::new(
- "Iced works very well with iterators! The list above is \
- basically created by folding a column over the different \
- choices, creating a radio button for each one of them!",
- ))
- }
-
- fn image(
- width: u16,
- slider: &'a mut slider::State,
- ) -> Column<'a, StepMessage> {
- Self::container("Image")
- .push(Text::new("An image that tries to keep its aspect ratio."))
- .push(
- Image::new("resources/ferris.png")
- .width(Length::Units(width))
- .align_self(Align::Center),
- )
- .push(Slider::new(
- slider,
- 100.0..=500.0,
- width as f32,
- StepMessage::ImageWidthChanged,
- ))
- .push(
- Text::new(&format!("Width: {} px", width.to_string()))
- .horizontal_alignment(HorizontalAlignment::Center),
- )
- }
-
- fn debugger(debug: bool) -> Column<'a, StepMessage> {
- Self::container("Debugger")
- .push(Text::new(
- "You can ask Iced to visually explain the layouting of the \
- different elements comprising your UI!",
- ))
- .push(Text::new(
- "Give it a shot! Check the following checkbox to be able to \
- see element boundaries.",
- ))
- .push(Checkbox::new(
- debug,
- "Explain layout",
- StepMessage::DebugToggled,
- ))
- .push(Text::new("Feel free to go back and take a look."))
- }
-
- fn end() -> Column<'a, StepMessage> {
- Self::container("You reached the end!")
- .push(Text::new(
- "This tour will be updated as more features are added.",
- ))
- .push(Text::new("Make sure to keep an eye on it!"))
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Language {
- Rust,
- Elm,
- Ruby,
- Haskell,
- C,
- Other,
-}
-
-impl Language {
- fn all() -> [Language; 6] {
- [
- Language::C,
- Language::Elm,
- Language::Ruby,
- Language::Haskell,
- Language::Rust,
- Language::Other,
- ]
- }
-}
-
-impl From<Language> for &str {
- fn from(language: Language) -> &'static str {
- match language {
- Language::Rust => "Rust",
- Language::Elm => "Elm",
- Language::Ruby => "Ruby",
- Language::Haskell => "Haskell",
- Language::C => "C",
- Language::Other => "Other",
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum Layout {
- Row,
- Column,
-}
diff --git a/examples/tour/src/web.rs b/examples/tour/src/web.rs
deleted file mode 100644
index a0a3060f..00000000
--- a/examples/tour/src/web.rs
+++ /dev/null
@@ -1,33 +0,0 @@
-use futures::Future;
-use iced_web::UserInterface;
-use wasm_bindgen::prelude::*;
-
-use crate::tour::{self, Tour};
-
-#[wasm_bindgen(start)]
-pub fn run() {
- console_error_panic_hook::set_once();
- console_log::init_with_level(log::Level::Trace)
- .expect("Initialize logging");
-
- let tour = Tour::new();
-
- tour.run();
-}
-
-impl iced_web::UserInterface for Tour {
- type Message = tour::Message;
-
- fn update(
- &mut self,
- message: tour::Message,
- ) -> Option<Box<dyn Future<Output = tour::Message>>> {
- self.update(message);
-
- None
- }
-
- fn view(&mut self) -> iced_web::Element<tour::Message> {
- self.view()
- }
-}
diff --git a/examples/tour/src/widget.rs b/examples/tour/src/widget.rs
deleted file mode 100644
index 9c2c4d5b..00000000
--- a/examples/tour/src/widget.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-#[cfg(target_arch = "wasm32")]
-pub use iced_web::*;
-
-#[cfg(not(target_arch = "wasm32"))]
-pub use crate::iced_ggez::*;