Merge branch 'master' into theming

This commit is contained in:
Héctor Ramón Jiménez 2022-07-08 19:31:45 +02:00
commit fa55dff61d
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
34 changed files with 444 additions and 337 deletions

View file

@ -18,27 +18,28 @@ Inspired by [Elm].
<img src="https://thumbs.gfycat.com/LittleSaneHalicore-small.gif" height="350px"> <img src="https://thumbs.gfycat.com/LittleSaneHalicore-small.gif" height="350px">
</a> </a>
<a href="https://gfycat.com/politeadorableiberianmole"> <a href="https://gfycat.com/politeadorableiberianmole">
<img src="https://thumbs.gfycat.com/PoliteAdorableIberianmole-small.gif"> <img src="https://thumbs.gfycat.com/PoliteAdorableIberianmole-small.gif" height="350px">
</a> </a>
</div> </div>
## Features ## Features
* Simple, easy-to-use, batteries-included API
* Type-safe, reactive programming model * Simple, easy-to-use, batteries-included API
* [Cross-platform support] (Windows, macOS, Linux, and [the Web]) * Type-safe, reactive programming model
* Responsive layout * [Cross-platform support] (Windows, macOS, Linux, and [the Web])
* Built-in widgets (including [text inputs], [scrollables], and more!) * Responsive layout
* Custom widget support (create your own!) * Built-in widgets (including [text inputs], [scrollables], and more!)
* [Debug overlay with performance metrics] * Custom widget support (create your own!)
* First-class support for async actions (use futures!) * [Debug overlay with performance metrics]
* [Modular ecosystem] split into reusable parts: * First-class support for async actions (use futures!)
* A [renderer-agnostic native runtime] enabling integration with existing systems * [Modular ecosystem] split into reusable parts:
* Two [built-in renderers] leveraging [`wgpu`] and [`glow`] * A [renderer-agnostic native runtime] enabling integration with existing systems
* [`iced_wgpu`] supporting Vulkan, Metal and DX12 * Two [built-in renderers] leveraging [`wgpu`] and [`glow`]
* [`iced_glow`] supporting OpenGL 2.1+ and OpenGL ES 2.0+ * [`iced_wgpu`] supporting Vulkan, Metal and DX12
* A [windowing shell] * [`iced_glow`] supporting OpenGL 2.1+ and OpenGL ES 2.0+
* A [web runtime] leveraging the DOM * A [windowing shell]
* A [web runtime] leveraging the DOM
__Iced is currently experimental software.__ [Take a look at the roadmap], __Iced is currently experimental software.__ [Take a look at the roadmap],
[check out the issues], and [feel free to contribute!] [check out the issues], and [feel free to contribute!]
@ -63,6 +64,7 @@ __Iced is currently experimental software.__ [Take a look at the roadmap],
[feel free to contribute!]: #contributing--feedback [feel free to contribute!]: #contributing--feedback
## Installation ## Installation
Add `iced` as a dependency in your `Cargo.toml`: Add `iced` as a dependency in your `Cargo.toml`:
```toml ```toml
@ -78,15 +80,16 @@ you want to learn about a specific release, check out [the release list].
[the release list]: https://github.com/iced-rs/iced/releases [the release list]: https://github.com/iced-rs/iced/releases
## Overview ## Overview
Inspired by [The Elm Architecture], Iced expects you to split user interfaces Inspired by [The Elm Architecture], Iced expects you to split user interfaces
into four different concepts: into four different concepts:
* __State__ — the state of your application * __State__ — the state of your application
* __Messages__ — user interactions or meaningful events that you care * __Messages__ — user interactions or meaningful events that you care
about about
* __View logic__ — a way to display your __state__ as widgets that * __View logic__ — a way to display your __state__ as widgets that
may produce __messages__ on user interaction may produce __messages__ on user interaction
* __Update logic__ — a way to react to __messages__ and update your * __Update logic__ — a way to react to __messages__ and update your
__state__ __state__
We can build something to see how this works! Let's say we want a simple counter We can build something to see how this works! Let's say we want a simple counter
@ -179,6 +182,7 @@ to:
Browse the [documentation] and the [examples] to learn more! Browse the [documentation] and the [examples] to learn more!
## Implementation details ## Implementation details
Iced was originally born as an attempt at bringing the simplicity of [Elm] and Iced was originally born as an attempt at bringing the simplicity of [Elm] and
[The Elm Architecture] into [Coffee], a 2D game engine I am working on. [The Elm Architecture] into [Coffee], a 2D game engine I am working on.
@ -204,7 +208,9 @@ end-user-oriented GUI library, while keeping [the ecosystem] modular:
[the ecosystem]: ECOSYSTEM.md [the ecosystem]: ECOSYSTEM.md
## Troubleshooting ## Troubleshooting
### `GraphicsAdapterNotFound` ### `GraphicsAdapterNotFound`
This occurs when the selected [built-in renderer] is not able to create a context. This occurs when the selected [built-in renderer] is not able to create a context.
Often this will occur while using [`iced_wgpu`] as the renderer without Often this will occur while using [`iced_wgpu`] as the renderer without
@ -212,22 +218,25 @@ supported hardware (needs Vulkan, Metal or DX12). In this case, you could try us
[`iced_glow`] renderer: [`iced_glow`] renderer:
First, check if it works with First, check if it works with
```console ```console
$ cargo run --features iced/glow --package game_of_life cargo run --features iced/glow --package game_of_life
``` ```
and then use it in your project with and then use it in your project with
```toml ```toml
iced = { version = "0.4", default-features = false, features = ["glow"] } iced = { version = "0.4", default-features = false, features = ["glow"] }
``` ```
**NOTE:** Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0, __NOTE:__ Chances are you have hardware that supports at least OpenGL 2.1 or OpenGL ES 2.0,
but if you don't, right now there's no software fallback, so it means your hardware but if you don't, right now there's no software fallback, so it means your hardware
doesn't support Iced. doesn't support Iced.
[built-in renderer]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md#Renderers [built-in renderer]: https://github.com/iced-rs/iced/blob/master/ECOSYSTEM.md#Renderers
## Contributing / Feedback ## Contributing / Feedback
Contributions are greatly appreciated! If you want to contribute, please Contributions are greatly appreciated! If you want to contribute, please
read our [contributing guidelines] for more details. read our [contributing guidelines] for more details.
@ -237,6 +246,7 @@ awesome folks) over the `#games-and-graphics` and `#gui-and-ui` channels in
the [Rust Community Discord]. I go by `lone_scientist#9554` there. the [Rust Community Discord]. I go by `lone_scientist#9554` there.
## Sponsors ## Sponsors
The development of Iced is sponsored by the [Cryptowatch] team at [Kraken.com] The development of Iced is sponsored by the [Cryptowatch] team at [Kraken.com]
[documentation]: https://docs.rs/iced/ [documentation]: https://docs.rs/iced/

