diff options
| author | 2020-05-29 02:00:28 +0200 | |
|---|---|---|
| committer | 2020-05-29 02:00:28 +0200 | |
| commit | 0cde20b3550ede81bc7ddef628b91eec225aa8af (patch) | |
| tree | 56920437979012cceb49718f2dd4ce27d5ba5d40 /winit | |
| parent | 67b6f044e870df41be92cdc79f571682b97a5d0d (diff) | |
| parent | e11b5c614f5bf73c137b8b4f24f56047617527eb (diff) | |
| download | iced-0cde20b3550ede81bc7ddef628b91eec225aa8af.tar.gz iced-0cde20b3550ede81bc7ddef628b91eec225aa8af.tar.bz2 iced-0cde20b3550ede81bc7ddef628b91eec225aa8af.zip | |
Merge branch 'master' into improvement/update-wgpu_glyph
Diffstat (limited to '')
| -rw-r--r-- | native/src/debug/basic.rs (renamed from winit/src/debug/basic.rs) | 5 | ||||
| -rw-r--r-- | native/src/debug/null.rs (renamed from winit/src/debug/null.rs) | 1 | ||||
| -rw-r--r-- | winit/Cargo.toml | 6 | ||||
| -rw-r--r-- | winit/src/application.rs | 566 | ||||
| -rw-r--r-- | winit/src/lib.rs | 16 | ||||
| -rw-r--r-- | winit/src/proxy.rs | 5 | ||||
| -rw-r--r-- | winit/src/settings.rs | 37 | ||||
| -rw-r--r-- | winit/src/size.rs | 30 | 
8 files changed, 280 insertions, 386 deletions
| diff --git a/winit/src/debug/basic.rs b/native/src/debug/basic.rs index d46edba6..5338d0d9 100644 --- a/winit/src/debug/basic.rs +++ b/native/src/debug/basic.rs @@ -1,5 +1,7 @@ +#![allow(missing_docs)]  use std::{collections::VecDeque, time}; +/// A bunch of time measurements for debugging purposes.  #[derive(Debug)]  pub struct Debug {      is_enabled: bool, @@ -30,6 +32,9 @@ pub struct Debug {  }  impl Debug { +    /// Creates a new [`Debug`]. +    /// +    /// [`Debug`]: struct.Debug.html      pub fn new() -> Self {          let now = time::Instant::now(); diff --git a/winit/src/debug/null.rs b/native/src/debug/null.rs index 2a9430cd..60e6122d 100644 --- a/winit/src/debug/null.rs +++ b/native/src/debug/null.rs @@ -1,3 +1,4 @@ +#![allow(missing_docs)]  #[derive(Debug)]  pub struct Debug; diff --git a/winit/Cargo.toml b/winit/Cargo.toml index b6662451..7fe83b96 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["gui", "ui", "graphics", "interface", "widgets"]  categories = ["gui"]  [features] -debug = [] +debug = ["iced_native/debug"]  [dependencies]  winit = "0.22" @@ -22,5 +22,9 @@ log = "0.4"  version = "0.2"  path = "../native" +[dependencies.iced_graphics] +version = "0.1" +path = "../graphics" +  [target.'cfg(target_os = "windows")'.dependencies.winapi]  version = "0.3.6" diff --git a/winit/src/application.rs b/winit/src/application.rs index f6bc8fcc..73ac72b2 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,8 +1,11 @@ +//! Create interactive, native cross-platform applications.  use crate::{ -    conversion, mouse, size::Size, window, Cache, Clipboard, Command, Debug, -    Element, Executor, Mode, Proxy, Runtime, Settings, Subscription, -    UserInterface, +    conversion, mouse, Clipboard, Command, Debug, Executor, Mode, Proxy, +    Runtime, Settings, Size, Subscription,  }; +use iced_graphics::window; +use iced_graphics::Viewport; +use iced_native::program::{self, Program};  /// An interactive, native cross-platform application.  /// @@ -15,22 +18,9 @@ use crate::{  ///  /// When using an [`Application`] with the `debug` feature enabled, a debug view  /// can be toggled by pressing `F12`. -pub trait Application: Sized { -    /// The graphics backend to use to draw the [`Application`]. -    /// -    /// [`Application`]: trait.Application.html -    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. -    /// -    /// [`Application`]: trait.Application.html -    type Message: std::fmt::Debug + Send; - +/// +/// [`Application`]: trait.Application.html +pub trait Application: Program {      /// The data needed to initialize your [`Application`].      ///      /// [`Application`]: trait.Application.html @@ -48,7 +38,7 @@ pub trait Application: Sized {      ///      /// [`Application`]: trait.Application.html      /// [`run`]: #method.run.html -    /// [`Settings`]: struct.Settings.html +    /// [`Settings`]: ../settings/struct.Settings.html      fn new(flags: Self::Flags) -> (Self, Command<Self::Message>);      /// Returns the current title of the [`Application`]. @@ -59,18 +49,6 @@ pub trait Application: Sized {      /// [`Application`]: trait.Application.html      fn title(&self) -> String; -    /// Handles a __message__ and updates the state of the [`Application`]. -    /// -    /// This is where you define your __update logic__. All the __messages__, -    /// produced by either user interactions or commands, will be handled by -    /// this method. -    /// -    /// Any [`Command`] returned will be executed immediately in the background. -    /// -    /// [`Application`]: trait.Application.html -    /// [`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.      /// @@ -78,16 +56,11 @@ pub trait Application: Sized {      /// [`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::Backend as window::Backend>::Renderer>; +    /// By default, it returns an empty subscription. +    fn subscription(&self) -> Subscription<Self::Message> { +        Subscription::none() +    }      /// Returns the current [`Application`] mode.      /// @@ -100,334 +73,243 @@ pub trait Application: Sized {      fn mode(&self) -> Mode {          Mode::Windowed      } +} -    /// Runs the [`Application`] with the provided [`Settings`]. -    /// -    /// On native platforms, this method will take control of the current thread -    /// and __will NOT return__. -    /// -    /// It should probably be that last thing you call in your `main` function. -    /// -    /// [`Application`]: trait.Application.html -    /// [`Settings`]: struct.Settings.html -    fn run( -        settings: Settings<Self::Flags>, -        backend_settings: <Self::Backend as window::Backend>::Settings, -    ) where -        Self: 'static, -    { -        use window::Backend as _; -        use winit::{ -            event::{self, WindowEvent}, -            event_loop::{ControlFlow, EventLoop}, -            window::WindowBuilder, -        }; - -        let mut debug = Debug::new(); - -        debug.startup_started(); -        let event_loop = EventLoop::with_user_event(); -        let mut external_messages = Vec::new(); - -        let mut runtime = { -            let executor = Self::Executor::new().expect("Create executor"); - -            Runtime::new(executor, Proxy::new(event_loop.create_proxy())) -        }; - -        let flags = settings.flags; -        let (mut application, init_command) = -            runtime.enter(|| Self::new(flags)); -        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; - -            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, -                )); - -            #[cfg(target_os = "windows")] -            { -                use winit::platform::windows::WindowBuilderExtWindows; - -                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 size = Size::new(window.inner_size(), window.scale_factor()); -        let mut resized = false; - -        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, -            ) -        }; - -        let user_interface = build_user_interface( -            &mut application, -            Cache::default(), -            &mut renderer, -            size.logical(), -            &mut debug, -        ); - -        debug.draw_started(); -        let mut primitive = user_interface.draw(&mut renderer); -        debug.draw_finished(); +/// Runs an [`Application`] with an executor, compositor, and the provided +/// settings. +/// +/// [`Application`]: trait.Application.html +pub fn run<A, E, C>( +    settings: Settings<A::Flags>, +    compositor_settings: C::Settings, +) where +    A: Application + 'static, +    E: Executor + 'static, +    C: window::Compositor<Renderer = A::Renderer> + 'static, +{ +    use winit::{ +        event, +        event_loop::{ControlFlow, EventLoop}, +    }; + +    let mut debug = Debug::new(); +    debug.startup_started(); + +    let event_loop = EventLoop::with_user_event(); +    let mut runtime = { +        let executor = E::new().expect("Create executor"); +        let proxy = Proxy::new(event_loop.create_proxy()); + +        Runtime::new(executor, proxy) +    }; + +    let flags = settings.flags; +    let (application, init_command) = runtime.enter(|| A::new(flags)); +    runtime.spawn(init_command); + +    let subscription = application.subscription(); +    runtime.track(subscription); + +    let mut title = application.title(); +    let mut mode = application.mode(); + +    let window = settings +        .window +        .into_builder(&title, mode, event_loop.primary_monitor()) +        .build(&event_loop) +        .expect("Open window"); + +    let clipboard = Clipboard::new(&window); +    let mut mouse_interaction = mouse::Interaction::default(); +    let mut modifiers = winit::event::ModifiersState::default(); + +    let physical_size = window.inner_size(); +    let mut viewport = Viewport::with_physical_size( +        Size::new(physical_size.width, physical_size.height), +        window.scale_factor(), +    ); +    let mut resized = false; -        let mut cache = Some(user_interface.into_cache()); -        let mut events = Vec::new(); -        let mut mouse_interaction = mouse::Interaction::default(); -        let mut modifiers = winit::event::ModifiersState::default(); -        debug.startup_finished(); +    let (mut compositor, mut renderer) = C::new(compositor_settings); -        window.request_redraw(); +    let surface = compositor.create_surface(&window); -        event_loop.run(move |event, _, control_flow| match event { -            event::Event::MainEventsCleared => { -                if events.is_empty() && external_messages.is_empty() { -                    return; -                } +    let mut swap_chain = compositor.create_swap_chain( +        &surface, +        physical_size.width, +        physical_size.height, +    ); -                // 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 = build_user_interface( -                    &mut application, -                    cache.take().unwrap(), +    let mut state = program::State::new( +        application, +        viewport.logical_size(), +        &mut renderer, +        &mut debug, +    ); +    debug.startup_finished(); + +    event_loop.run(move |event, _, control_flow| match event { +        event::Event::MainEventsCleared => { +            let command = runtime.enter(|| { +                state.update( +                    clipboard.as_ref().map(|c| c as _), +                    viewport.logical_size(),                      &mut renderer, -                    size.logical(),                      &mut debug, -                ); - -                debug.event_processing_started(); -                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(); - -                if messages.is_empty() { -                    debug.draw_started(); -                    primitive = user_interface.draw(&mut renderer); -                    debug.draw_finished(); +                ) +            }); -                    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(); +            // If the application was updated +            if let Some(command) = command { +                runtime.spawn(command); -                    for message in messages { -                        log::debug!("Updating"); +                let program = state.program(); -                        debug.log_message(&message); +                // Update subscriptions +                let subscription = program.subscription(); +                runtime.track(subscription); -                        debug.update_started(); -                        let command = -                            runtime.enter(|| application.update(message)); -                        runtime.spawn(command); -                        debug.update_finished(); -                    } +                // Update window title +                let new_title = program.title(); -                    let subscription = application.subscription(); -                    runtime.track(subscription); +                if title != new_title { +                    window.set_title(&new_title); -                    // Update window title -                    let new_title = application.title(); - -                    if title != new_title { -                        window.set_title(&new_title); - -                        title = new_title; -                    } - -                    // 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.draw_started(); -                    primitive = user_interface.draw(&mut renderer); -                    debug.draw_finished(); - -                    cache = Some(user_interface.into_cache()); +                    title = new_title;                  } -                window.request_redraw(); -            } -            event::Event::UserEvent(message) => { -                external_messages.push(message); -            } -            event::Event::RedrawRequested(_) => { -                debug.render_started(); - -                if resized { -                    let physical_size = size.physical(); +                // Update window mode +                let new_mode = program.mode(); -                    swap_chain = backend.create_swap_chain( -                        &surface, -                        physical_size.width, -                        physical_size.height, -                    ); +                if mode != new_mode { +                    window.set_fullscreen(conversion::fullscreen( +                        window.current_monitor(), +                        new_mode, +                    )); -                    resized = false; +                    mode = new_mode;                  } +            } -                let new_mouse_interaction = backend.draw( -                    &mut renderer, -                    &mut swap_chain, -                    &primitive, -                    size.scale_factor(), -                    &debug.overlay(), +            window.request_redraw(); +        } +        event::Event::UserEvent(message) => { +            state.queue_message(message); +        } +        event::Event::RedrawRequested(_) => { +            debug.render_started(); + +            if resized { +                let physical_size = viewport.physical_size(); + +                swap_chain = compositor.create_swap_chain( +                    &surface, +                    physical_size.width, +                    physical_size.height,                  ); -                debug.render_finished(); +                resized = false; +            } -                if new_mouse_interaction != mouse_interaction { -                    window.set_cursor_icon(conversion::mouse_interaction( -                        new_mouse_interaction, -                    )); +            let new_mouse_interaction = compositor.draw( +                &mut renderer, +                &mut swap_chain, +                &viewport, +                state.primitive(), +                &debug.overlay(), +            ); -                    mouse_interaction = new_mouse_interaction; -                } +            debug.render_finished(); -                // TODO: Handle animations! -                // Maybe we can use `ControlFlow::WaitUntil` for this. -            } -            event::Event::WindowEvent { -                event: window_event, -                .. -            } => { -                match window_event { -                    WindowEvent::Resized(new_size) => { -                        size = Size::new(new_size, window.scale_factor()); -                        resized = true; -                    } -                    WindowEvent::CloseRequested => { -                        *control_flow = ControlFlow::Exit; -                    } -                    WindowEvent::ModifiersChanged(new_modifiers) => { -                        modifiers = new_modifiers; -                    } -                    #[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; -                    } -                    #[cfg(feature = "debug")] -                    WindowEvent::KeyboardInput { -                        input: -                            winit::event::KeyboardInput { -                                virtual_keycode: -                                    Some(winit::event::VirtualKeyCode::F12), -                                state: winit::event::ElementState::Pressed, -                                .. -                            }, -                        .. -                    } => debug.toggle(), -                    _ => {} -                } +            if new_mouse_interaction != mouse_interaction { +                window.set_cursor_icon(conversion::mouse_interaction( +                    new_mouse_interaction, +                )); -                if let Some(event) = conversion::window_event( -                    &window_event, -                    size.scale_factor(), -                    modifiers, -                ) { -                    events.push(event); -                } +                mouse_interaction = new_mouse_interaction;              } -            _ => { -                *control_flow = ControlFlow::Wait; + +            // TODO: Handle animations! +            // Maybe we can use `ControlFlow::WaitUntil` for this. +        } +        event::Event::WindowEvent { +            event: window_event, +            .. +        } => { +            handle_window_event( +                &window_event, +                &window, +                control_flow, +                &mut modifiers, +                &mut viewport, +                &mut resized, +                &mut debug, +            ); + +            if let Some(event) = conversion::window_event( +                &window_event, +                viewport.scale_factor(), +                modifiers, +            ) { +                state.queue_event(event.clone()); +                runtime.broadcast(event);              } -        }) -    } +        } +        _ => { +            *control_flow = ControlFlow::Wait; +        } +    })  } -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, -) -> UserInterface<'a, A::Message, <A::Backend as window::Backend>::Renderer> { -    debug.view_started(); -    let view = application.view(); -    debug.view_finished(); - -    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 +/// Handles a `WindowEvent` and mutates the provided control flow, keyboard +/// modifiers, viewport, and resized flag accordingly. +pub fn handle_window_event( +    event: &winit::event::WindowEvent<'_>, +    window: &winit::window::Window, +    control_flow: &mut winit::event_loop::ControlFlow, +    modifiers: &mut winit::event::ModifiersState, +    viewport: &mut Viewport, +    resized: &mut bool, +    _debug: &mut Debug, +) { +    use winit::{event::WindowEvent, event_loop::ControlFlow}; + +    match event { +        WindowEvent::Resized(new_size) => { +            let size = Size::new(new_size.width, new_size.height); + +            *viewport = +                Viewport::with_physical_size(size, window.scale_factor()); +            *resized = true; +        } +        WindowEvent::CloseRequested => { +            *control_flow = ControlFlow::Exit; +        } +        WindowEvent::ModifiersChanged(new_modifiers) => { +            *modifiers = *new_modifiers; +        } +        #[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; +        } +        #[cfg(feature = "debug")] +        WindowEvent::KeyboardInput { +            input: +                winit::event::KeyboardInput { +                    virtual_keycode: Some(winit::event::VirtualKeyCode::F12), +                    state: winit::event::ElementState::Pressed, +                    .. +                }, +            .. +        } => _debug.toggle(), +        _ => {} +    }  } diff --git a/winit/src/lib.rs b/winit/src/lib.rs index f99e1290..bdab3ed7 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -25,28 +25,18 @@  pub use iced_native::*;  pub use winit; +pub mod application;  pub mod conversion;  pub mod settings; -mod application;  mod clipboard;  mod mode;  mod proxy; -mod size; - -// We disable debug capabilities on release builds unless the `debug` feature -// is explicitly enabled. -#[cfg(feature = "debug")] -#[path = "debug/basic.rs"] -mod debug; -#[cfg(not(feature = "debug"))] -#[path = "debug/null.rs"] -mod debug;  pub use application::Application;  pub use clipboard::Clipboard;  pub use mode::Mode; +pub use proxy::Proxy;  pub use settings::Settings; -use debug::Debug; -use proxy::Proxy; +pub use iced_graphics::Viewport; diff --git a/winit/src/proxy.rs b/winit/src/proxy.rs index cff6ca72..532f8c56 100644 --- a/winit/src/proxy.rs +++ b/winit/src/proxy.rs @@ -5,6 +5,8 @@ use iced_native::futures::{  };  use std::pin::Pin; +/// An event loop proxy that implements `Sink`. +#[derive(Debug)]  pub struct Proxy<Message: 'static> {      raw: winit::event_loop::EventLoopProxy<Message>,  } @@ -18,6 +20,9 @@ impl<Message: 'static> Clone for Proxy<Message> {  }  impl<Message: 'static> Proxy<Message> { +    /// Creates a new [`Proxy`] from an `EventLoopProxy`. +    /// +    /// [`Proxy`]: struct.Proxy.html      pub fn new(raw: winit::event_loop::EventLoopProxy<Message>) -> Self {          Self { raw }      } diff --git a/winit/src/settings.rs b/winit/src/settings.rs index d58c51f0..37cb832f 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -8,6 +8,11 @@ mod platform;  pub use platform::PlatformSpecific; +use crate::conversion; +use crate::Mode; +use winit::monitor::MonitorHandle; +use winit::window::WindowBuilder; +  /// The settings of an application.  #[derive(Debug, Clone, Copy, PartialEq, Default)]  pub struct Settings<Flags> { @@ -38,6 +43,38 @@ pub struct Window {      pub platform_specific: platform::PlatformSpecific,  } +impl Window { +    /// Converts the window settings into a `WindowBuilder` from `winit`. +    pub fn into_builder( +        self, +        title: &str, +        mode: Mode, +        primary_monitor: MonitorHandle, +    ) -> WindowBuilder { +        let mut window_builder = WindowBuilder::new(); + +        let (width, height) = self.size; + +        window_builder = window_builder +            .with_title(title) +            .with_inner_size(winit::dpi::LogicalSize { width, height }) +            .with_resizable(self.resizable) +            .with_decorations(self.decorations) +            .with_fullscreen(conversion::fullscreen(primary_monitor, mode)); + +        #[cfg(target_os = "windows")] +        { +            use winit::platform::windows::WindowBuilderExtWindows; + +            if let Some(parent) = self.platform_specific.parent { +                window_builder = window_builder.with_parent_window(parent); +            } +        } + +        window_builder +    } +} +  impl Default for Window {      fn default() -> Window {          Window { diff --git a/winit/src/size.rs b/winit/src/size.rs deleted file mode 100644 index 7e3056d4..00000000 --- a/winit/src/size.rs +++ /dev/null @@ -1,30 +0,0 @@ -pub struct Size { -    physical: winit::dpi::PhysicalSize<u32>, -    logical: winit::dpi::LogicalSize<f64>, -    scale_factor: f64, -} - -impl Size { -    pub fn new( -        physical: winit::dpi::PhysicalSize<u32>, -        scale_factor: f64, -    ) -> Size { -        Size { -            logical: physical.to_logical(scale_factor), -            physical, -            scale_factor, -        } -    } - -    pub fn physical(&self) -> winit::dpi::PhysicalSize<u32> { -        self.physical -    } - -    pub fn logical(&self) -> winit::dpi::LogicalSize<f64> { -        self.logical -    } - -    pub fn scale_factor(&self) -> f64 { -        self.scale_factor -    } -} | 
