summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/images/checkbox.pngbin0 -> 5697 bytes
-rw-r--r--docs/images/radio.pngbin0 -> 5415 bytes
-rw-r--r--docs/images/text.pngbin0 -> 5067 bytes
-rw-r--r--docs/images/text_input.pngbin0 -> 3254 bytes
-rw-r--r--examples/todos.rs9
-rw-r--r--native/src/widget.rs9
-rw-r--r--native/src/widget/checkbox.rs4
-rw-r--r--native/src/widget/image.rs19
-rw-r--r--native/src/widget/radio.rs4
-rw-r--r--native/src/widget/scrollable.rs17
-rw-r--r--native/src/widget/text.rs5
-rw-r--r--native/src/widget/text_input.rs5
-rw-r--r--src/lib.rs2
-rw-r--r--wgpu/Cargo.toml2
-rw-r--r--wgpu/src/image.rs172
-rw-r--r--wgpu/src/renderer/widget/image.rs6
-rw-r--r--winit/src/application.rs15
17 files changed, 152 insertions, 117 deletions
diff --git a/docs/images/checkbox.png b/docs/images/checkbox.png
new file mode 100644
index 00000000..9bb68edd
--- /dev/null
+++ b/docs/images/checkbox.png
Binary files differ
diff --git a/docs/images/radio.png b/docs/images/radio.png
new file mode 100644
index 00000000..359b7694
--- /dev/null
+++ b/docs/images/radio.png
Binary files differ
diff --git a/docs/images/text.png b/docs/images/text.png
new file mode 100644
index 00000000..6d520fa5
--- /dev/null
+++ b/docs/images/text.png
Binary files differ
diff --git a/docs/images/text_input.png b/docs/images/text_input.png
new file mode 100644
index 00000000..14c8326c
--- /dev/null
+++ b/docs/images/text_input.png
Binary files differ
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);
/// ```
///
-/// ![Checkbox drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/checkbox.png?raw=true)
+/// ![Checkbox drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/checkbox.png?raw=true)
#[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);
/// ```
///
-/// ![Radio buttons drawn by Coffee's renderer](https://github.com/hecrj/coffee/blob/bda9818f823dfcb8a7ad0ff4940b4d4b387b5208/images/ui/radio.png?raw=true)
+/// ![Radio buttons drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/radio.png?raw=true)
#[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);
/// ```
+///
+/// ![Text drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text.png?raw=true)
#[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);
/// ```
+/// ![Text input drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text_input.png?raw=true)
#[allow(missing_debug_implementations)]
pub struct TextInput<'a, Message> {
state: &'a mut State,
diff --git a/src/lib.rs b/src/lib.rs
index 48588261..1d79269b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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,
+ }
+}