diff options
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | examples/README.md | 4 | ||||
-rw-r--r-- | examples/integration_opengl/Cargo.toml | 13 | ||||
-rw-r--r-- | examples/integration_opengl/README.md | 16 | ||||
-rw-r--r-- | examples/integration_opengl/src/controls.rs | 110 | ||||
-rw-r--r-- | examples/integration_opengl/src/main.rs | 181 | ||||
-rw-r--r-- | examples/integration_opengl/src/scene.rs | 99 | ||||
-rw-r--r-- | examples/integration_wgpu/Cargo.toml (renamed from examples/integration/Cargo.toml) | 2 | ||||
-rw-r--r-- | examples/integration_wgpu/README.md (renamed from examples/integration/README.md) | 5 | ||||
-rw-r--r-- | examples/integration_wgpu/src/controls.rs (renamed from examples/integration/src/controls.rs) | 0 | ||||
-rw-r--r-- | examples/integration_wgpu/src/main.rs (renamed from examples/integration/src/main.rs) | 0 | ||||
-rw-r--r-- | examples/integration_wgpu/src/scene.rs (renamed from examples/integration/src/scene.rs) | 0 | ||||
-rw-r--r-- | examples/integration_wgpu/src/shader/frag.spv (renamed from examples/integration/src/shader/frag.spv) | bin | 352 -> 352 bytes | |||
-rw-r--r-- | examples/integration_wgpu/src/shader/vert.spv (renamed from examples/integration/src/shader/vert.spv) | bin | 904 -> 904 bytes | |||
-rw-r--r-- | glow/src/lib.rs | 4 |
15 files changed, 432 insertions, 9 deletions
@@ -70,7 +70,9 @@ members = [ "examples/events", "examples/game_of_life", "examples/geometry", - "examples/integration", + "examples/integration_opengl", + "examples/integration_wgpu", + "examples/menu", "examples/pane_grid", "examples/pick_list", "examples/pokedex", @@ -82,10 +84,9 @@ members = [ "examples/styling", "examples/svg", "examples/todos", - "examples/tour", "examples/tooltip", + "examples/tour", "examples/url_handler", - "examples/menu", ] [dependencies] diff --git a/examples/README.md b/examples/README.md index 10c28cf5..1b9f4b56 100644 --- a/examples/README.md +++ b/examples/README.md @@ -98,7 +98,8 @@ A bunch of simpler examples exist: - [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress. - [`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. +- [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application. +- [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application. - [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized. - [`pick_list`](pick_list), a dropdown list of selectable options. - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. @@ -116,6 +117,7 @@ cargo run --package <example> [`lyon`]: https://github.com/nical/lyon [PokéAPI]: https://pokeapi.co/ [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg +[`wgpu`]: https://github.com/gfx-rs/wgpu ## [Coffee] Since [Iced was born in May 2019], it has been powering the user interfaces in diff --git a/examples/integration_opengl/Cargo.toml b/examples/integration_opengl/Cargo.toml new file mode 100644 index 00000000..0917f2ec --- /dev/null +++ b/examples/integration_opengl/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "integration_opengl" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2018" +publish = false + +[dependencies] +iced_glutin = { path = "../../glutin" } +iced_glow = { path = "../../glow" } +iced_winit = { path = "../../winit" } +env_logger = "0.8" +glow = "0.6" diff --git a/examples/integration_opengl/README.md b/examples/integration_opengl/README.md new file mode 100644 index 00000000..08cce7a6 --- /dev/null +++ b/examples/integration_opengl/README.md @@ -0,0 +1,16 @@ +## OpenGL integration + +A demonstration of how to integrate Iced in an existing graphical OpenGL application. + +The __[`main`]__ file contains all the code of the example. + +<div align="center"> + <a href="https://imgbox.com/9P9ETcod" target="_blank"><img src="https://images2.imgbox.com/2a/51/9P9ETcod_o.gif" alt="image host"/></a> +</div> + +You can run it with `cargo run`: +``` +cargo run --package integration_gl +``` + +[`main`]: src/main.rs diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs new file mode 100644 index 00000000..13b7fbc2 --- /dev/null +++ b/examples/integration_opengl/src/controls.rs @@ -0,0 +1,110 @@ +use iced_glow::Renderer; +use iced_glutin::{ + slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, + Row, Slider, Text, +}; + +pub struct Controls { + background_color: Color, + sliders: [slider::State; 3], +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + sliders: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + type Clipboard = Clipboard; + + fn update( + &mut self, + message: Message, + _clipboard: &mut Clipboard, + ) -> Command<Message> { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + } + + Command::none() + } + + fn view(&mut self) -> Element<Message, Renderer> { + let [r, g, b] = &mut self.sliders; + let background_color = self.background_color; + + let sliders = Row::new() + .width(Length::Units(500)) + .spacing(20) + .push( + Slider::new(r, 0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(g, 0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(b, 0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }) + .step(0.01), + ); + + 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() + } +} diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs new file mode 100644 index 00000000..f80915d2 --- /dev/null +++ b/examples/integration_opengl/src/main.rs @@ -0,0 +1,181 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use glow; +use glow::*; +use iced_glow::{Backend, Renderer, Settings, Viewport}; +use iced_glutin::glutin; +use iced_glutin::glutin::event::{Event, WindowEvent}; +use iced_glutin::glutin::event_loop::ControlFlow; +use iced_glutin::{program, Clipboard, Debug, Size}; +use iced_winit::conversion; +use iced_winit::winit; +use winit::{dpi::PhysicalPosition, event::ModifiersState}; + +pub fn main() { + env_logger::init(); + let (gl, event_loop, windowed_context, shader_version) = { + let el = glutin::event_loop::EventLoop::new(); + + let wb = glutin::window::WindowBuilder::new() + .with_title("OpenGL integration example") + .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0)); + + let windowed_context = glutin::ContextBuilder::new() + .with_vsync(true) + .build_windowed(wb, &el) + .unwrap(); + + unsafe { + let windowed_context = windowed_context.make_current().unwrap(); + + let gl = glow::Context::from_loader_function(|s| { + windowed_context.get_proc_address(s) as *const _ + }); + + // Enable auto-conversion from/to sRGB + gl.enable(glow::FRAMEBUFFER_SRGB); + + // Enable alpha blending + gl.enable(glow::BLEND); + gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA); + + // Disable multisampling by default + gl.disable(glow::MULTISAMPLE); + + (gl, el, windowed_context, "#version 410") + } + }; + + let physical_size = windowed_context.window().inner_size(); + let mut viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + windowed_context.window().scale_factor(), + ); + + let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut modifiers = ModifiersState::default(); + let mut clipboard = Clipboard::connect(&windowed_context.window()); + + let mut renderer = Renderer::new(Backend::new(&gl, Settings::default())); + + let mut debug = Debug::new(); + + let controls = Controls::new(); + let mut state = program::State::new( + controls, + viewport.logical_size(), + conversion::cursor_position(cursor_position, viewport.scale_factor()), + &mut renderer, + &mut debug, + ); + let mut resized = false; + + event_loop.run(move |event, _, control_flow| { + let scene = Scene::new(&gl, &shader_version); + *control_flow = ControlFlow::Wait; + + match event { + Event::LoopDestroyed => return, + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::CursorMoved { position, .. } => { + cursor_position = position; + } + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } + WindowEvent::Resized(physical_size) => { + viewport = Viewport::with_physical_size( + Size::new( + physical_size.width, + physical_size.height, + ), + windowed_context.window().scale_factor(), + ); + + resized = true; + } + WindowEvent::CloseRequested => { + scene.cleanup(&gl); + *control_flow = ControlFlow::Exit + } + _ => (), + } + + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + &event, + windowed_context.window().scale_factor(), + modifiers, + ) { + state.queue_event(event); + } + } + Event::MainEventsCleared => { + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + conversion::cursor_position( + cursor_position, + viewport.scale_factor(), + ), + &mut renderer, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + windowed_context.window().request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = windowed_context.window().inner_size(); + + unsafe { + gl.viewport( + 0, + 0, + size.width as i32, + size.height as i32, + ); + } + + resized = false; + } + + let program = state.program(); + { + // We clear the frame + scene.clear(&gl, program.background_color()); + + // Draw the scene + scene.draw(&gl); + } + + // And then iced on top + let mouse_interaction = renderer.backend_mut().draw( + &gl, + &viewport, + state.primitive(), + &debug.overlay(), + ); + // Update the mouse cursor + windowed_context.window().set_cursor_icon( + iced_winit::conversion::mouse_interaction( + mouse_interaction, + ), + ); + + windowed_context.swap_buffers().unwrap(); + } + _ => (), + } + }); +} diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs new file mode 100644 index 00000000..ccca0d29 --- /dev/null +++ b/examples/integration_opengl/src/scene.rs @@ -0,0 +1,99 @@ +use glow::*; +use iced_glow::Color; + +pub struct Scene { + program: glow::Program, + vertex_array: glow::VertexArray, +} + +impl Scene { + pub fn new(gl: &glow::Context, shader_version: &str) -> Self { + unsafe { + let vertex_array = gl + .create_vertex_array() + .expect("Cannot create vertex array"); + gl.bind_vertex_array(Some(vertex_array)); + + let program = gl.create_program().expect("Cannot create program"); + + let (vertex_shader_source, fragment_shader_source) = ( + r#"const vec2 verts[3] = vec2[3]( + vec2(0.5f, 1.0f), + vec2(0.0f, 0.0f), + vec2(1.0f, 0.0f) + ); + out vec2 vert; + void main() { + vert = verts[gl_VertexID]; + gl_Position = vec4(vert - 0.5, 0.0, 1.0); + }"#, + r#"precision highp float; + in vec2 vert; + out vec4 color; + void main() { + color = vec4(vert, 0.5, 1.0); + }"#, + ); + + let shader_sources = [ + (glow::VERTEX_SHADER, vertex_shader_source), + (glow::FRAGMENT_SHADER, fragment_shader_source), + ]; + + let mut shaders = Vec::with_capacity(shader_sources.len()); + + for (shader_type, shader_source) in shader_sources.iter() { + let shader = gl + .create_shader(*shader_type) + .expect("Cannot create shader"); + gl.shader_source( + shader, + &format!("{}\n{}", shader_version, shader_source), + ); + gl.compile_shader(shader); + if !gl.get_shader_compile_status(shader) { + panic!("{}", gl.get_shader_info_log(shader)); + } + gl.attach_shader(program, shader); + shaders.push(shader); + } + + gl.link_program(program); + if !gl.get_program_link_status(program) { + panic!("{}", gl.get_program_info_log(program)); + } + + for shader in shaders { + gl.detach_shader(program, shader); + gl.delete_shader(shader); + } + + gl.use_program(Some(program)); + Self { + program, + vertex_array, + } + } + } + + pub fn clear(&self, gl: &glow::Context, background_color: Color) { + let [r, g, b, a] = background_color.into_linear(); + unsafe { + gl.clear_color(r, g, b, a); + gl.clear(glow::COLOR_BUFFER_BIT); + } + } + + pub fn draw(&self, gl: &glow::Context) { + unsafe { + gl.draw_arrays(glow::TRIANGLES, 0, 3); + } + } + + pub fn cleanup(&self, gl: &glow::Context) { + unsafe { + gl.delete_program(self.program); + gl.delete_vertex_array(self.vertex_array); + } + } +} diff --git a/examples/integration/Cargo.toml b/examples/integration_wgpu/Cargo.toml index 4515502f..743c655e 100644 --- a/examples/integration/Cargo.toml +++ b/examples/integration_wgpu/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "integration" +name = "integration_wgpu" version = "0.1.0" authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] edition = "2018" diff --git a/examples/integration/README.md b/examples/integration_wgpu/README.md index d5aabc19..c51c2c65 100644 --- a/examples/integration/README.md +++ b/examples/integration_wgpu/README.md @@ -1,6 +1,6 @@ -## Integration +## `wgpu` integration -A demonstration of how to integrate Iced in an existing graphical application. +A demonstration of how to integrate Iced in an existing [`wgpu`] application. The __[`main`]__ file contains all the code of the example. @@ -16,3 +16,4 @@ cargo run --package integration ``` [`main`]: src/main.rs +[`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration/src/controls.rs b/examples/integration_wgpu/src/controls.rs index 36ee9b7e..36ee9b7e 100644 --- a/examples/integration/src/controls.rs +++ b/examples/integration_wgpu/src/controls.rs diff --git a/examples/integration/src/main.rs b/examples/integration_wgpu/src/main.rs index 6f319466..6f319466 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration_wgpu/src/main.rs diff --git a/examples/integration/src/scene.rs b/examples/integration_wgpu/src/scene.rs index 3e8277c8..3e8277c8 100644 --- a/examples/integration/src/scene.rs +++ b/examples/integration_wgpu/src/scene.rs diff --git a/examples/integration/src/shader/frag.spv b/examples/integration_wgpu/src/shader/frag.spv Binary files differindex 9d6807c9..9d6807c9 100644 --- a/examples/integration/src/shader/frag.spv +++ b/examples/integration_wgpu/src/shader/frag.spv diff --git a/examples/integration/src/shader/vert.spv b/examples/integration_wgpu/src/shader/vert.spv Binary files differindex 0cabc9c0..0cabc9c0 100644 --- a/examples/integration/src/shader/vert.spv +++ b/examples/integration_wgpu/src/shader/vert.spv diff --git a/glow/src/lib.rs b/glow/src/lib.rs index 98faf24c..888492d8 100644 --- a/glow/src/lib.rs +++ b/glow/src/lib.rs @@ -4,14 +4,14 @@ //! //! [`glow`]: https://github.com/grovesNL/glow //! [`iced_native`]: https://github.com/hecrj/iced/tree/master/native -#![deny(missing_docs)] +//#![deny(missing_docs)] #![deny(missing_debug_implementations)] #![deny(unused_results)] #![forbid(rust_2018_idioms)] #![cfg_attr(docsrs, feature(doc_cfg))] mod backend; -mod program; +pub mod program; mod quad; mod text; mod triangle; |