From b9a9576207ddfc7afd89da30b7cfc7ca0d7e335c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 6 Jan 2023 23:29:38 +0100 Subject: Remove `iced_glow`, `glyph-brush`, and `wgpu_glyph` dependencies --- examples/integration/.gitignore | 2 + examples/integration/Cargo.toml | 22 ++ examples/integration/README.md | 36 +++ examples/integration/index.html | 21 ++ examples/integration/src/controls.rs | 112 ++++++++++ examples/integration/src/main.rs | 289 +++++++++++++++++++++++++ examples/integration/src/scene.rs | 102 +++++++++ examples/integration/src/shader/frag.wgsl | 4 + examples/integration/src/shader/vert.wgsl | 6 + examples/integration_opengl/Cargo.toml | 12 - examples/integration_opengl/README.md | 16 -- examples/integration_opengl/src/controls.rs | 101 --------- examples/integration_opengl/src/main.rs | 187 ---------------- examples/integration_opengl/src/scene.rs | 102 --------- examples/integration_wgpu/.gitignore | 2 - examples/integration_wgpu/Cargo.toml | 22 -- examples/integration_wgpu/README.md | 36 --- examples/integration_wgpu/index.html | 21 -- examples/integration_wgpu/src/controls.rs | 112 ---------- examples/integration_wgpu/src/main.rs | 289 ------------------------- examples/integration_wgpu/src/scene.rs | 102 --------- examples/integration_wgpu/src/shader/frag.wgsl | 4 - examples/integration_wgpu/src/shader/vert.wgsl | 6 - 23 files changed, 594 insertions(+), 1012 deletions(-) create mode 100644 examples/integration/.gitignore create mode 100644 examples/integration/Cargo.toml create mode 100644 examples/integration/README.md create mode 100644 examples/integration/index.html create mode 100644 examples/integration/src/controls.rs create mode 100644 examples/integration/src/main.rs create mode 100644 examples/integration/src/scene.rs create mode 100644 examples/integration/src/shader/frag.wgsl create mode 100644 examples/integration/src/shader/vert.wgsl delete mode 100644 examples/integration_opengl/Cargo.toml delete mode 100644 examples/integration_opengl/README.md delete mode 100644 examples/integration_opengl/src/controls.rs delete mode 100644 examples/integration_opengl/src/main.rs delete mode 100644 examples/integration_opengl/src/scene.rs delete mode 100644 examples/integration_wgpu/.gitignore delete mode 100644 examples/integration_wgpu/Cargo.toml delete mode 100644 examples/integration_wgpu/README.md delete mode 100644 examples/integration_wgpu/index.html delete mode 100644 examples/integration_wgpu/src/controls.rs delete mode 100644 examples/integration_wgpu/src/main.rs delete mode 100644 examples/integration_wgpu/src/scene.rs delete mode 100644 examples/integration_wgpu/src/shader/frag.wgsl delete mode 100644 examples/integration_wgpu/src/shader/vert.wgsl (limited to 'examples') diff --git a/examples/integration/.gitignore b/examples/integration/.gitignore new file mode 100644 index 00000000..e188dc28 --- /dev/null +++ b/examples/integration/.gitignore @@ -0,0 +1,2 @@ +*.wasm +*.js diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml new file mode 100644 index 00000000..200306aa --- /dev/null +++ b/examples/integration/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "integration" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2021" +publish = false + +[dependencies] +iced_winit = { path = "../../winit" } +iced_wgpu = { path = "../../wgpu", features = ["webgl"] } +env_logger = "0.8" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_error_panic_hook = "0.1.7" +console_log = "0.2.0" +log = "0.4" +wasm-bindgen = "0.2" +web-sys = { version = "0.3", features = ["Element", "HtmlCanvasElement", "Window", "Document"] } +# This dependency a little bit quirky, it is deep in the tree and without `js` feature it +# refuses to work with `wasm32-unknown-unknown target`. Unfortunately, we need this patch +# to make it work +getrandom = { version = "0.2", features = ["js"] } diff --git a/examples/integration/README.md b/examples/integration/README.md new file mode 100644 index 00000000..ece9ba1e --- /dev/null +++ b/examples/integration/README.md @@ -0,0 +1,36 @@ +## `wgpu` integration + +A demonstration of how to integrate Iced in an existing [`wgpu`] application. + +The __[`main`]__ file contains all the code of the example. + +
+ + + +
+ +You can run it with `cargo run`: +``` +cargo run --package integration_wgpu +``` + +### How to run this example with WebGL backend +NOTE: Currently, WebGL backend is is still experimental, so expect bugs. + +```sh +# 0. Install prerequisites +cargo install wasm-bindgen-cli https +# 1. cd to the current folder +# 2. Compile wasm module +cargo build -p integration_wgpu --target wasm32-unknown-unknown +# 3. Invoke wasm-bindgen +wasm-bindgen ../../target/wasm32-unknown-unknown/debug/integration_wgpu.wasm --out-dir . --target web --no-typescript +# 4. run http server +http +# 5. Open 127.0.0.1:8000 in browser +``` + + +[`main`]: src/main.rs +[`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration/index.html b/examples/integration/index.html new file mode 100644 index 00000000..920bc4a0 --- /dev/null +++ b/examples/integration/index.html @@ -0,0 +1,21 @@ + + + + + Iced - wgpu + WebGL integration + + +

integration_wgpu

+ + + + + diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs new file mode 100644 index 00000000..533cb6e2 --- /dev/null +++ b/examples/integration/src/controls.rs @@ -0,0 +1,112 @@ +use iced_wgpu::Renderer; +use iced_winit::widget::{slider, text_input, Column, Row, Text}; +use iced_winit::{Alignment, Color, Command, Element, Length, Program}; + +pub struct Controls { + background_color: Color, + text: String, +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), + TextChanged(String), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + text: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + + fn update(&mut self, message: Message) -> Command { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + Message::TextChanged(text) => { + self.text = text; + } + } + + Command::none() + } + + fn view(&self) -> Element { + let background_color = self.background_color; + let text = &self.text; + + let sliders = Row::new() + .width(500) + .spacing(20) + .push( + slider(0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + slider(0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + slider(0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }) + .step(0.01), + ); + + Row::new() + .width(Length::Fill) + .height(Length::Fill) + .align_items(Alignment::End) + .push( + Column::new() + .width(Length::Fill) + .align_items(Alignment::End) + .push( + Column::new() + .padding(10) + .spacing(10) + .push( + Text::new("Background color") + .style(Color::WHITE), + ) + .push(sliders) + .push( + Text::new(format!("{background_color:?}")) + .size(14) + .style(Color::WHITE), + ) + .push(text_input( + "Placeholder", + text, + Message::TextChanged, + )), + ), + ) + .into() + } +} diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs new file mode 100644 index 00000000..2a56b6fa --- /dev/null +++ b/examples/integration/src/main.rs @@ -0,0 +1,289 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; +use iced_winit::{ + conversion, futures, program, renderer, winit, Clipboard, Color, Debug, + Size, +}; + +use winit::{ + dpi::PhysicalPosition, + event::{Event, ModifiersState, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, +}; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsCast; +#[cfg(target_arch = "wasm32")] +use web_sys::HtmlCanvasElement; +#[cfg(target_arch = "wasm32")] +use winit::platform::web::WindowBuilderExtWebSys; + +pub fn main() { + #[cfg(target_arch = "wasm32")] + let canvas_element = { + console_log::init_with_level(log::Level::Debug) + .expect("could not initialize logger"); + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.get_element_by_id("iced_canvas")) + .and_then(|element| element.dyn_into::().ok()) + .expect("Canvas with id `iced_canvas` is missing") + }; + #[cfg(not(target_arch = "wasm32"))] + env_logger::init(); + + // Initialize winit + let event_loop = EventLoop::new(); + + #[cfg(target_arch = "wasm32")] + let window = winit::window::WindowBuilder::new() + .with_canvas(Some(canvas_element)) + .build(&event_loop) + .expect("Failed to build winit window"); + + #[cfg(not(target_arch = "wasm32"))] + let window = winit::window::Window::new(&event_loop).unwrap(); + + let physical_size = window.inner_size(); + let mut viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + window.scale_factor(), + ); + let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut modifiers = ModifiersState::default(); + let mut clipboard = Clipboard::connect(&window); + + // Initialize wgpu + + #[cfg(target_arch = "wasm32")] + let default_backend = wgpu::Backends::GL; + #[cfg(not(target_arch = "wasm32"))] + let default_backend = wgpu::Backends::PRIMARY; + + let backend = + wgpu::util::backend_bits_from_env().unwrap_or(default_backend); + + let instance = wgpu::Instance::new(backend); + let surface = unsafe { instance.create_surface(&window) }; + + let (format, (device, queue)) = futures::executor::block_on(async { + let adapter = wgpu::util::initialize_adapter_from_env_or_default( + &instance, + backend, + Some(&surface), + ) + .await + .expect("No suitable GPU adapters found on the system!"); + + let adapter_features = adapter.features(); + + #[cfg(target_arch = "wasm32")] + let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() + .using_resolution(adapter.limits()); + + #[cfg(not(target_arch = "wasm32"))] + let needed_limits = wgpu::Limits::default(); + + ( + surface + .get_supported_formats(&adapter) + .first() + .copied() + .expect("Get preferred format"), + adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: adapter_features & wgpu::Features::default(), + limits: needed_limits, + }, + None, + ) + .await + .expect("Request device"), + ) + }); + + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format, + width: physical_size.width, + height: physical_size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + }, + ); + + let mut resized = false; + + // Initialize staging belt + let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); + + // Initialize scene and GUI controls + let scene = Scene::new(&device, format); + let controls = Controls::new(); + + // Initialize iced + let mut debug = Debug::new(); + let mut renderer = + Renderer::new(Backend::new(&device, Settings::default(), format)); + + let mut state = program::State::new( + controls, + viewport.logical_size(), + &mut renderer, + &mut debug, + ); + + // Run event loop + event_loop.run(move |event, _, control_flow| { + // You should change this if you want to render continuosly + *control_flow = ControlFlow::Wait; + + match event { + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::CursorMoved { position, .. } => { + cursor_position = position; + } + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } + WindowEvent::Resized(_) => { + resized = true; + } + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + _ => {} + } + + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + &event, + window.scale_factor(), + modifiers, + ) { + state.queue_event(event); + } + } + Event::MainEventsCleared => { + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + conversion::cursor_position( + cursor_position, + viewport.scale_factor(), + ), + &mut renderer, + &iced_wgpu::Theme::Dark, + &renderer::Style { text_color: Color::WHITE }, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + window.request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = window.inner_size(); + + viewport = Viewport::with_physical_size( + Size::new(size.width, size.height), + window.scale_factor(), + ); + + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto + }, + ); + + resized = false; + } + + match surface.get_current_texture() { + Ok(frame) => { + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { label: None }, + ); + + let program = state.program(); + + let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default()); + + { + // We clear the frame + let mut render_pass = scene.clear( + &view, + &mut encoder, + program.background_color(), + ); + + // Draw the scene + scene.draw(&mut render_pass); + } + + // And then iced on top + renderer.with_primitives(|backend, primitive| { + backend.present( + &device, + &mut staging_belt, + &mut encoder, + &view, + primitive, + &viewport, + &debug.overlay(), + ); + }); + + // Then we submit the work + staging_belt.finish(); + queue.submit(Some(encoder.finish())); + frame.present(); + + // Update the mouse cursor + window.set_cursor_icon( + iced_winit::conversion::mouse_interaction( + state.mouse_interaction(), + ), + ); + + // And recall staging buffers + staging_belt.recall(); + + } + Err(error) => match error { + wgpu::SurfaceError::OutOfMemory => { + panic!("Swapchain error: {error}. Rendering cannot continue.") + } + _ => { + // Try rendering again next frame. + window.request_redraw(); + } + }, + } + } + _ => {} + } + }) +} diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs new file mode 100644 index 00000000..3e41fbda --- /dev/null +++ b/examples/integration/src/scene.rs @@ -0,0 +1,102 @@ +use iced_wgpu::wgpu; +use iced_winit::Color; + +pub struct Scene { + pipeline: wgpu::RenderPipeline, +} + +impl Scene { + pub fn new( + device: &wgpu::Device, + texture_format: wgpu::TextureFormat, + ) -> Scene { + let pipeline = build_pipeline(device, texture_format); + + Scene { pipeline } + } + + pub fn clear<'a>( + &self, + target: &'a wgpu::TextureView, + encoder: &'a mut wgpu::CommandEncoder, + background_color: Color, + ) -> wgpu::RenderPass<'a> { + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: target, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear({ + let [r, g, b, a] = background_color.into_linear(); + + wgpu::Color { + r: r as f64, + g: g as f64, + b: b as f64, + a: a as f64, + } + }), + store: true, + }, + })], + depth_stencil_attachment: None, + }) + } + + pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { + render_pass.set_pipeline(&self.pipeline); + render_pass.draw(0..3, 0..1); + } +} + +fn build_pipeline( + device: &wgpu::Device, + texture_format: wgpu::TextureFormat, +) -> wgpu::RenderPipeline { + let (vs_module, fs_module) = ( + device.create_shader_module(wgpu::include_wgsl!("shader/vert.wgsl")), + device.create_shader_module(wgpu::include_wgsl!("shader/frag.wgsl")), + ); + + let pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + push_constant_ranges: &[], + bind_group_layouts: &[], + }); + + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &vs_module, + entry_point: "main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &fs_module, + entry_point: "main", + targets: &[Some(wgpu::ColorTargetState { + format: texture_format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::REPLACE, + alpha: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Ccw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }) +} diff --git a/examples/integration/src/shader/frag.wgsl b/examples/integration/src/shader/frag.wgsl new file mode 100644 index 00000000..cf27bb56 --- /dev/null +++ b/examples/integration/src/shader/frag.wgsl @@ -0,0 +1,4 @@ +@fragment +fn main() -> @location(0) vec4 { + return vec4(1.0, 0.0, 0.0, 1.0); +} diff --git a/examples/integration/src/shader/vert.wgsl b/examples/integration/src/shader/vert.wgsl new file mode 100644 index 00000000..e353e6ba --- /dev/null +++ b/examples/integration/src/shader/vert.wgsl @@ -0,0 +1,6 @@ +@vertex +fn main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { + let x = f32(1 - i32(in_vertex_index)) * 0.5; + let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5; + return vec4(x, y, 0.0, 1.0); +} diff --git a/examples/integration_opengl/Cargo.toml b/examples/integration_opengl/Cargo.toml deleted file mode 100644 index 6dac999c..00000000 --- a/examples/integration_opengl/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "integration_opengl" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez "] -edition = "2021" -publish = false - -[dependencies] -iced_glutin = { path = "../../glutin" } -iced_glow = { path = "../../glow" } -iced_winit = { path = "../../winit" } -env_logger = "0.8" diff --git a/examples/integration_opengl/README.md b/examples/integration_opengl/README.md deleted file mode 100644 index b7c2c074..00000000 --- a/examples/integration_opengl/README.md +++ /dev/null @@ -1,16 +0,0 @@ -## OpenGL integration - -A demonstration of how to integrate Iced in an existing graphical OpenGL application. - -The __[`main`]__ file contains all the code of the example. - -
- image host -
- -You can run it with `cargo run`: -``` -cargo run --package integration_opengl -``` - -[`main`]: src/main.rs diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs deleted file mode 100644 index c3648f44..00000000 --- a/examples/integration_opengl/src/controls.rs +++ /dev/null @@ -1,101 +0,0 @@ -use iced_glow::Renderer; -use iced_glutin::widget::Slider; -use iced_glutin::widget::{Column, Row, Text}; -use iced_glutin::{Alignment, Color, Command, Element, Length, Program}; - -pub struct Controls { - background_color: Color, -} - -#[derive(Debug, Clone)] -pub enum Message { - BackgroundColorChanged(Color), -} - -impl Controls { - pub fn new() -> Controls { - Controls { - background_color: Color::BLACK, - } - } - - pub fn background_color(&self) -> Color { - self.background_color - } -} - -impl Program for Controls { - type Renderer = Renderer; - type Message = Message; - - fn update(&mut self, message: Message) -> Command { - match message { - Message::BackgroundColorChanged(color) => { - self.background_color = color; - } - } - - Command::none() - } - - fn view(&self) -> Element { - let background_color = self.background_color; - - let sliders = Row::new() - .width(500) - .spacing(20) - .push( - Slider::new(0.0..=1.0, background_color.r, move |r| { - Message::BackgroundColorChanged(Color { - r, - ..background_color - }) - }) - .step(0.01), - ) - .push( - Slider::new(0.0..=1.0, background_color.g, move |g| { - Message::BackgroundColorChanged(Color { - g, - ..background_color - }) - }) - .step(0.01), - ) - .push( - Slider::new(0.0..=1.0, background_color.b, move |b| { - Message::BackgroundColorChanged(Color { - b, - ..background_color - }) - }) - .step(0.01), - ); - - Row::new() - .width(Length::Fill) - .height(Length::Fill) - .align_items(Alignment::End) - .push( - Column::new() - .width(Length::Fill) - .align_items(Alignment::End) - .push( - Column::new() - .padding(10) - .spacing(10) - .push( - Text::new("Background color") - .style(Color::WHITE), - ) - .push(sliders) - .push( - Text::new(format!("{background_color:?}")) - .size(14) - .style(Color::WHITE), - ), - ), - ) - .into() - } -} diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs deleted file mode 100644 index f161c8a0..00000000 --- a/examples/integration_opengl/src/main.rs +++ /dev/null @@ -1,187 +0,0 @@ -mod controls; -mod scene; - -use controls::Controls; -use scene::Scene; - -use glow::*; -use glutin::dpi::PhysicalPosition; -use glutin::event::{Event, ModifiersState, WindowEvent}; -use glutin::event_loop::ControlFlow; -use iced_glow::glow; -use iced_glow::{Backend, Renderer, Settings, Viewport}; -use iced_glutin::conversion; -use iced_glutin::glutin; -use iced_glutin::renderer; -use iced_glutin::{program, Clipboard, Color, Debug, Size}; - -pub fn main() { - env_logger::init(); - let (gl, event_loop, windowed_context, shader_version) = { - let el = glutin::event_loop::EventLoop::new(); - - let wb = glutin::window::WindowBuilder::new() - .with_title("OpenGL integration example") - .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); - - let windowed_context = glutin::ContextBuilder::new() - .with_vsync(true) - .build_windowed(wb, &el) - .unwrap(); - - unsafe { - let windowed_context = windowed_context.make_current().unwrap(); - - let gl = glow::Context::from_loader_function(|s| { - windowed_context.get_proc_address(s) as *const _ - }); - - // Enable auto-conversion from/to sRGB - gl.enable(glow::FRAMEBUFFER_SRGB); - - // Enable alpha blending - gl.enable(glow::BLEND); - gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); - - // Disable multisampling by default - gl.disable(glow::MULTISAMPLE); - - (gl, el, windowed_context, "#version 410") - } - }; - - let physical_size = windowed_context.window().inner_size(); - let mut viewport = Viewport::with_physical_size( - Size::new(physical_size.width, physical_size.height), - windowed_context.window().scale_factor(), - ); - - let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); - let mut modifiers = ModifiersState::default(); - let mut clipboard = Clipboard::connect(windowed_context.window()); - - let mut renderer = Renderer::new(Backend::new(&gl, Settings::default())); - - let mut debug = Debug::new(); - - let controls = Controls::new(); - let mut state = program::State::new( - controls, - viewport.logical_size(), - &mut renderer, - &mut debug, - ); - let mut resized = false; - - let scene = Scene::new(&gl, shader_version); - - event_loop.run(move |event, _, control_flow| { - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CursorMoved { position, .. } => { - cursor_position = position; - } - WindowEvent::ModifiersChanged(new_modifiers) => { - modifiers = new_modifiers; - } - WindowEvent::Resized(physical_size) => { - viewport = Viewport::with_physical_size( - Size::new( - physical_size.width, - physical_size.height, - ), - windowed_context.window().scale_factor(), - ); - - resized = true; - } - WindowEvent::CloseRequested => { - scene.cleanup(&gl); - *control_flow = ControlFlow::Exit - } - _ => (), - } - - // Map window event to iced event - if let Some(event) = iced_winit::conversion::window_event( - &event, - windowed_context.window().scale_factor(), - modifiers, - ) { - state.queue_event(event); - } - } - Event::MainEventsCleared => { - // If there are events pending - if !state.is_queue_empty() { - // We update iced - let _ = state.update( - viewport.logical_size(), - conversion::cursor_position( - cursor_position, - viewport.scale_factor(), - ), - &mut renderer, - &iced_glow::Theme::Dark, - &renderer::Style { - text_color: Color::WHITE, - }, - &mut clipboard, - &mut debug, - ); - - // and request a redraw - windowed_context.window().request_redraw(); - } - } - Event::RedrawRequested(_) => { - if resized { - let size = windowed_context.window().inner_size(); - - unsafe { - gl.viewport( - 0, - 0, - size.width as i32, - size.height as i32, - ); - } - - resized = false; - } - - let program = state.program(); - { - // We clear the frame - scene.clear(&gl, program.background_color()); - - // Draw the scene - scene.draw(&gl); - } - - // And then iced on top - renderer.with_primitives(|backend, primitive| { - backend.present( - &gl, - primitive, - &viewport, - &debug.overlay(), - ); - }); - - // Update the mouse cursor - windowed_context.window().set_cursor_icon( - iced_winit::conversion::mouse_interaction( - state.mouse_interaction(), - ), - ); - - windowed_context.swap_buffers().unwrap(); - } - _ => (), - } - }); -} diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs deleted file mode 100644 index c1d05b65..00000000 --- a/examples/integration_opengl/src/scene.rs +++ /dev/null @@ -1,102 +0,0 @@ -use glow::*; -use iced_glow::glow; -use iced_glow::Color; - -pub struct Scene { - program: glow::Program, - vertex_array: glow::VertexArray, -} - -impl Scene { - pub fn new(gl: &glow::Context, shader_version: &str) -> Self { - unsafe { - let vertex_array = gl - .create_vertex_array() - .expect("Cannot create vertex array"); - gl.bind_vertex_array(Some(vertex_array)); - - let program = gl.create_program().expect("Cannot create program"); - - let (vertex_shader_source, fragment_shader_source) = ( - r#"const vec2 verts[3] = vec2[3]( - vec2(0.5f, 1.0f), - vec2(0.0f, 0.0f), - vec2(1.0f, 0.0f) - ); - out vec2 vert; - void main() { - vert = verts[gl_VertexID]; - gl_Position = vec4(vert - 0.5, 0.0, 1.0); - }"#, - r#"precision highp float; - in vec2 vert; - out vec4 color; - void main() { - color = vec4(vert, 0.5, 1.0); - }"#, - ); - - let shader_sources = [ - (glow::VERTEX_SHADER, vertex_shader_source), - (glow::FRAGMENT_SHADER, fragment_shader_source), - ]; - - let mut shaders = Vec::with_capacity(shader_sources.len()); - - for (shader_type, shader_source) in shader_sources.iter() { - let shader = gl - .create_shader(*shader_type) - .expect("Cannot create shader"); - gl.shader_source( - shader, - &format!("{shader_version}\n{shader_source}"), - ); - gl.compile_shader(shader); - if !gl.get_shader_compile_status(shader) { - panic!("{}", gl.get_shader_info_log(shader)); - } - gl.attach_shader(program, shader); - shaders.push(shader); - } - - gl.link_program(program); - if !gl.get_program_link_status(program) { - panic!("{}", gl.get_program_info_log(program)); - } - - for shader in shaders { - gl.detach_shader(program, shader); - gl.delete_shader(shader); - } - - gl.use_program(Some(program)); - Self { - program, - vertex_array, - } - } - } - - pub fn clear(&self, gl: &glow::Context, background_color: Color) { - let [r, g, b, a] = background_color.into_linear(); - unsafe { - gl.clear_color(r, g, b, a); - gl.clear(glow::COLOR_BUFFER_BIT); - } - } - - pub fn draw(&self, gl: &glow::Context) { - unsafe { - gl.bind_vertex_array(Some(self.vertex_array)); - gl.use_program(Some(self.program)); - gl.draw_arrays(glow::TRIANGLES, 0, 3); - } - } - - pub fn cleanup(&self, gl: &glow::Context) { - unsafe { - gl.delete_program(self.program); - gl.delete_vertex_array(self.vertex_array); - } - } -} diff --git a/examples/integration_wgpu/.gitignore b/examples/integration_wgpu/.gitignore deleted file mode 100644 index e188dc28..00000000 --- a/examples/integration_wgpu/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -*.wasm -*.js diff --git a/examples/integration_wgpu/Cargo.toml b/examples/integration_wgpu/Cargo.toml deleted file mode 100644 index eaa1df7e..00000000 --- a/examples/integration_wgpu/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "integration_wgpu" -version = "0.1.0" -authors = ["Héctor Ramón Jiménez "] -edition = "2021" -publish = false - -[dependencies] -iced_winit = { path = "../../winit" } -iced_wgpu = { path = "../../wgpu", features = ["webgl"] } -env_logger = "0.8" - -[target.'cfg(target_arch = "wasm32")'.dependencies] -console_error_panic_hook = "0.1.7" -console_log = "0.2.0" -log = "0.4" -wasm-bindgen = "0.2" -web-sys = { version = "0.3", features = ["Element", "HtmlCanvasElement", "Window", "Document"] } -# This dependency a little bit quirky, it is deep in the tree and without `js` feature it -# refuses to work with `wasm32-unknown-unknown target`. Unfortunately, we need this patch -# to make it work -getrandom = { version = "0.2", features = ["js"] } diff --git a/examples/integration_wgpu/README.md b/examples/integration_wgpu/README.md deleted file mode 100644 index ece9ba1e..00000000 --- a/examples/integration_wgpu/README.md +++ /dev/null @@ -1,36 +0,0 @@ -## `wgpu` integration - -A demonstration of how to integrate Iced in an existing [`wgpu`] application. - -The __[`main`]__ file contains all the code of the example. - - - -You can run it with `cargo run`: -``` -cargo run --package integration_wgpu -``` - -### How to run this example with WebGL backend -NOTE: Currently, WebGL backend is is still experimental, so expect bugs. - -```sh -# 0. Install prerequisites -cargo install wasm-bindgen-cli https -# 1. cd to the current folder -# 2. Compile wasm module -cargo build -p integration_wgpu --target wasm32-unknown-unknown -# 3. Invoke wasm-bindgen -wasm-bindgen ../../target/wasm32-unknown-unknown/debug/integration_wgpu.wasm --out-dir . --target web --no-typescript -# 4. run http server -http -# 5. Open 127.0.0.1:8000 in browser -``` - - -[`main`]: src/main.rs -[`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration_wgpu/index.html b/examples/integration_wgpu/index.html deleted file mode 100644 index 461e67a4..00000000 --- a/examples/integration_wgpu/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - Iced - wgpu + WebGL integration - - -

integration_wgpu

- - - - - diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs deleted file mode 100644 index 533cb6e2..00000000 --- a/examples/integration_wgpu/src/controls.rs +++ /dev/null @@ -1,112 +0,0 @@ -use iced_wgpu::Renderer; -use iced_winit::widget::{slider, text_input, Column, Row, Text}; -use iced_winit::{Alignment, Color, Command, Element, Length, Program}; - -pub struct Controls { - background_color: Color, - text: String, -} - -#[derive(Debug, Clone)] -pub enum Message { - BackgroundColorChanged(Color), - TextChanged(String), -} - -impl Controls { - pub fn new() -> Controls { - Controls { - background_color: Color::BLACK, - text: Default::default(), - } - } - - pub fn background_color(&self) -> Color { - self.background_color - } -} - -impl Program for Controls { - type Renderer = Renderer; - type Message = Message; - - fn update(&mut self, message: Message) -> Command { - match message { - Message::BackgroundColorChanged(color) => { - self.background_color = color; - } - Message::TextChanged(text) => { - self.text = text; - } - } - - Command::none() - } - - fn view(&self) -> Element { - let background_color = self.background_color; - let text = &self.text; - - let sliders = Row::new() - .width(500) - .spacing(20) - .push( - slider(0.0..=1.0, background_color.r, move |r| { - Message::BackgroundColorChanged(Color { - r, - ..background_color - }) - }) - .step(0.01), - ) - .push( - slider(0.0..=1.0, background_color.g, move |g| { - Message::BackgroundColorChanged(Color { - g, - ..background_color - }) - }) - .step(0.01), - ) - .push( - slider(0.0..=1.0, background_color.b, move |b| { - Message::BackgroundColorChanged(Color { - b, - ..background_color - }) - }) - .step(0.01), - ); - - Row::new() - .width(Length::Fill) - .height(Length::Fill) - .align_items(Alignment::End) - .push( - Column::new() - .width(Length::Fill) - .align_items(Alignment::End) - .push( - Column::new() - .padding(10) - .spacing(10) - .push( - Text::new("Background color") - .style(Color::WHITE), - ) - .push(sliders) - .push( - Text::new(format!("{background_color:?}")) - .size(14) - .style(Color::WHITE), - ) - .push(text_input( - "Placeholder", - text, - Message::TextChanged, - )), - ), - ) - .into() - } -} diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs deleted file mode 100644 index 2a56b6fa..00000000 --- a/examples/integration_wgpu/src/main.rs +++ /dev/null @@ -1,289 +0,0 @@ -mod controls; -mod scene; - -use controls::Controls; -use scene::Scene; - -use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; -use iced_winit::{ - conversion, futures, program, renderer, winit, Clipboard, Color, Debug, - Size, -}; - -use winit::{ - dpi::PhysicalPosition, - event::{Event, ModifiersState, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, -}; - -#[cfg(target_arch = "wasm32")] -use wasm_bindgen::JsCast; -#[cfg(target_arch = "wasm32")] -use web_sys::HtmlCanvasElement; -#[cfg(target_arch = "wasm32")] -use winit::platform::web::WindowBuilderExtWebSys; - -pub fn main() { - #[cfg(target_arch = "wasm32")] - let canvas_element = { - console_log::init_with_level(log::Level::Debug) - .expect("could not initialize logger"); - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok()) - .expect("Canvas with id `iced_canvas` is missing") - }; - #[cfg(not(target_arch = "wasm32"))] - env_logger::init(); - - // Initialize winit - let event_loop = EventLoop::new(); - - #[cfg(target_arch = "wasm32")] - let window = winit::window::WindowBuilder::new() - .with_canvas(Some(canvas_element)) - .build(&event_loop) - .expect("Failed to build winit window"); - - #[cfg(not(target_arch = "wasm32"))] - let window = winit::window::Window::new(&event_loop).unwrap(); - - let physical_size = window.inner_size(); - let mut viewport = Viewport::with_physical_size( - Size::new(physical_size.width, physical_size.height), - window.scale_factor(), - ); - let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); - let mut modifiers = ModifiersState::default(); - let mut clipboard = Clipboard::connect(&window); - - // Initialize wgpu - - #[cfg(target_arch = "wasm32")] - let default_backend = wgpu::Backends::GL; - #[cfg(not(target_arch = "wasm32"))] - let default_backend = wgpu::Backends::PRIMARY; - - let backend = - wgpu::util::backend_bits_from_env().unwrap_or(default_backend); - - let instance = wgpu::Instance::new(backend); - let surface = unsafe { instance.create_surface(&window) }; - - let (format, (device, queue)) = futures::executor::block_on(async { - let adapter = wgpu::util::initialize_adapter_from_env_or_default( - &instance, - backend, - Some(&surface), - ) - .await - .expect("No suitable GPU adapters found on the system!"); - - let adapter_features = adapter.features(); - - #[cfg(target_arch = "wasm32")] - let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()); - - #[cfg(not(target_arch = "wasm32"))] - let needed_limits = wgpu::Limits::default(); - - ( - surface - .get_supported_formats(&adapter) - .first() - .copied() - .expect("Get preferred format"), - adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: adapter_features & wgpu::Features::default(), - limits: needed_limits, - }, - None, - ) - .await - .expect("Request device"), - ) - }); - - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format, - width: physical_size.width, - height: physical_size.height, - present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - }, - ); - - let mut resized = false; - - // Initialize staging belt - let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); - - // Initialize scene and GUI controls - let scene = Scene::new(&device, format); - let controls = Controls::new(); - - // Initialize iced - let mut debug = Debug::new(); - let mut renderer = - Renderer::new(Backend::new(&device, Settings::default(), format)); - - let mut state = program::State::new( - controls, - viewport.logical_size(), - &mut renderer, - &mut debug, - ); - - // Run event loop - event_loop.run(move |event, _, control_flow| { - // You should change this if you want to render continuosly - *control_flow = ControlFlow::Wait; - - match event { - Event::WindowEvent { event, .. } => { - match event { - WindowEvent::CursorMoved { position, .. } => { - cursor_position = position; - } - WindowEvent::ModifiersChanged(new_modifiers) => { - modifiers = new_modifiers; - } - WindowEvent::Resized(_) => { - resized = true; - } - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - _ => {} - } - - // Map window event to iced event - if let Some(event) = iced_winit::conversion::window_event( - &event, - window.scale_factor(), - modifiers, - ) { - state.queue_event(event); - } - } - Event::MainEventsCleared => { - // If there are events pending - if !state.is_queue_empty() { - // We update iced - let _ = state.update( - viewport.logical_size(), - conversion::cursor_position( - cursor_position, - viewport.scale_factor(), - ), - &mut renderer, - &iced_wgpu::Theme::Dark, - &renderer::Style { text_color: Color::WHITE }, - &mut clipboard, - &mut debug, - ); - - // and request a redraw - window.request_redraw(); - } - } - Event::RedrawRequested(_) => { - if resized { - let size = window.inner_size(); - - viewport = Viewport::with_physical_size( - Size::new(size.width, size.height), - window.scale_factor(), - ); - - surface.configure( - &device, - &wgpu::SurfaceConfiguration { - format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto - }, - ); - - resized = false; - } - - match surface.get_current_texture() { - Ok(frame) => { - let mut encoder = device.create_command_encoder( - &wgpu::CommandEncoderDescriptor { label: None }, - ); - - let program = state.program(); - - let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default()); - - { - // We clear the frame - let mut render_pass = scene.clear( - &view, - &mut encoder, - program.background_color(), - ); - - // Draw the scene - scene.draw(&mut render_pass); - } - - // And then iced on top - renderer.with_primitives(|backend, primitive| { - backend.present( - &device, - &mut staging_belt, - &mut encoder, - &view, - primitive, - &viewport, - &debug.overlay(), - ); - }); - - // Then we submit the work - staging_belt.finish(); - queue.submit(Some(encoder.finish())); - frame.present(); - - // Update the mouse cursor - window.set_cursor_icon( - iced_winit::conversion::mouse_interaction( - state.mouse_interaction(), - ), - ); - - // And recall staging buffers - staging_belt.recall(); - - } - Err(error) => match error { - wgpu::SurfaceError::OutOfMemory => { - panic!("Swapchain error: {error}. Rendering cannot continue.") - } - _ => { - // Try rendering again next frame. - window.request_redraw(); - } - }, - } - } - _ => {} - } - }) -} diff --git a/examples/integration_wgpu/src/scene.rs b/examples/integration_wgpu/src/scene.rs deleted file mode 100644 index 3e41fbda..00000000 --- a/examples/integration_wgpu/src/scene.rs +++ /dev/null @@ -1,102 +0,0 @@ -use iced_wgpu::wgpu; -use iced_winit::Color; - -pub struct Scene { - pipeline: wgpu::RenderPipeline, -} - -impl Scene { - pub fn new( - device: &wgpu::Device, - texture_format: wgpu::TextureFormat, - ) -> Scene { - let pipeline = build_pipeline(device, texture_format); - - Scene { pipeline } - } - - pub fn clear<'a>( - &self, - target: &'a wgpu::TextureView, - encoder: &'a mut wgpu::CommandEncoder, - background_color: Color, - ) -> wgpu::RenderPass<'a> { - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: target, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear({ - let [r, g, b, a] = background_color.into_linear(); - - wgpu::Color { - r: r as f64, - g: g as f64, - b: b as f64, - a: a as f64, - } - }), - store: true, - }, - })], - depth_stencil_attachment: None, - }) - } - - pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - render_pass.set_pipeline(&self.pipeline); - render_pass.draw(0..3, 0..1); - } -} - -fn build_pipeline( - device: &wgpu::Device, - texture_format: wgpu::TextureFormat, -) -> wgpu::RenderPipeline { - let (vs_module, fs_module) = ( - device.create_shader_module(wgpu::include_wgsl!("shader/vert.wgsl")), - device.create_shader_module(wgpu::include_wgsl!("shader/frag.wgsl")), - ); - - let pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: None, - push_constant_ranges: &[], - bind_group_layouts: &[], - }); - - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: None, - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &vs_module, - entry_point: "main", - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &fs_module, - entry_point: "main", - targets: &[Some(wgpu::ColorTargetState { - format: texture_format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent::REPLACE, - alpha: wgpu::BlendComponent::REPLACE, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - front_face: wgpu::FrontFace::Ccw, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }) -} diff --git a/examples/integration_wgpu/src/shader/frag.wgsl b/examples/integration_wgpu/src/shader/frag.wgsl deleted file mode 100644 index cf27bb56..00000000 --- a/examples/integration_wgpu/src/shader/frag.wgsl +++ /dev/null @@ -1,4 +0,0 @@ -@fragment -fn main() -> @location(0) vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); -} diff --git a/examples/integration_wgpu/src/shader/vert.wgsl b/examples/integration_wgpu/src/shader/vert.wgsl deleted file mode 100644 index e353e6ba..00000000 --- a/examples/integration_wgpu/src/shader/vert.wgsl +++ /dev/null @@ -1,6 +0,0 @@ -@vertex -fn main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { - let x = f32(1 - i32(in_vertex_index)) * 0.5; - let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5; - return vec4(x, y, 0.0, 1.0); -} -- cgit From baf51a8fcffc78e4ca20f7dcbba18ca3655f2840 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 31 Jan 2023 06:29:21 +0100 Subject: Draft `glyphon` implementation of text pipeline for `iced_wgpu` --- examples/integration/src/main.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 2a56b6fa..69f6961e 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -134,8 +134,12 @@ pub fn main() { // Initialize iced let mut debug = Debug::new(); - let mut renderer = - Renderer::new(Backend::new(&device, Settings::default(), format)); + let mut renderer = Renderer::new(Backend::new( + &device, + &queue, + Settings::default(), + format, + )); let mut state = program::State::new( controls, @@ -247,6 +251,7 @@ pub fn main() { renderer.with_primitives(|backend, primitive| { backend.present( &device, + &queue, &mut staging_belt, &mut encoder, &view, -- cgit From b29de28d1f0f608f8029c93d154cfd1b0f8b8cbb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 4 Feb 2023 07:33:33 +0100 Subject: Overhaul `Font` type to allow font family selection --- examples/todos/fonts/icons.ttf | Bin 5596 -> 5732 bytes examples/todos/src/main.rs | 5 +---- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/todos/fonts/icons.ttf b/examples/todos/fonts/icons.ttf index 4498299d..7b65fd36 100644 Binary files a/examples/todos/fonts/icons.ttf and b/examples/todos/fonts/icons.ttf differ diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 6408f09c..5509cf0e 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -466,10 +466,7 @@ fn empty_message(message: &str) -> Element<'_, Message> { } // Fonts -const ICONS: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../../todos/fonts/icons.ttf"), -}; +const ICONS: Font = Font::Name("Iced-Todos-Icons"); fn icon(unicode: char) -> Text<'static> { text(unicode.to_string()) -- cgit From 238154af4ac8dda7f12dd90aa7be106e933bcb30 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 4 Feb 2023 11:12:15 +0100 Subject: Implement `font::load` command in `iced_native` --- examples/todos/src/main.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 5509cf0e..5df4e968 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -1,5 +1,6 @@ use iced::alignment::{self, Alignment}; use iced::event::{self, Event}; +use iced::font::{self, Font}; use iced::keyboard; use iced::subscription; use iced::theme::{self, Theme}; @@ -9,7 +10,7 @@ use iced::widget::{ }; use iced::window; use iced::{Application, Element}; -use iced::{Color, Command, Font, Length, Settings, Subscription}; +use iced::{Color, Command, Length, Settings, Subscription}; use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; @@ -44,6 +45,7 @@ struct State { #[derive(Debug, Clone)] enum Message { Loaded(Result), + FontLoaded(Result<(), font::Error>), Saved(Result<(), SaveError>), InputChanged(String), CreateTask, @@ -61,7 +63,11 @@ impl Application for Todos { fn new(_flags: ()) -> (Todos, Command) { ( Todos::Loading, - Command::perform(SavedState::load(), Message::Loaded), + Command::batch(vec![ + font::load(include_bytes!("../fonts/icons.ttf").as_slice()) + .map(Message::FontLoaded), + Command::perform(SavedState::load(), Message::Loaded), + ]), ) } @@ -384,7 +390,7 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element { let tasks_left = tasks.iter().filter(|task| !task.completed).count(); let filter_button = |label, filter, current_filter| { - let label = text(label).size(16); + let label = text(label); let button = button(label).style(if filter == current_filter { theme::Button::Primary @@ -401,8 +407,7 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element { tasks_left, if tasks_left == 1 { "task" } else { "tasks" } )) - .width(Length::Fill) - .size(16), + .width(Length::Fill), row![ filter_button("All", Filter::All, current_filter), filter_button("Active", Filter::Active, current_filter), -- cgit From b8c1809ea101cece6943432fd3597f785c39af09 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Feb 2023 23:55:16 +0100 Subject: Refactor `triangle::Pipeline` into `prepare` and `render` architecture And get rid of the staging belt! :tada: --- examples/integration/src/main.rs | 9 --------- 1 file changed, 9 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 69f6961e..5af30ab1 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -125,9 +125,6 @@ pub fn main() { let mut resized = false; - // Initialize staging belt - let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); - // Initialize scene and GUI controls let scene = Scene::new(&device, format); let controls = Controls::new(); @@ -252,7 +249,6 @@ pub fn main() { backend.present( &device, &queue, - &mut staging_belt, &mut encoder, &view, primitive, @@ -262,7 +258,6 @@ pub fn main() { }); // Then we submit the work - staging_belt.finish(); queue.submit(Some(encoder.finish())); frame.present(); @@ -272,10 +267,6 @@ pub fn main() { state.mouse_interaction(), ), ); - - // And recall staging buffers - staging_belt.recall(); - } Err(error) => match error { wgpu::SurfaceError::OutOfMemory => { -- cgit From 730d6a07564d014c470e02f233394ec98325d463 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 8 Feb 2023 00:47:16 +0100 Subject: Reuse a `RenderPass` as much as possible in `iced_wgpu` --- examples/integration/src/main.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 5af30ab1..a7a90ced 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -250,6 +250,7 @@ pub fn main() { &device, &queue, &mut encoder, + None, &view, primitive, &viewport, -- cgit From 700262e05c76e003158acfeb8edd9f6b026d78cf Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 24 Feb 2023 13:56:37 +0100 Subject: Fix `checkbox` example --- examples/checkbox/fonts/icons.ttf | Bin 1272 -> 1784 bytes examples/checkbox/src/main.rs | 28 +++++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) (limited to 'examples') diff --git a/examples/checkbox/fonts/icons.ttf b/examples/checkbox/fonts/icons.ttf index a2046844..82f28481 100644 Binary files a/examples/checkbox/fonts/icons.ttf and b/examples/checkbox/fonts/icons.ttf differ diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index 09950bb8..77111490 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -1,10 +1,9 @@ +use iced::executor; +use iced::font::{self, Font}; use iced::widget::{checkbox, column, container}; -use iced::{Element, Font, Length, Sandbox, Settings}; +use iced::{Application, Command, Element, Length, Settings, Theme}; -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; +const ICON_FONT: Font = Font::Name("icons"); pub fn main() -> iced::Result { Example::run(Settings::default()) @@ -20,24 +19,35 @@ struct Example { enum Message { DefaultChecked(bool), CustomChecked(bool), + FontLoaded(Result<(), font::Error>), } -impl Sandbox for Example { +impl Application for Example { type Message = Message; + type Flags = (); + type Executor = executor::Default; + type Theme = Theme; - fn new() -> Self { - Default::default() + fn new(_flags: Self::Flags) -> (Self, Command) { + ( + Self::default(), + font::load(include_bytes!("../fonts/icons.ttf").as_ref()) + .map(Message::FontLoaded), + ) } fn title(&self) -> String { String::from("Checkbox - Iced") } - fn update(&mut self, message: Message) { + fn update(&mut self, message: Message) -> Command { match message { Message::DefaultChecked(value) => self.default_checkbox = value, Message::CustomChecked(value) => self.custom_checkbox = value, + Message::FontLoaded(_) => (), } + + Command::none() } fn view(&self) -> Element { -- cgit From 5fd5d1cdf8e5354788dc40729c4565ef377d3bba Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 Mar 2023 21:34:26 +0100 Subject: Implement `Canvas` support for `iced_tiny_skia` --- examples/arc/src/main.rs | 9 ++-- examples/bezier_tool/src/main.rs | 25 +++++++---- examples/clock/src/main.rs | 9 ++-- examples/color_palette/src/main.rs | 9 ++-- examples/game_of_life/src/main.rs | 73 +++++++++++++++++--------------- examples/geometry/src/main.rs | 6 +-- examples/modern_art/src/main.rs | 5 ++- examples/multitouch/src/main.rs | 7 +-- examples/sierpinski_triangle/src/main.rs | 9 ++-- examples/solar_system/src/main.rs | 28 ++++++------ 10 files changed, 100 insertions(+), 80 deletions(-) (limited to 'examples') diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs index 7b6ea0e1..d71ba6f6 100644 --- a/examples/arc/src/main.rs +++ b/examples/arc/src/main.rs @@ -5,8 +5,8 @@ use iced::widget::canvas::{ self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke, }; use iced::{ - Application, Command, Element, Length, Point, Rectangle, Settings, - Subscription, Theme, + Application, Command, Element, Length, Point, Rectangle, Renderer, + Settings, Subscription, Theme, }; pub fn main() -> iced::Result { @@ -69,17 +69,18 @@ impl Application for Arc { } } -impl canvas::Program for Arc { +impl canvas::Program for Arc { type State = (); fn draw( &self, _state: &Self::State, + renderer: &Renderer, theme: &Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec { - let geometry = self.cache.draw(bounds.size(), |frame| { + let geometry = self.cache.draw(renderer, bounds.size(), |frame| { let palette = theme.palette(); let center = frame.center(); diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 7c3916d4..5bb463c3 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -64,7 +64,7 @@ mod bezier { use iced::widget::canvas::{ self, Canvas, Cursor, Frame, Geometry, Path, Stroke, }; - use iced::{Element, Length, Point, Rectangle, Theme}; + use iced::{Element, Length, Point, Rectangle, Renderer, Theme}; #[derive(Default)] pub struct State { @@ -92,7 +92,7 @@ mod bezier { curves: &'a [Curve], } - impl<'a> canvas::Program for Bezier<'a> { + impl<'a> canvas::Program for Bezier<'a> { type State = Option; fn update( @@ -152,22 +152,26 @@ mod bezier { fn draw( &self, state: &Self::State, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, cursor: Cursor, ) -> Vec { - let content = - self.state.cache.draw(bounds.size(), |frame: &mut Frame| { + let content = self.state.cache.draw( + renderer, + bounds.size(), + |frame: &mut Frame| { Curve::draw_all(self.curves, frame); frame.stroke( &Path::rectangle(Point::ORIGIN, frame.size()), Stroke::default().with_width(2.0), ); - }); + }, + ); if let Some(pending) = state { - let pending_curve = pending.draw(bounds, cursor); + let pending_curve = pending.draw(renderer, bounds, cursor); vec![content, pending_curve] } else { @@ -216,8 +220,13 @@ mod bezier { } impl Pending { - fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Geometry { - let mut frame = Frame::new(bounds.size()); + fn draw( + &self, + renderer: &Renderer, + bounds: Rectangle, + cursor: Cursor, + ) -> Geometry { + let mut frame = Frame::new(renderer, bounds.size()); if let Some(cursor_position) = cursor.position_in(&bounds) { match *self { diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index a389c54f..6425e2da 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -4,8 +4,8 @@ use iced::widget::canvas::{ }; use iced::widget::{canvas, container}; use iced::{ - Application, Color, Command, Element, Length, Point, Rectangle, Settings, - Subscription, Theme, Vector, + Application, Color, Command, Element, Length, Point, Rectangle, Renderer, + Settings, Subscription, Theme, Vector, }; pub fn main() -> iced::Result { @@ -83,17 +83,18 @@ impl Application for Clock { } } -impl canvas::Program for Clock { +impl canvas::Program for Clock { type State = (); fn draw( &self, _state: &Self::State, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec { - let clock = self.clock.draw(bounds.size(), |frame| { + let clock = self.clock.draw(renderer, bounds.size(), |frame| { let center = frame.center(); let radius = frame.width().min(frame.height()) / 2.0; diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index a2df36c2..1109a883 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,8 +1,8 @@ use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path}; use iced::widget::{column, row, text, Slider}; use iced::{ - alignment, Alignment, Color, Element, Length, Point, Rectangle, Sandbox, - Settings, Size, Vector, + alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer, + Sandbox, Settings, Size, Vector, }; use palette::{self, convert::FromColor, Hsl, Srgb}; use std::marker::PhantomData; @@ -237,17 +237,18 @@ impl Theme { } } -impl canvas::Program for Theme { +impl canvas::Program for Theme { type State = (); fn draw( &self, _state: &Self::State, + renderer: &Renderer, _theme: &iced::Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec { - let theme = self.canvas_cache.draw(bounds.size(), |frame| { + let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| { self.draw(frame); }); diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index ed911160..494f71a6 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -211,8 +211,8 @@ mod grid { Cache, Canvas, Cursor, Frame, Geometry, Path, Text, }; use iced::{ - alignment, mouse, Color, Element, Length, Point, Rectangle, Size, - Theme, Vector, + alignment, mouse, Color, Element, Length, Point, Rectangle, Renderer, + Size, Theme, Vector, }; use rustc_hash::{FxHashMap, FxHashSet}; use std::future::Future; @@ -393,7 +393,7 @@ mod grid { } } - impl canvas::Program for Grid { + impl canvas::Program for Grid { type State = Interaction; fn update( @@ -536,13 +536,14 @@ mod grid { fn draw( &self, _interaction: &Interaction, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, cursor: Cursor, ) -> Vec { let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0); - let life = self.life_cache.draw(bounds.size(), |frame| { + let life = self.life_cache.draw(renderer, bounds.size(), |frame| { let background = Path::rectangle(Point::ORIGIN, frame.size()); frame.fill(&background, Color::from_rgb8(0x40, 0x44, 0x4B)); @@ -565,7 +566,7 @@ mod grid { }); let overlay = { - let mut frame = Frame::new(bounds.size()); + let mut frame = Frame::new(renderer, bounds.size()); let hovered_cell = cursor.position_in(&bounds).map(|position| { @@ -626,38 +627,40 @@ mod grid { if self.scaling < 0.2 || !self.show_lines { vec![life, overlay] } else { - let grid = self.grid_cache.draw(bounds.size(), |frame| { - frame.translate(center); - frame.scale(self.scaling); - frame.translate(self.translation); - frame.scale(Cell::SIZE as f32); - - let region = self.visible_region(frame.size()); - let rows = region.rows(); - let columns = region.columns(); - let (total_rows, total_columns) = - (rows.clone().count(), columns.clone().count()); - let width = 2.0 / Cell::SIZE as f32; - let color = Color::from_rgb8(70, 74, 83); - - frame.translate(Vector::new(-width / 2.0, -width / 2.0)); + let grid = + self.grid_cache.draw(renderer, bounds.size(), |frame| { + frame.translate(center); + frame.scale(self.scaling); + frame.translate(self.translation); + frame.scale(Cell::SIZE as f32); - for row in region.rows() { - frame.fill_rectangle( - Point::new(*columns.start() as f32, row as f32), - Size::new(total_columns as f32, width), - color, - ); - } + let region = self.visible_region(frame.size()); + let rows = region.rows(); + let columns = region.columns(); + let (total_rows, total_columns) = + (rows.clone().count(), columns.clone().count()); + let width = 2.0 / Cell::SIZE as f32; + let color = Color::from_rgb8(70, 74, 83); + + frame + .translate(Vector::new(-width / 2.0, -width / 2.0)); + + for row in region.rows() { + frame.fill_rectangle( + Point::new(*columns.start() as f32, row as f32), + Size::new(total_columns as f32, width), + color, + ); + } - for column in region.columns() { - frame.fill_rectangle( - Point::new(column as f32, *rows.start() as f32), - Size::new(width, total_rows as f32), - color, - ); - } - }); + for column in region.columns() { + frame.fill_rectangle( + Point::new(column as f32, *rows.start() as f32), + Size::new(width, total_rows as f32), + color, + ); + } + }); vec![life, grid, overlay] } diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 9bacce7f..a77772d5 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -10,9 +10,9 @@ mod rainbow { // Of course, you can choose to make the implementation renderer-agnostic, // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. + use iced_graphics::primitive::{ColoredVertex2D, Primitive}; use iced_graphics::renderer::{self, Renderer}; - use iced_graphics::triangle::ColoredVertex2D; - use iced_graphics::{Backend, Primitive}; + use iced_graphics::Backend; use iced_native::layout; use iced_native::widget::{self, Widget}; @@ -59,7 +59,7 @@ mod rainbow { cursor_position: Point, _viewport: &Rectangle, ) { - use iced_graphics::triangle::Mesh2D; + use iced_graphics::primitive::Mesh2D; use iced_native::Renderer as _; let b = layout.bounds(); diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs index 28ed3e21..a43a2b2b 100644 --- a/examples/modern_art/src/main.rs +++ b/examples/modern_art/src/main.rs @@ -55,17 +55,18 @@ impl Application for ModernArt { } } -impl canvas::Program for ModernArt { +impl canvas::Program for ModernArt { type State = (); fn draw( &self, _state: &Self::State, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec { - let geometry = self.cache.draw(bounds.size(), |frame| { + let geometry = self.cache.draw(renderer, bounds.size(), |frame| { let num_squares = thread_rng().gen_range(0..1200); let mut i = 0; diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs index f5faae0f..7df6c929 100644 --- a/examples/multitouch/src/main.rs +++ b/examples/multitouch/src/main.rs @@ -6,7 +6,7 @@ use iced::widget::canvas::stroke::{self, Stroke}; use iced::widget::canvas::{self, Canvas, Cursor, Geometry}; use iced::{ executor, touch, window, Application, Color, Command, Element, Length, - Point, Rectangle, Settings, Subscription, Theme, + Point, Rectangle, Renderer, Settings, Subscription, Theme, }; use std::collections::HashMap; @@ -95,7 +95,7 @@ impl Application for Multitouch { } } -impl canvas::Program for State { +impl canvas::Program for State { type State = (); fn update( @@ -125,11 +125,12 @@ impl canvas::Program for State { fn draw( &self, _state: &Self::State, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec { - let fingerweb = self.cache.draw(bounds.size(), |frame| { + let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| { if self.fingers.len() < 2 { return; } diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs index 1d25d171..e85f8391 100644 --- a/examples/sierpinski_triangle/src/main.rs +++ b/examples/sierpinski_triangle/src/main.rs @@ -5,8 +5,8 @@ use iced::widget::canvas::event::{self, Event}; use iced::widget::canvas::{self, Canvas}; use iced::widget::{column, row, slider, text}; use iced::{ - Application, Color, Command, Length, Point, Rectangle, Settings, Size, - Theme, + Application, Color, Command, Length, Point, Rectangle, Renderer, Settings, + Size, Theme, }; use rand::Rng; @@ -97,7 +97,7 @@ struct SierpinskiGraph { cache: canvas::Cache, } -impl canvas::Program for SierpinskiGraph { +impl canvas::Program for SierpinskiGraph { type State = (); fn update( @@ -134,11 +134,12 @@ impl canvas::Program for SierpinskiGraph { fn draw( &self, _state: &Self::State, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: canvas::Cursor, ) -> Vec { - let geom = self.cache.draw(bounds.size(), |frame| { + let geom = self.cache.draw(renderer, bounds.size(), |frame| { frame.stroke( &canvas::Path::rectangle(Point::ORIGIN, frame.size()), canvas::Stroke::default(), diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 9a4ee754..0023a69b 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -15,8 +15,8 @@ use iced::widget::canvas::stroke::{self, Stroke}; use iced::widget::canvas::{Cursor, Path}; use iced::window; use iced::{ - Application, Color, Command, Element, Length, Point, Rectangle, Settings, - Size, Subscription, Vector, + Application, Color, Command, Element, Length, Point, Rectangle, Renderer, + Settings, Size, Subscription, Vector, }; use std::time::Instant; @@ -150,30 +150,32 @@ impl State { } } -impl canvas::Program for State { +impl canvas::Program for State { type State = (); fn draw( &self, _state: &Self::State, + renderer: &Renderer, _theme: &Theme, bounds: Rectangle, _cursor: Cursor, ) -> Vec { use std::f32::consts::PI; - let background = self.space_cache.draw(bounds.size(), |frame| { - let stars = Path::new(|path| { - for (p, size) in &self.stars { - path.rectangle(*p, Size::new(*size, *size)); - } - }); + let background = + self.space_cache.draw(renderer, bounds.size(), |frame| { + let stars = Path::new(|path| { + for (p, size) in &self.stars { + path.rectangle(*p, Size::new(*size, *size)); + } + }); - frame.translate(frame.center() - Point::ORIGIN); - frame.fill(&stars, Color::WHITE); - }); + frame.translate(frame.center() - Point::ORIGIN); + frame.fill(&stars, Color::WHITE); + }); - let system = self.system_cache.draw(bounds.size(), |frame| { + let system = self.system_cache.draw(renderer, bounds.size(), |frame| { let center = frame.center(); let sun = Path::circle(center, Self::SUN_RADIUS); -- cgit From 5c0427edbb4358896412c736af2f441c12601d1b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 1 Mar 2023 21:41:32 +0100 Subject: Fix `Clip` primitive translation in `iced_tiny_skia` --- examples/game_of_life/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index 494f71a6..cdb33aca 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -145,7 +145,7 @@ impl Application for GameOfLife { self.grid .view() .map(move |message| Message::Grid(message, version)), - controls + controls, ]; container(content) -- cgit From 6cc48b5c62bac287b8f9f1c79c1fb7486c51b18f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 3 Mar 2023 04:57:55 +0100 Subject: Move `Canvas` and `QRCode` to `iced` crate Rename `canvas` modules to `geometry` in graphics subcrates --- examples/arc/src/main.rs | 2 +- examples/bezier_tool/src/main.rs | 2 +- examples/color_palette/src/main.rs | 2 +- examples/game_of_life/src/main.rs | 2 +- examples/sierpinski_triangle/src/main.rs | 2 +- examples/solar_system/src/main.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs index d71ba6f6..80ad0b5b 100644 --- a/examples/arc/src/main.rs +++ b/examples/arc/src/main.rs @@ -69,7 +69,7 @@ impl Application for Arc { } } -impl canvas::Program for Arc { +impl canvas::Program for Arc { type State = (); fn draw( diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index 5bb463c3..f1c83a16 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -92,7 +92,7 @@ mod bezier { curves: &'a [Curve], } - impl<'a> canvas::Program for Bezier<'a> { + impl<'a> canvas::Program for Bezier<'a> { type State = Option; fn update( diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 1109a883..5c4304ee 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -237,7 +237,7 @@ impl Theme { } } -impl canvas::Program for Theme { +impl canvas::Program for Theme { type State = (); fn draw( diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index cdb33aca..eab8908b 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -393,7 +393,7 @@ mod grid { } } - impl canvas::Program for Grid { + impl canvas::Program for Grid { type State = Interaction; fn update( diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs index e85f8391..4faac6d6 100644 --- a/examples/sierpinski_triangle/src/main.rs +++ b/examples/sierpinski_triangle/src/main.rs @@ -97,7 +97,7 @@ struct SierpinskiGraph { cache: canvas::Cache, } -impl canvas::Program for SierpinskiGraph { +impl canvas::Program for SierpinskiGraph { type State = (); fn update( diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 0023a69b..f2606feb 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -150,7 +150,7 @@ impl State { } } -impl canvas::Program for State { +impl canvas::Program for State { type State = (); fn draw( -- cgit From 3a0d34c0240f4421737a6a08761f99d6f8140d02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 4 Mar 2023 05:37:11 +0100 Subject: Create `iced_widget` subcrate and re-organize the whole codebase --- examples/component/Cargo.toml | 4 +- examples/component/src/main.rs | 22 ++------- examples/custom_quad/Cargo.toml | 3 +- examples/custom_quad/src/main.rs | 8 ++-- examples/custom_widget/Cargo.toml | 3 +- examples/custom_widget/src/main.rs | 8 ++-- examples/events/Cargo.toml | 1 - examples/events/src/main.rs | 9 ++-- examples/geometry/Cargo.toml | 3 +- examples/geometry/src/main.rs | 38 +++++---------- examples/integration/Cargo.toml | 1 + examples/integration/src/controls.rs | 10 ++-- examples/integration/src/main.rs | 91 +++++++++++++++++++----------------- examples/integration/src/scene.rs | 2 +- examples/lazy/Cargo.toml | 3 +- examples/lazy/src/main.rs | 3 +- examples/modal/Cargo.toml | 3 +- examples/modal/src/main.rs | 44 +++++++++-------- examples/pane_grid/Cargo.toml | 4 +- examples/pane_grid/src/main.rs | 8 ++-- examples/toast/Cargo.toml | 3 +- examples/toast/src/main.rs | 24 ++++++---- examples/url_handler/Cargo.toml | 1 - examples/url_handler/src/main.rs | 10 ++-- 24 files changed, 143 insertions(+), 163 deletions(-) (limited to 'examples') diff --git a/examples/component/Cargo.toml b/examples/component/Cargo.toml index dd435201..9db1e6b4 100644 --- a/examples/component/Cargo.toml +++ b/examples/component/Cargo.toml @@ -6,6 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = ["debug"] } -iced_native = { path = "../../native" } -iced_lazy = { path = "../../lazy" } +iced = { path = "../..", features = ["debug", "lazy"] } diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index c407bb06..e59588b1 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -47,9 +47,8 @@ impl Sandbox for Component { mod numeric_input { use iced::alignment::{self, Alignment}; - use iced::widget::{self, button, row, text, text_input}; - use iced::{Element, Length}; - use iced_lazy::{self, Component}; + use iced::widget::{button, component, row, text, text_input, Component}; + use iced::{Element, Length, Renderer}; pub struct NumericInput { value: Option, @@ -82,13 +81,7 @@ mod numeric_input { } } - impl Component for NumericInput - where - Renderer: iced_native::text::Renderer + 'static, - Renderer::Theme: widget::button::StyleSheet - + widget::text_input::StyleSheet - + widget::text::StyleSheet, - { + impl Component for NumericInput { type State = (); type Event = Event; @@ -151,17 +144,12 @@ mod numeric_input { } } - impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> + impl<'a, Message> From> for Element<'a, Message, Renderer> where Message: 'a, - Renderer: 'static + iced_native::text::Renderer, - Renderer::Theme: widget::button::StyleSheet - + widget::text_input::StyleSheet - + widget::text::StyleSheet, { fn from(numeric_input: NumericInput) -> Self { - iced_lazy::component(numeric_input) + component(numeric_input) } } } diff --git a/examples/custom_quad/Cargo.toml b/examples/custom_quad/Cargo.toml index 39154786..f097c2dd 100644 --- a/examples/custom_quad/Cargo.toml +++ b/examples/custom_quad/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../.." } -iced_native = { path = "../../native" } +iced = { path = "../..", features = ["advanced"] } diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index 6509887c..b07f42ce 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -1,9 +1,9 @@ //! This example showcases a drawing a quad. mod quad { - use iced_native::layout::{self, Layout}; - use iced_native::renderer; - use iced_native::widget::{self, Widget}; - use iced_native::{Color, Element, Length, Point, Rectangle, Size}; + use iced::advanced::layout::{self, Layout}; + use iced::advanced::renderer; + use iced::advanced::widget::{self, Widget}; + use iced::{Color, Element, Length, Point, Rectangle, Size}; pub struct CustomQuad { size: f32, diff --git a/examples/custom_widget/Cargo.toml b/examples/custom_widget/Cargo.toml index 067aab4a..dda0efe8 100644 --- a/examples/custom_widget/Cargo.toml +++ b/examples/custom_widget/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../.." } -iced_native = { path = "../../native" } +iced = { path = "../..", features = ["advanced"] } diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index f6bb3b1e..7854548c 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -9,10 +9,10 @@ mod circle { // Of course, you can choose to make the implementation renderer-agnostic, // if you wish to, by creating your own `Renderer` trait, which could be // implemented by `iced_wgpu` and other renderers. - use iced_native::layout::{self, Layout}; - use iced_native::renderer; - use iced_native::widget::{self, Widget}; - use iced_native::{Color, Element, Length, Point, Rectangle, Size}; + use iced::advanced::layout::{self, Layout}; + use iced::advanced::renderer; + use iced::advanced::widget::{self, Widget}; + use iced::{Color, Element, Length, Point, Rectangle, Size}; pub struct Circle { radius: f32, diff --git a/examples/events/Cargo.toml b/examples/events/Cargo.toml index 8c56e471..15ffc0af 100644 --- a/examples/events/Cargo.toml +++ b/examples/events/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] iced = { path = "../..", features = ["debug"] } -iced_native = { path = "../../native" } diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs index 1b97018e..7f3a5e1d 100644 --- a/examples/events/src/main.rs +++ b/examples/events/src/main.rs @@ -1,12 +1,13 @@ use iced::alignment; use iced::executor; +use iced::subscription; use iced::widget::{button, checkbox, container, text, Column}; use iced::window; +use iced::Event; use iced::{ Alignment, Application, Command, Element, Length, Settings, Subscription, Theme, }; -use iced_native::Event; pub fn main() -> iced::Result { Events::run(Settings { @@ -17,13 +18,13 @@ pub fn main() -> iced::Result { #[derive(Debug, Default)] struct Events { - last: Vec, + last: Vec, enabled: bool, } #[derive(Debug, Clone)] enum Message { - EventOccurred(iced_native::Event), + EventOccurred(Event), Toggled(bool), Exit, } @@ -70,7 +71,7 @@ impl Application for Events { } fn subscription(&self) -> Subscription { - iced_native::subscription::events().map(Message::EventOccurred) + subscription::events().map(Message::EventOccurred) } fn view(&self) -> Element { diff --git a/examples/geometry/Cargo.toml b/examples/geometry/Cargo.toml index 22ede0e0..79fe52d5 100644 --- a/examples/geometry/Cargo.toml +++ b/examples/geometry/Cargo.toml @@ -6,6 +6,5 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../.." } -iced_native = { path = "../../native" } +iced = { path = "../..", features = ["advanced"] } iced_graphics = { path = "../../graphics" } diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index a77772d5..5cb41184 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -1,23 +1,13 @@ //! This example showcases a simple native custom widget that renders using //! arbitrary low-level geometry. mod rainbow { - // For now, to implement a custom native widget you will need to add - // `iced_native` and `iced_wgpu` to your dependencies. - // - // Then, you simply need to define your widget type and implement the - // `iced_native::Widget` trait with the `iced_wgpu::Renderer`. - // - // Of course, you can choose to make the implementation renderer-agnostic, - // if you wish to, by creating your own `Renderer` trait, which could be - // implemented by `iced_wgpu` and other renderers. use iced_graphics::primitive::{ColoredVertex2D, Primitive}; - use iced_graphics::renderer::{self, Renderer}; - use iced_graphics::Backend; - use iced_native::layout; - use iced_native::widget::{self, Widget}; - use iced_native::{ - Element, Layout, Length, Point, Rectangle, Size, Vector, + use iced::advanced::layout::{self, Layout}; + use iced::advanced::renderer; + use iced::advanced::widget::{self, Widget}; + use iced::{ + Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector, }; #[derive(Debug, Clone, Copy, Default)] @@ -27,10 +17,7 @@ mod rainbow { Rainbow } - impl Widget> for Rainbow - where - B: Backend, - { + impl Widget for Rainbow { fn width(&self) -> Length { Length::Fill } @@ -41,7 +28,7 @@ mod rainbow { fn layout( &self, - _renderer: &Renderer, + _renderer: &Renderer, limits: &layout::Limits, ) -> layout::Node { let size = limits.width(Length::Fill).resolve(Size::ZERO); @@ -52,15 +39,15 @@ mod rainbow { fn draw( &self, _tree: &widget::Tree, - renderer: &mut Renderer, - _theme: &T, + renderer: &mut Renderer, + _theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, _viewport: &Rectangle, ) { + use iced::advanced::Renderer as _; use iced_graphics::primitive::Mesh2D; - use iced_native::Renderer as _; let b = layout.bounds(); @@ -151,10 +138,7 @@ mod rainbow { } } - impl<'a, Message, B, T> From for Element<'a, Message, Renderer> - where - B: Backend, - { + impl<'a, Message> From for Element<'a, Message, Renderer> { fn from(rainbow: Rainbow) -> Self { Self::new(rainbow) } diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml index 200306aa..60390242 100644 --- a/examples/integration/Cargo.toml +++ b/examples/integration/Cargo.toml @@ -8,6 +8,7 @@ publish = false [dependencies] iced_winit = { path = "../../winit" } iced_wgpu = { path = "../../wgpu", features = ["webgl"] } +iced_widget = { path = "../../widget" } env_logger = "0.8" [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs index 533cb6e2..16e21709 100644 --- a/examples/integration/src/controls.rs +++ b/examples/integration/src/controls.rs @@ -1,6 +1,8 @@ use iced_wgpu::Renderer; -use iced_winit::widget::{slider, text_input, Column, Row, Text}; -use iced_winit::{Alignment, Color, Command, Element, Length, Program}; +use iced_widget::{slider, text_input, Column, Row, Text}; +use iced_winit::core::{Alignment, Color, Element, Length}; +use iced_winit::native::{Command, Program}; +use iced_winit::style::Theme; pub struct Controls { background_color: Color, @@ -27,7 +29,7 @@ impl Controls { } impl Program for Controls { - type Renderer = Renderer; + type Renderer = Renderer; type Message = Message; fn update(&mut self, message: Message) -> Command { @@ -43,7 +45,7 @@ impl Program for Controls { Command::none() } - fn view(&self) -> Element { + fn view(&self) -> Element> { let background_color = self.background_color; let text = &self.text; diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index a7a90ced..c1f1f076 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -4,11 +4,14 @@ mod scene; use controls::Controls; use scene::Scene; -use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; -use iced_winit::{ - conversion, futures, program, renderer, winit, Clipboard, Color, Debug, - Size, -}; +use iced_wgpu::graphics::Viewport; +use iced_wgpu::{wgpu, Backend, Renderer, Settings}; +use iced_winit::core::renderer; +use iced_winit::core::{Color, Size}; +use iced_winit::native::program; +use iced_winit::native::Debug; +use iced_winit::style::Theme; +use iced_winit::{conversion, futures, winit, Clipboard}; use winit::{ dpi::PhysicalPosition, @@ -73,43 +76,45 @@ pub fn main() { let instance = wgpu::Instance::new(backend); let surface = unsafe { instance.create_surface(&window) }; - let (format, (device, queue)) = futures::executor::block_on(async { - let adapter = wgpu::util::initialize_adapter_from_env_or_default( - &instance, - backend, - Some(&surface), - ) - .await - .expect("No suitable GPU adapters found on the system!"); - - let adapter_features = adapter.features(); - - #[cfg(target_arch = "wasm32")] - let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() - .using_resolution(adapter.limits()); - - #[cfg(not(target_arch = "wasm32"))] - let needed_limits = wgpu::Limits::default(); - - ( - surface - .get_supported_formats(&adapter) - .first() - .copied() - .expect("Get preferred format"), - adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: adapter_features & wgpu::Features::default(), - limits: needed_limits, - }, - None, - ) - .await - .expect("Request device"), - ) - }); + let (format, (device, queue)) = + futures::futures::executor::block_on(async { + let adapter = wgpu::util::initialize_adapter_from_env_or_default( + &instance, + backend, + Some(&surface), + ) + .await + .expect("No suitable GPU adapters found on the system!"); + + let adapter_features = adapter.features(); + + #[cfg(target_arch = "wasm32")] + let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() + .using_resolution(adapter.limits()); + + #[cfg(not(target_arch = "wasm32"))] + let needed_limits = wgpu::Limits::default(); + + ( + surface + .get_supported_formats(&adapter) + .first() + .copied() + .expect("Get preferred format"), + adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: adapter_features + & wgpu::Features::default(), + limits: needed_limits, + }, + None, + ) + .await + .expect("Request device"), + ) + }); surface.configure( &device, @@ -188,7 +193,7 @@ pub fn main() { viewport.scale_factor(), ), &mut renderer, - &iced_wgpu::Theme::Dark, + &Theme::Dark, &renderer::Style { text_color: Color::WHITE }, &mut clipboard, &mut debug, diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs index 3e41fbda..90c7efbf 100644 --- a/examples/integration/src/scene.rs +++ b/examples/integration/src/scene.rs @@ -1,5 +1,5 @@ use iced_wgpu::wgpu; -use iced_winit::Color; +use iced_winit::core::Color; pub struct Scene { pipeline: wgpu::RenderPipeline, diff --git a/examples/lazy/Cargo.toml b/examples/lazy/Cargo.toml index 79255c25..e03e89a9 100644 --- a/examples/lazy/Cargo.toml +++ b/examples/lazy/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = ["debug"] } -iced_lazy = { path = "../../lazy" } +iced = { path = "../..", features = ["debug", "lazy"] } diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 6512106f..e1cdaefe 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -1,10 +1,9 @@ use iced::theme; use iced::widget::{ - button, column, horizontal_space, pick_list, row, scrollable, text, + button, column, horizontal_space, lazy, pick_list, row, scrollable, text, text_input, }; use iced::{Element, Length, Sandbox, Settings}; -use iced_lazy::lazy; use std::collections::HashSet; use std::hash::Hash; diff --git a/examples/modal/Cargo.toml b/examples/modal/Cargo.toml index 8770acac..3ac61e6a 100644 --- a/examples/modal/Cargo.toml +++ b/examples/modal/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = [] } -iced_native = { path = "../../native" } +iced = { path = "../..", features = ["advanced"] } diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 54555684..214ec97e 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -178,12 +178,15 @@ impl App { } mod modal { - use iced_native::alignment::Alignment; - use iced_native::widget::{self, Tree}; - use iced_native::{ - event, layout, mouse, overlay, renderer, Clipboard, Color, Element, - Event, Layout, Length, Point, Rectangle, Shell, Size, Widget, - }; + use iced::advanced::layout::{self, Layout}; + use iced::advanced::overlay; + use iced::advanced::renderer; + use iced::advanced::widget::{self, Widget}; + use iced::advanced::{self, Clipboard, Shell}; + use iced::alignment::Alignment; + use iced::event; + use iced::mouse; + use iced::{Color, Element, Event, Length, Point, Rectangle, Size}; /// A widget that centers a modal element over some base element pub struct Modal<'a, Message, Renderer> { @@ -218,14 +221,17 @@ mod modal { impl<'a, Message, Renderer> Widget for Modal<'a, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: advanced::Renderer, Message: Clone, { - fn children(&self) -> Vec { - vec![Tree::new(&self.base), Tree::new(&self.modal)] + fn children(&self) -> Vec { + vec![ + widget::Tree::new(&self.base), + widget::Tree::new(&self.modal), + ] } - fn diff(&self, tree: &mut Tree) { + fn diff(&self, tree: &mut widget::Tree) { tree.diff_children(&[&self.base, &self.modal]); } @@ -247,7 +253,7 @@ mod modal { fn on_event( &mut self, - state: &mut Tree, + state: &mut widget::Tree, event: Event, layout: Layout<'_>, cursor_position: Point, @@ -268,9 +274,9 @@ mod modal { fn draw( &self, - state: &Tree, + state: &widget::Tree, renderer: &mut Renderer, - theme: &::Theme, + theme: &::Theme, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, @@ -289,7 +295,7 @@ mod modal { fn overlay<'b>( &'b mut self, - state: &'b mut Tree, + state: &'b mut widget::Tree, layout: Layout<'_>, _renderer: &Renderer, ) -> Option> { @@ -306,7 +312,7 @@ mod modal { fn mouse_interaction( &self, - state: &Tree, + state: &widget::Tree, layout: Layout<'_>, cursor_position: Point, viewport: &Rectangle, @@ -323,7 +329,7 @@ mod modal { fn operate( &self, - state: &mut Tree, + state: &mut widget::Tree, layout: Layout<'_>, renderer: &Renderer, operation: &mut dyn widget::Operation, @@ -339,7 +345,7 @@ mod modal { struct Overlay<'a, 'b, Message, Renderer> { content: &'b mut Element<'a, Message, Renderer>, - tree: &'b mut Tree, + tree: &'b mut widget::Tree, size: Size, on_blur: Option, } @@ -347,7 +353,7 @@ mod modal { impl<'a, 'b, Message, Renderer> overlay::Overlay for Overlay<'a, 'b, Message, Renderer> where - Renderer: iced_native::Renderer, + Renderer: advanced::Renderer, Message: Clone, { fn layout( @@ -469,7 +475,7 @@ mod modal { impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where - Renderer: 'a + iced_native::Renderer, + Renderer: 'a + advanced::Renderer, Message: 'a + Clone, { fn from(modal: Modal<'a, Message, Renderer>) -> Self { diff --git a/examples/pane_grid/Cargo.toml b/examples/pane_grid/Cargo.toml index dfd6dfa9..4c0bf072 100644 --- a/examples/pane_grid/Cargo.toml +++ b/examples/pane_grid/Cargo.toml @@ -6,6 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = ["debug"] } -iced_native = { path = "../../native" } -iced_lazy = { path = "../../lazy" } +iced = { path = "../..", features = ["debug", "lazy"] } diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index c9f1376c..dfb80853 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -1,14 +1,16 @@ use iced::alignment::{self, Alignment}; +use iced::event::{self, Event}; use iced::executor; use iced::keyboard; +use iced::subscription; use iced::theme::{self, Theme}; use iced::widget::pane_grid::{self, PaneGrid}; -use iced::widget::{button, column, container, row, scrollable, text}; +use iced::widget::{ + button, column, container, responsive, row, scrollable, text, +}; use iced::{ Application, Color, Command, Element, Length, Settings, Size, Subscription, }; -use iced_lazy::responsive; -use iced_native::{event, subscription, Event}; pub fn main() -> iced::Result { Example::run(Settings::default()) diff --git a/examples/toast/Cargo.toml b/examples/toast/Cargo.toml index f1f986aa..f703572c 100644 --- a/examples/toast/Cargo.toml +++ b/examples/toast/Cargo.toml @@ -6,5 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = [] } -iced_native = { path = "../../native" } +iced = { path = "../..", features = ["advanced"] } diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index e74b3ee6..78fb9de1 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -176,17 +176,23 @@ mod toast { use std::fmt; use std::time::{Duration, Instant}; + use iced::advanced; + use iced::advanced::layout::{self, Layout}; + use iced::advanced::overlay; + use iced::advanced::renderer; + use iced::advanced::widget::{self, Operation, Tree}; + use iced::advanced::{Clipboard, Shell, Widget}; + use iced::event::{self, Event}; + use iced::mouse; use iced::theme; use iced::widget::{ button, column, container, horizontal_rule, horizontal_space, row, text, }; + use iced::window; use iced::{ Alignment, Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector, }; - use iced_native::widget::{tree, Operation, Tree}; - use iced_native::{event, layout, mouse, overlay, renderer, window}; - use iced_native::{Clipboard, Event, Layout, Shell, Widget}; pub const DEFAULT_TIMEOUT: u64 = 5; @@ -324,13 +330,13 @@ mod toast { self.content.as_widget().layout(renderer, limits) } - fn tag(&self) -> tree::Tag { + fn tag(&self) -> widget::tree::Tag { struct Marker(Vec); - iced_native::widget::tree::Tag::of::() + widget::tree::Tag::of::() } - fn state(&self) -> tree::State { - iced_native::widget::tree::State::new(Vec::>::new()) + fn state(&self) -> widget::tree::State { + widget::tree::State::new(Vec::>::new()) } fn children(&self) -> Vec { @@ -584,7 +590,7 @@ mod toast { fn draw( &self, renderer: &mut Renderer, - theme: &::Theme, + theme: &::Theme, style: &renderer::Style, layout: Layout<'_>, cursor_position: Point, @@ -613,7 +619,7 @@ mod toast { &mut self, layout: Layout<'_>, renderer: &Renderer, - operation: &mut dyn iced_native::widget::Operation, + operation: &mut dyn widget::Operation, ) { operation.container(None, &mut |operation| { self.toasts diff --git a/examples/url_handler/Cargo.toml b/examples/url_handler/Cargo.toml index 63c7ec27..4dcff92d 100644 --- a/examples/url_handler/Cargo.toml +++ b/examples/url_handler/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] iced = { path = "../.." } -iced_native = { path = "../../native" } diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs index 3257b519..f63fa06a 100644 --- a/examples/url_handler/src/main.rs +++ b/examples/url_handler/src/main.rs @@ -1,12 +1,10 @@ +use iced::event::{Event, MacOS, PlatformSpecific}; use iced::executor; +use iced::subscription; use iced::widget::{container, text}; use iced::{ Application, Command, Element, Length, Settings, Subscription, Theme, }; -use iced_native::{ - event::{MacOS, PlatformSpecific}, - Event, -}; pub fn main() -> iced::Result { App::run(Settings::default()) @@ -19,7 +17,7 @@ struct App { #[derive(Debug, Clone)] enum Message { - EventOccurred(iced_native::Event), + EventOccurred(Event), } impl Application for App { @@ -52,7 +50,7 @@ impl Application for App { } fn subscription(&self) -> Subscription { - iced_native::subscription::events().map(Message::EventOccurred) + subscription::events().map(Message::EventOccurred) } fn view(&self) -> Element { -- cgit From f4cf488e0b083b5d7b7612c650917233163ee9cb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Mar 2023 04:15:10 +0100 Subject: Remove generic `Hasher` and `Event` from `subscription::Recipe` --- examples/download_progress/Cargo.toml | 2 -- examples/download_progress/src/download.rs | 2 +- examples/websocket/Cargo.toml | 2 -- examples/websocket/src/echo.rs | 4 ++-- examples/websocket/src/echo/server.rs | 2 +- 5 files changed, 4 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/download_progress/Cargo.toml b/examples/download_progress/Cargo.toml index f38679ea..212832f4 100644 --- a/examples/download_progress/Cargo.toml +++ b/examples/download_progress/Cargo.toml @@ -7,8 +7,6 @@ publish = false [dependencies] iced = { path = "../..", features = ["tokio"] } -iced_native = { path = "../../native" } -iced_futures = { path = "../../futures" } [dependencies.reqwest] version = "0.11" diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index 39dd843f..5ff951b3 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -1,4 +1,4 @@ -use iced_native::subscription; +use iced::subscription; use std::hash::Hash; diff --git a/examples/websocket/Cargo.toml b/examples/websocket/Cargo.toml index c25f067b..03b240c6 100644 --- a/examples/websocket/Cargo.toml +++ b/examples/websocket/Cargo.toml @@ -7,8 +7,6 @@ publish = false [dependencies] iced = { path = "../..", features = ["tokio", "debug"] } -iced_native = { path = "../../native" } -iced_futures = { path = "../../futures" } once_cell = "1.15" [dependencies.async-tungstenite] diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index e74768a6..122c20db 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -1,7 +1,7 @@ pub mod server; -use iced_futures::futures; -use iced_native::subscription::{self, Subscription}; +use iced::futures; +use iced::subscription::{self, Subscription}; use futures::channel::mpsc; use futures::sink::SinkExt; diff --git a/examples/websocket/src/echo/server.rs b/examples/websocket/src/echo/server.rs index dd234984..168a635e 100644 --- a/examples/websocket/src/echo/server.rs +++ b/examples/websocket/src/echo/server.rs @@ -1,4 +1,4 @@ -use iced_futures::futures; +use iced::futures; use futures::channel::mpsc; use futures::{SinkExt, StreamExt}; -- cgit From 99e0a71504456976ba88040f5d1d3bbc347694ea Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Mar 2023 06:35:20 +0100 Subject: Rename `iced_native` to `iced_runtime` --- examples/integration/src/controls.rs | 2 +- examples/integration/src/main.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs index 16e21709..5849f730 100644 --- a/examples/integration/src/controls.rs +++ b/examples/integration/src/controls.rs @@ -1,7 +1,7 @@ use iced_wgpu::Renderer; use iced_widget::{slider, text_input, Column, Row, Text}; use iced_winit::core::{Alignment, Color, Element, Length}; -use iced_winit::native::{Command, Program}; +use iced_winit::runtime::{Command, Program}; use iced_winit::style::Theme; pub struct Controls { diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index c1f1f076..9707eda7 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -8,8 +8,8 @@ use iced_wgpu::graphics::Viewport; use iced_wgpu::{wgpu, Backend, Renderer, Settings}; use iced_winit::core::renderer; use iced_winit::core::{Color, Size}; -use iced_winit::native::program; -use iced_winit::native::Debug; +use iced_winit::runtime::program; +use iced_winit::runtime::Debug; use iced_winit::style::Theme; use iced_winit::{conversion, futures, winit, Clipboard}; -- cgit From 06bbcc310e6e759a0839df6ca391ea5e0f0ee609 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Mar 2023 06:40:20 +0100 Subject: Move `webgl` feature selection for `wgpu` into `iced_wgpu` --- examples/integration/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml index 60390242..f429977f 100644 --- a/examples/integration/Cargo.toml +++ b/examples/integration/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] iced_winit = { path = "../../winit" } -iced_wgpu = { path = "../../wgpu", features = ["webgl"] } +iced_wgpu = { path = "../../wgpu" } iced_widget = { path = "../../widget" } env_logger = "0.8" -- cgit From 5b3977daf6df624ca5d5e1a21ce282161234b22d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Mar 2023 06:09:51 +0100 Subject: Implement `vector` pipeline in `iced_tiny_skia` --- examples/svg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/svg/Cargo.toml b/examples/svg/Cargo.toml index f5a6eaa2..7b45c672 100644 --- a/examples/svg/Cargo.toml +++ b/examples/svg/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = ["svg"] } +iced = { path = "../..", features = ["svg", "tiny-skia", "debug"], default-features = false } -- cgit From 81d154d63a637d69f0c710780e2bbcd45ef4683c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Mar 2023 06:15:38 +0100 Subject: Use default features in `svg` example --- examples/svg/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/svg/Cargo.toml b/examples/svg/Cargo.toml index 7b45c672..f5a6eaa2 100644 --- a/examples/svg/Cargo.toml +++ b/examples/svg/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" publish = false [dependencies] -iced = { path = "../..", features = ["svg", "tiny-skia", "debug"], default-features = false } +iced = { path = "../..", features = ["svg"] } -- cgit From d3900e067361c82fd857fc92b81284146140bc3b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Mar 2023 06:41:41 +0100 Subject: Enable renderer backends in `integration` example --- examples/integration/Cargo.toml | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml index f429977f..f6863cd3 100644 --- a/examples/integration/Cargo.toml +++ b/examples/integration/Cargo.toml @@ -9,6 +9,7 @@ publish = false iced_winit = { path = "../../winit" } iced_wgpu = { path = "../../wgpu" } iced_widget = { path = "../../widget" } +iced_renderer = { path = "../../renderer", features = ["wgpu", "tiny-skia"] } env_logger = "0.8" [target.'cfg(target_arch = "wasm32")'.dependencies] -- cgit From 1816c985fad2ead8dc1e7b62dc8e4bafeed856b2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Mar 2023 11:11:17 +0100 Subject: Fix `clippy` lints for Rust 1.68 --- examples/pick_list/src/main.rs | 9 ++------- examples/todos/src/main.rs | 11 ++++------- 2 files changed, 6 insertions(+), 14 deletions(-) (limited to 'examples') diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs index 62a4ef88..21200621 100644 --- a/examples/pick_list/src/main.rs +++ b/examples/pick_list/src/main.rs @@ -61,8 +61,9 @@ impl Sandbox for Example { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum Language { + #[default] Rust, Elm, Ruby, @@ -84,12 +85,6 @@ impl Language { ]; } -impl Default for Language { - fn default() -> Language { - Language::Rust - } -} - impl std::fmt::Display for Language { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 6a87f58c..6361667e 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -435,19 +435,16 @@ fn view_controls(tasks: &[Task], current_filter: Filter) -> Element { .into() } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize, +)] pub enum Filter { + #[default] All, Active, Completed, } -impl Default for Filter { - fn default() -> Self { - Filter::All - } -} - impl Filter { fn matches(&self, task: &Task) -> bool { match self { -- cgit From c407b4504cd5e7dcb04a8fd31ad0400c891fc3e1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 27 Mar 2023 15:51:32 +0200 Subject: Introduce `is_mouse_over_scrollbar` to `StyleSheet::hovered` for `Scrollable` --- examples/scrollable/src/main.rs | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 7c858961..f8c5aa74 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -339,22 +339,36 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle { style.active(&theme::Scrollable::Default) } - fn hovered(&self, style: &Self::Style) -> Scrollbar { - style.hovered(&theme::Scrollable::Default) + fn hovered( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> Scrollbar { + style.hovered(&theme::Scrollable::Default, is_mouse_over_scrollbar) } - fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar { - Scrollbar { - background: style.active(&theme::Scrollable::default()).background, - border_radius: 0.0, - border_width: 0.0, - border_color: Default::default(), - scroller: Scroller { - color: Color::from_rgb8(250, 85, 134), + fn hovered_horizontal( + &self, + style: &Self::Style, + is_mouse_over_scrollbar: bool, + ) -> Scrollbar { + if is_mouse_over_scrollbar { + Scrollbar { + background: style + .active(&theme::Scrollable::default()) + .background, border_radius: 0.0, border_width: 0.0, border_color: Default::default(), - }, + scroller: Scroller { + color: Color::from_rgb8(250, 85, 134), + border_radius: 0.0, + border_width: 0.0, + border_color: Default::default(), + }, + } + } else { + self.active(style) } } } -- cgit From 707de9d788dc3c49d4ac57a19afac1bb938b78d9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Mar 2023 00:56:00 +0200 Subject: Introduce support for `Font` attributes --- examples/checkbox/src/main.rs | 2 +- examples/todos/src/main.rs | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index 77111490..c5f3c134 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -3,7 +3,7 @@ use iced::font::{self, Font}; use iced::widget::{checkbox, column, container}; use iced::{Application, Command, Element, Length, Settings, Theme}; -const ICON_FONT: Font = Font::Name("icons"); +const ICON_FONT: Font = Font::with_name("icons"); pub fn main() -> iced::Result { Example::run(Settings::default()) diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 48b4f2f8..ed3684d3 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -392,10 +392,14 @@ impl Task { row![ text_input, - button(row![delete_icon(), "Delete"].spacing(10)) - .on_press(TaskMessage::Delete) - .padding(10) - .style(theme::Button::Destructive) + button( + row![delete_icon(), "Delete"] + .spacing(10) + .align_items(Alignment::Center) + ) + .on_press(TaskMessage::Delete) + .padding(10) + .style(theme::Button::Destructive) ] .spacing(20) .align_items(Alignment::Center) @@ -487,14 +491,13 @@ fn empty_message(message: &str) -> Element<'_, Message> { } // Fonts -const ICONS: Font = Font::Name("Iced-Todos-Icons"); +const ICONS: Font = Font::with_name("Iced-Todos-Icons"); fn icon(unicode: char) -> Text<'static> { text(unicode.to_string()) .font(ICONS) .width(20) .horizontal_alignment(alignment::Horizontal::Center) - .size(20) } fn edit_icon() -> Text<'static> { -- cgit From c0431aedd3bbef4161456f2fa5f29866e8f17fc5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 8 Apr 2023 04:47:05 +0200 Subject: Update `wgpu` and `cosmic-text` --- examples/integration/src/main.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 9707eda7..949a726a 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -26,18 +26,16 @@ use web_sys::HtmlCanvasElement; #[cfg(target_arch = "wasm32")] use winit::platform::web::WindowBuilderExtWebSys; -pub fn main() { +pub fn main() -> Result<(), Box> { #[cfg(target_arch = "wasm32")] let canvas_element = { - console_log::init_with_level(log::Level::Debug) - .expect("could not initialize logger"); + console_log::init_with_level(log::Level::Debug)?; std::panic::set_hook(Box::new(console_error_panic_hook::hook)); web_sys::window() .and_then(|win| win.document()) .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok()) - .expect("Canvas with id `iced_canvas` is missing") + .and_then(|element| element.dyn_into::().ok())? }; #[cfg(not(target_arch = "wasm32"))] env_logger::init(); @@ -48,8 +46,7 @@ pub fn main() { #[cfg(target_arch = "wasm32")] let window = winit::window::WindowBuilder::new() .with_canvas(Some(canvas_element)) - .build(&event_loop) - .expect("Failed to build winit window"); + .build(&event_loop)?; #[cfg(not(target_arch = "wasm32"))] let window = winit::window::Window::new(&event_loop).unwrap(); @@ -73,8 +70,11 @@ pub fn main() { let backend = wgpu::util::backend_bits_from_env().unwrap_or(default_backend); - let instance = wgpu::Instance::new(backend); - let surface = unsafe { instance.create_surface(&window) }; + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: backend, + ..Default::default() + }); + let surface = unsafe { instance.create_surface(&window) }?; let (format, (device, queue)) = futures::futures::executor::block_on(async { @@ -84,7 +84,7 @@ pub fn main() { Some(&surface), ) .await - .expect("No suitable GPU adapters found on the system!"); + .expect("Create adapter"); let adapter_features = adapter.features(); @@ -97,7 +97,8 @@ pub fn main() { ( surface - .get_supported_formats(&adapter) + .get_capabilities(&adapter) + .formats .first() .copied() .expect("Get preferred format"), @@ -125,6 +126,7 @@ pub fn main() { height: physical_size.height, present_mode: wgpu::PresentMode::AutoVsync, alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], }, ); @@ -220,7 +222,8 @@ pub fn main() { width: size.width, height: size.height, present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], }, ); -- cgit From 7b369842959511f17d5c27941fd0308484bff8ea Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 13 Feb 2023 11:38:05 +0100 Subject: feat: added handle to text_input --- examples/text_input/Cargo.toml | 9 ++++ examples/text_input/README.md | 10 ++++ examples/text_input/fonts/icons.ttf | Bin 0 -> 1612 bytes examples/text_input/src/main.rs | 93 ++++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+) create mode 100644 examples/text_input/Cargo.toml create mode 100644 examples/text_input/README.md create mode 100644 examples/text_input/fonts/icons.ttf create mode 100644 examples/text_input/src/main.rs (limited to 'examples') diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml new file mode 100644 index 00000000..5937ef8e --- /dev/null +++ b/examples/text_input/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "text_input" +version = "0.1.0" +authors = ["Casper Rogild Storm"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = ["debug"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md new file mode 100644 index 00000000..2b2d8059 --- /dev/null +++ b/examples/text_input/README.md @@ -0,0 +1,10 @@ +## TextInput + +A `TextInput` is a field that can be filled with text. + +You can run it with `cargo run`: +``` +cargo run --package text_input +``` + +[`main`]: src/main.rs diff --git a/examples/text_input/fonts/icons.ttf b/examples/text_input/fonts/icons.ttf new file mode 100644 index 00000000..bfe8a24b Binary files /dev/null and b/examples/text_input/fonts/icons.ttf differ diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs new file mode 100644 index 00000000..b25ed7e1 --- /dev/null +++ b/examples/text_input/src/main.rs @@ -0,0 +1,93 @@ +use iced::widget::{checkbox, column, container, text_input}; +use iced::{Element, Font, Length, Sandbox, Settings}; + +const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), +}; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +#[derive(Default)] +struct Example { + value: String, + is_showing_handle: bool, +} + +#[derive(Debug, Clone)] +enum Message { + Changed(String), + ToggleHandle(bool), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Self::default() + } + + fn title(&self) -> String { + String::from("Text Input - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::Changed(value) => self.value = value, + Message::ToggleHandle(_) => { + self.is_showing_handle = !self.is_showing_handle + } + } + } + + fn view(&self) -> Element { + let checkbox = + checkbox("Handle", self.is_showing_handle, Message::ToggleHandle) + .spacing(5) + .text_size(16); + + let mut text_input = + text_input("Placeholder", self.value.as_str(), Message::Changed); + + if self.is_showing_handle { + text_input = text_input.handle(text_input::Handle { + font: ICON_FONT, + text: String::from('\u{e900}'), + size: Some(18), + position: text_input::HandlePosition::Right, + }); + } + + let content = column!["What is blazing fast?", text_input, checkbox] + .width(Length::Units(200)) + .spacing(10); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } + + fn theme(&self) -> iced::Theme { + iced::Theme::default() + } + + fn style(&self) -> iced::theme::Application { + iced::theme::Application::default() + } + + fn scale_factor(&self) -> f64 { + 1.0 + } + + fn run(settings: Settings<()>) -> Result<(), iced::Error> + where + Self: 'static + Sized, + { + ::run(settings) + } +} -- cgit From d24a4a46895ed711ddfc3199a0445f0b69a812e4 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Thu, 16 Feb 2023 14:32:59 +0100 Subject: Changed `Handle` to `Icon` to be consistent --- examples/text_input/src/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'examples') diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs index b25ed7e1..e0ba1983 100644 --- a/examples/text_input/src/main.rs +++ b/examples/text_input/src/main.rs @@ -13,13 +13,13 @@ pub fn main() -> iced::Result { #[derive(Default)] struct Example { value: String, - is_showing_handle: bool, + is_showing_icon: bool, } #[derive(Debug, Clone)] enum Message { Changed(String), - ToggleHandle(bool), + ToggleIcon(bool), } impl Sandbox for Example { @@ -36,27 +36,27 @@ impl Sandbox for Example { fn update(&mut self, message: Message) { match message { Message::Changed(value) => self.value = value, - Message::ToggleHandle(_) => { - self.is_showing_handle = !self.is_showing_handle + Message::ToggleIcon(_) => { + self.is_showing_icon = !self.is_showing_icon } } } fn view(&self) -> Element { let checkbox = - checkbox("Handle", self.is_showing_handle, Message::ToggleHandle) + checkbox("Icon", self.is_showing_icon, Message::ToggleIcon) .spacing(5) .text_size(16); let mut text_input = text_input("Placeholder", self.value.as_str(), Message::Changed); - if self.is_showing_handle { - text_input = text_input.handle(text_input::Handle { + if self.is_showing_icon { + text_input = text_input.icon(text_input::Icon { font: ICON_FONT, - text: String::from('\u{e900}'), + code_point: '\u{e900}', size: Some(18), - position: text_input::HandlePosition::Right, + position: text_input::IconPosition::Right, }); } -- cgit From 898307e9ac8e11de275d7d4d58b93a6f24a1e800 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Mon, 20 Feb 2023 14:42:10 +0100 Subject: Removed text_input example in favor for Tour --- examples/text_input/Cargo.toml | 9 ---- examples/text_input/README.md | 10 ---- examples/text_input/fonts/icons.ttf | Bin 1612 -> 0 bytes examples/text_input/src/main.rs | 93 ------------------------------------ examples/tour/fonts/icons.ttf | Bin 0 -> 1612 bytes examples/tour/src/main.rs | 78 +++++++++++++++++++++++++----- 6 files changed, 66 insertions(+), 124 deletions(-) delete mode 100644 examples/text_input/Cargo.toml delete mode 100644 examples/text_input/README.md delete mode 100644 examples/text_input/fonts/icons.ttf delete mode 100644 examples/text_input/src/main.rs create mode 100644 examples/tour/fonts/icons.ttf (limited to 'examples') diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml deleted file mode 100644 index 5937ef8e..00000000 --- a/examples/text_input/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "text_input" -version = "0.1.0" -authors = ["Casper Rogild Storm"] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../..", features = ["debug"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md deleted file mode 100644 index 2b2d8059..00000000 --- a/examples/text_input/README.md +++ /dev/null @@ -1,10 +0,0 @@ -## TextInput - -A `TextInput` is a field that can be filled with text. - -You can run it with `cargo run`: -``` -cargo run --package text_input -``` - -[`main`]: src/main.rs diff --git a/examples/text_input/fonts/icons.ttf b/examples/text_input/fonts/icons.ttf deleted file mode 100644 index bfe8a24b..00000000 Binary files a/examples/text_input/fonts/icons.ttf and /dev/null differ diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs deleted file mode 100644 index e0ba1983..00000000 --- a/examples/text_input/src/main.rs +++ /dev/null @@ -1,93 +0,0 @@ -use iced::widget::{checkbox, column, container, text_input}; -use iced::{Element, Font, Length, Sandbox, Settings}; - -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; - -pub fn main() -> iced::Result { - Example::run(Settings::default()) -} - -#[derive(Default)] -struct Example { - value: String, - is_showing_icon: bool, -} - -#[derive(Debug, Clone)] -enum Message { - Changed(String), - ToggleIcon(bool), -} - -impl Sandbox for Example { - type Message = Message; - - fn new() -> Self { - Self::default() - } - - fn title(&self) -> String { - String::from("Text Input - Iced") - } - - fn update(&mut self, message: Message) { - match message { - Message::Changed(value) => self.value = value, - Message::ToggleIcon(_) => { - self.is_showing_icon = !self.is_showing_icon - } - } - } - - fn view(&self) -> Element { - let checkbox = - checkbox("Icon", self.is_showing_icon, Message::ToggleIcon) - .spacing(5) - .text_size(16); - - let mut text_input = - text_input("Placeholder", self.value.as_str(), Message::Changed); - - if self.is_showing_icon { - text_input = text_input.icon(text_input::Icon { - font: ICON_FONT, - code_point: '\u{e900}', - size: Some(18), - position: text_input::IconPosition::Right, - }); - } - - let content = column!["What is blazing fast?", text_input, checkbox] - .width(Length::Units(200)) - .spacing(10); - - container(content) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } - - fn theme(&self) -> iced::Theme { - iced::Theme::default() - } - - fn style(&self) -> iced::theme::Application { - iced::theme::Application::default() - } - - fn scale_factor(&self) -> f64 { - 1.0 - } - - fn run(settings: Settings<()>) -> Result<(), iced::Error> - where - Self: 'static + Sized, - { - ::run(settings) - } -} diff --git a/examples/tour/fonts/icons.ttf b/examples/tour/fonts/icons.ttf new file mode 100644 index 00000000..bfe8a24b Binary files /dev/null and b/examples/tour/fonts/icons.ttf differ diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index de063d00..5edee850 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -5,8 +5,14 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; +use iced::Font; use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), +}; + pub fn main() -> iced::Result { env_logger::init(); @@ -127,6 +133,7 @@ impl Steps { Step::TextInput { value: String::new(), is_secure: false, + is_showing_icon: false, }, Step::Debugger, Step::End, @@ -171,14 +178,32 @@ impl Steps { enum Step { Welcome, - Slider { value: u8 }, - RowsAndColumns { layout: Layout, spacing: u16 }, - Text { size: u16, color: Color }, - Radio { selection: Option }, - Toggler { can_continue: bool }, - Image { width: u16 }, + Slider { + value: u8, + }, + RowsAndColumns { + layout: Layout, + spacing: u16, + }, + Text { + size: u16, + color: Color, + }, + Radio { + selection: Option, + }, + Toggler { + can_continue: bool, + }, + Image { + width: u16, + }, Scrollable, - TextInput { value: String, is_secure: bool }, + TextInput { + value: String, + is_secure: bool, + is_showing_icon: bool, + }, Debugger, End, } @@ -194,6 +219,7 @@ pub enum StepMessage { ImageWidthChanged(u16), InputChanged(String), ToggleSecureInput(bool), + ToggleTextInputIcon(bool), DebugToggled(bool), TogglerChanged(bool), } @@ -256,6 +282,14 @@ impl<'a> Step { *can_continue = value; } } + StepMessage::ToggleTextInputIcon(toggle) => { + if let Step::TextInput { + is_showing_icon, .. + } = self + { + *is_showing_icon = toggle + } + } }; } @@ -303,9 +337,11 @@ impl<'a> Step { Self::rows_and_columns(*layout, *spacing) } Step::Scrollable => Self::scrollable(), - Step::TextInput { value, is_secure } => { - Self::text_input(value, *is_secure) - } + Step::TextInput { + value, + is_secure, + is_showing_icon, + } => Self::text_input(value, *is_secure, *is_showing_icon), Step::Debugger => Self::debugger(debug), Step::End => Self::end(), } @@ -530,8 +566,12 @@ impl<'a> Step { ) } - fn text_input(value: &str, is_secure: bool) -> Column<'a, StepMessage> { - let text_input = text_input( + fn text_input( + value: &str, + is_secure: bool, + is_showing_icon: bool, + ) -> Column<'a, StepMessage> { + let mut text_input = text_input( "Type something to continue...", value, StepMessage::InputChanged, @@ -539,6 +579,15 @@ impl<'a> Step { .padding(10) .size(30); + if is_showing_icon { + text_input = text_input.icon(text_input::Icon { + font: ICON_FONT, + code_point: '\u{e900}', + size: Some(35), + position: text_input::IconPosition::Right, + }); + } + Self::container("Text input") .push("Use a text input to ask for different kinds of information.") .push(if is_secure { @@ -551,6 +600,11 @@ impl<'a> Step { is_secure, StepMessage::ToggleSecureInput, )) + .push(checkbox( + "Show icon", + is_showing_icon, + StepMessage::ToggleTextInputIcon, + )) .push( "A text input produces a message every time it changes. It is \ very easy to keep track of its contents:", -- cgit From 0e2fc99eb864800d2d1522c015054d84cad078f4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:13:56 +0200 Subject: Use `f32` for `Icon::size` and remove unnecessary conversions --- examples/tour/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 5edee850..6a1380d7 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -583,7 +583,7 @@ impl<'a> Step { text_input = text_input.icon(text_input::Icon { font: ICON_FONT, code_point: '\u{e900}', - size: Some(35), + size: Some(35.0), position: text_input::IconPosition::Right, }); } -- cgit From 9852b4b36442ef036f0b308f798e892ddaa06c2d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:45:55 +0200 Subject: Move `Icon` layout logic to `layout` in `text_input` Also add `Icon::spacing` field. --- examples/tour/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 6a1380d7..8a1b8d5a 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -138,7 +138,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 0, + current: 8, } } @@ -582,8 +582,9 @@ impl<'a> Step { if is_showing_icon { text_input = text_input.icon(text_input::Icon { font: ICON_FONT, - code_point: '\u{e900}', + code_point: '\u{E900}', size: Some(35.0), + spacing: 10.0, position: text_input::IconPosition::Right, }); } -- cgit From cf9d8e01048845ee503a62eb55e634a76a0e9163 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:54:51 +0200 Subject: Rename `IconPosition` to `Side` in `text_input` --- examples/tour/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 8a1b8d5a..40ab33c2 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -585,7 +585,7 @@ impl<'a> Step { code_point: '\u{E900}', size: Some(35.0), spacing: 10.0, - position: text_input::IconPosition::Right, + side: text_input::Side::Right, }); } -- cgit From c794d8ba788b388d4fb7a8ef1bba208b98e1bd02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:56:34 +0200 Subject: Collapse `Font` import in `tour` example --- examples/tour/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 40ab33c2..9ee386d4 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -5,8 +5,7 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::Font; -use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; const ICON_FONT: Font = Font::External { name: "Icons", -- cgit From aa0be30656e30d116e60a5d06557aea27442ce76 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:57:01 +0200 Subject: Move `ICON_FONT` constant in `tour` to `text_input` helper --- examples/tour/src/main.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 9ee386d4..e9b9fb3a 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -7,11 +7,6 @@ use iced::widget::{ use iced::widget::{Button, Column, Container, Slider}; use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; -const ICON_FONT: Font = Font::External { - name: "Icons", - bytes: include_bytes!("../fonts/icons.ttf"), -}; - pub fn main() -> iced::Result { env_logger::init(); @@ -570,6 +565,11 @@ impl<'a> Step { is_secure: bool, is_showing_icon: bool, ) -> Column<'a, StepMessage> { + const ICON_FONT: Font = Font::External { + name: "Icons", + bytes: include_bytes!("../fonts/icons.ttf"), + }; + let mut text_input = text_input( "Type something to continue...", value, -- cgit From 45015e37d4123f01b546337e741820975fccf66a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 05:57:31 +0200 Subject: Fix `current` step in `tour` --- examples/tour/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index e9b9fb3a..90868877 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -132,7 +132,7 @@ impl Steps { Step::Debugger, Step::End, ], - current: 8, + current: 0, } } -- cgit From ae7e6b3d481e420acf981fabbeff9745d4b5347d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 07:46:54 +0200 Subject: Implement `subscription::channel` and simplify `unfold` --- examples/download_progress/src/download.rs | 17 ++--- examples/websocket/src/echo.rs | 100 +++++++++++++++-------------- 2 files changed, 59 insertions(+), 58 deletions(-) (limited to 'examples') diff --git a/examples/download_progress/src/download.rs b/examples/download_progress/src/download.rs index 39dd843f..cd7647e8 100644 --- a/examples/download_progress/src/download.rs +++ b/examples/download_progress/src/download.rs @@ -18,10 +18,7 @@ pub struct Download { url: String, } -async fn download( - id: I, - state: State, -) -> (Option<(I, Progress)>, State) { +async fn download(id: I, state: State) -> ((I, Progress), State) { match state { State::Ready(url) => { let response = reqwest::get(&url).await; @@ -30,7 +27,7 @@ async fn download( Ok(response) => { if let Some(total) = response.content_length() { ( - Some((id, Progress::Started)), + (id, Progress::Started), State::Downloading { response, total, @@ -38,10 +35,10 @@ async fn download( }, ) } else { - (Some((id, Progress::Errored)), State::Finished) + ((id, Progress::Errored), State::Finished) } } - Err(_) => (Some((id, Progress::Errored)), State::Finished), + Err(_) => ((id, Progress::Errored), State::Finished), } } State::Downloading { @@ -55,7 +52,7 @@ async fn download( let percentage = (downloaded as f32 / total as f32) * 100.0; ( - Some((id, Progress::Advanced(percentage))), + (id, Progress::Advanced(percentage)), State::Downloading { response, total, @@ -63,8 +60,8 @@ async fn download( }, ) } - Ok(None) => (Some((id, Progress::Finished)), State::Finished), - Err(_) => (Some((id, Progress::Errored)), State::Finished), + Ok(None) => ((id, Progress::Finished), State::Finished), + Err(_) => ((id, Progress::Errored), State::Finished), }, State::Finished => { // We do not let the stream die, as it would start a diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index e74768a6..4fabb660 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -13,63 +13,67 @@ use std::fmt; pub fn connect() -> Subscription { struct Connect; - subscription::unfold( + subscription::channel( std::any::TypeId::of::(), - State::Disconnected, - |state| async move { - match state { - State::Disconnected => { - const ECHO_SERVER: &str = "ws://localhost:3030"; - - match async_tungstenite::tokio::connect_async(ECHO_SERVER) + 100, + |mut output| async move { + let mut state = State::Disconnected; + + loop { + match &mut state { + State::Disconnected => { + const ECHO_SERVER: &str = "ws://localhost:3030"; + + match async_tungstenite::tokio::connect_async( + ECHO_SERVER, + ) .await - { - Ok((websocket, _)) => { - let (sender, receiver) = mpsc::channel(100); - - ( - Some(Event::Connected(Connection(sender))), - State::Connected(websocket, receiver), - ) - } - Err(_) => { - tokio::time::sleep( - tokio::time::Duration::from_secs(1), - ) - .await; + { + Ok((websocket, _)) => { + let (sender, receiver) = mpsc::channel(100); + + let _ = output + .send(Event::Connected(Connection(sender))) + .await; - (Some(Event::Disconnected), State::Disconnected) + state = State::Connected(websocket, receiver); + } + Err(_) => { + tokio::time::sleep( + tokio::time::Duration::from_secs(1), + ) + .await; + + let _ = output.send(Event::Disconnected).await; + } } } - } - State::Connected(mut websocket, mut input) => { - let mut fused_websocket = websocket.by_ref().fuse(); - - futures::select! { - received = fused_websocket.select_next_some() => { - match received { - Ok(tungstenite::Message::Text(message)) => { - ( - Some(Event::MessageReceived(Message::User(message))), - State::Connected(websocket, input) - ) - } - Ok(_) => { - (None, State::Connected(websocket, input)) - } - Err(_) => { - (Some(Event::Disconnected), State::Disconnected) + State::Connected(websocket, input) => { + let mut fused_websocket = websocket.by_ref().fuse(); + + futures::select! { + received = fused_websocket.select_next_some() => { + match received { + Ok(tungstenite::Message::Text(message)) => { + let _ = output.send(Event::MessageReceived(Message::User(message))).await; + } + Err(_) => { + let _ = output.send(Event::Disconnected).await; + + state = State::Disconnected; + } + Ok(_) => continue, } } - } - message = input.select_next_some() => { - let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; + message = input.select_next_some() => { + let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; + + if !result.is_ok() { + let _ = output.send(Event::Disconnected).await; - if result.is_ok() { - (None, State::Connected(websocket, input)) - } else { - (Some(Event::Disconnected), State::Disconnected) + state = State::Disconnected; + } } } } -- cgit From 5908205a62eb160af745039b9ce0aa5329f984ba Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 07:48:30 +0200 Subject: Use `127.0.0.1` instead of `localhost` in `websocket` example --- examples/websocket/src/echo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index 4fabb660..f6c46e95 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -22,7 +22,7 @@ pub fn connect() -> Subscription { loop { match &mut state { State::Disconnected => { - const ECHO_SERVER: &str = "ws://localhost:3030"; + const ECHO_SERVER: &str = "ws://127.0.0.1:3030"; match async_tungstenite::tokio::connect_async( ECHO_SERVER, -- cgit From 0ed54346b0d6271cf5874debef1de57f8322a249 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 11 Apr 2023 07:53:26 +0200 Subject: Use `Result::is_err` in `websocket` example --- examples/websocket/src/echo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/websocket/src/echo.rs b/examples/websocket/src/echo.rs index f6c46e95..f9807172 100644 --- a/examples/websocket/src/echo.rs +++ b/examples/websocket/src/echo.rs @@ -69,7 +69,7 @@ pub fn connect() -> Subscription { message = input.select_next_some() => { let result = websocket.send(tungstenite::Message::Text(message.to_string())).await; - if !result.is_ok() { + if result.is_err() { let _ = output.send(Event::Disconnected).await; state = State::Disconnected; -- cgit From f10e936f00d4b83dcacfdebd2727b1a5cd1add95 Mon Sep 17 00:00:00 2001 From: Dan Mishin Date: Fri, 3 Mar 2023 10:01:49 +0300 Subject: Introduce disabled state for `TextInput` --- examples/component/src/main.rs | 2 +- examples/integration_wgpu/src/controls.rs | 9 ++- examples/lazy/src/main.rs | 9 +-- examples/modal/src/main.rs | 14 ++--- examples/qr_code/src/main.rs | 12 ++-- examples/styling/src/main.rs | 11 ++-- examples/text_input/Cargo.toml | 11 ++++ examples/text_input/README.md | 15 +++++ examples/text_input/src/main.rs | 94 +++++++++++++++++++++++++++++++ examples/toast/src/main.rs | 6 +- examples/todos/src/main.rs | 29 ++++------ examples/tour/src/main.rs | 11 ++-- examples/websocket/src/main.rs | 9 +-- 13 files changed, 166 insertions(+), 66 deletions(-) create mode 100644 examples/text_input/Cargo.toml create mode 100644 examples/text_input/README.md create mode 100644 examples/text_input/src/main.rs (limited to 'examples') diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index bbf549e7..8be3f076 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -141,8 +141,8 @@ mod numeric_input { .map(u32::to_string) .as_deref() .unwrap_or(""), - Event::InputChanged, ) + .on_change(Event::InputChanged) .padding(10), button("+", Event::IncrementPressed), ] diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 533cb6e2..8c42513f 100644 --- a/examples/integration_wgpu/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs @@ -100,11 +100,10 @@ impl Program for Controls { .size(14) .style(Color::WHITE), ) - .push(text_input( - "Placeholder", - text, - Message::TextChanged, - )), + .push( + text_input("Placeholder", text) + .on_change(Message::TextChanged), + ), ), ) .into() diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 6512106f..6b6dca26 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -214,12 +214,9 @@ impl Sandbox for App { column![ scrollable(options).height(Length::Fill), row![ - text_input( - "Add a new option", - &self.input, - Message::InputChanged, - ) - .on_submit(Message::AddItem(self.input.clone())), + text_input("Add a new option", &self.input) + .on_change(Message::InputChanged) + .on_submit(Message::AddItem(self.input.clone())), button(text(format!("Toggle Order ({})", self.order))) .on_press(Message::ToggleOrder) ] diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 54555684..1377f054 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -133,18 +133,16 @@ impl Application for App { column![ column![ text("Email").size(12), - text_input( - "abc@123.com", - &self.email, - Message::Email - ) - .on_submit(Message::Submit) - .padding(5), + text_input("abc@123.com", &self.email,) + .on_change(Message::Email) + .on_submit(Message::Submit) + .padding(5), ] .spacing(5), column![ text("Password").size(12), - text_input("", &self.password, Message::Password) + text_input("", &self.password) + .on_change(Message::Password) .on_submit(Message::Submit) .password() .padding(5), diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index d8041745..0486b068 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -49,13 +49,11 @@ impl Sandbox for QRGenerator { .size(70) .style(Color::from([0.5, 0.5, 0.5])); - let input = text_input( - "Type the data of your QR code here...", - &self.data, - Message::DataChanged, - ) - .size(30) - .padding(15); + let input = + text_input("Type the data of your QR code here...", &self.data) + .on_change(Message::DataChanged) + .size(30) + .padding(15); let mut content = column![title, input] .width(700) diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 448c9792..58f983df 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -90,13 +90,10 @@ impl Sandbox for Styling { }, ); - let text_input = text_input( - "Type something...", - &self.input_value, - Message::InputChanged, - ) - .padding(10) - .size(20); + let text_input = text_input("Type something...", &self.input_value) + .on_change(Message::InputChanged) + .padding(10) + .size(20); let button = button("Submit") .padding(10) diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml new file mode 100644 index 00000000..aead9896 --- /dev/null +++ b/examples/text_input/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "text_input" +authors = ["Dan Mishin "] +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +iced = { path = "../..", features = ["tokio"] } +tokio = { version = "1.26.0", features = ["time"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md new file mode 100644 index 00000000..435989cc --- /dev/null +++ b/examples/text_input/README.md @@ -0,0 +1,15 @@ +# Text Input + +This example shows basic usage of text edit. +The button delays the change of the text field state to allow testing of the corner cases. + + + +You can run it with cargo run: +```bash +cargo run --package text_input +``` \ No newline at end of file diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs new file mode 100644 index 00000000..977b5099 --- /dev/null +++ b/examples/text_input/src/main.rs @@ -0,0 +1,94 @@ +use crate::Message::{StartTimer, TextEditModeChange}; +use iced::widget::{button, column, container, row, text, text_input}; +use iced::{ + executor, window, Application, Command, Element, Length, Renderer, + Settings, Theme, +}; +use tokio::time::{sleep, Duration}; + +fn main() -> iced::Result { + let settings = Settings { + window: window::Settings { + size: (700, 100), + ..window::Settings::default() + }, + ..Settings::default() + }; + + Example::run(settings) +} + +#[derive(Default)] +struct Example { + data: String, + text_edit_enabled: bool, +} + +#[derive(Debug, Clone)] +enum Message { + StartTimer, + TextEditModeChange, + TextInputChanged(String), +} + +impl Application for Example { + type Executor = executor::Default; + type Message = Message; + type Theme = Theme; + type Flags = (); + + fn new(_flags: Self::Flags) -> (Self, Command) { + (Self::default(), Command::none()) + } + + fn title(&self) -> String { + "TextInput example".into() + } + + fn update(&mut self, message: Self::Message) -> Command { + match message { + Message::TextEditModeChange => { + self.text_edit_enabled = !self.text_edit_enabled; + Command::none() + } + Message::TextInputChanged(updated_text) => { + self.data = updated_text; + Command::none() + } + StartTimer => { + let timer_f = async { + sleep(Duration::from_secs(3)).await; + }; + Command::perform(timer_f, |_| TextEditModeChange) + } + } + } + + fn view(&self) -> Element<'_, Self::Message, Renderer> { + let placeholder = if self.text_edit_enabled { + "Enabled TextEdit" + } else { + "Disabled TextEdit" + }; + + let mut txt_input = text_input(placeholder, &self.data); + + if self.text_edit_enabled { + txt_input = txt_input.on_change(Message::TextInputChanged); + } + + let btn = button("Enable/Disable").on_press(StartTimer); + let label = text( + "The mode will be changed after 3s when the button is pressed", + ); + + let content = row![txt_input, btn].spacing(10); + let content = column![content, label].spacing(10); + + container(content) + .width(Length::Shrink) + .height(Length::Shrink) + .padding(20) + .into() + } +} diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index e74b3ee6..765afb8f 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -119,13 +119,15 @@ impl Application for App { column![ subtitle( "Title", - text_input("", &self.editing.title, Message::Title) + text_input("", &self.editing.title) + .on_change(Message::Title) .on_submit(Message::Add) .into() ), subtitle( "Message", - text_input("", &self.editing.body, Message::Body) + text_input("", &self.editing.body) + .on_change(Message::Body) .on_submit(Message::Add) .into() ), diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 6361667e..a6670626 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -204,15 +204,12 @@ impl Application for Todos { .style(Color::from([0.5, 0.5, 0.5])) .horizontal_alignment(alignment::Horizontal::Center); - let input = text_input( - "What needs to be done?", - input_value, - Message::InputChanged, - ) - .id(INPUT_ID.clone()) - .padding(15) - .size(30) - .on_submit(Message::CreateTask); + let input = text_input("What needs to be done?", input_value) + .on_change(Message::InputChanged) + .id(INPUT_ID.clone()) + .padding(15) + .size(30) + .on_submit(Message::CreateTask); let controls = view_controls(tasks, *filter); let filtered_tasks = @@ -375,14 +372,12 @@ impl Task { .into() } TaskState::Editing => { - let text_input = text_input( - "Describe your task...", - &self.description, - TaskMessage::DescriptionEdited, - ) - .id(Self::text_input_id(i)) - .on_submit(TaskMessage::FinishEdition) - .padding(10); + let text_input = + text_input("Describe your task...", &self.description) + .id(Self::text_input_id(i)) + .on_change(TaskMessage::DescriptionEdited) + .on_submit(TaskMessage::FinishEdition) + .padding(10); row![ text_input, diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 90868877..3b4cfd2d 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -570,13 +570,10 @@ impl<'a> Step { bytes: include_bytes!("../fonts/icons.ttf"), }; - let mut text_input = text_input( - "Type something to continue...", - value, - StepMessage::InputChanged, - ) - .padding(10) - .size(30); + let mut text_input = text_input("Type something to continue...", value) + .on_change(StepMessage::InputChanged) + .padding(10) + .size(30); if is_showing_icon { text_input = text_input.icon(text_input::Icon { diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index e617b8ce..1fda7f18 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -125,12 +125,9 @@ impl Application for WebSocket { }; let new_message_input = { - let mut input = text_input( - "Type a message...", - &self.new_message, - Message::NewMessageChanged, - ) - .padding(10); + let mut input = text_input("Type a message...", &self.new_message) + .on_change(Message::NewMessageChanged) + .padding(10); let mut button = button( text("Send") -- cgit From e6a93e960c3ac71a74f11555fd2d225c185f6e8c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 12 Apr 2023 04:13:36 +0200 Subject: Rename `on_change` to `on_input` for `TextInput` --- examples/component/src/main.rs | 2 +- examples/integration_wgpu/src/controls.rs | 2 +- examples/lazy/src/main.rs | 2 +- examples/modal/src/main.rs | 4 ++-- examples/qr_code/src/main.rs | 2 +- examples/styling/src/main.rs | 2 +- examples/text_input/src/main.rs | 2 +- examples/toast/src/main.rs | 4 ++-- examples/todos/src/main.rs | 8 ++++---- examples/tour/src/main.rs | 2 +- examples/websocket/src/main.rs | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) (limited to 'examples') diff --git a/examples/component/src/main.rs b/examples/component/src/main.rs index 8be3f076..21c2747c 100644 --- a/examples/component/src/main.rs +++ b/examples/component/src/main.rs @@ -142,7 +142,7 @@ mod numeric_input { .as_deref() .unwrap_or(""), ) - .on_change(Event::InputChanged) + .on_input(Event::InputChanged) .padding(10), button("+", Event::IncrementPressed), ] diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 8c42513f..42623b15 100644 --- a/examples/integration_wgpu/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs @@ -102,7 +102,7 @@ impl Program for Controls { ) .push( text_input("Placeholder", text) - .on_change(Message::TextChanged), + .on_input(Message::TextChanged), ), ), ) diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs index 6b6dca26..55b13bcf 100644 --- a/examples/lazy/src/main.rs +++ b/examples/lazy/src/main.rs @@ -215,7 +215,7 @@ impl Sandbox for App { scrollable(options).height(Length::Fill), row![ text_input("Add a new option", &self.input) - .on_change(Message::InputChanged) + .on_input(Message::InputChanged) .on_submit(Message::AddItem(self.input.clone())), button(text(format!("Toggle Order ({})", self.order))) .on_press(Message::ToggleOrder) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 1377f054..49038475 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -134,7 +134,7 @@ impl Application for App { column![ text("Email").size(12), text_input("abc@123.com", &self.email,) - .on_change(Message::Email) + .on_input(Message::Email) .on_submit(Message::Submit) .padding(5), ] @@ -142,7 +142,7 @@ impl Application for App { column![ text("Password").size(12), text_input("", &self.password) - .on_change(Message::Password) + .on_input(Message::Password) .on_submit(Message::Submit) .password() .padding(5), diff --git a/examples/qr_code/src/main.rs b/examples/qr_code/src/main.rs index 0486b068..867ebfa4 100644 --- a/examples/qr_code/src/main.rs +++ b/examples/qr_code/src/main.rs @@ -51,7 +51,7 @@ impl Sandbox for QRGenerator { let input = text_input("Type the data of your QR code here...", &self.data) - .on_change(Message::DataChanged) + .on_input(Message::DataChanged) .size(30) .padding(15); diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 58f983df..e2015bac 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -91,7 +91,7 @@ impl Sandbox for Styling { ); let text_input = text_input("Type something...", &self.input_value) - .on_change(Message::InputChanged) + .on_input(Message::InputChanged) .padding(10) .size(20); diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs index 977b5099..418593f4 100644 --- a/examples/text_input/src/main.rs +++ b/examples/text_input/src/main.rs @@ -74,7 +74,7 @@ impl Application for Example { let mut txt_input = text_input(placeholder, &self.data); if self.text_edit_enabled { - txt_input = txt_input.on_change(Message::TextInputChanged); + txt_input = txt_input.on_input(Message::TextInputChanged); } let btn = button("Enable/Disable").on_press(StartTimer); diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 765afb8f..b4b4e007 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -120,14 +120,14 @@ impl Application for App { subtitle( "Title", text_input("", &self.editing.title) - .on_change(Message::Title) + .on_input(Message::Title) .on_submit(Message::Add) .into() ), subtitle( "Message", text_input("", &self.editing.body) - .on_change(Message::Body) + .on_input(Message::Body) .on_submit(Message::Add) .into() ), diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index a6670626..99cdb8f9 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -205,11 +205,11 @@ impl Application for Todos { .horizontal_alignment(alignment::Horizontal::Center); let input = text_input("What needs to be done?", input_value) - .on_change(Message::InputChanged) .id(INPUT_ID.clone()) + .on_input(Message::InputChanged) + .on_submit(Message::CreateTask) .padding(15) - .size(30) - .on_submit(Message::CreateTask); + .size(30); let controls = view_controls(tasks, *filter); let filtered_tasks = @@ -375,7 +375,7 @@ impl Task { let text_input = text_input("Describe your task...", &self.description) .id(Self::text_input_id(i)) - .on_change(TaskMessage::DescriptionEdited) + .on_input(TaskMessage::DescriptionEdited) .on_submit(TaskMessage::FinishEdition) .padding(10); diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 3b4cfd2d..16ee19c0 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -571,7 +571,7 @@ impl<'a> Step { }; let mut text_input = text_input("Type something to continue...", value) - .on_change(StepMessage::InputChanged) + .on_input(StepMessage::InputChanged) .padding(10) .size(30); diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs index 1fda7f18..920189f5 100644 --- a/examples/websocket/src/main.rs +++ b/examples/websocket/src/main.rs @@ -126,7 +126,7 @@ impl Application for WebSocket { let new_message_input = { let mut input = text_input("Type a message...", &self.new_message) - .on_change(Message::NewMessageChanged) + .on_input(Message::NewMessageChanged) .padding(10); let mut button = button( -- cgit From 250ba3a7f1b41c7f7ca32b8db40a8c4069ebef77 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 12 Apr 2023 04:19:54 +0200 Subject: Remove `text_input` example --- examples/text_input/Cargo.toml | 11 ----- examples/text_input/README.md | 15 ------- examples/text_input/src/main.rs | 94 ----------------------------------------- 3 files changed, 120 deletions(-) delete mode 100644 examples/text_input/Cargo.toml delete mode 100644 examples/text_input/README.md delete mode 100644 examples/text_input/src/main.rs (limited to 'examples') diff --git a/examples/text_input/Cargo.toml b/examples/text_input/Cargo.toml deleted file mode 100644 index aead9896..00000000 --- a/examples/text_input/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "text_input" -authors = ["Dan Mishin "] -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -iced = { path = "../..", features = ["tokio"] } -tokio = { version = "1.26.0", features = ["time"] } diff --git a/examples/text_input/README.md b/examples/text_input/README.md deleted file mode 100644 index 435989cc..00000000 --- a/examples/text_input/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Text Input - -This example shows basic usage of text edit. -The button delays the change of the text field state to allow testing of the corner cases. - - - -You can run it with cargo run: -```bash -cargo run --package text_input -``` \ No newline at end of file diff --git a/examples/text_input/src/main.rs b/examples/text_input/src/main.rs deleted file mode 100644 index 418593f4..00000000 --- a/examples/text_input/src/main.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::Message::{StartTimer, TextEditModeChange}; -use iced::widget::{button, column, container, row, text, text_input}; -use iced::{ - executor, window, Application, Command, Element, Length, Renderer, - Settings, Theme, -}; -use tokio::time::{sleep, Duration}; - -fn main() -> iced::Result { - let settings = Settings { - window: window::Settings { - size: (700, 100), - ..window::Settings::default() - }, - ..Settings::default() - }; - - Example::run(settings) -} - -#[derive(Default)] -struct Example { - data: String, - text_edit_enabled: bool, -} - -#[derive(Debug, Clone)] -enum Message { - StartTimer, - TextEditModeChange, - TextInputChanged(String), -} - -impl Application for Example { - type Executor = executor::Default; - type Message = Message; - type Theme = Theme; - type Flags = (); - - fn new(_flags: Self::Flags) -> (Self, Command) { - (Self::default(), Command::none()) - } - - fn title(&self) -> String { - "TextInput example".into() - } - - fn update(&mut self, message: Self::Message) -> Command { - match message { - Message::TextEditModeChange => { - self.text_edit_enabled = !self.text_edit_enabled; - Command::none() - } - Message::TextInputChanged(updated_text) => { - self.data = updated_text; - Command::none() - } - StartTimer => { - let timer_f = async { - sleep(Duration::from_secs(3)).await; - }; - Command::perform(timer_f, |_| TextEditModeChange) - } - } - } - - fn view(&self) -> Element<'_, Self::Message, Renderer> { - let placeholder = if self.text_edit_enabled { - "Enabled TextEdit" - } else { - "Disabled TextEdit" - }; - - let mut txt_input = text_input(placeholder, &self.data); - - if self.text_edit_enabled { - txt_input = txt_input.on_input(Message::TextInputChanged); - } - - let btn = button("Enable/Disable").on_press(StartTimer); - let label = text( - "The mode will be changed after 3s when the button is pressed", - ); - - let content = row![txt_input, btn].spacing(10); - let content = column![content, label].spacing(10); - - container(content) - .width(Length::Shrink) - .height(Length::Shrink) - .padding(20) - .into() - } -} -- cgit From d5453c62e9bdbf0cea030b009c41b892b700496d Mon Sep 17 00:00:00 2001 From: Elham Aryanpur Date: Wed, 12 Apr 2023 23:38:21 +0300 Subject: Update `wgpu` to `0.15` --- examples/integration_wgpu/src/main.rs | 37 ++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'examples') diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 2a56b6fa..6e868332 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -23,18 +23,17 @@ use web_sys::HtmlCanvasElement; #[cfg(target_arch = "wasm32")] use winit::platform::web::WindowBuilderExtWebSys; -pub fn main() { +pub fn main() -> Result<(), Box> { #[cfg(target_arch = "wasm32")] let canvas_element = { - console_log::init_with_level(log::Level::Debug) - .expect("could not initialize logger"); + console_log::init_with_level(log::Level::Debug)?; + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); web_sys::window() .and_then(|win| win.document()) .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok()) - .expect("Canvas with id `iced_canvas` is missing") + .and_then(|element| element.dyn_into::().ok())? }; #[cfg(not(target_arch = "wasm32"))] env_logger::init(); @@ -45,11 +44,10 @@ pub fn main() { #[cfg(target_arch = "wasm32")] let window = winit::window::WindowBuilder::new() .with_canvas(Some(canvas_element)) - .build(&event_loop) - .expect("Failed to build winit window"); + .build(&event_loop)?; #[cfg(not(target_arch = "wasm32"))] - let window = winit::window::Window::new(&event_loop).unwrap(); + let window = winit::window::Window::new(&event_loop)?; let physical_size = window.inner_size(); let mut viewport = Viewport::with_physical_size( @@ -61,7 +59,6 @@ pub fn main() { let mut clipboard = Clipboard::connect(&window); // Initialize wgpu - #[cfg(target_arch = "wasm32")] let default_backend = wgpu::Backends::GL; #[cfg(not(target_arch = "wasm32"))] @@ -70,8 +67,12 @@ pub fn main() { let backend = wgpu::util::backend_bits_from_env().unwrap_or(default_backend); - let instance = wgpu::Instance::new(backend); - let surface = unsafe { instance.create_surface(&window) }; + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: backend, + ..Default::default() + }); + + let surface = unsafe { instance.create_surface(&window) }?; let (format, (device, queue)) = futures::executor::block_on(async { let adapter = wgpu::util::initialize_adapter_from_env_or_default( @@ -93,9 +94,15 @@ pub fn main() { ( surface - .get_supported_formats(&adapter) - .first() + .get_capabilities(&adapter) + .formats + .iter() + .filter(|format| format.describe().srgb) .copied() + .next() + .or_else(|| { + surface.get_capabilities(&adapter).formats.first().copied() + }) .expect("Get preferred format"), adapter .request_device( @@ -120,6 +127,7 @@ pub fn main() { height: physical_size.height, present_mode: wgpu::PresentMode::AutoVsync, alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![], }, ); @@ -214,7 +222,8 @@ pub fn main() { width: size.width, height: size.height, present_mode: wgpu::PresentMode::AutoVsync, - alpha_mode: wgpu::CompositeAlphaMode::Auto + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![] }, ); -- cgit From b677345ac1b1d087bc7f331c9c8c5be06933ba6e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 13 Apr 2023 05:42:56 +0200 Subject: Get surface capabilities only once in `iced_wgpu` --- examples/integration_wgpu/src/main.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 6e868332..8e0056f3 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -92,17 +92,16 @@ pub fn main() -> Result<(), Box> { #[cfg(not(target_arch = "wasm32"))] let needed_limits = wgpu::Limits::default(); + let capabilities = surface.get_capabilities(&adapter); + ( - surface - .get_capabilities(&adapter) + capabilities .formats .iter() .filter(|format| format.describe().srgb) .copied() .next() - .or_else(|| { - surface.get_capabilities(&adapter).formats.first().copied() - }) + .or_else(|| capabilities.formats.first().copied()) .expect("Get preferred format"), adapter .request_device( -- cgit From 9410fb98275fb51f67c55b676d8b37a5197222b3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 13 Apr 2023 05:46:18 +0200 Subject: Update `env_logger` in `integration_wgpu` example --- examples/integration_wgpu/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration_wgpu/Cargo.toml b/examples/integration_wgpu/Cargo.toml index eaa1df7e..c0e4fd81 100644 --- a/examples/integration_wgpu/Cargo.toml +++ b/examples/integration_wgpu/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] iced_winit = { path = "../../winit" } iced_wgpu = { path = "../../wgpu", features = ["webgl"] } -env_logger = "0.8" +env_logger = "0.10" [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.7" -- cgit From db4b899fd215f0607474006a9aef0361e74d481f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 13 Apr 2023 06:03:44 +0200 Subject: Fix Wasm target for `integration_wgpu` --- examples/integration_wgpu/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 8e0056f3..3ac458b3 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -33,7 +33,8 @@ pub fn main() -> Result<(), Box> { web_sys::window() .and_then(|win| win.document()) .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok())? + .and_then(|element| element.dyn_into::().ok()) + .expect("Canvas with id `iced_canvas` is missing") }; #[cfg(not(target_arch = "wasm32"))] env_logger::init(); -- cgit From b623f280ed43b04cef16a82de4cdf13497b5d63e Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Fri, 14 Apr 2023 13:32:44 -0700 Subject: Add `scroll_to` operation for absolute scroll --- examples/scrollable/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 2e99b1ac..be8d0f52 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -36,7 +36,7 @@ enum Message { ScrollerWidthChanged(u16), ScrollToBeginning, ScrollToEnd, - Scrolled(scrollable::RelativeOffset), + Scrolled(scrollable::CurrentOffset), } impl Application for ScrollableDemo { @@ -105,7 +105,7 @@ impl Application for ScrollableDemo { ) } Message::Scrolled(offset) => { - self.current_scroll_offset = offset; + self.current_scroll_offset = offset.relative; Command::none() } -- cgit From 6ad5e03d71db3dee174ac6512dceccc80e3a70a8 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Mon, 17 Apr 2023 13:55:40 -0700 Subject: Add scrollable `Viewport` --- examples/scrollable/src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index be8d0f52..97344c94 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -36,7 +36,7 @@ enum Message { ScrollerWidthChanged(u16), ScrollToBeginning, ScrollToEnd, - Scrolled(scrollable::CurrentOffset), + Scrolled(scrollable::Viewport), } impl Application for ScrollableDemo { @@ -104,8 +104,8 @@ impl Application for ScrollableDemo { self.current_scroll_offset, ) } - Message::Scrolled(offset) => { - self.current_scroll_offset = offset.relative; + Message::Scrolled(viewport) => { + self.current_scroll_offset = viewport.relative_offset(); Command::none() } -- cgit From 8122904ca46b73ceda54bd73bd68cf4138d22f1b Mon Sep 17 00:00:00 2001 From: David Huculak Date: Thu, 20 Apr 2023 21:28:47 -0400 Subject: wgpu 0.16 --- examples/integration_wgpu/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 3ac458b3..7b9f4d4c 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -72,7 +72,6 @@ pub fn main() -> Result<(), Box> { backends: backend, ..Default::default() }); - let surface = unsafe { instance.create_surface(&window) }?; let (format, (device, queue)) = futures::executor::block_on(async { @@ -99,7 +98,7 @@ pub fn main() -> Result<(), Box> { capabilities .formats .iter() - .filter(|format| format.describe().srgb) + .filter(|format| format.is_srgb()) .copied() .next() .or_else(|| capabilities.formats.first().copied()) -- cgit From 3f0c226b74c6d0271e550161b6542e652c1875ca Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 21 Apr 2023 21:36:30 +0200 Subject: Use point-free notation --- examples/integration_wgpu/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index 7b9f4d4c..f5fe7636 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -98,8 +98,8 @@ pub fn main() -> Result<(), Box> { capabilities .formats .iter() - .filter(|format| format.is_srgb()) .copied() + .filter(wgpu::TextureFormat::is_srgb) .next() .or_else(|| capabilities.formats.first().copied()) .expect("Get preferred format"), -- cgit From cc20baad6f422271f052cf68474a4aee40dcdc82 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 21 Apr 2023 21:46:02 +0200 Subject: Use `find(..)` instead of `filter(..).next()` --- examples/integration_wgpu/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs index f5fe7636..15901db8 100644 --- a/examples/integration_wgpu/src/main.rs +++ b/examples/integration_wgpu/src/main.rs @@ -99,8 +99,7 @@ pub fn main() -> Result<(), Box> { .formats .iter() .copied() - .filter(wgpu::TextureFormat::is_srgb) - .next() + .find(wgpu::TextureFormat::is_srgb) .or_else(|| capabilities.formats.first().copied()) .expect("Get preferred format"), adapter -- cgit From 88d3247717b6deb38d500c8c1b1b1f32d6df9991 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 27 Apr 2023 15:17:06 +0200 Subject: Fix build of `integration` example for Wasm target --- examples/integration/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index 98d2bc59..d9aae7b9 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -36,7 +36,8 @@ pub fn main() -> Result<(), Box> { web_sys::window() .and_then(|win| win.document()) .and_then(|doc| doc.get_element_by_id("iced_canvas")) - .and_then(|element| element.dyn_into::().ok())? + .and_then(|element| element.dyn_into::().ok()) + .expect("Get canvas element") }; #[cfg(not(target_arch = "wasm32"))] env_logger::init(); -- cgit From 4bd290afe7d81d9aaf7467b3ce91491f6600261a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 19 Apr 2023 02:00:45 +0200 Subject: Introduce `text::Shaping` enum and replace magic boolean --- examples/todos/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs index 8bc7be09..6ad7b4fb 100644 --- a/examples/todos/src/main.rs +++ b/examples/todos/src/main.rs @@ -364,7 +364,8 @@ impl Task { self.completed, TaskMessage::Completed, ) - .width(Length::Fill); + .width(Length::Fill) + .text_shaping(text::Shaping::Advanced); row![ checkbox, -- cgit From edf3432bf5176be13437b9fd5d25b890ec9dbe69 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 May 2023 00:58:33 +0200 Subject: Update `glyphon` and `cosmic-text` --- examples/checkbox/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index c5f3c134..5852e978 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -1,6 +1,6 @@ use iced::executor; use iced::font::{self, Font}; -use iced::widget::{checkbox, column, container}; +use iced::widget::{checkbox, column, container, text}; use iced::{Application, Command, Element, Length, Settings, Theme}; const ICON_FONT: Font = Font::with_name("icons"); @@ -59,6 +59,7 @@ impl Application for Example { font: ICON_FONT, code_point: '\u{e901}', size: None, + shaping: text::Shaping::Basic, }); let content = column![default_checkbox, custom_checkbox].spacing(22); -- cgit From 180cb073bdb1429e566839b09d6fe86114c83673 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 May 2023 16:19:28 +0200 Subject: Add `line_height` to `checkbox::Icon` --- examples/checkbox/src/main.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'examples') diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index 5852e978..ef61a974 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -59,6 +59,7 @@ impl Application for Example { font: ICON_FONT, code_point: '\u{e901}', size: None, + line_height: text::LineHeight::Relative(1.0), shaping: text::Shaping::Basic, }); -- cgit From dd04c0b070b60b15293892e2a7c284787d3d63b1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 10 May 2023 22:21:31 +0200 Subject: Bundle `tiny-skia` backend together with `iced_renderer` --- examples/integration/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml index 2ab5d75f..22914742 100644 --- a/examples/integration/Cargo.toml +++ b/examples/integration/Cargo.toml @@ -9,7 +9,7 @@ publish = false iced_winit = { path = "../../winit" } iced_wgpu = { path = "../../wgpu" } iced_widget = { path = "../../widget" } -iced_renderer = { path = "../../renderer", features = ["wgpu", "tiny-skia"] } +iced_renderer = { path = "../../renderer", features = ["wgpu"] } env_logger = "0.10" [target.'cfg(target_arch = "wasm32")'.dependencies] -- cgit From 63d3fc6996b848e10e77e6924bfebdf6ba82852e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 11 May 2023 15:50:35 +0200 Subject: Remove OpenGL mentions in `README`s --- examples/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/README.md b/examples/README.md index 74cf145b..111e8910 100644 --- a/examples/README.md +++ b/examples/README.md @@ -93,8 +93,7 @@ A bunch of simpler examples exist: - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`events`](events), a log of native events displayed using a conditional `Subscription`. - [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu). -- [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application. -- [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application. +- [`integration`](integration), a demonstration of how to integrate Iced in an existing [`wgpu`] application. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. - [`pick_list`](pick_list), a dropdown list of selectable options. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. -- cgit From 6551a0b2ab6c831dd1d3646ecf55180339275e22 Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 11 May 2023 09:12:06 -0700 Subject: Added support for gradients as background variants + other optimizations. --- examples/modern_art/Cargo.toml | 11 --- examples/modern_art/src/main.rs | 143 -------------------------------------- examples/solar_system/Cargo.toml | 1 + examples/solar_system/src/main.rs | 21 +++--- examples/tour/Cargo.toml | 2 +- examples/tour/src/main.rs | 58 +++++++++++++--- 6 files changed, 62 insertions(+), 174 deletions(-) delete mode 100644 examples/modern_art/Cargo.toml delete mode 100644 examples/modern_art/src/main.rs (limited to 'examples') diff --git a/examples/modern_art/Cargo.toml b/examples/modern_art/Cargo.toml deleted file mode 100644 index 4242d209..00000000 --- a/examples/modern_art/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "modern_art" -version = "0.1.0" -authors = ["Bingus "] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../..", features = ["canvas", "tokio", "debug"] } -rand = "0.8.5" -env_logger = "0.9" diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs deleted file mode 100644 index a43a2b2b..00000000 --- a/examples/modern_art/src/main.rs +++ /dev/null @@ -1,143 +0,0 @@ -use iced::widget::canvas::{ - self, gradient::Location, gradient::Position, Cache, Canvas, Cursor, Frame, - Geometry, Gradient, -}; -use iced::{ - executor, Application, Color, Command, Element, Length, Point, Rectangle, - Renderer, Settings, Size, Theme, -}; -use rand::{thread_rng, Rng}; - -fn main() -> iced::Result { - env_logger::builder().format_timestamp(None).init(); - - ModernArt::run(Settings { - antialiasing: true, - ..Settings::default() - }) -} - -#[derive(Debug, Clone, Copy)] -enum Message {} - -struct ModernArt { - cache: Cache, -} - -impl Application for ModernArt { - type Executor = executor::Default; - type Message = Message; - type Theme = Theme; - type Flags = (); - - fn new(_flags: Self::Flags) -> (Self, Command) { - ( - ModernArt { - cache: Default::default(), - }, - Command::none(), - ) - } - - fn title(&self) -> String { - String::from("Modern Art") - } - - fn update(&mut self, _message: Message) -> Command { - Command::none() - } - - fn view(&self) -> Element<'_, Self::Message, Renderer> { - Canvas::new(self) - .width(Length::Fill) - .height(Length::Fill) - .into() - } -} - -impl canvas::Program for ModernArt { - type State = (); - - fn draw( - &self, - _state: &Self::State, - renderer: &Renderer, - _theme: &Theme, - bounds: Rectangle, - _cursor: Cursor, - ) -> Vec { - let geometry = self.cache.draw(renderer, bounds.size(), |frame| { - let num_squares = thread_rng().gen_range(0..1200); - - let mut i = 0; - while i <= num_squares { - generate_box(frame, bounds.size()); - i += 1; - } - }); - - vec![geometry] - } -} - -fn random_direction() -> Location { - match thread_rng().gen_range(0..8) { - 0 => Location::TopLeft, - 1 => Location::Top, - 2 => Location::TopRight, - 3 => Location::Right, - 4 => Location::BottomRight, - 5 => Location::Bottom, - 6 => Location::BottomLeft, - 7 => Location::Left, - _ => Location::TopLeft, - } -} - -fn generate_box(frame: &mut Frame, bounds: Size) -> bool { - let solid = rand::random::(); - - let random_color = || -> Color { - Color::from_rgb( - thread_rng().gen_range(0.0..1.0), - thread_rng().gen_range(0.0..1.0), - thread_rng().gen_range(0.0..1.0), - ) - }; - - let gradient = |top_left: Point, size: Size| -> Gradient { - let mut builder = Gradient::linear(Position::Relative { - top_left, - size, - start: random_direction(), - end: random_direction(), - }); - let stops = thread_rng().gen_range(1..15u32); - - let mut i = 0; - while i <= stops { - builder = builder.add_stop(i as f32 / stops as f32, random_color()); - i += 1; - } - - builder.build().unwrap() - }; - - let top_left = Point::new( - thread_rng().gen_range(0.0..bounds.width), - thread_rng().gen_range(0.0..bounds.height), - ); - - let size = Size::new( - thread_rng().gen_range(50.0..200.0), - thread_rng().gen_range(50.0..200.0), - ); - - if solid { - frame.fill_rectangle(top_left, size, random_color()); - } else { - frame.fill_rectangle(top_left, size, gradient(top_left, size)); - }; - - solid -} diff --git a/examples/solar_system/Cargo.toml b/examples/solar_system/Cargo.toml index 835396b0..1a98a87e 100644 --- a/examples/solar_system/Cargo.toml +++ b/examples/solar_system/Cargo.toml @@ -7,4 +7,5 @@ publish = false [dependencies] iced = { path = "../..", features = ["canvas", "tokio", "debug"] } +env_logger = "0.10.0" rand = "0.8.3" diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index f2606feb..42606e3f 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -10,9 +10,8 @@ use iced::application; use iced::executor; use iced::theme::{self, Theme}; use iced::widget::canvas; -use iced::widget::canvas::gradient::{self, Gradient}; use iced::widget::canvas::stroke::{self, Stroke}; -use iced::widget::canvas::{Cursor, Path}; +use iced::widget::canvas::{Cursor, Gradient, Path}; use iced::window; use iced::{ Application, Color, Command, Element, Length, Point, Rectangle, Renderer, @@ -22,6 +21,8 @@ use iced::{ use std::time::Instant; pub fn main() -> iced::Result { + env_logger::builder().format_timestamp(None).init(); + SolarSystem::run(Settings { antialiasing: true, ..Settings::default() @@ -208,15 +209,13 @@ impl canvas::Program for State { let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS); - let earth_fill = - Gradient::linear(gradient::Position::Absolute { - start: Point::new(-Self::EARTH_RADIUS, 0.0), - end: Point::new(Self::EARTH_RADIUS, 0.0), - }) - .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) - .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)) - .build() - .expect("Build Earth fill gradient"); + let earth_fill = Gradient::linear( + Point::new(-Self::EARTH_RADIUS, 0.0), + Point::new(Self::EARTH_RADIUS, 0.0), + ) + .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) + .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)) + .build(); frame.fill(&earth, earth_fill); diff --git a/examples/tour/Cargo.toml b/examples/tour/Cargo.toml index 39e83671..48471f2d 100644 --- a/examples/tour/Cargo.toml +++ b/examples/tour/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] iced = { path = "../..", features = ["image", "debug"] } -env_logger = "0.8" +env_logger = "0.10.0" diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 9c38ad0e..630b6359 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,11 +1,15 @@ -use iced::alignment; use iced::theme; +use iced::theme::Palette; use iced::widget::{ checkbox, column, container, horizontal_space, image, radio, row, scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; +use iced::{alignment, widget, Theme}; +use iced::{ + Color, Degrees, Element, Font, Gradient, Length, Radians, Renderer, + Sandbox, Settings, +}; pub fn main() -> iced::Result { env_logger::init(); @@ -53,9 +57,11 @@ impl Sandbox for Tour { if steps.has_previous() { controls = controls.push( - button("Back") - .on_press(Message::BackPressed) - .style(theme::Button::Secondary), + button("Back").on_press(Message::BackPressed).style( + theme::Button::Custom(Box::new( + CustomButtonStyle::Secondary, + )), + ), ); } @@ -63,9 +69,9 @@ impl Sandbox for Tour { if steps.can_continue() { controls = controls.push( - button("Next") - .on_press(Message::NextPressed) - .style(theme::Button::Primary), + button("Next").on_press(Message::NextPressed).style( + theme::Button::Custom(Box::new(CustomButtonStyle::Primary)), + ), ); } @@ -716,3 +722,39 @@ pub enum Layout { Row, Column, } + +enum CustomButtonStyle { + Primary, + Secondary, +} + +impl widget::button::StyleSheet for CustomButtonStyle { + type Style = Theme; + + fn active(&self, _style: &Self::Style) -> widget::button::Appearance { + match self { + CustomButtonStyle::Primary => widget::button::Appearance { + background: Gradient::linear(Degrees(270.0)) + .add_stop(0.0, Palette::LIGHT.primary) + .add_stop(1.0, Color::from_rgb8(54, 80, 168)) + .build() + .into(), + text_color: Color::WHITE, + border_radius: 5.0, + ..Default::default() + }, + CustomButtonStyle::Secondary => widget::button::Appearance { + background: Gradient::linear(Radians( + 3.0 * std::f32::consts::PI / 2.0, + )) + .add_stop(0.0, Color::from_rgb8(194, 194, 194)) + .add_stop(1.0, Color::from_rgb8(126, 126, 126)) + .build() + .into(), + text_color: Color::WHITE, + border_radius: 5.0, + ..Default::default() + }, + } + } +} -- cgit From 99aa54cd88d7eb99149699d539ee4d59e08047b1 Mon Sep 17 00:00:00 2001 From: Joao Freitas <51237625+jhff@users.noreply.github.com> Date: Tue, 16 May 2023 16:12:29 +0100 Subject: Add pane_grid functionality to split a pane with another pane --- examples/pane_grid/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index dfb80853..d670e1e8 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -108,8 +108,12 @@ impl Application for Example { Message::Dragged(pane_grid::DragEvent::Dropped { pane, target, + region, }) => { - self.panes.swap(&pane, &target); + if let Some(state) = self.panes.get(&pane) { + let pane = (*state, &pane); + self.panes.split_with(&target, pane, region); + } } Message::Dragged(_) => {} Message::TogglePin(pane) => { @@ -255,6 +259,7 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option { } } +#[derive(Clone, Copy)] struct Pane { id: usize, pub is_pinned: bool, -- cgit From 4c1a082f0468a59099bbf8aa8991420a41234948 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 19 May 2023 03:32:21 +0200 Subject: Remove `Builder` abstractions for gradients --- examples/solar_system/src/main.rs | 8 ++++---- examples/toast/src/main.rs | 2 +- examples/tour/src/main.rs | 30 ++++++++++++++++-------------- 3 files changed, 21 insertions(+), 19 deletions(-) (limited to 'examples') diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 42606e3f..d9e660d7 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -10,8 +10,9 @@ use iced::application; use iced::executor; use iced::theme::{self, Theme}; use iced::widget::canvas; +use iced::widget::canvas::gradient; use iced::widget::canvas::stroke::{self, Stroke}; -use iced::widget::canvas::{Cursor, Gradient, Path}; +use iced::widget::canvas::{Cursor, Path}; use iced::window; use iced::{ Application, Color, Command, Element, Length, Point, Rectangle, Renderer, @@ -209,13 +210,12 @@ impl canvas::Program for State { let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS); - let earth_fill = Gradient::linear( + let earth_fill = gradient::Linear::new( Point::new(-Self::EARTH_RADIUS, 0.0), Point::new(Self::EARTH_RADIUS, 0.0), ) .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) - .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)) - .build(); + .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)); frame.fill(&earth, earth_fill); diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 9d859258..515218e7 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -226,7 +226,7 @@ mod toast { }; container::Appearance { - background: pair.color.into(), + background: Some(pair.color.into()), text_color: pair.text.into(), ..Default::default() } diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 630b6359..a40f0f33 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,3 +1,4 @@ +use iced::gradient; use iced::theme; use iced::theme::Palette; use iced::widget::{ @@ -7,8 +8,7 @@ use iced::widget::{ use iced::widget::{Button, Column, Container, Slider}; use iced::{alignment, widget, Theme}; use iced::{ - Color, Degrees, Element, Font, Gradient, Length, Radians, Renderer, - Sandbox, Settings, + Color, Degrees, Element, Font, Length, Radians, Renderer, Sandbox, Settings, }; pub fn main() -> iced::Result { @@ -734,23 +734,25 @@ impl widget::button::StyleSheet for CustomButtonStyle { fn active(&self, _style: &Self::Style) -> widget::button::Appearance { match self { CustomButtonStyle::Primary => widget::button::Appearance { - background: Gradient::linear(Degrees(270.0)) - .add_stop(0.0, Palette::LIGHT.primary) - .add_stop(1.0, Color::from_rgb8(54, 80, 168)) - .build() - .into(), + background: Some( + gradient::Linear::new(Degrees(270.0)) + .add_stop(0.0, Palette::LIGHT.primary) + .add_stop(1.0, Color::from_rgb8(54, 80, 168)) + .into(), + ), text_color: Color::WHITE, border_radius: 5.0, ..Default::default() }, CustomButtonStyle::Secondary => widget::button::Appearance { - background: Gradient::linear(Radians( - 3.0 * std::f32::consts::PI / 2.0, - )) - .add_stop(0.0, Color::from_rgb8(194, 194, 194)) - .add_stop(1.0, Color::from_rgb8(126, 126, 126)) - .build() - .into(), + background: Some( + gradient::Linear::new(Radians( + 3.0 * std::f32::consts::PI / 2.0, + )) + .add_stop(0.0, Color::from_rgb8(194, 194, 194)) + .add_stop(1.0, Color::from_rgb8(126, 126, 126)) + .into(), + ), text_color: Color::WHITE, border_radius: 5.0, ..Default::default() -- cgit From 96aa0379d58ff799493097e3bd0572f9a87da453 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 19 May 2023 03:43:11 +0200 Subject: Implement `custom` helper for `theme::Button` --- examples/tour/src/main.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index a40f0f33..257a4def 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -57,11 +57,9 @@ impl Sandbox for Tour { if steps.has_previous() { controls = controls.push( - button("Back").on_press(Message::BackPressed).style( - theme::Button::Custom(Box::new( - CustomButtonStyle::Secondary, - )), - ), + button("Back") + .on_press(Message::BackPressed) + .style(theme::Button::custom(CustomButtonStyle::Secondary)), ); } @@ -69,9 +67,9 @@ impl Sandbox for Tour { if steps.can_continue() { controls = controls.push( - button("Next").on_press(Message::NextPressed).style( - theme::Button::Custom(Box::new(CustomButtonStyle::Primary)), - ), + button("Next") + .on_press(Message::NextPressed) + .style(theme::Button::custom(CustomButtonStyle::Primary)), ); } -- cgit From 10a8c599e62e46dad4da0e53e184861047ad34c0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 19 May 2023 03:47:00 +0200 Subject: Keep `tour` buttons solid The gradients feel a bit out of place currently. --- examples/tour/src/main.rs | 55 +++++------------------------------------------ 1 file changed, 5 insertions(+), 50 deletions(-) (limited to 'examples') diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 257a4def..13bcd5ff 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,15 +1,11 @@ -use iced::gradient; +use iced::alignment; use iced::theme; -use iced::theme::Palette; use iced::widget::{ checkbox, column, container, horizontal_space, image, radio, row, scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::{alignment, widget, Theme}; -use iced::{ - Color, Degrees, Element, Font, Length, Radians, Renderer, Sandbox, Settings, -}; +use iced::{Color, Element, Font, Length, Renderer, Sandbox, Settings}; pub fn main() -> iced::Result { env_logger::init(); @@ -59,18 +55,15 @@ impl Sandbox for Tour { controls = controls.push( button("Back") .on_press(Message::BackPressed) - .style(theme::Button::custom(CustomButtonStyle::Secondary)), + .style(theme::Button::Secondary), ); } controls = controls.push(horizontal_space(Length::Fill)); if steps.can_continue() { - controls = controls.push( - button("Next") - .on_press(Message::NextPressed) - .style(theme::Button::custom(CustomButtonStyle::Primary)), - ); + controls = + controls.push(button("Next").on_press(Message::NextPressed)); } let content: Element<_> = column![ @@ -720,41 +713,3 @@ pub enum Layout { Row, Column, } - -enum CustomButtonStyle { - Primary, - Secondary, -} - -impl widget::button::StyleSheet for CustomButtonStyle { - type Style = Theme; - - fn active(&self, _style: &Self::Style) -> widget::button::Appearance { - match self { - CustomButtonStyle::Primary => widget::button::Appearance { - background: Some( - gradient::Linear::new(Degrees(270.0)) - .add_stop(0.0, Palette::LIGHT.primary) - .add_stop(1.0, Color::from_rgb8(54, 80, 168)) - .into(), - ), - text_color: Color::WHITE, - border_radius: 5.0, - ..Default::default() - }, - CustomButtonStyle::Secondary => widget::button::Appearance { - background: Some( - gradient::Linear::new(Radians( - 3.0 * std::f32::consts::PI / 2.0, - )) - .add_stop(0.0, Color::from_rgb8(194, 194, 194)) - .add_stop(1.0, Color::from_rgb8(126, 126, 126)) - .into(), - ), - text_color: Color::WHITE, - border_radius: 5.0, - ..Default::default() - }, - } - } -} -- cgit From bc590e2d6f3a39b09b11928c829f05d7bc9f9211 Mon Sep 17 00:00:00 2001 From: Joao Freitas <51237625+jhff@users.noreply.github.com> Date: Fri, 19 May 2023 12:12:08 +0100 Subject: Take pane state internally --- examples/pane_grid/src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index d670e1e8..2ffdcc69 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -110,10 +110,7 @@ impl Application for Example { target, region, }) => { - if let Some(state) = self.panes.get(&pane) { - let pane = (*state, &pane); - self.panes.split_with(&target, pane, region); - } + self.panes.split_with(&target, &pane, region); } Message::Dragged(_) => {} Message::TogglePin(pane) => { -- cgit From d20493c8a0cc6e2e250434ed4cb6f350ed39d7e8 Mon Sep 17 00:00:00 2001 From: Marien Zwart Date: Sun, 21 May 2023 23:41:26 +1000 Subject: Support conversion from Fn trait to custom theme ...instead of just from function pointers. I'm making this change not because I actually want to pass a closure, but to make passing a single fixed function work. This commit also simplifies the scrollable example slightly, and without the other half of this change that simplified example fails to compile with: ``` error[E0277]: the trait bound `iced::theme::ProgressBar: From fn(&'a Theme) -> iced::widget::progress_bar::Appearance {progress_bar_custom_style}>` is not satisfied --> examples/scrollable/src/main.rs:292:28 | 292 | .style(progress_bar_custom_style) | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From fn(&'a Theme) -> iced::widget::progress_bar::Appearance {progress_bar_custom_style}>` is not implemented for `iced::theme::ProgressBar` | | | required by a bound introduced by this call | = help: the trait `From fn(&'a Theme) -> iced::widget::progress_bar::Appearance>` is implemented for `iced::theme::ProgressBar` = note: required for `for<'a> fn(&'a Theme) -> iced::widget::progress_bar::Appearance {progress_bar_custom_style}` to implement `Into` note: required by a bound in `iced::widget::ProgressBar::::style` --> /home/marienz/src/iced/widget/src/progress_bar.rs:77:21 | 77 | style: impl Into<::Style>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `ProgressBar::::style` ``` This happens because `progress_bar_custom_style` by itself is a function item, which is typically coerced to a function pointer when one is needed, but not in this case. It is possible to work around this on the caller's side, but especially since the compiler diagnostic for this is a bit rough (see https://github.com/rust-lang/rust/issues/100116) let's try to make it work out of the box. --- examples/scrollable/src/main.rs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 97344c94..9d57cb94 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -289,18 +289,13 @@ impl Application for ScrollableDemo { } Direction::Horizontal => { progress_bar(0.0..=1.0, self.current_scroll_offset.x) - .style(theme::ProgressBar::Custom(Box::new( - ProgressBarCustomStyle, - ))) + .style(progress_bar_custom_style) .into() } Direction::Multi => column![ progress_bar(0.0..=1.0, self.current_scroll_offset.y), - progress_bar(0.0..=1.0, self.current_scroll_offset.x).style( - theme::ProgressBar::Custom(Box::new( - ProgressBarCustomStyle, - )) - ) + progress_bar(0.0..=1.0, self.current_scroll_offset.x) + .style(progress_bar_custom_style) ] .spacing(10) .into(), @@ -372,16 +367,10 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle { } } -struct ProgressBarCustomStyle; - -impl progress_bar::StyleSheet for ProgressBarCustomStyle { - type Style = Theme; - - fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance { - progress_bar::Appearance { - background: style.extended_palette().background.strong.color.into(), - bar: Color::from_rgb8(250, 85, 134).into(), - border_radius: 0.0, - } +fn progress_bar_custom_style(theme: &Theme) -> progress_bar::Appearance { + progress_bar::Appearance { + background: theme.extended_palette().background.strong.color.into(), + bar: Color::from_rgb8(250, 85, 134).into(), + border_radius: 0.0, } } -- cgit From 1c86defab5f5491b5f6b6e45faabf1b91ed195a3 Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Tue, 23 May 2023 12:26:16 +0200 Subject: Extend border radius on relevant widgets --- examples/modal/src/main.rs | 2 +- examples/scrollable/src/main.rs | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index f48afb69..9e1e4c2f 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -418,7 +418,7 @@ mod modal { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), - border_radius: renderer::BorderRadius::from(0.0), + border_radius: Default::default(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 9d57cb94..efc880e3 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -289,13 +289,18 @@ impl Application for ScrollableDemo { } Direction::Horizontal => { progress_bar(0.0..=1.0, self.current_scroll_offset.x) - .style(progress_bar_custom_style) + .style(theme::ProgressBar::Custom(Box::new( + ProgressBarCustomStyle, + ))) .into() } Direction::Multi => column![ progress_bar(0.0..=1.0, self.current_scroll_offset.y), - progress_bar(0.0..=1.0, self.current_scroll_offset.x) - .style(progress_bar_custom_style) + progress_bar(0.0..=1.0, self.current_scroll_offset.x).style( + theme::ProgressBar::Custom(Box::new( + ProgressBarCustomStyle, + )) + ) ] .spacing(10) .into(), @@ -351,12 +356,12 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle { background: style .active(&theme::Scrollable::default()) .background, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Default::default(), scroller: Scroller { color: Color::from_rgb8(250, 85, 134), - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Default::default(), }, @@ -367,10 +372,16 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle { } } -fn progress_bar_custom_style(theme: &Theme) -> progress_bar::Appearance { - progress_bar::Appearance { - background: theme.extended_palette().background.strong.color.into(), - bar: Color::from_rgb8(250, 85, 134).into(), - border_radius: 0.0, +struct ProgressBarCustomStyle; + +impl progress_bar::StyleSheet for ProgressBarCustomStyle { + type Style = Theme; + + fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance { + progress_bar::Appearance { + background: style.extended_palette().background.strong.color.into(), + bar: Color::from_rgb8(250, 85, 134).into(), + border_radius: 0.0.into(), + } } } -- cgit From 25804d9e5ad67d26fafb437057bc0fddde04512a Mon Sep 17 00:00:00 2001 From: Casper Storm Date: Tue, 23 May 2023 19:42:01 +0200 Subject: clean up rebase mistake --- examples/scrollable/src/main.rs | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index efc880e3..3038661e 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -289,18 +289,13 @@ impl Application for ScrollableDemo { } Direction::Horizontal => { progress_bar(0.0..=1.0, self.current_scroll_offset.x) - .style(theme::ProgressBar::Custom(Box::new( - ProgressBarCustomStyle, - ))) + .style(progress_bar_custom_style) .into() } Direction::Multi => column![ progress_bar(0.0..=1.0, self.current_scroll_offset.y), - progress_bar(0.0..=1.0, self.current_scroll_offset.x).style( - theme::ProgressBar::Custom(Box::new( - ProgressBarCustomStyle, - )) - ) + progress_bar(0.0..=1.0, self.current_scroll_offset.x) + .style(progress_bar_custom_style) ] .spacing(10) .into(), @@ -372,16 +367,10 @@ impl scrollable::StyleSheet for ScrollbarCustomStyle { } } -struct ProgressBarCustomStyle; - -impl progress_bar::StyleSheet for ProgressBarCustomStyle { - type Style = Theme; - - fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance { - progress_bar::Appearance { - background: style.extended_palette().background.strong.color.into(), - bar: Color::from_rgb8(250, 85, 134).into(), - border_radius: 0.0.into(), - } +fn progress_bar_custom_style(theme: &Theme) -> progress_bar::Appearance { + progress_bar::Appearance { + background: theme.extended_palette().background.strong.color.into(), + bar: Color::from_rgb8(250, 85, 134).into(), + border_radius: 0.0.into(), } } -- cgit From 2d21d0900e9fcabfc01a7deaaab5b4fd4b8573e8 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 25 May 2023 13:59:58 -0500 Subject: Upgrade `palette` dependency --- examples/color_palette/Cargo.toml | 2 +- examples/color_palette/src/main.rs | 50 ++++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 24 deletions(-) (limited to 'examples') diff --git a/examples/color_palette/Cargo.toml b/examples/color_palette/Cargo.toml index 8fd37202..3be732bb 100644 --- a/examples/color_palette/Cargo.toml +++ b/examples/color_palette/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] iced = { path = "../..", features = ["canvas", "palette"] } -palette = "0.6.0" +palette = "0.7.0" diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index 5c4304ee..de01099e 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -4,7 +4,9 @@ use iced::{ alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer, Sandbox, Settings, Size, Vector, }; -use palette::{self, convert::FromColor, Hsl, Srgb}; +use palette::{ + self, convert::FromColor, rgb::Rgb, Darken, Hsl, Lighten, ShiftHue, +}; use std::marker::PhantomData; use std::ops::RangeInclusive; @@ -49,12 +51,12 @@ impl Sandbox for ColorPalette { fn update(&mut self, message: Message) { let srgb = match message { - Message::RgbColorChanged(rgb) => palette::Srgb::from(rgb), - Message::HslColorChanged(hsl) => palette::Srgb::from_color(hsl), - Message::HsvColorChanged(hsv) => palette::Srgb::from_color(hsv), - Message::HwbColorChanged(hwb) => palette::Srgb::from_color(hwb), - Message::LabColorChanged(lab) => palette::Srgb::from_color(lab), - Message::LchColorChanged(lch) => palette::Srgb::from_color(lch), + Message::RgbColorChanged(rgb) => Rgb::from(rgb), + Message::HslColorChanged(hsl) => Rgb::from_color(hsl), + Message::HsvColorChanged(hsv) => Rgb::from_color(hsv), + Message::HwbColorChanged(hwb) => Rgb::from_color(hwb), + Message::LabColorChanged(lab) => Rgb::from_color(lab), + Message::LchColorChanged(lch) => Rgb::from_color(lch), }; self.theme = Theme::new(srgb); @@ -63,7 +65,7 @@ impl Sandbox for ColorPalette { fn view(&self) -> Element { let base = self.theme.base; - let srgb = palette::Srgb::from(base); + let srgb = Rgb::from(base); let hsl = palette::Hsl::from_color(srgb); let hsv = palette::Hsv::from_color(srgb); let hwb = palette::Hwb::from_color(srgb); @@ -95,12 +97,10 @@ struct Theme { impl Theme { pub fn new(base: impl Into) -> Theme { - use palette::{Hue, Shade}; - let base = base.into(); // Convert to HSL color for manipulation - let hsl = Hsl::from_color(Srgb::from(base)); + let hsl = Hsl::from_color(Rgb::from(base)); let lower = [ hsl.shift_hue(-135.0).lighten(0.075), @@ -119,12 +119,12 @@ impl Theme { Theme { lower: lower .iter() - .map(|&color| Srgb::from_color(color).into()) + .map(|&color| Rgb::from_color(color).into()) .collect(), base, higher: higher .iter() - .map(|&color| Srgb::from_color(color).into()) + .map(|&color| Rgb::from_color(color).into()) .collect(), canvas_cache: canvas::Cache::default(), } @@ -209,14 +209,14 @@ impl Theme { text.vertical_alignment = alignment::Vertical::Bottom; - let hsl = Hsl::from_color(Srgb::from(self.base)); + let hsl = Hsl::from_color(Rgb::from(self.base)); for i in 0..self.len() { let pct = (i as f32 + 1.0) / (self.len() as f32 + 1.0); let graded = Hsl { lightness: 1.0 - pct, ..hsl }; - let color: Color = Srgb::from_color(graded).into(); + let color: Color = Rgb::from_color(graded).into(); let anchor = Point { x: (i as f32) * box_size.width, @@ -352,7 +352,7 @@ impl ColorSpace for palette::Hsl { fn components(&self) -> [f32; 3] { [ - self.hue.to_positive_degrees(), + self.hue.into_positive_degrees(), self.saturation, self.lightness, ] @@ -361,7 +361,7 @@ impl ColorSpace for palette::Hsl { fn to_string(&self) -> String { format!( "hsl({:.1}, {:.1}%, {:.1}%)", - self.hue.to_positive_degrees(), + self.hue.into_positive_degrees(), 100.0 * self.saturation, 100.0 * self.lightness ) @@ -378,13 +378,17 @@ impl ColorSpace for palette::Hsv { } fn components(&self) -> [f32; 3] { - [self.hue.to_positive_degrees(), self.saturation, self.value] + [ + self.hue.into_positive_degrees(), + self.saturation, + self.value, + ] } fn to_string(&self) -> String { format!( "hsv({:.1}, {:.1}%, {:.1}%)", - self.hue.to_positive_degrees(), + self.hue.into_positive_degrees(), 100.0 * self.saturation, 100.0 * self.value ) @@ -406,7 +410,7 @@ impl ColorSpace for palette::Hwb { fn components(&self) -> [f32; 3] { [ - self.hue.to_positive_degrees(), + self.hue.into_positive_degrees(), self.whiteness, self.blackness, ] @@ -415,7 +419,7 @@ impl ColorSpace for palette::Hwb { fn to_string(&self) -> String { format!( "hwb({:.1}, {:.1}%, {:.1}%)", - self.hue.to_positive_degrees(), + self.hue.into_positive_degrees(), 100.0 * self.whiteness, 100.0 * self.blackness ) @@ -450,7 +454,7 @@ impl ColorSpace for palette::Lch { } fn components(&self) -> [f32; 3] { - [self.l, self.chroma, self.hue.to_positive_degrees()] + [self.l, self.chroma, self.hue.into_positive_degrees()] } fn to_string(&self) -> String { @@ -458,7 +462,7 @@ impl ColorSpace for palette::Lch { "Lch({:.1}, {:.1}, {:.1})", self.l, self.chroma, - self.hue.to_positive_degrees() + self.hue.into_positive_degrees() ) } } -- cgit From 2a00aaa7477f3f46937bb568be2796b90fa27e12 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 25 May 2023 23:48:42 +0200 Subject: Use `as_slice` instead of `as_ref` in `checkbox` example --- examples/checkbox/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/checkbox/src/main.rs b/examples/checkbox/src/main.rs index ef61a974..ef1a054d 100644 --- a/examples/checkbox/src/main.rs +++ b/examples/checkbox/src/main.rs @@ -31,7 +31,7 @@ impl Application for Example { fn new(_flags: Self::Flags) -> (Self, Command) { ( Self::default(), - font::load(include_bytes!("../fonts/icons.ttf").as_ref()) + font::load(include_bytes!("../fonts/icons.ttf").as_slice()) .map(Message::FontLoaded), ) } -- cgit From faa7627ea41b1ce372bae7f0d2ae36e9b15a97a3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 31 May 2023 21:31:58 +0200 Subject: Introduce `web-colors` feature flag to enable sRGB linear blending This is how browsers perform color management. They treat gamma-corrected sRGB colors as if they were linear RGB. Correctness aside, this mode is introduced for legacy reasons. Most UI/UX tooling uses this color management as well, and many have created an intuition about how color should behave from interacting with a browser. This feature flag should facilitate application development with `iced` in those cases. More details: https://webcolorisstillbroken.com/ --- examples/geometry/src/main.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'examples') diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 5cb41184..a4183db9 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -3,6 +3,7 @@ mod rainbow { use iced_graphics::primitive::{ColoredVertex2D, Primitive}; + use iced::advanced::graphics::color; use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; @@ -84,39 +85,39 @@ mod rainbow { vertices: vec![ ColoredVertex2D { position: posn_center, - color: [1.0, 1.0, 1.0, 1.0], + color: color::pack([1.0, 1.0, 1.0, 1.0]), }, ColoredVertex2D { position: posn_tl, - color: color_r, + color: color::pack(color_r), }, ColoredVertex2D { position: posn_t, - color: color_o, + color: color::pack(color_o), }, ColoredVertex2D { position: posn_tr, - color: color_y, + color: color::pack(color_y), }, ColoredVertex2D { position: posn_r, - color: color_g, + color: color::pack(color_g), }, ColoredVertex2D { position: posn_br, - color: color_gb, + color: color::pack(color_gb), }, ColoredVertex2D { position: posn_b, - color: color_b, + color: color::pack(color_b), }, ColoredVertex2D { position: posn_bl, - color: color_i, + color: color::pack(color_i), }, ColoredVertex2D { position: posn_l, - color: color_v, + color: color::pack(color_v), }, ], indices: vec![ -- cgit From 233196eb14b40f8bd5201ea0262571f82136ad53 Mon Sep 17 00:00:00 2001 From: Bingus Date: Sat, 25 Mar 2023 10:45:39 -0700 Subject: Added offscreen rendering support for wgpu & tiny-skia exposed with the window::screenshot command. --- examples/screenshot/Cargo.toml | 11 ++ examples/screenshot/src/main.rs | 305 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 examples/screenshot/Cargo.toml create mode 100644 examples/screenshot/src/main.rs (limited to 'examples') diff --git a/examples/screenshot/Cargo.toml b/examples/screenshot/Cargo.toml new file mode 100644 index 00000000..b79300b7 --- /dev/null +++ b/examples/screenshot/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "screenshot" +version = "0.1.0" +authors = ["Bingus "] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../..", features = ["debug", "image", "advanced"] } +image = { version = "0.24.6", features = ["png"]} +env_logger = "0.10.0" diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs new file mode 100644 index 00000000..29d961d9 --- /dev/null +++ b/examples/screenshot/src/main.rs @@ -0,0 +1,305 @@ +use iced::alignment::{Horizontal, Vertical}; +use iced::keyboard::KeyCode; +use iced::theme::{Button, Container}; +use iced::widget::runtime::{CropError, Screenshot}; +use iced::widget::{ + button, column as col, container, image as iced_image, row, text, + text_input, +}; +use iced::{ + event, executor, keyboard, subscription, Alignment, Application, Command, + ContentFit, Element, Event, Length, Rectangle, Renderer, Subscription, + Theme, +}; +use image as img; +use image::ColorType; + +fn main() -> iced::Result { + env_logger::builder().format_timestamp(None).init(); + + Example::run(iced::Settings::default()) +} + +struct Example { + screenshot: Option, + saved_png_path: Option>, + png_saving: bool, + crop_error: Option, + x_input_value: u32, + y_input_value: u32, + width_input_value: u32, + height_input_value: u32, +} + +#[derive(Clone, Debug)] +enum Message { + Crop, + Screenshot, + ScreenshotData(Screenshot), + Png, + PngSaved(Result), + XInputChanged(String), + YInputChanged(String), + WidthInputChanged(String), + HeightInputChanged(String), +} + +impl Application for Example { + type Executor = executor::Default; + type Message = Message; + type Theme = Theme; + type Flags = (); + + fn new(_flags: Self::Flags) -> (Self, Command) { + ( + Example { + screenshot: None, + saved_png_path: None, + png_saving: false, + crop_error: None, + x_input_value: 0, + y_input_value: 0, + width_input_value: 0, + height_input_value: 0, + }, + Command::none(), + ) + } + + fn title(&self) -> String { + "Screenshot".to_string() + } + + fn update(&mut self, message: Self::Message) -> Command { + match message { + Message::Screenshot => { + return iced::window::screenshot(Message::ScreenshotData); + } + Message::ScreenshotData(screenshot) => { + self.screenshot = Some(screenshot); + } + Message::Png => { + if let Some(screenshot) = &self.screenshot { + return Command::perform( + save_to_png(screenshot.clone()), + Message::PngSaved, + ); + } + self.png_saving = true; + } + Message::PngSaved(res) => { + self.png_saving = false; + self.saved_png_path = Some(res); + } + Message::XInputChanged(new) => { + if let Ok(value) = new.parse::() { + self.x_input_value = value; + } + } + Message::YInputChanged(new) => { + if let Ok(value) = new.parse::() { + self.y_input_value = value; + } + } + Message::WidthInputChanged(new) => { + if let Ok(value) = new.parse::() { + self.width_input_value = value; + } + } + Message::HeightInputChanged(new) => { + if let Ok(value) = new.parse::() { + self.height_input_value = value; + } + } + Message::Crop => { + if let Some(screenshot) = &self.screenshot { + let cropped = screenshot.crop(Rectangle:: { + x: self.x_input_value, + y: self.y_input_value, + width: self.width_input_value, + height: self.height_input_value, + }); + + match cropped { + Ok(screenshot) => { + self.screenshot = Some(screenshot); + self.crop_error = None; + } + Err(crop_error) => { + self.crop_error = Some(crop_error); + } + } + } + } + } + + Command::none() + } + + fn view(&self) -> Element<'_, Self::Message, Renderer> { + let image: Element = if let Some(screenshot) = &self.screenshot + { + iced_image(iced_image::Handle::from_pixels( + screenshot.size.width, + screenshot.size.height, + screenshot.bytes.clone(), + )) + .content_fit(ContentFit::ScaleDown) + .width(Length::Fill) + .height(Length::Fill) + .into() + } else { + text("Press the button to take a screenshot!").into() + }; + + let image = container(image) + .padding(10) + .style(Container::Custom(Box::new(ScreenshotDisplayContainer))) + .width(Length::FillPortion(2)) + .height(Length::Fill) + .center_x() + .center_y(); + + let crop_origin_controls = row![ + text("X:").vertical_alignment(Vertical::Center).width(14), + text_input("0", &format!("{}", self.x_input_value),) + .on_input(Message::XInputChanged) + .width(40), + text("Y:").vertical_alignment(Vertical::Center).width(14), + text_input("0", &format!("{}", self.y_input_value),) + .on_input(Message::YInputChanged) + .width(40), + ] + .spacing(10) + .align_items(Alignment::Center); + + let crop_dimension_controls = row![ + text("W:").vertical_alignment(Vertical::Center).width(14), + text_input("0", &format!("{}", self.width_input_value),) + .on_input(Message::WidthInputChanged) + .width(40), + text("H:").vertical_alignment(Vertical::Center).width(14), + text_input("0", &format!("{}", self.height_input_value),) + .on_input(Message::HeightInputChanged) + .width(40), + ] + .spacing(10) + .align_items(Alignment::Center); + + let mut crop_controls = + col![crop_origin_controls, crop_dimension_controls] + .spacing(10) + .align_items(Alignment::Center); + + if let Some(crop_error) = &self.crop_error { + crop_controls = crop_controls + .push(text(format!("Crop error! \n{}", crop_error))); + } + + let png_button = if !self.png_saving { + button("Save to png.") + .style(Button::Secondary) + .padding([10, 20, 10, 20]) + .on_press(Message::Png) + } else { + button("Saving..") + .style(Button::Secondary) + .padding([10, 20, 10, 20]) + }; + + let mut controls = col![ + button("Screenshot!") + .padding([10, 20, 10, 20]) + .on_press(Message::Screenshot), + button("Crop") + .style(Button::Destructive) + .padding([10, 20, 10, 20]) + .on_press(Message::Crop), + crop_controls, + png_button, + ] + .spacing(40) + .align_items(Alignment::Center); + + if let Some(png_result) = &self.saved_png_path { + let msg = match png_result { + Ok(path) => format!("Png saved as: {:?}!", path), + Err(msg) => { + format!("Png could not be saved due to:\n{:?}", msg) + } + }; + + controls = controls.push(text(msg)); + } + + let side_content = container(controls) + .align_x(Horizontal::Center) + .width(Length::FillPortion(1)) + .height(Length::Fill) + .center_y() + .center_x(); + + let content = row![side_content, image] + .width(Length::Fill) + .height(Length::Fill) + .align_items(Alignment::Center); + + container(content) + .padding(10) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } + + fn subscription(&self) -> Subscription { + subscription::events_with(|event, status| { + if let event::Status::Captured = status { + return None; + } + + if let Event::Keyboard(keyboard::Event::KeyPressed { + key_code: KeyCode::F5, + .. + }) = event + { + Some(Message::Screenshot) + } else { + None + } + }) + } +} + +struct ScreenshotDisplayContainer; + +impl container::StyleSheet for ScreenshotDisplayContainer { + type Style = Theme; + + fn appearance(&self, style: &Self::Style) -> container::Appearance { + container::Appearance { + text_color: None, + background: None, + border_radius: 5.0, + border_width: 4.0, + border_color: style.palette().primary, + } + } +} + +async fn save_to_png(screenshot: Screenshot) -> Result { + let path = "screenshot.png".to_string(); + img::save_buffer( + &path, + &screenshot.bytes, + screenshot.size.width, + screenshot.size.height, + ColorType::Rgba8, + ) + .map(|_| path) + .map_err(|err| PngError(format!("{:?}", err))) +} + +#[derive(Clone, Debug)] +struct PngError(String); -- cgit From 5ed945287745f5c40ca5cc8013458eee89e76f35 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 15:39:29 +0200 Subject: Use `Container::Box` in `screenshot` example --- examples/screenshot/src/main.rs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 29d961d9..cfdddd72 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -154,7 +154,7 @@ impl Application for Example { let image = container(image) .padding(10) - .style(Container::Custom(Box::new(ScreenshotDisplayContainer))) + .style(Container::Box) .width(Length::FillPortion(2)) .height(Length::Fill) .center_x() @@ -272,22 +272,6 @@ impl Application for Example { } } -struct ScreenshotDisplayContainer; - -impl container::StyleSheet for ScreenshotDisplayContainer { - type Style = Theme; - - fn appearance(&self, style: &Self::Style) -> container::Appearance { - container::Appearance { - text_color: None, - background: None, - border_radius: 5.0, - border_width: 4.0, - border_color: style.palette().primary, - } - } -} - async fn save_to_png(screenshot: Screenshot) -> Result { let path = "screenshot.png".to_string(); img::save_buffer( -- cgit From 8820583cc04f468b403e0660118760389f9e4370 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 15:51:32 +0200 Subject: Create `numeric_input` helper in `screenshot` example --- examples/screenshot/src/main.rs | 98 +++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 44 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index cfdddd72..8e3bcaec 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -25,10 +25,10 @@ struct Example { saved_png_path: Option>, png_saving: bool, crop_error: Option, - x_input_value: u32, - y_input_value: u32, - width_input_value: u32, - height_input_value: u32, + x_input_value: Option, + y_input_value: Option, + width_input_value: Option, + height_input_value: Option, } #[derive(Clone, Debug)] @@ -38,10 +38,10 @@ enum Message { ScreenshotData(Screenshot), Png, PngSaved(Result), - XInputChanged(String), - YInputChanged(String), - WidthInputChanged(String), - HeightInputChanged(String), + XInputChanged(Option), + YInputChanged(Option), + WidthInputChanged(Option), + HeightInputChanged(Option), } impl Application for Example { @@ -57,10 +57,10 @@ impl Application for Example { saved_png_path: None, png_saving: false, crop_error: None, - x_input_value: 0, - y_input_value: 0, - width_input_value: 0, - height_input_value: 0, + x_input_value: None, + y_input_value: None, + width_input_value: None, + height_input_value: None, }, Command::none(), ) @@ -91,33 +91,25 @@ impl Application for Example { self.png_saving = false; self.saved_png_path = Some(res); } - Message::XInputChanged(new) => { - if let Ok(value) = new.parse::() { - self.x_input_value = value; - } + Message::XInputChanged(new_value) => { + self.x_input_value = new_value; } - Message::YInputChanged(new) => { - if let Ok(value) = new.parse::() { - self.y_input_value = value; - } + Message::YInputChanged(new_value) => { + self.y_input_value = new_value; } - Message::WidthInputChanged(new) => { - if let Ok(value) = new.parse::() { - self.width_input_value = value; - } + Message::WidthInputChanged(new_value) => { + self.width_input_value = new_value; } - Message::HeightInputChanged(new) => { - if let Ok(value) = new.parse::() { - self.height_input_value = value; - } + Message::HeightInputChanged(new_value) => { + self.height_input_value = new_value; } Message::Crop => { if let Some(screenshot) = &self.screenshot { let cropped = screenshot.crop(Rectangle:: { - x: self.x_input_value, - y: self.y_input_value, - width: self.width_input_value, - height: self.height_input_value, + x: self.x_input_value.unwrap_or(0), + y: self.y_input_value.unwrap_or(0), + width: self.width_input_value.unwrap_or(0), + height: self.height_input_value.unwrap_or(0), }); match cropped { @@ -162,26 +154,20 @@ impl Application for Example { let crop_origin_controls = row![ text("X:").vertical_alignment(Vertical::Center).width(14), - text_input("0", &format!("{}", self.x_input_value),) - .on_input(Message::XInputChanged) - .width(40), + numeric_input("0", self.x_input_value).map(Message::XInputChanged), text("Y:").vertical_alignment(Vertical::Center).width(14), - text_input("0", &format!("{}", self.y_input_value),) - .on_input(Message::YInputChanged) - .width(40), + numeric_input("0", self.y_input_value).map(Message::YInputChanged) ] .spacing(10) .align_items(Alignment::Center); let crop_dimension_controls = row![ text("W:").vertical_alignment(Vertical::Center).width(14), - text_input("0", &format!("{}", self.width_input_value),) - .on_input(Message::WidthInputChanged) - .width(40), + numeric_input("0", self.width_input_value) + .map(Message::WidthInputChanged), text("H:").vertical_alignment(Vertical::Center).width(14), - text_input("0", &format!("{}", self.height_input_value),) - .on_input(Message::HeightInputChanged) - .width(40), + numeric_input("0", self.height_input_value) + .map(Message::HeightInputChanged) ] .spacing(10) .align_items(Alignment::Center); @@ -287,3 +273,27 @@ async fn save_to_png(screenshot: Screenshot) -> Result { #[derive(Clone, Debug)] struct PngError(String); + +fn numeric_input( + placeholder: &str, + value: Option, +) -> Element<'_, Option> { + text_input( + placeholder, + &value + .as_ref() + .map(ToString::to_string) + .unwrap_or_else(String::new), + ) + .on_input(move |text| { + if text.is_empty() { + None + } else if let Ok(new_value) = text.parse() { + Some(new_value) + } else { + value + } + }) + .width(40) + .into() +} -- cgit From cd15f8305ab1094ff9887f8d8822e385a381ab1c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 15:59:36 +0200 Subject: Fix width of crop labels in `screenshot` example --- examples/screenshot/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 8e3bcaec..0d77d316 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -153,19 +153,19 @@ impl Application for Example { .center_y(); let crop_origin_controls = row![ - text("X:").vertical_alignment(Vertical::Center).width(14), + text("X:").vertical_alignment(Vertical::Center).width(20), numeric_input("0", self.x_input_value).map(Message::XInputChanged), - text("Y:").vertical_alignment(Vertical::Center).width(14), + text("Y:").vertical_alignment(Vertical::Center).width(20), numeric_input("0", self.y_input_value).map(Message::YInputChanged) ] .spacing(10) .align_items(Alignment::Center); let crop_dimension_controls = row![ - text("W:").vertical_alignment(Vertical::Center).width(14), + text("W:").vertical_alignment(Vertical::Center).width(20), numeric_input("0", self.width_input_value) .map(Message::WidthInputChanged), - text("H:").vertical_alignment(Vertical::Center).width(14), + text("H:").vertical_alignment(Vertical::Center).width(20), numeric_input("0", self.height_input_value) .map(Message::HeightInputChanged) ] -- cgit From c1021c71758d0c850c7b3fea26075bb83830cb7d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 15:59:56 +0200 Subject: Fix punctuation in `screenshot` example --- examples/screenshot/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 0d77d316..0f1d8dce 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -183,12 +183,12 @@ impl Application for Example { } let png_button = if !self.png_saving { - button("Save to png.") + button("Save to png") .style(Button::Secondary) .padding([10, 20, 10, 20]) .on_press(Message::Png) } else { - button("Saving..") + button("Saving...") .style(Button::Secondary) .padding([10, 20, 10, 20]) }; -- cgit From 7adfaa88a68e1accfaddf13e82b8bd7a11ee8786 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 16:05:46 +0200 Subject: Avoid `iced_image` import in `screenshot` example --- examples/screenshot/src/main.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 0f1d8dce..66cbcd8c 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -1,18 +1,17 @@ +use ::image as img; +use ::image::ColorType; use iced::alignment::{Horizontal, Vertical}; use iced::keyboard::KeyCode; use iced::theme::{Button, Container}; use iced::widget::runtime::{CropError, Screenshot}; use iced::widget::{ - button, column as col, container, image as iced_image, row, text, - text_input, + button, column as col, container, image, row, text, text_input, }; use iced::{ event, executor, keyboard, subscription, Alignment, Application, Command, ContentFit, Element, Event, Length, Rectangle, Renderer, Subscription, Theme, }; -use image as img; -use image::ColorType; fn main() -> iced::Result { env_logger::builder().format_timestamp(None).init(); @@ -131,7 +130,7 @@ impl Application for Example { fn view(&self) -> Element<'_, Self::Message, Renderer> { let image: Element = if let Some(screenshot) = &self.screenshot { - iced_image(iced_image::Handle::from_pixels( + image(image::Handle::from_pixels( screenshot.size.width, screenshot.size.height, screenshot.bytes.clone(), -- cgit From 5324928044cba800454b1861eb9999038bc28c2e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 16:14:42 +0200 Subject: Wrap `Screenshot::bytes` in an `Arc` and implement `AsRef<[u8]>` --- examples/screenshot/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 66cbcd8c..7d53b3b2 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -133,7 +133,7 @@ impl Application for Example { image(image::Handle::from_pixels( screenshot.size.width, screenshot.size.height, - screenshot.bytes.clone(), + screenshot.clone(), )) .content_fit(ContentFit::ScaleDown) .width(Length::Fill) -- cgit From 5b5000e3ae9789cf1d83cecd08f4d0cedc16d788 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 16:18:20 +0200 Subject: Introduce `on_press_maybe` helper for `Button` --- examples/screenshot/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 7d53b3b2..be39f829 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -79,12 +79,13 @@ impl Application for Example { } Message::Png => { if let Some(screenshot) = &self.screenshot { + self.png_saving = true; + return Command::perform( save_to_png(screenshot.clone()), Message::PngSaved, ); } - self.png_saving = true; } Message::PngSaved(res) => { self.png_saving = false; @@ -185,7 +186,7 @@ impl Application for Example { button("Save to png") .style(Button::Secondary) .padding([10, 20, 10, 20]) - .on_press(Message::Png) + .on_press_maybe(self.screenshot.is_some().then(|| Message::Png)) } else { button("Saving...") .style(Button::Secondary) -- cgit From 38582873b7d64174f11a7c4d74ed8f654320f7f8 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 16:32:14 +0200 Subject: Rearrange controls of the `screenshot` example --- examples/screenshot/src/main.rs | 84 +++++++++++++++++++++++++---------------- 1 file changed, 52 insertions(+), 32 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index be39f829..7b9ac4e0 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -1,12 +1,10 @@ use ::image as img; use ::image::ColorType; -use iced::alignment::{Horizontal, Vertical}; +use iced::alignment; use iced::keyboard::KeyCode; use iced::theme::{Button, Container}; use iced::widget::runtime::{CropError, Screenshot}; -use iced::widget::{ - button, column as col, container, image, row, text, text_input, -}; +use iced::widget::{button, column, container, image, row, text, text_input}; use iced::{ event, executor, keyboard, subscription, Alignment, Application, Command, ContentFit, Element, Event, Length, Rectangle, Renderer, Subscription, @@ -153,19 +151,27 @@ impl Application for Example { .center_y(); let crop_origin_controls = row![ - text("X:").vertical_alignment(Vertical::Center).width(20), + text("X:") + .vertical_alignment(alignment::Vertical::Center) + .width(20), numeric_input("0", self.x_input_value).map(Message::XInputChanged), - text("Y:").vertical_alignment(Vertical::Center).width(20), + text("Y:") + .vertical_alignment(alignment::Vertical::Center) + .width(20), numeric_input("0", self.y_input_value).map(Message::YInputChanged) ] .spacing(10) .align_items(Alignment::Center); let crop_dimension_controls = row![ - text("W:").vertical_alignment(Vertical::Center).width(20), + text("W:") + .vertical_alignment(alignment::Vertical::Center) + .width(20), numeric_input("0", self.width_input_value) .map(Message::WidthInputChanged), - text("H:").vertical_alignment(Vertical::Center).width(20), + text("H:") + .vertical_alignment(alignment::Vertical::Center) + .width(20), numeric_input("0", self.height_input_value) .map(Message::HeightInputChanged) ] @@ -173,7 +179,7 @@ impl Application for Example { .align_items(Alignment::Center); let mut crop_controls = - col![crop_origin_controls, crop_dimension_controls] + column![crop_origin_controls, crop_dimension_controls] .spacing(10) .align_items(Alignment::Center); @@ -182,30 +188,36 @@ impl Application for Example { .push(text(format!("Crop error! \n{}", crop_error))); } - let png_button = if !self.png_saving { - button("Save to png") - .style(Button::Secondary) - .padding([10, 20, 10, 20]) - .on_press_maybe(self.screenshot.is_some().then(|| Message::Png)) - } else { - button("Saving...") + let mut controls = column![ + column![ + button(centered_text("Screenshot!")) + .padding([10, 20, 10, 20]) + .width(Length::Fill) + .on_press(Message::Screenshot), + if !self.png_saving { + button(centered_text("Save as png")).on_press_maybe( + self.screenshot.is_some().then(|| Message::Png), + ) + } else { + button(centered_text("Saving...")).style(Button::Secondary) + } .style(Button::Secondary) .padding([10, 20, 10, 20]) - }; - - let mut controls = col![ - button("Screenshot!") - .padding([10, 20, 10, 20]) - .on_press(Message::Screenshot), - button("Crop") - .style(Button::Destructive) - .padding([10, 20, 10, 20]) - .on_press(Message::Crop), - crop_controls, - png_button, + .width(Length::Fill) + ] + .spacing(10), + column![ + crop_controls, + button(centered_text("Crop")) + .on_press(Message::Crop) + .style(Button::Destructive) + .padding([10, 20, 10, 20]) + .width(Length::Fill), + ] + .spacing(10) + .align_items(Alignment::Center), ] - .spacing(40) - .align_items(Alignment::Center); + .spacing(40); if let Some(png_result) = &self.saved_png_path { let msg = match png_result { @@ -219,21 +231,22 @@ impl Application for Example { } let side_content = container(controls) - .align_x(Horizontal::Center) + .align_x(alignment::Horizontal::Center) .width(Length::FillPortion(1)) .height(Length::Fill) .center_y() .center_x(); let content = row![side_content, image] + .spacing(10) .width(Length::Fill) .height(Length::Fill) .align_items(Alignment::Center); container(content) - .padding(10) .width(Length::Fill) .height(Length::Fill) + .padding(10) .center_x() .center_y() .into() @@ -297,3 +310,10 @@ fn numeric_input( .width(40) .into() } + +fn centered_text(content: &str) -> Element<'_, Message> { + text(content) + .width(Length::Fill) + .horizontal_alignment(alignment::Horizontal::Center) + .into() +} -- cgit From 78c0189824bbae2ba679c8f8b5ae9552debcb0fd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 16:36:20 +0200 Subject: Fix width of crop labels in `screenshot` example (again) --- examples/screenshot/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 7b9ac4e0..59dfdf14 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -153,11 +153,11 @@ impl Application for Example { let crop_origin_controls = row![ text("X:") .vertical_alignment(alignment::Vertical::Center) - .width(20), + .width(30), numeric_input("0", self.x_input_value).map(Message::XInputChanged), text("Y:") .vertical_alignment(alignment::Vertical::Center) - .width(20), + .width(30), numeric_input("0", self.y_input_value).map(Message::YInputChanged) ] .spacing(10) @@ -166,12 +166,12 @@ impl Application for Example { let crop_dimension_controls = row![ text("W:") .vertical_alignment(alignment::Vertical::Center) - .width(20), + .width(30), numeric_input("0", self.width_input_value) .map(Message::WidthInputChanged), text("H:") .vertical_alignment(alignment::Vertical::Center) - .width(20), + .width(30), numeric_input("0", self.height_input_value) .map(Message::HeightInputChanged) ] -- cgit From cdfb8b30680de164280b8b90fbc08a1638e597e2 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Tue, 6 Jun 2023 21:12:41 -0700 Subject: feat: add progress indicators example --- examples/progress_indicators/Cargo.toml | 13 + examples/progress_indicators/README.md | 14 + examples/progress_indicators/src/circular.rs | 425 +++++++++++++++++++++++++++ examples/progress_indicators/src/easing.rs | 67 +++++ examples/progress_indicators/src/linear.rs | 325 ++++++++++++++++++++ examples/progress_indicators/src/main.rs | 104 +++++++ 6 files changed, 948 insertions(+) create mode 100644 examples/progress_indicators/Cargo.toml create mode 100644 examples/progress_indicators/README.md create mode 100644 examples/progress_indicators/src/circular.rs create mode 100644 examples/progress_indicators/src/easing.rs create mode 100644 examples/progress_indicators/src/linear.rs create mode 100644 examples/progress_indicators/src/main.rs (limited to 'examples') diff --git a/examples/progress_indicators/Cargo.toml b/examples/progress_indicators/Cargo.toml new file mode 100644 index 00000000..89ceb127 --- /dev/null +++ b/examples/progress_indicators/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "progress_indicators" +version = "0.1.0" +authors = ["Nick Senger "] +edition = "2021" +publish = false + +[dependencies] +flo_curves = "0.7" +iced = { path = "../..", features = ["canvas"] } +iced_core = { path = "../../core" } +iced_widget = { path = "../../widget" } +lazy_static = "1.4" diff --git a/examples/progress_indicators/README.md b/examples/progress_indicators/README.md new file mode 100644 index 00000000..acacca8f --- /dev/null +++ b/examples/progress_indicators/README.md @@ -0,0 +1,14 @@ +## Progress indicators + +Example implementation of animated indeterminate progress indicators. + + + +You can run it with `cargo run`: +``` +cargo run --package progress_indicators +``` diff --git a/examples/progress_indicators/src/circular.rs b/examples/progress_indicators/src/circular.rs new file mode 100644 index 00000000..2a9f7e1e --- /dev/null +++ b/examples/progress_indicators/src/circular.rs @@ -0,0 +1,425 @@ +//! Show a circular progress indicator. +use iced::widget::canvas::{self, Cursor, Program, Renderer as CanvasRenderer}; +use iced::window; +use iced_core::event::{self, Event}; +use iced_core::time::Instant; +use iced_core::widget::tree::{self, Tree}; +use iced_core::window::RedrawRequest; +use iced_core::{layout, Size}; +use iced_core::{renderer, Vector}; +use iced_core::{ + Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, + Renderer, Shell, Widget, +}; + +use super::easing::{self, Easing}; + +use std::borrow::Cow; +use std::f32::consts::PI; +use std::time::Duration; + +type R = iced_widget::renderer::Renderer; + +const MIN_RADIANS: f32 = PI / 8.0; +const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0; +const PROCESSION_VELOCITY: u32 = u32::MAX / 120; + +#[allow(missing_debug_implementations)] +pub struct Circular<'a, Theme> +where + Theme: StyleSheet, +{ + size: f32, + bar_height: f32, + style: ::Style, + easing: Cow<'a, Easing>, + cycle_duration: Duration, +} + +impl<'a, Theme> Circular<'a, Theme> +where + Theme: StyleSheet, +{ + /// Creates a new [`Circular`] with the given content. + pub fn new() -> Self { + Circular { + size: 40.0, + bar_height: 4.0, + style: ::Style::default(), + easing: Cow::Borrowed(&easing::STANDARD), + cycle_duration: Duration::from_millis(600), + } + } + + /// Sets the size of the [`Circular`]. + pub fn size(mut self, size: f32) -> Self { + self.size = size; + self + } + + /// Sets the bar height of the [`Circular`]. + pub fn bar_height(mut self, bar_height: f32) -> Self { + self.bar_height = bar_height; + self + } + + /// Sets the style variant of this [`Circular`]. + pub fn style(mut self, style: ::Style) -> Self { + self.style = style; + self + } + + /// Sets the easing of this [`Circular`]. + pub fn easing(mut self, easing: impl Into>) -> Self { + self.easing = easing.into(); + self + } + + /// Sets the cycle duration of this [`Circular`]. + pub fn cycle_duration(mut self, duration: Duration) -> Self { + self.cycle_duration = duration / 2; + self + } +} + +impl<'a, Theme> Default for Circular<'a, Theme> +where + Theme: StyleSheet, +{ + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy)] +enum State { + Expanding { + start: Instant, + progress: f32, + procession: u32, + }, + Contracting { + start: Instant, + progress: f32, + procession: u32, + }, +} + +impl Default for State { + fn default() -> Self { + Self::Expanding { + start: Instant::now(), + progress: 0.0, + procession: 0, + } + } +} + +impl State { + fn next(&self, now: Instant) -> Self { + match self { + Self::Expanding { procession, .. } => Self::Contracting { + start: now, + progress: 0.0, + procession: procession.wrapping_add(PROCESSION_VELOCITY), + }, + Self::Contracting { procession, .. } => Self::Expanding { + start: now, + progress: 0.0, + procession: procession.wrapping_add( + PROCESSION_VELOCITY.wrapping_add( + ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32, + ), + ), + }, + } + } + + fn start(&self) -> Instant { + match self { + Self::Expanding { start, .. } | Self::Contracting { start, .. } => { + *start + } + } + } + + fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { + let elapsed = now.duration_since(self.start()); + + match elapsed { + elapsed if elapsed > cycle_duration => self.next(now), + _ => self.with_elapsed(cycle_duration, elapsed), + } + } + + fn with_elapsed( + &self, + cycle_duration: Duration, + elapsed: Duration, + ) -> Self { + let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); + match self { + Self::Expanding { + start, procession, .. + } => Self::Expanding { + start: *start, + progress, + procession: procession.wrapping_add(PROCESSION_VELOCITY), + }, + Self::Contracting { + start, procession, .. + } => Self::Contracting { + start: *start, + progress, + procession: procession.wrapping_add(PROCESSION_VELOCITY), + }, + } + } + + fn procession(&self) -> f32 { + match self { + Self::Expanding { procession, .. } + | Self::Contracting { procession, .. } => { + *procession as f32 / u32::MAX as f32 + } + } + } +} + +impl<'a, Message, Theme> Widget> for Circular<'a, Theme> +where + Message: 'a + Clone, + Theme: StyleSheet, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + + fn width(&self) -> Length { + Length::Fixed(self.size) + } + + fn height(&self) -> Length { + Length::Fixed(self.size) + } + + fn layout( + &self, + _renderer: &iced_widget::renderer::Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.size).height(self.size); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + _layout: Layout<'_>, + _cursor_position: Point, + _renderer: &iced_widget::renderer::Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + let state = tree.state.downcast_mut::(); + + if let Event::Window(window::Event::RedrawRequested(now)) = event { + *state = state.timed_transition(self.cycle_duration, now); + + shell.request_redraw(RedrawRequest::NextFrame); + } + + event::Status::Ignored + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut R, + theme: &Theme, + _style: &renderer::Style, + layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let state = tree.state.downcast_ref::(); + + renderer.with_translation( + Vector::new(bounds.x, bounds.y), + |renderer| { + renderer.draw( as Program< + Message, + R, + >>::draw( + &StateWithStyle { + state, + style: &self.style, + bar_height: self.bar_height, + easing: self.easing.as_ref(), + }, + &(), + renderer, + theme, + bounds, + Cursor::Unavailable, + )); + }, + ); + } +} + +impl<'a, Message, Theme> From> + for Element<'a, Message, R> +where + Message: Clone + 'a, + Theme: StyleSheet + 'a, +{ + fn from(circular: Circular<'a, Theme>) -> Self { + Self::new(circular) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Appearance { + /// The [`Background`] of the progress indicator. + pub background: Option, + /// The track [`Color`] of the progress indicator. + pub track_color: Color, + /// The bar [`Color`] of the progress indicator. + pub bar_color: Color, +} + +impl std::default::Default for Appearance { + fn default() -> Self { + Self { + background: None, + track_color: Color::TRANSPARENT, + bar_color: Color::BLACK, + } + } +} + +/// A set of rules that dictate the style of an indicator. +pub trait StyleSheet { + /// The supported style of the [`StyleSheet`]. + type Style: Default; + + /// Produces the active [`Appearance`] of a indicator. + fn appearance(&self, style: &Self::Style) -> Appearance; +} + +impl StyleSheet for iced::Theme { + type Style = (); + + fn appearance(&self, _style: &Self::Style) -> Appearance { + let palette = self.extended_palette(); + + Appearance { + background: None, + track_color: palette.background.weak.color, + bar_color: palette.primary.base.color, + } + } +} + +struct StateWithStyle<'a, Theme> +where + Theme: StyleSheet, +{ + state: &'a State, + style: &'a ::Style, + easing: &'a Easing, + bar_height: f32, +} + +impl<'a, Message, Theme> + canvas::Program> + for StateWithStyle<'a, Theme> +where + Theme: StyleSheet, +{ + type State = (); + + fn update( + &self, + _state: &mut Self::State, + _event: canvas::Event, + _bounds: Rectangle, + _cursor: canvas::Cursor, + ) -> (canvas::event::Status, Option) { + (canvas::event::Status::Ignored, None) + } + + fn draw( + &self, + _state: &Self::State, + renderer: &iced_widget::renderer::Renderer, + theme: &Theme, + bounds: Rectangle, + _cursor: canvas::Cursor, + ) -> Vec { + let mut frame = canvas::Frame::new(renderer, bounds.size()); + let custom_style = ::appearance(theme, self.style); + let track_radius = frame.width() / 2.0 - self.bar_height; + + if let Some(Background::Color(color)) = custom_style.background { + let background_path = + canvas::Path::circle(frame.center(), track_radius); + frame.fill(&background_path, color); + } + + let track_path = canvas::Path::circle(frame.center(), track_radius); + + frame.stroke( + &track_path, + canvas::Stroke::default() + .with_color(custom_style.track_color) + .with_width(self.bar_height), + ); + + let mut builder = canvas::path::Builder::new(); + + let start = self.state.procession() * 2.0 * PI; + + match self.state { + State::Expanding { progress, .. } => { + builder.arc(canvas::path::Arc { + center: frame.center(), + radius: track_radius, + start_angle: start, + end_angle: start + + MIN_RADIANS + + WRAP_RADIANS * (self.easing.y_at_x(*progress)), + }); + } + State::Contracting { progress, .. } => { + builder.arc(canvas::path::Arc { + center: frame.center(), + radius: track_radius, + start_angle: start + + WRAP_RADIANS * (self.easing.y_at_x(*progress)), + end_angle: start + MIN_RADIANS + WRAP_RADIANS, + }); + } + } + + let bar_path = builder.build(); + + frame.stroke( + &bar_path, + canvas::Stroke::default() + .with_color(custom_style.bar_color) + .with_width(self.bar_height), + ); + + vec![frame.into_geometry()] + } +} diff --git a/examples/progress_indicators/src/easing.rs b/examples/progress_indicators/src/easing.rs new file mode 100644 index 00000000..10479659 --- /dev/null +++ b/examples/progress_indicators/src/easing.rs @@ -0,0 +1,67 @@ +use flo_curves::bezier::Curve; +use flo_curves::*; +use lazy_static::lazy_static; + +use std::borrow::Cow; + +lazy_static! { + pub static ref EXAMPLES: [Easing; 3] = [ + Easing::CubicBezier(Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.05, 0.7), Coord2(0.1, 1.0)), + Coord2(1.0, 1.0), + )), + Easing::CubicBezier(Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.3, 0.0), Coord2(0.8, 0.15)), + Coord2(1.0, 1.0), + )), + Easing::CubicBezier(Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.2, 0.0), Coord2(0.0, 1.0)), + Coord2(1.0, 1.0), + )) + ]; + pub static ref STANDARD: Easing = { + Easing::CubicBezier(Curve::from_points( + Coord2(0.0, 0.0), + (Coord2(0.2, 0.0), Coord2(0.0, 1.0)), + Coord2(1.0, 1.0), + )) + }; +} + +#[derive(Clone, Debug)] +pub enum Easing { + BezierPath(Vec>), + CubicBezier(Curve), +} + +impl Easing { + pub fn y_at_x(&self, x: f32) -> f32 { + let x = x as f64; + + match self { + Self::BezierPath(curves) => curves + .iter() + .find_map(|curve| { + (curve.start_point().0 <= x && curve.end_point().0 >= x) + .then(|| curve.point_at_pos(x).1 as f32) + }) + .unwrap_or_default(), + Self::CubicBezier(curve) => curve.point_at_pos(x).1 as f32, + } + } +} + +impl<'a> From for Cow<'a, Easing> { + fn from(easing: Easing) -> Self { + Cow::Owned(easing) + } +} + +impl<'a> From<&'a Easing> for Cow<'a, Easing> { + fn from(easing: &'a Easing) -> Self { + Cow::Borrowed(easing) + } +} diff --git a/examples/progress_indicators/src/linear.rs b/examples/progress_indicators/src/linear.rs new file mode 100644 index 00000000..735892e7 --- /dev/null +++ b/examples/progress_indicators/src/linear.rs @@ -0,0 +1,325 @@ +//! Show a linear progress indicator. +use iced::window; +use iced_core::event::{self, Event}; +use iced_core::renderer; +use iced_core::time::Instant; +use iced_core::widget::tree::{self, Tree}; +use iced_core::window::RedrawRequest; +use iced_core::{layout, Size}; +use iced_core::{ + Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, + Shell, Widget, +}; + +use super::easing::{self, Easing}; + +use std::borrow::Cow; +use std::time::Duration; + +#[allow(missing_debug_implementations)] +pub struct Linear<'a, Renderer> +where + Renderer: iced_core::Renderer, + Renderer::Theme: StyleSheet, +{ + width: Length, + height: Length, + style: ::Style, + easing: Cow<'a, Easing>, + cycle_duration: Duration, +} + +impl<'a, Renderer> Linear<'a, Renderer> +where + Renderer: iced_widget::core::Renderer, + Renderer::Theme: StyleSheet, +{ + /// Creates a new [`Linear`] with the given content. + pub fn new() -> Self { + Linear { + width: Length::Fixed(100.0), + height: Length::Fixed(4.0), + style: ::Style::default(), + easing: Cow::Borrowed(&easing::STANDARD), + cycle_duration: Duration::from_millis(600), + } + } + + /// Sets the width of the [`Linear`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`Linear`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } + + /// Sets the style variant of this [`Linear`]. + pub fn style( + mut self, + style: ::Style, + ) -> Self { + self.style = style; + self + } + + /// Sets the motion easing of this [`Linear`]. + pub fn easing(mut self, easing: impl Into>) -> Self { + self.easing = easing.into(); + self + } + + /// Sets the cycle duration of this [`Linear`]. + pub fn cycle_duration(mut self, duration: Duration) -> Self { + self.cycle_duration = duration / 2; + self + } +} + +impl<'a, Renderer> Default for Linear<'a, Renderer> +where + Renderer: iced_core::Renderer, + Renderer::Theme: StyleSheet, +{ + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy)] +enum State { + Expanding { start: Instant, progress: f32 }, + Contracting { start: Instant, progress: f32 }, +} + +impl Default for State { + fn default() -> Self { + Self::Expanding { + start: Instant::now(), + progress: 0.0, + } + } +} + +impl State { + fn next(&self, now: Instant) -> Self { + match self { + Self::Expanding { .. } => Self::Contracting { + start: now, + progress: 0.0, + }, + Self::Contracting { .. } => Self::Expanding { + start: now, + progress: 0.0, + }, + } + } + + fn start(&self) -> Instant { + match self { + Self::Expanding { start, .. } | Self::Contracting { start, .. } => { + *start + } + } + } + + fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { + let elapsed = now.duration_since(self.start()); + + match elapsed { + elapsed if elapsed > cycle_duration => self.next(now), + _ => self.with_elapsed(cycle_duration, elapsed), + } + } + + fn with_elapsed( + &self, + cycle_duration: Duration, + elapsed: Duration, + ) -> Self { + let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); + match self { + Self::Expanding { start, .. } => Self::Expanding { + start: *start, + progress, + }, + Self::Contracting { start, .. } => Self::Contracting { + start: *start, + progress, + }, + } + } +} + +impl<'a, Message, Renderer> Widget for Linear<'a, Renderer> +where + Message: 'a + Clone, + Renderer: 'a + iced_core::Renderer, + Renderer::Theme: StyleSheet, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + _layout: Layout<'_>, + _cursor_position: Point, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + let state = tree.state.downcast_mut::(); + + if let Event::Window(window::Event::RedrawRequested(now)) = event { + *state = state.timed_transition(self.cycle_duration, now); + + shell.request_redraw(RedrawRequest::NextFrame); + } + + event::Status::Ignored + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Renderer::Theme, + _style: &renderer::Style, + layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let custom_style = theme.appearance(&self.style); + let state = tree.state.downcast_ref::(); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: bounds.height, + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Background::Color(custom_style.track_color), + ); + + match state { + State::Expanding { progress, .. } => renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: bounds.y, + width: self.easing.y_at_x(*progress) * bounds.width, + height: bounds.height, + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Background::Color(custom_style.bar_color), + ), + + State::Contracting { progress, .. } => renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + + self.easing.y_at_x(*progress) * bounds.width, + y: bounds.y, + width: (1.0 - self.easing.y_at_x(*progress)) + * bounds.width, + height: bounds.height, + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Background::Color(custom_style.bar_color), + ), + } + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Message: Clone + 'a, + Renderer: iced_core::Renderer + 'a, + Renderer::Theme: StyleSheet, +{ + fn from(linear: Linear<'a, Renderer>) -> Self { + Self::new(linear) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Appearance { + /// The track [`Color`] of the progress indicator. + pub track_color: Color, + /// The bar [`Color`] of the progress indicator. + pub bar_color: Color, +} + +impl std::default::Default for Appearance { + fn default() -> Self { + Self { + track_color: Color::TRANSPARENT, + bar_color: Color::BLACK, + } + } +} + +/// A set of rules that dictate the style of an indicator. +pub trait StyleSheet { + /// The supported style of the [`StyleSheet`]. + type Style: Default; + + /// Produces the active [`Appearance`] of a indicator. + fn appearance(&self, style: &Self::Style) -> Appearance; +} + +impl StyleSheet for iced::Theme { + type Style = (); + + fn appearance(&self, _style: &Self::Style) -> Appearance { + let palette = self.extended_palette(); + + Appearance { + track_color: palette.background.weak.color, + bar_color: palette.primary.base.color, + } + } +} diff --git a/examples/progress_indicators/src/main.rs b/examples/progress_indicators/src/main.rs new file mode 100644 index 00000000..136b8d8c --- /dev/null +++ b/examples/progress_indicators/src/main.rs @@ -0,0 +1,104 @@ +use iced::executor; +use iced::widget::{column, container, row, slider, text}; +use iced::{Application, Command, Element, Length, Settings, Theme}; + +use std::time::Duration; + +mod circular; +mod easing; +mod linear; + +use circular::Circular; +use linear::Linear; + +pub fn main() -> iced::Result { + ProgressIndicators::run(Settings { + antialiasing: true, + ..Default::default() + }) +} + +struct ProgressIndicators { + cycle_duration: f32, +} + +impl Default for ProgressIndicators { + fn default() -> Self { + Self { + cycle_duration: 2.0, + } + } +} + +#[derive(Debug, Clone, Copy)] +enum Message { + CycleDurationChanged(f32), +} + +impl Application for ProgressIndicators { + type Message = Message; + type Flags = (); + type Executor = executor::Default; + type Theme = Theme; + + fn new(_flags: Self::Flags) -> (Self, Command) { + (Self::default(), Command::none()) + } + + fn title(&self) -> String { + String::from("Progress Indicators - Iced") + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::CycleDurationChanged(duration) => { + self.cycle_duration = duration; + } + } + + Command::none() + } + + fn view(&self) -> Element { + let column = easing::EXAMPLES + .iter() + .zip(["Decelerating:", "Accelerating:", "Standard:"]) + .fold(column![], |column, (easing, label)| { + column.push( + row![ + text(label).width(150), + Linear::new().easing(easing).cycle_duration( + Duration::from_secs_f32(self.cycle_duration) + ), + Circular::new().easing(easing).cycle_duration( + Duration::from_secs_f32(self.cycle_duration) + ) + ] + .align_items(iced::Alignment::Center) + .spacing(20.0), + ) + }) + .spacing(20); + + container( + column.push( + row(vec![ + text("Cycle duration:").into(), + slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| { + Message::CycleDurationChanged(x / 100.0) + }) + .width(150.0) + .into(), + text(format!("{:.2}s", self.cycle_duration)).into(), + ]) + .align_items(iced::Alignment::Center) + .spacing(20.0), + ), + ) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} -- cgit From 2ebc92319711e6fa0dda310939257334625b59c9 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Wed, 7 Jun 2023 15:19:11 -0700 Subject: feat: use lyon for easing --- examples/loading_spinners/Cargo.toml | 14 + examples/loading_spinners/README.md | 14 + examples/loading_spinners/src/circular.rs | 444 +++++++++++++++++++++++++++ examples/loading_spinners/src/easing.rs | 132 ++++++++ examples/loading_spinners/src/linear.rs | 324 +++++++++++++++++++ examples/loading_spinners/src/main.rs | 118 +++++++ examples/progress_indicators/Cargo.toml | 13 - examples/progress_indicators/README.md | 14 - examples/progress_indicators/src/circular.rs | 425 ------------------------- examples/progress_indicators/src/easing.rs | 67 ---- examples/progress_indicators/src/linear.rs | 325 -------------------- examples/progress_indicators/src/main.rs | 104 ------- 12 files changed, 1046 insertions(+), 948 deletions(-) create mode 100644 examples/loading_spinners/Cargo.toml create mode 100644 examples/loading_spinners/README.md create mode 100644 examples/loading_spinners/src/circular.rs create mode 100644 examples/loading_spinners/src/easing.rs create mode 100644 examples/loading_spinners/src/linear.rs create mode 100644 examples/loading_spinners/src/main.rs delete mode 100644 examples/progress_indicators/Cargo.toml delete mode 100644 examples/progress_indicators/README.md delete mode 100644 examples/progress_indicators/src/circular.rs delete mode 100644 examples/progress_indicators/src/easing.rs delete mode 100644 examples/progress_indicators/src/linear.rs delete mode 100644 examples/progress_indicators/src/main.rs (limited to 'examples') diff --git a/examples/loading_spinners/Cargo.toml b/examples/loading_spinners/Cargo.toml new file mode 100644 index 00000000..f37017c6 --- /dev/null +++ b/examples/loading_spinners/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "loading_spinners" +version = "0.1.0" +authors = ["Nick Senger "] +edition = "2021" +publish = false + +[dependencies] +flo_curves = "0.7" +iced = { path = "../..", features = ["canvas"] } +iced_core = { path = "../../core" } +iced_widget = { path = "../../widget" } +once_cell = "1" +lyon = "1" diff --git a/examples/loading_spinners/README.md b/examples/loading_spinners/README.md new file mode 100644 index 00000000..3573c6f6 --- /dev/null +++ b/examples/loading_spinners/README.md @@ -0,0 +1,14 @@ +## Loading Spinners + +Example implementation of animated indeterminate loading spinners. + + + +You can run it with `cargo run`: +``` +cargo run --package loading_spinners +``` diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs new file mode 100644 index 00000000..0d01e7a1 --- /dev/null +++ b/examples/loading_spinners/src/circular.rs @@ -0,0 +1,444 @@ +//! Show a circular progress indicator. +use iced::widget::canvas::{self, Cursor, Program, Renderer as CanvasRenderer}; +use iced::window; +use iced_core::event::{self, Event}; +use iced_core::time::Instant; +use iced_core::widget::tree::{self, Tree}; +use iced_core::window::RedrawRequest; +use iced_core::{layout, Size}; +use iced_core::{renderer, Vector}; +use iced_core::{ + Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, + Renderer, Shell, Widget, +}; + +use super::easing::{self, Easing}; + +use std::f32::consts::PI; +use std::time::Duration; + +type R = iced_widget::renderer::Renderer; + +const MIN_RADIANS: f32 = PI / 8.0; +const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0; +const BASE_ROTATION_SPEED: u32 = u32::MAX / 80; + +#[allow(missing_debug_implementations)] +pub struct Circular<'a, Theme> +where + Theme: StyleSheet, +{ + size: f32, + bar_height: f32, + style: ::Style, + easing: &'a Easing, + cycle_duration: Duration, + rotation_speed: u32, +} + +impl<'a, Theme> Circular<'a, Theme> +where + Theme: StyleSheet, +{ + /// Creates a new [`Circular`] with the given content. + pub fn new() -> Self { + Circular { + size: 40.0, + bar_height: 4.0, + style: ::Style::default(), + easing: &easing::STANDARD, + cycle_duration: Duration::from_millis(600), + rotation_speed: BASE_ROTATION_SPEED, + } + } + + /// Sets the size of the [`Circular`]. + pub fn size(mut self, size: f32) -> Self { + self.size = size; + self + } + + /// Sets the bar height of the [`Circular`]. + pub fn bar_height(mut self, bar_height: f32) -> Self { + self.bar_height = bar_height; + self + } + + /// Sets the style variant of this [`Circular`]. + pub fn style(mut self, style: ::Style) -> Self { + self.style = style; + self + } + + /// Sets the easing of this [`Circular`]. + pub fn easing(mut self, easing: &'a Easing) -> Self { + self.easing = easing; + self + } + + /// Sets the cycle duration of this [`Circular`]. + pub fn cycle_duration(mut self, duration: Duration) -> Self { + self.cycle_duration = duration / 2; + self + } + + /// Sets the rotation speed of this [`Circular`]. Must be set to between 0.0 and 10.0. + /// Defaults to 1.0. + pub fn rotation_speed(mut self, speed: f32) -> Self { + let multiplier = speed.min(10.0).max(0.0); + self.rotation_speed = (BASE_ROTATION_SPEED as f32 * multiplier) as u32; + self + } +} + +impl<'a, Theme> Default for Circular<'a, Theme> +where + Theme: StyleSheet, +{ + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy)] +enum State { + Expanding { + start: Instant, + progress: f32, + procession: u32, + }, + Contracting { + start: Instant, + progress: f32, + procession: u32, + }, +} + +impl Default for State { + fn default() -> Self { + Self::Expanding { + start: Instant::now(), + progress: 0.0, + procession: 0, + } + } +} + +impl State { + fn next(&self, now: Instant) -> Self { + match self { + Self::Expanding { procession, .. } => Self::Contracting { + start: now, + progress: 0.0, + procession: procession.wrapping_add(BASE_ROTATION_SPEED), + }, + Self::Contracting { procession, .. } => Self::Expanding { + start: now, + progress: 0.0, + procession: procession.wrapping_add( + BASE_ROTATION_SPEED.wrapping_add( + ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32, + ), + ), + }, + } + } + + fn start(&self) -> Instant { + match self { + Self::Expanding { start, .. } | Self::Contracting { start, .. } => { + *start + } + } + } + + fn timed_transition( + &self, + cycle_duration: Duration, + rotation_speed: u32, + now: Instant, + ) -> Self { + let elapsed = now.duration_since(self.start()); + + match elapsed { + elapsed if elapsed > cycle_duration => self.next(now), + _ => self.with_elapsed(cycle_duration, rotation_speed, elapsed), + } + } + + fn with_elapsed( + &self, + cycle_duration: Duration, + rotation_speed: u32, + elapsed: Duration, + ) -> Self { + let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); + match self { + Self::Expanding { + start, procession, .. + } => Self::Expanding { + start: *start, + progress, + procession: procession.wrapping_add(rotation_speed), + }, + Self::Contracting { + start, procession, .. + } => Self::Contracting { + start: *start, + progress, + procession: procession.wrapping_add(rotation_speed), + }, + } + } + + fn procession(&self) -> f32 { + match self { + Self::Expanding { procession, .. } + | Self::Contracting { procession, .. } => { + *procession as f32 / u32::MAX as f32 + } + } + } +} + +impl<'a, Message, Theme> Widget> for Circular<'a, Theme> +where + Message: 'a + Clone, + Theme: StyleSheet, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + + fn width(&self) -> Length { + Length::Fixed(self.size) + } + + fn height(&self) -> Length { + Length::Fixed(self.size) + } + + fn layout( + &self, + _renderer: &iced_widget::renderer::Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.size).height(self.size); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + _layout: Layout<'_>, + _cursor_position: Point, + _renderer: &iced_widget::renderer::Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + let state = tree.state.downcast_mut::(); + + if let Event::Window(window::Event::RedrawRequested(now)) = event { + *state = state.timed_transition( + self.cycle_duration, + self.rotation_speed, + now, + ); + + shell.request_redraw(RedrawRequest::NextFrame); + } + + event::Status::Ignored + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut R, + theme: &Theme, + _style: &renderer::Style, + layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let state = tree.state.downcast_ref::(); + + renderer.with_translation( + Vector::new(bounds.x, bounds.y), + |renderer| { + renderer.draw( as Program< + Message, + R, + >>::draw( + &StateWithStyle { + state, + style: &self.style, + bar_height: self.bar_height, + easing: self.easing, + }, + &(), + renderer, + theme, + bounds, + Cursor::Unavailable, + )); + }, + ); + } +} + +impl<'a, Message, Theme> From> + for Element<'a, Message, R> +where + Message: Clone + 'a, + Theme: StyleSheet + 'a, +{ + fn from(circular: Circular<'a, Theme>) -> Self { + Self::new(circular) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Appearance { + /// The [`Background`] of the progress indicator. + pub background: Option, + /// The track [`Color`] of the progress indicator. + pub track_color: Color, + /// The bar [`Color`] of the progress indicator. + pub bar_color: Color, +} + +impl std::default::Default for Appearance { + fn default() -> Self { + Self { + background: None, + track_color: Color::TRANSPARENT, + bar_color: Color::BLACK, + } + } +} + +/// A set of rules that dictate the style of an indicator. +pub trait StyleSheet { + /// The supported style of the [`StyleSheet`]. + type Style: Default; + + /// Produces the active [`Appearance`] of a indicator. + fn appearance(&self, style: &Self::Style) -> Appearance; +} + +impl StyleSheet for iced::Theme { + type Style = (); + + fn appearance(&self, _style: &Self::Style) -> Appearance { + let palette = self.extended_palette(); + + Appearance { + background: None, + track_color: palette.background.weak.color, + bar_color: palette.primary.base.color, + } + } +} + +struct StateWithStyle<'a, Theme> +where + Theme: StyleSheet, +{ + state: &'a State, + style: &'a ::Style, + easing: &'a Easing, + bar_height: f32, +} + +impl<'a, Message, Theme> + canvas::Program> + for StateWithStyle<'a, Theme> +where + Theme: StyleSheet, +{ + type State = (); + + fn update( + &self, + _state: &mut Self::State, + _event: canvas::Event, + _bounds: Rectangle, + _cursor: canvas::Cursor, + ) -> (canvas::event::Status, Option) { + (canvas::event::Status::Ignored, None) + } + + fn draw( + &self, + _state: &Self::State, + renderer: &iced_widget::renderer::Renderer, + theme: &Theme, + bounds: Rectangle, + _cursor: canvas::Cursor, + ) -> Vec { + let mut frame = canvas::Frame::new(renderer, bounds.size()); + let custom_style = ::appearance(theme, self.style); + let track_radius = frame.width() / 2.0 - self.bar_height; + + if let Some(Background::Color(color)) = custom_style.background { + let background_path = + canvas::Path::circle(frame.center(), track_radius); + frame.fill(&background_path, color); + } + + let track_path = canvas::Path::circle(frame.center(), track_radius); + + frame.stroke( + &track_path, + canvas::Stroke::default() + .with_color(custom_style.track_color) + .with_width(self.bar_height), + ); + + let mut builder = canvas::path::Builder::new(); + + let start = self.state.procession() * 2.0 * PI; + + match self.state { + State::Expanding { progress, .. } => { + builder.arc(canvas::path::Arc { + center: frame.center(), + radius: track_radius, + start_angle: start, + end_angle: start + + MIN_RADIANS + + WRAP_RADIANS * (self.easing.y_at_x(*progress)), + }); + } + State::Contracting { progress, .. } => { + builder.arc(canvas::path::Arc { + center: frame.center(), + radius: track_radius, + start_angle: start + + WRAP_RADIANS * (self.easing.y_at_x(*progress)), + end_angle: start + MIN_RADIANS + WRAP_RADIANS, + }); + } + } + + let bar_path = builder.build(); + + frame.stroke( + &bar_path, + canvas::Stroke::default() + .with_color(custom_style.bar_color) + .with_width(self.bar_height), + ); + + vec![frame.into_geometry()] + } +} diff --git a/examples/loading_spinners/src/easing.rs b/examples/loading_spinners/src/easing.rs new file mode 100644 index 00000000..04befa15 --- /dev/null +++ b/examples/loading_spinners/src/easing.rs @@ -0,0 +1,132 @@ +use iced_core::Point; +use lyon::algorithms::measure::PathMeasurements; +use lyon::path::builder::NoAttributes; +use lyon::path::path::BuilderImpl; +use lyon::path::Path; + +use once_cell::sync::Lazy; + +pub static EMPHASIZED: Lazy = Lazy::new(|| { + Easing::builder() + .cubic_bezier_to([0.05, 0.0], [0.133333, 0.06], [0.166666, 0.4]) + .cubic_bezier_to([0.208333, 0.82], [0.25, 1.0], [1.0, 1.0]) + .build() +}); + +pub static EMPHASIZED_DECELERATE: Lazy = Lazy::new(|| { + Easing::builder() + .cubic_bezier_to([0.05, 0.7], [0.1, 1.0], [1.0, 1.0]) + .build() +}); + +pub static EMPHASIZED_ACCELERATE: Lazy = Lazy::new(|| { + Easing::builder() + .cubic_bezier_to([0.3, 0.0], [0.8, 0.15], [1.0, 1.0]) + .build() +}); + +pub static STANDARD: Lazy = Lazy::new(|| { + Easing::builder() + .cubic_bezier_to([0.2, 0.0], [0.0, 1.0], [1.0, 1.0]) + .build() +}); + +pub static STANDARD_DECELERATE: Lazy = Lazy::new(|| { + Easing::builder() + .cubic_bezier_to([0.0, 0.0], [0.0, 1.0], [1.0, 1.0]) + .build() +}); + +pub static STANDARD_ACCELERATE: Lazy = Lazy::new(|| { + Easing::builder() + .cubic_bezier_to([0.3, 0.0], [1.0, 1.0], [1.0, 1.0]) + .build() +}); + +pub struct Easing { + path: Path, + measurements: PathMeasurements, +} + +impl Easing { + pub fn builder() -> Builder { + Builder::new() + } + + pub fn y_at_x(&self, x: f32) -> f32 { + let mut sampler = self.measurements.create_sampler( + &self.path, + lyon::algorithms::measure::SampleType::Normalized, + ); + let sample = sampler.sample(x); + + sample.position().y + } +} + +pub struct Builder(NoAttributes); + +impl Builder { + pub fn new() -> Self { + let mut builder = Path::builder(); + builder.begin(lyon::geom::point(0.0, 0.0)); + + Self(builder) + } + + /// Adds a line segment. Points must be between 0,0 and 1,1 + pub fn line_to(mut self, to: impl Into) -> Self { + self.0.line_to(Self::point(to)); + + self + } + + /// Adds a quadratic bézier curve. Points must be between 0,0 and 1,1 + pub fn quadratic_bezier_to( + mut self, + ctrl: impl Into, + to: impl Into, + ) -> Self { + self.0 + .quadratic_bezier_to(Self::point(ctrl), Self::point(to)); + + self + } + + /// Adds a cubic bézier curve. Points must be between 0,0 and 1,1 + pub fn cubic_bezier_to( + mut self, + ctrl1: impl Into, + ctrl2: impl Into, + to: impl Into, + ) -> Self { + self.0.cubic_bezier_to( + Self::point(ctrl1), + Self::point(ctrl2), + Self::point(to), + ); + + self + } + + pub fn build(mut self) -> Easing { + self.0.line_to(lyon::geom::point(1.0, 1.0)); + self.0.end(false); + + let path = self.0.build(); + let measurements = PathMeasurements::from_path(&path, 0.0); + + Easing { path, measurements } + } + + fn point(p: impl Into) -> lyon::geom::Point { + let p: Point = p.into(); + lyon::geom::point(p.x.min(1.0).max(0.0), p.y.min(1.0).max(0.0)) + } +} + +impl Default for Builder { + fn default() -> Self { + Self::new() + } +} diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs new file mode 100644 index 00000000..eb8a7408 --- /dev/null +++ b/examples/loading_spinners/src/linear.rs @@ -0,0 +1,324 @@ +//! Show a linear progress indicator. +use iced::window; +use iced_core::event::{self, Event}; +use iced_core::renderer; +use iced_core::time::Instant; +use iced_core::widget::tree::{self, Tree}; +use iced_core::window::RedrawRequest; +use iced_core::{layout, Size}; +use iced_core::{ + Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, + Shell, Widget, +}; + +use super::easing::{self, Easing}; + +use std::time::Duration; + +#[allow(missing_debug_implementations)] +pub struct Linear<'a, Renderer> +where + Renderer: iced_core::Renderer, + Renderer::Theme: StyleSheet, +{ + width: Length, + height: Length, + style: ::Style, + easing: &'a Easing, + cycle_duration: Duration, +} + +impl<'a, Renderer> Linear<'a, Renderer> +where + Renderer: iced_widget::core::Renderer, + Renderer::Theme: StyleSheet, +{ + /// Creates a new [`Linear`] with the given content. + pub fn new() -> Self { + Linear { + width: Length::Fixed(100.0), + height: Length::Fixed(4.0), + style: ::Style::default(), + easing: &easing::STANDARD, + cycle_duration: Duration::from_millis(600), + } + } + + /// Sets the width of the [`Linear`]. + pub fn width(mut self, width: impl Into) -> Self { + self.width = width.into(); + self + } + + /// Sets the height of the [`Linear`]. + pub fn height(mut self, height: impl Into) -> Self { + self.height = height.into(); + self + } + + /// Sets the style variant of this [`Linear`]. + pub fn style( + mut self, + style: ::Style, + ) -> Self { + self.style = style; + self + } + + /// Sets the motion easing of this [`Linear`]. + pub fn easing(mut self, easing: &'a Easing) -> Self { + self.easing = easing; + self + } + + /// Sets the cycle duration of this [`Linear`]. + pub fn cycle_duration(mut self, duration: Duration) -> Self { + self.cycle_duration = duration / 2; + self + } +} + +impl<'a, Renderer> Default for Linear<'a, Renderer> +where + Renderer: iced_core::Renderer, + Renderer::Theme: StyleSheet, +{ + fn default() -> Self { + Self::new() + } +} + +#[derive(Clone, Copy)] +enum State { + Expanding { start: Instant, progress: f32 }, + Contracting { start: Instant, progress: f32 }, +} + +impl Default for State { + fn default() -> Self { + Self::Expanding { + start: Instant::now(), + progress: 0.0, + } + } +} + +impl State { + fn next(&self, now: Instant) -> Self { + match self { + Self::Expanding { .. } => Self::Contracting { + start: now, + progress: 0.0, + }, + Self::Contracting { .. } => Self::Expanding { + start: now, + progress: 0.0, + }, + } + } + + fn start(&self) -> Instant { + match self { + Self::Expanding { start, .. } | Self::Contracting { start, .. } => { + *start + } + } + } + + fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { + let elapsed = now.duration_since(self.start()); + + match elapsed { + elapsed if elapsed > cycle_duration => self.next(now), + _ => self.with_elapsed(cycle_duration, elapsed), + } + } + + fn with_elapsed( + &self, + cycle_duration: Duration, + elapsed: Duration, + ) -> Self { + let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); + match self { + Self::Expanding { start, .. } => Self::Expanding { + start: *start, + progress, + }, + Self::Contracting { start, .. } => Self::Contracting { + start: *start, + progress, + }, + } + } +} + +impl<'a, Message, Renderer> Widget for Linear<'a, Renderer> +where + Message: 'a + Clone, + Renderer: 'a + iced_core::Renderer, + Renderer::Theme: StyleSheet, +{ + fn tag(&self) -> tree::Tag { + tree::Tag::of::() + } + + fn state(&self) -> tree::State { + tree::State::new(State::default()) + } + + fn width(&self) -> Length { + self.width + } + + fn height(&self) -> Length { + self.height + } + + fn layout( + &self, + _renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + let limits = limits.width(self.width).height(self.height); + let size = limits.resolve(Size::ZERO); + + layout::Node::new(size) + } + + fn on_event( + &mut self, + tree: &mut Tree, + event: Event, + _layout: Layout<'_>, + _cursor_position: Point, + _renderer: &Renderer, + _clipboard: &mut dyn Clipboard, + shell: &mut Shell<'_, Message>, + ) -> event::Status { + let state = tree.state.downcast_mut::(); + + if let Event::Window(window::Event::RedrawRequested(now)) = event { + *state = state.timed_transition(self.cycle_duration, now); + + shell.request_redraw(RedrawRequest::NextFrame); + } + + event::Status::Ignored + } + + fn draw( + &self, + tree: &Tree, + renderer: &mut Renderer, + theme: &Renderer::Theme, + _style: &renderer::Style, + layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + let custom_style = theme.appearance(&self.style); + let state = tree.state.downcast_ref::(); + + renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: bounds.y, + width: bounds.width, + height: bounds.height, + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Background::Color(custom_style.track_color), + ); + + match state { + State::Expanding { progress, .. } => renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x, + y: bounds.y, + width: self.easing.y_at_x(*progress) * bounds.width, + height: bounds.height, + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Background::Color(custom_style.bar_color), + ), + + State::Contracting { progress, .. } => renderer.fill_quad( + renderer::Quad { + bounds: Rectangle { + x: bounds.x + + self.easing.y_at_x(*progress) * bounds.width, + y: bounds.y, + width: (1.0 - self.easing.y_at_x(*progress)) + * bounds.width, + height: bounds.height, + }, + border_radius: 0.0.into(), + border_width: 0.0, + border_color: Color::TRANSPARENT, + }, + Background::Color(custom_style.bar_color), + ), + } + } +} + +impl<'a, Message, Renderer> From> + for Element<'a, Message, Renderer> +where + Message: Clone + 'a, + Renderer: iced_core::Renderer + 'a, + Renderer::Theme: StyleSheet, +{ + fn from(linear: Linear<'a, Renderer>) -> Self { + Self::new(linear) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Appearance { + /// The track [`Color`] of the progress indicator. + pub track_color: Color, + /// The bar [`Color`] of the progress indicator. + pub bar_color: Color, +} + +impl std::default::Default for Appearance { + fn default() -> Self { + Self { + track_color: Color::TRANSPARENT, + bar_color: Color::BLACK, + } + } +} + +/// A set of rules that dictate the style of an indicator. +pub trait StyleSheet { + /// The supported style of the [`StyleSheet`]. + type Style: Default; + + /// Produces the active [`Appearance`] of a indicator. + fn appearance(&self, style: &Self::Style) -> Appearance; +} + +impl StyleSheet for iced::Theme { + type Style = (); + + fn appearance(&self, _style: &Self::Style) -> Appearance { + let palette = self.extended_palette(); + + Appearance { + track_color: palette.background.weak.color, + bar_color: palette.primary.base.color, + } + } +} diff --git a/examples/loading_spinners/src/main.rs b/examples/loading_spinners/src/main.rs new file mode 100644 index 00000000..a78e9590 --- /dev/null +++ b/examples/loading_spinners/src/main.rs @@ -0,0 +1,118 @@ +use iced::executor; +use iced::widget::{column, container, row, slider, text}; +use iced::{Application, Command, Element, Length, Settings, Theme}; + +use std::time::Duration; + +mod circular; +mod easing; +mod linear; + +use circular::Circular; +use linear::Linear; + +pub fn main() -> iced::Result { + LoadingSpinners::run(Settings { + antialiasing: true, + ..Default::default() + }) +} + +struct LoadingSpinners { + cycle_duration: f32, +} + +impl Default for LoadingSpinners { + fn default() -> Self { + Self { + cycle_duration: 2.0, + } + } +} + +#[derive(Debug, Clone, Copy)] +enum Message { + CycleDurationChanged(f32), +} + +impl Application for LoadingSpinners { + type Message = Message; + type Flags = (); + type Executor = executor::Default; + type Theme = Theme; + + fn new(_flags: Self::Flags) -> (Self, Command) { + (Self::default(), Command::none()) + } + + fn title(&self) -> String { + String::from("Loading Spinners - Iced") + } + + fn update(&mut self, message: Message) -> Command { + match message { + Message::CycleDurationChanged(duration) => { + self.cycle_duration = duration; + } + } + + Command::none() + } + + fn view(&self) -> Element { + let column = [ + &easing::EMPHASIZED, + &easing::EMPHASIZED_DECELERATE, + &easing::EMPHASIZED_ACCELERATE, + &easing::STANDARD, + &easing::STANDARD_DECELERATE, + &easing::STANDARD_ACCELERATE, + ] + .iter() + .zip([ + "Emphasized:", + "Emphasized Decelerate:", + "Emphasized Accelerate:", + "Standard:", + "Standard Decelerate:", + "Standard Accelerate:", + ]) + .fold(column![], |column, (easing, label)| { + column.push( + row![ + text(label).width(250), + Linear::new().easing(easing).cycle_duration( + Duration::from_secs_f32(self.cycle_duration) + ), + Circular::new().easing(easing).cycle_duration( + Duration::from_secs_f32(self.cycle_duration) + ) + ] + .align_items(iced::Alignment::Center) + .spacing(20.0), + ) + }) + .spacing(20); + + container( + column.push( + row(vec![ + text("Cycle duration:").into(), + slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| { + Message::CycleDurationChanged(x / 100.0) + }) + .width(200.0) + .into(), + text(format!("{:.2}s", self.cycle_duration)).into(), + ]) + .align_items(iced::Alignment::Center) + .spacing(20.0), + ), + ) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} diff --git a/examples/progress_indicators/Cargo.toml b/examples/progress_indicators/Cargo.toml deleted file mode 100644 index 89ceb127..00000000 --- a/examples/progress_indicators/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "progress_indicators" -version = "0.1.0" -authors = ["Nick Senger "] -edition = "2021" -publish = false - -[dependencies] -flo_curves = "0.7" -iced = { path = "../..", features = ["canvas"] } -iced_core = { path = "../../core" } -iced_widget = { path = "../../widget" } -lazy_static = "1.4" diff --git a/examples/progress_indicators/README.md b/examples/progress_indicators/README.md deleted file mode 100644 index acacca8f..00000000 --- a/examples/progress_indicators/README.md +++ /dev/null @@ -1,14 +0,0 @@ -## Progress indicators - -Example implementation of animated indeterminate progress indicators. - - - -You can run it with `cargo run`: -``` -cargo run --package progress_indicators -``` diff --git a/examples/progress_indicators/src/circular.rs b/examples/progress_indicators/src/circular.rs deleted file mode 100644 index 2a9f7e1e..00000000 --- a/examples/progress_indicators/src/circular.rs +++ /dev/null @@ -1,425 +0,0 @@ -//! Show a circular progress indicator. -use iced::widget::canvas::{self, Cursor, Program, Renderer as CanvasRenderer}; -use iced::window; -use iced_core::event::{self, Event}; -use iced_core::time::Instant; -use iced_core::widget::tree::{self, Tree}; -use iced_core::window::RedrawRequest; -use iced_core::{layout, Size}; -use iced_core::{renderer, Vector}; -use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Renderer, Shell, Widget, -}; - -use super::easing::{self, Easing}; - -use std::borrow::Cow; -use std::f32::consts::PI; -use std::time::Duration; - -type R = iced_widget::renderer::Renderer; - -const MIN_RADIANS: f32 = PI / 8.0; -const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0; -const PROCESSION_VELOCITY: u32 = u32::MAX / 120; - -#[allow(missing_debug_implementations)] -pub struct Circular<'a, Theme> -where - Theme: StyleSheet, -{ - size: f32, - bar_height: f32, - style: ::Style, - easing: Cow<'a, Easing>, - cycle_duration: Duration, -} - -impl<'a, Theme> Circular<'a, Theme> -where - Theme: StyleSheet, -{ - /// Creates a new [`Circular`] with the given content. - pub fn new() -> Self { - Circular { - size: 40.0, - bar_height: 4.0, - style: ::Style::default(), - easing: Cow::Borrowed(&easing::STANDARD), - cycle_duration: Duration::from_millis(600), - } - } - - /// Sets the size of the [`Circular`]. - pub fn size(mut self, size: f32) -> Self { - self.size = size; - self - } - - /// Sets the bar height of the [`Circular`]. - pub fn bar_height(mut self, bar_height: f32) -> Self { - self.bar_height = bar_height; - self - } - - /// Sets the style variant of this [`Circular`]. - pub fn style(mut self, style: ::Style) -> Self { - self.style = style; - self - } - - /// Sets the easing of this [`Circular`]. - pub fn easing(mut self, easing: impl Into>) -> Self { - self.easing = easing.into(); - self - } - - /// Sets the cycle duration of this [`Circular`]. - pub fn cycle_duration(mut self, duration: Duration) -> Self { - self.cycle_duration = duration / 2; - self - } -} - -impl<'a, Theme> Default for Circular<'a, Theme> -where - Theme: StyleSheet, -{ - fn default() -> Self { - Self::new() - } -} - -#[derive(Clone, Copy)] -enum State { - Expanding { - start: Instant, - progress: f32, - procession: u32, - }, - Contracting { - start: Instant, - progress: f32, - procession: u32, - }, -} - -impl Default for State { - fn default() -> Self { - Self::Expanding { - start: Instant::now(), - progress: 0.0, - procession: 0, - } - } -} - -impl State { - fn next(&self, now: Instant) -> Self { - match self { - Self::Expanding { procession, .. } => Self::Contracting { - start: now, - progress: 0.0, - procession: procession.wrapping_add(PROCESSION_VELOCITY), - }, - Self::Contracting { procession, .. } => Self::Expanding { - start: now, - progress: 0.0, - procession: procession.wrapping_add( - PROCESSION_VELOCITY.wrapping_add( - ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32, - ), - ), - }, - } - } - - fn start(&self) -> Instant { - match self { - Self::Expanding { start, .. } | Self::Contracting { start, .. } => { - *start - } - } - } - - fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { - let elapsed = now.duration_since(self.start()); - - match elapsed { - elapsed if elapsed > cycle_duration => self.next(now), - _ => self.with_elapsed(cycle_duration, elapsed), - } - } - - fn with_elapsed( - &self, - cycle_duration: Duration, - elapsed: Duration, - ) -> Self { - let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); - match self { - Self::Expanding { - start, procession, .. - } => Self::Expanding { - start: *start, - progress, - procession: procession.wrapping_add(PROCESSION_VELOCITY), - }, - Self::Contracting { - start, procession, .. - } => Self::Contracting { - start: *start, - progress, - procession: procession.wrapping_add(PROCESSION_VELOCITY), - }, - } - } - - fn procession(&self) -> f32 { - match self { - Self::Expanding { procession, .. } - | Self::Contracting { procession, .. } => { - *procession as f32 / u32::MAX as f32 - } - } - } -} - -impl<'a, Message, Theme> Widget> for Circular<'a, Theme> -where - Message: 'a + Clone, - Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State::default()) - } - - fn width(&self) -> Length { - Length::Fixed(self.size) - } - - fn height(&self) -> Length { - Length::Fixed(self.size) - } - - fn layout( - &self, - _renderer: &iced_widget::renderer::Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.width(self.size).height(self.size); - let size = limits.resolve(Size::ZERO); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - _layout: Layout<'_>, - _cursor_position: Point, - _renderer: &iced_widget::renderer::Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let state = tree.state.downcast_mut::(); - - if let Event::Window(window::Event::RedrawRequested(now)) = event { - *state = state.timed_transition(self.cycle_duration, now); - - shell.request_redraw(RedrawRequest::NextFrame); - } - - event::Status::Ignored - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut R, - theme: &Theme, - _style: &renderer::Style, - layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - let bounds = layout.bounds(); - let state = tree.state.downcast_ref::(); - - renderer.with_translation( - Vector::new(bounds.x, bounds.y), - |renderer| { - renderer.draw( as Program< - Message, - R, - >>::draw( - &StateWithStyle { - state, - style: &self.style, - bar_height: self.bar_height, - easing: self.easing.as_ref(), - }, - &(), - renderer, - theme, - bounds, - Cursor::Unavailable, - )); - }, - ); - } -} - -impl<'a, Message, Theme> From> - for Element<'a, Message, R> -where - Message: Clone + 'a, - Theme: StyleSheet + 'a, -{ - fn from(circular: Circular<'a, Theme>) -> Self { - Self::new(circular) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Appearance { - /// The [`Background`] of the progress indicator. - pub background: Option, - /// The track [`Color`] of the progress indicator. - pub track_color: Color, - /// The bar [`Color`] of the progress indicator. - pub bar_color: Color, -} - -impl std::default::Default for Appearance { - fn default() -> Self { - Self { - background: None, - track_color: Color::TRANSPARENT, - bar_color: Color::BLACK, - } - } -} - -/// A set of rules that dictate the style of an indicator. -pub trait StyleSheet { - /// The supported style of the [`StyleSheet`]. - type Style: Default; - - /// Produces the active [`Appearance`] of a indicator. - fn appearance(&self, style: &Self::Style) -> Appearance; -} - -impl StyleSheet for iced::Theme { - type Style = (); - - fn appearance(&self, _style: &Self::Style) -> Appearance { - let palette = self.extended_palette(); - - Appearance { - background: None, - track_color: palette.background.weak.color, - bar_color: palette.primary.base.color, - } - } -} - -struct StateWithStyle<'a, Theme> -where - Theme: StyleSheet, -{ - state: &'a State, - style: &'a ::Style, - easing: &'a Easing, - bar_height: f32, -} - -impl<'a, Message, Theme> - canvas::Program> - for StateWithStyle<'a, Theme> -where - Theme: StyleSheet, -{ - type State = (); - - fn update( - &self, - _state: &mut Self::State, - _event: canvas::Event, - _bounds: Rectangle, - _cursor: canvas::Cursor, - ) -> (canvas::event::Status, Option) { - (canvas::event::Status::Ignored, None) - } - - fn draw( - &self, - _state: &Self::State, - renderer: &iced_widget::renderer::Renderer, - theme: &Theme, - bounds: Rectangle, - _cursor: canvas::Cursor, - ) -> Vec { - let mut frame = canvas::Frame::new(renderer, bounds.size()); - let custom_style = ::appearance(theme, self.style); - let track_radius = frame.width() / 2.0 - self.bar_height; - - if let Some(Background::Color(color)) = custom_style.background { - let background_path = - canvas::Path::circle(frame.center(), track_radius); - frame.fill(&background_path, color); - } - - let track_path = canvas::Path::circle(frame.center(), track_radius); - - frame.stroke( - &track_path, - canvas::Stroke::default() - .with_color(custom_style.track_color) - .with_width(self.bar_height), - ); - - let mut builder = canvas::path::Builder::new(); - - let start = self.state.procession() * 2.0 * PI; - - match self.state { - State::Expanding { progress, .. } => { - builder.arc(canvas::path::Arc { - center: frame.center(), - radius: track_radius, - start_angle: start, - end_angle: start - + MIN_RADIANS - + WRAP_RADIANS * (self.easing.y_at_x(*progress)), - }); - } - State::Contracting { progress, .. } => { - builder.arc(canvas::path::Arc { - center: frame.center(), - radius: track_radius, - start_angle: start - + WRAP_RADIANS * (self.easing.y_at_x(*progress)), - end_angle: start + MIN_RADIANS + WRAP_RADIANS, - }); - } - } - - let bar_path = builder.build(); - - frame.stroke( - &bar_path, - canvas::Stroke::default() - .with_color(custom_style.bar_color) - .with_width(self.bar_height), - ); - - vec![frame.into_geometry()] - } -} diff --git a/examples/progress_indicators/src/easing.rs b/examples/progress_indicators/src/easing.rs deleted file mode 100644 index 10479659..00000000 --- a/examples/progress_indicators/src/easing.rs +++ /dev/null @@ -1,67 +0,0 @@ -use flo_curves::bezier::Curve; -use flo_curves::*; -use lazy_static::lazy_static; - -use std::borrow::Cow; - -lazy_static! { - pub static ref EXAMPLES: [Easing; 3] = [ - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.05, 0.7), Coord2(0.1, 1.0)), - Coord2(1.0, 1.0), - )), - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.3, 0.0), Coord2(0.8, 0.15)), - Coord2(1.0, 1.0), - )), - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.2, 0.0), Coord2(0.0, 1.0)), - Coord2(1.0, 1.0), - )) - ]; - pub static ref STANDARD: Easing = { - Easing::CubicBezier(Curve::from_points( - Coord2(0.0, 0.0), - (Coord2(0.2, 0.0), Coord2(0.0, 1.0)), - Coord2(1.0, 1.0), - )) - }; -} - -#[derive(Clone, Debug)] -pub enum Easing { - BezierPath(Vec>), - CubicBezier(Curve), -} - -impl Easing { - pub fn y_at_x(&self, x: f32) -> f32 { - let x = x as f64; - - match self { - Self::BezierPath(curves) => curves - .iter() - .find_map(|curve| { - (curve.start_point().0 <= x && curve.end_point().0 >= x) - .then(|| curve.point_at_pos(x).1 as f32) - }) - .unwrap_or_default(), - Self::CubicBezier(curve) => curve.point_at_pos(x).1 as f32, - } - } -} - -impl<'a> From for Cow<'a, Easing> { - fn from(easing: Easing) -> Self { - Cow::Owned(easing) - } -} - -impl<'a> From<&'a Easing> for Cow<'a, Easing> { - fn from(easing: &'a Easing) -> Self { - Cow::Borrowed(easing) - } -} diff --git a/examples/progress_indicators/src/linear.rs b/examples/progress_indicators/src/linear.rs deleted file mode 100644 index 735892e7..00000000 --- a/examples/progress_indicators/src/linear.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Show a linear progress indicator. -use iced::window; -use iced_core::event::{self, Event}; -use iced_core::renderer; -use iced_core::time::Instant; -use iced_core::widget::tree::{self, Tree}; -use iced_core::window::RedrawRequest; -use iced_core::{layout, Size}; -use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Shell, Widget, -}; - -use super::easing::{self, Easing}; - -use std::borrow::Cow; -use std::time::Duration; - -#[allow(missing_debug_implementations)] -pub struct Linear<'a, Renderer> -where - Renderer: iced_core::Renderer, - Renderer::Theme: StyleSheet, -{ - width: Length, - height: Length, - style: ::Style, - easing: Cow<'a, Easing>, - cycle_duration: Duration, -} - -impl<'a, Renderer> Linear<'a, Renderer> -where - Renderer: iced_widget::core::Renderer, - Renderer::Theme: StyleSheet, -{ - /// Creates a new [`Linear`] with the given content. - pub fn new() -> Self { - Linear { - width: Length::Fixed(100.0), - height: Length::Fixed(4.0), - style: ::Style::default(), - easing: Cow::Borrowed(&easing::STANDARD), - cycle_duration: Duration::from_millis(600), - } - } - - /// Sets the width of the [`Linear`]. - pub fn width(mut self, width: impl Into) -> Self { - self.width = width.into(); - self - } - - /// Sets the height of the [`Linear`]. - pub fn height(mut self, height: impl Into) -> Self { - self.height = height.into(); - self - } - - /// Sets the style variant of this [`Linear`]. - pub fn style( - mut self, - style: ::Style, - ) -> Self { - self.style = style; - self - } - - /// Sets the motion easing of this [`Linear`]. - pub fn easing(mut self, easing: impl Into>) -> Self { - self.easing = easing.into(); - self - } - - /// Sets the cycle duration of this [`Linear`]. - pub fn cycle_duration(mut self, duration: Duration) -> Self { - self.cycle_duration = duration / 2; - self - } -} - -impl<'a, Renderer> Default for Linear<'a, Renderer> -where - Renderer: iced_core::Renderer, - Renderer::Theme: StyleSheet, -{ - fn default() -> Self { - Self::new() - } -} - -#[derive(Clone, Copy)] -enum State { - Expanding { start: Instant, progress: f32 }, - Contracting { start: Instant, progress: f32 }, -} - -impl Default for State { - fn default() -> Self { - Self::Expanding { - start: Instant::now(), - progress: 0.0, - } - } -} - -impl State { - fn next(&self, now: Instant) -> Self { - match self { - Self::Expanding { .. } => Self::Contracting { - start: now, - progress: 0.0, - }, - Self::Contracting { .. } => Self::Expanding { - start: now, - progress: 0.0, - }, - } - } - - fn start(&self) -> Instant { - match self { - Self::Expanding { start, .. } | Self::Contracting { start, .. } => { - *start - } - } - } - - fn timed_transition(&self, cycle_duration: Duration, now: Instant) -> Self { - let elapsed = now.duration_since(self.start()); - - match elapsed { - elapsed if elapsed > cycle_duration => self.next(now), - _ => self.with_elapsed(cycle_duration, elapsed), - } - } - - fn with_elapsed( - &self, - cycle_duration: Duration, - elapsed: Duration, - ) -> Self { - let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); - match self { - Self::Expanding { start, .. } => Self::Expanding { - start: *start, - progress, - }, - Self::Contracting { start, .. } => Self::Contracting { - start: *start, - progress, - }, - } - } -} - -impl<'a, Message, Renderer> Widget for Linear<'a, Renderer> -where - Message: 'a + Clone, - Renderer: 'a + iced_core::Renderer, - Renderer::Theme: StyleSheet, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State::default()) - } - - fn width(&self) -> Length { - self.width - } - - fn height(&self) -> Length { - self.height - } - - fn layout( - &self, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - let limits = limits.width(self.width).height(self.height); - let size = limits.resolve(Size::ZERO); - - layout::Node::new(size) - } - - fn on_event( - &mut self, - tree: &mut Tree, - event: Event, - _layout: Layout<'_>, - _cursor_position: Point, - _renderer: &Renderer, - _clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let state = tree.state.downcast_mut::(); - - if let Event::Window(window::Event::RedrawRequested(now)) = event { - *state = state.timed_transition(self.cycle_duration, now); - - shell.request_redraw(RedrawRequest::NextFrame); - } - - event::Status::Ignored - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Renderer::Theme, - _style: &renderer::Style, - layout: Layout<'_>, - _cursor_position: Point, - _viewport: &Rectangle, - ) { - let bounds = layout.bounds(); - let custom_style = theme.appearance(&self.style); - let state = tree.state.downcast_ref::(); - - renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: bounds.y, - width: bounds.width, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(custom_style.track_color), - ); - - match state { - State::Expanding { progress, .. } => renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x, - y: bounds.y, - width: self.easing.y_at_x(*progress) * bounds.width, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(custom_style.bar_color), - ), - - State::Contracting { progress, .. } => renderer.fill_quad( - renderer::Quad { - bounds: Rectangle { - x: bounds.x - + self.easing.y_at_x(*progress) * bounds.width, - y: bounds.y, - width: (1.0 - self.easing.y_at_x(*progress)) - * bounds.width, - height: bounds.height, - }, - border_radius: 0.0.into(), - border_width: 0.0, - border_color: Color::TRANSPARENT, - }, - Background::Color(custom_style.bar_color), - ), - } - } -} - -impl<'a, Message, Renderer> From> - for Element<'a, Message, Renderer> -where - Message: Clone + 'a, - Renderer: iced_core::Renderer + 'a, - Renderer::Theme: StyleSheet, -{ - fn from(linear: Linear<'a, Renderer>) -> Self { - Self::new(linear) - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Appearance { - /// The track [`Color`] of the progress indicator. - pub track_color: Color, - /// The bar [`Color`] of the progress indicator. - pub bar_color: Color, -} - -impl std::default::Default for Appearance { - fn default() -> Self { - Self { - track_color: Color::TRANSPARENT, - bar_color: Color::BLACK, - } - } -} - -/// A set of rules that dictate the style of an indicator. -pub trait StyleSheet { - /// The supported style of the [`StyleSheet`]. - type Style: Default; - - /// Produces the active [`Appearance`] of a indicator. - fn appearance(&self, style: &Self::Style) -> Appearance; -} - -impl StyleSheet for iced::Theme { - type Style = (); - - fn appearance(&self, _style: &Self::Style) -> Appearance { - let palette = self.extended_palette(); - - Appearance { - track_color: palette.background.weak.color, - bar_color: palette.primary.base.color, - } - } -} diff --git a/examples/progress_indicators/src/main.rs b/examples/progress_indicators/src/main.rs deleted file mode 100644 index 136b8d8c..00000000 --- a/examples/progress_indicators/src/main.rs +++ /dev/null @@ -1,104 +0,0 @@ -use iced::executor; -use iced::widget::{column, container, row, slider, text}; -use iced::{Application, Command, Element, Length, Settings, Theme}; - -use std::time::Duration; - -mod circular; -mod easing; -mod linear; - -use circular::Circular; -use linear::Linear; - -pub fn main() -> iced::Result { - ProgressIndicators::run(Settings { - antialiasing: true, - ..Default::default() - }) -} - -struct ProgressIndicators { - cycle_duration: f32, -} - -impl Default for ProgressIndicators { - fn default() -> Self { - Self { - cycle_duration: 2.0, - } - } -} - -#[derive(Debug, Clone, Copy)] -enum Message { - CycleDurationChanged(f32), -} - -impl Application for ProgressIndicators { - type Message = Message; - type Flags = (); - type Executor = executor::Default; - type Theme = Theme; - - fn new(_flags: Self::Flags) -> (Self, Command) { - (Self::default(), Command::none()) - } - - fn title(&self) -> String { - String::from("Progress Indicators - Iced") - } - - fn update(&mut self, message: Message) -> Command { - match message { - Message::CycleDurationChanged(duration) => { - self.cycle_duration = duration; - } - } - - Command::none() - } - - fn view(&self) -> Element { - let column = easing::EXAMPLES - .iter() - .zip(["Decelerating:", "Accelerating:", "Standard:"]) - .fold(column![], |column, (easing, label)| { - column.push( - row![ - text(label).width(150), - Linear::new().easing(easing).cycle_duration( - Duration::from_secs_f32(self.cycle_duration) - ), - Circular::new().easing(easing).cycle_duration( - Duration::from_secs_f32(self.cycle_duration) - ) - ] - .align_items(iced::Alignment::Center) - .spacing(20.0), - ) - }) - .spacing(20); - - container( - column.push( - row(vec![ - text("Cycle duration:").into(), - slider(1.0..=1000.0, self.cycle_duration * 100.0, |x| { - Message::CycleDurationChanged(x / 100.0) - }) - .width(150.0) - .into(), - text(format!("{:.2}s", self.cycle_duration)).into(), - ]) - .align_items(iced::Alignment::Center) - .spacing(20.0), - ), - ) - .width(Length::Fill) - .height(Length::Fill) - .center_x() - .center_y() - .into() - } -} -- cgit From 204c9d6f52dab7b04ecec35220b15768296dfe1e Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Wed, 7 Jun 2023 16:48:11 -0700 Subject: fix: rotation in terms of duration --- examples/loading_spinners/src/circular.rs | 83 ++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 29 deletions(-) (limited to 'examples') diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 0d01e7a1..4f5b6fe4 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -33,7 +33,7 @@ where style: ::Style, easing: &'a Easing, cycle_duration: Duration, - rotation_speed: u32, + rotaion_duration: Duration, } impl<'a, Theme> Circular<'a, Theme> @@ -48,7 +48,7 @@ where style: ::Style::default(), easing: &easing::STANDARD, cycle_duration: Duration::from_millis(600), - rotation_speed: BASE_ROTATION_SPEED, + rotaion_duration: Duration::from_secs(2), } } @@ -82,11 +82,10 @@ where self } - /// Sets the rotation speed of this [`Circular`]. Must be set to between 0.0 and 10.0. - /// Defaults to 1.0. - pub fn rotation_speed(mut self, speed: f32) -> Self { - let multiplier = speed.min(10.0).max(0.0); - self.rotation_speed = (BASE_ROTATION_SPEED as f32 * multiplier) as u32; + /// Sets the base rotation duration of this [`Circular`]. This is the duration that a full + /// rotation would take if the cycle rotation were set to 0.0 (no expanding or contracting) + pub fn rotation_duration(mut self, duration: Duration) -> Self { + self.rotaion_duration = duration; self } } @@ -105,12 +104,14 @@ enum State { Expanding { start: Instant, progress: f32, - procession: u32, + rotation: u32, + last: Instant, }, Contracting { start: Instant, progress: f32, - procession: u32, + rotation: u32, + last: Instant, }, } @@ -119,27 +120,30 @@ impl Default for State { Self::Expanding { start: Instant::now(), progress: 0.0, - procession: 0, + rotation: 0, + last: Instant::now(), } } } impl State { - fn next(&self, now: Instant) -> Self { + fn next(&self, additional_rotation: u32, now: Instant) -> Self { match self { - Self::Expanding { procession, .. } => Self::Contracting { + Self::Expanding { rotation, .. } => Self::Contracting { start: now, progress: 0.0, - procession: procession.wrapping_add(BASE_ROTATION_SPEED), + rotation: rotation.wrapping_add(additional_rotation), + last: now, }, - Self::Contracting { procession, .. } => Self::Expanding { + Self::Contracting { rotation, .. } => Self::Expanding { start: now, progress: 0.0, - procession: procession.wrapping_add( + rotation: rotation.wrapping_add( BASE_ROTATION_SPEED.wrapping_add( ((WRAP_RADIANS / (2.0 * PI)) * u32::MAX as f32) as u32, ), ), + last: now, }, } } @@ -152,50 +156,71 @@ impl State { } } + fn last(&self) -> Instant { + match self { + Self::Expanding { last, .. } | Self::Contracting { last, .. } => { + *last + } + } + } + fn timed_transition( &self, cycle_duration: Duration, - rotation_speed: u32, + rotation_duration: Duration, now: Instant, ) -> Self { let elapsed = now.duration_since(self.start()); + let additional_rotation = ((now - self.last()).as_secs_f32() + / rotation_duration.as_secs_f32() + * (u32::MAX) as f32) as u32; match elapsed { - elapsed if elapsed > cycle_duration => self.next(now), - _ => self.with_elapsed(cycle_duration, rotation_speed, elapsed), + elapsed if elapsed > cycle_duration => { + self.next(additional_rotation, now) + } + _ => self.with_elapsed( + cycle_duration, + additional_rotation, + elapsed, + now, + ), } } fn with_elapsed( &self, cycle_duration: Duration, - rotation_speed: u32, + additional_rotation: u32, elapsed: Duration, + now: Instant, ) -> Self { let progress = elapsed.as_secs_f32() / cycle_duration.as_secs_f32(); match self { Self::Expanding { - start, procession, .. + start, rotation, .. } => Self::Expanding { start: *start, progress, - procession: procession.wrapping_add(rotation_speed), + rotation: rotation.wrapping_add(additional_rotation), + last: now, }, Self::Contracting { - start, procession, .. + start, rotation, .. } => Self::Contracting { start: *start, progress, - procession: procession.wrapping_add(rotation_speed), + rotation: rotation.wrapping_add(additional_rotation), + last: now, }, } } - fn procession(&self) -> f32 { + fn rotation(&self) -> f32 { match self { - Self::Expanding { procession, .. } - | Self::Contracting { procession, .. } => { - *procession as f32 / u32::MAX as f32 + Self::Expanding { rotation, .. } + | Self::Contracting { rotation, .. } => { + *rotation as f32 / u32::MAX as f32 } } } @@ -248,7 +273,7 @@ where if let Event::Window(window::Event::RedrawRequested(now)) = event { *state = state.timed_transition( self.cycle_duration, - self.rotation_speed, + self.rotaion_duration, now, ); @@ -406,7 +431,7 @@ where let mut builder = canvas::path::Builder::new(); - let start = self.state.procession() * 2.0 * PI; + let start = self.state.rotation() * 2.0 * PI; match self.state { State::Expanding { progress, .. } => { -- cgit From 56eacdb3583e701cde8cca074e151b24c3fd8df3 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Thu, 8 Jun 2023 07:47:57 -0700 Subject: comment: cleanup imports --- examples/loading_spinners/Cargo.toml | 7 +--- examples/loading_spinners/src/circular.rs | 63 +++++++++++++++---------------- examples/loading_spinners/src/easing.rs | 21 ++++++----- examples/loading_spinners/src/linear.rs | 32 ++++++++-------- 4 files changed, 58 insertions(+), 65 deletions(-) (limited to 'examples') diff --git a/examples/loading_spinners/Cargo.toml b/examples/loading_spinners/Cargo.toml index f37017c6..ee9a48aa 100644 --- a/examples/loading_spinners/Cargo.toml +++ b/examples/loading_spinners/Cargo.toml @@ -6,9 +6,6 @@ edition = "2021" publish = false [dependencies] -flo_curves = "0.7" -iced = { path = "../..", features = ["canvas"] } -iced_core = { path = "../../core" } -iced_widget = { path = "../../widget" } +iced = { path = "../..", features = ["advanced", "canvas"] } +lyon_algorithms = "1" once_cell = "1" -lyon = "1" diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 4f5b6fe4..8639780a 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -1,23 +1,21 @@ //! Show a circular progress indicator. -use iced::widget::canvas::{self, Cursor, Program, Renderer as CanvasRenderer}; -use iced::window; -use iced_core::event::{self, Event}; -use iced_core::time::Instant; -use iced_core::widget::tree::{self, Tree}; -use iced_core::window::RedrawRequest; -use iced_core::{layout, Size}; -use iced_core::{renderer, Vector}; -use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Renderer, Shell, Widget, -}; +use iced::advanced::layout; +use iced::advanced::renderer; +use iced::advanced::widget::tree::{self, Tree}; +use iced::advanced::{Clipboard, Layout, Renderer, Shell, Widget}; +use iced::event; +use iced::time::Instant; +use iced::widget::canvas::{self, Cursor, Program}; +use iced::window::{self, RedrawRequest}; +use iced::{Background, Color, Element, Rectangle}; +use iced::{Event, Length, Point, Size, Vector}; use super::easing::{self, Easing}; use std::f32::consts::PI; use std::time::Duration; -type R = iced_widget::renderer::Renderer; +type R = iced::Renderer; const MIN_RADIANS: f32 = PI / 8.0; const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0; @@ -249,7 +247,7 @@ where fn layout( &self, - _renderer: &iced_widget::renderer::Renderer, + _renderer: &iced::Renderer, limits: &layout::Limits, ) -> layout::Node { let limits = limits.width(self.size).height(self.size); @@ -264,7 +262,7 @@ where event: Event, _layout: Layout<'_>, _cursor_position: Point, - _renderer: &iced_widget::renderer::Renderer, + _renderer: &iced::Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { @@ -299,22 +297,22 @@ where renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - renderer.draw( as Program< - Message, - R, - >>::draw( - &StateWithStyle { - state, - style: &self.style, - bar_height: self.bar_height, - easing: self.easing, - }, - &(), + canvas::Renderer::draw( renderer, - theme, - bounds, - Cursor::Unavailable, - )); + as Program>>::draw( + &StateWithStyle { + state, + style: &self.style, + bar_height: self.bar_height, + easing: self.easing, + }, + &(), + renderer, + theme, + bounds, + Cursor::Unavailable, + ), + ); }, ); } @@ -384,8 +382,7 @@ where bar_height: f32, } -impl<'a, Message, Theme> - canvas::Program> +impl<'a, Message, Theme> Program> for StateWithStyle<'a, Theme> where Theme: StyleSheet, @@ -405,7 +402,7 @@ where fn draw( &self, _state: &Self::State, - renderer: &iced_widget::renderer::Renderer, + renderer: &iced::Renderer, theme: &Theme, bounds: Rectangle, _cursor: canvas::Cursor, diff --git a/examples/loading_spinners/src/easing.rs b/examples/loading_spinners/src/easing.rs index 04befa15..665b3329 100644 --- a/examples/loading_spinners/src/easing.rs +++ b/examples/loading_spinners/src/easing.rs @@ -1,9 +1,7 @@ -use iced_core::Point; -use lyon::algorithms::measure::PathMeasurements; -use lyon::path::builder::NoAttributes; -use lyon::path::path::BuilderImpl; -use lyon::path::Path; +use iced::Point; +use lyon_algorithms::measure::PathMeasurements; +use lyon_algorithms::path::{builder::NoAttributes, path::BuilderImpl, Path}; use once_cell::sync::Lazy; pub static EMPHASIZED: Lazy = Lazy::new(|| { @@ -56,7 +54,7 @@ impl Easing { pub fn y_at_x(&self, x: f32) -> f32 { let mut sampler = self.measurements.create_sampler( &self.path, - lyon::algorithms::measure::SampleType::Normalized, + lyon_algorithms::measure::SampleType::Normalized, ); let sample = sampler.sample(x); @@ -69,7 +67,7 @@ pub struct Builder(NoAttributes); impl Builder { pub fn new() -> Self { let mut builder = Path::builder(); - builder.begin(lyon::geom::point(0.0, 0.0)); + builder.begin(lyon_algorithms::geom::point(0.0, 0.0)); Self(builder) } @@ -110,7 +108,7 @@ impl Builder { } pub fn build(mut self) -> Easing { - self.0.line_to(lyon::geom::point(1.0, 1.0)); + self.0.line_to(lyon_algorithms::geom::point(1.0, 1.0)); self.0.end(false); let path = self.0.build(); @@ -119,9 +117,12 @@ impl Builder { Easing { path, measurements } } - fn point(p: impl Into) -> lyon::geom::Point { + fn point(p: impl Into) -> lyon_algorithms::geom::Point { let p: Point = p.into(); - lyon::geom::point(p.x.min(1.0).max(0.0), p.y.min(1.0).max(0.0)) + lyon_algorithms::geom::point( + p.x.min(1.0).max(0.0), + p.y.min(1.0).max(0.0), + ) } } diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs index eb8a7408..6d151760 100644 --- a/examples/loading_spinners/src/linear.rs +++ b/examples/loading_spinners/src/linear.rs @@ -1,15 +1,13 @@ //! Show a linear progress indicator. -use iced::window; -use iced_core::event::{self, Event}; -use iced_core::renderer; -use iced_core::time::Instant; -use iced_core::widget::tree::{self, Tree}; -use iced_core::window::RedrawRequest; -use iced_core::{layout, Size}; -use iced_core::{ - Background, Clipboard, Color, Element, Layout, Length, Point, Rectangle, - Shell, Widget, -}; +use iced::advanced::layout; +use iced::advanced::renderer::{self, Quad}; +use iced::advanced::widget::tree::{self, Tree}; +use iced::advanced::{Clipboard, Layout, Shell, Widget}; +use iced::event; +use iced::time::Instant; +use iced::window::{self, RedrawRequest}; +use iced::{Background, Color, Element, Rectangle}; +use iced::{Event, Length, Point, Size}; use super::easing::{self, Easing}; @@ -18,7 +16,7 @@ use std::time::Duration; #[allow(missing_debug_implementations)] pub struct Linear<'a, Renderer> where - Renderer: iced_core::Renderer, + Renderer: iced::advanced::Renderer, Renderer::Theme: StyleSheet, { width: Length, @@ -30,7 +28,7 @@ where impl<'a, Renderer> Linear<'a, Renderer> where - Renderer: iced_widget::core::Renderer, + Renderer: iced::advanced::Renderer, Renderer::Theme: StyleSheet, { /// Creates a new [`Linear`] with the given content. @@ -80,7 +78,7 @@ where impl<'a, Renderer> Default for Linear<'a, Renderer> where - Renderer: iced_core::Renderer, + Renderer: iced::advanced::Renderer, Renderer::Theme: StyleSheet, { fn default() -> Self { @@ -156,7 +154,7 @@ impl State { impl<'a, Message, Renderer> Widget for Linear<'a, Renderer> where Message: 'a + Clone, - Renderer: 'a + iced_core::Renderer, + Renderer: 'a + iced::advanced::Renderer, Renderer::Theme: StyleSheet, { fn tag(&self) -> tree::Tag { @@ -253,7 +251,7 @@ where ), State::Contracting { progress, .. } => renderer.fill_quad( - renderer::Quad { + Quad { bounds: Rectangle { x: bounds.x + self.easing.y_at_x(*progress) * bounds.width, @@ -276,7 +274,7 @@ impl<'a, Message, Renderer> From> for Element<'a, Message, Renderer> where Message: Clone + 'a, - Renderer: iced_core::Renderer + 'a, + Renderer: iced::advanced::Renderer + 'a, Renderer::Theme: StyleSheet, { fn from(linear: Linear<'a, Renderer>) -> Self { -- cgit From 0148cfc82b0076a050e13d72a7c1b144a8c67033 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Thu, 8 Jun 2023 07:51:32 -0700 Subject: fix: RedrawRequest::NextFrame -> RedrawRequest::At() --- examples/loading_spinners/src/circular.rs | 6 +++++- examples/loading_spinners/src/linear.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 8639780a..56ffa6f5 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -266,6 +266,8 @@ where _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { + const FRAME_RATE: u64 = 60; + let state = tree.state.downcast_mut::(); if let Event::Window(window::Event::RedrawRequested(now)) = event { @@ -275,7 +277,9 @@ where now, ); - shell.request_redraw(RedrawRequest::NextFrame); + shell.request_redraw(RedrawRequest::At( + now + Duration::from_millis(1000 / FRAME_RATE), + )); } event::Status::Ignored diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs index 6d151760..ea0807c2 100644 --- a/examples/loading_spinners/src/linear.rs +++ b/examples/loading_spinners/src/linear.rs @@ -194,12 +194,16 @@ where _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, ) -> event::Status { + const FRAME_RATE: u64 = 60; + let state = tree.state.downcast_mut::(); if let Event::Window(window::Event::RedrawRequested(now)) = event { *state = state.timed_transition(self.cycle_duration, now); - shell.request_redraw(RedrawRequest::NextFrame); + shell.request_redraw(RedrawRequest::At( + now + Duration::from_millis(1000 / FRAME_RATE), + )); } event::Status::Ignored -- cgit From 05e238e9ed5f0c6cade87228f8f3044ee26df756 Mon Sep 17 00:00:00 2001 From: Bingus Date: Thu, 8 Jun 2023 10:10:26 -0700 Subject: Adjusted offscreen pass to be a render pass vs compute for compat with web-colors flag. --- examples/screenshot/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 59dfdf14..ef2be4fe 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -134,7 +134,7 @@ impl Application for Example { screenshot.size.height, screenshot.clone(), )) - .content_fit(ContentFit::ScaleDown) + .content_fit(ContentFit::Contain) .width(Length::Fill) .height(Length::Fill) .into() -- cgit From 34451bff185d8875f55747ee97ed746828e30f40 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 8 Jun 2023 20:11:59 +0200 Subject: Implement basic cursor availability --- examples/arc/src/main.rs | 5 ++-- examples/bezier_tool/src/main.rs | 12 ++++----- examples/clock/src/main.rs | 7 +++--- examples/color_palette/src/main.rs | 10 +++++--- examples/custom_quad/src/main.rs | 5 ++-- examples/custom_widget/src/main.rs | 5 ++-- examples/game_of_life/src/main.rs | 15 ++++++------ examples/geometry/src/main.rs | 40 +++++++++++++++--------------- examples/modal/src/main.rs | 35 +++++++++++++------------- examples/multitouch/src/main.rs | 7 +++--- examples/sierpinski_triangle/src/main.rs | 5 ++-- examples/solar_system/src/main.rs | 5 ++-- examples/toast/src/main.rs | 42 ++++++++++++-------------------- 13 files changed, 95 insertions(+), 98 deletions(-) (limited to 'examples') diff --git a/examples/arc/src/main.rs b/examples/arc/src/main.rs index 80ad0b5b..df565859 100644 --- a/examples/arc/src/main.rs +++ b/examples/arc/src/main.rs @@ -1,8 +1,9 @@ use std::{f32::consts::PI, time::Instant}; use iced::executor; +use iced::mouse; use iced::widget::canvas::{ - self, stroke, Cache, Canvas, Cursor, Geometry, Path, Stroke, + self, stroke, Cache, Canvas, Geometry, Path, Stroke, }; use iced::{ Application, Command, Element, Length, Point, Rectangle, Renderer, @@ -78,7 +79,7 @@ impl canvas::Program for Arc { renderer: &Renderer, theme: &Theme, bounds: Rectangle, - _cursor: Cursor, + _cursor: mouse::Cursor, ) -> Vec { let geometry = self.cache.draw(renderer, bounds.size(), |frame| { let palette = theme.palette(); diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index f1c83a16..b4568cbf 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -61,9 +61,7 @@ impl Sandbox for Example { mod bezier { use iced::mouse; use iced::widget::canvas::event::{self, Event}; - use iced::widget::canvas::{ - self, Canvas, Cursor, Frame, Geometry, Path, Stroke, - }; + use iced::widget::canvas::{self, Canvas, Frame, Geometry, Path, Stroke}; use iced::{Element, Length, Point, Rectangle, Renderer, Theme}; #[derive(Default)] @@ -100,7 +98,7 @@ mod bezier { state: &mut Self::State, event: Event, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> (event::Status, Option) { let cursor_position = if let Some(position) = cursor.position_in(&bounds) { @@ -155,7 +153,7 @@ mod bezier { renderer: &Renderer, _theme: &Theme, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> Vec { let content = self.state.cache.draw( renderer, @@ -183,7 +181,7 @@ mod bezier { &self, _state: &Self::State, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> mouse::Interaction { if cursor.is_over(&bounds) { mouse::Interaction::Crosshair @@ -224,7 +222,7 @@ mod bezier { &self, renderer: &Renderer, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> Geometry { let mut frame = Frame::new(renderer, bounds.size()); diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 6425e2da..fae77bc0 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -1,7 +1,6 @@ use iced::executor; -use iced::widget::canvas::{ - stroke, Cache, Cursor, Geometry, LineCap, Path, Stroke, -}; +use iced::mouse; +use iced::widget::canvas::{stroke, Cache, Geometry, LineCap, Path, Stroke}; use iced::widget::{canvas, container}; use iced::{ Application, Color, Command, Element, Length, Point, Rectangle, Renderer, @@ -92,7 +91,7 @@ impl canvas::Program for Clock { renderer: &Renderer, _theme: &Theme, bounds: Rectangle, - _cursor: Cursor, + _cursor: mouse::Cursor, ) -> Vec { let clock = self.clock.draw(renderer, bounds.size(), |frame| { let center = frame.center(); diff --git a/examples/color_palette/src/main.rs b/examples/color_palette/src/main.rs index de01099e..736a9d53 100644 --- a/examples/color_palette/src/main.rs +++ b/examples/color_palette/src/main.rs @@ -1,8 +1,10 @@ -use iced::widget::canvas::{self, Canvas, Cursor, Frame, Geometry, Path}; +use iced::alignment::{self, Alignment}; +use iced::mouse; +use iced::widget::canvas::{self, Canvas, Frame, Geometry, Path}; use iced::widget::{column, row, text, Slider}; use iced::{ - alignment, Alignment, Color, Element, Length, Point, Rectangle, Renderer, - Sandbox, Settings, Size, Vector, + Color, Element, Length, Point, Rectangle, Renderer, Sandbox, Settings, + Size, Vector, }; use palette::{ self, convert::FromColor, rgb::Rgb, Darken, Hsl, Lighten, ShiftHue, @@ -246,7 +248,7 @@ impl canvas::Program for Theme { renderer: &Renderer, _theme: &iced::Theme, bounds: Rectangle, - _cursor: Cursor, + _cursor: mouse::Cursor, ) -> Vec { let theme = self.canvas_cache.draw(renderer, bounds.size(), |frame| { self.draw(frame); diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index b07f42ce..4b300116 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -3,7 +3,8 @@ mod quad { use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; - use iced::{Color, Element, Length, Point, Rectangle, Size}; + use iced::mouse; + use iced::{Color, Element, Length, Rectangle, Size}; pub struct CustomQuad { size: f32, @@ -48,7 +49,7 @@ mod quad { _theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _viewport: &Rectangle, ) { renderer.fill_quad( diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index 7854548c..713bc62d 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -12,7 +12,8 @@ mod circle { use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; - use iced::{Color, Element, Length, Point, Rectangle, Size}; + use iced::mouse; + use iced::{Color, Element, Length, Rectangle, Size}; pub struct Circle { radius: f32, @@ -55,7 +56,7 @@ mod circle { _theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _viewport: &Rectangle, ) { renderer.fill_quad( diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index eab8908b..aaa8efd1 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -204,15 +204,14 @@ fn view_controls<'a>( mod grid { use crate::Preset; + use iced::alignment; + use iced::mouse; use iced::touch; use iced::widget::canvas; use iced::widget::canvas::event::{self, Event}; - use iced::widget::canvas::{ - Cache, Canvas, Cursor, Frame, Geometry, Path, Text, - }; + use iced::widget::canvas::{Cache, Canvas, Frame, Geometry, Path, Text}; use iced::{ - alignment, mouse, Color, Element, Length, Point, Rectangle, Renderer, - Size, Theme, Vector, + Color, Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector, }; use rustc_hash::{FxHashMap, FxHashSet}; use std::future::Future; @@ -401,7 +400,7 @@ mod grid { interaction: &mut Interaction, event: Event, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> (event::Status, Option) { if let Event::Mouse(mouse::Event::ButtonReleased(_)) = event { *interaction = Interaction::None; @@ -539,7 +538,7 @@ mod grid { renderer: &Renderer, _theme: &Theme, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> Vec { let center = Vector::new(bounds.width / 2.0, bounds.height / 2.0); @@ -670,7 +669,7 @@ mod grid { &self, interaction: &Interaction, bounds: Rectangle, - cursor: Cursor, + cursor: mouse::Cursor, ) -> mouse::Interaction { match interaction { Interaction::Drawing => mouse::Interaction::Crosshair, diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index a4183db9..1f949d10 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -7,9 +7,8 @@ mod rainbow { use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; - use iced::{ - Element, Length, Point, Rectangle, Renderer, Size, Theme, Vector, - }; + use iced::mouse; + use iced::{Element, Length, Rectangle, Renderer, Size, Theme, Vector}; #[derive(Debug, Clone, Copy, Default)] pub struct Rainbow; @@ -44,13 +43,13 @@ mod rainbow { _theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, _viewport: &Rectangle, ) { use iced::advanced::Renderer as _; use iced_graphics::primitive::Mesh2D; - let b = layout.bounds(); + let bounds = layout.bounds(); // R O Y G B I V let color_r = [1.0, 0.0, 0.0, 1.0]; @@ -63,24 +62,24 @@ mod rainbow { let color_v = [0.75, 0.0, 0.5, 1.0]; let posn_center = { - if b.contains(cursor_position) { - [cursor_position.x - b.x, cursor_position.y - b.y] + if let Some(cursor_position) = cursor.position_in(&bounds) { + [cursor_position.x, cursor_position.y] } else { - [b.width / 2.0, b.height / 2.0] + [bounds.width / 2.0, bounds.height / 2.0] } }; let posn_tl = [0.0, 0.0]; - let posn_t = [b.width / 2.0, 0.0]; - let posn_tr = [b.width, 0.0]; - let posn_r = [b.width, b.height / 2.0]; - let posn_br = [b.width, b.height]; - let posn_b = [(b.width / 2.0), b.height]; - let posn_bl = [0.0, b.height]; - let posn_l = [0.0, b.height / 2.0]; + let posn_t = [bounds.width / 2.0, 0.0]; + let posn_tr = [bounds.width, 0.0]; + let posn_r = [bounds.width, bounds.height / 2.0]; + let posn_br = [bounds.width, bounds.height]; + let posn_b = [(bounds.width / 2.0), bounds.height]; + let posn_bl = [0.0, bounds.height]; + let posn_l = [0.0, bounds.height / 2.0]; let mesh = Primitive::SolidMesh { - size: b.size(), + size: bounds.size(), buffers: Mesh2D { vertices: vec![ ColoredVertex2D { @@ -133,9 +132,12 @@ mod rainbow { }, }; - renderer.with_translation(Vector::new(b.x, b.y), |renderer| { - renderer.draw_primitive(mesh); - }); + renderer.with_translation( + Vector::new(bounds.x, bounds.y), + |renderer| { + renderer.draw_primitive(mesh); + }, + ); } } diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 9e1e4c2f..82ed05d2 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -1,10 +1,11 @@ +use iced::executor; +use iced::keyboard; +use iced::subscription::{self, Subscription}; +use iced::theme; use iced::widget::{ self, button, column, container, horizontal_space, row, text, text_input, }; -use iced::{ - executor, keyboard, subscription, theme, Alignment, Application, Command, - Element, Event, Length, Settings, Subscription, -}; +use iced::{Alignment, Application, Command, Element, Event, Length, Settings}; use self::modal::Modal; @@ -254,7 +255,7 @@ mod modal { state: &mut widget::Tree, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -263,7 +264,7 @@ mod modal { &mut state.children[0], event, layout, - cursor_position, + cursor, renderer, clipboard, shell, @@ -277,7 +278,7 @@ mod modal { theme: &::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { self.base.as_widget().draw( @@ -286,7 +287,7 @@ mod modal { theme, style, layout, - cursor_position, + cursor, viewport, ); } @@ -312,14 +313,14 @@ mod modal { &self, state: &widget::Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.base.as_widget().mouse_interaction( &state.children[0], layout, - cursor_position, + cursor, viewport, renderer, ) @@ -377,7 +378,7 @@ mod modal { &mut self, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -389,7 +390,7 @@ mod modal { mouse::Button::Left, )) = &event { - if !content_bounds.contains(cursor_position) { + if !cursor.is_over(&content_bounds) { shell.publish(message.clone()); return event::Status::Captured; } @@ -400,7 +401,7 @@ mod modal { self.tree, event, layout.children().next().unwrap(), - cursor_position, + cursor, renderer, clipboard, shell, @@ -413,7 +414,7 @@ mod modal { theme: &Renderer::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { renderer.fill_quad( renderer::Quad { @@ -434,7 +435,7 @@ mod modal { theme, style, layout.children().next().unwrap(), - cursor_position, + cursor, &layout.bounds(), ); } @@ -456,14 +457,14 @@ mod modal { fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.content.as_widget().mouse_interaction( self.tree, layout.children().next().unwrap(), - cursor_position, + cursor, viewport, renderer, ) diff --git a/examples/multitouch/src/main.rs b/examples/multitouch/src/main.rs index 7df6c929..2830b78d 100644 --- a/examples/multitouch/src/main.rs +++ b/examples/multitouch/src/main.rs @@ -1,9 +1,10 @@ //! This example shows how to use touch events in `Canvas` to draw //! a circle around each fingertip. This only works on touch-enabled //! computers like Microsoft Surface. +use iced::mouse; use iced::widget::canvas::event; use iced::widget::canvas::stroke::{self, Stroke}; -use iced::widget::canvas::{self, Canvas, Cursor, Geometry}; +use iced::widget::canvas::{self, Canvas, Geometry}; use iced::{ executor, touch, window, Application, Color, Command, Element, Length, Point, Rectangle, Renderer, Settings, Subscription, Theme, @@ -103,7 +104,7 @@ impl canvas::Program for State { _state: &mut Self::State, event: event::Event, _bounds: Rectangle, - _cursor: Cursor, + _cursor: mouse::Cursor, ) -> (event::Status, Option) { match event { event::Event::Touch(touch_event) => match touch_event { @@ -128,7 +129,7 @@ impl canvas::Program for State { renderer: &Renderer, _theme: &Theme, bounds: Rectangle, - _cursor: Cursor, + _cursor: mouse::Cursor, ) -> Vec { let fingerweb = self.cache.draw(renderer, bounds.size(), |frame| { if self.fingers.len() < 2 { diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs index 4faac6d6..ad681a91 100644 --- a/examples/sierpinski_triangle/src/main.rs +++ b/examples/sierpinski_triangle/src/main.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use iced::executor; +use iced::mouse; use iced::widget::canvas::event::{self, Event}; use iced::widget::canvas::{self, Canvas}; use iced::widget::{column, row, slider, text}; @@ -105,7 +106,7 @@ impl canvas::Program for SierpinskiGraph { _state: &mut Self::State, event: Event, bounds: Rectangle, - cursor: canvas::Cursor, + cursor: mouse::Cursor, ) -> (event::Status, Option) { let cursor_position = if let Some(position) = cursor.position_in(&bounds) { @@ -137,7 +138,7 @@ impl canvas::Program for SierpinskiGraph { renderer: &Renderer, _theme: &Theme, bounds: Rectangle, - _cursor: canvas::Cursor, + _cursor: mouse::Cursor, ) -> Vec { let geom = self.cache.draw(renderer, bounds.size(), |frame| { frame.stroke( diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index d9e660d7..58d06206 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -8,11 +8,12 @@ //! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system use iced::application; use iced::executor; +use iced::mouse; use iced::theme::{self, Theme}; use iced::widget::canvas; use iced::widget::canvas::gradient; use iced::widget::canvas::stroke::{self, Stroke}; -use iced::widget::canvas::{Cursor, Path}; +use iced::widget::canvas::Path; use iced::window; use iced::{ Application, Color, Command, Element, Length, Point, Rectangle, Renderer, @@ -161,7 +162,7 @@ impl canvas::Program for State { renderer: &Renderer, _theme: &Theme, bounds: Rectangle, - _cursor: Cursor, + _cursor: mouse::Cursor, ) -> Vec { use std::f32::consts::PI; diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 515218e7..395cbc10 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -1,10 +1,10 @@ +use iced::executor; +use iced::keyboard; +use iced::subscription::{self, Subscription}; use iced::widget::{ self, button, column, container, pick_list, row, slider, text, text_input, }; -use iced::{ - executor, keyboard, subscription, Alignment, Application, Command, Element, - Event, Length, Settings, Subscription, -}; +use iced::{Alignment, Application, Command, Element, Event, Length, Settings}; use toast::{Status, Toast}; @@ -396,7 +396,7 @@ mod toast { state: &mut Tree, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -405,7 +405,7 @@ mod toast { &mut state.children[0], event, layout, - cursor_position, + cursor, renderer, clipboard, shell, @@ -419,7 +419,7 @@ mod toast { theme: &Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, ) { self.content.as_widget().draw( @@ -428,7 +428,7 @@ mod toast { theme, style, layout, - cursor_position, + cursor, viewport, ); } @@ -437,14 +437,14 @@ mod toast { &self, state: &Tree, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { self.content.as_widget().mouse_interaction( &state.children[0], layout, - cursor_position, + cursor, viewport, renderer, ) @@ -523,7 +523,7 @@ mod toast { &mut self, event: Event, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, renderer: &Renderer, clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -572,7 +572,7 @@ mod toast { state, event.clone(), layout, - cursor_position, + cursor, renderer, clipboard, &mut local_shell, @@ -595,7 +595,7 @@ mod toast { theme: &::Theme, style: &renderer::Style, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, ) { let viewport = layout.bounds(); @@ -606,13 +606,7 @@ mod toast { .zip(layout.children()) { child.as_widget().draw( - state, - renderer, - theme, - style, - layout, - cursor_position, - &viewport, + state, renderer, theme, style, layout, cursor, &viewport, ); } } @@ -639,7 +633,7 @@ mod toast { fn mouse_interaction( &self, layout: Layout<'_>, - cursor_position: Point, + cursor: mouse::Cursor, viewport: &Rectangle, renderer: &Renderer, ) -> mouse::Interaction { @@ -649,11 +643,7 @@ mod toast { .zip(layout.children()) .map(|((child, state), layout)| { child.as_widget().mouse_interaction( - state, - layout, - cursor_position, - viewport, - renderer, + state, layout, cursor, viewport, renderer, ) }) .max() -- cgit From 5c8cfb411ed0a9a6e55bd1193cd7e97252e63d28 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 8 Jun 2023 20:16:46 +0200 Subject: Take `Rectangle` by value in `Cursor` API --- examples/bezier_tool/src/main.rs | 6 +++--- examples/game_of_life/src/main.rs | 11 +++++------ examples/geometry/src/main.rs | 2 +- examples/modal/src/main.rs | 2 +- examples/sierpinski_triangle/src/main.rs | 12 ++++++------ 5 files changed, 16 insertions(+), 17 deletions(-) (limited to 'examples') diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index b4568cbf..310be28f 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -101,7 +101,7 @@ mod bezier { cursor: mouse::Cursor, ) -> (event::Status, Option) { let cursor_position = - if let Some(position) = cursor.position_in(&bounds) { + if let Some(position) = cursor.position_in(bounds) { position } else { return (event::Status::Ignored, None); @@ -183,7 +183,7 @@ mod bezier { bounds: Rectangle, cursor: mouse::Cursor, ) -> mouse::Interaction { - if cursor.is_over(&bounds) { + if cursor.is_over(bounds) { mouse::Interaction::Crosshair } else { mouse::Interaction::default() @@ -226,7 +226,7 @@ mod bezier { ) -> Geometry { let mut frame = Frame::new(renderer, bounds.size()); - if let Some(cursor_position) = cursor.position_in(&bounds) { + if let Some(cursor_position) = cursor.position_in(bounds) { match *self { Pending::One { from } => { let line = Path::line(from, cursor_position); diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs index aaa8efd1..e951d734 100644 --- a/examples/game_of_life/src/main.rs +++ b/examples/game_of_life/src/main.rs @@ -407,7 +407,7 @@ mod grid { } let cursor_position = - if let Some(position) = cursor.position_in(&bounds) { + if let Some(position) = cursor.position_in(bounds) { position } else { return (event::Status::Ignored, None); @@ -567,10 +567,9 @@ mod grid { let overlay = { let mut frame = Frame::new(renderer, bounds.size()); - let hovered_cell = - cursor.position_in(&bounds).map(|position| { - Cell::at(self.project(position, frame.size())) - }); + let hovered_cell = cursor.position_in(bounds).map(|position| { + Cell::at(self.project(position, frame.size())) + }); if let Some(cell) = hovered_cell { frame.with_save(|frame| { @@ -675,7 +674,7 @@ mod grid { Interaction::Drawing => mouse::Interaction::Crosshair, Interaction::Erasing => mouse::Interaction::Crosshair, Interaction::Panning { .. } => mouse::Interaction::Grabbing, - Interaction::None if cursor.is_over(&bounds) => { + Interaction::None if cursor.is_over(bounds) => { mouse::Interaction::Crosshair } _ => mouse::Interaction::default(), diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 1f949d10..29f78ea1 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -62,7 +62,7 @@ mod rainbow { let color_v = [0.75, 0.0, 0.5, 1.0]; let posn_center = { - if let Some(cursor_position) = cursor.position_in(&bounds) { + if let Some(cursor_position) = cursor.position_in(bounds) { [cursor_position.x, cursor_position.y] } else { [bounds.width / 2.0, bounds.height / 2.0] diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 82ed05d2..5c43c203 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -390,7 +390,7 @@ mod modal { mouse::Button::Left, )) = &event { - if !cursor.is_over(&content_bounds) { + if !cursor.is_over(content_bounds) { shell.publish(message.clone()); return event::Status::Captured; } diff --git a/examples/sierpinski_triangle/src/main.rs b/examples/sierpinski_triangle/src/main.rs index ad681a91..885d3c63 100644 --- a/examples/sierpinski_triangle/src/main.rs +++ b/examples/sierpinski_triangle/src/main.rs @@ -108,12 +108,12 @@ impl canvas::Program for SierpinskiGraph { bounds: Rectangle, cursor: mouse::Cursor, ) -> (event::Status, Option) { - let cursor_position = - if let Some(position) = cursor.position_in(&bounds) { - position - } else { - return (event::Status::Ignored, None); - }; + let cursor_position = if let Some(position) = cursor.position_in(bounds) + { + position + } else { + return (event::Status::Ignored, None); + }; match event { Event::Mouse(mouse_event) => { -- cgit From 57db196c3a169fe1ecd654e9b58ce66defa5dd7e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 8 Jun 2023 20:23:47 +0200 Subject: Increase width of controls in `pane_grid` example --- examples/pane_grid/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 2ffdcc69..54c36d69 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -300,7 +300,7 @@ fn view_content<'a>( ) ] .spacing(5) - .max_width(150); + .max_width(160); if total_panes > 1 && !is_pinned { controls = controls.push( -- cgit From aba98e49654852281ed17bedd1becac6f9db8700 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 8 Jun 2023 20:35:40 +0200 Subject: Extend cursor availability to the shell level --- examples/integration/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index c935aca7..daecfeb2 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -6,6 +6,7 @@ use scene::Scene; use iced_wgpu::graphics::Viewport; use iced_wgpu::{wgpu, Backend, Renderer, Settings}; +use iced_winit::core::mouse; use iced_winit::core::renderer; use iced_winit::core::{Color, Size}; use iced_winit::runtime::program; @@ -194,10 +195,10 @@ pub fn main() -> Result<(), Box> { // We update iced let _ = state.update( viewport.logical_size(), - conversion::cursor_position( + mouse::Cursor::Available(conversion::cursor_position( cursor_position, viewport.scale_factor(), - ), + )), &mut renderer, &Theme::Dark, &renderer::Style { text_color: Color::WHITE }, -- cgit From 733c2bd9f594a21ce20444e8edcb4c5f88118e0a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 8 Jun 2023 20:45:48 +0200 Subject: Use `mouse::Cursor` in `integration` example --- examples/integration/src/main.rs | 42 ++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) (limited to 'examples') diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index daecfeb2..342d4c69 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -15,7 +15,6 @@ use iced_winit::style::Theme; use iced_winit::{conversion, futures, winit, Clipboard}; use winit::{ - dpi::PhysicalPosition, event::{Event, ModifiersState, WindowEvent}, event_loop::{ControlFlow, EventLoop}, }; @@ -40,6 +39,7 @@ pub fn main() -> Result<(), Box> { .and_then(|element| element.dyn_into::().ok()) .expect("Get canvas element") }; + #[cfg(not(target_arch = "wasm32"))] env_logger::init(); @@ -59,7 +59,7 @@ pub fn main() -> Result<(), Box> { Size::new(physical_size.width, physical_size.height), window.scale_factor(), ); - let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut cursor_position = None; let mut modifiers = ModifiersState::default(); let mut clipboard = Clipboard::connect(&window); @@ -166,7 +166,7 @@ pub fn main() -> Result<(), Box> { Event::WindowEvent { event, .. } => { match event { WindowEvent::CursorMoved { position, .. } => { - cursor_position = position; + cursor_position = Some(position); } WindowEvent::ModifiersChanged(new_modifiers) => { modifiers = new_modifiers; @@ -195,13 +195,20 @@ pub fn main() -> Result<(), Box> { // We update iced let _ = state.update( viewport.logical_size(), - mouse::Cursor::Available(conversion::cursor_position( - cursor_position, - viewport.scale_factor(), - )), + cursor_position + .map(|p| { + conversion::cursor_position( + p, + viewport.scale_factor(), + ) + }) + .map(mouse::Cursor::Available) + .unwrap_or(mouse::Cursor::Unavailable), &mut renderer, &Theme::Dark, - &renderer::Style { text_color: Color::WHITE }, + &renderer::Style { + text_color: Color::WHITE, + }, &mut clipboard, &mut debug, ); @@ -243,7 +250,9 @@ pub fn main() -> Result<(), Box> { let program = state.program(); - let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default()); + let view = frame.texture.create_view( + &wgpu::TextureViewDescriptor::default(), + ); { // We clear the frame @@ -276,15 +285,18 @@ pub fn main() -> Result<(), Box> { frame.present(); // Update the mouse cursor - window.set_cursor_icon( - iced_winit::conversion::mouse_interaction( - state.mouse_interaction(), - ), - ); + window.set_cursor_icon( + iced_winit::conversion::mouse_interaction( + state.mouse_interaction(), + ), + ); } Err(error) => match error { wgpu::SurfaceError::OutOfMemory => { - panic!("Swapchain error: {error}. Rendering cannot continue.") + panic!( + "Swapchain error: {error}. \ + Rendering cannot continue." + ) } _ => { // Try rendering again next frame. -- cgit From 9d69af10ccb4317f01bff32e2e1e58d498b2fc89 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Thu, 8 Jun 2023 20:09:10 -0700 Subject: comment: fix typo --- examples/loading_spinners/src/circular.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 56ffa6f5..4e7bdc18 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -31,7 +31,7 @@ where style: ::Style, easing: &'a Easing, cycle_duration: Duration, - rotaion_duration: Duration, + rotation_duration: Duration, } impl<'a, Theme> Circular<'a, Theme> @@ -46,7 +46,7 @@ where style: ::Style::default(), easing: &easing::STANDARD, cycle_duration: Duration::from_millis(600), - rotaion_duration: Duration::from_secs(2), + rotation_duration: Duration::from_secs(2), } } @@ -83,7 +83,7 @@ where /// Sets the base rotation duration of this [`Circular`]. This is the duration that a full /// rotation would take if the cycle rotation were set to 0.0 (no expanding or contracting) pub fn rotation_duration(mut self, duration: Duration) -> Self { - self.rotaion_duration = duration; + self.rotation_duration = duration; self } } @@ -273,7 +273,7 @@ where if let Event::Window(window::Event::RedrawRequested(now)) = event { *state = state.timed_transition( self.cycle_duration, - self.rotaion_duration, + self.rotation_duration, now, ); -- cgit From 290b47f312471db74e8b149e0f78df3891456208 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Fri, 9 Jun 2023 11:03:52 -0700 Subject: refactor: remove unnecessary canvas complexity --- examples/loading_spinners/src/circular.rs | 187 +++++++++++------------------- 1 file changed, 67 insertions(+), 120 deletions(-) (limited to 'examples') diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 4e7bdc18..55363de1 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -5,7 +5,7 @@ use iced::advanced::widget::tree::{self, Tree}; use iced::advanced::{Clipboard, Layout, Renderer, Shell, Widget}; use iced::event; use iced::time::Instant; -use iced::widget::canvas::{self, Cursor, Program}; +use iced::widget::canvas; use iced::window::{self, RedrawRequest}; use iced::{Background, Color, Element, Rectangle}; use iced::{Event, Length, Point, Size, Vector}; @@ -15,8 +15,6 @@ use super::easing::{self, Easing}; use std::f32::consts::PI; use std::time::Duration; -type R = iced::Renderer; - const MIN_RADIANS: f32 = PI / 8.0; const WRAP_RADIANS: f32 = 2.0 * PI - PI / 4.0; const BASE_ROTATION_SPEED: u32 = u32::MAX / 80; @@ -98,7 +96,7 @@ where } #[derive(Clone, Copy)] -enum State { +enum Animation { Expanding { start: Instant, progress: f32, @@ -113,7 +111,7 @@ enum State { }, } -impl Default for State { +impl Default for Animation { fn default() -> Self { Self::Expanding { start: Instant::now(), @@ -124,7 +122,7 @@ impl Default for State { } } -impl State { +impl Animation { fn next(&self, additional_rotation: u32, now: Instant) -> Self { match self { Self::Expanding { rotation, .. } => Self::Contracting { @@ -224,7 +222,14 @@ impl State { } } -impl<'a, Message, Theme> Widget> for Circular<'a, Theme> +#[derive(Default)] +struct State { + animation: Animation, + cache: canvas::Cache, +} + +impl<'a, Message, Theme> Widget> + for Circular<'a, Theme> where Message: 'a + Clone, Theme: StyleSheet, @@ -271,12 +276,13 @@ where let state = tree.state.downcast_mut::(); if let Event::Window(window::Event::RedrawRequested(now)) = event { - *state = state.timed_transition( + state.animation = state.animation.timed_transition( self.cycle_duration, self.rotation_duration, now, ); + state.cache.clear(); shell.request_redraw(RedrawRequest::At( now + Duration::from_millis(1000 / FRAME_RATE), )); @@ -288,42 +294,76 @@ where fn draw( &self, tree: &Tree, - renderer: &mut R, + renderer: &mut iced::Renderer, theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, _cursor_position: Point, _viewport: &Rectangle, ) { - let bounds = layout.bounds(); let state = tree.state.downcast_ref::(); + let bounds = layout.bounds(); + let custom_style = + ::appearance(theme, &self.style); + + let geometry = state.cache.draw(renderer, bounds.size(), |frame| { + let track_radius = frame.width() / 2.0 - self.bar_height; + let track_path = canvas::Path::circle(frame.center(), track_radius); + + frame.stroke( + &track_path, + canvas::Stroke::default() + .with_color(custom_style.track_color) + .with_width(self.bar_height), + ); + + let mut builder = canvas::path::Builder::new(); + + let start = state.animation.rotation() * 2.0 * PI; + + match state.animation { + Animation::Expanding { progress, .. } => { + builder.arc(canvas::path::Arc { + center: frame.center(), + radius: track_radius, + start_angle: start, + end_angle: start + + MIN_RADIANS + + WRAP_RADIANS * (self.easing.y_at_x(progress)), + }); + } + Animation::Contracting { progress, .. } => { + builder.arc(canvas::path::Arc { + center: frame.center(), + radius: track_radius, + start_angle: start + + WRAP_RADIANS * (self.easing.y_at_x(progress)), + end_angle: start + MIN_RADIANS + WRAP_RADIANS, + }); + } + } + + let bar_path = builder.build(); + + frame.stroke( + &bar_path, + canvas::Stroke::default() + .with_color(custom_style.bar_color) + .with_width(self.bar_height), + ); + }); renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - canvas::Renderer::draw( - renderer, - as Program>>::draw( - &StateWithStyle { - state, - style: &self.style, - bar_height: self.bar_height, - easing: self.easing, - }, - &(), - renderer, - theme, - bounds, - Cursor::Unavailable, - ), - ); + renderer.draw_primitive(geometry.0); }, ); } } impl<'a, Message, Theme> From> - for Element<'a, Message, R> + for Element<'a, Message, iced::Renderer> where Message: Clone + 'a, Theme: StyleSheet + 'a, @@ -375,96 +415,3 @@ impl StyleSheet for iced::Theme { } } } - -struct StateWithStyle<'a, Theme> -where - Theme: StyleSheet, -{ - state: &'a State, - style: &'a ::Style, - easing: &'a Easing, - bar_height: f32, -} - -impl<'a, Message, Theme> Program> - for StateWithStyle<'a, Theme> -where - Theme: StyleSheet, -{ - type State = (); - - fn update( - &self, - _state: &mut Self::State, - _event: canvas::Event, - _bounds: Rectangle, - _cursor: canvas::Cursor, - ) -> (canvas::event::Status, Option) { - (canvas::event::Status::Ignored, None) - } - - fn draw( - &self, - _state: &Self::State, - renderer: &iced::Renderer, - theme: &Theme, - bounds: Rectangle, - _cursor: canvas::Cursor, - ) -> Vec { - let mut frame = canvas::Frame::new(renderer, bounds.size()); - let custom_style = ::appearance(theme, self.style); - let track_radius = frame.width() / 2.0 - self.bar_height; - - if let Some(Background::Color(color)) = custom_style.background { - let background_path = - canvas::Path::circle(frame.center(), track_radius); - frame.fill(&background_path, color); - } - - let track_path = canvas::Path::circle(frame.center(), track_radius); - - frame.stroke( - &track_path, - canvas::Stroke::default() - .with_color(custom_style.track_color) - .with_width(self.bar_height), - ); - - let mut builder = canvas::path::Builder::new(); - - let start = self.state.rotation() * 2.0 * PI; - - match self.state { - State::Expanding { progress, .. } => { - builder.arc(canvas::path::Arc { - center: frame.center(), - radius: track_radius, - start_angle: start, - end_angle: start - + MIN_RADIANS - + WRAP_RADIANS * (self.easing.y_at_x(*progress)), - }); - } - State::Contracting { progress, .. } => { - builder.arc(canvas::path::Arc { - center: frame.center(), - radius: track_radius, - start_angle: start - + WRAP_RADIANS * (self.easing.y_at_x(*progress)), - end_angle: start + MIN_RADIANS + WRAP_RADIANS, - }); - } - } - - let bar_path = builder.build(); - - frame.stroke( - &bar_path, - canvas::Stroke::default() - .with_color(custom_style.bar_color) - .with_width(self.bar_height), - ); - - vec![frame.into_geometry()] - } -} -- cgit From 55dc3b5619392f4a20389255708c61082b3d4c1a Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 14:31:38 -0800 Subject: Introduce internal `overlay::Nested` for `UserInterface` --- examples/toast/src/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/toast/src/main.rs b/examples/toast/src/main.rs index 395cbc10..4282ddcf 100644 --- a/examples/toast/src/main.rs +++ b/examples/toast/src/main.rs @@ -650,7 +650,12 @@ mod toast { .unwrap_or_default() } - fn is_over(&self, layout: Layout<'_>, cursor_position: Point) -> bool { + fn is_over( + &self, + layout: Layout<'_>, + _renderer: &Renderer, + cursor_position: Point, + ) -> bool { layout .children() .any(|layout| layout.bounds().contains(cursor_position)) -- cgit From f608056c5029727a9523e7a245b7118eb48caa5f Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Sat, 18 Feb 2023 13:49:11 -0800 Subject: Add nested picklist to modal example --- examples/modal/src/main.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'examples') diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 5c43c203..7fcbbfe4 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -3,11 +3,13 @@ use iced::keyboard; use iced::subscription::{self, Subscription}; use iced::theme; use iced::widget::{ - self, button, column, container, horizontal_space, row, text, text_input, + self, button, column, container, horizontal_space, pick_list, row, text, + text_input, }; use iced::{Alignment, Application, Command, Element, Event, Length, Settings}; -use self::modal::Modal; +use modal::Modal; +use std::fmt; pub fn main() -> iced::Result { App::run(Settings::default()) @@ -18,6 +20,7 @@ struct App { show_modal: bool, email: String, password: String, + plan: Plan, } #[derive(Debug, Clone)] @@ -26,6 +29,7 @@ enum Message { HideModal, Email(String), Password(String), + Plan(Plan), Submit, Event(Event), } @@ -66,6 +70,10 @@ impl Application for App { self.password = password; Command::none() } + Message::Plan(plan) => { + self.plan = plan; + Command::none() + } Message::Submit => { if !self.email.is_empty() && !self.password.is_empty() { self.hide_modal(); @@ -149,6 +157,16 @@ impl Application for App { .padding(5), ] .spacing(5), + column![ + text("Plan").size(12), + pick_list( + Plan::ALL, + Some(self.plan), + Message::Plan + ) + .padding(5), + ] + .spacing(5), button(text("Submit")).on_press(Message::HideModal), ] .spacing(10) @@ -176,6 +194,29 @@ impl App { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +enum Plan { + #[default] + Basic, + Pro, + Enterprise, +} + +impl Plan { + pub const ALL: &[Self] = &[Self::Basic, Self::Pro, Self::Enterprise]; +} + +impl fmt::Display for Plan { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Plan::Basic => "Basic", + Plan::Pro => "Pro", + Plan::Enterprise => "Enterprise", + } + .fmt(f) + } +} + mod modal { use iced::advanced::layout::{self, Layout}; use iced::advanced::overlay; @@ -469,6 +510,18 @@ mod modal { renderer, ) } + + fn overlay<'c>( + &'c mut self, + layout: Layout<'_>, + renderer: &Renderer, + ) -> Option> { + self.content.as_widget_mut().overlay( + self.tree, + layout.children().next().unwrap(), + renderer, + ) + } } impl<'a, Message, Renderer> From> -- cgit From b38f7d28372712e345672515d8c1643bc040c1f1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 21 Jun 2023 18:07:26 +0200 Subject: Center `Submit` button vertically in `styling` example --- examples/styling/src/main.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'examples') diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index e2015bac..f8a4c80a 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -127,7 +127,9 @@ impl Sandbox for Styling { let content = column![ choose_theme, horizontal_rule(38), - row![text_input, button].spacing(10), + row![text_input, button] + .spacing(10) + .align_items(Alignment::Center), slider, progress_bar, row![ -- cgit From 5ae726e02c4d6c9889ef7335d9bc80ef1992e34f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 27 Jun 2023 19:41:03 +0200 Subject: Move `Screenshot` inside `window` module --- examples/screenshot/src/main.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index ef2be4fe..83824535 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -1,16 +1,17 @@ -use ::image as img; -use ::image::ColorType; use iced::alignment; use iced::keyboard::KeyCode; use iced::theme::{Button, Container}; -use iced::widget::runtime::{CropError, Screenshot}; use iced::widget::{button, column, container, image, row, text, text_input}; +use iced::window::screenshot::{self, Screenshot}; use iced::{ event, executor, keyboard, subscription, Alignment, Application, Command, ContentFit, Element, Event, Length, Rectangle, Renderer, Subscription, Theme, }; +use ::image as img; +use ::image::ColorType; + fn main() -> iced::Result { env_logger::builder().format_timestamp(None).init(); @@ -21,7 +22,7 @@ struct Example { screenshot: Option, saved_png_path: Option>, png_saving: bool, - crop_error: Option, + crop_error: Option, x_input_value: Option, y_input_value: Option, width_input_value: Option, -- cgit From fa04f40524ab7a4ee23bcbc09bc4960c05b192db Mon Sep 17 00:00:00 2001 From: "Austin M. Reppert" Date: Fri, 26 May 2023 20:27:17 -0400 Subject: Make vertical scroll properties optional Co-Authored-By: Austin M. Reppert --- examples/scrollable/src/main.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 3038661e..1ba81609 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -1,4 +1,6 @@ -use iced::widget::scrollable::{Properties, Scrollbar, Scroller}; +use iced::widget::scrollable::{ + Properties, ScrollbarProperties, Scrollbar, Scroller, +}; use iced::widget::{ button, column, container, horizontal_space, progress_bar, radio, row, scrollable, slider, text, vertical_space, @@ -199,12 +201,12 @@ impl Application for ScrollableDemo { .spacing(40), ) .height(Length::Fill) - .vertical_scroll( + .scrollbar_properties(ScrollbarProperties::Vertical( Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) .scroller_width(self.scroller_width), - ) + )) .id(SCROLLABLE_ID.clone()) .on_scroll(Message::Scrolled), Direction::Horizontal => scrollable( @@ -223,12 +225,12 @@ impl Application for ScrollableDemo { .spacing(40), ) .height(Length::Fill) - .horizontal_scroll( + .scrollbar_properties(ScrollbarProperties::Horizontal( Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) .scroller_width(self.scroller_width), - ) + )) .style(theme::Scrollable::custom(ScrollbarCustomStyle)) .id(SCROLLABLE_ID.clone()) .on_scroll(Message::Scrolled), @@ -264,18 +266,16 @@ impl Application for ScrollableDemo { .spacing(40), ) .height(Length::Fill) - .vertical_scroll( + .scrollbar_properties(ScrollbarProperties::Both( Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) .scroller_width(self.scroller_width), - ) - .horizontal_scroll( Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) .scroller_width(self.scroller_width), - ) + )) .style(theme::Scrollable::Custom(Box::new( ScrollbarCustomStyle, ))) -- cgit From 493571695a8853ee91309a92d04b8dbea29bab8d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 27 Jun 2023 22:30:54 +0200 Subject: Rename `ScrollbarProperties` to `Direction` in `scrollable` --- examples/scrollable/src/main.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 1ba81609..4104871f 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -1,12 +1,11 @@ -use iced::widget::scrollable::{ - Properties, ScrollbarProperties, Scrollbar, Scroller, -}; +use iced::widget::scrollable::{Properties, Scrollbar, Scroller}; use iced::widget::{ button, column, container, horizontal_space, progress_bar, radio, row, scrollable, slider, text, vertical_space, }; use iced::{executor, theme, Alignment, Color}; use iced::{Application, Command, Element, Length, Settings, Theme}; + use once_cell::sync::Lazy; static SCROLLABLE_ID: Lazy = Lazy::new(scrollable::Id::unique); @@ -201,7 +200,7 @@ impl Application for ScrollableDemo { .spacing(40), ) .height(Length::Fill) - .scrollbar_properties(ScrollbarProperties::Vertical( + .direction(scrollable::Direction::Vertical( Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) @@ -225,7 +224,7 @@ impl Application for ScrollableDemo { .spacing(40), ) .height(Length::Fill) - .scrollbar_properties(ScrollbarProperties::Horizontal( + .direction(scrollable::Direction::Horizontal( Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) @@ -266,16 +265,17 @@ impl Application for ScrollableDemo { .spacing(40), ) .height(Length::Fill) - .scrollbar_properties(ScrollbarProperties::Both( - Properties::new() - .width(self.scrollbar_width) - .margin(self.scrollbar_margin) - .scroller_width(self.scroller_width), - Properties::new() + .direction({ + let properties = Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) - .scroller_width(self.scroller_width), - )) + .scroller_width(self.scroller_width); + + scrollable::Direction::Both { + horizontal: properties, + vertical: properties, + } + }) .style(theme::Scrollable::Custom(Box::new( ScrollbarCustomStyle, ))) -- cgit From af62ec1c877d0d6d29277fb7dcfd4c681fd499af Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 28 Jun 2023 00:11:41 +0200 Subject: Fix `loading_spinners` example --- examples/loading_spinners/src/circular.rs | 10 ++++++---- examples/loading_spinners/src/linear.rs | 8 ++++---- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 55363de1..4cef081e 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -4,11 +4,13 @@ use iced::advanced::renderer; use iced::advanced::widget::tree::{self, Tree}; use iced::advanced::{Clipboard, Layout, Renderer, Shell, Widget}; use iced::event; +use iced::mouse; use iced::time::Instant; use iced::widget::canvas; use iced::window::{self, RedrawRequest}; -use iced::{Background, Color, Element, Rectangle}; -use iced::{Event, Length, Point, Size, Vector}; +use iced::{ + Background, Color, Element, Event, Length, Rectangle, Size, Vector, +}; use super::easing::{self, Easing}; @@ -266,7 +268,7 @@ where tree: &mut Tree, event: Event, _layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _renderer: &iced::Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -298,7 +300,7 @@ where theme: &Theme, _style: &renderer::Style, layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _viewport: &Rectangle, ) { let state = tree.state.downcast_ref::(); diff --git a/examples/loading_spinners/src/linear.rs b/examples/loading_spinners/src/linear.rs index ea0807c2..3d95729b 100644 --- a/examples/loading_spinners/src/linear.rs +++ b/examples/loading_spinners/src/linear.rs @@ -4,10 +4,10 @@ use iced::advanced::renderer::{self, Quad}; use iced::advanced::widget::tree::{self, Tree}; use iced::advanced::{Clipboard, Layout, Shell, Widget}; use iced::event; +use iced::mouse; use iced::time::Instant; use iced::window::{self, RedrawRequest}; -use iced::{Background, Color, Element, Rectangle}; -use iced::{Event, Length, Point, Size}; +use iced::{Background, Color, Element, Event, Length, Rectangle, Size}; use super::easing::{self, Easing}; @@ -189,7 +189,7 @@ where tree: &mut Tree, event: Event, _layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _renderer: &Renderer, _clipboard: &mut dyn Clipboard, shell: &mut Shell<'_, Message>, @@ -216,7 +216,7 @@ where theme: &Renderer::Theme, _style: &renderer::Style, layout: Layout<'_>, - _cursor_position: Point, + _cursor: mouse::Cursor, _viewport: &Rectangle, ) { let bounds = layout.bounds(); -- cgit From 0ae1baa37bd7b6607f79b33b8a6d8c5daafde0b2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 22 Jun 2023 00:38:36 +0200 Subject: Introduce custom backend-specific primitives --- examples/geometry/Cargo.toml | 1 - examples/geometry/src/main.rs | 7 +++---- examples/loading_spinners/src/circular.rs | 4 +++- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'examples') diff --git a/examples/geometry/Cargo.toml b/examples/geometry/Cargo.toml index 79fe52d5..6068d651 100644 --- a/examples/geometry/Cargo.toml +++ b/examples/geometry/Cargo.toml @@ -7,4 +7,3 @@ publish = false [dependencies] iced = { path = "../..", features = ["advanced"] } -iced_graphics = { path = "../../graphics" } diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index 29f78ea1..e146c6bd 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -1,9 +1,8 @@ //! This example showcases a simple native custom widget that renders using //! arbitrary low-level geometry. mod rainbow { - use iced_graphics::primitive::{ColoredVertex2D, Primitive}; - use iced::advanced::graphics::color; + use iced::advanced::graphics::primitive::{ColoredVertex2D, Primitive}; use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; @@ -46,8 +45,8 @@ mod rainbow { cursor: mouse::Cursor, _viewport: &Rectangle, ) { + use iced::advanced::graphics::primitive::Mesh2D; use iced::advanced::Renderer as _; - use iced_graphics::primitive::Mesh2D; let bounds = layout.bounds(); @@ -135,7 +134,7 @@ mod rainbow { renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - renderer.draw_primitive(mesh); + renderer.draw_with_wgpu(mesh); }, ); } diff --git a/examples/loading_spinners/src/circular.rs b/examples/loading_spinners/src/circular.rs index 4cef081e..3a35e029 100644 --- a/examples/loading_spinners/src/circular.rs +++ b/examples/loading_spinners/src/circular.rs @@ -358,7 +358,9 @@ where renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - renderer.draw_primitive(geometry.0); + use iced::advanced::graphics::geometry::Renderer as _; + + renderer.draw(vec![geometry]); }, ); } -- cgit From fa5650cfd1115e6ccec2ad795cf58fd970d5b43c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 29 Jun 2023 07:48:03 +0200 Subject: Decouple `Mesh` primitives from main `Primitive` type --- examples/geometry/src/main.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) (limited to 'examples') diff --git a/examples/geometry/src/main.rs b/examples/geometry/src/main.rs index e146c6bd..3bc7f46b 100644 --- a/examples/geometry/src/main.rs +++ b/examples/geometry/src/main.rs @@ -2,7 +2,6 @@ //! arbitrary low-level geometry. mod rainbow { use iced::advanced::graphics::color; - use iced::advanced::graphics::primitive::{ColoredVertex2D, Primitive}; use iced::advanced::layout::{self, Layout}; use iced::advanced::renderer; use iced::advanced::widget::{self, Widget}; @@ -45,7 +44,7 @@ mod rainbow { cursor: mouse::Cursor, _viewport: &Rectangle, ) { - use iced::advanced::graphics::primitive::Mesh2D; + use iced::advanced::graphics::mesh::{self, Mesh, SolidVertex2D}; use iced::advanced::Renderer as _; let bounds = layout.bounds(); @@ -77,43 +76,43 @@ mod rainbow { let posn_bl = [0.0, bounds.height]; let posn_l = [0.0, bounds.height / 2.0]; - let mesh = Primitive::SolidMesh { + let mesh = Mesh::Solid { size: bounds.size(), - buffers: Mesh2D { + buffers: mesh::Indexed { vertices: vec![ - ColoredVertex2D { + SolidVertex2D { position: posn_center, color: color::pack([1.0, 1.0, 1.0, 1.0]), }, - ColoredVertex2D { + SolidVertex2D { position: posn_tl, color: color::pack(color_r), }, - ColoredVertex2D { + SolidVertex2D { position: posn_t, color: color::pack(color_o), }, - ColoredVertex2D { + SolidVertex2D { position: posn_tr, color: color::pack(color_y), }, - ColoredVertex2D { + SolidVertex2D { position: posn_r, color: color::pack(color_g), }, - ColoredVertex2D { + SolidVertex2D { position: posn_br, color: color::pack(color_gb), }, - ColoredVertex2D { + SolidVertex2D { position: posn_b, color: color::pack(color_b), }, - ColoredVertex2D { + SolidVertex2D { position: posn_bl, color: color::pack(color_i), }, - ColoredVertex2D { + SolidVertex2D { position: posn_l, color: color::pack(color_v), }, @@ -134,7 +133,7 @@ mod rainbow { renderer.with_translation( Vector::new(bounds.x, bounds.y), |renderer| { - renderer.draw_with_wgpu(mesh); + renderer.draw_mesh(mesh); }, ); } -- cgit From 4f066b516bd7c5a8a3a55f01d09d650e10567839 Mon Sep 17 00:00:00 2001 From: Cory Forsstrom Date: Mon, 12 Jun 2023 21:04:43 -0700 Subject: Add scrollable alignment option --- examples/scrollable/src/main.rs | 52 +++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) (limited to 'examples') diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 4104871f..8c08d993 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -20,6 +20,7 @@ struct ScrollableDemo { scrollbar_margin: u16, scroller_width: u16, current_scroll_offset: scrollable::RelativeOffset, + alignment: scrollable::Alignment, } #[derive(Debug, Clone, Eq, PartialEq, Copy)] @@ -32,6 +33,7 @@ enum Direction { #[derive(Debug, Clone)] enum Message { SwitchDirection(Direction), + AlignmentChanged(scrollable::Alignment), ScrollbarWidthChanged(u16), ScrollbarMarginChanged(u16), ScrollerWidthChanged(u16), @@ -54,6 +56,7 @@ impl Application for ScrollableDemo { scrollbar_margin: 0, scroller_width: 10, current_scroll_offset: scrollable::RelativeOffset::START, + alignment: scrollable::Alignment::Start, }, Command::none(), ) @@ -74,6 +77,15 @@ impl Application for ScrollableDemo { self.current_scroll_offset, ) } + Message::AlignmentChanged(alignment) => { + self.current_scroll_offset = scrollable::RelativeOffset::START; + self.alignment = alignment; + + scrollable::snap_to( + SCROLLABLE_ID.clone(), + self.current_scroll_offset, + ) + } Message::ScrollbarWidthChanged(width) => { self.scrollbar_width = width; @@ -165,10 +177,33 @@ impl Application for ScrollableDemo { .spacing(10) .width(Length::Fill); - let scroll_controls = - row![scroll_slider_controls, scroll_orientation_controls] - .spacing(20) - .width(Length::Fill); + let scroll_alignment_controls = column(vec![ + text("Scrollable alignment:").into(), + radio( + "Start", + scrollable::Alignment::Start, + Some(self.alignment), + Message::AlignmentChanged, + ) + .into(), + radio( + "End", + scrollable::Alignment::End, + Some(self.alignment), + Message::AlignmentChanged, + ) + .into(), + ]) + .spacing(10) + .width(Length::Fill); + + let scroll_controls = row![ + scroll_slider_controls, + scroll_orientation_controls, + scroll_alignment_controls + ] + .spacing(20) + .width(Length::Fill); let scroll_to_end_button = || { button("Scroll to end") @@ -204,7 +239,8 @@ impl Application for ScrollableDemo { Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) - .scroller_width(self.scroller_width), + .scroller_width(self.scroller_width) + .alignment(self.alignment), )) .id(SCROLLABLE_ID.clone()) .on_scroll(Message::Scrolled), @@ -228,7 +264,8 @@ impl Application for ScrollableDemo { Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) - .scroller_width(self.scroller_width), + .scroller_width(self.scroller_width) + .alignment(self.alignment), )) .style(theme::Scrollable::custom(ScrollbarCustomStyle)) .id(SCROLLABLE_ID.clone()) @@ -269,7 +306,8 @@ impl Application for ScrollableDemo { let properties = Properties::new() .width(self.scrollbar_width) .margin(self.scrollbar_margin) - .scroller_width(self.scroller_width); + .scroller_width(self.scroller_width) + .alignment(self.alignment); scrollable::Direction::Both { horizontal: properties, -- cgit From e5c9dd54b3f51e913f39b38e8907c321c8bfd040 Mon Sep 17 00:00:00 2001 From: Joao Freitas <51237625+jhff@users.noreply.github.com> Date: Fri, 19 May 2023 11:24:52 +0100 Subject: Add ability to drag pane to the pane grid edges & optional style for dragged pane --- examples/pane_grid/src/main.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 54c36d69..c5652e2d 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -108,10 +108,15 @@ impl Application for Example { Message::Dragged(pane_grid::DragEvent::Dropped { pane, target, - region, - }) => { - self.panes.split_with(&target, &pane, region); - } + }) => match target { + pane_grid::Target::PaneGrid(edge) => { + self.panes.move_to_edge(&pane, edge) + } + pane_grid::Target::Pane { + pane: target, + region, + } => self.panes.split_with(&target, &pane, region), + }, Message::Dragged(_) => {} Message::TogglePin(pane) => { if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane) -- cgit From ecce8bbcee45dddedef8a33bf3dc086d76c27b39 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 6 Jul 2023 07:37:51 +0200 Subject: Simplify `Target` enum in `widget::pane_grid` --- examples/pane_grid/src/main.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index c5652e2d..1361eeb9 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -109,13 +109,12 @@ impl Application for Example { pane, target, }) => match target { - pane_grid::Target::PaneGrid(edge) => { + pane_grid::Target::Edge(edge) => { self.panes.move_to_edge(&pane, edge) } - pane_grid::Target::Pane { - pane: target, - region, - } => self.panes.split_with(&target, &pane, region), + pane_grid::Target::Pane(target, region) => { + self.panes.split_with(&target, &pane, region) + } }, Message::Dragged(_) => {} Message::TogglePin(pane) => { -- cgit From c5a623f32b3d972501bb02d87d296381b66f9481 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 6 Jul 2023 07:45:47 +0200 Subject: Introduce `drop` helper to `pane_grid::State` --- examples/pane_grid/src/main.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'examples') diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs index 1361eeb9..04896e20 100644 --- a/examples/pane_grid/src/main.rs +++ b/examples/pane_grid/src/main.rs @@ -108,14 +108,9 @@ impl Application for Example { Message::Dragged(pane_grid::DragEvent::Dropped { pane, target, - }) => match target { - pane_grid::Target::Edge(edge) => { - self.panes.move_to_edge(&pane, edge) - } - pane_grid::Target::Pane(target, region) => { - self.panes.split_with(&target, &pane, region) - } - }, + }) => { + self.panes.drop(&pane, target); + } Message::Dragged(_) => {} Message::TogglePin(pane) => { if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane) -- cgit