//! Convert [`winit`] types into [`iced_runtime`] types, and viceversa. //! //! [`winit`]: https://github.com/rust-windowing/winit //! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.10/runtime use crate::core::keyboard; use crate::core::mouse; use crate::core::touch; use crate::core::window; 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, _id: Option, ) -> 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::keyboard::ModifiersState, ) -> Option { use winit::event::WindowEvent; match event { WindowEvent::Resized(new_size) => { let logical_size = new_size.to_logical(scale_factor); Some(Event::Window( id, window::Event::Resized { width: logical_size.width, height: logical_size.height, }, )) } WindowEvent::CloseRequested => { Some(Event::Window(id, window::Event::CloseRequested)) } WindowEvent::CursorMoved { position, .. } => { let position = position.to_logical::(scale_factor); Some(Event::Mouse(mouse::Event::CursorMoved { position: Point::new(position.x as f32, position.y as f32), })) } WindowEvent::CursorEntered { .. } => { Some(Event::Mouse(mouse::Event::CursorEntered)) } WindowEvent::CursorLeft { .. } => { Some(Event::Mouse(mouse::Event::CursorLeft)) } WindowEvent::MouseInput { button, state, .. } => { let button = mouse_button(*button); Some(Event::Mouse(match state { winit::event::ElementState::Pressed => { mouse::Event::ButtonPressed(button) } winit::event::ElementState::Released => { mouse::Event::ButtonReleased(button) } })) } WindowEvent::MouseWheel { delta, .. } => match delta { winit::event::MouseScrollDelta::LineDelta(delta_x, delta_y) => { Some(Event::Mouse(mouse::Event::WheelScrolled { delta: mouse::ScrollDelta::Lines { x: *delta_x, y: *delta_y, }, })) } winit::event::MouseScrollDelta::PixelDelta(position) => { Some(Event::Mouse(mouse::Event::WheelScrolled { delta: mouse::ScrollDelta::Pixels { x: position.x as f32, y: position.y as f32, }, })) } }, WindowEvent::KeyboardInput { event: winit::event::KeyEvent { logical_key, state, text, .. }, .. } => Some(Event::Keyboard({ let key_code = key_code(logical_key); let modifiers = self::modifiers(modifiers); match state { winit::event::ElementState::Pressed => { keyboard::Event::KeyPressed { key_code, modifiers, text: text .as_ref() .map(winit::keyboard::SmolStr::to_string), } } winit::event::ElementState::Released => { keyboard::Event::KeyReleased { key_code, modifiers, } } } })), WindowEvent::ModifiersChanged(new_modifiers) => { Some(Event::Keyboard(keyboard::Event::ModifiersChanged( self::modifiers(new_modifiers.state()), ))) } WindowEvent::Focused(focused) => Some(Event::Window( id, if *focused { window::Event::Focused } else { window::Event::Unfocused }, )), WindowEvent::HoveredFile(path) => { Some(Event::Window(id, window::Event::FileHovered(path.clone()))) } WindowEvent::DroppedFile(path) => { Some(Event::Window(id, window::Event::FileDropped(path.clone()))) } WindowEvent::HoveredFileCancelled => { Some(Event::Window(id, window::Event::FilesHoveredLeft)) } WindowEvent::Touch(touch) => { Some(Event::Touch(touch_event(*touch, scale_factor))) } WindowEvent::Moved(position) => { let winit::dpi::LogicalPosition { x, y } = position.to_logical(scale_factor); Some(Event::Window(id, window::Event::Moved { x, y })) } _ => None, } } /// Converts a [`window::Level`] to a [`winit`] window level. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn window_level(level: window::Level) -> winit::window::WindowLevel { match level { window::Level::Normal => winit::window::WindowLevel::Normal, window::Level::AlwaysOnBottom => { winit::window::WindowLevel::AlwaysOnBottom } window::Level::AlwaysOnTop => winit::window::WindowLevel::AlwaysOnTop, } } /// 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>, size: Size, position: window::Position, ) -> Option { match position { window::Position::Default => None, window::Position::Specific(position) => { Some(winit::dpi::Position::Logical(winit::dpi::LogicalPosition { x: f64::from(position.x), y: f64::from(position.y), })) } window::Position::Centered => { if let Some(monitor) = monitor { let start = monitor.position(); let resolution: winit::dpi::LogicalSize = monitor.size().to_logical(monitor.scale_factor()); let centered: winit::dpi::PhysicalPosition = winit::dpi::LogicalPosition { x: (resolution.width - f64::from(size.width)) / 2.0, y: (resolution.height - f64::from(size.height)) / 2.0, } .to_physical(monitor.scale_factor()); Some(winit::dpi::Position::Physical( winit::dpi::PhysicalPosition { x: start.x + centered.x, y: start.y + centered.y, }, )) } else { None } } } } /// Converts a [`window::Mode`] to a [`winit`] fullscreen mode. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn fullscreen( monitor: Option, mode: window::Mode, ) -> Option { match mode { window::Mode::Windowed | window::Mode::Hidden => None, window::Mode::Fullscreen => { Some(winit::window::Fullscreen::Borderless(monitor)) } } } /// Converts a [`window::Mode`] to a visibility flag. pub fn visible(mode: window::Mode) -> bool { match mode { window::Mode::Windowed | window::Mode::Fullscreen => true, window::Mode::Hidden => false, } } /// Converts a [`winit`] fullscreen mode to a [`window::Mode`]. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn mode(mode: Option) -> window::Mode { match mode { None => window::Mode::Windowed, Some(_) => window::Mode::Fullscreen, } } /// Converts a [`mouse::Interaction`] to a [`winit`] cursor icon. /// /// [`winit`]: https://github.com/rust-windowing/winit pub fn mouse_interaction( interaction: mouse::Interaction, ) -> winit::window::CursorIcon { use mouse::Interaction; match interaction { Interaction::Idle => winit::window::CursorIcon::Default, Interaction::Pointer => winit::window::CursorIcon::Pointer, Interaction::Working => winit::window::CursorIcon::Progress, Interaction::Grab => winit::window::CursorIcon::Grab, Interaction::Grabbing => winit::window::CursorIcon::Grabbing, Interaction::Crosshair => winit::window::CursorIcon::Crosshair, Interaction::Text => winit::window::CursorIcon::Text, Interaction::ResizingHorizontally => { winit::window::CursorIcon::EwResize } Interaction::ResizingVertically => winit::window::CursorIcon::NsResize, Interaction::NotAllowed => winit::window::CursorIcon::NotAllowed, } } /// Converts a `MouseButton` from [`winit`] to an [`iced`] mouse button. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced`]: https://github.com/iced-rs/iced/tree/0.10 pub fn mouse_button(mouse_button: winit::event::MouseButton) -> mouse::Button { match mouse_button { winit::event::MouseButton::Left => mouse::Button::Left, winit::event::MouseButton::Right => mouse::Button::Right, winit::event::MouseButton::Middle => mouse::Button::Middle, winit::event::MouseButton::Back => mouse::Button::Back, winit::event::MouseButton::Forward => mouse::Button::Forward, winit::event::MouseButton::Other(other) => mouse::Button::Other(other), } } /// Converts some `ModifiersState` from [`winit`] to an [`iced`] modifiers /// state. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced`]: https://github.com/iced-rs/iced/tree/0.10 pub fn modifiers( modifiers: winit::keyboard::ModifiersState, ) -> keyboard::Modifiers { let mut result = keyboard::Modifiers::empty(); result.set(keyboard::Modifiers::SHIFT, modifiers.shift_key()); result.set(keyboard::Modifiers::CTRL, modifiers.control_key()); result.set(keyboard::Modifiers::ALT, modifiers.alt_key()); result.set(keyboard::Modifiers::LOGO, modifiers.super_key()); result } /// Converts a physical cursor position to a logical `Point`. pub fn cursor_position( position: winit::dpi::PhysicalPosition, scale_factor: f64, ) -> Point { let logical_position = position.to_logical(scale_factor); Point::new(logical_position.x, logical_position.y) } /// Converts a `Touch` from [`winit`] to an [`iced`] touch event. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced`]: https://github.com/iced-rs/iced/tree/0.10 pub fn touch_event( touch: winit::event::Touch, scale_factor: f64, ) -> touch::Event { let id = touch::Finger(touch.id); let position = { let location = touch.location.to_logical::(scale_factor); Point::new(location.x as f32, location.y as f32) }; match touch.phase { winit::event::TouchPhase::Started => { touch::Event::FingerPressed { id, position } } winit::event::TouchPhase::Moved => { touch::Event::FingerMoved { id, position } } winit::event::TouchPhase::Ended => { touch::Event::FingerLifted { id, position } } winit::event::TouchPhase::Cancelled => { touch::Event::FingerLost { id, position } } } } /// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code. /// /// [`winit`]: https://github.com/rust-windowing/winit /// [`iced`]: https://github.com/iced-rs/iced/tree/0.10 pub fn key_code(key: &winit::keyboard::Key) -> keyboard::KeyCode { use keyboard::KeyCode; use winit::keyboard::NamedKey; match key { winit::keyboard::Key::Character(c) => match c.as_str() { "1" => KeyCode::Key1, "2" => KeyCode::Key2, "3" => KeyCode::Key3, "4" => KeyCode::Key4, "5" => KeyCode::Key5, "6" => KeyCode::Key6, "7" => KeyCode::Key7, "8" => KeyCode::Key8, "9" => KeyCode::Key9, "0" => KeyCode::Key0, "a" => KeyCode::A, "b" => KeyCode::B, "c" => KeyCode::C, "d" => KeyCode::D, "e" => KeyCode::E, "f" => KeyCode::F, "g" => KeyCode::G, "h" => KeyCode::H, "i" => KeyCode::I, "j" => KeyCode::J, "k" => KeyCode::K, "l" => KeyCode::L, "m" => KeyCode::M, "n" => KeyCode::N, "o" => KeyCode::O, "p" => KeyCode::P, "q" => KeyCode::Q, "r" => KeyCode::R, "s" => KeyCode::S, "t" => KeyCode::T, "u" => KeyCode::U, "v" => KeyCode::V, "w" => KeyCode::W, "x" => KeyCode::X, "y" => KeyCode::Y, "z" => KeyCode::Z, _ => KeyCode::Unlabeled, }, winit::keyboard::Key::Named(named_key) => match named_key { NamedKey::Escape => KeyCode::Escape, NamedKey::F1 => KeyCode::F1, NamedKey::F2 => KeyCode::F2, NamedKey::F3 => KeyCode::F3, NamedKey::F4 => KeyCode::F4, NamedKey::F5 => KeyCode::F5, NamedKey::F6 => KeyCode::F6, NamedKey::F7 => KeyCode::F7, NamedKey::F8 => KeyCode::F8, NamedKey::F9 => KeyCode::F9, NamedKey::F10 => KeyCode::F10, NamedKey::F11 => KeyCode::F11, NamedKey::F12 => KeyCode::F12, NamedKey::F13 => KeyCode::F13, NamedKey::F14 => KeyCode::F14, NamedKey::F15 => KeyCode::F15, NamedKey::F16 => KeyCode::F16, NamedKey::F17 => KeyCode::F17, NamedKey::F18 => KeyCode::F18, NamedKey::F19 => KeyCode::F19, NamedKey::F20 => KeyCode::F20, NamedKey::F21 => KeyCode::F21, NamedKey::F22 => KeyCode::F22, NamedKey::F23 => KeyCode::F23, NamedKey::F24 => KeyCode::F24, NamedKey::PrintScreen => KeyCode::Snapshot, NamedKey::ScrollLock => KeyCode::Scroll, NamedKey::Pause => KeyCode::Pause, NamedKey::Insert => KeyCode::Insert, NamedKey::Home => KeyCode::Home, NamedKey::Delete => KeyCode::Delete, NamedKey::End => KeyCode::End, NamedKey::PageDown => KeyCode::PageDown, NamedKey::PageUp => KeyCode::PageUp, NamedKey::ArrowLeft => KeyCode::Left, NamedKey::ArrowUp => KeyCode::Up, NamedKey::ArrowRight => KeyCode::Right, NamedKey::ArrowDown => KeyCode::Down, NamedKey::Backspace => KeyCode::Backspace, NamedKey::Enter => KeyCode::Enter, NamedKey::Space => KeyCode::Space, NamedKey::Compose => KeyCode::Compose, NamedKey::NumLock => KeyCode::Numlock, NamedKey::AppSwitch => KeyCode::Apps, NamedKey::Convert => KeyCode::Convert, NamedKey::LaunchMail => KeyCode::Mail, NamedKey::MediaApps => KeyCode::MediaSelect, NamedKey::MediaStop => KeyCode::MediaStop, NamedKey::AudioVolumeMute => KeyCode::Mute, NamedKey::MediaStepForward => KeyCode::NavigateForward, NamedKey::MediaStepBackward => KeyCode::NavigateBackward, NamedKey::MediaSkipForward => KeyCode::NextTrack, NamedKey::NonConvert => KeyCode::NoConvert, NamedKey::MediaPlayPause => KeyCode::PlayPause, NamedKey::Power => KeyCode::Power, NamedKey::MediaSkipBackward => KeyCode::PrevTrack, NamedKey::PowerOff => KeyCode::Sleep, NamedKey::Tab => KeyCode::Tab, NamedKey::AudioVolumeDown => KeyCode::VolumeDown, NamedKey::AudioVolumeUp => KeyCode::VolumeUp, NamedKey::WakeUp => KeyCode::Wake, NamedKey::BrowserBack => KeyCode::WebBack, NamedKey::BrowserFavorites => KeyCode::WebFavorites, NamedKey::BrowserForward => KeyCode::WebForward, NamedKey::BrowserHome => KeyCode::WebHome, NamedKey::BrowserRefresh => KeyCode::WebRefresh, NamedKey::BrowserSearch => KeyCode::WebSearch, NamedKey::BrowserStop => KeyCode::WebStop, NamedKey::Copy => KeyCode::Copy, NamedKey::Paste => KeyCode::Paste, NamedKey::Cut => KeyCode::Cut, _ => KeyCode::Unlabeled, }, _ => KeyCode::Unlabeled, } } /// Converts some [`UserAttention`] into it's `winit` counterpart. /// /// [`UserAttention`]: window::UserAttention pub fn user_attention( user_attention: window::UserAttention, ) -> winit::window::UserAttentionType { match user_attention { window::UserAttention::Critical => { winit::window::UserAttentionType::Critical } window::UserAttention::Informational => { winit::window::UserAttentionType::Informational } } } /// Converts some [`window::Icon`] into it's `winit` counterpart. /// /// Returns `None` if there is an error during the conversion. pub fn icon(icon: window::Icon) -> Option { let (pixels, size) = icon.into_raw(); winit::window::Icon::from_rgba(pixels, size.width, size.height).ok() }