diff options
-rw-r--r-- | docs/images/checkbox.png | bin | 0 -> 5697 bytes | |||
-rw-r--r-- | docs/images/radio.png | bin | 0 -> 5415 bytes | |||
-rw-r--r-- | docs/images/text.png | bin | 0 -> 5067 bytes | |||
-rw-r--r-- | docs/images/text_input.png | bin | 0 -> 3254 bytes | |||
-rw-r--r-- | examples/todos.rs | 9 | ||||
-rw-r--r-- | native/src/widget.rs | 9 | ||||
-rw-r--r-- | native/src/widget/checkbox.rs | 4 | ||||
-rw-r--r-- | native/src/widget/image.rs | 19 | ||||
-rw-r--r-- | native/src/widget/radio.rs | 4 | ||||
-rw-r--r-- | native/src/widget/scrollable.rs | 17 | ||||
-rw-r--r-- | native/src/widget/text.rs | 5 | ||||
-rw-r--r-- | native/src/widget/text_input.rs | 5 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | wgpu/Cargo.toml | 2 | ||||
-rw-r--r-- | wgpu/src/image.rs | 172 | ||||
-rw-r--r-- | wgpu/src/renderer/widget/image.rs | 6 | ||||
-rw-r--r-- | winit/src/application.rs | 15 |
17 files changed, 152 insertions, 117 deletions
diff --git a/docs/images/checkbox.png b/docs/images/checkbox.png Binary files differnew file mode 100644 index 00000000..9bb68edd --- /dev/null +++ b/docs/images/checkbox.png diff --git a/docs/images/radio.png b/docs/images/radio.png Binary files differnew file mode 100644 index 00000000..359b7694 --- /dev/null +++ b/docs/images/radio.png diff --git a/docs/images/text.png b/docs/images/text.png Binary files differnew file mode 100644 index 00000000..6d520fa5 --- /dev/null +++ b/docs/images/text.png diff --git a/docs/images/text_input.png b/docs/images/text_input.png Binary files differnew file mode 100644 index 00000000..14c8326c --- /dev/null +++ b/docs/images/text_input.png diff --git a/examples/todos.rs b/examples/todos.rs index a73c45ce..77013dcc 100644 --- a/examples/todos.rs +++ b/examples/todos.rs @@ -508,8 +508,7 @@ impl SavedState { { project_dirs.data_dir().into() } else { - std::env::current_dir() - .expect("The current directory is not accessible") + std::env::current_dir().unwrap_or(std::path::PathBuf::new()) }; path.push("todos.json"); @@ -538,9 +537,11 @@ impl SavedState { .map_err(|_| SaveError::FormatError)?; let path = Self::path(); - let dir = path.parent().ok_or(SaveError::DirectoryError)?; - std::fs::create_dir_all(dir).map_err(|_| SaveError::DirectoryError)?; + if let Some(dir) = path.parent() { + std::fs::create_dir_all(dir) + .map_err(|_| SaveError::DirectoryError)?; + } let mut file = std::fs::File::create(path).map_err(|_| SaveError::FileError)?; diff --git a/native/src/widget.rs b/native/src/widget.rs index 48605ee3..71dcdc0d 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -83,9 +83,9 @@ where /// This [`Node`] is used by the runtime to compute the [`Layout`] of the /// user interface. /// - /// [`Node`]: ../struct.Node.html + /// [`Node`]: ../layout/struct.Node.html /// [`Widget`]: trait.Widget.html - /// [`Layout`]: ../struct.Layout.html + /// [`Layout`]: ../layout/struct.Layout.html fn layout( &self, renderer: &Renderer, @@ -113,7 +113,7 @@ where /// its value cannot affect the overall [`Layout`] of the user interface. /// /// [`Widget`]: trait.Widget.html - /// [`Layout`]: ../struct.Layout.html + /// [`Layout`]: ../layout/struct.Layout.html /// [`Text`]: text/struct.Text.html fn hash_layout(&self, state: &mut Hasher); @@ -125,12 +125,13 @@ where /// * the current cursor position /// * a mutable `Message` list, allowing the [`Widget`] to produce /// new messages based on user interaction. + /// * the `Renderer` /// /// By default, it does nothing. /// /// [`Event`]: ../enum.Event.html /// [`Widget`]: trait.Widget.html - /// [`Layout`]: ../struct.Layout.html + /// [`Layout`]: ../layout/struct.Layout.html fn on_event( &mut self, _event: Event, diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 655fd0ae..9563291c 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -14,7 +14,7 @@ use crate::{ /// /// ``` /// # use iced_native::Checkbox; -/// +/// # /// pub enum Message { /// CheckboxToggled(bool), /// } @@ -24,7 +24,7 @@ use crate::{ /// Checkbox::new(is_checked, "Toggle me!", Message::CheckboxToggled); /// ``` /// -///  +///  #[allow(missing_debug_implementations)] pub struct Checkbox<Message> { is_checked: bool, diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index c64f07b1..4c588c9d 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -10,19 +10,16 @@ use std::hash::Hash; /// /// ``` /// # use iced_native::Image; -/// +/// # /// let image = Image::new("resources/ferris.png"); /// ``` +/// +/// <img src="https://github.com/hecrj/iced/blob/9712b319bb7a32848001b96bd84977430f14b623/examples/resources/ferris.png?raw=true" width="300"> #[derive(Debug)] pub struct Image { - /// The image path - pub path: String, - - /// The width of the image - pub width: Length, - - /// The height of the image - pub height: Length, + path: String, + width: Length, + height: Length, } impl Image { @@ -99,7 +96,7 @@ where layout: Layout<'_>, _cursor_position: Point, ) -> Renderer::Output { - renderer.draw(&self, layout) + renderer.draw(&self.path, layout) } fn hash_layout(&self, state: &mut Hasher) { @@ -124,7 +121,7 @@ pub trait Renderer: crate::Renderer { /// Draws an [`Image`]. /// /// [`Image`]: struct.Image.html - fn draw(&mut self, image: &Image, layout: Layout<'_>) -> Self::Output; + fn draw(&mut self, path: &str, layout: Layout<'_>) -> Self::Output; } impl<'a, Message, Renderer> From<Image> for Element<'a, Message, Renderer> diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index cc642d1c..a9d145db 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -13,7 +13,7 @@ use std::hash::Hash; /// # Example /// ``` /// # use iced_native::Radio; -/// +/// # /// #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// pub enum Choice { /// A, @@ -32,7 +32,7 @@ use std::hash::Hash; /// Radio::new(Choice::B, "This is B", selected_choice, Message::RadioSelected); /// ``` /// -///  +///  #[allow(missing_debug_implementations)] pub struct Radio<Message> { is_selected: bool, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 0d745756..678d837a 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -163,7 +163,7 @@ where match delta { mouse::ScrollDelta::Lines { y, .. } => { // TODO: Configurable speed (?) - self.state.scroll(y * 15.0, bounds, content_bounds); + self.state.scroll(y * 60.0, bounds, content_bounds); } mouse::ScrollDelta::Pixels { y, .. } => { self.state.scroll(y, bounds, content_bounds); @@ -295,7 +295,7 @@ where #[derive(Debug, Clone, Copy, Default)] pub struct State { scrollbar_grabbed_at: Option<Point>, - offset: u32, + offset: f32, } impl State { @@ -321,10 +321,9 @@ impl State { return; } - self.offset = (self.offset as i32 - delta_y.round() as i32) - .max(0) - .min((content_bounds.height - bounds.height) as i32) - as u32; + self.offset = (self.offset - delta_y) + .max(0.0) + .min((content_bounds.height - bounds.height) as f32); } /// Moves the scroll position to a relative amount, given the bounds of @@ -341,8 +340,8 @@ impl State { bounds: Rectangle, content_bounds: Rectangle, ) { - self.offset = ((content_bounds.height - bounds.height) * percentage) - .max(0.0) as u32; + self.offset = + ((content_bounds.height - bounds.height) * percentage).max(0.0); } /// Returns the current scrolling offset of the [`State`], given the bounds @@ -354,7 +353,7 @@ impl State { let hidden_content = (content_bounds.height - bounds.height).max(0.0).round() as u32; - self.offset.min(hidden_content) + self.offset.min(hidden_content as f32) as u32 } /// Returns whether the scrollbar is currently grabbed or not. diff --git a/native/src/widget/text.rs b/native/src/widget/text.rs index cf0701b9..cf9c9565 100644 --- a/native/src/widget/text.rs +++ b/native/src/widget/text.rs @@ -12,10 +12,13 @@ use std::hash::Hash; /// /// ``` /// # use iced_native::Text; -/// +/// # /// Text::new("I <3 iced!") +/// .color([0.0, 0.0, 1.0]) /// .size(40); /// ``` +/// +///  #[derive(Debug, Clone)] pub struct Text { content: String, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index fbf144e3..f97ed424 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -16,6 +16,7 @@ use crate::{ /// ``` /// # use iced_native::{text_input, TextInput}; /// # +/// #[derive(Debug, Clone)] /// enum Message { /// TextInputChanged(String), /// } @@ -28,8 +29,10 @@ use crate::{ /// "This is the placeholder...", /// value, /// Message::TextInputChanged, -/// ); +/// ) +/// .padding(10); /// ``` +///  #[allow(missing_debug_implementations)] pub struct TextInput<'a, Message> { state: &'a mut State, @@ -30,7 +30,7 @@ //! [windowing shell]: https://github.com/hecrj/iced/tree/master/winit //! [`dodrio`]: https://github.com/fitzgen/dodrio //! [web runtime]: https://github.com/hecrj/iced/tree/master/web -//! [examples]: https://github.com/hecrj/iced/tree/0.1.0/examples +//! [examples]: https://github.com/hecrj/iced/tree/master/examples //! [repository]: https://github.com/hecrj/iced //! //! # Overview diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 0d80357c..f008a99c 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -11,7 +11,7 @@ repository = "https://github.com/hecrj/iced" iced_native = { version = "0.1.0", path = "../native" } wgpu = "0.4" glyph_brush = "0.6" -wgpu_glyph = { version = "0.5", git = "https://github.com/hecrj/wgpu_glyph", branch = "feature/scissoring" } +wgpu_glyph = "0.6" raw-window-handle = "0.3" image = "0.22" glam = "0.8" diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 96c9ab6d..5dc972ac 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -8,7 +8,7 @@ pub struct Pipeline { cache: RefCell<HashMap<String, Memory>>, pipeline: wgpu::RenderPipeline, - transform: wgpu::Buffer, + uniforms: wgpu::Buffer, vertices: wgpu::Buffer, indices: wgpu::Buffer, instances: wgpu::Buffer, @@ -46,14 +46,16 @@ impl Pipeline { ], }); - let matrix: [f32; 16] = Transformation::identity().into(); + let uniforms = Uniforms { + transform: Transformation::identity().into(), + }; - let transform_buffer = device + let uniforms_buffer = device .create_buffer_mapped( - 16, + 1, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, ) - .fill_from_slice(&matrix[..]); + .fill_from_slice(&[uniforms]); let constant_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { @@ -62,8 +64,8 @@ impl Pipeline { wgpu::Binding { binding: 0, resource: wgpu::BindingResource::Buffer { - buffer: &transform_buffer, - range: 0..64, + buffer: &uniforms_buffer, + range: 0..std::mem::size_of::<Uniforms>() as u64, }, }, wgpu::Binding { @@ -186,7 +188,7 @@ impl Pipeline { cache: RefCell::new(HashMap::new()), pipeline, - transform: transform_buffer, + uniforms: uniforms_buffer, vertices, indices, instances, @@ -203,12 +205,15 @@ impl Pipeline { fn load(&self, path: &str) { if !self.cache.borrow().contains_key(path) { - let image = image::open(path).expect("Load image").to_bgra(); - - let _ = self - .cache - .borrow_mut() - .insert(path.to_string(), Memory::Host { image }); + let memory = if let Ok(image) = image::open(path) { + Memory::Host { + image: image.to_bgra(), + } + } else { + Memory::NotFound + }; + + let _ = self.cache.borrow_mut().insert(path.to_string(), memory); } } @@ -221,16 +226,18 @@ impl Pipeline { bounds: Rectangle<u32>, target: &wgpu::TextureView, ) { - let transform_buffer = device - .create_buffer_mapped(16, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(transformation.as_ref()); + let uniforms_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[Uniforms { + transform: transformation.into(), + }]); encoder.copy_buffer_to_buffer( - &transform_buffer, + &uniforms_buffer, 0, - &self.transform, + &self.uniforms, 0, - 16 * 4, + std::mem::size_of::<Uniforms>() as u64, ); // TODO: Batch draw calls using a texture atlas @@ -240,68 +247,70 @@ impl Pipeline { for image in instances { self.load(&image.path); - let texture = self + if let Some(texture) = self .cache .borrow_mut() .get_mut(&image.path) .unwrap() - .upload(device, encoder, &self.texture_layout); - - let instance_buffer = device - .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) - .fill_from_slice(&[Instance { - _position: image.position, - _scale: image.scale, - }]); - - encoder.copy_buffer_to_buffer( - &instance_buffer, - 0, - &self.instances, - 0, - mem::size_of::<Image>() as u64, - ); - + .upload(device, encoder, &self.texture_layout) { - let mut render_pass = - encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[ - wgpu::RenderPassColorAttachmentDescriptor { - attachment: target, - resolve_target: None, - load_op: wgpu::LoadOp::Load, - store_op: wgpu::StoreOp::Store, - clear_color: wgpu::Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 0.0, - }, - }, - ], - depth_stencil_attachment: None, - }); - - render_pass.set_pipeline(&self.pipeline); - render_pass.set_bind_group(0, &self.constants, &[]); - render_pass.set_bind_group(1, &texture, &[]); - render_pass.set_index_buffer(&self.indices, 0); - render_pass.set_vertex_buffers( + let instance_buffer = device + .create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC) + .fill_from_slice(&[Instance { + _position: image.position, + _scale: image.scale, + }]); + + encoder.copy_buffer_to_buffer( + &instance_buffer, 0, - &[(&self.vertices, 0), (&self.instances, 0)], - ); - render_pass.set_scissor_rect( - bounds.x, - bounds.y, - bounds.width, - bounds.height, - ); - - render_pass.draw_indexed( - 0..QUAD_INDICES.len() as u32, + &self.instances, 0, - 0..1 as u32, + mem::size_of::<Instance>() as u64, ); + + { + let mut render_pass = encoder.begin_render_pass( + &wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: target, + resolve_target: None, + load_op: wgpu::LoadOp::Load, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 0.0, + }, + }, + ], + depth_stencil_attachment: None, + }, + ); + + render_pass.set_pipeline(&self.pipeline); + render_pass.set_bind_group(0, &self.constants, &[]); + render_pass.set_bind_group(1, &texture, &[]); + render_pass.set_index_buffer(&self.indices, 0); + render_pass.set_vertex_buffers( + 0, + &[(&self.vertices, 0), (&self.instances, 0)], + ); + render_pass.set_scissor_rect( + bounds.x, + bounds.y, + bounds.width, + bounds.height, + ); + + render_pass.draw_indexed( + 0..QUAD_INDICES.len() as u32, + 0, + 0..1 as u32, + ); + } } } } @@ -317,6 +326,7 @@ enum Memory { width: u32, height: u32, }, + NotFound, } impl Memory { @@ -324,6 +334,7 @@ impl Memory { match self { Memory::Host { image } => image.dimensions(), Memory::Device { width, height, .. } => (*width, *height), + Memory::NotFound => (1, 1), } } @@ -332,7 +343,7 @@ impl Memory { device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, texture_layout: &wgpu::BindGroupLayout, - ) -> Rc<wgpu::BindGroup> { + ) -> Option<Rc<wgpu::BindGroup>> { match self { Memory::Host { image } => { let (width, height) = image.dimensions(); @@ -402,9 +413,10 @@ impl Memory { height, }; - bind_group + Some(bind_group) } - Memory::Device { bind_group, .. } => bind_group.clone(), + Memory::Device { bind_group, .. } => Some(bind_group.clone()), + Memory::NotFound => None, } } } @@ -442,3 +454,9 @@ struct Instance { _position: [f32; 2], _scale: [f32; 2], } + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +struct Uniforms { + transform: [f32; 16], +} diff --git a/wgpu/src/renderer/widget/image.rs b/wgpu/src/renderer/widget/image.rs index fe594365..0006dde1 100644 --- a/wgpu/src/renderer/widget/image.rs +++ b/wgpu/src/renderer/widget/image.rs @@ -1,15 +1,15 @@ use crate::{Primitive, Renderer}; -use iced_native::{image, Image, Layout, MouseCursor}; +use iced_native::{image, Layout, MouseCursor}; impl image::Renderer for Renderer { fn dimensions(&self, path: &str) -> (u32, u32) { self.image_pipeline.dimensions(path) } - fn draw(&mut self, image: &Image, layout: Layout<'_>) -> Self::Output { + fn draw(&mut self, path: &str, layout: Layout<'_>) -> Self::Output { ( Primitive::Image { - path: image.path.clone(), + path: String::from(path), bounds: layout.bounds(), }, MouseCursor::OutOfBounds, diff --git a/winit/src/application.rs b/winit/src/application.rs index 4a5fd66b..ec1444f6 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -286,7 +286,9 @@ pub trait Application: Sized { )); } }, - WindowEvent::ReceivedCharacter(c) => { + WindowEvent::ReceivedCharacter(c) + if !is_private_use_character(c) => + { events.push(Event::Keyboard( keyboard::Event::CharacterReceived(c), )); @@ -379,3 +381,14 @@ fn spawn<Message: Send>( thread_pool.spawn_ok(future); } } + +// As defined in: http://www.unicode.org/faq/private_use.html +// TODO: Remove once https://github.com/rust-windowing/winit/pull/1254 lands +fn is_private_use_character(c: char) -> bool { + match c { + '\u{E000}'..='\u{F8FF}' + | '\u{F0000}'..='\u{FFFFD}' + | '\u{100000}'..='\u{10FFFD}' => true, + _ => false, + } +} |