Merge branch 'master' into feature/shrink-by-default

This commit is contained in:
Héctor Ramón Jiménez 2020-01-09 01:37:57 +01:00
commit a4e833e860
31 changed files with 1041 additions and 16 deletions

View File

@ -12,4 +12,4 @@ impl From<Color> for Background {
fn from(color: Color) -> Self {
Background::Color(color)
}
}
}

View File

@ -4,6 +4,15 @@ pub enum Length {
/// Fill all the remaining space
Fill,
/// Fill a portion of the remaining space relative to other elements.
///
/// Let's say we have two elements: one with `FillPortion(2)` and one with
/// `FillPortion(3)`. The first will get 2 portions of the available space,
/// while the second one would get 3.
///
/// `Length::Fill` is equivalent to `Length::FillPortion(1)`.
FillPortion(u16),
/// Fill the least amount of space
Shrink,
@ -22,6 +31,7 @@ impl Length {
pub fn fill_factor(&self) -> u16 {
match self {
Length::Fill => 1,
Length::FillPortion(factor) => *factor,
Length::Shrink => 0,
Length::Units(_) => 0,
}

209
examples/geometry.rs Normal file
View File

@ -0,0 +1,209 @@
//! 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_native::{
layout, Element, Hasher, Layout, Length, MouseCursor, Point, Size,
Widget,
};
use iced_wgpu::{
triangle::{Mesh2D, Vertex2D},
Primitive, Renderer,
};
pub struct Rainbow;
impl Rainbow {
pub fn new() -> Self {
Self
}
}
impl<Message> Widget<Message, Renderer> for Rainbow {
fn width(&self) -> Length {
Length::Fill
}
fn height(&self) -> Length {
Length::Shrink
}
fn layout(
&self,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let size = limits.width(Length::Fill).resolve(Size::ZERO);
layout::Node::new(Size::new(size.width, size.width))
}
fn hash_layout(&self, _state: &mut Hasher) {}
fn draw(
&self,
_renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> (Primitive, MouseCursor) {
let b = layout.bounds();
// R O Y G B I V
let color_r = [1.0, 0.0, 0.0, 1.0];
let color_o = [1.0, 0.5, 0.0, 1.0];
let color_y = [1.0, 1.0, 0.0, 1.0];
let color_g = [0.0, 1.0, 0.0, 1.0];
let color_gb = [0.0, 1.0, 0.5, 1.0];
let color_b = [0.0, 0.2, 1.0, 1.0];
let color_i = [0.5, 0.0, 1.0, 1.0];
let color_v = [0.75, 0.0, 0.5, 1.0];
let posn_center = {
if b.contains(cursor_position) {
[cursor_position.x, cursor_position.y]
} else {
[b.x + (b.width / 2.0), b.y + (b.height / 2.0)]
}
};
let posn_tl = [b.x, b.y];
let posn_t = [b.x + (b.width / 2.0), b.y];
let posn_tr = [b.x + b.width, b.y];
let posn_r = [b.x + b.width, b.y + (b.height / 2.0)];
let posn_br = [b.x + b.width, b.y + b.height];
let posn_b = [b.x + (b.width / 2.0), b.y + b.height];
let posn_bl = [b.x, b.y + b.height];
let posn_l = [b.x, b.y + (b.height / 2.0)];
(
Primitive::Mesh2D(std::sync::Arc::new(Mesh2D {
vertices: vec![
Vertex2D {
position: posn_center,
color: [1.0, 1.0, 1.0, 1.0],
},
Vertex2D {
position: posn_tl,
color: color_r,
},
Vertex2D {
position: posn_t,
color: color_o,
},
Vertex2D {
position: posn_tr,
color: color_y,
},
Vertex2D {
position: posn_r,
color: color_g,
},
Vertex2D {
position: posn_br,
color: color_gb,
},
Vertex2D {
position: posn_b,
color: color_b,
},
Vertex2D {
position: posn_bl,
color: color_i,
},
Vertex2D {
position: posn_l,
color: color_v,
},
],
indices: vec![
0, 1, 2, // TL
0, 2, 3, // T
0, 3, 4, // TR
0, 4, 5, // R
0, 5, 6, // BR
0, 6, 7, // B
0, 7, 8, // BL
0, 8, 1, // L
],
})),
MouseCursor::OutOfBounds,
)
}
}
impl<'a, Message> Into<Element<'a, Message, Renderer>> for Rainbow {
fn into(self) -> Element<'a, Message, Renderer> {
Element::new(self)
}
}
}
use iced::{
scrollable, Align, Column, Container, Element, Length, Sandbox, Scrollable,
Settings, Text,
};
use rainbow::Rainbow;
pub fn main() {
Example::run(Settings::default())
}
struct Example {
scroll: scrollable::State,
}
impl Sandbox for Example {
type Message = ();
fn new() -> Self {
Example {
scroll: scrollable::State::new(),
}
}
fn title(&self) -> String {
String::from("Custom 2D geometry - Iced")
}
fn update(&mut self, _: ()) {}
fn view(&mut self) -> Element<()> {
let content = Column::new()
.padding(20)
.spacing(20)
.max_width(500)
.align_items(Align::Start)
.push(Rainbow::new())
.push(Text::new(
"In this example we draw a custom widget Rainbow, using \
the Mesh2D primitive. This primitive supplies a list of \
triangles, expressed as vertices and indices.",
))
.push(Text::new(
"Move your cursor over it, and see the center vertex \
follow you!",
))
.push(Text::new(
"Every Vertex2D defines its own color. You could use the \
Mesh2D primitive to render virtually any two-dimensional \
geometry for your widget.",
));
let scrollable = Scrollable::new(&mut self.scroll)
.push(Container::new(content).width(Length::Fill).center_x());
Container::new(scrollable)
.width(Length::Fill)
.height(Length::Fill)
.center_y()
.into()
}
}

63
examples/progress_bar.rs Normal file
View File

@ -0,0 +1,63 @@
use iced::{
settings::Window, slider, Background, Color, Column, Element, Length,
ProgressBar, Sandbox, Settings, Slider,
};
pub fn main() {
Progress::run(Settings {
window: Window {
size: (700, 300),
resizable: true,
decorations: true,
},
})
}
#[derive(Default)]
struct Progress {
value: f32,
progress_bar_slider: slider::State,
}
#[derive(Debug, Clone, Copy)]
enum Message {
SliderChanged(f32),
}
impl Sandbox for Progress {
type Message = Message;
fn new() -> Self {
Self::default()
}
fn title(&self) -> String {
String::from("A simple Progressbar")
}
fn update(&mut self, message: Message) {
match message {
Message::SliderChanged(x) => self.value = x,
}
}
fn view(&mut self) -> Element<Message> {
Column::new()
.padding(20)
.push(
ProgressBar::new(0.0..=100.0, self.value)
.background(Background::Color(Color::from_rgb(
0.6, 0.6, 0.6,
)))
.active_color(Color::from_rgb(0.0, 0.95, 0.0))
.height(Length::Units(30)),
)
.push(Slider::new(
&mut self.progress_bar_slider,
0.0..=100.0,
self.value,
Message::SliderChanged,
))
.into()
}
}

View File

@ -1,7 +1,7 @@
use iced::{
button, scrollable, slider, text_input, Button, Checkbox, Color, Column,
Container, Element, HorizontalAlignment, Image, Length, Radio, Row,
Sandbox, Scrollable, Settings, Slider, Text, TextInput,
Sandbox, Scrollable, Settings, Slider, Space, Text, TextInput,
};
pub fn main() {
@ -67,7 +67,7 @@ impl Sandbox for Tour {
);
}
controls = controls.push(Column::new().width(Length::Fill));
controls = controls.push(Space::with_width(Length::Fill));
if steps.can_continue() {
controls = controls.push(

View File

@ -52,7 +52,7 @@ impl Limits {
Length::Shrink => {
self.fill.width = self.min.width;
}
Length::Fill => {
Length::Fill | Length::FillPortion(_) => {
self.fill.width = self.fill.width.min(self.max.width);
}
Length::Units(units) => {
@ -76,7 +76,7 @@ impl Limits {
Length::Shrink => {
self.fill.height = self.min.height;
}
Length::Fill => {
Length::Fill | Length::FillPortion(_) => {
self.fill.height = self.fill.height.min(self.max.height);
}
Length::Units(units) => {

View File

@ -22,3 +22,9 @@ pub enum MouseCursor {
/// The cursor is over a text widget.
Text,
}
impl Default for MouseCursor {
fn default() -> MouseCursor {
MouseCursor::OutOfBounds
}
}

View File

@ -25,10 +25,12 @@ pub mod checkbox;
pub mod column;
pub mod container;
pub mod image;
pub mod progress_bar;
pub mod radio;
pub mod row;
pub mod scrollable;
pub mod slider;
pub mod space;
pub mod svg;
pub mod text;
pub mod text_input;
@ -44,6 +46,8 @@ pub use container::Container;
#[doc(no_inline)]
pub use image::Image;
#[doc(no_inline)]
pub use progress_bar::ProgressBar;
#[doc(no_inline)]
pub use radio::Radio;
#[doc(no_inline)]
pub use row::Row;
@ -52,6 +56,8 @@ pub use scrollable::Scrollable;
#[doc(no_inline)]
pub use slider::Slider;
#[doc(no_inline)]
pub use space::Space;
#[doc(no_inline)]
pub use svg::Svg;
#[doc(no_inline)]
pub use text::Text;

View File

@ -0,0 +1,173 @@
//! Provide progress feedback to your users.
use crate::{
layout, Background, Color, Element, Hasher, Layout, Length, Point,
Rectangle, Size, Widget,
};
use std::{hash::Hash, ops::RangeInclusive};
/// A bar that displays progress.
///
/// # Example
/// ```
/// # use iced_native::ProgressBar;
/// #
/// let value = 50.0;
///
/// ProgressBar::new(0.0..=100.0, value);
/// ```
///
/// ![Progress bar drawn with `iced_wgpu`](https://user-images.githubusercontent.com/18618951/71662391-a316c200-2d51-11ea-9cef-52758cab85e3.png)
#[allow(missing_debug_implementations)]
pub struct ProgressBar {
range: RangeInclusive<f32>,
value: f32,
width: Length,
height: Option<Length>,
background: Option<Background>,
active_color: Option<Color>,
}
impl ProgressBar {
/// Creates a new [`ProgressBar`].
///
/// It expects:
/// * an inclusive range of possible values
/// * the current value of the [`ProgressBar`]
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn new(range: RangeInclusive<f32>, value: f32) -> Self {
ProgressBar {
value: value.max(*range.start()).min(*range.end()),
range,
width: Length::Fill,
height: None,
background: None,
active_color: None,
}
}
/// Sets the width of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn height(mut self, height: Length) -> Self {
self.height = Some(height);
self
}
/// Sets the background of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn background(mut self, background: Background) -> Self {
self.background = Some(background);
self
}
/// Sets the active color of the [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
pub fn active_color(mut self, active_color: Color) -> Self {
self.active_color = Some(active_color);
self
}
}
impl<Message, Renderer> Widget<Message, Renderer> for ProgressBar
where
Renderer: self::Renderer,
{
fn width(&self) -> Length {
self.width
}
fn height(&self) -> Length {
self.height
.unwrap_or(Length::Units(Renderer::DEFAULT_HEIGHT))
}
fn layout(
&self,
_renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits.width(self.width).height(
self.height
.unwrap_or(Length::Units(Renderer::DEFAULT_HEIGHT)),
);
let size = limits.resolve(Size::ZERO);
layout::Node::new(size)
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
renderer.draw(
layout.bounds(),
self.range.clone(),
self.value,
self.background,
self.active_color,
)
}
fn hash_layout(&self, state: &mut Hasher) {
self.width.hash(state);
self.height.hash(state);
}
}
/// The renderer of a [`ProgressBar`].
///
/// Your [renderer] will need to implement this trait before being
/// able to use a [`ProgressBar`] in your user interface.
///
/// [`ProgressBar`]: struct.ProgressBar.html
/// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer {
/// The default height of a [`ProgressBar`].
///
/// [`ProgressBar`]: struct.ProgressBar.html
const DEFAULT_HEIGHT: u16;
/// Draws a [`ProgressBar`].
///
/// It receives:
/// * the bounds of the [`ProgressBar`]
/// * the range of values of the [`ProgressBar`]
/// * the current value of the [`ProgressBar`]
/// * maybe a specific background of the [`ProgressBar`]
/// * maybe a specific active color of the [`ProgressBar`]
///
/// [`ProgressBar`]: struct.ProgressBar.html
fn draw(
&self,
bounds: Rectangle,
range: RangeInclusive<f32>,
value: f32,
background: Option<Background>,
active_color: Option<Color>,
) -> Self::Output;
}
impl<'a, Message, Renderer> From<ProgressBar> for Element<'a, Message, Renderer>
where
Renderer: self::Renderer,
Message: 'static,
{
fn from(progress_bar: ProgressBar) -> Element<'a, Message, Renderer> {
Element::new(progress_bar)
}
}

104
native/src/widget/space.rs Normal file
View File

@ -0,0 +1,104 @@
//! Distribute content vertically.
use std::hash::Hash;
use crate::{
layout, Element, Hasher, Layout, Length, Point, Rectangle, Size, Widget,
};
/// An amount of empty space.
///
/// It can be useful if you want to fill some space with nothing.
#[derive(Debug)]
pub struct Space {
width: Length,
height: Length,
}
impl Space {
/// Creates an amount of empty [`Space`] with the given width and height.
///
/// [`Space`]: struct.Space.html
pub fn new(width: Length, height: Length) -> Self {
Space { width, height }
}
/// Creates an amount of horizontal [`Space`].
///
/// [`Space`]: struct.Space.html
pub fn with_width(width: Length) -> Self {
Space {
width,
height: Length::Shrink,
}
}
/// Creates an amount of vertical [`Space`].
///
/// [`Space`]: struct.Space.html
pub fn with_height(height: Length) -> Self {
Space {
width: Length::Shrink,
height,
}
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer> for Space
where
Renderer: self::Renderer,
{
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);
layout::Node::new(limits.resolve(Size::ZERO))
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
_cursor_position: Point,
) -> Renderer::Output {
renderer.draw(layout.bounds())
}
fn hash_layout(&self, state: &mut Hasher) {
std::any::TypeId::of::<Space>().hash(state);
self.width.hash(state);
self.height.hash(state);
}
}
/// The renderer of an amount of [`Space`].
///
/// [`Space`]: struct.Space.html
pub trait Renderer: crate::Renderer {
/// Draws an amount of empty [`Space`].
///
/// You should most likely return an empty primitive here.
///
/// [`Space`]: struct.Space.html
fn draw(&mut self, bounds: Rectangle) -> Self::Output;
}
impl<'a, Message, Renderer> From<Space> for Element<'a, Message, Renderer>
where
Renderer: self::Renderer,
Message: 'static,
{
fn from(space: Space) -> Element<'a, Message, Renderer> {
Element::new(space)
}
}

View File

@ -633,7 +633,8 @@ impl Value {
.unwrap_or(self.len())
}
/// Returns a new [`Value`] containing the graphemes until the given `index`.
/// Returns a new [`Value`] containing the graphemes until the given
/// `index`.
///
/// [`Value`]: struct.Value.html
pub fn until(&self, index: usize) -> Self {

View File

@ -1,6 +1,6 @@
pub use iced_winit::{
Align, Background, Color, Command, Font, HorizontalAlignment, Length,
Subscription, VerticalAlignment,
Space, Subscription, VerticalAlignment,
};
pub mod widget {
@ -85,7 +85,7 @@ pub mod widget {
pub use iced_winit::svg::{Handle, Svg};
}
pub use iced_winit::{Checkbox, Radio, Text};
pub use iced_winit::{Checkbox, ProgressBar, Radio, Text};
#[doc(no_inline)]
pub use {

View File

@ -39,7 +39,11 @@ cargo build --example tour --target wasm32-unknown-unknown
wasm-bindgen ../target/wasm32-unknown-unknown/debug/examples/tour.wasm --out-dir tour --web
```
Then, we need to create an `.html` file to load our application:
*__Note:__ Keep in mind that Iced is still in early exploration stages and most of the work needs to happen on the native side of the ecosystem. At this stage, it is important to be able to batch work without having to constantly jump back and forth. Because of this, there is currently no requirement for the `master` branch to contain a cross-platform API at all times. If you hit an issue when building an example and want to help, it may be a good way to [start contributing]!*
[start contributing]: ../CONTRIBUTING.md
Once the example is compiled, we need to create an `.html` file to load our application:
```html
<!DOCTYPE html>

View File

@ -139,7 +139,7 @@ pub fn length(length: Length) -> String {
match length {
Length::Shrink => String::from("auto"),
Length::Units(px) => format!("{}px", px),
Length::Fill => String::from("100%"),
Length::Fill | Length::FillPortion(_) => String::from("100%"),
}
}

View File

@ -28,6 +28,7 @@ mod container;
mod image;
mod radio;
mod row;
mod space;
mod text;
#[doc(no_inline)]
@ -47,6 +48,7 @@ pub use container::Container;
pub use image::Image;
pub use radio::Radio;
pub use row::Row;
pub use space::Space;
/// A component that displays information and allows interaction.
///

View File

@ -67,7 +67,7 @@ impl<Message> Widget<Message> for Image {
match self.width {
Length::Shrink => {}
Length::Fill => {
Length::Fill | Length::FillPortion(_) => {
image = image.attr("width", "100%");
}
Length::Units(px) => {

69
web/src/widget/space.rs Normal file
View File

@ -0,0 +1,69 @@
use crate::{style, Bus, Element, Length, Widget};
use dodrio::bumpalo;
/// An amount of empty space.
///
/// It can be useful if you want to fill some space with nothing.
#[derive(Debug)]
pub struct Space {
width: Length,
height: Length,
}
impl Space {
/// Creates an amount of empty [`Space`] with the given width and height.
///
/// [`Space`]: struct.Space.html
pub fn new(width: Length, height: Length) -> Self {
Space { width, height }
}
/// Creates an amount of horizontal [`Space`].
///
/// [`Space`]: struct.Space.html
pub fn with_width(width: Length) -> Self {
Space {
width,
height: Length::Shrink,
}
}
/// Creates an amount of vertical [`Space`].
///
/// [`Space`]: struct.Space.html
pub fn with_height(height: Length) -> Self {
Space {
width: Length::Shrink,
height,
}
}
}
impl<'a, Message> Widget<Message> for Space {
fn node<'b>(
&self,
bump: &'b bumpalo::Bump,
_publish: &Bus<Message>,
_style_sheet: &mut style::Sheet<'b>,
) -> dodrio::Node<'b> {
use dodrio::builder::*;
let width = style::length(self.width);
let height = style::length(self.height);
let style = bumpalo::format!(
in bump,
"width: {}; height: {};",
width,
height
);
div(bump).attr("style", style.into_bump_str()).finish()
}
}
impl<'a, Message> From<Space> for Element<'a, Message> {
fn from(space: Space) -> Element<'a, Message> {
Element::new(space)
}
}

View File

@ -32,6 +32,7 @@ pub struct TextInput<'a, Message> {
_state: &'a mut State,
placeholder: String,
value: String,
is_secure: bool,
width: Length,
max_width: Length,
padding: u16,
@ -64,6 +65,7 @@ impl<'a, Message> TextInput<'a, Message> {
_state: state,
placeholder: String::from(placeholder),
value: String::from(value),
is_secure: false,
width: Length::Fill,
max_width: Length::Shrink,
padding: 0,
@ -73,6 +75,14 @@ impl<'a, Message> TextInput<'a, Message> {
}
}
/// Converts the [`TextInput`] into a secure password input.
///
/// [`TextInput`]: struct.TextInput.html
pub fn password(mut self) -> Self {
self.is_secure = true;
self
}
/// Sets the width of the [`TextInput`].
///
/// [`TextInput`]: struct.TextInput.html
@ -161,6 +171,10 @@ where
"value",
bumpalo::format!(in bump, "{}", self.value).into_bump_str(),
)
.attr(
"type",
bumpalo::format!(in bump, "{}", if self.is_secure { "password" } else { "text" }).into_bump_str(),
)
.on("input", move |root, vdom, event| {
let text_input = match event.target().and_then(|t| {
t.dyn_into::<web_sys::HtmlInputElement>().ok()

View File

@ -24,6 +24,8 @@
#![deny(unused_results)]
#![deny(unsafe_code)]
#![deny(rust_2018_idioms)]
pub mod triangle;
mod image;
mod primitive;
mod quad;

View File

@ -3,6 +3,9 @@ use iced_native::{
Vector, VerticalAlignment,
};
use crate::triangle;
use std::sync::Arc;
/// A rendering primitive.
#[derive(Debug, Clone)]
pub enum Primitive {
@ -63,4 +66,14 @@ pub enum Primitive {
/// The content of the clip
content: Box<Primitive>,
},
/// A low-level primitive to render a mesh of triangles.
///
/// It can be used to render many kinds of geometry freely.
Mesh2D(Arc<triangle::Mesh2D>),
}
impl Default for Primitive {
fn default() -> Primitive {
Primitive::None
}
}

View File

@ -1,9 +1,11 @@
use crate::{image, quad, text, Image, Primitive, Quad, Transformation};
use crate::{
image, quad, text, triangle, Image, Primitive, Quad, Transformation,
};
use iced_native::{
renderer::{Debugger, Windowed},
Background, Color, Layout, MouseCursor, Point, Rectangle, Vector, Widget,
};
use std::sync::Arc;
use wgpu::{
Adapter, BackendBit, CommandEncoderDescriptor, Device, DeviceDescriptor,
Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,
@ -24,6 +26,7 @@ pub struct Renderer {
quad_pipeline: quad::Pipeline,
image_pipeline: crate::image::Pipeline,
text_pipeline: text::Pipeline,
triangle_pipeline: crate::triangle::Pipeline,
}
struct Layer<'a> {
@ -31,6 +34,7 @@ struct Layer<'a> {
offset: Vector<u32>,
quads: Vec<Quad>,
images: Vec<Image>,
meshes: Vec<Arc<triangle::Mesh2D>>,
text: Vec<wgpu_glyph::Section<'a>>,
}
@ -42,6 +46,7 @@ impl<'a> Layer<'a> {
quads: Vec::new(),
images: Vec::new(),
text: Vec::new(),
meshes: Vec::new(),
}
}
}
@ -64,6 +69,7 @@ impl Renderer {
let text_pipeline = text::Pipeline::new(&mut device);
let quad_pipeline = quad::Pipeline::new(&mut device);
let image_pipeline = crate::image::Pipeline::new(&mut device);
let triangle_pipeline = triangle::Pipeline::new(&mut device);
Self {
device,
@ -71,6 +77,7 @@ impl Renderer {
quad_pipeline,
image_pipeline,
text_pipeline,
triangle_pipeline,
}
}
@ -244,6 +251,9 @@ impl Renderer {
scale: [bounds.width, bounds.height],
});
}
Primitive::Mesh2D(mesh) => {
layer.meshes.push(mesh.clone());
}
Primitive::Clip {
bounds,
offset,
@ -322,6 +332,24 @@ impl Renderer {
) {
let bounds = layer.bounds * dpi;
if layer.meshes.len() > 0 {
let translated = transformation
* Transformation::translate(
-(layer.offset.x as f32) * dpi,
-(layer.offset.y as f32) * dpi,
);
self.triangle_pipeline.draw(
&mut self.device,
encoder,
target,
translated,
dpi,
&layer.meshes,
bounds,
);
}
if layer.quads.len() > 0 {
self.quad_pipeline.draw(
&mut self.device,

View File

@ -2,10 +2,12 @@ mod button;
mod checkbox;
mod column;
mod image;
mod progress_bar;
mod radio;
mod row;
mod scrollable;
mod slider;
mod space;
mod text;
mod text_input;

View File

@ -0,0 +1,47 @@
use crate::{Primitive, Renderer};
use iced_native::{progress_bar, Background, Color, MouseCursor, Rectangle};
impl progress_bar::Renderer for Renderer {
const DEFAULT_HEIGHT: u16 = 30;
fn draw(
&self,
bounds: Rectangle,
range: std::ops::RangeInclusive<f32>,
value: f32,
background: Option<Background>,
active_color: Option<Color>,
) -> Self::Output {
let (range_start, range_end) = range.into_inner();
let active_progress_width = bounds.width
* ((value - range_start) / (range_end - range_start).max(1.0));
let background = Primitive::Group {
primitives: vec![Primitive::Quad {
bounds: Rectangle { ..bounds },
background: background
.unwrap_or(Background::Color([0.6, 0.6, 0.6].into()))
.into(),
border_radius: 5,
}],
};
let active_progress = Primitive::Quad {
bounds: Rectangle {
width: active_progress_width,
..bounds
},
background: Background::Color(
active_color.unwrap_or([0.0, 0.95, 0.0].into()),
),
border_radius: 5,
};
(
Primitive::Group {
primitives: vec![background, active_progress],
},
MouseCursor::OutOfBounds,
)
}
}

View File

@ -0,0 +1,8 @@
use crate::{Primitive, Renderer};
use iced_native::{space, MouseCursor, Rectangle};
impl space::Renderer for Renderer {
fn draw(&mut self, _bounds: Rectangle) -> Self::Output {
(Primitive::None, MouseCursor::OutOfBounds)
}
}

View File

@ -0,0 +1,8 @@
#version 450
layout(location = 0) in vec4 i_Color;
layout(location = 0) out vec4 o_Color;
void main() {
o_Color = i_Color;
}

Binary file not shown.

View File

@ -0,0 +1,17 @@
#version 450
layout(location = 0) in vec2 i_Position;
layout(location = 1) in vec4 i_Color;
layout(location = 0) out vec4 o_Color;
layout (set = 0, binding = 0) uniform Globals {
mat4 u_Transform;
float u_Scale;
};
void main() {
vec2 p_Position = i_Position * u_Scale;
gl_Position = u_Transform * vec4(p_Position, 0.0, 1.0);
o_Color = i_Color;
}

Binary file not shown.

239
wgpu/src/triangle.rs Normal file
View File

@ -0,0 +1,239 @@
//! Draw meshes of triangles.
use crate::Transformation;
use iced_native::Rectangle;
use std::{mem, sync::Arc};
#[derive(Debug)]
pub(crate) struct Pipeline {
pipeline: wgpu::RenderPipeline,
constants: wgpu::BindGroup,
constants_buffer: wgpu::Buffer,
}
impl Pipeline {
pub fn new(device: &mut wgpu::Device) -> Pipeline {
let constant_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
bindings: &[wgpu::BindGroupLayoutBinding {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::UniformBuffer { dynamic: false },
}],
});
let constants_buffer = device
.create_buffer_mapped(
1,
wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
)
.fill_from_slice(&[Uniforms::default()]);
let constant_bind_group =
device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &constant_layout,
bindings: &[wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::Buffer {
buffer: &constants_buffer,
range: 0..std::mem::size_of::<Uniforms>() as u64,
},
}],
});
let layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&constant_layout],
});
let vs = include_bytes!("shader/triangle.vert.spv");
let vs_module = device.create_shader_module(
&wgpu::read_spirv(std::io::Cursor::new(&vs[..]))
.expect("Read triangle vertex shader as SPIR-V"),
);
let fs = include_bytes!("shader/triangle.frag.spv");
let fs_module = device.create_shader_module(
&wgpu::read_spirv(std::io::Cursor::new(&fs[..]))
.expect("Read triangle fragment shader as SPIR-V"),
);
let pipeline =
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &layout,
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Cw,
cull_mode: wgpu::CullMode::None,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[wgpu::ColorStateDescriptor {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
color_blend: wgpu::BlendDescriptor {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha_blend: wgpu::BlendDescriptor {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
write_mask: wgpu::ColorWrite::ALL,
}],
depth_stencil_state: None,
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[wgpu::VertexBufferDescriptor {
stride: mem::size_of::<Vertex2D>() as u64,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &[
// Position
wgpu::VertexAttributeDescriptor {
shader_location: 0,
format: wgpu::VertexFormat::Float2,
offset: 0,
},
// Color
wgpu::VertexAttributeDescriptor {
shader_location: 1,
format: wgpu::VertexFormat::Float4,
offset: 4 * 2,
},
],
}],
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
Pipeline {
pipeline,
constants: constant_bind_group,
constants_buffer,
}
}
pub fn draw(
&mut self,
device: &mut wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
transformation: Transformation,
scale: f32,
meshes: &Vec<Arc<Mesh2D>>,
bounds: Rectangle<u32>,
) {
let uniforms = Uniforms {
transform: transformation.into(),
scale,
};
let constants_buffer = device
.create_buffer_mapped(1, wgpu::BufferUsage::COPY_SRC)
.fill_from_slice(&[uniforms]);
encoder.copy_buffer_to_buffer(
&constants_buffer,
0,
&self.constants_buffer,
0,
std::mem::size_of::<Uniforms>() 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,
});
for mesh in meshes {
let vertices_buffer = device
.create_buffer_mapped(
mesh.vertices.len(),
wgpu::BufferUsage::VERTEX,
)
.fill_from_slice(&mesh.vertices);
let indices_buffer = device
.create_buffer_mapped(
mesh.indices.len(),
wgpu::BufferUsage::INDEX,
)
.fill_from_slice(&mesh.indices);
render_pass.set_pipeline(&self.pipeline);
render_pass.set_bind_group(0, &self.constants, &[]);
render_pass.set_index_buffer(&indices_buffer, 0);
render_pass.set_vertex_buffers(0, &[(&vertices_buffer, 0)]);
render_pass.set_scissor_rect(
bounds.x,
bounds.y,
bounds.width,
bounds.height,
);
render_pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
struct Uniforms {
transform: [f32; 16],
scale: f32,
}
impl Default for Uniforms {
fn default() -> Self {
Self {
transform: *Transformation::identity().as_ref(),
scale: 1.0,
}
}
}
/// A two-dimensional vertex with some color in __linear__ RGBA.
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Vertex2D {
/// The vertex position
pub position: [f32; 2],
/// The vertex color in __linear__ RGBA.
pub color: [f32; 4],
}
/// A set of [`Vertex2D`] and indices representing a list of triangles.
///
/// [`Vertex2D`]: struct.Vertex2D.html
#[derive(Clone, Debug)]
pub struct Mesh2D {
/// The vertices of the mesh
pub vertices: Vec<Vertex2D>,
/// The list of vertex indices that defines the triangles of the mesh.
///
/// Therefore, this list should always have a length that is a multiple of 3.
pub indices: Vec<u16>,
}

View File

@ -50,7 +50,8 @@ pub fn button_state(element_state: winit::event::ElementState) -> ButtonState {
}
}
/// Convert some `ModifiersState` from [`winit`] to an [`iced_native`] modifiers state.
/// Convert some `ModifiersState` from [`winit`] to an [`iced_native`] modifiers
/// state.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced_native`]: https://github.com/hecrj/iced/tree/master/native

View File

@ -1,5 +1,4 @@
use std::collections::VecDeque;
use std::time;
use std::{collections::VecDeque, time};
#[derive(Debug)]
pub struct Debug {