Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with existing wgpu projects #183

Merged
merged 12 commits into from
Feb 10, 2020
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ members = [
"examples/custom_widget",
"examples/events",
"examples/geometry",
"examples/integration",
"examples/pokedex",
"examples/progress_bar",
"examples/stopwatch",
Expand Down
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
11 changes: 11 additions & 0 deletions examples/integration/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 changes: 102 additions & 0 deletions examples/integration/src/controls.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 204 additions & 0 deletions examples/integration/src/main.rs
Original file line number Diff line number Diff line change
@@ -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,
));
}
_ => {}
}
})
}
Loading