Add integration example
It showcases how to integrate iced in an existing graphical application.
This commit is contained in:
parent
a244f93243
commit
4d7979aa77
@ -40,6 +40,7 @@ members = [
|
|||||||
"examples/custom_widget",
|
"examples/custom_widget",
|
||||||
"examples/events",
|
"examples/events",
|
||||||
"examples/geometry",
|
"examples/geometry",
|
||||||
|
"examples/integration",
|
||||||
"examples/pokedex",
|
"examples/pokedex",
|
||||||
"examples/progress_bar",
|
"examples/progress_bar",
|
||||||
"examples/stopwatch",
|
"examples/stopwatch",
|
||||||
|
|||||||
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.
Loading…
x
Reference in New Issue
Block a user