diff options
Diffstat (limited to 'examples/tour')
22 files changed, 0 insertions, 1867 deletions
diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml deleted file mode 100644 index 2c79cbf7..00000000 --- a/examples/tour/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "iced_tour" -version = "0.0.0" -authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] -description = "Tour example for Iced" -license = "MIT" -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" diff --git a/examples/tour/README.md b/examples/tour/README.md deleted file mode 100644 index 7ef1a212..00000000 --- a/examples/tour/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# Tour - -A simple UI tour showcasing different widgets that can be built using Iced. It -also shows how the library can be integrated into an existing system. - -The example can run both on native and web platforms, using the same GUI code! - -The native renderer of the example is built on top of [`ggez`], a game library -for Rust. Currently, it is using a [personal fork] to [add a `FontCache` type] -and [fix some issues with HiDPI]. - -The web version uses `iced_web` directly. This crate is still a work in -progress. In particular, the styling of elements is not finished yet -(text color, alignment, sizing, etc). - -The implementation consists of different modules: - - __[`tour`]__ contains the actual cross-platform GUI code: __state__, - __messages__, __update logic__ and __view logic__. - - __[`iced_ggez`]__ implements a simple renderer for each of the used widgets - on top of the graphics module of [`ggez`]. - - __[`widget`]__ conditionally re-exposes the correct platform widgets based - on the target architecture. - - __[`main`]__ integrates Iced with [`ggez`] and connects the [`tour`] with - the native [`renderer`]. - - __[`lib`]__ exposes the [`tour`] types and conditionally implements the - WebAssembly entrypoint in the [`web`] module. - -The conditional compilation awkwardness from targetting both native and web -platforms should be handled seamlessly by the `iced` crate in the near future! - -If you want to run it as a native app: - -``` -cd examples/tour -cargo run -``` - -If you want to run it on web, you will need [`wasm-pack`]: - -``` -cd examples/tour -wasm-pack build --target web -``` - -Then, simply serve the directory with any HTTP server. For instance: - -``` -python3 -m http.server -``` - -[![Tour - Iced][gui_gif]][gui_gfycat] - -[`ggez`]: https://github.com/ggez/ggez -[`tour`]: src/tour.rs -[`iced_ggez`]: src/iced_ggez -[`renderer`]: src/iced_ggez/renderer -[`widget`]: src/widget.rs -[`main`]: src/main.rs -[`lib`]: src/lib.rs -[`web`]: src/web.rs -[`wasm-pack`]: https://rustwasm.github.io/wasm-pack/installer/ -[personal fork]: https://github.com/hecrj/ggez -[add a `FontCache` type]: https://github.com/ggez/ggez/pull/679 -[fix some issues with HiDPI]: https://github.com/hecrj/ggez/commit/dfe2fd2423c51a6daf42c75f66dfaeaacd439fb1 -[gui_gif]: https://thumbs.gfycat.com/VeneratedSourAurochs-small.gif -[gui_gfycat]: https://gfycat.com/veneratedsouraurochs diff --git a/examples/tour/index.html b/examples/tour/index.html deleted file mode 100644 index b17ac4a2..00000000 --- a/examples/tour/index.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta http-equiv="Content-type" content="text/html; charset=utf-8"/> - <title>Tour - Iced</title> - </head> - <body> - <script type="module"> - import init from "./pkg/iced_tour.js"; - init("./pkg/iced_tour_bg.wasm"); - </script> - </body> -</html> diff --git a/examples/tour/resources/Roboto-LICENSE b/examples/tour/resources/Roboto-LICENSE deleted file mode 100644 index 75b52484..00000000 --- a/examples/tour/resources/Roboto-LICENSE +++ /dev/null @@ -1,202 +0,0 @@ -
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/examples/tour/resources/Roboto-Regular.ttf b/examples/tour/resources/Roboto-Regular.ttf Binary files differdeleted file mode 100644 index 2b6392ff..00000000 --- a/examples/tour/resources/Roboto-Regular.ttf +++ /dev/null diff --git a/examples/tour/resources/ferris.png b/examples/tour/resources/ferris.png Binary files differdeleted file mode 100644 index ebce1a14..00000000 --- a/examples/tour/resources/ferris.png +++ /dev/null diff --git a/examples/tour/resources/ui.png b/examples/tour/resources/ui.png Binary files differdeleted file mode 100644 index 4fd3beb3..00000000 --- a/examples/tour/resources/ui.png +++ /dev/null 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 deleted file mode 100644 index a34d3298..00000000 --- a/examples/tour/src/main.rs +++ /dev/null @@ -1,191 +0,0 @@ -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) -} - -struct Game { - spritesheet: graphics::Image, - font: graphics::Font, - images: iced_ggez::ImageCache, - tour: Tour, - - events: Vec<iced_native::Event>, - cache: Option<iced_native::Cache>, -} - -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(), - - events: Vec::new(), - cache: Some(iced_native::Cache::default()), - }) - } -} - -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, - ); - - let mut ui = iced_native::UserInterface::build( - content, - self.cache.take().unwrap(), - renderer, - ); - - let messages = ui.update(self.events.drain(..)); - let cursor = ui.draw(renderer); - - self.cache = Some(ui.into_cache()); - - renderer.flush(); - - (messages, cursor) - }; - - for message in messages { - self.tour.update(message); - } - - let cursor_type = into_cursor_type(cursor); - - if mouse::cursor_type(context) != cursor_type { - mouse::set_cursor_type(context, cursor_type); - } - - graphics::present(context)?; - Ok(()) - } -} - -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, - } -} diff --git a/examples/tour/src/tour.rs b/examples/tour/src/tour.rs deleted file mode 100644 index 60a251d2..00000000 --- a/examples/tour/src/tour.rs +++ /dev/null @@ -1,567 +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 is 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. On native \ - platforms, this example runs on a fairly simple renderer \ - built on top of ggez, another game library.", - )) - .push(Text::new( - "Additionally, this tour can also run on WebAssembly thanks \ - to dodrio, an experimental VDOM library for Rust.", - )) - .push(Text::new( - "You will need to interact with the UI in order to reach the \ - end of this tour!", - )) - } - - 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::*; |