Merge pull request #183 from hecrj/feature/wgpu-integration
Integration with existing `wgpu` projects
This commit is contained in:
commit
5d16f431b3
@ -40,6 +40,7 @@ members = [
|
||||
"examples/custom_widget",
|
||||
"examples/events",
|
||||
"examples/geometry",
|
||||
"examples/integration",
|
||||
"examples/pokedex",
|
||||
"examples/progress_bar",
|
||||
"examples/stopwatch",
|
||||
|
@ -74,6 +74,7 @@ A bunch of simpler examples exist:
|
||||
- [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle.
|
||||
- [`events`](events), a log of native events displayed using a conditional `Subscription`.
|
||||
- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu).
|
||||
- [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application.
|
||||
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
|
||||
- [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider.
|
||||
- [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time.
|
||||
|
11
examples/integration/Cargo.toml
Normal file
11
examples/integration/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "integration"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced_winit = { path = "../../winit" }
|
||||
iced_wgpu = { path = "../../wgpu" }
|
||||
env_logger = "0.7"
|
102
examples/integration/src/controls.rs
Normal file
102
examples/integration/src/controls.rs
Normal file
@ -0,0 +1,102 @@
|
||||
use crate::Scene;
|
||||
|
||||
use iced_wgpu::Renderer;
|
||||
use iced_winit::{
|
||||
slider, Align, Color, Column, Element, Length, Row, Slider, Text,
|
||||
};
|
||||
|
||||
pub struct Controls {
|
||||
sliders: [slider::State; 3],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Message {
|
||||
BackgroundColorChanged(Color),
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
pub fn new() -> Controls {
|
||||
Controls {
|
||||
sliders: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&self, message: Message, scene: &mut Scene) {
|
||||
match message {
|
||||
Message::BackgroundColorChanged(color) => {
|
||||
scene.background_color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view<'a>(
|
||||
&'a mut self,
|
||||
scene: &Scene,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
let [r, g, b] = &mut self.sliders;
|
||||
let background_color = scene.background_color;
|
||||
|
||||
let sliders = Row::new()
|
||||
.width(Length::Units(500))
|
||||
.spacing(20)
|
||||
.push(Slider::new(
|
||||
r,
|
||||
0.0..=1.0,
|
||||
scene.background_color.r,
|
||||
move |r| {
|
||||
Message::BackgroundColorChanged(Color {
|
||||
r,
|
||||
..background_color
|
||||
})
|
||||
},
|
||||
))
|
||||
.push(Slider::new(
|
||||
g,
|
||||
0.0..=1.0,
|
||||
scene.background_color.g,
|
||||
move |g| {
|
||||
Message::BackgroundColorChanged(Color {
|
||||
g,
|
||||
..background_color
|
||||
})
|
||||
},
|
||||
))
|
||||
.push(Slider::new(
|
||||
b,
|
||||
0.0..=1.0,
|
||||
scene.background_color.b,
|
||||
move |b| {
|
||||
Message::BackgroundColorChanged(Color {
|
||||
b,
|
||||
..background_color
|
||||
})
|
||||
},
|
||||
));
|
||||
|
||||
Row::new()
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_items(Align::End)
|
||||
.push(
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.align_items(Align::End)
|
||||
.push(
|
||||
Column::new()
|
||||
.padding(10)
|
||||
.spacing(10)
|
||||
.push(
|
||||
Text::new("Background color")
|
||||
.color(Color::WHITE),
|
||||
)
|
||||
.push(sliders)
|
||||
.push(
|
||||
Text::new(format!("{:?}", background_color))
|
||||
.size(14)
|
||||
.color(Color::WHITE),
|
||||
),
|
||||
),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
204
examples/integration/src/main.rs
Normal file
204
examples/integration/src/main.rs
Normal file
@ -0,0 +1,204 @@
|
||||
mod controls;
|
||||
mod scene;
|
||||
|
||||
use controls::Controls;
|
||||
use scene::Scene;
|
||||
|
||||
use iced_wgpu::{
|
||||
wgpu, window::SwapChain, Primitive, Renderer, Settings, Target,
|
||||
};
|
||||
use iced_winit::{winit, Cache, Clipboard, MouseCursor, Size, UserInterface};
|
||||
|
||||
use winit::{
|
||||
event::{DeviceEvent, Event, ModifiersState, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
env_logger::init();
|
||||
|
||||
// Initialize winit
|
||||
let event_loop = EventLoop::new();
|
||||
let window = winit::window::Window::new(&event_loop).unwrap();
|
||||
let mut logical_size =
|
||||
window.inner_size().to_logical(window.scale_factor());
|
||||
let mut modifiers = ModifiersState::default();
|
||||
|
||||
// Initialize WGPU
|
||||
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::Default,
|
||||
backends: wgpu::BackendBit::PRIMARY,
|
||||
})
|
||||
.expect("Request adapter");
|
||||
|
||||
let (mut device, mut queue) =
|
||||
adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
extensions: wgpu::Extensions {
|
||||
anisotropic_filtering: false,
|
||||
},
|
||||
limits: wgpu::Limits::default(),
|
||||
});
|
||||
|
||||
let surface = wgpu::Surface::create(&window);
|
||||
|
||||
let mut swap_chain = {
|
||||
let size = window.inner_size();
|
||||
|
||||
SwapChain::new(&device, &surface, size.width, size.height)
|
||||
};
|
||||
let mut resized = false;
|
||||
|
||||
// Initialize iced
|
||||
let mut events = Vec::new();
|
||||
let mut cache = Some(Cache::default());
|
||||
let mut renderer = Renderer::new(&mut device, Settings::default());
|
||||
let mut output = (Primitive::None, MouseCursor::OutOfBounds);
|
||||
let clipboard = Clipboard::new(&window);
|
||||
|
||||
// Initialize scene and GUI controls
|
||||
let mut scene = Scene::new(&device);
|
||||
let mut controls = Controls::new();
|
||||
|
||||
// Run event loop
|
||||
event_loop.run(move |event, _, control_flow| {
|
||||
// You should change this if you want to render continuosly
|
||||
*control_flow = ControlFlow::Wait;
|
||||
|
||||
match event {
|
||||
Event::DeviceEvent {
|
||||
event: DeviceEvent::ModifiersChanged(new_modifiers),
|
||||
..
|
||||
} => {
|
||||
modifiers = new_modifiers;
|
||||
}
|
||||
Event::WindowEvent { event, .. } => {
|
||||
match event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
logical_size =
|
||||
new_size.to_logical(window.scale_factor());
|
||||
resized = true;
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Map window event to iced event
|
||||
if let Some(event) = iced_winit::conversion::window_event(
|
||||
event,
|
||||
window.scale_factor(),
|
||||
modifiers,
|
||||
) {
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
Event::MainEventsCleared => {
|
||||
// If no relevant events happened, we can simply skip this
|
||||
if events.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to:
|
||||
// 1. Process events of our user interface.
|
||||
// 2. Update state as a result of any interaction.
|
||||
// 3. Generate a new output for our renderer.
|
||||
|
||||
// First, we build our user interface.
|
||||
let mut user_interface = UserInterface::build(
|
||||
controls.view(&scene),
|
||||
Size::new(logical_size.width, logical_size.height),
|
||||
cache.take().unwrap(),
|
||||
&mut renderer,
|
||||
);
|
||||
|
||||
// Then, we process the events, obtaining messages in return.
|
||||
let messages = user_interface.update(
|
||||
events.drain(..),
|
||||
clipboard.as_ref().map(|c| c as _),
|
||||
&renderer,
|
||||
);
|
||||
|
||||
let user_interface = if messages.is_empty() {
|
||||
// If there are no messages, no interactions we care about have
|
||||
// happened. We can simply leave our user interface as it is.
|
||||
user_interface
|
||||
} else {
|
||||
// If there are messages, we need to update our state
|
||||
// accordingly and rebuild our user interface.
|
||||
// We can only do this if we drop our user interface first
|
||||
// by turning it into its cache.
|
||||
cache = Some(user_interface.into_cache());
|
||||
|
||||
// In this example, `Controls` is the only part that cares
|
||||
// about messages, so updating our state is pretty
|
||||
// straightforward.
|
||||
for message in messages {
|
||||
controls.update(message, &mut scene);
|
||||
}
|
||||
|
||||
// Once the state has been changed, we rebuild our updated
|
||||
// user interface.
|
||||
UserInterface::build(
|
||||
controls.view(&scene),
|
||||
Size::new(logical_size.width, logical_size.height),
|
||||
cache.take().unwrap(),
|
||||
&mut renderer,
|
||||
)
|
||||
};
|
||||
|
||||
// Finally, we just need to draw a new output for our renderer,
|
||||
output = user_interface.draw(&mut renderer);
|
||||
|
||||
// update our cache,
|
||||
cache = Some(user_interface.into_cache());
|
||||
|
||||
// and request a redraw
|
||||
window.request_redraw();
|
||||
}
|
||||
Event::RedrawRequested(_) => {
|
||||
if resized {
|
||||
let size = window.inner_size();
|
||||
|
||||
swap_chain = SwapChain::new(
|
||||
&device,
|
||||
&surface,
|
||||
size.width,
|
||||
size.height,
|
||||
);
|
||||
}
|
||||
|
||||
let (frame, viewport) = swap_chain.next_frame();
|
||||
|
||||
let mut encoder = device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor { todo: 0 },
|
||||
);
|
||||
|
||||
// We draw the scene first
|
||||
scene.draw(&mut encoder, &frame.view);
|
||||
|
||||
// And then iced on top
|
||||
let mouse_cursor = renderer.draw(
|
||||
&mut device,
|
||||
&mut encoder,
|
||||
Target {
|
||||
texture: &frame.view,
|
||||
viewport,
|
||||
},
|
||||
&output,
|
||||
window.scale_factor(),
|
||||
&["Some debug information!"],
|
||||
);
|
||||
|
||||
// Then we submit the work
|
||||
queue.submit(&[encoder.finish()]);
|
||||
|
||||
// And update the mouse cursor
|
||||
window.set_cursor_icon(iced_winit::conversion::mouse_cursor(
|
||||
mouse_cursor,
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
119
examples/integration/src/scene.rs
Normal file
119
examples/integration/src/scene.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use iced_wgpu::wgpu;
|
||||
use iced_winit::Color;
|
||||
|
||||
pub struct Scene {
|
||||
pub background_color: Color,
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new(device: &wgpu::Device) -> Scene {
|
||||
let (pipeline, bind_group) = build_pipeline(device);
|
||||
|
||||
Scene {
|
||||
background_color: Color::BLACK,
|
||||
pipeline,
|
||||
bind_group,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&self,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: &wgpu::TextureView,
|
||||
) {
|
||||
let mut rpass =
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[
|
||||
wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: target,
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: {
|
||||
let [r, g, b, a] =
|
||||
self.background_color.into_linear();
|
||||
|
||||
wgpu::Color {
|
||||
r: r as f64,
|
||||
g: g as f64,
|
||||
b: b as f64,
|
||||
a: a as f64,
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
rpass.set_pipeline(&self.pipeline);
|
||||
rpass.set_bind_group(0, &self.bind_group, &[]);
|
||||
rpass.draw(0..3, 0..1);
|
||||
}
|
||||
}
|
||||
|
||||
fn build_pipeline(
|
||||
device: &wgpu::Device,
|
||||
) -> (wgpu::RenderPipeline, wgpu::BindGroup) {
|
||||
let vs = include_bytes!("shader/vert.spv");
|
||||
let fs = include_bytes!("shader/frag.spv");
|
||||
|
||||
let vs_module = device.create_shader_module(
|
||||
&wgpu::read_spirv(std::io::Cursor::new(&vs[..])).unwrap(),
|
||||
);
|
||||
|
||||
let fs_module = device.create_shader_module(
|
||||
&wgpu::read_spirv(std::io::Cursor::new(&fs[..])).unwrap(),
|
||||
);
|
||||
|
||||
let bind_group_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
bindings: &[],
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
bindings: &[],
|
||||
});
|
||||
|
||||
let pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
});
|
||||
|
||||
let pipeline =
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
layout: &pipeline_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::Ccw,
|
||||
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::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
depth_stencil_state: None,
|
||||
index_format: wgpu::IndexFormat::Uint16,
|
||||
vertex_buffers: &[],
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
});
|
||||
|
||||
(pipeline, bind_group)
|
||||
}
|
BIN
examples/integration/src/shader/frag.spv
Normal file
BIN
examples/integration/src/shader/frag.spv
Normal file
Binary file not shown.
BIN
examples/integration/src/shader/vert.spv
Normal file
BIN
examples/integration/src/shader/vert.spv
Normal file
Binary file not shown.
@ -162,7 +162,7 @@ where
|
||||
/// );
|
||||
///
|
||||
/// // Update the user interface
|
||||
/// let messages = user_interface.update(&renderer, None, events.drain(..));
|
||||
/// let messages = user_interface.update(events.drain(..), None, &renderer);
|
||||
///
|
||||
/// cache = user_interface.into_cache();
|
||||
///
|
||||
@ -174,9 +174,9 @@ where
|
||||
/// ```
|
||||
pub fn update(
|
||||
&mut self,
|
||||
renderer: &Renderer,
|
||||
clipboard: Option<&dyn Clipboard>,
|
||||
events: impl IntoIterator<Item = Event>,
|
||||
clipboard: Option<&dyn Clipboard>,
|
||||
renderer: &Renderer,
|
||||
) -> Vec<Message> {
|
||||
let mut messages = Vec::new();
|
||||
|
||||
@ -246,7 +246,7 @@ where
|
||||
/// &mut renderer,
|
||||
/// );
|
||||
///
|
||||
/// let messages = user_interface.update(&renderer, None, events.drain(..));
|
||||
/// let messages = user_interface.update(events.drain(..), None, &renderer);
|
||||
///
|
||||
/// // Draw the user interface
|
||||
/// let mouse_cursor = user_interface.draw(&mut renderer);
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Build window-based GUI applications.
|
||||
mod backend;
|
||||
mod event;
|
||||
mod renderer;
|
||||
|
||||
pub use backend::Backend;
|
||||
pub use event::Event;
|
||||
pub use renderer::{Renderer, Target};
|
||||
|
55
native/src/window/backend.rs
Normal file
55
native/src/window/backend.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::MouseCursor;
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
|
||||
/// A graphics backend that can render to windows.
|
||||
pub trait Backend: Sized {
|
||||
/// The settings of the backend.
|
||||
type Settings: Default;
|
||||
|
||||
/// The iced renderer of the backend.
|
||||
type Renderer: crate::Renderer;
|
||||
|
||||
/// The surface of the backend.
|
||||
type Surface;
|
||||
|
||||
/// The swap chain of the backend.
|
||||
type SwapChain;
|
||||
|
||||
/// Creates a new [`Backend`] and an associated iced renderer.
|
||||
///
|
||||
/// [`Backend`]: trait.Backend.html
|
||||
fn new(settings: Self::Settings) -> (Self, Self::Renderer);
|
||||
|
||||
/// Crates a new [`Surface`] for the given window.
|
||||
///
|
||||
/// [`Surface`]: #associatedtype.Surface
|
||||
fn create_surface<W: HasRawWindowHandle>(
|
||||
&mut self,
|
||||
window: &W,
|
||||
) -> Self::Surface;
|
||||
|
||||
/// Crates a new [`SwapChain`] for the given [`Surface`].
|
||||
///
|
||||
/// [`SwapChain`]: #associatedtype.SwapChain
|
||||
/// [`Surface`]: #associatedtype.Surface
|
||||
fn create_swap_chain(
|
||||
&mut self,
|
||||
surface: &Self::Surface,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Self::SwapChain;
|
||||
|
||||
/// Draws the output primitives to the next frame of the given [`SwapChain`].
|
||||
///
|
||||
/// [`SwapChain`]: #associatedtype.SwapChain
|
||||
/// [`Surface`]: #associatedtype.Surface
|
||||
fn draw<T: AsRef<str>>(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
swap_chain: &mut Self::SwapChain,
|
||||
output: &<Self::Renderer as crate::Renderer>::Output,
|
||||
scale_factor: f64,
|
||||
overlay: &[T],
|
||||
) -> MouseCursor;
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
use crate::MouseCursor;
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
|
||||
/// A renderer that can target windows.
|
||||
pub trait Renderer: crate::Renderer + Sized {
|
||||
/// The settings of the renderer.
|
||||
type Settings: Default;
|
||||
|
||||
/// The type of target.
|
||||
type Target: Target<Renderer = Self>;
|
||||
|
||||
/// Creates a new window [`Renderer`].
|
||||
///
|
||||
/// [`Renderer`]: trait.Renderer.html
|
||||
fn new(settings: Self::Settings) -> Self;
|
||||
|
||||
/// Performs the drawing operations described in the output on the given
|
||||
/// target.
|
||||
///
|
||||
/// The overlay can be a bunch of debug text logs. It should be rendered on
|
||||
/// top of the GUI on most scenarios.
|
||||
fn draw<T: AsRef<str>>(
|
||||
&mut self,
|
||||
output: &Self::Output,
|
||||
overlay: &[T],
|
||||
target: &mut Self::Target,
|
||||
) -> MouseCursor;
|
||||
}
|
||||
|
||||
/// A rendering target.
|
||||
pub trait Target {
|
||||
/// The renderer of this target.
|
||||
type Renderer;
|
||||
|
||||
/// Creates a new rendering [`Target`] from the given window handle, width,
|
||||
/// height and dpi factor.
|
||||
///
|
||||
/// [`Target`]: trait.Target.html
|
||||
fn new<W: HasRawWindowHandle>(
|
||||
window: &W,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f64,
|
||||
renderer: &Self::Renderer,
|
||||
) -> Self;
|
||||
|
||||
/// Resizes the current [`Target`].
|
||||
///
|
||||
/// [`Target`]: trait.Target.html
|
||||
fn resize(
|
||||
&mut self,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f64,
|
||||
renderer: &Self::Renderer,
|
||||
);
|
||||
}
|
@ -193,7 +193,7 @@ impl<A> iced_winit::Application for Instance<A>
|
||||
where
|
||||
A: Application,
|
||||
{
|
||||
type Renderer = iced_wgpu::Renderer;
|
||||
type Backend = iced_wgpu::window::Backend;
|
||||
type Executor = A::Executor;
|
||||
type Message = A::Message;
|
||||
|
||||
|
@ -27,19 +27,27 @@
|
||||
pub mod defaults;
|
||||
pub mod triangle;
|
||||
pub mod widget;
|
||||
pub mod window;
|
||||
|
||||
mod image;
|
||||
mod primitive;
|
||||
mod quad;
|
||||
mod renderer;
|
||||
mod settings;
|
||||
mod target;
|
||||
mod text;
|
||||
mod transformation;
|
||||
mod viewport;
|
||||
|
||||
pub use wgpu;
|
||||
|
||||
pub use defaults::Defaults;
|
||||
pub use primitive::Primitive;
|
||||
pub use renderer::{Renderer, Target};
|
||||
pub use renderer::Renderer;
|
||||
pub use settings::Settings;
|
||||
pub use target::Target;
|
||||
pub use viewport::Viewport;
|
||||
|
||||
#[doc(no_inline)]
|
||||
pub use widget::*;
|
||||
|
||||
|
@ -1,29 +1,20 @@
|
||||
use crate::{
|
||||
image, quad, text, triangle, Defaults, Image, Primitive, Quad, Settings,
|
||||
Transformation,
|
||||
Target, Transformation,
|
||||
};
|
||||
use iced_native::{
|
||||
layout, window, Background, Color, Layout, MouseCursor, Point, Rectangle,
|
||||
Vector, Widget,
|
||||
layout, Background, Color, Layout, MouseCursor, Point, Rectangle, Vector,
|
||||
Widget,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use wgpu::{
|
||||
Adapter, BackendBit, CommandEncoderDescriptor, Device, DeviceDescriptor,
|
||||
Extensions, Limits, PowerPreference, Queue, RequestAdapterOptions,
|
||||
};
|
||||
|
||||
mod target;
|
||||
mod widget;
|
||||
|
||||
pub use target::Target;
|
||||
|
||||
/// A [`wgpu`] renderer.
|
||||
///
|
||||
/// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs
|
||||
#[derive(Debug)]
|
||||
pub struct Renderer {
|
||||
device: Device,
|
||||
queue: Queue,
|
||||
quad_pipeline: quad::Pipeline,
|
||||
image_pipeline: image::Pipeline,
|
||||
text_pipeline: text::Pipeline,
|
||||
@ -53,29 +44,16 @@ impl<'a> Layer<'a> {
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
fn new(settings: Settings) -> Self {
|
||||
let adapter = Adapter::request(&RequestAdapterOptions {
|
||||
power_preference: PowerPreference::Default,
|
||||
backends: BackendBit::all(),
|
||||
})
|
||||
.expect("Request adapter");
|
||||
|
||||
let (mut device, queue) = adapter.request_device(&DeviceDescriptor {
|
||||
extensions: Extensions {
|
||||
anisotropic_filtering: false,
|
||||
},
|
||||
limits: Limits { max_bind_groups: 2 },
|
||||
});
|
||||
|
||||
let text_pipeline =
|
||||
text::Pipeline::new(&mut device, settings.default_font);
|
||||
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);
|
||||
/// Creates a new [`Renderer`].
|
||||
///
|
||||
/// [`Renderer`]: struct.Renderer.html
|
||||
pub fn new(device: &mut wgpu::Device, settings: Settings) -> Self {
|
||||
let text_pipeline = text::Pipeline::new(device, settings.default_font);
|
||||
let quad_pipeline = quad::Pipeline::new(device);
|
||||
let image_pipeline = crate::image::Pipeline::new(device);
|
||||
let triangle_pipeline = triangle::Pipeline::new(device);
|
||||
|
||||
Self {
|
||||
device,
|
||||
queue,
|
||||
quad_pipeline,
|
||||
image_pipeline,
|
||||
text_pipeline,
|
||||
@ -83,38 +61,26 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
fn draw<T: AsRef<str>>(
|
||||
/// Draws the provided primitives in the given [`Target`].
|
||||
///
|
||||
/// The text provided as overlay will be renderer on top of the primitives.
|
||||
/// This is useful for rendering debug information.
|
||||
///
|
||||
/// [`Target`]: struct.Target.html
|
||||
pub fn draw<T: AsRef<str>>(
|
||||
&mut self,
|
||||
device: &mut wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
target: Target<'_>,
|
||||
(primitive, mouse_cursor): &(Primitive, MouseCursor),
|
||||
scale_factor: f64,
|
||||
overlay: &[T],
|
||||
target: &mut Target,
|
||||
) -> MouseCursor {
|
||||
log::debug!("Drawing");
|
||||
|
||||
let (width, height) = target.dimensions();
|
||||
let scale_factor = target.scale_factor();
|
||||
let transformation = target.transformation();
|
||||
let frame = target.next_frame();
|
||||
|
||||
let mut encoder = self
|
||||
.device
|
||||
.create_command_encoder(&CommandEncoderDescriptor { todo: 0 });
|
||||
|
||||
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: &frame.view,
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: wgpu::Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
let (width, height) = target.viewport.dimensions();
|
||||
let scale_factor = scale_factor as f32;
|
||||
let transformation = target.viewport.transformation();
|
||||
|
||||
let mut layers = Vec::new();
|
||||
|
||||
@ -133,15 +99,15 @@ impl Renderer {
|
||||
|
||||
for layer in layers {
|
||||
self.flush(
|
||||
device,
|
||||
scale_factor,
|
||||
transformation,
|
||||
&layer,
|
||||
&mut encoder,
|
||||
&frame.view,
|
||||
encoder,
|
||||
target.texture,
|
||||
);
|
||||
}
|
||||
|
||||
self.queue.submit(&[encoder.finish()]);
|
||||
self.image_pipeline.trim_cache();
|
||||
|
||||
*mouse_cursor
|
||||
@ -336,6 +302,7 @@ impl Renderer {
|
||||
|
||||
fn flush(
|
||||
&mut self,
|
||||
device: &mut wgpu::Device,
|
||||
scale_factor: f32,
|
||||
transformation: Transformation,
|
||||
layer: &Layer<'_>,
|
||||
@ -352,7 +319,7 @@ impl Renderer {
|
||||
);
|
||||
|
||||
self.triangle_pipeline.draw(
|
||||
&mut self.device,
|
||||
device,
|
||||
encoder,
|
||||
target,
|
||||
translated,
|
||||
@ -364,7 +331,7 @@ impl Renderer {
|
||||
|
||||
if layer.quads.len() > 0 {
|
||||
self.quad_pipeline.draw(
|
||||
&mut self.device,
|
||||
device,
|
||||
encoder,
|
||||
&layer.quads,
|
||||
transformation,
|
||||
@ -383,7 +350,7 @@ impl Renderer {
|
||||
);
|
||||
|
||||
self.image_pipeline.draw(
|
||||
&mut self.device,
|
||||
device,
|
||||
encoder,
|
||||
&layer.images,
|
||||
translated_and_scaled,
|
||||
@ -429,7 +396,7 @@ impl Renderer {
|
||||
}
|
||||
|
||||
self.text_pipeline.draw_queued(
|
||||
&mut self.device,
|
||||
device,
|
||||
encoder,
|
||||
target,
|
||||
transformation,
|
||||
@ -461,24 +428,6 @@ impl iced_native::Renderer for Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
impl window::Renderer for Renderer {
|
||||
type Settings = Settings;
|
||||
type Target = Target;
|
||||
|
||||
fn new(settings: Settings) -> Self {
|
||||
Self::new(settings)
|
||||
}
|
||||
|
||||
fn draw<T: AsRef<str>>(
|
||||
&mut self,
|
||||
output: &Self::Output,
|
||||
overlay: &[T],
|
||||
target: &mut Target,
|
||||
) -> MouseCursor {
|
||||
self.draw(output, overlay, target)
|
||||
}
|
||||
}
|
||||
|
||||
impl layout::Debugger for Renderer {
|
||||
fn explain<Message>(
|
||||
&mut self,
|
||||
|
@ -1,91 +0,0 @@
|
||||
use crate::{Renderer, Transformation};
|
||||
use iced_native::window;
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
|
||||
/// A rendering target.
|
||||
#[derive(Debug)]
|
||||
pub struct Target {
|
||||
surface: wgpu::Surface,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f32,
|
||||
transformation: Transformation,
|
||||
swap_chain: wgpu::SwapChain,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub(crate) fn dimensions(&self) -> (u32, u32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub(crate) fn scale_factor(&self) -> f32 {
|
||||
self.scale_factor
|
||||
}
|
||||
|
||||
pub(crate) fn transformation(&self) -> Transformation {
|
||||
self.transformation
|
||||
}
|
||||
|
||||
pub(crate) fn next_frame(&mut self) -> wgpu::SwapChainOutput<'_> {
|
||||
self.swap_chain.get_next_texture()
|
||||
}
|
||||
}
|
||||
|
||||
impl window::Target for Target {
|
||||
type Renderer = Renderer;
|
||||
|
||||
fn new<W: HasRawWindowHandle>(
|
||||
window: &W,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f64,
|
||||
renderer: &Renderer,
|
||||
) -> Target {
|
||||
let surface = wgpu::Surface::create(window);
|
||||
let swap_chain =
|
||||
new_swap_chain(&surface, width, height, &renderer.device);
|
||||
|
||||
Target {
|
||||
surface,
|
||||
width,
|
||||
height,
|
||||
scale_factor: scale_factor as f32,
|
||||
transformation: Transformation::orthographic(width, height),
|
||||
swap_chain,
|
||||
}
|
||||
}
|
||||
|
||||
fn resize(
|
||||
&mut self,
|
||||
width: u32,
|
||||
height: u32,
|
||||
scale_factor: f64,
|
||||
renderer: &Renderer,
|
||||
) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
self.scale_factor = scale_factor as f32;
|
||||
self.transformation = Transformation::orthographic(width, height);
|
||||
self.swap_chain =
|
||||
new_swap_chain(&self.surface, width, height, &renderer.device);
|
||||
}
|
||||
}
|
||||
|
||||
fn new_swap_chain(
|
||||
surface: &wgpu::Surface,
|
||||
width: u32,
|
||||
height: u32,
|
||||
device: &wgpu::Device,
|
||||
) -> wgpu::SwapChain {
|
||||
device.create_swap_chain(
|
||||
&surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width,
|
||||
height,
|
||||
present_mode: wgpu::PresentMode::Vsync,
|
||||
},
|
||||
)
|
||||
}
|
14
wgpu/src/target.rs
Normal file
14
wgpu/src/target.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use crate::Viewport;
|
||||
|
||||
/// A rendering target.
|
||||
#[derive(Debug)]
|
||||
pub struct Target<'a> {
|
||||
/// The texture where graphics will be rendered.
|
||||
pub texture: &'a wgpu::TextureView,
|
||||
|
||||
/// The viewport of the target.
|
||||
///
|
||||
/// Most of the time, you will want this to match the dimensions of the
|
||||
/// texture.
|
||||
pub viewport: &'a Viewport,
|
||||
}
|
29
wgpu/src/viewport.rs
Normal file
29
wgpu/src/viewport.rs
Normal file
@ -0,0 +1,29 @@
|
||||
use crate::Transformation;
|
||||
|
||||
/// A viewing region for displaying computer graphics.
|
||||
#[derive(Debug)]
|
||||
pub struct Viewport {
|
||||
width: u32,
|
||||
height: u32,
|
||||
transformation: Transformation,
|
||||
}
|
||||
|
||||
impl Viewport {
|
||||
/// Creates a new [`Viewport`] with the given dimensions.
|
||||
pub fn new(width: u32, height: u32) -> Viewport {
|
||||
Viewport {
|
||||
width,
|
||||
height,
|
||||
transformation: Transformation::orthographic(width, height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the dimensions of the [`Viewport`].
|
||||
pub fn dimensions(&self) -> (u32, u32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub(crate) fn transformation(&self) -> Transformation {
|
||||
self.transformation
|
||||
}
|
||||
}
|
6
wgpu/src/window.rs
Normal file
6
wgpu/src/window.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! Display rendering results on windows.
|
||||
mod backend;
|
||||
mod swap_chain;
|
||||
|
||||
pub use backend::Backend;
|
||||
pub use swap_chain::SwapChain;
|
101
wgpu/src/window/backend.rs
Normal file
101
wgpu/src/window/backend.rs
Normal file
@ -0,0 +1,101 @@
|
||||
use crate::{window::SwapChain, Renderer, Settings, Target};
|
||||
|
||||
use iced_native::MouseCursor;
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
|
||||
/// A window graphics backend for iced powered by `wgpu`.
|
||||
#[derive(Debug)]
|
||||
pub struct Backend {
|
||||
device: wgpu::Device,
|
||||
queue: wgpu::Queue,
|
||||
}
|
||||
|
||||
impl iced_native::window::Backend for Backend {
|
||||
type Settings = Settings;
|
||||
type Renderer = Renderer;
|
||||
type Surface = wgpu::Surface;
|
||||
type SwapChain = SwapChain;
|
||||
|
||||
fn new(settings: Self::Settings) -> (Backend, Renderer) {
|
||||
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::Default,
|
||||
backends: wgpu::BackendBit::all(),
|
||||
})
|
||||
.expect("Request adapter");
|
||||
|
||||
let (mut device, queue) =
|
||||
adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
extensions: wgpu::Extensions {
|
||||
anisotropic_filtering: false,
|
||||
},
|
||||
limits: wgpu::Limits { max_bind_groups: 2 },
|
||||
});
|
||||
|
||||
let renderer = Renderer::new(&mut device, settings);
|
||||
|
||||
(Backend { device, queue }, renderer)
|
||||
}
|
||||
|
||||
fn create_surface<W: HasRawWindowHandle>(
|
||||
&mut self,
|
||||
window: &W,
|
||||
) -> wgpu::Surface {
|
||||
wgpu::Surface::create(window)
|
||||
}
|
||||
|
||||
fn create_swap_chain(
|
||||
&mut self,
|
||||
surface: &Self::Surface,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> SwapChain {
|
||||
SwapChain::new(&self.device, surface, width, height)
|
||||
}
|
||||
|
||||
fn draw<T: AsRef<str>>(
|
||||
&mut self,
|
||||
renderer: &mut Self::Renderer,
|
||||
swap_chain: &mut SwapChain,
|
||||
output: &<Self::Renderer as iced_native::Renderer>::Output,
|
||||
scale_factor: f64,
|
||||
overlay: &[T],
|
||||
) -> MouseCursor {
|
||||
let (frame, viewport) = swap_chain.next_frame();
|
||||
|
||||
let mut encoder = self.device.create_command_encoder(
|
||||
&wgpu::CommandEncoderDescriptor { todo: 0 },
|
||||
);
|
||||
|
||||
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: &frame.view,
|
||||
resolve_target: None,
|
||||
load_op: wgpu::LoadOp::Clear,
|
||||
store_op: wgpu::StoreOp::Store,
|
||||
clear_color: wgpu::Color {
|
||||
r: 1.0,
|
||||
g: 1.0,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
let mouse_cursor = renderer.draw(
|
||||
&mut self.device,
|
||||
&mut encoder,
|
||||
Target {
|
||||
texture: &frame.view,
|
||||
viewport,
|
||||
},
|
||||
output,
|
||||
scale_factor,
|
||||
overlay,
|
||||
);
|
||||
|
||||
self.queue.submit(&[encoder.finish()]);
|
||||
|
||||
mouse_cursor
|
||||
}
|
||||
}
|
55
wgpu/src/window/swap_chain.rs
Normal file
55
wgpu/src/window/swap_chain.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use crate::Viewport;
|
||||
|
||||
/// The rendering target of a window.
|
||||
///
|
||||
/// It represents a series of virtual framebuffers with a scale factor.
|
||||
#[derive(Debug)]
|
||||
pub struct SwapChain {
|
||||
raw: wgpu::SwapChain,
|
||||
viewport: Viewport,
|
||||
}
|
||||
|
||||
impl SwapChain {}
|
||||
|
||||
impl SwapChain {
|
||||
/// Creates a new [`SwapChain`] for the given surface.
|
||||
///
|
||||
/// [`SwapChain`]: struct.SwapChain.html
|
||||
pub fn new(
|
||||
device: &wgpu::Device,
|
||||
surface: &wgpu::Surface,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> SwapChain {
|
||||
SwapChain {
|
||||
raw: new_swap_chain(surface, width, height, device),
|
||||
viewport: Viewport::new(width, height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the next frame of the [`SwapChain`] alongside its [`Viewport`].
|
||||
///
|
||||
/// [`SwapChain`]: struct.SwapChain.html
|
||||
/// [`Viewport`]: ../struct.Viewport.html
|
||||
pub fn next_frame(&mut self) -> (wgpu::SwapChainOutput<'_>, &Viewport) {
|
||||
(self.raw.get_next_texture(), &self.viewport)
|
||||
}
|
||||
}
|
||||
|
||||
fn new_swap_chain(
|
||||
surface: &wgpu::Surface,
|
||||
width: u32,
|
||||
height: u32,
|
||||
device: &wgpu::Device,
|
||||
) -> wgpu::SwapChain {
|
||||
device.create_swap_chain(
|
||||
&surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
width,
|
||||
height,
|
||||
present_mode: wgpu::PresentMode::Vsync,
|
||||
},
|
||||
)
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
use crate::{
|
||||
conversion,
|
||||
input::{keyboard, mouse},
|
||||
window, Cache, Clipboard, Command, Debug, Element, Event, Executor, Mode,
|
||||
MouseCursor, Proxy, Runtime, Settings, Size, Subscription, UserInterface,
|
||||
conversion, size::Size, window, Cache, Clipboard, Command, Debug, Element,
|
||||
Executor, Mode, MouseCursor, Proxy, Runtime, Settings, Subscription,
|
||||
UserInterface,
|
||||
};
|
||||
|
||||
/// An interactive, native cross-platform application.
|
||||
@ -14,10 +13,10 @@ use crate::{
|
||||
/// An [`Application`](trait.Application.html) can execute asynchronous actions
|
||||
/// by returning a [`Command`](struct.Command.html) in some of its methods.
|
||||
pub trait Application: Sized {
|
||||
/// The renderer to use to draw the [`Application`].
|
||||
/// The graphics backend to use to draw the [`Application`].
|
||||
///
|
||||
/// [`Application`]: trait.Application.html
|
||||
type Renderer: window::Renderer;
|
||||
type Backend: window::Backend;
|
||||
|
||||
/// The [`Executor`] that will run commands and subscriptions.
|
||||
///
|
||||
@ -75,7 +74,9 @@ pub trait Application: Sized {
|
||||
/// These widgets can produce __messages__ based on user interaction.
|
||||
///
|
||||
/// [`Application`]: trait.Application.html
|
||||
fn view(&mut self) -> Element<'_, Self::Message, Self::Renderer>;
|
||||
fn view(
|
||||
&mut self,
|
||||
) -> Element<'_, Self::Message, <Self::Backend as window::Backend>::Renderer>;
|
||||
|
||||
/// Returns the current [`Application`] mode.
|
||||
///
|
||||
@ -99,11 +100,11 @@ pub trait Application: Sized {
|
||||
/// [`Application`]: trait.Application.html
|
||||
fn run(
|
||||
settings: Settings,
|
||||
renderer_settings: <Self::Renderer as window::Renderer>::Settings,
|
||||
backend_settings: <Self::Backend as window::Backend>::Settings,
|
||||
) where
|
||||
Self: 'static,
|
||||
{
|
||||
use window::{Renderer as _, Target as _};
|
||||
use window::Backend as _;
|
||||
use winit::{
|
||||
event::{self, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
@ -162,17 +163,17 @@ pub trait Application: Sized {
|
||||
let mut resized = false;
|
||||
|
||||
let clipboard = Clipboard::new(&window);
|
||||
let mut renderer = Self::Renderer::new(renderer_settings);
|
||||
let (mut backend, mut renderer) = Self::Backend::new(backend_settings);
|
||||
|
||||
let mut target = {
|
||||
let surface = backend.create_surface(&window);
|
||||
|
||||
let mut swap_chain = {
|
||||
let physical_size = size.physical();
|
||||
|
||||
<Self::Renderer as window::Renderer>::Target::new(
|
||||
&window,
|
||||
backend.create_swap_chain(
|
||||
&surface,
|
||||
physical_size.width,
|
||||
physical_size.height,
|
||||
size.scale_factor(),
|
||||
&renderer,
|
||||
)
|
||||
};
|
||||
|
||||
@ -198,8 +199,7 @@ pub trait Application: Sized {
|
||||
|
||||
event_loop.run(move |event, _, control_flow| match event {
|
||||
event::Event::MainEventsCleared => {
|
||||
if events.is_empty() && external_messages.is_empty() && !resized
|
||||
{
|
||||
if events.is_empty() && external_messages.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -223,11 +223,11 @@ pub trait Application: Sized {
|
||||
.for_each(|event| runtime.broadcast(event));
|
||||
|
||||
let mut messages = user_interface.update(
|
||||
&renderer,
|
||||
events.drain(..),
|
||||
clipboard
|
||||
.as_ref()
|
||||
.map(|c| c as &dyn iced_native::Clipboard),
|
||||
events.drain(..),
|
||||
&renderer,
|
||||
);
|
||||
messages.extend(external_messages.drain(..));
|
||||
debug.event_processing_finished();
|
||||
@ -306,18 +306,22 @@ pub trait Application: Sized {
|
||||
if resized {
|
||||
let physical_size = size.physical();
|
||||
|
||||
target.resize(
|
||||
swap_chain = backend.create_swap_chain(
|
||||
&surface,
|
||||
physical_size.width,
|
||||
physical_size.height,
|
||||
size.scale_factor(),
|
||||
&renderer,
|
||||
);
|
||||
|
||||
resized = false;
|
||||
}
|
||||
|
||||
let new_mouse_cursor =
|
||||
renderer.draw(&primitive, &debug.overlay(), &mut target);
|
||||
let new_mouse_cursor = backend.draw(
|
||||
&mut renderer,
|
||||
&mut swap_chain,
|
||||
&primitive,
|
||||
size.scale_factor(),
|
||||
&debug.overlay(),
|
||||
);
|
||||
|
||||
debug.render_finished();
|
||||
|
||||
@ -335,106 +339,37 @@ pub trait Application: Sized {
|
||||
event::Event::WindowEvent {
|
||||
event: window_event,
|
||||
..
|
||||
} => match window_event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
size = Size::new(new_size, size.scale_factor());
|
||||
|
||||
events.push(Event::Window(window::Event::Resized {
|
||||
width: size.logical().width.round() as u32,
|
||||
height: size.logical().height.round() as u32,
|
||||
}));
|
||||
|
||||
resized = true;
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let position =
|
||||
position.to_logical::<f64>(size.scale_factor());
|
||||
|
||||
events.push(Event::Mouse(mouse::Event::CursorMoved {
|
||||
x: position.x as f32,
|
||||
y: position.y as f32,
|
||||
}));
|
||||
}
|
||||
WindowEvent::MouseInput { button, state, .. } => {
|
||||
events.push(Event::Mouse(mouse::Event::Input {
|
||||
button: conversion::mouse_button(button),
|
||||
state: conversion::button_state(state),
|
||||
}));
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(
|
||||
delta_x,
|
||||
delta_y,
|
||||
) => {
|
||||
events.push(Event::Mouse(
|
||||
mouse::Event::WheelScrolled {
|
||||
delta: mouse::ScrollDelta::Lines {
|
||||
x: delta_x,
|
||||
y: delta_y,
|
||||
},
|
||||
} => {
|
||||
match window_event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
size = Size::new(new_size, window.scale_factor());
|
||||
resized = true;
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
*control_flow = ControlFlow::Exit;
|
||||
}
|
||||
#[cfg(feature = "debug")]
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::event::KeyboardInput {
|
||||
virtual_keycode:
|
||||
Some(winit::event::VirtualKeyCode::F12),
|
||||
state: winit::event::ElementState::Pressed,
|
||||
..
|
||||
},
|
||||
));
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(position) => {
|
||||
events.push(Event::Mouse(
|
||||
mouse::Event::WheelScrolled {
|
||||
delta: mouse::ScrollDelta::Pixels {
|
||||
x: position.x as f32,
|
||||
y: position.y as f32,
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
},
|
||||
WindowEvent::ReceivedCharacter(c)
|
||||
if !is_private_use_character(c) =>
|
||||
{
|
||||
events.push(Event::Keyboard(
|
||||
keyboard::Event::CharacterReceived(c),
|
||||
));
|
||||
..
|
||||
} => debug.toggle(),
|
||||
_ => {}
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::event::KeyboardInput {
|
||||
virtual_keycode: Some(virtual_keycode),
|
||||
state,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => {
|
||||
match (virtual_keycode, state) {
|
||||
(
|
||||
winit::event::VirtualKeyCode::F12,
|
||||
winit::event::ElementState::Pressed,
|
||||
) => debug.toggle(),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
events.push(Event::Keyboard(keyboard::Event::Input {
|
||||
key_code: conversion::key_code(virtual_keycode),
|
||||
state: conversion::button_state(state),
|
||||
modifiers: conversion::modifiers_state(modifiers),
|
||||
}));
|
||||
if let Some(event) = conversion::window_event(
|
||||
window_event,
|
||||
size.scale_factor(),
|
||||
modifiers,
|
||||
) {
|
||||
events.push(event);
|
||||
}
|
||||
WindowEvent::HoveredFile(path) => {
|
||||
events
|
||||
.push(Event::Window(window::Event::FileHovered(path)));
|
||||
}
|
||||
WindowEvent::DroppedFile(path) => {
|
||||
events
|
||||
.push(Event::Window(window::Event::FileDropped(path)));
|
||||
}
|
||||
WindowEvent::HoveredFileCancelled => {
|
||||
events.push(Event::Window(window::Event::FilesHoveredLeft));
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
size = Size::new(size.physical(), scale_factor);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
event::Event::DeviceEvent {
|
||||
event: event::DeviceEvent::ModifiersChanged(new_modifiers),
|
||||
..
|
||||
@ -451,10 +386,10 @@ pub trait Application: Sized {
|
||||
fn build_user_interface<'a, A: Application>(
|
||||
application: &'a mut A,
|
||||
cache: Cache,
|
||||
renderer: &mut A::Renderer,
|
||||
renderer: &mut <A::Backend as window::Backend>::Renderer,
|
||||
size: winit::dpi::LogicalSize<f64>,
|
||||
debug: &mut Debug,
|
||||
) -> UserInterface<'a, A::Message, A::Renderer> {
|
||||
) -> UserInterface<'a, A::Message, <A::Backend as window::Backend>::Renderer> {
|
||||
debug.view_started();
|
||||
let view = application.view();
|
||||
debug.view_finished();
|
||||
@ -473,13 +408,3 @@ fn build_user_interface<'a, A: Application>(
|
||||
|
||||
user_interface
|
||||
}
|
||||
|
||||
// As defined in: http://www.unicode.org/faq/private_use.html
|
||||
fn is_private_use_character(c: char) -> bool {
|
||||
match c {
|
||||
'\u{E000}'..='\u{F8FF}'
|
||||
| '\u{F0000}'..='\u{FFFFD}'
|
||||
| '\u{100000}'..='\u{10FFFD}' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,90 @@
|
||||
//! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native
|
||||
use crate::{
|
||||
input::{
|
||||
keyboard::{KeyCode, ModifiersState},
|
||||
keyboard::{self, KeyCode, ModifiersState},
|
||||
mouse, ButtonState,
|
||||
},
|
||||
Mode, MouseCursor,
|
||||
window, Event, Mode, MouseCursor,
|
||||
};
|
||||
|
||||
/// Converts a winit window event into an iced event.
|
||||
pub fn window_event(
|
||||
event: winit::event::WindowEvent<'_>,
|
||||
scale_factor: f64,
|
||||
modifiers: winit::event::ModifiersState,
|
||||
) -> Option<Event> {
|
||||
use winit::event::WindowEvent;
|
||||
|
||||
match event {
|
||||
WindowEvent::Resized(new_size) => {
|
||||
let logical_size = new_size.to_logical(scale_factor);
|
||||
|
||||
Some(Event::Window(window::Event::Resized {
|
||||
width: logical_size.width,
|
||||
height: logical_size.height,
|
||||
}))
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let position = position.to_logical::<f64>(scale_factor);
|
||||
|
||||
Some(Event::Mouse(mouse::Event::CursorMoved {
|
||||
x: position.x as f32,
|
||||
y: position.y as f32,
|
||||
}))
|
||||
}
|
||||
WindowEvent::MouseInput { button, state, .. } => {
|
||||
Some(Event::Mouse(mouse::Event::Input {
|
||||
button: mouse_button(button),
|
||||
state: button_state(state),
|
||||
}))
|
||||
}
|
||||
WindowEvent::MouseWheel { delta, .. } => match delta {
|
||||
winit::event::MouseScrollDelta::LineDelta(delta_x, delta_y) => {
|
||||
Some(Event::Mouse(mouse::Event::WheelScrolled {
|
||||
delta: mouse::ScrollDelta::Lines {
|
||||
x: delta_x,
|
||||
y: delta_y,
|
||||
},
|
||||
}))
|
||||
}
|
||||
winit::event::MouseScrollDelta::PixelDelta(position) => {
|
||||
Some(Event::Mouse(mouse::Event::WheelScrolled {
|
||||
delta: mouse::ScrollDelta::Pixels {
|
||||
x: position.x as f32,
|
||||
y: position.y as f32,
|
||||
},
|
||||
}))
|
||||
}
|
||||
},
|
||||
WindowEvent::ReceivedCharacter(c) if !is_private_use_character(c) => {
|
||||
Some(Event::Keyboard(keyboard::Event::CharacterReceived(c)))
|
||||
}
|
||||
WindowEvent::KeyboardInput {
|
||||
input:
|
||||
winit::event::KeyboardInput {
|
||||
virtual_keycode: Some(virtual_keycode),
|
||||
state,
|
||||
..
|
||||
},
|
||||
..
|
||||
} => Some(Event::Keyboard(keyboard::Event::Input {
|
||||
key_code: key_code(virtual_keycode),
|
||||
state: button_state(state),
|
||||
modifiers: modifiers_state(modifiers),
|
||||
})),
|
||||
WindowEvent::HoveredFile(path) => {
|
||||
Some(Event::Window(window::Event::FileHovered(path)))
|
||||
}
|
||||
WindowEvent::DroppedFile(path) => {
|
||||
Some(Event::Window(window::Event::FileDropped(path)))
|
||||
}
|
||||
WindowEvent::HoveredFileCancelled => {
|
||||
Some(Event::Window(window::Event::FilesHoveredLeft))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a [`Mode`] to a [`winit`] fullscreen mode.
|
||||
///
|
||||
/// [`Mode`]:
|
||||
@ -254,3 +332,13 @@ pub fn key_code(virtual_keycode: winit::event::VirtualKeyCode) -> KeyCode {
|
||||
winit::event::VirtualKeyCode::Cut => KeyCode::Cut,
|
||||
}
|
||||
}
|
||||
|
||||
// As defined in: http://www.unicode.org/faq/private_use.html
|
||||
pub(crate) fn is_private_use_character(c: char) -> bool {
|
||||
match c {
|
||||
'\u{E000}'..='\u{F8FF}'
|
||||
| '\u{F0000}'..='\u{FFFFD}'
|
||||
| '\u{100000}'..='\u{10FFFD}' => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ impl Debug {
|
||||
Self
|
||||
}
|
||||
|
||||
pub fn toggle(&mut self) {}
|
||||
|
||||
pub fn startup_started(&mut self) {}
|
||||
|
||||
pub fn startup_finished(&mut self) {}
|
||||
|
@ -50,4 +50,3 @@ pub use settings::Settings;
|
||||
|
||||
use debug::Debug;
|
||||
use proxy::Proxy;
|
||||
use size::Size;
|
||||
|
Loading…
Reference in New Issue
Block a user