View file

@ -27,10 +27,6 @@ You can run the native version with `cargo run`:
cargo run --package tour cargo run --package tour
``` ```
The web version can be run by following [the usage instructions of `iced_web`] or by accessing [iced.rs](https://iced.rs/)!
[the usage instructions of `iced_web`]: https://github.com/iced-rs/iced_web#usage
## [Todos](todos) ## [Todos](todos)
A todos tracker inspired by [TodoMVC]. It showcases dynamic layout, text input, checkboxes, scrollables, icons, and async actions! It automatically saves your tasks in the background, even if you did not finish typing them. A todos tracker inspired by [TodoMVC]. It showcases dynamic layout, text input, checkboxes, scrollables, icons, and async actions! It automatically saves your tasks in the background, even if you did not finish typing them.
@ -46,7 +42,6 @@ You can run the native version with `cargo run`:
``` ```
cargo run --package todos cargo run --package todos
``` ```
We have not yet implemented a `LocalStorage` version of the auto-save feature. Therefore, it does not work on web _yet_!
[TodoMVC]: http://todomvc.com/ [TodoMVC]: http://todomvc.com/

View file

@ -7,7 +7,6 @@ use scene::Scene;
use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size}; use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size};
use futures::task::SpawnExt;
use winit::{ use winit::{
dpi::PhysicalPosition, dpi::PhysicalPosition,
event::{Event, ModifiersState, WindowEvent}, event::{Event, ModifiersState, WindowEvent},
@ -91,7 +90,9 @@ pub fn main() {
( (
surface surface
.get_preferred_format(&adapter) .get_supported_formats(&adapter)
.first()
.copied()
.expect("Get preferred format"), .expect("Get preferred format"),
adapter adapter
.request_device( .request_device(
@ -114,15 +115,14 @@ pub fn main() {
format, format,
width: physical_size.width, width: physical_size.width,
height: physical_size.height, height: physical_size.height,
present_mode: wgpu::PresentMode::Mailbox, present_mode: wgpu::PresentMode::AutoVsync,
}, },
); );
let mut resized = false; let mut resized = false;
// Initialize staging belt and local pool // Initialize staging belt
let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024);
let mut local_pool = futures::executor::LocalPool::new();
// Initialize scene and GUI controls // Initialize scene and GUI controls
let scene = Scene::new(&mut device, format); let scene = Scene::new(&mut device, format);
@ -208,7 +208,7 @@ pub fn main() {
format: format, format: format,
width: size.width, width: size.width,
height: size.height, height: size.height,
present_mode: wgpu::PresentMode::Mailbox, present_mode: wgpu::PresentMode::AutoVsync,
}, },
); );
@ -263,12 +263,8 @@ pub fn main() {
); );
// And recall staging buffers // And recall staging buffers
local_pool staging_belt.recall();
.spawner()
.spawn(staging_belt.recall())
.expect("Recall staging buffers");
local_pool.run_until_stalled();
} }
Err(error) => match error { Err(error) => match error {
wgpu::SurfaceError::OutOfMemory => { wgpu::SurfaceError::OutOfMemory => {

View file

@ -23,7 +23,7 @@ impl Scene {
) -> wgpu::RenderPass<'a> { ) -> wgpu::RenderPass<'a> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None, label: None,
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target, view: target,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
@ -39,7 +39,7 @@ impl Scene {
}), }),
store: true, store: true,
}, },
}], })],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}) })
} }
@ -55,8 +55,8 @@ fn build_pipeline(
texture_format: wgpu::TextureFormat, texture_format: wgpu::TextureFormat,
) -> wgpu::RenderPipeline { ) -> wgpu::RenderPipeline {
let (vs_module, fs_module) = ( let (vs_module, fs_module) = (
device.create_shader_module(&wgpu::include_wgsl!("shader/vert.wgsl")), device.create_shader_module(wgpu::include_wgsl!("shader/vert.wgsl")),
device.create_shader_module(&wgpu::include_wgsl!("shader/frag.wgsl")), device.create_shader_module(wgpu::include_wgsl!("shader/frag.wgsl")),
); );
let pipeline_layout = let pipeline_layout =
@ -78,14 +78,14 @@ fn build_pipeline(
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &fs_module, module: &fs_module,
entry_point: "main", entry_point: "main",
targets: &[wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format: texture_format, format: texture_format,
blend: Some(wgpu::BlendState { blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent::REPLACE, color: wgpu::BlendComponent::REPLACE,
alpha: wgpu::BlendComponent::REPLACE, alpha: wgpu::BlendComponent::REPLACE,
}), }),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}], })],
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,

View file

@ -1,4 +1,4 @@
[[stage(fragment)]] @fragment
fn main() -> [[location(0)]] vec4<f32> { fn main() -> @location(0) vec4<f32> {
return vec4<f32>(1.0, 0.0, 0.0, 1.0); return vec4<f32>(1.0, 0.0, 0.0, 1.0);
} }

View file

