Merge branch 'master' into feature/canvas-interaction

This commit is contained in:
Héctor Ramón Jiménez 2020-04-29 07:34:14 +02:00
commit 61c707fe04
18 changed files with 147 additions and 39 deletions

View file

@ -29,3 +29,5 @@ jobs:
run: cargo check --package iced --target wasm32-unknown-unknown run: cargo check --package iced --target wasm32-unknown-unknown
- name: Check compilation of `tour` example - name: Check compilation of `tour` example
run: cargo build --package tour --target wasm32-unknown-unknown run: cargo build --package tour --target wasm32-unknown-unknown
- name: Check compilation of `pokedex` example
run: cargo build --package pokedex --target wasm32-unknown-unknown

View file

@ -13,7 +13,7 @@ Once a step is completed, it is collapsed and added to this list:
* [x] Scrollables / Clippables ([#24]) * [x] Scrollables / Clippables ([#24])
* [x] Text input widget ([#25]) * [x] Text input widget ([#25])
* [x] TodoMVC example ([#26]) * [x] TodoMVC example ([#26])
* [x] Async actions ([#27]) * [x] Async actions ([#28])
* [x] Custom layout engine ([#52]) * [x] Custom layout engine ([#52])
* [x] Event subscriptions ([#122]) * [x] Event subscriptions ([#122])
* [x] Custom styling ([#146]) * [x] Custom styling ([#146])

View file

@ -16,3 +16,9 @@ pub enum Font {
bytes: &'static [u8], bytes: &'static [u8],
}, },
} }
impl Default for Font {
fn default() -> Font {
Font::Default
}
}

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_futures" name = "iced_futures"
version = "0.1.1" version = "0.1.2"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018" edition = "2018"
description = "Commands, subscriptions, and runtimes for Iced" description = "Commands, subscriptions, and runtimes for Iced"

View file

@ -27,6 +27,7 @@ impl<T> Command<T> {
/// Creates a [`Command`] that performs the action of the given future. /// Creates a [`Command`] that performs the action of the given future.
/// ///
/// [`Command`]: struct.Command.html /// [`Command`]: struct.Command.html
#[cfg(not(target_arch = "wasm32"))]
pub fn perform<A>( pub fn perform<A>(
future: impl Future<Output = T> + 'static + Send, future: impl Future<Output = T> + 'static + Send,
f: impl Fn(T) -> A + 'static + Send, f: impl Fn(T) -> A + 'static + Send,
@ -36,9 +37,23 @@ impl<T> Command<T> {
} }
} }
/// Creates a [`Command`] that performs the action of the given future.
///
/// [`Command`]: struct.Command.html
#[cfg(target_arch = "wasm32")]
pub fn perform<A>(
future: impl Future<Output = T> + 'static,
f: impl Fn(T) -> A + 'static + Send,
) -> Command<A> {
Command {
futures: vec![Box::pin(future.map(f))],
}
}
/// Applies a transformation to the result of a [`Command`]. /// Applies a transformation to the result of a [`Command`].
/// ///
/// [`Command`]: struct.Command.html /// [`Command`]: struct.Command.html
#[cfg(not(target_arch = "wasm32"))]
pub fn map<A>( pub fn map<A>(
mut self, mut self,
f: impl Fn(T) -> A + 'static + Send + Sync, f: impl Fn(T) -> A + 'static + Send + Sync,
@ -62,6 +77,30 @@ impl<T> Command<T> {
} }
} }
/// Applies a transformation to the result of a [`Command`].
///
/// [`Command`]: struct.Command.html
#[cfg(target_arch = "wasm32")]
pub fn map<A>(mut self, f: impl Fn(T) -> A + 'static) -> Command<A>
where
T: 'static,
{
let f = std::rc::Rc::new(f);
Command {
futures: self
.futures
.drain(..)
.map(|future| {
let f = f.clone();
Box::pin(future.map(move |result| f(result)))
as BoxFuture<A>
})
.collect(),
}
}
/// Creates a [`Command`] that performs the actions of all the given /// Creates a [`Command`] that performs the actions of all the given
/// commands. /// commands.
/// ///
@ -85,6 +124,7 @@ impl<T> Command<T> {
} }
} }
#[cfg(not(target_arch = "wasm32"))]
impl<T, A> From<A> for Command<T> impl<T, A> From<A> for Command<T>
where where
A: Future<Output = T> + 'static + Send, A: Future<Output = T> + 'static + Send,
@ -96,6 +136,18 @@ where
} }
} }
#[cfg(target_arch = "wasm32")]
impl<T, A> From<A> for Command<T>
where
A: Future<Output = T> + 'static,
{
fn from(future: A) -> Self {
Self {
futures: vec![future.boxed_local()],
}
}
}
impl<T> std::fmt::Debug for Command<T> { impl<T> std::fmt::Debug for Command<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Command").finish() f.debug_struct("Command").finish()

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_native" name = "iced_native"
version = "0.2.1" version = "0.2.2"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018" edition = "2018"
description = "A renderer-agnostic library for native GUIs" description = "A renderer-agnostic library for native GUIs"

View file

@ -81,7 +81,7 @@ where
/// ///
/// ``` /// ```
/// # mod counter { /// # mod counter {
/// # use iced_native::{text, Text}; /// # type Text = iced_native::Text<iced_native::renderer::Null>;
/// # /// #
/// # #[derive(Debug, Clone, Copy)] /// # #[derive(Debug, Clone, Copy)]
/// # pub enum Message {} /// # pub enum Message {}
@ -227,6 +227,28 @@ where
self.widget.layout(renderer, limits) self.widget.layout(renderer, limits)
} }
/// Processes a runtime [`Event`].
///
/// [`Event`]: enum.Event.html
pub fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
renderer: &Renderer,
clipboard: Option<&dyn Clipboard>,
) {
self.widget.on_event(
event,
layout,
cursor_position,
messages,
renderer,
clipboard,
);
}
/// Draws the [`Element`] and its children using the given [`Layout`]. /// Draws the [`Element`] and its children using the given [`Layout`].
/// ///
/// [`Element`]: struct.Element.html /// [`Element`]: struct.Element.html

View file

@ -47,6 +47,8 @@ impl row::Renderer for Null {
} }
impl text::Renderer for Null { impl text::Renderer for Null {
type Font = Font;
const DEFAULT_SIZE: u16 = 20; const DEFAULT_SIZE: u16 = 20;
fn measure( fn measure(

View file

@ -3,7 +3,7 @@ use std::hash::Hash;
use crate::{ use crate::{
input::{mouse, ButtonState}, input::{mouse, ButtonState},
layout, row, text, Align, Clipboard, Element, Event, Font, Hasher, layout, row, text, Align, Clipboard, Element, Event, Hasher,
HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
VerticalAlignment, Widget, VerticalAlignment, Widget,
}; };
@ -186,7 +186,7 @@ where
label_layout.bounds(), label_layout.bounds(),
&self.label, &self.label,
self.text_size, self.text_size,
Font::Default, Default::default(),
None, None,
HorizontalAlignment::Left, HorizontalAlignment::Left,
VerticalAlignment::Center, VerticalAlignment::Center,

View file

@ -123,6 +123,8 @@ pub struct Handle {
impl Handle { impl Handle {
/// Creates an image [`Handle`] pointing to the image of the given path. /// Creates an image [`Handle`] pointing to the image of the given path.
/// ///
/// Makes an educated guess about the image format by examining the data in the file.
///
/// [`Handle`]: struct.Handle.html /// [`Handle`]: struct.Handle.html
pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle { pub fn from_path<T: Into<PathBuf>>(path: T) -> Handle {
Self::from_data(Data::Path(path.into())) Self::from_data(Data::Path(path.into()))
@ -145,6 +147,8 @@ impl Handle {
/// Creates an image [`Handle`] containing the image data directly. /// Creates an image [`Handle`] containing the image data directly.
/// ///
/// Makes an educated guess about the image format by examining the given data.
///
/// This is useful if you already have your image loaded in-memory, maybe /// This is useful if you already have your image loaded in-memory, maybe
/// because you downloaded or generated it procedurally. /// because you downloaded or generated it procedurally.
/// ///

View file

@ -1,7 +1,7 @@
//! Create choices using radio buttons. //! Create choices using radio buttons.
use crate::{ use crate::{
input::{mouse, ButtonState}, input::{mouse, ButtonState},
layout, row, text, Align, Clipboard, Element, Event, Font, Hasher, layout, row, text, Align, Clipboard, Element, Event, Hasher,
HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text, HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
VerticalAlignment, Widget, VerticalAlignment, Widget,
}; };
@ -155,7 +155,7 @@ where
label_layout.bounds(), label_layout.bounds(),
&self.label, &self.label,
<Renderer as text::Renderer>::DEFAULT_SIZE, <Renderer as text::Renderer>::DEFAULT_SIZE,
Font::Default, Default::default(),
None, None,
HorizontalAlignment::Left, HorizontalAlignment::Left,
VerticalAlignment::Center, VerticalAlignment::Center,

View file

@ -1,7 +1,7 @@
//! Write some text for your users to read. //! Write some text for your users to read.
use crate::{ use crate::{
layout, Color, Element, Font, Hasher, HorizontalAlignment, Layout, Length, layout, Color, Element, Hasher, HorizontalAlignment, Layout, Length, Point,
Point, Rectangle, Size, VerticalAlignment, Widget, Rectangle, Size, VerticalAlignment, Widget,
}; };
use std::hash::Hash; use std::hash::Hash;
@ -11,7 +11,7 @@ use std::hash::Hash;
/// # Example /// # Example
/// ///
/// ``` /// ```
/// # use iced_native::Text; /// # type Text = iced_native::Text<iced_native::renderer::Null>;
/// # /// #
/// Text::new("I <3 iced!") /// Text::new("I <3 iced!")
/// .color([0.0, 0.0, 1.0]) /// .color([0.0, 0.0, 1.0])
@ -20,18 +20,18 @@ use std::hash::Hash;
/// ///
/// ![Text drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text.png?raw=true) /// ![Text drawn by `iced_wgpu`](https://github.com/hecrj/iced/blob/7760618fb112074bc40b148944521f312152012a/docs/images/text.png?raw=true)
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Text { pub struct Text<Renderer: self::Renderer> {
content: String, content: String,
size: Option<u16>, size: Option<u16>,
color: Option<Color>, color: Option<Color>,
font: Font, font: Renderer::Font,
width: Length, width: Length,
height: Length, height: Length,
horizontal_alignment: HorizontalAlignment, horizontal_alignment: HorizontalAlignment,
vertical_alignment: VerticalAlignment, vertical_alignment: VerticalAlignment,
} }
impl Text { impl<Renderer: self::Renderer> Text<Renderer> {
/// Create a new fragment of [`Text`] with the given contents. /// Create a new fragment of [`Text`] with the given contents.
/// ///
/// [`Text`]: struct.Text.html /// [`Text`]: struct.Text.html
@ -40,7 +40,7 @@ impl Text {
content: label.into(), content: label.into(),
size: None, size: None,
color: None, color: None,
font: Font::Default, font: Default::default(),
width: Length::Shrink, width: Length::Shrink,
height: Length::Shrink, height: Length::Shrink,
horizontal_alignment: HorizontalAlignment::Left, horizontal_alignment: HorizontalAlignment::Left,
@ -69,8 +69,8 @@ impl Text {
/// ///
/// [`Text`]: struct.Text.html /// [`Text`]: struct.Text.html
/// [`Font`]: ../../struct.Font.html /// [`Font`]: ../../struct.Font.html
pub fn font(mut self, font: Font) -> Self { pub fn font(mut self, font: impl Into<Renderer::Font>) -> Self {
self.font = font; self.font = font.into();
self self
} }
@ -112,7 +112,7 @@ impl Text {
} }
} }
impl<Message, Renderer> Widget<Message, Renderer> for Text impl<Message, Renderer> Widget<Message, Renderer> for Text<Renderer>
where where
Renderer: self::Renderer, Renderer: self::Renderer,
{ {
@ -163,7 +163,8 @@ where
} }
fn hash_layout(&self, state: &mut Hasher) { fn hash_layout(&self, state: &mut Hasher) {
std::any::TypeId::of::<Text>().hash(state); struct Marker;
std::any::TypeId::of::<Marker>().hash(state);
self.content.hash(state); self.content.hash(state);
self.size.hash(state); self.size.hash(state);
@ -181,6 +182,11 @@ where
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
/// [`UserInterface`]: ../../struct.UserInterface.html /// [`UserInterface`]: ../../struct.UserInterface.html
pub trait Renderer: crate::Renderer { pub trait Renderer: crate::Renderer {
/// The font type used for [`Text`].
///
/// [`Text`]: struct.Text.html
type Font: Default + Copy;
/// The default size of [`Text`]. /// The default size of [`Text`].
/// ///
/// [`Text`]: struct.Text.html /// [`Text`]: struct.Text.html
@ -194,7 +200,7 @@ pub trait Renderer: crate::Renderer {
&self, &self,
content: &str, content: &str,
size: u16, size: u16,
font: Font, font: Self::Font,
bounds: Size, bounds: Size,
) -> (f32, f32); ) -> (f32, f32);
@ -217,18 +223,19 @@ pub trait Renderer: crate::Renderer {
bounds: Rectangle, bounds: Rectangle,
content: &str, content: &str,
size: u16, size: u16,
font: Font, font: Self::Font,
color: Option<Color>, color: Option<Color>,
horizontal_alignment: HorizontalAlignment, horizontal_alignment: HorizontalAlignment,
vertical_alignment: VerticalAlignment, vertical_alignment: VerticalAlignment,
) -> Self::Output; ) -> Self::Output;
} }
impl<'a, Message, Renderer> From<Text> for Element<'a, Message, Renderer> impl<'a, Message, Renderer> From<Text<Renderer>>
for Element<'a, Message, Renderer>
where where
Renderer: self::Renderer, Renderer: self::Renderer + 'a,
{ {
fn from(text: Text) -> Element<'a, Message, Renderer> { fn from(text: Text<Renderer>) -> Element<'a, Message, Renderer> {
Element::new(text) Element::new(text)
} }
} }

View file

@ -20,7 +20,7 @@
mod platform { mod platform {
pub use iced_wgpu::widget::{ pub use iced_wgpu::widget::{
button, checkbox, container, pane_grid, progress_bar, radio, button, checkbox, container, pane_grid, progress_bar, radio,
scrollable, slider, text_input, scrollable, slider, text_input, Text,
}; };
#[cfg(feature = "canvas")] #[cfg(feature = "canvas")]
@ -39,7 +39,7 @@ mod platform {
pub use iced_winit::svg::{Handle, Svg}; pub use iced_winit::svg::{Handle, Svg};
} }
pub use iced_winit::{Space, Text}; pub use iced_winit::Space;
#[doc(no_inline)] #[doc(no_inline)]
pub use { pub use {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_wgpu" name = "iced_wgpu"
version = "0.2.1" version = "0.2.2"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018" edition = "2018"
description = "A wgpu renderer for Iced" description = "A wgpu renderer for Iced"

View file

@ -355,6 +355,18 @@ impl Renderer {
) { ) {
let bounds = layer.bounds * scale_factor; let bounds = layer.bounds * scale_factor;
if !layer.quads.is_empty() {
self.quad_pipeline.draw(
device,
encoder,
&layer.quads,
transformation,
scale_factor,
bounds,
target,
);
}
if !layer.meshes.is_empty() { if !layer.meshes.is_empty() {
let scaled = transformation let scaled = transformation
* Transformation::scale(scale_factor, scale_factor); * Transformation::scale(scale_factor, scale_factor);
@ -371,18 +383,6 @@ impl Renderer {
); );
} }
if !layer.quads.is_empty() {
self.quad_pipeline.draw(
device,
encoder,
&layer.quads,
transformation,
scale_factor,
bounds,
target,
);
}
#[cfg(any(feature = "image", feature = "svg"))] #[cfg(any(feature = "image", feature = "svg"))]
{ {
if !layer.images.is_empty() { if !layer.images.is_empty() {

View file

@ -7,6 +7,8 @@ use iced_native::{
use std::f32; use std::f32;
impl text::Renderer for Renderer { impl text::Renderer for Renderer {
type Font = Font;
const DEFAULT_SIZE: u16 = 20; const DEFAULT_SIZE: u16 = 20;
fn measure( fn measure(

View file

@ -17,6 +17,8 @@ pub mod scrollable;
pub mod slider; pub mod slider;
pub mod text_input; pub mod text_input;
mod text;
#[doc(no_inline)] #[doc(no_inline)]
pub use button::Button; pub use button::Button;
#[doc(no_inline)] #[doc(no_inline)]
@ -36,6 +38,8 @@ pub use slider::Slider;
#[doc(no_inline)] #[doc(no_inline)]
pub use text_input::TextInput; pub use text_input::TextInput;
pub use text::Text;
#[cfg(feature = "canvas")] #[cfg(feature = "canvas")]
#[cfg_attr(docsrs, doc(cfg(feature = "canvas")))] #[cfg_attr(docsrs, doc(cfg(feature = "canvas")))]
pub mod canvas; pub mod canvas;

7
wgpu/src/widget/text.rs Normal file
View file

@ -0,0 +1,7 @@
//! Write some text for your users to read.
use crate::Renderer;
/// A paragraph of text.
///
/// This is an alias of an `iced_native` text with an `iced_wgpu::Renderer`.
pub type Text = iced_native::Text<Renderer>;