diff options
40 files changed, 349 insertions, 186 deletions
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8c5ded3a..520d8da9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,3 +27,5 @@ jobs: - uses: actions/checkout@master - name: Run checks run: cargo check --package iced --target wasm32-unknown-unknown + - name: Check compilation of `tour` example + run: cargo build --package tour --target wasm32-unknown-unknown diff --git a/CHANGELOG.md b/CHANGELOG.md index 8652fa63..59471abc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.1] - 2020-04-15 +### Added +- `Settings::with_flags` to easily initialize some default settings with flags. [#266] +- `Default` implementation for `canvas::layer::Cache`. [#267] +- `Ctrl + Del` support for `TextInput`. [#268] +- Helper methods in `canvas::Path` to easily draw lines, rectangles, and circles. [#293] +- `From<Color>` implementation for `canvas::Fill`. [#293] +- `From<String>` implementation for `canvas::Text`. [#293] +- `From<&str>` implementation for `canvas::Text`. [#293] + +### Changed +- `new` method of `Radio` and `Checkbox` now take a generic `Into<String>` for the label. [#260] +- `Frame::fill` now takes a generic `Into<canvas::Fill>`. [#293] +- `Frame::stroke` now takes a generic `Into<canvas::Stroke>`. [#293] +- `Frame::fill_text` now takes a generic `Into<canvas::Text>`. [#293] + +### Fixed +- Feature flags not being referenced in documentation. [#259] +- Crash in some graphics drivers when displaying an empty `Canvas`. [#278] +- Text measuring when spaces where present at the beginning of a `TextInput` value. [#279] +- `TextInput` producing a `Clip` primitive when unnecessary. [#279] +- Alignment of `Text` primitive in `iced_wgpu`. [#281] +- `CursorEntered` and `CursorLeft` not being generated. [#289] + +### Removed +- Unnecessary `'static` lifetimes in `Renderer` bounds. [#290] + +[#259]: https://github.com/hecrj/iced/pull/259 +[#260]: https://github.com/hecrj/iced/pull/260 +[#266]: https://github.com/hecrj/iced/pull/266 +[#267]: https://github.com/hecrj/iced/pull/267 +[#268]: https://github.com/hecrj/iced/pull/268 +[#278]: https://github.com/hecrj/iced/pull/278 +[#279]: https://github.com/hecrj/iced/pull/279 +[#281]: https://github.com/hecrj/iced/pull/281 +[#289]: https://github.com/hecrj/iced/pull/289 +[#290]: https://github.com/hecrj/iced/pull/290 +[#293]: https://github.com/hecrj/iced/pull/293 + ## [0.1.0] - 2020-04-02 ### Added @@ -71,7 +110,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - First release! :tada: -[Unreleased]: https://github.com/hecrj/iced/compare/0.1.0...HEAD +[Unreleased]: https://github.com/hecrj/iced/compare/0.1.1...HEAD +[0.1.1]: https://github.com/hecrj/iced/compare/0.1.0...0.1.1 [0.1.0]: https://github.com/hecrj/iced/compare/0.1.0-beta...0.1.0 [0.1.0-beta]: https://github.com/hecrj/iced/compare/0.1.0-alpha...0.1.0-beta [0.1.0-alpha]: https://github.com/hecrj/iced/releases/tag/0.1.0-alpha @@ -1,6 +1,6 @@ [package] name = "iced" -version = "0.1.0" +version = "0.1.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A cross-platform GUI library inspired by Elm" diff --git a/core/Cargo.toml b/core/Cargo.toml index 24e9513a..837f6aae 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_core" -version = "0.2.0" +version = "0.2.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "The essential concepts of Iced" diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 7ed3d2df..aead6e9a 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -17,6 +17,32 @@ pub struct Rectangle<T = f32> { } impl Rectangle<f32> { + /// Returns the [`Point`] at the center of the [`Rectangle`]. + /// + /// [`Point`]: struct.Point.html + /// [`Rectangle`]: struct.Rectangle.html + pub fn center(&self) -> Point { + Point::new(self.center_x(), self.center_y()) + } + + /// Returns the X coordinate of the [`Point`] at the center of the + /// [`Rectangle`]. + /// + /// [`Point`]: struct.Point.html + /// [`Rectangle`]: struct.Rectangle.html + pub fn center_x(&self) -> f32 { + self.x + self.width / 2.0 + } + + /// Returns the Y coordinate of the [`Point`] at the center of the + /// [`Rectangle`]. + /// + /// [`Point`]: struct.Point.html + /// [`Rectangle`]: struct.Rectangle.html + pub fn center_y(&self) -> f32 { + self.y + self.height / 2.0 + } + /// Returns true if the given [`Point`] is contained in the [`Rectangle`]. /// /// [`Point`]: struct.Point.html diff --git a/examples/bezier_tool/src/main.rs b/examples/bezier_tool/src/main.rs index c3fbf276..fcb7733c 100644 --- a/examples/bezier_tool/src/main.rs +++ b/examples/bezier_tool/src/main.rs @@ -12,8 +12,8 @@ mod bezier { // implemented by `iced_wgpu` and other renderers. use iced_native::{ input, layout, Clipboard, Color, Element, Event, Font, Hasher, - HorizontalAlignment, Layout, Length, MouseCursor, Point, Size, Vector, - VerticalAlignment, Widget, + HorizontalAlignment, Layout, Length, MouseCursor, Point, Rectangle, + Size, Vector, VerticalAlignment, Widget, }; use iced_wgpu::{ triangle::{Mesh2D, Vertex2D}, @@ -189,7 +189,11 @@ mod bezier { && self.state.pending.is_none() { let instructions = Primitive::Text { - bounds, + bounds: Rectangle { + x: bounds.center_x(), + y: bounds.center_y(), + ..bounds + }, color: Color { a: defaults.text.color.a * 0.7, ..defaults.text.color diff --git a/examples/clock/src/main.rs b/examples/clock/src/main.rs index 88f8322c..827379fa 100644 --- a/examples/clock/src/main.rs +++ b/examples/clock/src/main.rs @@ -29,7 +29,7 @@ impl Application for Clock { ( Clock { now: chrono::Local::now().into(), - clock: canvas::layer::Cache::new(), + clock: Default::default(), }, Command::none(), ) @@ -95,68 +95,57 @@ impl From<chrono::DateTime<chrono::Local>> for LocalTime { impl canvas::Drawable for LocalTime { fn draw(&self, frame: &mut canvas::Frame) { + use canvas::Path; + let center = frame.center(); let radius = frame.width().min(frame.height()) / 2.0; - let offset = Vector::new(center.x, center.y); - let clock = canvas::Path::new(|path| path.circle(center, radius)); + let clock = Path::circle(center, radius); + frame.fill(&clock, Color::from_rgb8(0x12, 0x93, 0xD8)); - frame.fill( - &clock, - canvas::Fill::Color(Color::from_rgb8(0x12, 0x93, 0xD8)), - ); + let short_hand = + Path::line(Point::ORIGIN, Point::new(0.0, -0.5 * radius)); - fn draw_hand( - n: u32, - total: u32, - length: f32, - offset: Vector, - path: &mut canvas::path::Builder, - ) { - let turns = n as f32 / total as f32; - let t = 2.0 * std::f32::consts::PI * (turns - 0.25); + let long_hand = + Path::line(Point::ORIGIN, Point::new(0.0, -0.8 * radius)); - let x = length * t.cos(); - let y = length * t.sin(); + let thin_stroke = canvas::Stroke { + width: radius / 100.0, + color: Color::WHITE, + line_cap: canvas::LineCap::Round, + ..canvas::Stroke::default() + }; - path.line_to(Point::new(x, y) + offset); - } + let wide_stroke = canvas::Stroke { + width: thin_stroke.width * 3.0, + ..thin_stroke + }; - let hour_and_minute_hands = canvas::Path::new(|path| { - path.move_to(center); - draw_hand(self.hour, 12, 0.5 * radius, offset, path); + frame.translate(Vector::new(center.x, center.y)); - path.move_to(center); - draw_hand(self.minute, 60, 0.8 * radius, offset, path) + frame.with_save(|frame| { + frame.rotate(hand_rotation(self.hour, 12)); + frame.stroke(&short_hand, wide_stroke); }); - frame.stroke( - &hour_and_minute_hands, - canvas::Stroke { - width: radius / 100.0 * 3.0, - color: Color::WHITE, - line_cap: canvas::LineCap::Round, - ..canvas::Stroke::default() - }, - ); - - let second_hand = canvas::Path::new(|path| { - path.move_to(center); - draw_hand(self.second, 60, 0.8 * radius, offset, path) + frame.with_save(|frame| { + frame.rotate(hand_rotation(self.minute, 60)); + frame.stroke(&long_hand, wide_stroke); }); - frame.stroke( - &second_hand, - canvas::Stroke { - width: radius / 100.0, - color: Color::WHITE, - line_cap: canvas::LineCap::Round, - ..canvas::Stroke::default() - }, - ); + frame.with_save(|frame| { + frame.rotate(hand_rotation(self.second, 60)); + frame.stroke(&long_hand, thin_stroke); + }); } } +fn hand_rotation(n: u32, total: u32) -> f32 { + let turns = n as f32 / total as f32; + + 2.0 * std::f32::consts::PI * turns +} + mod time { use iced::futures; diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 1967b7c5..bcd1dc71 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -39,7 +39,7 @@ impl Application for SolarSystem { ( SolarSystem { state: State::new(), - solar_system: canvas::layer::Cache::new(), + solar_system: Default::default(), }, Command::none(), ) @@ -128,14 +128,12 @@ impl State { impl canvas::Drawable for State { fn draw(&self, frame: &mut canvas::Frame) { - use canvas::{Fill, Path, Stroke}; + use canvas::{Path, Stroke}; use std::f32::consts::PI; let center = frame.center(); - let space = Path::new(|path| { - path.rectangle(Point::new(0.0, 0.0), frame.size()) - }); + let space = Path::rectangle(Point::new(0.0, 0.0), frame.size()); let stars = Path::new(|path| { for (p, size) in &self.stars { @@ -143,12 +141,12 @@ impl canvas::Drawable for State { } }); - let sun = Path::new(|path| path.circle(center, Self::SUN_RADIUS)); - let orbit = Path::new(|path| path.circle(center, Self::ORBIT_RADIUS)); + let sun = Path::circle(center, Self::SUN_RADIUS); + let orbit = Path::circle(center, Self::ORBIT_RADIUS); - frame.fill(&space, Fill::Color(Color::BLACK)); - frame.fill(&stars, Fill::Color(Color::WHITE)); - frame.fill(&sun, Fill::Color(Color::from_rgb8(0xF9, 0xD7, 0x1C))); + frame.fill(&space, Color::BLACK); + frame.fill(&stars, Color::WHITE); + frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C)); frame.stroke( &orbit, Stroke { @@ -170,21 +168,13 @@ impl canvas::Drawable for State { ); frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); - let earth = Path::new(|path| { - path.circle(Point::ORIGIN, Self::EARTH_RADIUS) - }); - - let shadow = Path::new(|path| { - path.rectangle( - Point::new(0.0, -Self::EARTH_RADIUS), - Size::new( - Self::EARTH_RADIUS * 4.0, - Self::EARTH_RADIUS * 2.0, - ), - ) - }); + let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS); + let shadow = Path::rectangle( + Point::new(0.0, -Self::EARTH_RADIUS), + Size::new(Self::EARTH_RADIUS * 4.0, Self::EARTH_RADIUS * 2.0), + ); - frame.fill(&earth, Fill::Color(Color::from_rgb8(0x6B, 0x93, 0xD6))); + frame.fill(&earth, Color::from_rgb8(0x6B, 0x93, 0xD6)); frame.with_save(|frame| { frame.rotate( @@ -193,19 +183,16 @@ impl canvas::Drawable for State { ); frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); - let moon = Path::new(|path| { - path.circle(Point::ORIGIN, Self::MOON_RADIUS) - }); - - frame.fill(&moon, Fill::Color(Color::WHITE)); + let moon = Path::circle(Point::ORIGIN, Self::MOON_RADIUS); + frame.fill(&moon, Color::WHITE); }); frame.fill( &shadow, - Fill::Color(Color { + Color { a: 0.7, ..Color::BLACK - }), + }, ); }); } diff --git a/futures/Cargo.toml b/futures/Cargo.toml index 3fe8719b..e0815d9d 100644 --- a/futures/Cargo.toml +++ b/futures/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_futures" -version = "0.1.0" +version = "0.1.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "Commands, subscriptions, and runtimes for Iced" diff --git a/native/Cargo.toml b/native/Cargo.toml index eb21c262..ca58d75c 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_native" -version = "0.2.0" +version = "0.2.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A renderer-agnostic library for native GUIs" diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 5d9221e9..88ffd6d6 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -188,8 +188,15 @@ where let mut messages = Vec::new(); for event in events { - if let Event::Mouse(mouse::Event::CursorMoved { x, y }) = event { - self.cursor_position = Point::new(x, y); + match event { + Event::Mouse(mouse::Event::CursorMoved { x, y }) => { + self.cursor_position = Point::new(x, y); + } + Event::Mouse(mouse::Event::CursorLeft) => { + // TODO: Encode cursor availability + self.cursor_position = Point::new(-1.0, -1.0); + } + _ => {} } self.root.widget.on_event( diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 7b5c349c..3cf4f780 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -143,7 +143,7 @@ impl State { impl<'a, Message, Renderer> Widget<Message, Renderer> for Button<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, Message: Clone, { fn width(&self) -> Length { @@ -234,7 +234,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Button<'_, (), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.width.hash(state); self.content.hash_layout(state); @@ -276,7 +277,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<Button<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a + Clone, { fn from( diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index ccf13848..d611993f 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -110,7 +110,7 @@ impl<Message, Renderer: self::Renderer + text::Renderer> impl<Message, Renderer> Widget<Message, Renderer> for Checkbox<Message, Renderer> where - Renderer: 'static + self::Renderer + text::Renderer + row::Renderer, + Renderer: self::Renderer + text::Renderer + row::Renderer, { fn width(&self) -> Length { self.width @@ -205,7 +205,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Checkbox<(), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.label.hash(state); } @@ -254,7 +255,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From<Checkbox<Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer + text::Renderer + row::Renderer, + Renderer: 'a + self::Renderer + text::Renderer + row::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 77b8496b..259a7e6e 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -124,7 +124,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget<Message, Renderer> for Column<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -190,7 +190,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Column<'_, (), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.width.hash(state); self.height.hash(state); @@ -234,7 +235,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<Column<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 34032c3a..2590fe3b 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -132,7 +132,7 @@ where impl<'a, Message, Renderer> Widget<Message, Renderer> for Container<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -203,7 +203,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Container<'_, (), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.padding.hash(state); self.width.hash(state); @@ -243,7 +244,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/image.rs b/native/src/widget/image.rs index 0f38a90e..6bd0fd68 100644 --- a/native/src/widget/image.rs +++ b/native/src/widget/image.rs @@ -102,7 +102,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Image>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.handle.hash(state); self.width.hash(state); diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index f6dd328e..f84775ed 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -356,7 +356,7 @@ pub struct KeyPressEvent { impl<'a, Message, Renderer> Widget<Message, Renderer> for PaneGrid<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -597,8 +597,9 @@ where fn hash_layout(&self, state: &mut Hasher) { use std::hash::Hash; + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); - std::any::TypeId::of::<PaneGrid<'_, (), Renderer>>().hash(state); self.width.hash(state); self.height.hash(state); self.state.hash_layout(state); @@ -643,7 +644,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<PaneGrid<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index 897edb30..5ab76d47 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -72,7 +72,7 @@ impl<Renderer: self::Renderer> ProgressBar<Renderer> { impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar<Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -114,7 +114,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<ProgressBar<Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.width.hash(state); self.height.hash(state); @@ -159,7 +160,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From<ProgressBar<Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index bc23c116..0ec621bf 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -82,7 +82,7 @@ impl<Message, Renderer: self::Renderer> Radio<Message, Renderer> { impl<Message, Renderer> Widget<Message, Renderer> for Radio<Message, Renderer> where - Renderer: 'static + self::Renderer + text::Renderer + row::Renderer, + Renderer: self::Renderer + text::Renderer + row::Renderer, Message: Clone, { fn width(&self) -> Length { @@ -174,7 +174,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Radio<(), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.label.hash(state); } @@ -218,7 +219,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From<Radio<Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer + row::Renderer + text::Renderer, + Renderer: 'a + self::Renderer + row::Renderer + text::Renderer, Message: 'a + Clone, { fn from(radio: Radio<Message, Renderer>) -> Element<'a, Message, Renderer> { diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 5f139f66..31f7472f 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -125,7 +125,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget<Message, Renderer> for Row<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -191,7 +191,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Row<'_, (), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.width.hash(state); self.height.hash(state); @@ -236,7 +237,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<Row<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from(row: Row<'a, Message, Renderer>) -> Element<'a, Message, Renderer> { diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index ba39edb1..393095a4 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -115,7 +115,7 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget<Message, Renderer> for Scrollable<'a, Message, Renderer> where - Renderer: 'static + self::Renderer + column::Renderer, + Renderer: self::Renderer + column::Renderer, { fn width(&self) -> Length { Widget::<Message, Renderer>::width(&self.content) @@ -311,7 +311,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Scrollable<'_, (), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.height.hash(state); self.max_height.hash(state); @@ -505,7 +506,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer + column::Renderer, + Renderer: 'a + self::Renderer + column::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index b2fa5c9d..1feb7825 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -114,7 +114,7 @@ impl State { impl<'a, Message, Renderer> Widget<Message, Renderer> for Slider<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, { fn width(&self) -> Length { self.width @@ -205,7 +205,8 @@ where } fn hash_layout(&self, state: &mut Hasher) { - std::any::TypeId::of::<Slider<'_, (), Renderer>>().hash(state); + struct Marker; + std::any::TypeId::of::<Marker>().hash(state); self.width.hash(state); } @@ -253,7 +254,7 @@ pub trait Renderer: crate::Renderer { impl<'a, Message, Renderer> From<Slider<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a, { fn from( diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index ae3d74ae..7d1a7415 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -171,7 +171,7 @@ impl<'a, Message, Renderer: self::Renderer> TextInput<'a, Message, Renderer> { impl<'a, Message, Renderer> Widget<Message, Renderer> for TextInput<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: self::Renderer, Message: Clone, { fn width(&self) -> Length { @@ -526,8 +526,8 @@ where fn hash_layout(&self, state: &mut Hasher) { use std::{any::TypeId, hash::Hash}; - - TypeId::of::<TextInput<'_, (), Renderer>>().hash(state); + struct Marker; + TypeId::of::<Marker>().hash(state); self.width.hash(state); self.max_width.hash(state); @@ -632,7 +632,7 @@ pub trait Renderer: crate::Renderer + Sized { impl<'a, Message, Renderer> From<TextInput<'a, Message, Renderer>> for Element<'a, Message, Renderer> where - Renderer: 'static + self::Renderer, + Renderer: 'a + self::Renderer, Message: 'a + Clone, { fn from( diff --git a/src/sandbox.rs b/src/sandbox.rs index 5233ebae..c6fa45d0 100644 --- a/src/sandbox.rs +++ b/src/sandbox.rs @@ -6,11 +6,11 @@ use crate::{executor, Application, Command, Element, Settings, Subscription}; /// simpler interface than [`Application`]. /// /// Unlike an [`Application`], a [`Sandbox`] cannot run any asynchronous -/// actions. However, both traits are very similar and upgrading from a -/// [`Sandbox`] is very straightforward. +/// actions or be initialized with some external flags. However, both traits +/// are very similar and upgrading from a [`Sandbox`] is very straightforward. /// /// Therefore, it is recommended to always start by implementing this trait and -/// upgrade only once you need to perform asynchronous work. +/// upgrade only once necessary. /// /// [`Application`]: trait.Application.html /// [`Sandbox`]: trait.Sandbox.html diff --git a/web/Cargo.toml b/web/Cargo.toml index a0c7c609..12d3865e 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_web" -version = "0.2.0" +version = "0.2.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A web backend for Iced" diff --git a/web/src/widget/checkbox.rs b/web/src/widget/checkbox.rs index 0657ccfb..5ebc26c8 100644 --- a/web/src/widget/checkbox.rs +++ b/web/src/widget/checkbox.rs @@ -43,14 +43,14 @@ impl<Message> Checkbox<Message> { /// `Message`. /// /// [`Checkbox`]: struct.Checkbox.html - pub fn new<F>(is_checked: bool, label: &str, f: F) -> Self + pub fn new<F>(is_checked: bool, label: impl Into<String>, f: F) -> Self where F: 'static + Fn(bool) -> Message, { Checkbox { is_checked, on_toggle: Rc::new(f), - label: String::from(label), + label: label.into(), width: Length::Shrink, style: Default::default(), } diff --git a/web/src/widget/radio.rs b/web/src/widget/radio.rs index e00e26db..520b24cd 100644 --- a/web/src/widget/radio.rs +++ b/web/src/widget/radio.rs @@ -49,7 +49,12 @@ impl<Message> Radio<Message> { /// receives the value of the radio and must produce a `Message`. /// /// [`Radio`]: struct.Radio.html - pub fn new<F, V>(value: V, label: &str, selected: Option<V>, f: F) -> Self + pub fn new<F, V>( + value: V, + label: impl Into<String>, + selected: Option<V>, + f: F, + ) -> Self where V: Eq + Copy, F: 'static + Fn(V) -> Message, @@ -57,7 +62,7 @@ impl<Message> Radio<Message> { Radio { is_selected: Some(value) == selected, on_click: f(value), - label: String::from(label), + label: label.into(), style: Default::default(), } } diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index de496aa9..0794b970 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_wgpu" -version = "0.2.0" +version = "0.2.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A wgpu renderer for Iced" diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index e847cb64..c886bed0 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -152,31 +152,14 @@ impl Renderer { horizontal_alignment, vertical_alignment, } => { - let x = match horizontal_alignment { - iced_native::HorizontalAlignment::Left => bounds.x, - iced_native::HorizontalAlignment::Center => { - bounds.x + bounds.width / 2.0 - } - iced_native::HorizontalAlignment::Right => { - bounds.x + bounds.width - } - }; - - let y = match vertical_alignment { - iced_native::VerticalAlignment::Top => bounds.y, - iced_native::VerticalAlignment::Center => { - bounds.y + bounds.height / 2.0 - } - iced_native::VerticalAlignment::Bottom => { - bounds.y + bounds.height - } - }; - let layer = layers.last_mut().unwrap(); layer.text.push(wgpu_glyph::Section { text: &content, - screen_position: (x + translation.x, y + translation.y), + screen_position: ( + bounds.x + translation.x, + bounds.y + translation.y, + ), bounds: (bounds.width, bounds.height), scale: wgpu_glyph::Scale { x: *size, y: *size }, color: color.into_linear(), diff --git a/wgpu/src/renderer/widget/checkbox.rs b/wgpu/src/renderer/widget/checkbox.rs index ecacf1de..c0f1bf21 100644 --- a/wgpu/src/renderer/widget/checkbox.rs +++ b/wgpu/src/renderer/widget/checkbox.rs @@ -38,7 +38,11 @@ impl checkbox::Renderer for Renderer { content: crate::text::CHECKMARK_ICON.to_string(), font: crate::text::BUILTIN_ICONS, size: bounds.height * 0.7, - bounds, + bounds: Rectangle { + x: bounds.center_x(), + y: bounds.center_y(), + ..bounds + }, color: style.checkmark_color, horizontal_alignment: HorizontalAlignment::Center, vertical_alignment: VerticalAlignment::Center, diff --git a/wgpu/src/renderer/widget/text.rs b/wgpu/src/renderer/widget/text.rs index 33e549cd..80bff574 100644 --- a/wgpu/src/renderer/widget/text.rs +++ b/wgpu/src/renderer/widget/text.rs @@ -31,11 +31,23 @@ impl text::Renderer for Renderer { horizontal_alignment: HorizontalAlignment, vertical_alignment: VerticalAlignment, ) -> Self::Output { + let x = match horizontal_alignment { + iced_native::HorizontalAlignment::Left => bounds.x, + iced_native::HorizontalAlignment::Center => bounds.center_x(), + iced_native::HorizontalAlignment::Right => bounds.x + bounds.width, + }; + + let y = match vertical_alignment { + iced_native::VerticalAlignment::Top => bounds.y, + iced_native::VerticalAlignment::Center => bounds.center_y(), + iced_native::VerticalAlignment::Bottom => bounds.y + bounds.height, + }; + ( Primitive::Text { content: content.to_string(), size: f32::from(size), - bounds, + bounds: Rectangle { x, y, ..bounds }, color: color.unwrap_or(defaults.text.color), font, horizontal_alignment, diff --git a/wgpu/src/renderer/widget/text_input.rs b/wgpu/src/renderer/widget/text_input.rs index 9093b0c6..6f72db68 100644 --- a/wgpu/src/renderer/widget/text_input.rs +++ b/wgpu/src/renderer/widget/text_input.rs @@ -23,11 +23,11 @@ impl text_input::Renderer for Renderer { Size::INFINITY, ); - let spaces_at_the_end = value.len() - value.trim_end().len(); + let spaces_around = value.len() - value.trim().len(); - if spaces_at_the_end > 0 { + if spaces_around > 0 { let space_width = self.text_pipeline.space_width(size as f32); - width += spaces_at_the_end as f32 * space_width; + width += spaces_around as f32 * space_width; } width @@ -109,6 +109,7 @@ impl text_input::Renderer for Renderer { }, font, bounds: Rectangle { + y: text_bounds.center_y(), width: f32::INFINITY, ..text_bounds }, @@ -210,10 +211,20 @@ impl text_input::Renderer for Renderer { (text_value, Vector::new(0, 0)) }; - let contents = Primitive::Clip { - bounds: text_bounds, - offset, - content: Box::new(contents_primitive), + let text_width = self.measure_value( + if text.is_empty() { placeholder } else { &text }, + size, + font, + ); + + let contents = if text_width > text_bounds.width { + Primitive::Clip { + bounds: text_bounds, + offset, + content: Box::new(contents_primitive), + } + } else { + contents_primitive }; ( diff --git a/wgpu/src/widget/canvas/fill.rs b/wgpu/src/widget/canvas/fill.rs index 5ce24cf3..a2010e45 100644 --- a/wgpu/src/widget/canvas/fill.rs +++ b/wgpu/src/widget/canvas/fill.rs @@ -12,3 +12,9 @@ impl Default for Fill { Fill::Color(Color::BLACK) } } + +impl From<Color> for Fill { + fn from(color: Color) -> Fill { + Fill::Color(color) + } +} diff --git a/wgpu/src/widget/canvas/frame.rs b/wgpu/src/widget/canvas/frame.rs index f6495bdc..24fd0d7e 100644 --- a/wgpu/src/widget/canvas/frame.rs +++ b/wgpu/src/widget/canvas/frame.rs @@ -89,14 +89,14 @@ impl Frame { /// /// [`Path`]: path/struct.Path.html /// [`Frame`]: struct.Frame.html - pub fn fill(&mut self, path: &Path, fill: Fill) { + pub fn fill(&mut self, path: &Path, fill: impl Into<Fill>) { use lyon::tessellation::{ BuffersBuilder, FillOptions, FillTessellator, }; let mut buffers = BuffersBuilder::new( &mut self.buffers, - FillVertex(match fill { + FillVertex(match fill.into() { Fill::Color(color) => color.into_linear(), }), ); @@ -127,11 +127,13 @@ impl Frame { /// /// [`Path`]: path/struct.Path.html /// [`Frame`]: struct.Frame.html - pub fn stroke(&mut self, path: &Path, stroke: Stroke) { + pub fn stroke(&mut self, path: &Path, stroke: impl Into<Stroke>) { use lyon::tessellation::{ BuffersBuilder, StrokeOptions, StrokeTessellator, }; + let stroke = stroke.into(); + let mut buffers = BuffersBuilder::new( &mut self.buffers, StrokeVertex(stroke.color.into_linear()), @@ -173,9 +175,11 @@ impl Frame { /// [`Text`]: struct.Text.html /// [`Frame`]: struct.Frame.html /// [`Canvas`]: struct.Canvas.html - pub fn fill_text(&mut self, text: Text) { + pub fn fill_text(&mut self, text: impl Into<Text>) { use std::f32; + let text = text.into(); + let position = if self.transforms.current.is_identity { text.position } else { @@ -262,13 +266,15 @@ impl Frame { /// /// [`Frame`]: struct.Frame.html pub fn into_primitive(mut self) -> Primitive { - self.primitives.push(Primitive::Mesh2D { - origin: Point::ORIGIN, - buffers: triangle::Mesh2D { - vertices: self.buffers.vertices, - indices: self.buffers.indices, - }, - }); + if !self.buffers.indices.is_empty() { + self.primitives.push(Primitive::Mesh2D { + origin: Point::ORIGIN, + buffers: triangle::Mesh2D { + vertices: self.buffers.vertices, + indices: self.buffers.indices, + }, + }); + } Primitive::Group { primitives: self.primitives, diff --git a/wgpu/src/widget/canvas/layer/cache.rs b/wgpu/src/widget/canvas/layer/cache.rs index 20a095bd..4f8c2bec 100644 --- a/wgpu/src/widget/canvas/layer/cache.rs +++ b/wgpu/src/widget/canvas/layer/cache.rs @@ -6,6 +6,19 @@ use crate::{ use iced_native::Size; use std::{cell::RefCell, marker::PhantomData, sync::Arc}; +enum State { + Empty, + Filled { + bounds: Size, + primitive: Arc<Primitive>, + }, +} + +impl Default for State { + fn default() -> Self { + State::Empty + } +} /// A simple cache that stores generated geometry to avoid recomputation. /// /// A [`Cache`] will not redraw its geometry unless the dimensions of its layer @@ -19,12 +32,16 @@ pub struct Cache<T: Drawable> { state: RefCell<State>, } -enum State { - Empty, - Filled { - bounds: Size, - primitive: Arc<Primitive>, - }, +impl<T> Default for Cache<T> +where + T: Drawable, +{ + fn default() -> Self { + Self { + input: PhantomData, + state: Default::default(), + } + } } impl<T> Cache<T> @@ -37,7 +54,7 @@ where pub fn new() -> Self { Cache { input: PhantomData, - state: RefCell::new(State::Empty), + state: Default::default(), } } diff --git a/wgpu/src/widget/canvas/path.rs b/wgpu/src/widget/canvas/path.rs index e7ff47f3..c26bf187 100644 --- a/wgpu/src/widget/canvas/path.rs +++ b/wgpu/src/widget/canvas/path.rs @@ -7,6 +7,8 @@ mod builder; pub use arc::Arc; pub use builder::Builder; +use iced_native::{Point, Size}; + /// An immutable set of points that may or may not be connected. /// /// A single [`Path`] can represent different kinds of 2D shapes! @@ -33,6 +35,33 @@ impl Path { builder.build() } + /// Creates a new [`Path`] representing a line segment given its starting + /// and end points. + /// + /// [`Path`]: struct.Path.html + pub fn line(from: Point, to: Point) -> Self { + Self::new(|p| { + p.move_to(from); + p.line_to(to); + }) + } + + /// Creates a new [`Path`] representing a rectangle given its top-left + /// corner coordinate and its `Size`. + /// + /// [`Path`]: struct.Path.html + pub fn rectangle(top_left: Point, size: Size) -> Self { + Self::new(|p| p.rectangle(top_left, size)) + } + + /// Creates a new [`Path`] representing a circle given its center + /// coordinate and its radius. + /// + /// [`Path`]: struct.Path.html + pub fn circle(center: Point, radius: f32) -> Self { + Self::new(|p| p.circle(center, radius)) + } + #[inline] pub(crate) fn raw(&self) -> &lyon::path::Path { &self.raw diff --git a/wgpu/src/widget/canvas/path/builder.rs b/wgpu/src/widget/canvas/path/builder.rs index a013149e..6511fa52 100644 --- a/wgpu/src/widget/canvas/path/builder.rs +++ b/wgpu/src/widget/canvas/path/builder.rs @@ -133,11 +133,14 @@ impl Builder { /// /// [`Path`]: struct.Path.html #[inline] - pub fn rectangle(&mut self, p: Point, size: Size) { - self.move_to(p); - self.line_to(Point::new(p.x + size.width, p.y)); - self.line_to(Point::new(p.x + size.width, p.y + size.height)); - self.line_to(Point::new(p.x, p.y + size.height)); + pub fn rectangle(&mut self, top_left: Point, size: Size) { + self.move_to(top_left); + self.line_to(Point::new(top_left.x + size.width, top_left.y)); + self.line_to(Point::new( + top_left.x + size.width, + top_left.y + size.height, + )); + self.line_to(Point::new(top_left.x, top_left.y + size.height)); self.close(); } diff --git a/wgpu/src/widget/canvas/text.rs b/wgpu/src/widget/canvas/text.rs index d1cf1a0f..c4cae30e 100644 --- a/wgpu/src/widget/canvas/text.rs +++ b/wgpu/src/widget/canvas/text.rs @@ -32,3 +32,18 @@ impl Default for Text { } } } + +impl From<String> for Text { + fn from(content: String) -> Text { + Text { + content, + ..Default::default() + } + } +} + +impl From<&str> for Text { + fn from(content: &str) -> Text { + String::from(content).into() + } +} diff --git a/winit/Cargo.toml b/winit/Cargo.toml index f59254c1..b6662451 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iced_winit" -version = "0.1.0" +version = "0.1.1" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" description = "A winit runtime for Iced" diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index eaa26ace..78686424 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -27,6 +27,12 @@ pub fn window_event( height: logical_size.height, })) } + WindowEvent::CursorEntered { .. } => { + Some(Event::Mouse(mouse::Event::CursorEntered)) + } + WindowEvent::CursorLeft { .. } => { + Some(Event::Mouse(mouse::Event::CursorLeft)) + } WindowEvent::CursorMoved { position, .. } => { let position = position.to_logical::<f64>(scale_factor); |