@ -1,5 +1,5 @@
[[stage(vertex)]] @vertex
fn main([[builtin(vertex_index)]] in_vertex_index: u32) -> [[builtin(position)]] vec4<f32> { fn main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
let x = f32(1 - i32(in_vertex_index)) * 0.5; let x = f32(1 - i32(in_vertex_index)) * 0.5;
let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5; let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5;
return vec4<f32>(x, y, 0.0, 1.0); return vec4<f32>(x, y, 0.0, 1.0);

View file

@ -73,6 +73,10 @@ impl Application for SolarSystem {
.height(Length::Fill) .height(Length::Fill)
.into() .into()
} }
fn theme(&self) -> Theme {
Theme::Dark
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -142,16 +146,12 @@ impl<Message> canvas::Program<Message> for State {
use std::f32::consts::PI; use std::f32::consts::PI;
let background = self.space_cache.draw(bounds.size(), |frame| { let background = self.space_cache.draw(bounds.size(), |frame| {
let space = Path::rectangle(Point::new(0.0, 0.0), frame.size());
let stars = Path::new(|path| { let stars = Path::new(|path| {
for (p, size) in &self.stars { for (p, size) in &self.stars {
path.rectangle(*p, Size::new(*size, *size)); path.rectangle(*p, Size::new(*size, *size));
} }
}); });
frame.fill(&space, Color::BLACK);
frame.translate(frame.center() - Point::ORIGIN); frame.translate(frame.center() - Point::ORIGIN);
frame.fill(&stars, Color::WHITE); frame.fill(&stars, Color::WHITE);
}); });

View file

@ -23,6 +23,11 @@ You can run the native version with `cargo run`:
cargo run --package tour cargo run --package tour
``` ```
The web version can be run by following [the usage instructions of `iced_web`] or by accessing [iced.rs](https://iced.rs/)! The web version can be run with [`trunk`]:
[the usage instructions of `iced_web`]: https://github.com/iced-rs/iced_web#usage ```
cd examples/tour
trunk serve
```
[`trunk`]: https://trunkrs.dev/

View file

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

View file

@ -1,6 +1,9 @@
use crate::{BoxFuture, MaybeSend, Subscription}; use crate::{BoxFuture, MaybeSend, Subscription};
use futures::{channel::mpsc, sink::Sink}; use futures::{
channel::mpsc,
sink::{Sink, SinkExt},
};
use std::{collections::HashMap, marker::PhantomData}; use std::{collections::HashMap, marker::PhantomData};
/// A registry of subscription streams. /// A registry of subscription streams.
@ -64,7 +67,7 @@ where
+ MaybeSend + MaybeSend
+ Clone, + Clone,
{ {
use futures::{future::FutureExt, stream::StreamExt}; use futures::stream::StreamExt;
let mut futures: Vec<BoxFuture<()>> = Vec::new(); let mut futures: Vec<BoxFuture<()>> = Vec::new();
@ -85,19 +88,29 @@ where
continue; continue;
} }
let (cancel, cancelled) = futures::channel::oneshot::channel(); let (cancel, mut canceled) = futures::channel::oneshot::channel();
// TODO: Use bus if/when it supports async // TODO: Use bus if/when it supports async
let (event_sender, event_receiver) = let (event_sender, event_receiver) =
futures::channel::mpsc::channel(100); futures::channel::mpsc::channel(100);
let stream = recipe.stream(event_receiver.boxed()); let mut receiver = receiver.clone();
let mut stream = recipe.stream(event_receiver.boxed());
let future = futures::future::select( let future = async move {
cancelled, loop {
stream.map(Ok).forward(receiver.clone()), let select =
) futures::future::select(&mut canceled, stream.next());
.map(|_| ());
match select.await {
futures::future::Either::Left(_)
| futures::future::Either::Right((None, _)) => break,
futures::future::Either::Right((Some(message), _)) => {
let _ = receiver.send(message).await;
}
}
}
};
let _ = self.subscriptions.insert( let _ = self.subscriptions.insert(
id, id,

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_graphics" name = "iced_graphics"
version = "0.3.0" version = "0.3.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021" edition = "2021"
description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced" description = "A bunch of backend-agnostic types that can be leveraged to build a renderer for Iced"

View file

@ -6,7 +6,14 @@ use crate::{Color, Font, Point};
pub struct Text { pub struct Text {
/// The contents of the text /// The contents of the text
pub content: String, pub content: String,
/// The position where to begin drawing the text (top-left corner coordinates) /// The position of the text relative to the alignment properties.
/// By default, this position will be relative to the top-left corner coordinate meaning that
/// if the horizontal and vertical alignments are unchanged, this property will tell where the
/// top-left corner of the text should be placed.
/// By changing the horizontal_alignment and vertical_alignment properties, you are are able to
/// change what part of text is placed at this positions.
/// For example, when the horizontal_alignment and vertical_alignment are set to Center, the
/// center of the text will be placed at the given position NOT the top-left coordinate.
pub position: Point, pub position: Point,
/// The color of the text /// The color of the text
pub color: Color, pub color: Color,

View file

@ -111,7 +111,8 @@ where
B: Backend, B: Backend,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
tree::Tag::of::<P::State>() struct Tag<T>(T);
tree::Tag::of::<Tag<P::State>>()
} }
fn state(&self) -> tree::State { fn state(&self) -> tree::State {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_lazy" name = "iced_lazy"
version = "0.1.0" version = "0.1.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021" edition = "2021"
description = "Lazy widgets for Iced" description = "Lazy widgets for Iced"

View file

@ -419,9 +419,7 @@ where
Some( Some(
CacheBuilder { CacheBuilder {
element: state.view(), element: state.view(),
overlay_builder: |element| { overlay_builder: |_| None,
element.overlay(layout, renderer)
},
} }
.build(), .build(),
) )

View file

@ -70,8 +70,6 @@ where
}) })
} }
struct Tag<T>(T);
struct Instance<'a, Message, Renderer, Event, S> { struct Instance<'a, Message, Renderer, Event, S> {
state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>, state: RefCell<Option<State<'a, Message, Renderer, Event, S>>>,
} }
@ -132,6 +130,7 @@ where
Renderer: iced_native::Renderer, Renderer: iced_native::Renderer,
{ {
fn tag(&self) -> tree::Tag { fn tag(&self) -> tree::Tag {
struct Tag<T>(T);
tree::Tag::of::<Tag<S>>() tree::Tag::of::<Tag<S>>()
} }
@ -467,16 +466,7 @@ where
instance_ref_builder: |instance| instance.state.borrow(), instance_ref_builder: |instance| instance.state.borrow(),
tree: overlay.tree, tree: overlay.tree,
types: PhantomData, types: PhantomData,
overlay_builder: |instance, tree| { overlay_builder: |_, _| None,
instance
.as_ref()
.unwrap()
.borrow_element()
.as_ref()
.unwrap()
.as_widget()
.overlay(&mut tree.children[0], layout, renderer)
},
} }
.build(), .build(),
); );

View file

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

View file

@ -31,6 +31,11 @@ impl<'a, Message> Shell<'a, Message> {
} }
} }
/// Returns whether the current layout is invalid or not.
pub fn is_layout_invalid(&self) -> bool {
self.is_layout_invalid
}
/// Publish the given `Message` for an application to process it. /// Publish the given `Message` for an application to process it.
pub fn publish(&mut self, message: Message) { pub fn publish(&mut self, message: Message) {
self.messages.push(message); self.messages.push(message);

View file

@ -179,40 +179,61 @@ where
clipboard: &mut dyn Clipboard, clipboard: &mut dyn Clipboard,
messages: &mut Vec<Message>, messages: &mut Vec<Message>,
) -> (State, Vec<event::Status>) { ) -> (State, Vec<event::Status>) {
use std::mem::ManuallyDrop;
let mut state = State::Updated; let mut state = State::Updated;
let mut manual_overlay = ManuallyDrop::new(
self.root.overlay(Layout::new(&self.base), renderer),
);
let (base_cursor, overlay_statuses) = if let Some(mut overlay) = let (base_cursor, overlay_statuses) = if manual_overlay.is_some() {
self.root.overlay(Layout::new(&self.base), renderer)
{
let bounds = self.bounds; let bounds = self.bounds;
let mut overlay = manual_overlay.as_mut().unwrap();
let mut layout = overlay.layout(renderer, bounds); let mut layout = overlay.layout(renderer, bounds);
let mut event_statuses = Vec::new();
let event_statuses = events for event in events.iter().cloned() {
.iter() let mut shell = Shell::new(messages);
.cloned()
.map(|event| {
let mut shell = Shell::new(messages);
let event_status = overlay.on_event( let event_status = overlay.on_event(
event, event,
Layout::new(&layout), Layout::new(&layout),
cursor_position, cursor_position,
renderer, renderer,
clipboard, clipboard,
&mut shell, &mut shell,
);
event_statuses.push(event_status);
if shell.is_layout_invalid() {
let _ = ManuallyDrop::into_inner(manual_overlay);
self.base = renderer.layout(
&self.root,
&layout::Limits::new(Size::ZERO, self.bounds),
); );
manual_overlay = ManuallyDrop::new(
self.root.overlay(Layout::new(&self.base), renderer),
);
if manual_overlay.is_none() {
break;
}
overlay = manual_overlay.as_mut().unwrap();
shell.revalidate_layout(|| { shell.revalidate_layout(|| {
layout = overlay.layout(renderer, bounds); layout = overlay.layout(renderer, bounds);
}); });
}
if shell.are_widgets_invalid() { if shell.are_widgets_invalid() {
state = State::Outdated; state = State::Outdated;
} }
}
event_status
})
.collect();
let base_cursor = if layout.bounds().contains(cursor_position) { let base_cursor = if layout.bounds().contains(cursor_position) {
// TODO: Type-safe cursor availability // TODO: Type-safe cursor availability
@ -228,11 +249,17 @@ where
(cursor_position, vec![event::Status::Ignored; events.len()]) (cursor_position, vec![event::Status::Ignored; events.len()])
}; };
let _ = ManuallyDrop::into_inner(manual_overlay);
let event_statuses = events let event_statuses = events
.iter() .iter()
.cloned() .cloned()
.zip(overlay_statuses.into_iter()) .zip(overlay_statuses.into_iter())
.map(|(event, overlay_status)| { .map(|(event, overlay_status)| {
if matches!(overlay_status, event::Status::Captured) {
return overlay_status;
}
let mut shell = Shell::new(messages); let mut shell = Shell::new(messages);
let event_status = self.root.widget.on_event( let event_status = self.root.widget.on_event(

View file

@ -114,20 +114,17 @@ where
let mut children = padded.children(); let mut children = padded.children();
let title_layout = children.next().unwrap(); let title_layout = children.next().unwrap();
let mut show_title = true;
self.content.draw(
renderer,
theme,
&inherited_style,
title_layout,
cursor_position,
viewport,
);
if let Some(controls) = &self.controls { if let Some(controls) = &self.controls {
let controls_layout = children.next().unwrap(); let controls_layout = children.next().unwrap();
if show_controls || self.always_show_controls { if show_controls || self.always_show_controls {
if title_layout.bounds().width + controls_layout.bounds().width
> padded.bounds().width
{
show_title = false;
}
controls.draw( controls.draw(
renderer, renderer,
theme, theme,
@ -138,6 +135,17 @@ where
); );
} }
} }
if show_title {
self.content.draw(
renderer,
theme,
&inherited_style,
title_layout,
cursor_position,
viewport,
);
}
} }
/// Returns whether the mouse cursor is over the pick area of the /// Returns whether the mouse cursor is over the pick area of the
@ -225,9 +233,15 @@ where
let mut children = padded.children(); let mut children = padded.children();
let title_layout = children.next().unwrap(); let title_layout = children.next().unwrap();
let mut show_title = true;
let control_status = if let Some(controls) = &mut self.controls { let control_status = if let Some(controls) = &mut self.controls {
let controls_layout = children.next().unwrap(); let controls_layout = children.next().unwrap();
if title_layout.bounds().width + controls_layout.bounds().width
> padded.bounds().width
{
show_title = false;
}
controls.on_event( controls.on_event(
event.clone(), event.clone(),
@ -241,14 +255,18 @@ where
event::Status::Ignored event::Status::Ignored
}; };
let title_status = self.content.on_event( let title_status = if show_title {
event, self.content.on_event(
title_layout, event,
cursor_position, title_layout,
renderer, cursor_position,
clipboard, renderer,
shell, clipboard,
); shell,
)
} else {
event::Status::Ignored
};
control_status.merge(title_status) control_status.merge(title_status)
} }
@ -275,15 +293,20 @@ where
if let Some(controls) = &self.controls { if let Some(controls) = &self.controls {
let controls_layout = children.next().unwrap(); let controls_layout = children.next().unwrap();
let controls_interaction = controls.mouse_interaction(
controls_layout,
cursor_position,
viewport,
renderer,
);
controls if title_layout.bounds().width + controls_layout.bounds().width
.mouse_interaction( > padded.bounds().width
controls_layout, {
cursor_position, controls_interaction
viewport, } else {
renderer, controls_interaction.max(title_interaction)
) }
.max(title_interaction)
} else { } else {
title_interaction title_interaction
} }

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_pure" name = "iced_pure"
version = "0.2.1" version = "0.2.2"
edition = "2021" edition = "2021"
description = "Pure widgets for Iced" description = "Pure widgets for Iced"
license = "MIT" license = "MIT"

View file

@ -141,19 +141,15 @@ where
let mut children = padded.children(); let mut children = padded.children();
let title_layout = children.next().unwrap(); let title_layout = children.next().unwrap();
let mut show_title = true;
self.content.as_widget().draw(
&tree.children[0],
renderer,
theme,
&inherited_style,
title_layout,
cursor_position,
viewport,
);
if let Some(controls) = &self.controls { if let Some(controls) = &self.controls {
let controls_layout = children.next().unwrap(); let controls_layout = children.next().unwrap();
if title_layout.bounds().width + controls_layout.bounds().width
> padded.bounds().width
{
show_title = false;
}
if show_controls || self.always_show_controls { if show_controls || self.always_show_controls {
controls.as_widget().draw( controls.as_widget().draw(
@ -167,6 +163,18 @@ where
); );
} }
} }
if show_title {
self.content.as_widget().draw(
&tree.children[0],
renderer,
theme,
&inherited_style,
title_layout,
cursor_position,
viewport,
);
}
} }
/// Returns whether the mouse cursor is over the pick area of the /// Returns whether the mouse cursor is over the pick area of the
@ -258,9 +266,15 @@ where
let mut children = padded.children(); let mut children = padded.children();
let title_layout = children.next().unwrap(); let title_layout = children.next().unwrap();
let mut show_title = true;
let control_status = if let Some(controls) = &mut self.controls { let control_status = if let Some(controls) = &mut self.controls {
let controls_layout = children.next().unwrap(); let controls_layout = children.next().unwrap();
if title_layout.bounds().width + controls_layout.bounds().width
> padded.bounds().width
{
show_title = false;
}
controls.as_widget_mut().on_event( controls.as_widget_mut().on_event(
&mut tree.children[1], &mut tree.children[1],
@ -275,15 +289,19 @@ where
event::Status::Ignored event::Status::Ignored
}; };
let title_status = self.content.as_widget_mut().on_event( let title_status = if show_title {
&mut tree.children[0], self.content.as_widget_mut().on_event(
event, &mut tree.children[0],
title_layout, event,
cursor_position, title_layout,
renderer, cursor_position,
clipboard, renderer,
shell, clipboard,
); shell,
)
} else {
event::Status::Ignored
};
control_status.merge(title_status) control_status.merge(title_status)
} }
@ -312,17 +330,21 @@ where
if let Some(controls) = &self.controls { if let Some(controls) = &self.controls {
let controls_layout = children.next().unwrap(); let controls_layout = children.next().unwrap();
let controls_interaction = controls.as_widget().mouse_interaction(
&tree.children[1],
controls_layout,
cursor_position,
viewport,
renderer,
);
controls if title_layout.bounds().width + controls_layout.bounds().width
.as_widget() > padded.bounds().width
.mouse_interaction( {
&tree.children[1], controls_interaction
controls_layout, } else {
cursor_position, controls_interaction.max(title_interaction)
viewport, }
renderer,
)
.max(title_interaction)
} else { } else {
title_interaction title_interaction
} }

View file

@ -126,6 +126,34 @@ where
self.style = style.into(); self.style = style.into();
self self
} }
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
/// [`text_input::Value`] if provided.
///
/// [`Renderer`]: text::Renderer
pub fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Renderer::Theme,
layout: Layout<'_>,
cursor_position: Point,
value: Option<&text_input::Value>,
) {
text_input::draw(
renderer,
theme,
layout,
cursor_position,
tree.state.downcast_ref::<text_input::State>(),
value.unwrap_or(&self.value),
&self.placeholder,
self.size,
&self.font,
self.is_secure,
self.style,
)
}
} }
impl<'a, Message, Renderer> Widget<Message, Renderer> impl<'a, Message, Renderer> Widget<Message, Renderer>

View file

@ -1,6 +1,6 @@
[package] [package]
name = "iced_wgpu" name = "iced_wgpu"
version = "0.5.0" version = "0.5.1"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2021" edition = "2021"
description = "A wgpu renderer for Iced" description = "A wgpu renderer for Iced"
@ -28,8 +28,8 @@ spirv = ["wgpu/spirv"]
webgl = ["wgpu/webgl"] webgl = ["wgpu/webgl"]
[dependencies] [dependencies]
wgpu = "0.12" wgpu = "0.13"
wgpu_glyph = "0.16" wgpu_glyph = "0.17"
glyph_brush = "0.7" glyph_brush = "0.7"
raw-window-handle = "0.4" raw-window-handle = "0.4"
log = "0.4" log = "0.4"
@ -39,7 +39,7 @@ kamadak-exif = "0.5"
bitflags = "1.2" bitflags = "1.2"
[dependencies.bytemuck] [dependencies.bytemuck]
version = "1.4" version = "1.9"
features = ["derive"] features = ["derive"]
[dependencies.iced_native] [dependencies.iced_native]

View file

@ -136,7 +136,7 @@ impl Pipeline {
}); });
let shader = let shader =
device.create_shader_module(&wgpu::ShaderModuleDescriptor { device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu::image::shader"), label: Some("iced_wgpu::image::shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
include_str!("shader/image.wgsl"), include_str!("shader/image.wgsl"),
@ -176,7 +176,7 @@ impl Pipeline {
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
entry_point: "fs_main", entry_point: "fs_main",
targets: &[wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format, format,
blend: Some(wgpu::BlendState { blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent { color: wgpu::BlendComponent {
@ -191,7 +191,7 @@ impl Pipeline {
}, },
}), }),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}], })],
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
@ -406,14 +406,16 @@ impl Pipeline {
let mut render_pass = let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("iced_wgpu::image render pass"), label: Some("iced_wgpu::image render pass"),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(
view: target, wgpu::RenderPassColorAttachment {
resolve_target: None, view: target,
ops: wgpu::Operations { resolve_target: None,
load: wgpu::LoadOp::Load, ops: wgpu::Operations {
store: true, load: wgpu::LoadOp::Load,
store: true,
},
}, },
}], )],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });

View file

@ -59,7 +59,7 @@ impl Pipeline {
}); });
let shader = let shader =
device.create_shader_module(&wgpu::ShaderModuleDescriptor { device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu::quad::shader"), label: Some("iced_wgpu::quad::shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
include_str!("shader/quad.wgsl"), include_str!("shader/quad.wgsl"),
@ -100,7 +100,7 @@ impl Pipeline {
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
entry_point: "fs_main", entry_point: "fs_main",
targets: &[wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format, format,
blend: Some(wgpu::BlendState { blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent { color: wgpu::BlendComponent {
@ -115,7 +115,7 @@ impl Pipeline {
}, },
}), }),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}], })],
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
@ -211,14 +211,16 @@ impl Pipeline {
let mut render_pass = let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("iced_wgpu::quad render pass"), label: Some("iced_wgpu::quad render pass"),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(
view: target, wgpu::RenderPassColorAttachment {
resolve_target: None, view: target,
ops: wgpu::Operations { resolve_target: None,
load: wgpu::LoadOp::Load, ops: wgpu::Operations {
store: true, load: wgpu::LoadOp::Load,
store: true,
},
}, },
}], )],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });

View file

@ -63,7 +63,7 @@ impl Settings {
impl Default for Settings { impl Default for Settings {
fn default() -> Settings { fn default() -> Settings {
Settings { Settings {
present_mode: wgpu::PresentMode::Mailbox, present_mode: wgpu::PresentMode::AutoVsync,
internal_backend: wgpu::Backends::all(), internal_backend: wgpu::Backends::all(),
default_font: None, default_font: None,
default_text_size: 20, default_text_size: 20,

View file

@ -16,19 +16,19 @@ var<private> uvs: array<vec2<f32>, 6> = array<vec2<f32>, 6>(
vec2<f32>(1.0, 1.0) vec2<f32>(1.0, 1.0)
); );
[[group(0), binding(0)]] var u_sampler: sampler; @group(0) @binding(0) var u_sampler: sampler;
[[group(1), binding(0)]] var u_texture: texture_2d<f32>; @group(1) @binding(0) var u_texture: texture_2d<f32>;
struct VertexInput { struct VertexInput {
[[builtin(vertex_index)]] vertex_index: u32; @builtin(vertex_index) vertex_index: u32,
}; }
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] position: vec4<f32>; @builtin(position) position: vec4<f32>,
[[location(0)]] uv: vec2<f32>; @location(0) uv: vec2<f32>,
}; }
[[stage(vertex)]] @vertex
fn vs_main(input: VertexInput) -> VertexOutput { fn vs_main(input: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.uv = uvs[input.vertex_index]; out.uv = uvs[input.vertex_index];
@ -37,7 +37,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
return out; return out;
} }
[[stage(fragment)]] @fragment
fn fs_main(input: VertexOutput) -> [[location(0)]] vec4<f32> { fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(u_texture, u_sampler, input.uv); return textureSample(u_texture, u_sampler, input.uv);
} }

View file

@ -1,27 +1,27 @@
struct Globals { struct Globals {
transform: mat4x4<f32>; transform: mat4x4<f32>,
}; }
[[group(0), binding(0)]] var<uniform> globals: Globals; @group(0) @binding(0) var<uniform> globals: Globals;
[[group(0), binding(1)]] var u_sampler: sampler; @group(0) @binding(1) var u_sampler: sampler;
[[group(1), binding(0)]] var u_texture: texture_2d_array<f32>; @group(1) @binding(0) var u_texture: texture_2d_array<f32>;
struct VertexInput { struct VertexInput {
[[location(0)]] v_pos: vec2<f32>; @location(0) v_pos: vec2<f32>,
[[location(1)]] pos: vec2<f32>; @location(1) pos: vec2<f32>,
[[location(2)]] scale: vec2<f32>; @location(2) scale: vec2<f32>,
[[location(3)]] atlas_pos: vec2<f32>; @location(3) atlas_pos: vec2<f32>,
[[location(4)]] atlas_scale: vec2<f32>; @location(4) atlas_scale: vec2<f32>,
[[location(5)]] layer: i32; @location(5) layer: i32,
}; }
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] position: vec4<f32>; @builtin(position) position: vec4<f32>,
[[location(0)]] uv: vec2<f32>; @location(0) uv: vec2<f32>,
[[location(1)]] layer: f32; // this should be an i32, but naga currently reads that as requiring interpolation. @location(1) layer: f32, // this should be an i32, but naga currently reads that as requiring interpolation.
}; }
[[stage(vertex)]] @vertex
fn vs_main(input: VertexInput) -> VertexOutput { fn vs_main(input: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
@ -40,7 +40,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
return out; return out;
} }
[[stage(fragment)]] @fragment
fn fs_main(input: VertexOutput) -> [[location(0)]] vec4<f32> { fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
return textureSample(u_texture, u_sampler, input.uv, i32(input.layer)); return textureSample(u_texture, u_sampler, input.uv, i32(input.layer));
} }

View file

@ -1,31 +1,31 @@
struct Globals { struct Globals {
transform: mat4x4<f32>; transform: mat4x4<f32>,
scale: f32; scale: f32,
}; }
[[group(0), binding(0)]] var<uniform> globals: Globals; @group(0) @binding(0) var<uniform> globals: Globals;
struct VertexInput { struct VertexInput {
[[location(0)]] v_pos: vec2<f32>; @location(0) v_pos: vec2<f32>,
[[location(1)]] pos: vec2<f32>; @location(1) pos: vec2<f32>,
[[location(2)]] scale: vec2<f32>; @location(2) scale: vec2<f32>,
[[location(3)]] color: vec4<f32>; @location(3) color: vec4<f32>,
[[location(4)]] border_color: vec4<f32>; @location(4) border_color: vec4<f32>,
[[location(5)]] border_radius: f32; @location(5) border_radius: f32,
[[location(6)]] border_width: f32; @location(6) border_width: f32,
}; }
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] position: vec4<f32>; @builtin(position) position: vec4<f32>,
[[location(0)]] color: vec4<f32>; @location(0) color: vec4<f32>,
[[location(1)]] border_color: vec4<f32>; @location(1) border_color: vec4<f32>,
[[location(2)]] pos: vec2<f32>; @location(2) pos: vec2<f32>,
[[location(3)]] scale: vec2<f32>; @location(3) scale: vec2<f32>,
[[location(4)]] border_radius: f32; @location(4) border_radius: f32,
[[location(5)]] border_width: f32; @location(5) border_width: f32,
}; }
[[stage(vertex)]] @vertex
fn vs_main(input: VertexInput) -> VertexOutput { fn vs_main(input: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
@ -77,10 +77,10 @@ fn distance_alg(
} }
[[stage(fragment)]] @fragment
fn fs_main( fn fs_main(
input: VertexOutput input: VertexOutput
) -> [[location(0)]] vec4<f32> { ) -> @location(0) vec4<f32> {
var mixed_color: vec4<f32> = input.color; var mixed_color: vec4<f32> = input.color;
if (input.border_width > 0.0) { if (input.border_width > 0.0) {
@ -96,7 +96,7 @@ fn fs_main(
internal_border internal_border
); );
var border_mix: f32 = smoothStep( var border_mix: f32 = smoothstep(
max(internal_border - 0.5, 0.0), max(internal_border - 0.5, 0.0),
internal_border + 0.5, internal_border + 0.5,
internal_distance internal_distance
@ -112,7 +112,7 @@ fn fs_main(
input.border_radius input.border_radius
); );
var radius_alpha: f32 = 1.0 - smoothStep( var radius_alpha: f32 = 1.0 - smoothstep(
max(input.border_radius - 0.5, 0.0), max(input.border_radius - 0.5, 0.0),
input.border_radius + 0.5, input.border_radius + 0.5,
dist); dist);

View file

@ -1,20 +1,20 @@
struct Globals { struct Globals {
transform: mat4x4<f32>; transform: mat4x4<f32>,
}; }
[[group(0), binding(0)]] var<uniform> globals: Globals; @group(0) @binding(0) var<uniform> globals: Globals;
struct VertexInput { struct VertexInput {
[[location(0)]] position: vec2<f32>; @location(0) position: vec2<f32>,
[[location(1)]] color: vec4<f32>; @location(1) color: vec4<f32>,
}; }
struct VertexOutput { struct VertexOutput {
[[builtin(position)]] position: vec4<f32>; @builtin(position) position: vec4<f32>,
[[location(0)]] color: vec4<f32>; @location(0) color: vec4<f32>,
}; }
[[stage(vertex)]] @vertex
fn vs_main(input: VertexInput) -> VertexOutput { fn vs_main(input: VertexInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
@ -24,7 +24,7 @@ fn vs_main(input: VertexInput) -> VertexOutput {
return out; return out;
} }
[[stage(fragment)]] @fragment
fn fs_main(input: VertexOutput) -> [[location(0)]] vec4<f32> { fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
return input.color; return input.color;
} }

View file

@ -132,7 +132,7 @@ impl Pipeline {
}); });
let shader = let shader =
device.create_shader_module(&wgpu::ShaderModuleDescriptor { device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu::triangle::shader"), label: Some("iced_wgpu::triangle::shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
include_str!("shader/triangle.wgsl"), include_str!("shader/triangle.wgsl"),
@ -160,22 +160,11 @@ impl Pipeline {
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
entry_point: "fs_main", entry_point: "fs_main",
targets: &[wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format, format,
blend: Some(wgpu::BlendState { blend: Some(wgpu::BlendState::ALPHA_BLENDING),
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}], })],
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
@ -361,11 +350,13 @@ impl Pipeline {
let mut render_pass = let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("iced_wgpu::triangle render pass"), label: Some("iced_wgpu::triangle render pass"),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(
view: attachment, wgpu::RenderPassColorAttachment {
resolve_target, view: attachment,
ops: wgpu::Operations { load, store: true }, resolve_target,
}], ops: wgpu::Operations { load, store: true },
},
)],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });

View file

@ -74,7 +74,7 @@ impl Blit {
}); });
let shader = let shader =
device.create_shader_module(&wgpu::ShaderModuleDescriptor { device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("iced_wgpu::triangle::blit_shader"), label: Some("iced_wgpu::triangle::blit_shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(
include_str!("../shader/blit.wgsl"), include_str!("../shader/blit.wgsl"),
@ -93,22 +93,13 @@ impl Blit {
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
module: &shader, module: &shader,
entry_point: "fs_main", entry_point: "fs_main",
targets: &[wgpu::ColorTargetState { targets: &[Some(wgpu::ColorTargetState {
format, format,
blend: Some(wgpu::BlendState { blend: Some(
color: wgpu::BlendComponent { wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
src_factor: wgpu::BlendFactor::SrcAlpha, ),
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrites::ALL, write_mask: wgpu::ColorWrites::ALL,
}], })],
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList, topology: wgpu::PrimitiveTopology::TriangleList,
@ -178,14 +169,14 @@ impl Blit {
let mut render_pass = let mut render_pass =
encoder.begin_render_pass(&wgpu::RenderPassDescriptor { encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("iced_wgpu::triangle::msaa render pass"), label: Some("iced_wgpu::triangle::msaa render pass"),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target, view: target,
resolve_target: None, resolve_target: None,
ops: wgpu::Operations { ops: wgpu::Operations {
load: wgpu::LoadOp::Load, load: wgpu::LoadOp::Load,
store: true, store: true,
}, },
}], })],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });

View file

@ -1,6 +1,7 @@
use crate::{Backend, Color, Error, Renderer, Settings, Viewport}; use crate::{Backend, Color, Error, Renderer, Settings, Viewport};
use futures::task::SpawnExt; use futures::stream::{self, StreamExt};
use iced_graphics::compositor; use iced_graphics::compositor;
use iced_native::futures; use iced_native::futures;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
@ -16,7 +17,6 @@ pub struct Compositor<Theme> {
device: wgpu::Device, device: wgpu::Device,
queue: wgpu::Queue, queue: wgpu::Queue,
staging_belt: wgpu::util::StagingBelt, staging_belt: wgpu::util::StagingBelt,
local_pool: futures::executor::LocalPool,
format: wgpu::TextureFormat, format: wgpu::TextureFormat,
theme: PhantomData<Theme>, theme: PhantomData<Theme>,
} }
@ -62,38 +62,43 @@ impl<Theme> Compositor<Theme> {
log::info!("Selected: {:#?}", adapter.get_info()); log::info!("Selected: {:#?}", adapter.get_info());
let format = compatible_surface let format = compatible_surface.as_ref().and_then(|surface| {
.as_ref() surface.get_supported_formats(&adapter).first().copied()
.and_then(|surface| surface.get_preferred_format(&adapter))?; })?;
log::info!("Selected format: {:?}", format); log::info!("Selected format: {:?}", format);
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
let limits = wgpu::Limits::downlevel_webgl2_defaults() let limits = [wgpu::Limits::downlevel_webgl2_defaults()
.using_resolution(adapter.limits()); .using_resolution(adapter.limits())];
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
let limits = wgpu::Limits::downlevel_defaults(); let limits =
[wgpu::Limits::default(), wgpu::Limits::downlevel_defaults()];
let (device, queue) = adapter let limits = limits.into_iter().map(|limits| wgpu::Limits {
.request_device( max_bind_groups: 2,
&wgpu::DeviceDescriptor { ..limits
label: Some( });
"iced_wgpu::window::compositor device descriptor",
), let (device, queue) = stream::iter(limits)
features: wgpu::Features::empty(), .filter_map(|limits| async {
limits: wgpu::Limits { adapter.request_device(
max_bind_groups: 2, &wgpu::DeviceDescriptor {
..limits label: Some(
"iced_wgpu::window::compositor device descriptor",
),
features: wgpu::Features::empty(),
limits,
}, },
}, None,
None, ).await.ok()
) })
.await .boxed()
.ok()?; .next()
.await?;
let staging_belt = wgpu::util::StagingBelt::new(Self::CHUNK_SIZE); let staging_belt = wgpu::util::StagingBelt::new(Self::CHUNK_SIZE);
let local_pool = futures::executor::LocalPool::new();
Some(Compositor { Some(Compositor {
instance, instance,
@ -102,7 +107,6 @@ impl<Theme> Compositor<Theme> {
device, device,
queue, queue,
staging_belt, staging_belt,
local_pool,
format, format,
theme: PhantomData, theme: PhantomData,
}) })
@ -196,24 +200,26 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
label: Some( label: Some(
"iced_wgpu::window::Compositor render pass", "iced_wgpu::window::Compositor render pass",
), ),
color_attachments: &[wgpu::RenderPassColorAttachment { color_attachments: &[Some(
view, wgpu::RenderPassColorAttachment {
resolve_target: None, view,
ops: wgpu::Operations { resolve_target: None,
load: wgpu::LoadOp::Clear({ ops: wgpu::Operations {
let [r, g, b, a] = load: wgpu::LoadOp::Clear({
background_color.into_linear(); let [r, g, b, a] =
background_color.into_linear();
wgpu::Color { wgpu::Color {
r: f64::from(r), r: f64::from(r),
g: f64::from(g), g: f64::from(g),
b: f64::from(b), b: f64::from(b),
a: f64::from(a), a: f64::from(a),
} }
}), }),
store: true, store: true,
},
}, },
}], )],
depth_stencil_attachment: None, depth_stencil_attachment: None,
}); });
@ -231,16 +237,11 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
// Submit work // Submit work
self.staging_belt.finish(); self.staging_belt.finish();
self.queue.submit(Some(encoder.finish())); let _submission = self.queue.submit(Some(encoder.finish()));
frame.present(); frame.present();
// Recall staging buffers // Recall staging buffers
self.local_pool self.staging_belt.recall();
.spawner()
.spawn(self.staging_belt.recall())
.expect("Recall staging belt");
self.local_pool.run_until_stalled();
Ok(()) Ok(())
} }