diff options
author | 2019-10-09 05:36:49 +0200 | |
---|---|---|
committer | 2019-10-09 05:36:49 +0200 | |
commit | a92a0b73ed7ed935df762d06c4249894fd35b227 (patch) | |
tree | fe5f67c3b7c2cc84d29e5345e02463f0b4ef6913 | |
parent | 1a93f0ef4a669f37654efcf2102a9d59ada34f9e (diff) | |
download | iced-a92a0b73ed7ed935df762d06c4249894fd35b227.tar.gz iced-a92a0b73ed7ed935df762d06c4249894fd35b227.tar.bz2 iced-a92a0b73ed7ed935df762d06c4249894fd35b227.zip |
Move `winit` logic from `iced` to `iced_winit`
- Added new `renderer::Windowed` trait. This shoud allow users to easily
try different renderers by simply changing one line.
- Renamed `UserInterface` traits to `Application`, as the `run` method
takes total control of the current thread.
- Moved `MouseCursor` back to `iced_native`. The new
`renderer::Windowed` trait returns one on `draw`.
- Split `iced_native` renderer in multiple modules, for consistency.
-rw-r--r-- | examples/tour.rs | 8 | ||||
-rw-r--r-- | native/Cargo.toml | 1 | ||||
-rw-r--r-- | native/src/lib.rs | 2 | ||||
-rw-r--r-- | native/src/mouse_cursor.rs (renamed from wgpu/src/mouse_cursor.rs) | 0 | ||||
-rw-r--r-- | native/src/renderer.rs | 39 | ||||
-rw-r--r-- | native/src/renderer/debugger.rs | 25 | ||||
-rw-r--r-- | native/src/renderer/windowed.rs | 17 | ||||
-rw-r--r-- | src/lib.rs | 122 | ||||
-rw-r--r-- | web/src/bus.rs | 4 | ||||
-rw-r--r-- | web/src/lib.rs | 18 | ||||
-rw-r--r-- | wgpu/src/lib.rs | 2 | ||||
-rw-r--r-- | wgpu/src/renderer.rs | 36 | ||||
-rw-r--r-- | winit/src/lib.rs | 126 |
13 files changed, 245 insertions, 155 deletions
diff --git a/examples/tour.rs b/examples/tour.rs index 542efc66..6ce1e9f1 100644 --- a/examples/tour.rs +++ b/examples/tour.rs @@ -1,7 +1,7 @@ use iced::{ - button, slider, text::HorizontalAlignment, Align, Background, Button, - Checkbox, Color, Column, Element, Image, Justify, Length, Radio, Row, - Slider, Text, UserInterface, + button, slider, text::HorizontalAlignment, Align, Application, Background, + Button, Checkbox, Color, Column, Element, Image, Justify, Length, Radio, + Row, Slider, Text, }; pub fn main() { @@ -30,7 +30,7 @@ impl Tour { } } -impl UserInterface for Tour { +impl Application for Tour { type Message = Message; fn update(&mut self, event: Message) { diff --git a/native/Cargo.toml b/native/Cargo.toml index 5f7e5e41..07c14535 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -14,6 +14,7 @@ features = ["winit"] iced_core = { version = "0.1.0-alpha", path = "../core" } stretch = "0.2" twox-hash = "1.5" +raw-window-handle = "0.1" # Enable to obtain conversion traits winit = { version = "0.20.0-alpha3", optional = true } diff --git a/native/src/lib.rs b/native/src/lib.rs index 18ce3a37..cada56f9 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -206,6 +206,7 @@ mod element; mod event; mod hasher; mod layout; +mod mouse_cursor; mod node; mod style; mod user_interface; @@ -223,6 +224,7 @@ pub use element::Element; pub use event::Event; pub use hasher::Hasher; pub use layout::Layout; +pub use mouse_cursor::MouseCursor; pub use node::Node; pub use renderer::Renderer; pub use style::Style; diff --git a/wgpu/src/mouse_cursor.rs b/native/src/mouse_cursor.rs index 4ef6361a..4ef6361a 100644 --- a/wgpu/src/mouse_cursor.rs +++ b/native/src/mouse_cursor.rs diff --git a/native/src/renderer.rs b/native/src/renderer.rs index d16a0289..a56d7b76 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -1,8 +1,10 @@ //! Write your own renderer. //! -//! There is not a common entrypoint or trait for a __renderer__ in Iced. -//! Instead, every [`Widget`] constrains its generic `Renderer` type as -//! necessary. +//! You will need to implement the `Renderer` trait first. It simply contains +//! a `Primitive` associated type. +//! +//! There is no common trait to draw all the widgets. Instead, every [`Widget`] +//! constrains its generic `Renderer` type as necessary. //! //! This approach is flexible and composable. For instance, the //! [`Text`] widget only needs a [`text::Renderer`] while a [`Checkbox`] widget @@ -17,32 +19,13 @@ //! [`text::Renderer`]: ../widget/text/trait.Renderer.html //! [`Checkbox`]: ../widget/checkbox/struct.Checkbox.html //! [`checkbox::Renderer`]: ../widget/checkbox/trait.Renderer.html -use crate::{Color, Layout, Point, Widget}; + +mod debugger; +mod windowed; + +pub use debugger::Debugger; +pub use windowed::Windowed; pub trait Renderer { type Primitive; } - -/// A renderer able to graphically explain a [`Layout`]. -/// -/// [`Layout`]: ../struct.Layout.html -pub trait Debugger: Renderer { - /// Explains the [`Layout`] of an [`Element`] for debugging purposes. - /// - /// This will be called when [`Element::explain`] has been used. It should - /// _explain_ the given [`Layout`] graphically. - /// - /// A common approach consists in recursively rendering the bounds of the - /// [`Layout`] and its children. - /// - /// [`Layout`]: struct.Layout.html - /// [`Element`]: struct.Element.html - /// [`Element::explain`]: struct.Element.html#method.explain - fn explain<Message>( - &mut self, - widget: &dyn Widget<Message, Self>, - layout: Layout<'_>, - cursor_position: Point, - color: Color, - ) -> Self::Primitive; -} diff --git a/native/src/renderer/debugger.rs b/native/src/renderer/debugger.rs new file mode 100644 index 00000000..cb472dd4 --- /dev/null +++ b/native/src/renderer/debugger.rs @@ -0,0 +1,25 @@ +use crate::{Color, Layout, Point, Widget}; + +/// A renderer able to graphically explain a [`Layout`]. +/// +/// [`Layout`]: ../struct.Layout.html +pub trait Debugger: super::Renderer { + /// Explains the [`Layout`] of an [`Element`] for debugging purposes. + /// + /// This will be called when [`Element::explain`] has been used. It should + /// _explain_ the given [`Layout`] graphically. + /// + /// A common approach consists in recursively rendering the bounds of the + /// [`Layout`] and its children. + /// + /// [`Layout`]: struct.Layout.html + /// [`Element`]: struct.Element.html + /// [`Element::explain`]: struct.Element.html#method.explain + fn explain<Message>( + &mut self, + widget: &dyn Widget<Message, Self>, + layout: Layout<'_>, + cursor_position: Point, + color: Color, + ) -> Self::Primitive; +} diff --git a/native/src/renderer/windowed.rs b/native/src/renderer/windowed.rs new file mode 100644 index 00000000..f89da40b --- /dev/null +++ b/native/src/renderer/windowed.rs @@ -0,0 +1,17 @@ +use crate::MouseCursor; + +use raw_window_handle::HasRawWindowHandle; + +pub trait Windowed: super::Renderer { + type Target; + + fn new<W: HasRawWindowHandle>(window: &W) -> Self; + + fn target(&self, width: u16, height: u16) -> Self::Target; + + fn draw( + &mut self, + target: &mut Self::Target, + primitive: &Self::Primitive, + ) -> MouseCursor; +} @@ -9,123 +9,35 @@ pub type Row<'a, Message> = iced_winit::Row<'a, Message, Renderer>; pub type Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>; pub type Button<'a, Message> = iced_winit::Button<'a, Message, Renderer>; -pub trait UserInterface { +pub trait Application { type Message; fn update(&mut self, message: Self::Message); fn view(&mut self) -> Element<Self::Message>; - fn run(mut self) + fn run(self) where Self: 'static + Sized, { - use winit::{ - event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::WindowBuilder, - }; - - let event_loop = EventLoop::new(); - - // TODO: Ask for window settings and configure this properly - let window = WindowBuilder::new() - .build(&event_loop) - .expect("Open window"); - - let size = window.inner_size().to_physical(window.hidpi_factor());; - let (width, height) = (size.width as u16, size.height as u16); - - let mut renderer = Renderer::new(&window); - let mut target = renderer.target(width, height); - - let user_interface = iced_winit::UserInterface::build( - document(&mut self, width, height), - iced_winit::Cache::default(), - &mut renderer, - ); - - let mut primitive = user_interface.draw(&mut renderer); - let mut cache = Some(user_interface.into_cache()); - let mut events = Vec::new(); - - window.request_redraw(); - - event_loop.run(move |event, _, control_flow| match event { - Event::EventsCleared => { - // TODO: We should be able to keep a user interface alive - // between events once we remove state references. - // - // This will allow us to rebuild it only when a message is - // handled. - let mut user_interface = iced_winit::UserInterface::build( - document(&mut self, width, height), - cache.take().unwrap(), - &mut renderer, - ); - - let messages = user_interface.update(events.drain(..)); - - if messages.is_empty() { - primitive = user_interface.draw(&mut renderer); - - cache = Some(user_interface.into_cache()); - } else { - // When there are messages, we are forced to rebuild twice - // for now :^) - let temp_cache = user_interface.into_cache(); - - for message in messages { - self.update(message); - } - - let user_interface = iced_winit::UserInterface::build( - document(&mut self, width, height), - temp_cache, - &mut renderer, - ); - - primitive = user_interface.draw(&mut renderer); - - cache = Some(user_interface.into_cache()); - } - - window.request_redraw(); - } - Event::WindowEvent { - event: WindowEvent::RedrawRequested, - .. - } => { - renderer.draw(&mut target, &primitive); - - // TODO: Handle animations! - // Maybe we can use `ControlFlow::WaitUntil` for this. - } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - *control_flow = ControlFlow::Exit; - } - _ => { - *control_flow = ControlFlow::Wait; - } - }) + iced_winit::Application::run(Instance(self)) } } -fn document<UserInterface>( - user_interface: &mut UserInterface, - width: u16, - height: u16, -) -> Element<UserInterface::Message> +struct Instance<A: Application>(A); + +impl<A> iced_winit::Application for Instance<A> where - UserInterface: self::UserInterface, - UserInterface::Message: 'static, + A: Application, { - Column::new() - .width(Length::Units(width)) - .height(Length::Units(height)) - .push(user_interface.view()) - .into() + type Renderer = Renderer; + type Message = A::Message; + + fn update(&mut self, message: Self::Message) { + self.0.update(message); + } + + fn view(&mut self) -> Element<Self::Message> { + self.0.view() + } } diff --git a/web/src/bus.rs b/web/src/bus.rs index d76466f5..b4fd67c7 100644 --- a/web/src/bus.rs +++ b/web/src/bus.rs @@ -1,4 +1,4 @@ -use crate::Application; +use crate::Instance; use std::rc::Rc; @@ -14,7 +14,7 @@ where pub fn new() -> Self { Self { publish: Rc::new(Box::new(|message, root| { - let app = root.unwrap_mut::<Application<Message>>(); + let app = root.unwrap_mut::<Instance<Message>>(); app.update(message) })), diff --git a/web/src/lib.rs b/web/src/lib.rs index caf17df5..04848d07 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -11,7 +11,7 @@ pub use element::Element; pub use iced_core::{Align, Color, Justify, Length}; pub use widget::*; -pub trait UserInterface { +pub trait Application { type Message; fn update( @@ -25,23 +25,23 @@ pub trait UserInterface { where Self: 'static + Sized, { + let app = Instance::new(self); + let window = web_sys::window().unwrap(); let document = window.document().unwrap(); let body = document.body().unwrap(); - - let app = Application::new(self); - let vdom = dodrio::Vdom::new(&body, app); + vdom.forget(); } } -struct Application<Message> { - ui: RefCell<Box<dyn UserInterface<Message = Message>>>, +struct Instance<Message> { + ui: RefCell<Box<dyn Application<Message = Message>>>, } -impl<Message> Application<Message> { - fn new(ui: impl UserInterface<Message = Message> + 'static) -> Self { +impl<Message> Instance<Message> { + fn new(ui: impl Application<Message = Message> + 'static) -> Self { Self { ui: RefCell::new(Box::new(ui)), } @@ -55,7 +55,7 @@ impl<Message> Application<Message> { } } -impl<Message> dodrio::Render for Application<Message> +impl<Message> dodrio::Render for Instance<Message> where Message: 'static, { diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 8f8d50e9..46849aab 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1,4 +1,3 @@ -mod mouse_cursor; mod primitive; mod quad; mod renderer; @@ -7,6 +6,5 @@ mod transformation; pub(crate) use quad::Quad; pub(crate) use transformation::Transformation; -pub use mouse_cursor::MouseCursor; pub use primitive::Primitive; pub use renderer::{Renderer, Target}; diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs index ae5692e3..036efd27 100644 --- a/wgpu/src/renderer.rs +++ b/wgpu/src/renderer.rs @@ -1,6 +1,7 @@ use crate::{quad, Primitive, Quad, Transformation}; use iced_native::{ - renderer::Debugger, Background, Color, Layout, Point, Widget, + renderer::Debugger, renderer::Windowed, Background, Color, Layout, + MouseCursor, Point, Widget, }; use raw_window_handle::HasRawWindowHandle; @@ -41,7 +42,7 @@ pub struct Target { } impl Renderer { - pub fn new<W: HasRawWindowHandle>(window: &W) -> Self { + fn new<W: HasRawWindowHandle>(window: &W) -> Self { let adapter = Adapter::request(&RequestAdapterOptions { power_preference: PowerPreference::LowPower, backends: BackendBit::all(), @@ -79,7 +80,7 @@ impl Renderer { } } - pub fn target(&self, width: u16, height: u16) -> Target { + fn target(&self, width: u16, height: u16) -> Target { Target { width, height, @@ -97,7 +98,11 @@ impl Renderer { } } - pub fn draw(&mut self, target: &mut Target, primitive: &Primitive) { + fn draw( + &mut self, + target: &mut Target, + primitive: &Primitive, + ) -> MouseCursor { log::debug!("Drawing"); let frame = target.swap_chain.get_next_texture(); @@ -146,8 +151,9 @@ impl Renderer { .expect("Draw text"); self.queue.submit(&[encoder.finish()]); - } + MouseCursor::OutOfBounds + } fn draw_primitive(&mut self, primitive: &Primitive) { match primitive { Primitive::None => {} @@ -240,6 +246,26 @@ impl iced_native::Renderer for Renderer { type Primitive = Primitive; } +impl Windowed for Renderer { + type Target = Target; + + fn new<W: HasRawWindowHandle>(window: &W) -> Self { + Self::new(window) + } + + fn target(&self, width: u16, height: u16) -> Target { + self.target(width, height) + } + + fn draw( + &mut self, + target: &mut Target, + primitive: &Primitive, + ) -> MouseCursor { + self.draw(target, primitive) + } +} + impl Debugger for Renderer { fn explain<Message>( &mut self, diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 54a0bd9a..93e922d5 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -1,2 +1,128 @@ pub use iced_native::*; pub use winit; + +pub use iced_native::renderer::Windowed; + +pub trait Application { + type Renderer: iced_native::renderer::Windowed + + iced_native::column::Renderer; + + type Message; + + fn update(&mut self, message: Self::Message); + + fn view(&mut self) -> Element<Self::Message, Self::Renderer>; + + fn run(mut self) + where + Self: 'static + Sized, + { + use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder, + }; + + let event_loop = EventLoop::new(); + + // TODO: Ask for window settings and configure this properly + let window = WindowBuilder::new() + .build(&event_loop) + .expect("Open window"); + + let size = window.inner_size().to_physical(window.hidpi_factor());; + let (width, height) = (size.width as u16, size.height as u16); + + let mut renderer = Self::Renderer::new(&window); + let mut target = renderer.target(width, height); + + let user_interface = UserInterface::build( + document(&mut self, width, height), + Cache::default(), + &mut renderer, + ); + + let mut primitive = user_interface.draw(&mut renderer); + let mut cache = Some(user_interface.into_cache()); + let mut events = Vec::new(); + + window.request_redraw(); + + event_loop.run(move |event, _, control_flow| match event { + Event::EventsCleared => { + // TODO: We should be able to keep a user interface alive + // between events once we remove state references. + // + // This will allow us to rebuild it only when a message is + // handled. + let mut user_interface = UserInterface::build( + document(&mut self, width, height), + cache.take().unwrap(), + &mut renderer, + ); + + let messages = user_interface.update(events.drain(..)); + + if messages.is_empty() { + primitive = user_interface.draw(&mut renderer); + + cache = Some(user_interface.into_cache()); + } else { + // When there are messages, we are forced to rebuild twice + // for now :^) + let temp_cache = user_interface.into_cache(); + + for message in messages { + self.update(message); + } + + let user_interface = UserInterface::build( + document(&mut self, width, height), + temp_cache, + &mut renderer, + ); + + primitive = user_interface.draw(&mut renderer); + + cache = Some(user_interface.into_cache()); + } + + window.request_redraw(); + } + Event::WindowEvent { + event: WindowEvent::RedrawRequested, + .. + } => { + renderer.draw(&mut target, &primitive); + + // TODO: Handle animations! + // Maybe we can use `ControlFlow::WaitUntil` for this. + } + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + *control_flow = ControlFlow::Exit; + } + _ => { + *control_flow = ControlFlow::Wait; + } + }) + } +} + +fn document<Application>( + application: &mut Application, + width: u16, + height: u16, +) -> Element<Application::Message, Application::Renderer> +where + Application: self::Application, + Application::Message: 'static, +{ + Column::new() + .width(Length::Units(width)) + .height(Length::Units(height)) + .push(application.view()) + .into() +} |