diff options
Diffstat (limited to 'winit/src/application.rs')
| -rw-r--r-- | winit/src/application.rs | 398 |
1 files changed, 212 insertions, 186 deletions
diff --git a/winit/src/application.rs b/winit/src/application.rs index 1042b412..f5aa799c 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,9 +1,7 @@ use crate::{ - conversion, - input::{keyboard, mouse}, - renderer::{Target, Windowed}, - Cache, Command, Container, Debug, Element, Event, Length, MouseCursor, - Settings, UserInterface, + conversion, size::Size, window, Cache, Clipboard, Command, Debug, Element, + Executor, Mode, MouseCursor, Proxy, Runtime, Settings, Subscription, + UserInterface, }; /// An interactive, native cross-platform application. @@ -15,10 +13,15 @@ use crate::{ /// An [`Application`](trait.Application.html) can execute asynchronous actions /// by returning a [`Command`](struct.Command.html) in some of its methods. pub trait Application: Sized { - /// The renderer to use to draw the [`Application`]. + /// The graphics backend to use to draw the [`Application`]. /// /// [`Application`]: trait.Application.html - type Renderer: Windowed; + type Backend: window::Backend; + + /// The [`Executor`] that will run commands and subscriptions. + /// + /// [`Executor`]: trait.Executor.html + type Executor: Executor; /// The type of __messages__ your [`Application`] will produce. /// @@ -57,12 +60,35 @@ pub trait Application: Sized { /// [`Command`]: struct.Command.html fn update(&mut self, message: Self::Message) -> Command<Self::Message>; + /// Returns the event `Subscription` for the current state of the + /// application. + /// + /// The messages produced by the `Subscription` will be handled by + /// [`update`](#tymethod.update). + /// + /// A `Subscription` will be kept alive as long as you keep returning it! + fn subscription(&self) -> Subscription<Self::Message>; + /// Returns the widgets to display in the [`Application`]. /// /// These widgets can produce __messages__ based on user interaction. /// /// [`Application`]: trait.Application.html - fn view(&mut self) -> Element<'_, Self::Message, Self::Renderer>; + fn view( + &mut self, + ) -> Element<'_, Self::Message, <Self::Backend as window::Backend>::Renderer>; + + /// Returns the current [`Application`] mode. + /// + /// The runtime will automatically transition your application if a new mode + /// is returned. + /// + /// By default, an application will run in windowed mode. + /// + /// [`Application`]: trait.Application.html + fn mode(&self) -> Mode { + Mode::Windowed + } /// Runs the [`Application`]. /// @@ -72,10 +98,13 @@ pub trait Application: Sized { /// It should probably be that last thing you call in your `main` function. /// /// [`Application`]: trait.Application.html - fn run(settings: Settings) - where + fn run( + settings: Settings, + backend_settings: <Self::Backend as window::Backend>::Settings, + ) where Self: 'static, { + use window::Backend as _; use winit::{ event::{self, WindowEvent}, event_loop::{ControlFlow, EventLoop}, @@ -86,49 +115,75 @@ pub trait Application: Sized { debug.startup_started(); let event_loop = EventLoop::with_user_event(); - let proxy = event_loop.create_proxy(); - let mut thread_pool = - futures::executor::ThreadPool::new().expect("Create thread pool"); let mut external_messages = Vec::new(); - let (mut application, init_command) = Self::new(); - spawn(init_command, &mut thread_pool, &proxy); + let mut runtime = { + let executor = Self::Executor::new().expect("Create executor"); + + Runtime::new(executor, Proxy::new(event_loop.create_proxy())) + }; + + let (mut application, init_command) = runtime.enter(|| Self::new()); + runtime.spawn(init_command); + + let subscription = application.subscription(); + runtime.track(subscription); let mut title = application.title(); + let mut mode = application.mode(); + + let window = { + let mut window_builder = WindowBuilder::new(); - let (width, height) = settings.window.size; + let (width, height) = settings.window.size; - let window = WindowBuilder::new() - .with_title(&title) - .with_inner_size(winit::dpi::LogicalSize { - width: f64::from(width), - height: f64::from(height), - }) - .with_resizable(settings.window.resizable) - .build(&event_loop) - .expect("Open window"); + window_builder = window_builder + .with_title(&title) + .with_inner_size(winit::dpi::LogicalSize { width, height }) + .with_resizable(settings.window.resizable) + .with_decorations(settings.window.decorations) + .with_fullscreen(conversion::fullscreen( + event_loop.primary_monitor(), + mode, + )); - let dpi = window.hidpi_factor(); - let mut size = window.inner_size(); - let mut new_size: Option<winit::dpi::LogicalSize> = None; + #[cfg(target_os = "windows")] + { + use winit::platform::windows::WindowBuilderExtWindows; - let mut renderer = Self::Renderer::new(); + if let Some(parent) = settings.window.platform_specific.parent { + window_builder = window_builder.with_parent_window(parent); + } + } + + window_builder.build(&event_loop).expect("Open window") + }; - let mut target = { - let (width, height) = to_physical(size, dpi); + let mut size = Size::new(window.inner_size(), window.scale_factor()); + let mut resized = false; - <Self::Renderer as Windowed>::Target::new( - &window, width, height, dpi as f32, &renderer, + let clipboard = Clipboard::new(&window); + let (mut backend, mut renderer) = Self::Backend::new(backend_settings); + + let surface = backend.create_surface(&window); + + let mut swap_chain = { + let physical_size = size.physical(); + + backend.create_swap_chain( + &surface, + physical_size.width, + physical_size.height, ) }; - debug.layout_started(); - let user_interface = UserInterface::build( - document(&mut application, size, &mut debug), + let user_interface = build_user_interface( + &mut application, Cache::default(), &mut renderer, + size.logical(), + &mut debug, ); - debug.layout_finished(); debug.draw_started(); let mut primitive = user_interface.draw(&mut renderer); @@ -137,28 +192,43 @@ pub trait Application: Sized { let mut cache = Some(user_interface.into_cache()); let mut events = Vec::new(); let mut mouse_cursor = MouseCursor::OutOfBounds; + let mut modifiers = winit::event::ModifiersState::default(); debug.startup_finished(); window.request_redraw(); event_loop.run(move |event, _, control_flow| match event { event::Event::MainEventsCleared => { + if events.is_empty() && external_messages.is_empty() { + return; + } + // 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. - debug.layout_started(); - let mut user_interface = UserInterface::build( - document(&mut application, size, &mut debug), + let mut user_interface = build_user_interface( + &mut application, cache.take().unwrap(), &mut renderer, + size.logical(), + &mut debug, ); - debug.layout_finished(); debug.event_processing_started(); - let mut messages = - user_interface.update(&renderer, events.drain(..)); + events + .iter() + .cloned() + .for_each(|event| runtime.broadcast(event)); + + let mut messages = user_interface.update( + events.drain(..), + clipboard + .as_ref() + .map(|c| c as &dyn iced_native::Clipboard), + &renderer, + ); messages.extend(external_messages.drain(..)); debug.event_processing_finished(); @@ -179,12 +249,15 @@ pub trait Application: Sized { debug.log_message(&message); debug.update_started(); - let command = application.update(message); - - spawn(command, &mut thread_pool, &proxy); + let command = + runtime.enter(|| application.update(message)); + runtime.spawn(command); debug.update_finished(); } + let subscription = application.subscription(); + runtime.track(subscription); + // Update window title let new_title = application.title(); @@ -194,13 +267,25 @@ pub trait Application: Sized { title = new_title; } - debug.layout_started(); - let user_interface = UserInterface::build( - document(&mut application, size, &mut debug), + // Update window mode + let new_mode = application.mode(); + + if mode != new_mode { + window.set_fullscreen(conversion::fullscreen( + window.current_monitor(), + new_mode, + )); + + mode = new_mode; + } + + let user_interface = build_user_interface( + &mut application, temp_cache, &mut renderer, + size.logical(), + &mut debug, ); - debug.layout_finished(); debug.draw_started(); primitive = user_interface.draw(&mut renderer); @@ -217,22 +302,25 @@ pub trait Application: Sized { event::Event::RedrawRequested(_) => { debug.render_started(); - if let Some(new_size) = new_size.take() { - let dpi = window.hidpi_factor(); - let (width, height) = to_physical(new_size, dpi); + if resized { + let physical_size = size.physical(); - target.resize( - width, - height, - window.hidpi_factor() as f32, - &renderer, + swap_chain = backend.create_swap_chain( + &surface, + physical_size.width, + physical_size.height, ); - size = new_size; + resized = false; } - let new_mouse_cursor = - renderer.draw(&primitive, &debug.overlay(), &mut target); + let new_mouse_cursor = backend.draw( + &mut renderer, + &mut swap_chain, + &primitive, + size.scale_factor(), + &debug.overlay(), + ); debug.render_finished(); @@ -250,83 +338,56 @@ pub trait Application: Sized { event::Event::WindowEvent { event: window_event, .. - } => match window_event { - WindowEvent::CursorMoved { position, .. } => { - events.push(Event::Mouse(mouse::Event::CursorMoved { - x: position.x as f32, - y: position.y as f32, - })); - } - WindowEvent::MouseInput { button, state, .. } => { - events.push(Event::Mouse(mouse::Event::Input { - button: conversion::mouse_button(button), - state: conversion::button_state(state), - })); - } - WindowEvent::MouseWheel { delta, .. } => match delta { - winit::event::MouseScrollDelta::LineDelta( - delta_x, - delta_y, - ) => { - events.push(Event::Mouse( - mouse::Event::WheelScrolled { - delta: mouse::ScrollDelta::Lines { - x: delta_x, - y: delta_y, - }, - }, - )); + } => { + match window_event { + WindowEvent::Resized(new_size) => { + size = Size::new(new_size, window.scale_factor()); + resized = true; } - winit::event::MouseScrollDelta::PixelDelta(position) => { - events.push(Event::Mouse( - mouse::Event::WheelScrolled { - delta: mouse::ScrollDelta::Pixels { - x: position.x as f32, - y: position.y as f32, - }, - }, - )); + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; } - }, - WindowEvent::ReceivedCharacter(c) - if !is_private_use_character(c) => - { - events.push(Event::Keyboard( - keyboard::Event::CharacterReceived(c), - )); - } - WindowEvent::KeyboardInput { - input: - winit::event::KeyboardInput { - virtual_keycode: Some(virtual_keycode), - state, - .. - }, - .. - } => { - match (virtual_keycode, state) { - ( - winit::event::VirtualKeyCode::F12, - winit::event::ElementState::Pressed, - ) => debug.toggle(), - _ => {} + #[cfg(target_os = "macos")] + WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: + Some(winit::event::VirtualKeyCode::Q), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } if modifiers.logo() => { + *control_flow = ControlFlow::Exit; } - - events.push(Event::Keyboard(keyboard::Event::Input { - key_code: conversion::key_code(virtual_keycode), - state: conversion::button_state(state), - })); - } - WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; + #[cfg(feature = "debug")] + WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: + Some(winit::event::VirtualKeyCode::F12), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } => debug.toggle(), + _ => {} } - WindowEvent::Resized(size) => { - new_size = Some(size.into()); - log::debug!("Resized: {:?}", new_size); + if let Some(event) = conversion::window_event( + window_event, + size.scale_factor(), + modifiers, + ) { + events.push(event); } - _ => {} - }, + } + event::Event::DeviceEvent { + event: event::DeviceEvent::ModifiersChanged(new_modifiers), + .. + } => { + modifiers = new_modifiers; + } _ => { *control_flow = ControlFlow::Wait; } @@ -334,63 +395,28 @@ pub trait Application: Sized { } } -fn to_physical(size: winit::dpi::LogicalSize, dpi: f64) -> (u16, u16) { - let physical_size = size.to_physical(dpi); - - ( - physical_size.width.round() as u16, - physical_size.height.round() as u16, - ) -} - -fn document<'a, Application>( - application: &'a mut Application, - size: winit::dpi::LogicalSize, +fn build_user_interface<'a, A: Application>( + application: &'a mut A, + cache: Cache, + renderer: &mut <A::Backend as window::Backend>::Renderer, + size: winit::dpi::LogicalSize<f64>, debug: &mut Debug, -) -> Element<'a, Application::Message, Application::Renderer> -where - Application: self::Application, - Application::Message: 'static, -{ +) -> UserInterface<'a, A::Message, <A::Backend as window::Backend>::Renderer> { debug.view_started(); let view = application.view(); debug.view_finished(); - Container::new(view) - .width(Length::Units(size.width.round() as u16)) - .height(Length::Units(size.height.round() as u16)) - .into() -} - -fn spawn<Message: Send>( - command: Command<Message>, - thread_pool: &mut futures::executor::ThreadPool, - proxy: &winit::event_loop::EventLoopProxy<Message>, -) { - use futures::FutureExt; - - let futures = command.futures(); - - for future in futures { - let proxy = proxy.clone(); - - let future = future.map(move |message| { - proxy - .send_event(message) - .expect("Send command result to event loop"); - }); - - thread_pool.spawn_ok(future); - } -} - -// As defined in: http://www.unicode.org/faq/private_use.html -// TODO: Remove once https://github.com/rust-windowing/winit/pull/1254 lands -fn is_private_use_character(c: char) -> bool { - match c { - '\u{E000}'..='\u{F8FF}' - | '\u{F0000}'..='\u{FFFFD}' - | '\u{100000}'..='\u{10FFFD}' => true, - _ => false, - } + debug.layout_started(); + let user_interface = UserInterface::build( + view, + iced_native::Size::new( + size.width.round() as f32, + size.height.round() as f32, + ), + cache, + renderer, + ); + debug.layout_finished(); + + user_interface } |
