diff options
| author | 2023-12-05 01:03:09 +0100 | |
|---|---|---|
| committer | 2023-12-05 01:03:09 +0100 | |
| commit | fc285d3e461626408c56bbc1605fcf0c974b2f69 (patch) | |
| tree | 8aca292516d9aa43b78a14f51dd90caf60c691d7 /winit | |
| parent | 8727b3fc50ec251d9c117c51ca1289be5ba9b117 (diff) | |
| parent | 5c5e7653bed248ba63faa6563e4d673e4441415e (diff) | |
| download | iced-fc285d3e461626408c56bbc1605fcf0c974b2f69.tar.gz iced-fc285d3e461626408c56bbc1605fcf0c974b2f69.tar.bz2 iced-fc285d3e461626408c56bbc1605fcf0c974b2f69.zip  | |
Merge pull request #1964 from bungoboingo/feat/multi-window-support
[Feature] 🪟 Multi Window 🪟 .. redux!
Diffstat (limited to '')
| -rw-r--r-- | core/src/window/position.rs (renamed from winit/src/position.rs) | 6 | ||||
| -rw-r--r-- | core/src/window/settings/linux.rs (renamed from winit/src/settings/linux.rs) | 0 | ||||
| -rw-r--r-- | core/src/window/settings/macos.rs (renamed from winit/src/settings/macos.rs) | 0 | ||||
| -rw-r--r-- | core/src/window/settings/other.rs (renamed from winit/src/settings/other.rs) | 0 | ||||
| -rw-r--r-- | core/src/window/settings/wasm.rs (renamed from winit/src/settings/wasm.rs) | 0 | ||||
| -rw-r--r-- | core/src/window/settings/windows.rs (renamed from winit/src/settings/windows.rs) | 0 | ||||
| -rw-r--r-- | winit/Cargo.toml | 2 | ||||
| -rw-r--r-- | winit/src/application.rs | 140 | ||||
| -rw-r--r-- | winit/src/application/profiler.rs | 101 | ||||
| -rw-r--r-- | winit/src/conversion.rs | 186 | ||||
| -rw-r--r-- | winit/src/lib.rs | 9 | ||||
| -rw-r--r-- | winit/src/multi_window.rs | 1212 | ||||
| -rw-r--r-- | winit/src/multi_window/state.rs | 240 | ||||
| -rw-r--r-- | winit/src/multi_window/window_manager.rs | 156 | ||||
| -rw-r--r-- | winit/src/settings.rs | 232 | 
15 files changed, 1829 insertions, 455 deletions
diff --git a/winit/src/position.rs b/core/src/window/position.rs index c260c29e..73391e75 100644 --- a/winit/src/position.rs +++ b/core/src/window/position.rs @@ -1,5 +1,7 @@ +use crate::Point; +  /// The position of a window in a given screen. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq)]  pub enum Position {      /// The platform-specific default position for a new window.      Default, @@ -12,7 +14,7 @@ pub enum Position {      /// position. So if you have decorations enabled and want the window to be      /// at (0, 0) you would have to set the position to      /// `(PADDING_X, PADDING_Y)`. -    Specific(i32, i32), +    Specific(Point),  }  impl Default for Position { diff --git a/winit/src/settings/linux.rs b/core/src/window/settings/linux.rs index 009b9d9e..009b9d9e 100644 --- a/winit/src/settings/linux.rs +++ b/core/src/window/settings/linux.rs diff --git a/winit/src/settings/macos.rs b/core/src/window/settings/macos.rs index f86e63ad..f86e63ad 100644 --- a/winit/src/settings/macos.rs +++ b/core/src/window/settings/macos.rs diff --git a/winit/src/settings/other.rs b/core/src/window/settings/other.rs index b1103f62..b1103f62 100644 --- a/winit/src/settings/other.rs +++ b/core/src/window/settings/other.rs diff --git a/winit/src/settings/wasm.rs b/core/src/window/settings/wasm.rs index 8e0f1bbc..8e0f1bbc 100644 --- a/winit/src/settings/wasm.rs +++ b/core/src/window/settings/wasm.rs diff --git a/winit/src/settings/windows.rs b/core/src/window/settings/windows.rs index 45d753bd..45d753bd 100644 --- a/winit/src/settings/windows.rs +++ b/core/src/window/settings/windows.rs diff --git a/winit/Cargo.toml b/winit/Cargo.toml index 674a66d3..87e600ae 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -19,6 +19,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"]  [dependencies]  iced_graphics.workspace = true @@ -26,7 +27,6 @@ iced_runtime.workspace = true  iced_style.workspace = true  log.workspace = true -raw-window-handle.workspace = true  thiserror.workspace = true  tracing.workspace = true  window_clipboard.workspace = true diff --git a/winit/src/application.rs b/winit/src/application.rs index 2c5c864a..d9700075 100644 --- a/winit/src/application.rs +++ b/winit/src/application.rs @@ -1,6 +1,4 @@  //! Create interactive, native cross-platform applications. -#[cfg(feature = "trace")] -mod profiler;  mod state;  pub use state::State; @@ -27,11 +25,6 @@ use futures::channel::mpsc;  use std::mem::ManuallyDrop; -#[cfg(feature = "trace")] -pub use profiler::Profiler; -#[cfg(feature = "trace")] -use tracing::{info_span, instrument::Instrument}; -  /// An interactive, native cross-platform application.  ///  /// This trait is the main entrypoint of Iced. Once implemented, you can run @@ -119,15 +112,9 @@ where      use futures::Future;      use winit::event_loop::EventLoopBuilder; -    #[cfg(feature = "trace")] -    let _guard = Profiler::init(); -      let mut debug = Debug::new();      debug.startup_started(); -    #[cfg(feature = "trace")] -    let _ = info_span!("Application", "RUN").entered(); -      let event_loop = EventLoopBuilder::with_user_event().build();      let proxy = event_loop.create_proxy(); @@ -148,14 +135,15 @@ where      let target = settings.window.platform_specific.target.clone();      let should_be_visible = settings.window.visible; -    let builder = settings -        .window -        .into_builder( -            &application.title(), -            event_loop.primary_monitor(), -            settings.id, -        ) -        .with_visible(false); +    let exit_on_close_request = settings.window.exit_on_close_request; + +    let builder = conversion::window_settings( +        settings.window, +        &application.title(), +        event_loop.primary_monitor(), +        settings.id, +    ) +    .with_visible(false);      log::debug!("Window builder: {builder:#?}"); @@ -193,8 +181,8 @@ where          };      } -    let (compositor, mut renderer) = -        C::new(compositor_settings, Some(&window))?; +    let compositor = C::new(compositor_settings, Some(&window))?; +    let mut renderer = compositor.create_renderer();      for font in settings.fonts {          use crate::core::text::Renderer; @@ -205,28 +193,20 @@ where      let (mut event_sender, event_receiver) = mpsc::unbounded();      let (control_sender, mut control_receiver) = mpsc::unbounded(); -    let mut instance = Box::pin({ -        let run_instance = run_instance::<A, E, C>( -            application, -            compositor, -            renderer, -            runtime, -            proxy, -            debug, -            event_receiver, -            control_sender, -            init_command, -            window, -            should_be_visible, -            settings.exit_on_close_request, -        ); - -        #[cfg(feature = "trace")] -        let run_instance = -            run_instance.instrument(info_span!("Application", "LOOP")); - -        run_instance -    }); +    let mut instance = Box::pin(run_instance::<A, E, C>( +        application, +        compositor, +        renderer, +        runtime, +        proxy, +        debug, +        event_receiver, +        control_sender, +        init_command, +        window, +        should_be_visible, +        exit_on_close_request, +    ));      let mut context = task::Context::from_waker(task::noop_waker_ref()); @@ -426,6 +406,7 @@ async fn run_instance<A, E, C>(                  // 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 redraw_event = Event::Window( +                    window::Id::MAIN,                      window::Event::RedrawRequested(Instant::now()),                  ); @@ -488,9 +469,6 @@ async fn run_instance<A, E, C>(                  messages.push(message);              }              event::Event::RedrawRequested(_) => { -                #[cfg(feature = "trace")] -                let _ = info_span!("Application", "FRAME").entered(); -                  let physical_size = state.physical_size();                  if physical_size.width == 0 || physical_size.height == 0 { @@ -578,6 +556,7 @@ async fn run_instance<A, E, C>(                  state.update(&window, &window_event, &mut debug);                  if let Some(event) = conversion::window_event( +                    window::Id::MAIN,                      &window_event,                      state.scale_factor(),                      state.modifiers(), @@ -629,24 +608,12 @@ pub fn build_user_interface<'a, A: Application>(  where      <A::Renderer as core::Renderer>::Theme: StyleSheet,  { -    #[cfg(feature = "trace")] -    let view_span = info_span!("Application", "VIEW").entered(); -      debug.view_started();      let view = application.view(); - -    #[cfg(feature = "trace")] -    let _ = view_span.exit();      debug.view_finished(); -    #[cfg(feature = "trace")] -    let layout_span = info_span!("Application", "LAYOUT").entered(); -      debug.layout_started();      let user_interface = UserInterface::build(view, size, cache, renderer); - -    #[cfg(feature = "trace")] -    let _ = layout_span.exit();      debug.layout_finished();      user_interface @@ -673,16 +640,10 @@ pub fn update<A: Application, C, E: Executor>(      <A::Renderer as core::Renderer>::Theme: StyleSheet,  {      for message in messages.drain(..) { -        #[cfg(feature = "trace")] -        let update_span = info_span!("Application", "UPDATE").entered(); -          debug.log_message(&message);          debug.update_started();          let command = runtime.enter(|| application.update(message)); - -        #[cfg(feature = "trace")] -        let _ = update_span.exit();          debug.update_finished();          run_command( @@ -752,20 +713,27 @@ pub fn run_command<A, C, E>(                  }              },              command::Action::Window(action) => match action { -                window::Action::Close => { +                window::Action::Close(_id) => {                      *should_exit = true;                  } -                window::Action::Drag => { +                window::Action::Drag(_id) => {                      let _res = window.drag_window();                  } -                window::Action::Resize(size) => { +                window::Action::Spawn { .. } => { +                    log::warn!( +                        "Spawning a window is only available with \ +                        multi-window applications." +                    ); +                } +                window::Action::Resize(_id, size) => {                      window.set_inner_size(winit::dpi::LogicalSize {                          width: size.width,                          height: size.height,                      });                  } -                window::Action::FetchSize(callback) => { -                    let size = window.inner_size(); +                window::Action::FetchSize(_id, callback) => { +                    let size = +                        window.inner_size().to_logical(window.scale_factor());                      proxy                          .send_event(callback(Size::new( @@ -774,29 +742,29 @@ pub fn run_command<A, C, E>(                          )))                          .expect("Send message to event loop");                  } -                window::Action::Maximize(maximized) => { +                window::Action::Maximize(_id, maximized) => {                      window.set_maximized(maximized);                  } -                window::Action::Minimize(minimized) => { +                window::Action::Minimize(_id, minimized) => {                      window.set_minimized(minimized);                  } -                window::Action::Move { x, y } => { +                window::Action::Move(_id, position) => {                      window.set_outer_position(winit::dpi::LogicalPosition { -                        x, -                        y, +                        x: position.x, +                        y: position.y,                      });                  } -                window::Action::ChangeMode(mode) => { +                window::Action::ChangeMode(_id, mode) => {                      window.set_visible(conversion::visible(mode));                      window.set_fullscreen(conversion::fullscreen(                          window.current_monitor(),                          mode,                      ));                  } -                window::Action::ChangeIcon(icon) => { +                window::Action::ChangeIcon(_id, icon) => {                      window.set_window_icon(conversion::icon(icon));                  } -                window::Action::FetchMode(tag) => { +                window::Action::FetchMode(_id, tag) => {                      let mode = if window.is_visible().unwrap_or(true) {                          conversion::mode(window.fullscreen())                      } else { @@ -807,29 +775,29 @@ pub fn run_command<A, C, E>(                          .send_event(tag(mode))                          .expect("Send message to event loop");                  } -                window::Action::ToggleMaximize => { +                window::Action::ToggleMaximize(_id) => {                      window.set_maximized(!window.is_maximized());                  } -                window::Action::ToggleDecorations => { +                window::Action::ToggleDecorations(_id) => {                      window.set_decorations(!window.is_decorated());                  } -                window::Action::RequestUserAttention(user_attention) => { +                window::Action::RequestUserAttention(_id, user_attention) => {                      window.request_user_attention(                          user_attention.map(conversion::user_attention),                      );                  } -                window::Action::GainFocus => { +                window::Action::GainFocus(_id) => {                      window.focus_window();                  } -                window::Action::ChangeLevel(level) => { +                window::Action::ChangeLevel(_id, level) => {                      window.set_window_level(conversion::window_level(level));                  } -                window::Action::FetchId(tag) => { +                window::Action::FetchId(_id, tag) => {                      proxy                          .send_event(tag(window.id().into()))                          .expect("Send message to event loop");                  } -                window::Action::Screenshot(tag) => { +                window::Action::Screenshot(_id, tag) => {                      let bytes = compositor.screenshot(                          renderer,                          surface, diff --git a/winit/src/application/profiler.rs b/winit/src/application/profiler.rs deleted file mode 100644 index 7031507a..00000000 --- a/winit/src/application/profiler.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! A simple profiler for Iced. -use std::ffi::OsStr; -use std::path::Path; -use std::time::Duration; -use tracing_subscriber::prelude::*; -use tracing_subscriber::Registry; -#[cfg(feature = "chrome-trace")] -use { -    tracing_chrome::FlushGuard, -    tracing_subscriber::fmt::{format::DefaultFields, FormattedFields}, -}; - -/// Profiler state. This will likely need to be updated or reworked when adding new tracing backends. -#[allow(missing_debug_implementations)] -pub struct Profiler { -    #[cfg(feature = "chrome-trace")] -    /// [`FlushGuard`] must not be dropped until the application scope is dropped for accurate tracing. -    _guard: FlushGuard, -} - -impl Profiler { -    /// Initializes the [`Profiler`]. -    pub fn init() -> Self { -        // Registry stores the spans & generates unique span IDs -        let subscriber = Registry::default(); - -        let default_path = Path::new(env!("CARGO_MANIFEST_DIR")); -        let curr_exe = std::env::current_exe() -            .unwrap_or_else(|_| default_path.to_path_buf()); -        let out_dir = curr_exe.parent().unwrap_or(default_path).join("traces"); - -        #[cfg(feature = "chrome-trace")] -        let (chrome_layer, guard) = { -            let mut layer = tracing_chrome::ChromeLayerBuilder::new(); - -            // Optional configurable env var: CHROME_TRACE_FILE=/path/to/trace_file/file.json, -            // for uploading to chrome://tracing (old) or ui.perfetto.dev (new). -            if let Ok(path) = std::env::var("CHROME_TRACE_FILE") { -                layer = layer.file(path); -            } else if std::fs::create_dir_all(&out_dir).is_ok() { -                let time = std::time::SystemTime::now() -                    .duration_since(std::time::UNIX_EPOCH) -                    .unwrap_or(Duration::from_millis(0)) -                    .as_millis(); - -                let curr_exe_name = curr_exe -                    .file_name() -                    .unwrap_or_else(|| OsStr::new("trace")) -                    .to_str() -                    .unwrap_or("trace"); - -                let path = -                    out_dir.join(format!("{curr_exe_name}_trace_{time}.json")); - -                layer = layer.file(path); -            } else { -                layer = layer.file(env!("CARGO_MANIFEST_DIR")) -            } - -            let (chrome_layer, guard) = layer -                .name_fn(Box::new(|event_or_span| match event_or_span { -                    tracing_chrome::EventOrSpan::Event(event) => { -                        event.metadata().name().into() -                    } -                    tracing_chrome::EventOrSpan::Span(span) => { -                        if let Some(fields) = span -                            .extensions() -                            .get::<FormattedFields<DefaultFields>>() -                        { -                            format!( -                                "{}: {}", -                                span.metadata().name(), -                                fields.fields.as_str() -                            ) -                        } else { -                            span.metadata().name().into() -                        } -                    } -                })) -                .build(); - -            (chrome_layer, guard) -        }; - -        let fmt_layer = tracing_subscriber::fmt::Layer::default(); -        let subscriber = subscriber.with(fmt_layer); - -        #[cfg(feature = "chrome-trace")] -        let subscriber = subscriber.with(chrome_layer); - -        // create dispatcher which will forward span events to the subscriber -        // this can only be set once or will panic -        tracing::subscriber::set_global_default(subscriber) -            .expect("Tracer could not set the global default subscriber."); - -        Profiler { -            #[cfg(feature = "chrome-trace")] -            _guard: guard, -        } -    } -} diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index 3ecd044c..7e51a2d4 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -6,11 +6,128 @@ use crate::core::keyboard;  use crate::core::mouse;  use crate::core::touch;  use crate::core::window; -use crate::core::{Event, Point}; -use crate::Position; +use crate::core::{Event, Point, Size}; + +/// Converts some [`window::Settings`] into a `WindowBuilder` from `winit`. +pub fn window_settings( +    settings: window::Settings, +    title: &str, +    primary_monitor: Option<winit::monitor::MonitorHandle>, +    _id: Option<String>, +) -> winit::window::WindowBuilder { +    let mut window_builder = winit::window::WindowBuilder::new(); + +    window_builder = window_builder +        .with_title(title) +        .with_inner_size(winit::dpi::LogicalSize { +            width: settings.size.width, +            height: settings.size.height, +        }) +        .with_resizable(settings.resizable) +        .with_enabled_buttons(if settings.resizable { +            winit::window::WindowButtons::all() +        } else { +            winit::window::WindowButtons::CLOSE +                | winit::window::WindowButtons::MINIMIZE +        }) +        .with_decorations(settings.decorations) +        .with_transparent(settings.transparent) +        .with_window_icon(settings.icon.and_then(icon)) +        .with_window_level(window_level(settings.level)) +        .with_visible(settings.visible); + +    if let Some(position) = +        position(primary_monitor.as_ref(), settings.size, settings.position) +    { +        window_builder = window_builder.with_position(position); +    } + +    if let Some(min_size) = settings.min_size { +        window_builder = +            window_builder.with_min_inner_size(winit::dpi::LogicalSize { +                width: min_size.width, +                height: min_size.height, +            }); +    } + +    if let Some(max_size) = settings.max_size { +        window_builder = +            window_builder.with_max_inner_size(winit::dpi::LogicalSize { +                width: max_size.width, +                height: max_size.height, +            }); +    } + +    #[cfg(any( +        target_os = "dragonfly", +        target_os = "freebsd", +        target_os = "netbsd", +        target_os = "openbsd" +    ))] +    { +        // `with_name` is available on both `WindowBuilderExtWayland` and `WindowBuilderExtX11` and they do +        // exactly the same thing. We arbitrarily choose `WindowBuilderExtWayland` here. +        use ::winit::platform::wayland::WindowBuilderExtWayland; + +        if let Some(id) = _id { +            window_builder = window_builder.with_name(id.clone(), id); +        } +    } + +    #[cfg(target_os = "windows")] +    { +        use winit::platform::windows::WindowBuilderExtWindows; +        #[allow(unsafe_code)] +        unsafe { +            window_builder = window_builder +                .with_parent_window(settings.platform_specific.parent); +        } +        window_builder = window_builder +            .with_drag_and_drop(settings.platform_specific.drag_and_drop); +    } + +    #[cfg(target_os = "macos")] +    { +        use winit::platform::macos::WindowBuilderExtMacOS; + +        window_builder = window_builder +            .with_title_hidden(settings.platform_specific.title_hidden) +            .with_titlebar_transparent( +                settings.platform_specific.titlebar_transparent, +            ) +            .with_fullsize_content_view( +                settings.platform_specific.fullsize_content_view, +            ); +    } + +    #[cfg(target_os = "linux")] +    { +        #[cfg(feature = "x11")] +        { +            use winit::platform::x11::WindowBuilderExtX11; + +            window_builder = window_builder.with_name( +                &settings.platform_specific.application_id, +                &settings.platform_specific.application_id, +            ); +        } +        #[cfg(feature = "wayland")] +        { +            use winit::platform::wayland::WindowBuilderExtWayland; + +            window_builder = window_builder.with_name( +                &settings.platform_specific.application_id, +                &settings.platform_specific.application_id, +            ); +        } +    } + +    window_builder +}  /// Converts a winit window event into an iced event.  pub fn window_event( +    id: window::Id,      event: &winit::event::WindowEvent<'_>,      scale_factor: f64,      modifiers: winit::event::ModifiersState, @@ -21,21 +138,27 @@ pub fn window_event(          WindowEvent::Resized(new_size) => {              let logical_size = new_size.to_logical(scale_factor); -            Some(Event::Window(window::Event::Resized { -                width: logical_size.width, -                height: logical_size.height, -            })) +            Some(Event::Window( +                id, +                window::Event::Resized { +                    width: logical_size.width, +                    height: logical_size.height, +                }, +            ))          }          WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {              let logical_size = new_inner_size.to_logical(scale_factor); -            Some(Event::Window(window::Event::Resized { -                width: logical_size.width, -                height: logical_size.height, -            })) +            Some(Event::Window( +                id, +                window::Event::Resized { +                    width: logical_size.width, +                    height: logical_size.height, +                }, +            ))          }          WindowEvent::CloseRequested => { -            Some(Event::Window(window::Event::CloseRequested)) +            Some(Event::Window(id, window::Event::CloseRequested))          }          WindowEvent::CursorMoved { position, .. } => {              let position = position.to_logical::<f64>(scale_factor); @@ -113,19 +236,22 @@ pub fn window_event(          WindowEvent::ModifiersChanged(new_modifiers) => Some(Event::Keyboard(              keyboard::Event::ModifiersChanged(self::modifiers(*new_modifiers)),          )), -        WindowEvent::Focused(focused) => Some(Event::Window(if *focused { -            window::Event::Focused -        } else { -            window::Event::Unfocused -        })), +        WindowEvent::Focused(focused) => Some(Event::Window( +            id, +            if *focused { +                window::Event::Focused +            } else { +                window::Event::Unfocused +            }, +        )),          WindowEvent::HoveredFile(path) => { -            Some(Event::Window(window::Event::FileHovered(path.clone()))) +            Some(Event::Window(id, window::Event::FileHovered(path.clone())))          }          WindowEvent::DroppedFile(path) => { -            Some(Event::Window(window::Event::FileDropped(path.clone()))) +            Some(Event::Window(id, window::Event::FileDropped(path.clone())))          }          WindowEvent::HoveredFileCancelled => { -            Some(Event::Window(window::Event::FilesHoveredLeft)) +            Some(Event::Window(id, window::Event::FilesHoveredLeft))          }          WindowEvent::Touch(touch) => {              Some(Event::Touch(touch_event(*touch, scale_factor))) @@ -134,7 +260,7 @@ pub fn window_event(              let winit::dpi::LogicalPosition { x, y } =                  position.to_logical(scale_factor); -            Some(Event::Window(window::Event::Moved { x, y })) +            Some(Event::Window(id, window::Event::Moved { x, y }))          }          _ => None,      } @@ -153,23 +279,23 @@ pub fn window_level(level: window::Level) -> winit::window::WindowLevel {      }  } -/// Converts a [`Position`] to a [`winit`] logical position for a given monitor. +/// Converts a [`window::Position`] to a [`winit`] logical position for a given monitor.  ///  /// [`winit`]: https://github.com/rust-windowing/winit  pub fn position(      monitor: Option<&winit::monitor::MonitorHandle>, -    (width, height): (u32, u32), -    position: Position, +    size: Size, +    position: window::Position,  ) -> Option<winit::dpi::Position> {      match position { -        Position::Default => None, -        Position::Specific(x, y) => { +        window::Position::Default => None, +        window::Position::Specific(position) => {              Some(winit::dpi::Position::Logical(winit::dpi::LogicalPosition { -                x: f64::from(x), -                y: f64::from(y), +                x: f64::from(position.x), +                y: f64::from(position.y),              }))          } -        Position::Centered => { +        window::Position::Centered => {              if let Some(monitor) = monitor {                  let start = monitor.position(); @@ -178,8 +304,8 @@ pub fn position(                  let centered: winit::dpi::PhysicalPosition<i32> =                      winit::dpi::LogicalPosition { -                        x: (resolution.width - f64::from(width)) / 2.0, -                        y: (resolution.height - f64::from(height)) / 2.0, +                        x: (resolution.width - f64::from(size.width)) / 2.0, +                        y: (resolution.height - f64::from(size.height)) / 2.0,                      }                      .to_physical(monitor.scale_factor()); diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 95b55bb9..948576a2 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -33,6 +33,9 @@ pub use iced_runtime::futures;  pub use iced_style as style;  pub use winit; +#[cfg(feature = "multi-window")] +pub mod multi_window; +  #[cfg(feature = "application")]  pub mod application;  pub mod clipboard; @@ -43,17 +46,11 @@ pub mod settings;  pub mod system;  mod error; -mod position;  mod proxy;  #[cfg(feature = "application")]  pub use application::Application; -#[cfg(feature = "trace")] -pub use application::Profiler;  pub use clipboard::Clipboard;  pub use error::Error; -pub use position::Position;  pub use proxy::Proxy;  pub use settings::Settings; - -pub use iced_graphics::Viewport; diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs new file mode 100644 index 00000000..84651d40 --- /dev/null +++ b/winit/src/multi_window.rs @@ -0,0 +1,1212 @@ +//! Create interactive, native cross-platform applications for WGPU. +mod state; +mod window_manager; + +pub use state::State; + +use crate::conversion; +use crate::core; +use crate::core::renderer; +use crate::core::widget::operation; +use crate::core::window; +use crate::core::Size; +use crate::futures::futures::channel::mpsc; +use crate::futures::futures::{task, Future, StreamExt}; +use crate::futures::{Executor, Runtime, Subscription}; +use crate::graphics::{compositor, Compositor}; +use crate::multi_window::window_manager::WindowManager; +use crate::runtime::command::{self, Command}; +use crate::runtime::multi_window::Program; +use crate::runtime::user_interface::{self, UserInterface}; +use crate::runtime::Debug; +use crate::style::application::StyleSheet; +use crate::{Clipboard, Error, Proxy, Settings}; + +use std::collections::HashMap; +use std::mem::ManuallyDrop; +use std::time::Instant; + +/// An interactive, native, cross-platform, multi-windowed application. +/// +/// This trait is the main entrypoint of multi-window Iced. Once implemented, you can run +/// your GUI application by simply calling [`run`]. It will run in +/// its own window. +/// +/// An [`Application`] can execute asynchronous actions by returning a +/// [`Command`] in some of its methods. +/// +/// When using an [`Application`] with the `debug` feature enabled, a debug view +/// can be toggled by pressing `F12`. +pub trait Application: Program +where +    <Self::Renderer as core::Renderer>::Theme: StyleSheet, +{ +    /// The data needed to initialize your [`Application`]. +    type Flags; + +    /// Initializes the [`Application`] with the flags provided to +    /// [`run`] as part of the [`Settings`]. +    /// +    /// Here is where you should return the initial state of your app. +    /// +    /// Additionally, you can return a [`Command`] if you need to perform some +    /// async action in the background on startup. This is useful if you want to +    /// load state from a file, perform an initial HTTP request, etc. +    fn new(flags: Self::Flags) -> (Self, Command<Self::Message>); + +    /// Returns the current title of the [`Application`]. +    /// +    /// This title can be dynamic! The runtime will automatically update the +    /// title of your application when necessary. +    fn title(&self, window: window::Id) -> String; + +    /// Returns the current `Theme` of the [`Application`]. +    fn theme( +        &self, +        window: window::Id, +    ) -> <Self::Renderer as core::Renderer>::Theme; + +    /// Returns the `Style` variation of the `Theme`. +    fn style( +        &self, +    ) -> <<Self::Renderer as core::Renderer>::Theme as StyleSheet>::Style { +        Default::default() +    } + +    /// 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! +    /// +    /// By default, it returns an empty subscription. +    fn subscription(&self) -> Subscription<Self::Message> { +        Subscription::none() +    } + +    /// Returns the scale factor of the window of the [`Application`]. +    /// +    /// It can be used to dynamically control the size of the UI at runtime +    /// (i.e. zooming). +    /// +    /// For instance, a scale factor of `2.0` will make widgets twice as big, +    /// while a scale factor of `0.5` will shrink them to half their size. +    /// +    /// By default, it returns `1.0`. +    #[allow(unused_variables)] +    fn scale_factor(&self, window: window::Id) -> f64 { +        1.0 +    } +} + +/// Runs an [`Application`] with an executor, compositor, and the provided +/// settings. +pub fn run<A, E, C>( +    settings: Settings<A::Flags>, +    compositor_settings: C::Settings, +) -> Result<(), Error> +where +    A: Application + 'static, +    E: Executor + 'static, +    C: Compositor<Renderer = A::Renderer> + 'static, +    <A::Renderer as core::Renderer>::Theme: StyleSheet, +{ +    use winit::event_loop::EventLoopBuilder; + +    let mut debug = Debug::new(); +    debug.startup_started(); + +    let event_loop = EventLoopBuilder::with_user_event().build(); +    let proxy = event_loop.create_proxy(); + +    let runtime = { +        let proxy = Proxy::new(event_loop.create_proxy()); +        let executor = E::new().map_err(Error::ExecutorCreationFailed)?; + +        Runtime::new(executor, proxy) +    }; + +    let (application, init_command) = { +        let flags = settings.flags; + +        runtime.enter(|| A::new(flags)) +    }; + +    let should_main_be_visible = settings.window.visible; +    let exit_on_close_request = settings.window.exit_on_close_request; + +    let builder = conversion::window_settings( +        settings.window, +        &application.title(window::Id::MAIN), +        event_loop.primary_monitor(), +        settings.id, +    ) +    .with_visible(false); + +    log::info!("Window builder: {:#?}", builder); + +    let main_window = builder +        .build(&event_loop) +        .map_err(Error::WindowCreationFailed)?; + +    #[cfg(target_arch = "wasm32")] +    { +        use winit::platform::web::WindowExtWebSys; + +        let canvas = main_window.canvas(); + +        let window = web_sys::window().unwrap(); +        let document = window.document().unwrap(); +        let body = document.body().unwrap(); + +        let target = target.and_then(|target| { +            body.query_selector(&format!("#{}", target)) +                .ok() +                .unwrap_or(None) +        }); + +        match target { +            Some(node) => { +                let _ = node +                    .replace_with_with_node_1(&canvas) +                    .expect(&format!("Could not replace #{}", node.id())); +            } +            None => { +                let _ = body +                    .append_child(&canvas) +                    .expect("Append canvas to HTML body"); +            } +        }; +    } + +    let mut compositor = C::new(compositor_settings, Some(&main_window))?; + +    let mut window_manager = WindowManager::new(); +    let _ = window_manager.insert( +        window::Id::MAIN, +        main_window, +        &application, +        &mut compositor, +        exit_on_close_request, +    ); + +    let (mut event_sender, event_receiver) = mpsc::unbounded(); +    let (control_sender, mut control_receiver) = mpsc::unbounded(); + +    let mut instance = Box::pin(run_instance::<A, E, C>( +        application, +        compositor, +        runtime, +        proxy, +        debug, +        event_receiver, +        control_sender, +        init_command, +        window_manager, +        should_main_be_visible, +    )); + +    let mut context = task::Context::from_waker(task::noop_waker_ref()); + +    platform::run(event_loop, move |event, window_target, control_flow| { +        use winit::event_loop::ControlFlow; + +        if let ControlFlow::ExitWithCode(_) = control_flow { +            return; +        } + +        let event = match event { +            winit::event::Event::WindowEvent { +                event: +                    winit::event::WindowEvent::ScaleFactorChanged { +                        new_inner_size, +                        .. +                    }, +                window_id, +            } => Some(winit::event::Event::WindowEvent { +                event: winit::event::WindowEvent::Resized(*new_inner_size), +                window_id, +            }), +            _ => event.to_static(), +        }; + +        if let Some(event) = event { +            event_sender +                .start_send(Event::EventLoopAwakened(event)) +                .expect("Send event"); + +            loop { +                let poll = instance.as_mut().poll(&mut context); + +                match poll { +                    task::Poll::Pending => match control_receiver.try_next() { +                        Ok(Some(control)) => match control { +                            Control::ChangeFlow(flow) => { +                                *control_flow = flow; +                            } +                            Control::CreateWindow { +                                id, +                                settings, +                                title, +                                monitor, +                            } => { +                                let exit_on_close_request = +                                    settings.exit_on_close_request; + +                                let window = conversion::window_settings( +                                    settings, &title, monitor, None, +                                ) +                                .build(window_target) +                                .expect("Failed to build window"); + +                                event_sender +                                    .start_send(Event::WindowCreated { +                                        id, +                                        window, +                                        exit_on_close_request, +                                    }) +                                    .expect("Send event"); +                            } +                        }, +                        _ => { +                            break; +                        } +                    }, +                    task::Poll::Ready(_) => { +                        *control_flow = ControlFlow::Exit; +                        break; +                    } +                }; +            } +        } +    }) +} + +enum Event<Message: 'static> { +    WindowCreated { +        id: window::Id, +        window: winit::window::Window, +        exit_on_close_request: bool, +    }, +    EventLoopAwakened(winit::event::Event<'static, Message>), +} + +enum Control { +    ChangeFlow(winit::event_loop::ControlFlow), +    CreateWindow { +        id: window::Id, +        settings: window::Settings, +        title: String, +        monitor: Option<winit::monitor::MonitorHandle>, +    }, +} + +async fn run_instance<A, E, C>( +    mut application: A, +    mut compositor: C, +    mut runtime: Runtime<E, Proxy<A::Message>, A::Message>, +    mut proxy: winit::event_loop::EventLoopProxy<A::Message>, +    mut debug: Debug, +    mut event_receiver: mpsc::UnboundedReceiver<Event<A::Message>>, +    mut control_sender: mpsc::UnboundedSender<Control>, +    init_command: Command<A::Message>, +    mut window_manager: WindowManager<A, C>, +    should_main_window_be_visible: bool, +) where +    A: Application + 'static, +    E: Executor + 'static, +    C: Compositor<Renderer = A::Renderer> + 'static, +    <A::Renderer as core::Renderer>::Theme: StyleSheet, +{ +    use winit::event; +    use winit::event_loop::ControlFlow; + +    let main_window = window_manager +        .get_mut(window::Id::MAIN) +        .expect("Get main window"); + +    if should_main_window_be_visible { +        main_window.raw.set_visible(true); +    } + +    let mut clipboard = Clipboard::connect(&main_window.raw); +    let mut events = { +        vec![( +            Some(window::Id::MAIN), +            core::Event::Window( +                window::Id::MAIN, +                window::Event::Opened { +                    position: main_window.position(), +                    size: main_window.size(), +                }, +            ), +        )] +    }; + +    let mut ui_caches = HashMap::new(); +    let mut user_interfaces = ManuallyDrop::new(build_user_interfaces( +        &application, +        &mut debug, +        &mut window_manager, +        HashMap::from_iter([( +            window::Id::MAIN, +            user_interface::Cache::default(), +        )]), +    )); + +    run_command( +        &application, +        &mut compositor, +        init_command, +        &mut runtime, +        &mut clipboard, +        &mut control_sender, +        &mut proxy, +        &mut debug, +        &mut window_manager, +        &mut ui_caches, +    ); + +    runtime.track(application.subscription().into_recipes()); + +    let mut messages = Vec::new(); +    let mut redraw_pending = false; + +    debug.startup_finished(); + +    'main: while let Some(event) = event_receiver.next().await { +        match event { +            Event::WindowCreated { +                id, +                window, +                exit_on_close_request, +            } => { +                let window = window_manager.insert( +                    id, +                    window, +                    &application, +                    &mut compositor, +                    exit_on_close_request, +                ); + +                let logical_size = window.state.logical_size(); + +                let _ = user_interfaces.insert( +                    id, +                    build_user_interface( +                        &application, +                        user_interface::Cache::default(), +                        &mut window.renderer, +                        logical_size, +                        &mut debug, +                        id, +                    ), +                ); +                let _ = ui_caches.insert(id, user_interface::Cache::default()); + +                events.push(( +                    Some(id), +                    core::Event::Window( +                        id, +                        window::Event::Opened { +                            position: window.position(), +                            size: window.size(), +                        }, +                    ), +                )); +            } +            Event::EventLoopAwakened(event) => { +                match event { +                    event::Event::NewEvents(start_cause) => { +                        redraw_pending = matches!( +                            start_cause, +                            event::StartCause::Init +                                | event::StartCause::Poll +                                | event::StartCause::ResumeTimeReached { .. } +                        ); +                    } +                    event::Event::MainEventsCleared => { +                        debug.event_processing_started(); +                        let mut uis_stale = false; + +                        for (id, window) in window_manager.iter_mut() { +                            let mut window_events = vec![]; + +                            events.retain(|(window_id, event)| { +                                if *window_id == Some(id) || window_id.is_none() +                                { +                                    window_events.push(event.clone()); +                                    false +                                } else { +                                    true +                                } +                            }); + +                            if !redraw_pending +                                && window_events.is_empty() +                                && messages.is_empty() +                            { +                                continue; +                            } + +                            let (ui_state, statuses) = user_interfaces +                                .get_mut(&id) +                                .expect("Get user interface") +                                .update( +                                    &window_events, +                                    window.state.cursor(), +                                    &mut window.renderer, +                                    &mut clipboard, +                                    &mut messages, +                                ); + +                            if !uis_stale { +                                uis_stale = matches!( +                                    ui_state, +                                    user_interface::State::Outdated +                                ); +                            } + +                            for (event, status) in window_events +                                .into_iter() +                                .zip(statuses.into_iter()) +                            { +                                runtime.broadcast(event, status); +                            } +                        } + +                        debug.event_processing_finished(); + +                        // TODO mw application update returns which window IDs to update +                        if !messages.is_empty() || uis_stale { +                            let mut cached_interfaces: HashMap< +                                window::Id, +                                user_interface::Cache, +                            > = ManuallyDrop::into_inner(user_interfaces) +                                .drain() +                                .map(|(id, ui)| (id, ui.into_cache())) +                                .collect(); + +                            // Update application +                            update( +                                &mut application, +                                &mut compositor, +                                &mut runtime, +                                &mut clipboard, +                                &mut control_sender, +                                &mut proxy, +                                &mut debug, +                                &mut messages, +                                &mut window_manager, +                                &mut cached_interfaces, +                            ); + +                            // we must synchronize all window states with application state after an +                            // application update since we don't know what changed +                            for (id, window) in window_manager.iter_mut() { +                                window.state.synchronize( +                                    &application, +                                    id, +                                    &window.raw, +                                ); +                            } + +                            // rebuild UIs with the synchronized states +                            user_interfaces = +                                ManuallyDrop::new(build_user_interfaces( +                                    &application, +                                    &mut debug, +                                    &mut window_manager, +                                    cached_interfaces, +                                )); +                        } + +                        debug.draw_started(); + +                        for (id, window) in window_manager.iter_mut() { +                            // 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 redraw_event = core::Event::Window( +                                id, +                                window::Event::RedrawRequested(Instant::now()), +                            ); + +                            let cursor = window.state.cursor(); + +                            let ui = user_interfaces +                                .get_mut(&id) +                                .expect("Get user interface"); + +                            let (ui_state, _) = ui.update( +                                &[redraw_event.clone()], +                                cursor, +                                &mut window.renderer, +                                &mut clipboard, +                                &mut messages, +                            ); + +                            let new_mouse_interaction = { +                                let state = &window.state; + +                                ui.draw( +                                    &mut window.renderer, +                                    state.theme(), +                                    &renderer::Style { +                                        text_color: state.text_color(), +                                    }, +                                    cursor, +                                ) +                            }; + +                            if new_mouse_interaction != window.mouse_interaction +                            { +                                window.raw.set_cursor_icon( +                                    conversion::mouse_interaction( +                                        new_mouse_interaction, +                                    ), +                                ); + +                                window.mouse_interaction = +                                    new_mouse_interaction; +                            } + +                            // TODO once widgets can request to be redrawn, we can avoid always requesting a +                            // redraw +                            window.raw.request_redraw(); + +                            runtime.broadcast( +                                redraw_event.clone(), +                                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 => { +                                            ControlFlow::Poll +                                        } +                                        window::RedrawRequest::At(at) => { +                                            ControlFlow::WaitUntil(at) +                                        } +                                    }, +                                    _ => ControlFlow::Wait, +                                }), +                            ); +                        } + +                        redraw_pending = false; + +                        debug.draw_finished(); +                    } +                    event::Event::PlatformSpecific( +                        event::PlatformSpecific::MacOS( +                            event::MacOS::ReceivedUrl(url), +                        ), +                    ) => { +                        use crate::core::event; + +                        events.push(( +                            None, +                            event::Event::PlatformSpecific( +                                event::PlatformSpecific::MacOS( +                                    event::MacOS::ReceivedUrl(url), +                                ), +                            ), +                        )); +                    } +                    event::Event::UserEvent(message) => { +                        messages.push(message); +                    } +                    event::Event::RedrawRequested(id) => { +                        let Some((id, window)) = +                            window_manager.get_mut_alias(id) +                        else { +                            continue; +                        }; + +                        let physical_size = window.state.physical_size(); + +                        if physical_size.width == 0 || physical_size.height == 0 +                        { +                            continue; +                        } + +                        debug.render_started(); +                        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(), +                                ); + +                            if new_mouse_interaction != window.mouse_interaction +                            { +                                window.raw.set_cursor_icon( +                                    conversion::mouse_interaction( +                                        new_mouse_interaction, +                                    ), +                                ); + +                                window.mouse_interaction = +                                    new_mouse_interaction; +                            } +                            debug.draw_finished(); + +                            compositor.configure_surface( +                                &mut window.surface, +                                physical_size.width, +                                physical_size.height, +                            ); + +                            window.viewport_version = +                                window.state.viewport_version(); +                        } + +                        match compositor.present( +                            &mut window.renderer, +                            &mut window.surface, +                            window.state.viewport(), +                            window.state.background_color(), +                            &debug.overlay(), +                        ) { +                            Ok(()) => { +                                debug.render_finished(); + +                                // TODO: Handle animations! +                                // Maybe we can use `ControlFlow::WaitUntil` for this. +                            } +                            Err(error) => match error { +                                // This is an unrecoverable error. +                                compositor::SurfaceError::OutOfMemory => { +                                    panic!("{:?}", error); +                                } +                                _ => { +                                    debug.render_finished(); +                                    log::error!( +                                "Error {error:?} when presenting surface." +                            ); + +                                    // Try rendering all windows again next frame. +                                    for (_id, window) in +                                        window_manager.iter_mut() +                                    { +                                        window.raw.request_redraw(); +                                    } +                                } +                            }, +                        } +                    } +                    event::Event::WindowEvent { +                        event: window_event, +                        window_id, +                    } => { +                        let Some((id, window)) = +                            window_manager.get_mut_alias(window_id) +                        else { +                            continue; +                        }; + +                        if matches!( +                            window_event, +                            winit::event::WindowEvent::CloseRequested +                        ) && window.exit_on_close_request +                        { +                            let _ = window_manager.remove(id); +                            let _ = user_interfaces.remove(&id); +                            let _ = ui_caches.remove(&id); + +                            events.push(( +                                None, +                                core::Event::Window(id, window::Event::Closed), +                            )); + +                            if window_manager.is_empty() { +                                break 'main; +                            } +                        } else { +                            window.state.update( +                                &window.raw, +                                &window_event, +                                &mut debug, +                            ); + +                            if let Some(event) = conversion::window_event( +                                id, +                                &window_event, +                                window.state.scale_factor(), +                                window.state.modifiers(), +                            ) { +                                events.push((Some(id), event)); +                            } +                        } +                    } +                    _ => {} +                } +            } +        } +    } + +    let _ = ManuallyDrop::into_inner(user_interfaces); +} + +/// Builds a window's [`UserInterface`] for the [`Application`]. +fn build_user_interface<'a, A: Application>( +    application: &'a A, +    cache: user_interface::Cache, +    renderer: &mut A::Renderer, +    size: Size, +    debug: &mut Debug, +    id: window::Id, +) -> UserInterface<'a, A::Message, A::Renderer> +where +    <A::Renderer as core::Renderer>::Theme: StyleSheet, +{ +    debug.view_started(); +    let view = application.view(id); +    debug.view_finished(); + +    debug.layout_started(); +    let user_interface = UserInterface::build(view, size, cache, renderer); +    debug.layout_finished(); + +    user_interface +} + +/// Updates a multi-window [`Application`] by feeding it messages, spawning any +/// resulting [`Command`], and tracking its [`Subscription`]. +fn update<A: Application, C, E: Executor>( +    application: &mut A, +    compositor: &mut C, +    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, +    clipboard: &mut Clipboard, +    control_sender: &mut mpsc::UnboundedSender<Control>, +    proxy: &mut winit::event_loop::EventLoopProxy<A::Message>, +    debug: &mut Debug, +    messages: &mut Vec<A::Message>, +    window_manager: &mut WindowManager<A, C>, +    ui_caches: &mut HashMap<window::Id, user_interface::Cache>, +) where +    C: Compositor<Renderer = A::Renderer> + 'static, +    <A::Renderer as core::Renderer>::Theme: StyleSheet, +{ +    for message in messages.drain(..) { +        debug.log_message(&message); +        debug.update_started(); + +        let command = runtime.enter(|| application.update(message)); +        debug.update_finished(); + +        run_command( +            application, +            compositor, +            command, +            runtime, +            clipboard, +            control_sender, +            proxy, +            debug, +            window_manager, +            ui_caches, +        ); +    } + +    let subscription = application.subscription(); +    runtime.track(subscription.into_recipes()); +} + +/// Runs the actions of a [`Command`]. +fn run_command<A, C, E>( +    application: &A, +    compositor: &mut C, +    command: Command<A::Message>, +    runtime: &mut Runtime<E, Proxy<A::Message>, A::Message>, +    clipboard: &mut Clipboard, +    control_sender: &mut mpsc::UnboundedSender<Control>, +    proxy: &mut winit::event_loop::EventLoopProxy<A::Message>, +    debug: &mut Debug, +    window_manager: &mut WindowManager<A, C>, +    ui_caches: &mut HashMap<window::Id, user_interface::Cache>, +) where +    A: Application, +    E: Executor, +    C: Compositor<Renderer = A::Renderer> + 'static, +    <A::Renderer as core::Renderer>::Theme: StyleSheet, +{ +    use crate::runtime::clipboard; +    use crate::runtime::system; +    use crate::runtime::window; + +    for action in command.actions() { +        match action { +            command::Action::Future(future) => { +                runtime.spawn(Box::pin(future)); +            } +            command::Action::Stream(stream) => { +                runtime.run(Box::pin(stream)); +            } +            command::Action::Clipboard(action) => match action { +                clipboard::Action::Read(tag) => { +                    let message = tag(clipboard.read()); + +                    proxy +                        .send_event(message) +                        .expect("Send message to event loop"); +                } +                clipboard::Action::Write(contents) => { +                    clipboard.write(contents); +                } +            }, +            command::Action::Window(action) => match action { +                window::Action::Spawn(id, settings) => { +                    let monitor = window_manager.last_monitor(); + +                    control_sender +                        .start_send(Control::CreateWindow { +                            id, +                            settings, +                            title: application.title(id), +                            monitor, +                        }) +                        .expect("Send control action"); +                } +                window::Action::Close(id) => { +                    use winit::event_loop::ControlFlow; + +                    let _ = window_manager.remove(id); +                    let _ = ui_caches.remove(&id); + +                    if window_manager.is_empty() { +                        control_sender +                            .start_send(Control::ChangeFlow( +                                ControlFlow::ExitWithCode(0), +                            )) +                            .expect("Send control action"); +                    } +                } +                window::Action::Drag(id) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        let _ = window.raw.drag_window(); +                    } +                } +                window::Action::Resize(id, size) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_inner_size(winit::dpi::LogicalSize { +                            width: size.width, +                            height: size.height, +                        }); +                    } +                } +                window::Action::FetchSize(id, callback) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        let size = window +                            .raw +                            .inner_size() +                            .to_logical(window.raw.scale_factor()); + +                        proxy +                            .send_event(callback(Size::new( +                                size.width, +                                size.height, +                            ))) +                            .expect("Send message to event loop"); +                    } +                } +                window::Action::Maximize(id, maximized) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_maximized(maximized); +                    } +                } +                window::Action::Minimize(id, minimized) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_minimized(minimized); +                    } +                } +                window::Action::Move(id, position) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_outer_position( +                            winit::dpi::LogicalPosition { +                                x: position.x, +                                y: position.y, +                            }, +                        ); +                    } +                } +                window::Action::ChangeMode(id, mode) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_visible(conversion::visible(mode)); +                        window.raw.set_fullscreen(conversion::fullscreen( +                            window.raw.current_monitor(), +                            mode, +                        )); +                    } +                } +                window::Action::ChangeIcon(id, icon) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_window_icon(conversion::icon(icon)); +                    } +                } +                window::Action::FetchMode(id, tag) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        let mode = if window.raw.is_visible().unwrap_or(true) { +                            conversion::mode(window.raw.fullscreen()) +                        } else { +                            core::window::Mode::Hidden +                        }; + +                        proxy +                            .send_event(tag(mode)) +                            .expect("Event loop doesn't exist."); +                    } +                } +                window::Action::ToggleMaximize(id) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_maximized(!window.raw.is_maximized()); +                    } +                } +                window::Action::ToggleDecorations(id) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.set_decorations(!window.raw.is_decorated()); +                    } +                } +                window::Action::RequestUserAttention(id, attention_type) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.request_user_attention( +                            attention_type.map(conversion::user_attention), +                        ); +                    } +                } +                window::Action::GainFocus(id) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window.raw.focus_window(); +                    } +                } +                window::Action::ChangeLevel(id, level) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        window +                            .raw +                            .set_window_level(conversion::window_level(level)); +                    } +                } +                window::Action::FetchId(id, tag) => { +                    if let Some(window) = window_manager.get_mut(id) { +                        proxy +                            .send_event(tag(window.raw.id().into())) +                            .expect("Event loop doesn't exist."); +                    } +                } +                window::Action::Screenshot(id, tag) => { +                    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(), +                        ); + +                        proxy +                            .send_event(tag(window::Screenshot::new( +                                bytes, +                                window.state.physical_size(), +                            ))) +                            .expect("Event loop doesn't exist."); +                    } +                } +            }, +            command::Action::System(action) => match action { +                system::Action::QueryInformation(_tag) => { +                    #[cfg(feature = "system")] +                    { +                        let graphics_info = compositor.fetch_information(); +                        let proxy = proxy.clone(); + +                        let _ = std::thread::spawn(move || { +                            let information = +                                crate::system::information(graphics_info); + +                            let message = _tag(information); + +                            proxy +                                .send_event(message) +                                .expect("Event loop doesn't exist."); +                        }); +                    } +                } +            }, +            command::Action::Widget(action) => { +                let mut current_operation = Some(action); + +                let mut uis = build_user_interfaces( +                    application, +                    debug, +                    window_manager, +                    std::mem::take(ui_caches), +                ); + +                'operate: while let Some(mut operation) = +                    current_operation.take() +                { +                    for (id, ui) in uis.iter_mut() { +                        if let Some(window) = window_manager.get_mut(*id) { +                            ui.operate(&window.renderer, operation.as_mut()); + +                            match operation.finish() { +                                operation::Outcome::None => {} +                                operation::Outcome::Some(message) => { +                                    proxy +                                        .send_event(message) +                                        .expect("Event loop doesn't exist."); + +                                    // operation completed, don't need to try to operate on rest of UIs +                                    break 'operate; +                                } +                                operation::Outcome::Chain(next) => { +                                    current_operation = Some(next); +                                } +                            } +                        } +                    } +                } + +                *ui_caches = +                    uis.drain().map(|(id, ui)| (id, ui.into_cache())).collect(); +            } +            command::Action::LoadFont { bytes, tagger } => { +                use crate::core::text::Renderer; + +                // TODO change this once we change each renderer to having a single backend reference.. :pain: +                // TODO: Error handling (?) +                for (_, window) in window_manager.iter_mut() { +                    window.renderer.load_font(bytes.clone()); +                } + +                proxy +                    .send_event(tagger(Ok(()))) +                    .expect("Send message to event loop"); +            } +        } +    } +} + +/// Build the user interface for every window. +pub fn build_user_interfaces<'a, A: Application, C: Compositor>( +    application: &'a A, +    debug: &mut Debug, +    window_manager: &mut WindowManager<A, C>, +    mut cached_user_interfaces: HashMap<window::Id, user_interface::Cache>, +) -> HashMap<window::Id, UserInterface<'a, A::Message, A::Renderer>> +where +    <A::Renderer as core::Renderer>::Theme: StyleSheet, +    C: Compositor<Renderer = A::Renderer>, +{ +    cached_user_interfaces +        .drain() +        .filter_map(|(id, cache)| { +            let window = window_manager.get_mut(id)?; + +            Some(( +                id, +                build_user_interface( +                    application, +                    cache, +                    &mut window.renderer, +                    window.state.logical_size(), +                    debug, +                    id, +                ), +            )) +        }) +        .collect() +} + +/// Returns true if the provided event should cause an [`Application`] to +/// exit. +pub fn user_force_quit( +    event: &winit::event::WindowEvent<'_>, +    _modifiers: winit::event::ModifiersState, +) -> bool { +    match event { +        #[cfg(target_os = "macos")] +        winit::event::WindowEvent::KeyboardInput { +            input: +                winit::event::KeyboardInput { +                    virtual_keycode: Some(winit::event::VirtualKeyCode::Q), +                    state: winit::event::ElementState::Pressed, +                    .. +                }, +            .. +        } if _modifiers.logo() => true, +        _ => false, +    } +} + +#[cfg(not(target_arch = "wasm32"))] +mod platform { +    pub fn run<T, F>( +        mut event_loop: winit::event_loop::EventLoop<T>, +        event_handler: F, +    ) -> Result<(), super::Error> +    where +        F: 'static +            + FnMut( +                winit::event::Event<'_, T>, +                &winit::event_loop::EventLoopWindowTarget<T>, +                &mut winit::event_loop::ControlFlow, +            ), +    { +        use winit::platform::run_return::EventLoopExtRunReturn; + +        let _ = event_loop.run_return(event_handler); + +        Ok(()) +    } +} + +#[cfg(target_arch = "wasm32")] +mod platform { +    pub fn run<T, F>( +        event_loop: winit::event_loop::EventLoop<T>, +        event_handler: F, +    ) -> ! +    where +        F: 'static +            + FnMut( +                winit::event::Event<'_, T>, +                &winit::event_loop::EventLoopWindowTarget<T>, +                &mut winit::event_loop::ControlFlow, +            ), +    { +        event_loop.run(event_handler) +    } +} diff --git a/winit/src/multi_window/state.rs b/winit/src/multi_window/state.rs new file mode 100644 index 00000000..03da5ad7 --- /dev/null +++ b/winit/src/multi_window/state.rs @@ -0,0 +1,240 @@ +use crate::conversion; +use crate::core; +use crate::core::{mouse, window}; +use crate::core::{Color, Size}; +use crate::graphics::Viewport; +use crate::multi_window::Application; +use crate::style::application; +use std::fmt::{Debug, Formatter}; + +use iced_style::application::StyleSheet; +use winit::event::{Touch, WindowEvent}; +use winit::window::Window; + +/// The state of a multi-windowed [`Application`]. +pub struct State<A: Application> +where +    <A::Renderer as core::Renderer>::Theme: application::StyleSheet, +{ +    title: String, +    scale_factor: f64, +    viewport: Viewport, +    viewport_version: u64, +    cursor_position: Option<winit::dpi::PhysicalPosition<f64>>, +    modifiers: winit::event::ModifiersState, +    theme: <A::Renderer as core::Renderer>::Theme, +    appearance: application::Appearance, +} + +impl<A: Application> Debug for State<A> +where +    <A::Renderer as core::Renderer>::Theme: application::StyleSheet, +{ +    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { +        f.debug_struct("multi_window::State") +            .field("title", &self.title) +            .field("scale_factor", &self.scale_factor) +            .field("viewport", &self.viewport) +            .field("viewport_version", &self.viewport_version) +            .field("cursor_position", &self.cursor_position) +            .field("appearance", &self.appearance) +            .finish() +    } +} + +impl<A: Application> State<A> +where +    <A::Renderer as core::Renderer>::Theme: application::StyleSheet, +{ +    /// Creates a new [`State`] for the provided [`Application`]'s `window`. +    pub fn new( +        application: &A, +        window_id: window::Id, +        window: &Window, +    ) -> Self { +        let title = application.title(window_id); +        let scale_factor = application.scale_factor(window_id); +        let theme = application.theme(window_id); +        let appearance = theme.appearance(&application.style()); + +        let viewport = { +            let physical_size = window.inner_size(); + +            Viewport::with_physical_size( +                Size::new(physical_size.width, physical_size.height), +                window.scale_factor() * scale_factor, +            ) +        }; + +        Self { +            title, +            scale_factor, +            viewport, +            viewport_version: 0, +            cursor_position: None, +            modifiers: winit::event::ModifiersState::default(), +            theme, +            appearance, +        } +    } + +    /// Returns the current [`Viewport`] of the [`State`]. +    pub fn viewport(&self) -> &Viewport { +        &self.viewport +    } + +    /// Returns the version of the [`Viewport`] of the [`State`]. +    /// +    /// The version is incremented every time the [`Viewport`] changes. +    pub fn viewport_version(&self) -> u64 { +        self.viewport_version +    } + +    /// Returns the physical [`Size`] of the [`Viewport`] of the [`State`]. +    pub fn physical_size(&self) -> Size<u32> { +        self.viewport.physical_size() +    } + +    /// Returns the logical [`Size`] of the [`Viewport`] of the [`State`]. +    pub fn logical_size(&self) -> Size<f32> { +        self.viewport.logical_size() +    } + +    /// Returns the current scale factor of the [`Viewport`] of the [`State`]. +    pub fn scale_factor(&self) -> f64 { +        self.viewport.scale_factor() +    } + +    /// Returns the current cursor position of the [`State`]. +    pub fn cursor(&self) -> mouse::Cursor { +        self.cursor_position +            .map(|cursor_position| { +                conversion::cursor_position( +                    cursor_position, +                    self.viewport.scale_factor(), +                ) +            }) +            .map(mouse::Cursor::Available) +            .unwrap_or(mouse::Cursor::Unavailable) +    } + +    /// Returns the current keyboard modifiers of the [`State`]. +    pub fn modifiers(&self) -> winit::event::ModifiersState { +        self.modifiers +    } + +    /// Returns the current theme of the [`State`]. +    pub fn theme(&self) -> &<A::Renderer as core::Renderer>::Theme { +        &self.theme +    } + +    /// Returns the current background [`Color`] of the [`State`]. +    pub fn background_color(&self) -> Color { +        self.appearance.background_color +    } + +    /// Returns the current text [`Color`] of the [`State`]. +    pub fn text_color(&self) -> Color { +        self.appearance.text_color +    } + +    /// Processes the provided window event and updates the [`State`] accordingly. +    pub fn update( +        &mut self, +        window: &Window, +        event: &WindowEvent<'_>, +        _debug: &mut crate::runtime::Debug, +    ) { +        match event { +            WindowEvent::Resized(new_size) => { +                let size = Size::new(new_size.width, new_size.height); + +                self.viewport = Viewport::with_physical_size( +                    size, +                    window.scale_factor() * self.scale_factor, +                ); + +                self.viewport_version = self.viewport_version.wrapping_add(1); +            } +            WindowEvent::ScaleFactorChanged { +                scale_factor: new_scale_factor, +                new_inner_size, +            } => { +                let size = +                    Size::new(new_inner_size.width, new_inner_size.height); + +                self.viewport = Viewport::with_physical_size( +                    size, +                    new_scale_factor * self.scale_factor, +                ); + +                self.viewport_version = self.viewport_version.wrapping_add(1); +            } +            WindowEvent::CursorMoved { position, .. } +            | WindowEvent::Touch(Touch { +                location: position, .. +            }) => { +                self.cursor_position = Some(*position); +            } +            WindowEvent::CursorLeft { .. } => { +                self.cursor_position = None; +            } +            WindowEvent::ModifiersChanged(new_modifiers) => { +                self.modifiers = *new_modifiers; +            } +            #[cfg(feature = "debug")] +            WindowEvent::KeyboardInput { +                input: +                    winit::event::KeyboardInput { +                        virtual_keycode: Some(winit::event::VirtualKeyCode::F12), +                        state: winit::event::ElementState::Pressed, +                        .. +                    }, +                .. +            } => _debug.toggle(), +            _ => {} +        } +    } + +    /// Synchronizes the [`State`] with its [`Application`] and its respective +    /// window. +    /// +    /// Normally, an [`Application`] should be synchronized with its [`State`] +    /// and window after calling [`State::update`]. +    pub fn synchronize( +        &mut self, +        application: &A, +        window_id: window::Id, +        window: &Window, +    ) { +        // Update window title +        let new_title = application.title(window_id); + +        if self.title != new_title { +            window.set_title(&new_title); +            self.title = new_title; +        } + +        // Update scale factor and size +        let new_scale_factor = application.scale_factor(window_id); +        let new_size = window.inner_size(); +        let current_size = self.viewport.physical_size(); + +        if self.scale_factor != new_scale_factor +            || (current_size.width, current_size.height) +                != (new_size.width, new_size.height) +        { +            self.viewport = Viewport::with_physical_size( +                Size::new(new_size.width, new_size.height), +                window.scale_factor() * new_scale_factor, +            ); +            self.viewport_version = self.viewport_version.wrapping_add(1); + +            self.scale_factor = new_scale_factor; +        } + +        // Update theme and appearance +        self.theme = application.theme(window_id); +        self.appearance = self.theme.appearance(&application.style()); +    } +} diff --git a/winit/src/multi_window/window_manager.rs b/winit/src/multi_window/window_manager.rs new file mode 100644 index 00000000..d54156e7 --- /dev/null +++ b/winit/src/multi_window/window_manager.rs @@ -0,0 +1,156 @@ +use crate::core::mouse; +use crate::core::window::Id; +use crate::core::{Point, Size}; +use crate::graphics::Compositor; +use crate::multi_window::{Application, State}; +use crate::style::application::StyleSheet; + +use std::collections::BTreeMap; +use winit::monitor::MonitorHandle; + +#[allow(missing_debug_implementations)] +pub struct WindowManager<A: Application, C: Compositor> +where +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +    C: Compositor<Renderer = A::Renderer>, +{ +    aliases: BTreeMap<winit::window::WindowId, Id>, +    entries: BTreeMap<Id, Window<A, C>>, +} + +impl<A, C> WindowManager<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    pub fn new() -> Self { +        Self { +            aliases: BTreeMap::new(), +            entries: BTreeMap::new(), +        } +    } + +    pub fn insert( +        &mut self, +        id: Id, +        window: winit::window::Window, +        application: &A, +        compositor: &mut C, +        exit_on_close_request: bool, +    ) -> &mut Window<A, C> { +        let state = State::new(application, id, &window); +        let viewport_version = state.viewport_version(); +        let physical_size = state.physical_size(); +        let surface = compositor.create_surface( +            &window, +            physical_size.width, +            physical_size.height, +        ); +        let renderer = compositor.create_renderer(); + +        let _ = self.aliases.insert(window.id(), id); + +        let _ = self.entries.insert( +            id, +            Window { +                raw: window, +                state, +                viewport_version, +                exit_on_close_request, +                surface, +                renderer, +                mouse_interaction: mouse::Interaction::Idle, +            }, +        ); + +        self.entries +            .get_mut(&id) +            .expect("Get window that was just inserted") +    } + +    pub fn is_empty(&self) -> bool { +        self.entries.is_empty() +    } + +    pub fn iter_mut( +        &mut self, +    ) -> impl Iterator<Item = (Id, &mut Window<A, C>)> { +        self.entries.iter_mut().map(|(k, v)| (*k, v)) +    } + +    pub fn get_mut(&mut self, id: Id) -> Option<&mut Window<A, C>> { +        self.entries.get_mut(&id) +    } + +    pub fn get_mut_alias( +        &mut self, +        id: winit::window::WindowId, +    ) -> Option<(Id, &mut Window<A, C>)> { +        let id = self.aliases.get(&id).copied()?; + +        Some((id, self.get_mut(id)?)) +    } + +    pub fn last_monitor(&self) -> Option<MonitorHandle> { +        self.entries.values().last()?.raw.current_monitor() +    } + +    pub fn remove(&mut self, id: Id) -> Option<Window<A, C>> { +        let window = self.entries.remove(&id)?; +        let _ = self.aliases.remove(&window.raw.id()); + +        Some(window) +    } +} + +impl<A, C> Default for WindowManager<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    fn default() -> Self { +        Self::new() +    } +} + +#[allow(missing_debug_implementations)] +pub struct Window<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    pub raw: winit::window::Window, +    pub state: State<A>, +    pub viewport_version: u64, +    pub exit_on_close_request: bool, +    pub mouse_interaction: mouse::Interaction, +    pub surface: C::Surface, +    pub renderer: A::Renderer, +} + +impl<A, C> Window<A, C> +where +    A: Application, +    C: Compositor<Renderer = A::Renderer>, +    <A::Renderer as crate::core::Renderer>::Theme: StyleSheet, +{ +    pub fn position(&self) -> Option<Point> { +        self.raw +            .inner_position() +            .ok() +            .map(|position| position.to_logical(self.raw.scale_factor())) +            .map(|position| Point { +                x: position.x, +                y: position.y, +            }) +    } + +    pub fn size(&self) -> Size { +        let size = self.raw.inner_size().to_logical(self.raw.scale_factor()); + +        Size::new(size.width, size.height) +    } +} diff --git a/winit/src/settings.rs b/winit/src/settings.rs index 599f33f1..2e541128 100644 --- a/winit/src/settings.rs +++ b/winit/src/settings.rs @@ -1,40 +1,7 @@  //! Configure your application. -#[cfg(target_os = "windows")] -#[path = "settings/windows.rs"] -mod platform; - -#[cfg(target_os = "macos")] -#[path = "settings/macos.rs"] -mod platform; - -#[cfg(target_os = "linux")] -#[path = "settings/linux.rs"] -mod platform; - -#[cfg(target_arch = "wasm32")] -#[path = "settings/wasm.rs"] -mod platform; - -#[cfg(not(any( -    target_os = "windows", -    target_os = "macos", -    target_os = "linux", -    target_arch = "wasm32" -)))] -#[path = "settings/other.rs"] -mod platform; - -pub use platform::PlatformSpecific; - -use crate::conversion; -use crate::core::window::{Icon, Level}; -use crate::Position; - -use winit::monitor::MonitorHandle; -use winit::window::WindowBuilder; +use crate::core::window;  use std::borrow::Cow; -use std::fmt;  /// The settings of an application.  #[derive(Debug, Clone, Default)] @@ -45,8 +12,8 @@ pub struct Settings<Flags> {      /// communicate with it through the windowing system.      pub id: Option<String>, -    /// The [`Window`] settings. -    pub window: Window, +    /// The [`window::Settings`]. +    pub window: window::Settings,      /// The data needed to initialize an [`Application`].      /// @@ -55,197 +22,4 @@ pub struct Settings<Flags> {      /// The fonts to load on boot.      pub fonts: Vec<Cow<'static, [u8]>>, - -    /// Whether the [`Application`] should exit when the user requests the -    /// window to close (e.g. the user presses the close button). -    /// -    /// [`Application`]: crate::Application -    pub exit_on_close_request: bool, -} - -/// The window settings of an application. -#[derive(Clone)] -pub struct Window { -    /// The size of the window. -    pub size: (u32, u32), - -    /// The position of the window. -    pub position: Position, - -    /// The minimum size of the window. -    pub min_size: Option<(u32, u32)>, - -    /// The maximum size of the window. -    pub max_size: Option<(u32, u32)>, - -    /// Whether the window should be visible or not. -    pub visible: bool, - -    /// Whether the window should be resizable or not. -    pub resizable: bool, - -    /// Whether the window should have a border, a title bar, etc. -    pub decorations: bool, - -    /// Whether the window should be transparent. -    pub transparent: bool, - -    /// The window [`Level`]. -    pub level: Level, - -    /// The window icon, which is also usually used in the taskbar -    pub icon: Option<Icon>, - -    /// Platform specific settings. -    pub platform_specific: platform::PlatformSpecific, -} - -impl fmt::Debug for Window { -    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -        f.debug_struct("Window") -            .field("size", &self.size) -            .field("position", &self.position) -            .field("min_size", &self.min_size) -            .field("max_size", &self.max_size) -            .field("visible", &self.visible) -            .field("resizable", &self.resizable) -            .field("decorations", &self.decorations) -            .field("transparent", &self.transparent) -            .field("level", &self.level) -            .field("icon", &self.icon.is_some()) -            .field("platform_specific", &self.platform_specific) -            .finish() -    } -} - -impl Window { -    /// Converts the window settings into a `WindowBuilder` from `winit`. -    pub fn into_builder( -        self, -        title: &str, -        primary_monitor: Option<MonitorHandle>, -        _id: Option<String>, -    ) -> 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_enabled_buttons(if self.resizable { -                winit::window::WindowButtons::all() -            } else { -                winit::window::WindowButtons::CLOSE -                    | winit::window::WindowButtons::MINIMIZE -            }) -            .with_decorations(self.decorations) -            .with_transparent(self.transparent) -            .with_window_icon(self.icon.and_then(conversion::icon)) -            .with_window_level(conversion::window_level(self.level)) -            .with_visible(self.visible); - -        if let Some(position) = conversion::position( -            primary_monitor.as_ref(), -            self.size, -            self.position, -        ) { -            window_builder = window_builder.with_position(position); -        } - -        if let Some((width, height)) = self.min_size { -            window_builder = window_builder -                .with_min_inner_size(winit::dpi::LogicalSize { width, height }); -        } - -        if let Some((width, height)) = self.max_size { -            window_builder = window_builder -                .with_max_inner_size(winit::dpi::LogicalSize { width, height }); -        } - -        #[cfg(any( -            target_os = "dragonfly", -            target_os = "freebsd", -            target_os = "netbsd", -            target_os = "openbsd" -        ))] -        { -            // `with_name` is available on both `WindowBuilderExtWayland` and `WindowBuilderExtX11` and they do -            // exactly the same thing. We arbitrarily choose `WindowBuilderExtWayland` here. -            use ::winit::platform::wayland::WindowBuilderExtWayland; - -            if let Some(id) = _id { -                window_builder = window_builder.with_name(id.clone(), id); -            } -        } - -        #[cfg(target_os = "windows")] -        { -            use winit::platform::windows::WindowBuilderExtWindows; -            #[allow(unsafe_code)] -            unsafe { -                window_builder = window_builder -                    .with_parent_window(self.platform_specific.parent); -            } -            window_builder = window_builder -                .with_drag_and_drop(self.platform_specific.drag_and_drop); -        } - -        #[cfg(target_os = "macos")] -        { -            use winit::platform::macos::WindowBuilderExtMacOS; - -            window_builder = window_builder -                .with_title_hidden(self.platform_specific.title_hidden) -                .with_titlebar_transparent( -                    self.platform_specific.titlebar_transparent, -                ) -                .with_fullsize_content_view( -                    self.platform_specific.fullsize_content_view, -                ); -        } - -        #[cfg(target_os = "linux")] -        { -            #[cfg(feature = "x11")] -            { -                use winit::platform::x11::WindowBuilderExtX11; - -                window_builder = window_builder.with_name( -                    &self.platform_specific.application_id, -                    &self.platform_specific.application_id, -                ); -            } -            #[cfg(feature = "wayland")] -            { -                use winit::platform::wayland::WindowBuilderExtWayland; - -                window_builder = window_builder.with_name( -                    &self.platform_specific.application_id, -                    &self.platform_specific.application_id, -                ); -            } -        } - -        window_builder -    } -} - -impl Default for Window { -    fn default() -> Window { -        Window { -            size: (1024, 768), -            position: Position::default(), -            min_size: None, -            max_size: None, -            visible: true, -            resizable: true, -            decorations: true, -            transparent: false, -            level: Level::default(), -            icon: None, -            platform_specific: PlatformSpecific::default(), -        } -    }  }  | 
