diff options
Diffstat (limited to 'winit')
-rw-r--r-- | winit/Cargo.toml | 2 | ||||
-rw-r--r-- | winit/src/conversion.rs | 2 | ||||
-rw-r--r-- | winit/src/program.rs | 257 | ||||
-rw-r--r-- | winit/src/program/state.rs | 32 | ||||
-rw-r--r-- | winit/src/program/window_manager.rs | 31 | ||||
-rw-r--r-- | winit/src/settings.rs | 11 |
6 files changed, 182 insertions, 153 deletions
diff --git a/winit/Cargo.toml b/winit/Cargo.toml index bd6feb00..10a6369b 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -22,7 +22,7 @@ x11 = ["winit/x11"] wayland = ["winit/wayland"] wayland-dlopen = ["winit/wayland-dlopen"] wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] -multi-window = ["iced_runtime/multi-window"] +unconditional-rendering = [] [dependencies] iced_futures.workspace = true diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index e454c208..01c6abc8 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -197,6 +197,8 @@ pub fn window_event( })) } }, + // Ignore keyboard presses/releases during window focus/unfocus + WindowEvent::KeyboardInput { is_synthetic, .. } if is_synthetic => None, WindowEvent::KeyboardInput { event, .. } => Some(Event::Keyboard({ let key = { #[cfg(not(target_arch = "wasm32"))] diff --git a/winit/src/program.rs b/winit/src/program.rs index 8d1eec3a..cc19a4e0 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -8,10 +8,11 @@ use crate::conversion; use crate::core; use crate::core::mouse; use crate::core::renderer; +use crate::core::theme; use crate::core::time::Instant; use crate::core::widget::operation; use crate::core::window; -use crate::core::{Color, Element, Point, Size, Theme}; +use crate::core::{Element, Point, Size}; use crate::futures::futures::channel::mpsc; use crate::futures::futures::channel::oneshot; use crate::futures::futures::task; @@ -46,7 +47,7 @@ use std::sync::Arc; pub trait Program where Self: Sized, - Self::Theme: DefaultStyle, + Self::Theme: theme::Base, { /// The type of __messages__ your [`Program`] will produce. type Message: std::fmt::Debug + Send; @@ -106,8 +107,8 @@ where fn theme(&self, window: window::Id) -> Self::Theme; /// Returns the `Style` variation of the `Theme`. - fn style(&self, theme: &Self::Theme) -> Appearance { - theme.default_style() + fn style(&self, theme: &Self::Theme) -> theme::Style { + theme::Base::base(theme) } /// Returns the event `Subscription` for the current state of the @@ -138,37 +139,6 @@ where } } -/// The appearance of a program. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Appearance { - /// The background [`Color`] of the application. - pub background_color: Color, - - /// The default text [`Color`] of the application. - pub text_color: Color, -} - -/// The default style of a [`Program`]. -pub trait DefaultStyle { - /// Returns the default style of a [`Program`]. - fn default_style(&self) -> Appearance; -} - -impl DefaultStyle for Theme { - fn default_style(&self) -> Appearance { - default(self) - } -} - -/// The default [`Appearance`] of a [`Program`] with the built-in [`Theme`]. -pub fn default(theme: &Theme) -> Appearance { - let palette = theme.extended_palette(); - - Appearance { - background_color: palette.background.base.color, - text_color: palette.background.base.text, - } -} /// Runs a [`Program`] with an executor, compositor, and the provided /// settings. pub fn run<P, C>( @@ -180,7 +150,7 @@ pub fn run<P, C>( where P: Program + 'static, C: Compositor<Renderer = P::Renderer> + 'static, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { use winit::event_loop::EventLoop; @@ -538,10 +508,25 @@ where log::info!("Window attributes for id `{id:#?}`: {window_attributes:#?}"); + // On macOS, the `position` in `WindowAttributes` represents the "inner" + // position of the window; while on other platforms it's the "outer" position. + // We fix the inconsistency on macOS by positioning the window after creation. + #[cfg(target_os = "macos")] + let mut window_attributes = window_attributes; + + #[cfg(target_os = "macos")] + let position = + window_attributes.position.take(); + let window = event_loop .create_window(window_attributes) .expect("Create window"); + #[cfg(target_os = "macos")] + if let Some(position) = position { + window.set_outer_position(position); + } + #[cfg(target_arch = "wasm32")] { use winit::platform::web::WindowExtWebSys; @@ -674,7 +659,7 @@ async fn run_instance<P, C>( ) where P: Program + 'static, C: Compositor<Renderer = P::Renderer> + 'static, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { use winit::event; use winit::event_loop::ControlFlow; @@ -758,12 +743,23 @@ async fn run_instance<P, C>( } Event::EventLoopAwakened(event) => { match event { + event::Event::NewEvents(event::StartCause::Init) => { + for (_id, window) in window_manager.iter_mut() { + window.raw.request_redraw(); + } + } event::Event::NewEvents( - event::StartCause::Init - | event::StartCause::ResumeTimeReached { .. }, + event::StartCause::ResumeTimeReached { .. }, ) => { + let now = Instant::now(); + for (_id, window) in window_manager.iter_mut() { - window.raw.request_redraw(); + if let Some(redraw_at) = window.redraw_at { + if redraw_at <= now { + window.raw.request_redraw(); + window.redraw_at = None; + } + } } } event::Event::PlatformSpecific( @@ -807,11 +803,39 @@ async fn run_instance<P, C>( continue; }; - // TODO: Avoid redrawing all the time by forcing widgets to - // request redraws on state changes - // - // Then, we can use the `interface_state` here to decide if a redraw - // is needed right away, or simply wait until a specific time. + let physical_size = window.state.physical_size(); + + if physical_size.width == 0 || physical_size.height == 0 + { + continue; + } + + if window.viewport_version + != window.state.viewport_version() + { + let logical_size = window.state.logical_size(); + + debug.layout_started(); + let ui = user_interfaces + .remove(&id) + .expect("Remove user interface"); + + let _ = user_interfaces.insert( + id, + ui.relayout(logical_size, &mut window.renderer), + ); + debug.layout_finished(); + + compositor.configure_surface( + &mut window.surface, + physical_size.width, + physical_size.height, + ); + + window.viewport_version = + window.state.viewport_version(); + } + let redraw_event = core::Event::Window( window::Event::RedrawRequested(Instant::now()), ); @@ -857,81 +881,18 @@ async fn run_instance<P, C>( status: core::event::Status::Ignored, }); - let _ = control_sender.start_send(Control::ChangeFlow( - match ui_state { - user_interface::State::Updated { - redraw_request: Some(redraw_request), - } => match redraw_request { - window::RedrawRequest::NextFrame => { - window.raw.request_redraw(); - - ControlFlow::Wait - } - window::RedrawRequest::At(at) => { - ControlFlow::WaitUntil(at) - } - }, - _ => ControlFlow::Wait, - }, - )); - - let physical_size = window.state.physical_size(); - - if physical_size.width == 0 || physical_size.height == 0 + if let user_interface::State::Updated { + redraw_request: Some(redraw_request), + } = ui_state { - continue; - } - - if window.viewport_version - != window.state.viewport_version() - { - let logical_size = window.state.logical_size(); - - debug.layout_started(); - let ui = user_interfaces - .remove(&id) - .expect("Remove user interface"); - - let _ = user_interfaces.insert( - id, - ui.relayout(logical_size, &mut window.renderer), - ); - debug.layout_finished(); - - debug.draw_started(); - let new_mouse_interaction = user_interfaces - .get_mut(&id) - .expect("Get user interface") - .draw( - &mut window.renderer, - window.state.theme(), - &renderer::Style { - text_color: window.state.text_color(), - }, - window.state.cursor(), - ); - debug.draw_finished(); - - if new_mouse_interaction != window.mouse_interaction - { - window.raw.set_cursor( - conversion::mouse_interaction( - new_mouse_interaction, - ), - ); - - window.mouse_interaction = - new_mouse_interaction; + match redraw_request { + window::RedrawRequest::NextFrame => { + window.raw.request_redraw(); + } + window::RedrawRequest::At(at) => { + window.redraw_at = Some(at); + } } - - compositor.configure_surface( - &mut window.surface, - physical_size.width, - physical_size.height, - ); - - window.viewport_version = - window.state.viewport_version(); } debug.render_started(); @@ -995,6 +956,13 @@ async fn run_instance<P, C>( if matches!( window_event, + winit::event::WindowEvent::Resized(_) + ) { + window.raw.request_redraw(); + } + + if matches!( + window_event, winit::event::WindowEvent::CloseRequested ) && window.exit_on_close_request { @@ -1031,7 +999,10 @@ async fn run_instance<P, C>( } } event::Event::AboutToWait => { - if events.is_empty() && messages.is_empty() { + if events.is_empty() + && messages.is_empty() + && window_manager.is_idle() + { continue; } @@ -1065,13 +1036,27 @@ async fn run_instance<P, C>( &mut messages, ); + #[cfg(feature = "unconditional-rendering")] window.raw.request_redraw(); - if !uis_stale { - uis_stale = matches!( - ui_state, - user_interface::State::Outdated - ); + match ui_state { + #[cfg(not( + feature = "unconditional-rendering" + ))] + user_interface::State::Updated { + redraw_request: Some(redraw_request), + } => match redraw_request { + window::RedrawRequest::NextFrame => { + window.raw.request_redraw(); + } + window::RedrawRequest::At(at) => { + window.redraw_at = Some(at); + } + }, + user_interface::State::Outdated => { + uis_stale = true; + } + user_interface::State::Updated { .. } => {} } for (event, status) in window_events @@ -1139,6 +1124,17 @@ async fn run_instance<P, C>( actions = 0; } } + + if let Some(redraw_at) = window_manager.redraw_at() { + let _ = + control_sender.start_send(Control::ChangeFlow( + ControlFlow::WaitUntil(redraw_at), + )); + } else { + let _ = control_sender.start_send( + Control::ChangeFlow(ControlFlow::Wait), + ); + } } _ => {} } @@ -1159,7 +1155,7 @@ fn build_user_interface<'a, P: Program>( id: window::Id, ) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> where - P::Theme: DefaultStyle, + P::Theme: theme::Base, { debug.view_started(); let view = program.view(id); @@ -1178,7 +1174,7 @@ fn update<P: Program, E: Executor>( debug: &mut Debug, messages: &mut Vec<P::Message>, ) where - P::Theme: DefaultStyle, + P::Theme: theme::Base, { for message in messages.drain(..) { debug.log_message(&message); @@ -1215,7 +1211,7 @@ fn run_action<P, C>( ) where P: Program, C: Compositor<Renderer = P::Renderer> + 'static, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { use crate::runtime::clipboard; use crate::runtime::system; @@ -1329,7 +1325,7 @@ fn run_action<P, C>( if let Some(window) = window_manager.get(id) { let position = window .raw - .inner_position() + .outer_position() .map(|position| { let position = position .to_logical::<f32>(window.raw.scale_factor()); @@ -1445,13 +1441,12 @@ fn run_action<P, C>( if let Some(window) = window_manager.get_mut(id) { let bytes = compositor.screenshot( &mut window.renderer, - &mut window.surface, window.state.viewport(), window.state.background_color(), &debug.overlay(), ); - let _ = channel.send(window::Screenshot::new( + let _ = channel.send(core::window::Screenshot::new( bytes, window.state.physical_size(), window.state.viewport().scale_factor(), @@ -1526,7 +1521,7 @@ pub fn build_user_interfaces<'a, P: Program, C>( ) -> FxHashMap<window::Id, UserInterface<'a, P::Message, P::Theme, P::Renderer>> where C: Compositor<Renderer = P::Renderer>, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { cached_user_interfaces .drain() diff --git a/winit/src/program/state.rs b/winit/src/program/state.rs index a7fa2788..e883d04a 100644 --- a/winit/src/program/state.rs +++ b/winit/src/program/state.rs @@ -1,17 +1,18 @@ use crate::conversion; -use crate::core::{mouse, window}; +use crate::core::{mouse, theme, window}; use crate::core::{Color, Size}; use crate::graphics::Viewport; -use crate::program::{self, Program}; -use std::fmt::{Debug, Formatter}; +use crate::program::Program; use winit::event::{Touch, WindowEvent}; use winit::window::Window; +use std::fmt::{Debug, Formatter}; + /// The state of a multi-windowed [`Program`]. pub struct State<P: Program> where - P::Theme: program::DefaultStyle, + P::Theme: theme::Base, { title: String, scale_factor: f64, @@ -20,12 +21,12 @@ where cursor_position: Option<winit::dpi::PhysicalPosition<f64>>, modifiers: winit::keyboard::ModifiersState, theme: P::Theme, - appearance: program::Appearance, + style: theme::Style, } impl<P: Program> Debug for State<P> where - P::Theme: program::DefaultStyle, + P::Theme: theme::Base, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("multi_window::State") @@ -34,14 +35,14 @@ where .field("viewport", &self.viewport) .field("viewport_version", &self.viewport_version) .field("cursor_position", &self.cursor_position) - .field("appearance", &self.appearance) + .field("style", &self.style) .finish() } } impl<P: Program> State<P> where - P::Theme: program::DefaultStyle, + P::Theme: theme::Base, { /// Creates a new [`State`] for the provided [`Program`]'s `window`. pub fn new( @@ -52,7 +53,7 @@ where let title = application.title(window_id); let scale_factor = application.scale_factor(window_id); let theme = application.theme(window_id); - let appearance = application.style(&theme); + let style = application.style(&theme); let viewport = { let physical_size = window.inner_size(); @@ -71,7 +72,7 @@ where cursor_position: None, modifiers: winit::keyboard::ModifiersState::default(), theme, - appearance, + style, } } @@ -127,12 +128,12 @@ where /// Returns the current background [`Color`] of the [`State`]. pub fn background_color(&self) -> Color { - self.appearance.background_color + self.style.background_color } /// Returns the current text [`Color`] of the [`State`]. pub fn text_color(&self) -> Color { - self.appearance.text_color + self.style.text_color } /// Processes the provided window event and updates the [`State`] accordingly. @@ -190,7 +191,10 @@ where .. }, .. - } => _debug.toggle(), + } => { + _debug.toggle(); + window.request_redraw(); + } _ => {} } } @@ -234,6 +238,6 @@ where // Update theme and appearance self.theme = application.theme(window_id); - self.appearance = application.style(&self.theme); + self.style = application.style(&self.theme); } } diff --git a/winit/src/program/window_manager.rs b/winit/src/program/window_manager.rs index 3d22e155..a3c991df 100644 --- a/winit/src/program/window_manager.rs +++ b/winit/src/program/window_manager.rs @@ -1,8 +1,10 @@ use crate::core::mouse; +use crate::core::theme; +use crate::core::time::Instant; use crate::core::window::Id; use crate::core::{Point, Size}; use crate::graphics::Compositor; -use crate::program::{DefaultStyle, Program, State}; +use crate::program::{Program, State}; use std::collections::BTreeMap; use std::sync::Arc; @@ -13,7 +15,7 @@ pub struct WindowManager<P, C> where P: Program, C: Compositor<Renderer = P::Renderer>, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { aliases: BTreeMap<winit::window::WindowId, Id>, entries: BTreeMap<Id, Window<P, C>>, @@ -23,7 +25,7 @@ impl<P, C> WindowManager<P, C> where P: Program, C: Compositor<Renderer = P::Renderer>, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { pub fn new() -> Self { Self { @@ -62,6 +64,7 @@ where surface, renderer, mouse_interaction: mouse::Interaction::None, + redraw_at: None, }, ); @@ -74,6 +77,19 @@ where self.entries.is_empty() } + pub fn is_idle(&self) -> bool { + self.entries + .values() + .all(|window| window.redraw_at.is_none()) + } + + pub fn redraw_at(&self) -> Option<Instant> { + self.entries + .values() + .filter_map(|window| window.redraw_at) + .min() + } + pub fn first(&self) -> Option<&Window<P, C>> { self.entries.first_key_value().map(|(_id, window)| window) } @@ -117,7 +133,7 @@ impl<P, C> Default for WindowManager<P, C> where P: Program, C: Compositor<Renderer = P::Renderer>, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { fn default() -> Self { Self::new() @@ -129,7 +145,7 @@ pub struct Window<P, C> where P: Program, C: Compositor<Renderer = P::Renderer>, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { pub raw: Arc<winit::window::Window>, pub state: State<P>, @@ -138,17 +154,18 @@ where pub mouse_interaction: mouse::Interaction, pub surface: C::Surface, pub renderer: P::Renderer, + pub redraw_at: Option<Instant>, } impl<P, C> Window<P, C> where P: Program, C: Compositor<Renderer = P::Renderer>, - P::Theme: DefaultStyle, + P::Theme: theme::Base, { pub fn position(&self) -> Option<Point> { self.raw - .inner_position() + .outer_position() .ok() .map(|position| position.to_logical(self.raw.scale_factor())) .map(|position| Point { diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 78368a04..e2bf8abf 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -1,4 +1,6 @@ //! Configure your application. +use crate::core; + use std::borrow::Cow; /// The settings of an application. @@ -13,3 +15,12 @@ pub struct Settings { /// The fonts to load on boot. pub fonts: Vec<Cow<'static, [u8]>>, } + +impl From<core::Settings> for Settings { + fn from(settings: core::Settings) -> Self { + Self { + id: settings.id, + fonts: settings.fonts, + } + } +} |