Skip to content

Commit 5d16f43

Browse files
authored
Merge pull request #183 from hecrj/feature/wgpu-integration
Integration with existing `wgpu` projects
2 parents 95880ca + 4337dad commit 5d16f43

File tree

25 files changed

+893
-377
lines changed

25 files changed

+893
-377
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ members = [
4040
"examples/custom_widget",
4141
"examples/events",
4242
"examples/geometry",
43+
"examples/integration",
4344
"examples/pokedex",
4445
"examples/progress_bar",
4546
"examples/stopwatch",

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ A bunch of simpler examples exist:
7474
- [`custom_widget`](custom_widget), a demonstration of how to build a custom widget that draws a circle.
7575
- [`events`](events), a log of native events displayed using a conditional `Subscription`.
7676
- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu).
77+
- [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application.
7778
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
7879
- [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider.
7980
- [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time.

examples/integration/Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "integration"
3+
version = "0.1.0"
4+
authors = ["Héctor Ramón Jiménez <[email protected]>"]
5+
edition = "2018"
6+
publish = false
7+
8+
[dependencies]
9+
iced_winit = { path = "../../winit" }
10+
iced_wgpu = { path = "../../wgpu" }
11+
env_logger = "0.7"

examples/integration/src/controls.rs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use crate::Scene;
2+
3+
use iced_wgpu::Renderer;
4+
use iced_winit::{
5+
slider, Align, Color, Column, Element, Length, Row, Slider, Text,
6+
};
7+
8+
pub struct Controls {
9+
sliders: [slider::State; 3],
10+
}
11+
12+
#[derive(Debug)]
13+
pub enum Message {
14+
BackgroundColorChanged(Color),
15+
}
16+
17+
impl Controls {
18+
pub fn new() -> Controls {
19+
Controls {
20+
sliders: Default::default(),
21+
}
22+
}
23+
24+
pub fn update(&self, message: Message, scene: &mut Scene) {
25+
match message {
26+
Message::BackgroundColorChanged(color) => {
27+
scene.background_color = color;
28+
}
29+
}
30+
}
31+
32+
pub fn view<'a>(
33+
&'a mut self,
34+
scene: &Scene,
35+
) -> Element<'a, Message, Renderer> {
36+
let [r, g, b] = &mut self.sliders;
37+
let background_color = scene.background_color;
38+
39+
let sliders = Row::new()
40+
.width(Length::Units(500))
41+
.spacing(20)
42+
.push(Slider::new(
43+
r,
44+
0.0..=1.0,
45+
scene.background_color.r,
46+
move |r| {
47+
Message::BackgroundColorChanged(Color {
48+
r,
49+
..background_color
50+
})
51+
},
52+
))
53+
.push(Slider::new(
54+
g,
55+
0.0..=1.0,
56+
scene.background_color.g,
57+
move |g| {
58+
Message::BackgroundColorChanged(Color {
59+
g,
60+
..background_color
61+
})
62+
},
63+
))
64+
.push(Slider::new(
65+
b,
66+
0.0..=1.0,
67+
scene.background_color.b,
68+
move |b| {
69+
Message::BackgroundColorChanged(Color {
70+
b,
71+
..background_color
72+
})
73+
},
74+
));
75+
76+
Row::new()
77+
.width(Length::Fill)
78+
.height(Length::Fill)
79+
.align_items(Align::End)
80+
.push(
81+
Column::new()
82+
.width(Length::Fill)
83+
.align_items(Align::End)
84+
.push(
85+
Column::new()
86+
.padding(10)
87+
.spacing(10)
88+
.push(
89+
Text::new("Background color")
90+
.color(Color::WHITE),
91+
)
92+
.push(sliders)
93+
.push(
94+
Text::new(format!("{:?}", background_color))
95+
.size(14)
96+
.color(Color::WHITE),
97+
),
98+
),
99+
)
100+
.into()
101+
}
102+
}

examples/integration/src/main.rs

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
mod controls;
2+
mod scene;
3+
4+
use controls::Controls;
5+
use scene::Scene;
6+
7+
use iced_wgpu::{
8+
wgpu, window::SwapChain, Primitive, Renderer, Settings, Target,
9+
};
10+
use iced_winit::{winit, Cache, Clipboard, MouseCursor, Size, UserInterface};
11+
12+
use winit::{
13+
event::{DeviceEvent, Event, ModifiersState, WindowEvent},
14+
event_loop::{ControlFlow, EventLoop},
15+
};
16+
17+
pub fn main() {
18+
env_logger::init();
19+
20+
// Initialize winit
21+
let event_loop = EventLoop::new();
22+
let window = winit::window::Window::new(&event_loop).unwrap();
23+
let mut logical_size =
24+
window.inner_size().to_logical(window.scale_factor());
25+
let mut modifiers = ModifiersState::default();
26+
27+
// Initialize WGPU
28+
let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {
29+
power_preference: wgpu::PowerPreference::Default,
30+
backends: wgpu::BackendBit::PRIMARY,
31+
})
32+
.expect("Request adapter");
33+
34+
let (mut device, mut queue) =
35+
adapter.request_device(&wgpu::DeviceDescriptor {
36+
extensions: wgpu::Extensions {
37+
anisotropic_filtering: false,
38+
},
39+
limits: wgpu::Limits::default(),
40+
});
41+
42+
let surface = wgpu::Surface::create(&window);
43+
44+
let mut swap_chain = {
45+
let size = window.inner_size();
46+
47+
SwapChain::new(&device, &surface, size.width, size.height)
48+
};
49+
let mut resized = false;
50+
51+
// Initialize iced
52+
let mut events = Vec::new();
53+
let mut cache = Some(Cache::default());
54+
let mut renderer = Renderer::new(&mut device, Settings::default());
55+
let mut output = (Primitive::None, MouseCursor::OutOfBounds);
56+
let clipboard = Clipboard::new(&window);
57+
58+
// Initialize scene and GUI controls
59+
let mut scene = Scene::new(&device);
60+
let mut controls = Controls::new();
61+
62+
// Run event loop
63+
event_loop.run(move |event, _, control_flow| {
64+
// You should change this if you want to render continuosly
65+
*control_flow = ControlFlow::Wait;
66+
67+
match event {
68+
Event::DeviceEvent {
69+
event: DeviceEvent::ModifiersChanged(new_modifiers),
70+
..
71+
} => {
72+
modifiers = new_modifiers;
73+
}
74+
Event::WindowEvent { event, .. } => {
75+
match event {
76+
WindowEvent::Resized(new_size) => {
77+
logical_size =
78+
new_size.to_logical(window.scale_factor());
79+
resized = true;
80+
}
81+
WindowEvent::CloseRequested => {
82+
*control_flow = ControlFlow::Exit;
83+
}
84+
_ => {}
85+
}
86+
87+
// Map window event to iced event
88+
if let Some(event) = iced_winit::conversion::window_event(
89+
event,
90+
window.scale_factor(),
91+
modifiers,
92+
) {
93+
events.push(event);
94+
}
95+
}
96+
Event::MainEventsCleared => {
97+
// If no relevant events happened, we can simply skip this
98+
if events.is_empty() {
99+
return;
100+
}
101+
102+
// We need to:
103+
// 1. Process events of our user interface.
104+
// 2. Update state as a result of any interaction.
105+
// 3. Generate a new output for our renderer.
106+
107+
// First, we build our user interface.
108+
let mut user_interface = UserInterface::build(
109+
controls.view(&scene),
110+
Size::new(logical_size.width, logical_size.height),
111+
cache.take().unwrap(),
112+
&mut renderer,
113+
);
114+
115+
// Then, we process the events, obtaining messages in return.
116+
let messages = user_interface.update(
117+
events.drain(..),
118+
clipboard.as_ref().map(|c| c as _),
119+
&renderer,
120+
);
121+
122+
let user_interface = if messages.is_empty() {
123+
// If there are no messages, no interactions we care about have
124+
// happened. We can simply leave our user interface as it is.
125+
user_interface
126+
} else {
127+
// If there are messages, we need to update our state
128+
// accordingly and rebuild our user interface.
129+
// We can only do this if we drop our user interface first
130+
// by turning it into its cache.
131+
cache = Some(user_interface.into_cache());
132+
133+
// In this example, `Controls` is the only part that cares
134+
// about messages, so updating our state is pretty
135+
// straightforward.
136+
for message in messages {
137+
controls.update(message, &mut scene);
138+
}
139+
140+
// Once the state has been changed, we rebuild our updated
141+
// user interface.
142+
UserInterface::build(
143+
controls.view(&scene),
144+
Size::new(logical_size.width, logical_size.height),
145+
cache.take().unwrap(),
146+
&mut renderer,
147+
)
148+
};
149+
150+
// Finally, we just need to draw a new output for our renderer,
151+
output = user_interface.draw(&mut renderer);
152+
153+
// update our cache,
154+
cache = Some(user_interface.into_cache());
155+
156+
// and request a redraw
157+
window.request_redraw();
158+
}
159+
Event::RedrawRequested(_) => {
160+
if resized {
161+
let size = window.inner_size();
162+
163+
swap_chain = SwapChain::new(
164+
&device,
165+
&surface,
166+
size.width,
167+
size.height,
168+
);
169+
}
170+
171+
let (frame, viewport) = swap_chain.next_frame();
172+
173+
let mut encoder = device.create_command_encoder(
174+
&wgpu::CommandEncoderDescriptor { todo: 0 },
175+
);
176+
177+
// We draw the scene first
178+
scene.draw(&mut encoder, &frame.view);
179+
180+
// And then iced on top
181+
let mouse_cursor = renderer.draw(
182+
&mut device,
183+
&mut encoder,
184+
Target {
185+
texture: &frame.view,
186+
viewport,
187+
},
188+
&output,
189+
window.scale_factor(),
190+
&["Some debug information!"],
191+
);
192+
193+
// Then we submit the work
194+
queue.submit(&[encoder.finish()]);
195+
196+
// And update the mouse cursor
197+
window.set_cursor_icon(iced_winit::conversion::mouse_cursor(
198+
mouse_cursor,
199+
));
200+
}
201+
_ => {}
202+
}
203+
})
204+
}

0 commit comments

Comments
 (0)