Merge branch 'master' into beacon

This commit is contained in:
Héctor Ramón Jiménez 2025-03-04 19:11:37 +01:00
commit 8bd5de72ea
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
371 changed files with 33138 additions and 12950 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,209 +0,0 @@
use crate::application;
use crate::conversion;
use crate::core::mouse;
use crate::core::{Color, Size};
use crate::debug;
use crate::graphics::Viewport;
use crate::Application;
use std::marker::PhantomData;
use winit::event::{Touch, WindowEvent};
use winit::window::Window;
/// The state of a windowed [`Application`].
#[allow(missing_debug_implementations)]
pub struct State<A: Application>
where
A::Theme: application::DefaultStyle,
{
title: String,
scale_factor: f64,
viewport: Viewport,
viewport_version: usize,
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
modifiers: winit::keyboard::ModifiersState,
theme: A::Theme,
appearance: application::Appearance,
application: PhantomData<A>,
}
impl<A: Application> State<A>
where
A::Theme: application::DefaultStyle,
{
/// Creates a new [`State`] for the provided [`Application`] and window.
pub fn new(application: &A, window: &Window) -> Self {
let title = application.title();
let scale_factor = application.scale_factor();
let theme = application.theme();
let appearance = application.style(&theme);
debug::theme_changed(|| application::DefaultStyle::palette(&theme));
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::keyboard::ModifiersState::default(),
theme,
appearance,
application: PhantomData,
}
}
/// 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) -> usize {
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::keyboard::ModifiersState {
self.modifiers
}
/// Returns the current theme of the [`State`].
pub fn theme(&self) -> &A::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) {
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,
..
} => {
let size = self.viewport.physical_size();
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.state();
}
_ => {}
}
}
/// Synchronizes the [`State`] with its [`Application`] and its respective
/// window.
///
/// Normally an [`Application`] should be synchronized with its [`State`]
/// and window after calling [`crate::application::update`].
pub fn synchronize(&mut self, application: &A, window: &Window) {
// Update window title
let new_title = application.title();
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();
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();
self.appearance = application.style(&self.theme);
debug::theme_changed(|| {
application::DefaultStyle::palette(&self.theme)
});
}
}

View file

@ -1,6 +1,8 @@
//! Access the clipboard.
use crate::core::clipboard::Kind;
use std::sync::Arc;
use winit::window::{Window, WindowId};
/// A buffer for short-term storage and transfer within and between
/// applications.
@ -10,18 +12,33 @@ pub struct Clipboard {
}
enum State {
Connected(window_clipboard::Clipboard),
Connected {
clipboard: window_clipboard::Clipboard,
// Held until drop to satisfy the safety invariants of
// `window_clipboard::Clipboard`.
//
// Note that the field ordering is load-bearing.
#[allow(dead_code)]
window: Arc<Window>,
},
Unavailable,
}
impl Clipboard {
/// Creates a new [`Clipboard`] for the given window.
pub fn connect(window: &winit::window::Window) -> Clipboard {
pub fn connect(window: Arc<Window>) -> Clipboard {
// SAFETY: The window handle will stay alive throughout the entire
// lifetime of the `window_clipboard::Clipboard` because we hold
// the `Arc<Window>` together with `State`, and enum variant fields
// get dropped in declaration order.
#[allow(unsafe_code)]
let state = unsafe { window_clipboard::Clipboard::connect(window) }
.ok()
.map(State::Connected)
.unwrap_or(State::Unavailable);
let clipboard =
unsafe { window_clipboard::Clipboard::connect(&window) };
let state = match clipboard {
Ok(clipboard) => State::Connected { clipboard, window },
Err(_) => State::Unavailable,
};
Clipboard { state }
}
@ -37,7 +54,7 @@ impl Clipboard {
/// Reads the current content of the [`Clipboard`] as text.
pub fn read(&self, kind: Kind) -> Option<String> {
match &self.state {
State::Connected(clipboard) => match kind {
State::Connected { clipboard, .. } => match kind {
Kind::Standard => clipboard.read().ok(),
Kind::Primary => clipboard.read_primary().and_then(Result::ok),
},
@ -48,7 +65,7 @@ impl Clipboard {
/// Writes the given text contents to the [`Clipboard`].
pub fn write(&mut self, kind: Kind, contents: String) {
match &mut self.state {
State::Connected(clipboard) => {
State::Connected { clipboard, .. } => {
let result = match kind {
Kind::Standard => clipboard.write(contents),
Kind::Primary => {
@ -66,6 +83,14 @@ impl Clipboard {
State::Unavailable => {}
}
}
/// Returns the identifier of the window used to create the [`Clipboard`], if any.
pub fn window_id(&self) -> Option<WindowId> {
match &self.state {
State::Connected { window, .. } => Some(window.id()),
State::Unavailable => None,
}
}
}
impl crate::core::Clipboard for Clipboard {

View file

@ -1,7 +1,8 @@
//! 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.12/runtime
//! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.13/runtime
use crate::core::input_method;
use crate::core::keyboard;
use crate::core::mouse;
use crate::core::touch;
@ -23,6 +24,12 @@ pub fn window_attributes(
width: settings.size.width,
height: settings.size.height,
})
.with_maximized(settings.maximized)
.with_fullscreen(
settings
.fullscreen
.then_some(winit::window::Fullscreen::Borderless(None)),
)
.with_resizable(settings.resizable)
.with_enabled_buttons(if settings.resizable {
winit::window::WindowButtons::all()
@ -73,16 +80,16 @@ pub fn window_attributes(
#[cfg(target_os = "windows")]
{
use winit::platform::windows::WindowAttributesExtWindows;
#[allow(unsafe_code)]
unsafe {
attributes = attributes
.with_parent_window(settings.platform_specific.parent);
}
attributes = attributes
.with_drag_and_drop(settings.platform_specific.drag_and_drop);
attributes = attributes
.with_skip_taskbar(settings.platform_specific.skip_taskbar);
attributes = attributes.with_undecorated_shadow(
settings.platform_specific.undecorated_shadow,
);
}
#[cfg(target_os = "macos")]
@ -105,10 +112,14 @@ pub fn window_attributes(
{
use winit::platform::x11::WindowAttributesExtX11;
attributes = attributes.with_name(
&settings.platform_specific.application_id,
&settings.platform_specific.application_id,
);
attributes = attributes
.with_override_redirect(
settings.platform_specific.override_redirect,
)
.with_name(
&settings.platform_specific.application_id,
&settings.platform_specific.application_id,
);
}
#[cfg(feature = "wayland")]
{
@ -126,27 +137,24 @@ pub fn window_attributes(
/// 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<Event> {
use winit::event::Ime;
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,
},
))
Some(Event::Window(window::Event::Resized(Size {
width: logical_size.width,
height: logical_size.height,
})))
}
WindowEvent::CloseRequested => {
Some(Event::Window(id, window::Event::CloseRequested))
Some(Event::Window(window::Event::CloseRequested))
}
WindowEvent::CursorMoved { position, .. } => {
let position = position.to_logical::<f64>(scale_factor);
@ -191,8 +199,10 @@ pub fn window_event(
}))
}
},
// Ignore keyboard presses/releases during window focus/unfocus
WindowEvent::KeyboardInput { is_synthetic, .. } if is_synthetic => None,
WindowEvent::KeyboardInput { event, .. } => Some(Event::Keyboard({
let logical_key = {
let key = {
#[cfg(not(target_arch = "wasm32"))]
{
use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
@ -202,7 +212,7 @@ pub fn window_event(
#[cfg(target_arch = "wasm32")]
{
// TODO: Fix inconsistent API on Wasm
event.logical_key
event.logical_key.clone()
}
};
@ -223,9 +233,16 @@ pub fn window_event(
}.filter(|text| !text.as_str().chars().any(is_private_use));
let winit::event::KeyEvent {
state, location, ..
state,
location,
logical_key,
physical_key,
..
} = event;
let key = key(logical_key);
let key = self::key(key);
let modified_key = self::key(logical_key);
let physical_key = self::physical_key(physical_key);
let modifiers = self::modifiers(modifiers);
let location = match location {
@ -245,6 +262,8 @@ pub fn window_event(
winit::event::ElementState::Pressed => {
keyboard::Event::KeyPressed {
key,
modified_key,
physical_key,
modifiers,
location,
text,
@ -253,6 +272,8 @@ pub fn window_event(
winit::event::ElementState::Released => {
keyboard::Event::KeyReleased {
key,
modified_key,
physical_key,
modifiers,
location,
}
@ -264,22 +285,28 @@ pub fn window_event(
self::modifiers(new_modifiers.state()),
)))
}
WindowEvent::Focused(focused) => Some(Event::Window(
id,
if focused {
window::Event::Focused
} else {
window::Event::Unfocused
},
)),
WindowEvent::Ime(event) => Some(Event::InputMethod(match event {
Ime::Enabled => input_method::Event::Opened,
Ime::Preedit(content, size) => input_method::Event::Preedit(
content,
size.map(|(start, end)| (start..end)),
),
Ime::Commit(content) => input_method::Event::Commit(content),
Ime::Disabled => input_method::Event::Closed,
})),
WindowEvent::Focused(focused) => Some(Event::Window(if focused {
window::Event::Focused
} else {
window::Event::Unfocused
})),
WindowEvent::HoveredFile(path) => {
Some(Event::Window(id, window::Event::FileHovered(path.clone())))
Some(Event::Window(window::Event::FileHovered(path.clone())))
}
WindowEvent::DroppedFile(path) => {
Some(Event::Window(id, window::Event::FileDropped(path.clone())))
Some(Event::Window(window::Event::FileDropped(path.clone())))
}
WindowEvent::HoveredFileCancelled => {
Some(Event::Window(id, window::Event::FilesHoveredLeft))
Some(Event::Window(window::Event::FilesHoveredLeft))
}
WindowEvent::Touch(touch) => {
Some(Event::Touch(touch_event(touch, scale_factor)))
@ -288,7 +315,7 @@ pub fn window_event(
let winit::dpi::LogicalPosition { x, y } =
position.to_logical(scale_factor);
Some(Event::Window(id, window::Event::Moved { x, y }))
Some(Event::Window(window::Event::Moved(Point::new(x, y))))
}
_ => None,
}
@ -434,8 +461,19 @@ pub fn mouse_interaction(
winit::window::CursorIcon::EwResize
}
Interaction::ResizingVertically => winit::window::CursorIcon::NsResize,
Interaction::ResizingDiagonallyUp => {
winit::window::CursorIcon::NeswResize
}
Interaction::ResizingDiagonallyDown => {
winit::window::CursorIcon::NwseResize
}
Interaction::NotAllowed => winit::window::CursorIcon::NotAllowed,
Interaction::ZoomIn => winit::window::CursorIcon::ZoomIn,
Interaction::ZoomOut => winit::window::CursorIcon::ZoomOut,
Interaction::Cell => winit::window::CursorIcon::Cell,
Interaction::Move => winit::window::CursorIcon::Move,
Interaction::Copy => winit::window::CursorIcon::Copy,
Interaction::Help => winit::window::CursorIcon::Help,
}
}
@ -513,7 +551,7 @@ pub fn touch_event(
}
}
/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code.
/// Converts a `Key` from [`winit`] to an [`iced`] key.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12
@ -842,7 +880,258 @@ pub fn key(key: winit::keyboard::Key) -> keyboard::Key {
}
}
/// Converts some [`UserAttention`] into it's `winit` counterpart.
/// Converts a `PhysicalKey` from [`winit`] to an [`iced`] physical key.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12
pub fn physical_key(
physical_key: winit::keyboard::PhysicalKey,
) -> keyboard::key::Physical {
match physical_key {
winit::keyboard::PhysicalKey::Code(code) => key_code(code)
.map(keyboard::key::Physical::Code)
.unwrap_or(keyboard::key::Physical::Unidentified(
keyboard::key::NativeCode::Unidentified,
)),
winit::keyboard::PhysicalKey::Unidentified(code) => {
keyboard::key::Physical::Unidentified(native_key_code(code))
}
}
}
/// Converts a `KeyCode` from [`winit`] to an [`iced`] key code.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12
pub fn key_code(
key_code: winit::keyboard::KeyCode,
) -> Option<keyboard::key::Code> {
use winit::keyboard::KeyCode;
Some(match key_code {
KeyCode::Backquote => keyboard::key::Code::Backquote,
KeyCode::Backslash => keyboard::key::Code::Backslash,
KeyCode::BracketLeft => keyboard::key::Code::BracketLeft,
KeyCode::BracketRight => keyboard::key::Code::BracketRight,
KeyCode::Comma => keyboard::key::Code::Comma,
KeyCode::Digit0 => keyboard::key::Code::Digit0,
KeyCode::Digit1 => keyboard::key::Code::Digit1,
KeyCode::Digit2 => keyboard::key::Code::Digit2,
KeyCode::Digit3 => keyboard::key::Code::Digit3,
KeyCode::Digit4 => keyboard::key::Code::Digit4,
KeyCode::Digit5 => keyboard::key::Code::Digit5,
KeyCode::Digit6 => keyboard::key::Code::Digit6,
KeyCode::Digit7 => keyboard::key::Code::Digit7,
KeyCode::Digit8 => keyboard::key::Code::Digit8,
KeyCode::Digit9 => keyboard::key::Code::Digit9,
KeyCode::Equal => keyboard::key::Code::Equal,
KeyCode::IntlBackslash => keyboard::key::Code::IntlBackslash,
KeyCode::IntlRo => keyboard::key::Code::IntlRo,
KeyCode::IntlYen => keyboard::key::Code::IntlYen,
KeyCode::KeyA => keyboard::key::Code::KeyA,
KeyCode::KeyB => keyboard::key::Code::KeyB,
KeyCode::KeyC => keyboard::key::Code::KeyC,
KeyCode::KeyD => keyboard::key::Code::KeyD,
KeyCode::KeyE => keyboard::key::Code::KeyE,
KeyCode::KeyF => keyboard::key::Code::KeyF,
KeyCode::KeyG => keyboard::key::Code::KeyG,
KeyCode::KeyH => keyboard::key::Code::KeyH,
KeyCode::KeyI => keyboard::key::Code::KeyI,
KeyCode::KeyJ => keyboard::key::Code::KeyJ,
KeyCode::KeyK => keyboard::key::Code::KeyK,
KeyCode::KeyL => keyboard::key::Code::KeyL,
KeyCode::KeyM => keyboard::key::Code::KeyM,
KeyCode::KeyN => keyboard::key::Code::KeyN,
KeyCode::KeyO => keyboard::key::Code::KeyO,
KeyCode::KeyP => keyboard::key::Code::KeyP,
KeyCode::KeyQ => keyboard::key::Code::KeyQ,
KeyCode::KeyR => keyboard::key::Code::KeyR,
KeyCode::KeyS => keyboard::key::Code::KeyS,
KeyCode::KeyT => keyboard::key::Code::KeyT,
KeyCode::KeyU => keyboard::key::Code::KeyU,
KeyCode::KeyV => keyboard::key::Code::KeyV,
KeyCode::KeyW => keyboard::key::Code::KeyW,
KeyCode::KeyX => keyboard::key::Code::KeyX,
KeyCode::KeyY => keyboard::key::Code::KeyY,
KeyCode::KeyZ => keyboard::key::Code::KeyZ,
KeyCode::Minus => keyboard::key::Code::Minus,
KeyCode::Period => keyboard::key::Code::Period,
KeyCode::Quote => keyboard::key::Code::Quote,
KeyCode::Semicolon => keyboard::key::Code::Semicolon,
KeyCode::Slash => keyboard::key::Code::Slash,
KeyCode::AltLeft => keyboard::key::Code::AltLeft,
KeyCode::AltRight => keyboard::key::Code::AltRight,
KeyCode::Backspace => keyboard::key::Code::Backspace,
KeyCode::CapsLock => keyboard::key::Code::CapsLock,
KeyCode::ContextMenu => keyboard::key::Code::ContextMenu,
KeyCode::ControlLeft => keyboard::key::Code::ControlLeft,
KeyCode::ControlRight => keyboard::key::Code::ControlRight,
KeyCode::Enter => keyboard::key::Code::Enter,
KeyCode::SuperLeft => keyboard::key::Code::SuperLeft,
KeyCode::SuperRight => keyboard::key::Code::SuperRight,
KeyCode::ShiftLeft => keyboard::key::Code::ShiftLeft,
KeyCode::ShiftRight => keyboard::key::Code::ShiftRight,
KeyCode::Space => keyboard::key::Code::Space,
KeyCode::Tab => keyboard::key::Code::Tab,
KeyCode::Convert => keyboard::key::Code::Convert,
KeyCode::KanaMode => keyboard::key::Code::KanaMode,
KeyCode::Lang1 => keyboard::key::Code::Lang1,
KeyCode::Lang2 => keyboard::key::Code::Lang2,
KeyCode::Lang3 => keyboard::key::Code::Lang3,
KeyCode::Lang4 => keyboard::key::Code::Lang4,
KeyCode::Lang5 => keyboard::key::Code::Lang5,
KeyCode::NonConvert => keyboard::key::Code::NonConvert,
KeyCode::Delete => keyboard::key::Code::Delete,
KeyCode::End => keyboard::key::Code::End,
KeyCode::Help => keyboard::key::Code::Help,
KeyCode::Home => keyboard::key::Code::Home,
KeyCode::Insert => keyboard::key::Code::Insert,
KeyCode::PageDown => keyboard::key::Code::PageDown,
KeyCode::PageUp => keyboard::key::Code::PageUp,
KeyCode::ArrowDown => keyboard::key::Code::ArrowDown,
KeyCode::ArrowLeft => keyboard::key::Code::ArrowLeft,
KeyCode::ArrowRight => keyboard::key::Code::ArrowRight,
KeyCode::ArrowUp => keyboard::key::Code::ArrowUp,
KeyCode::NumLock => keyboard::key::Code::NumLock,
KeyCode::Numpad0 => keyboard::key::Code::Numpad0,
KeyCode::Numpad1 => keyboard::key::Code::Numpad1,
KeyCode::Numpad2 => keyboard::key::Code::Numpad2,
KeyCode::Numpad3 => keyboard::key::Code::Numpad3,
KeyCode::Numpad4 => keyboard::key::Code::Numpad4,
KeyCode::Numpad5 => keyboard::key::Code::Numpad5,
KeyCode::Numpad6 => keyboard::key::Code::Numpad6,
KeyCode::Numpad7 => keyboard::key::Code::Numpad7,
KeyCode::Numpad8 => keyboard::key::Code::Numpad8,
KeyCode::Numpad9 => keyboard::key::Code::Numpad9,
KeyCode::NumpadAdd => keyboard::key::Code::NumpadAdd,
KeyCode::NumpadBackspace => keyboard::key::Code::NumpadBackspace,
KeyCode::NumpadClear => keyboard::key::Code::NumpadClear,
KeyCode::NumpadClearEntry => keyboard::key::Code::NumpadClearEntry,
KeyCode::NumpadComma => keyboard::key::Code::NumpadComma,
KeyCode::NumpadDecimal => keyboard::key::Code::NumpadDecimal,
KeyCode::NumpadDivide => keyboard::key::Code::NumpadDivide,
KeyCode::NumpadEnter => keyboard::key::Code::NumpadEnter,
KeyCode::NumpadEqual => keyboard::key::Code::NumpadEqual,
KeyCode::NumpadHash => keyboard::key::Code::NumpadHash,
KeyCode::NumpadMemoryAdd => keyboard::key::Code::NumpadMemoryAdd,
KeyCode::NumpadMemoryClear => keyboard::key::Code::NumpadMemoryClear,
KeyCode::NumpadMemoryRecall => keyboard::key::Code::NumpadMemoryRecall,
KeyCode::NumpadMemoryStore => keyboard::key::Code::NumpadMemoryStore,
KeyCode::NumpadMemorySubtract => {
keyboard::key::Code::NumpadMemorySubtract
}
KeyCode::NumpadMultiply => keyboard::key::Code::NumpadMultiply,
KeyCode::NumpadParenLeft => keyboard::key::Code::NumpadParenLeft,
KeyCode::NumpadParenRight => keyboard::key::Code::NumpadParenRight,
KeyCode::NumpadStar => keyboard::key::Code::NumpadStar,
KeyCode::NumpadSubtract => keyboard::key::Code::NumpadSubtract,
KeyCode::Escape => keyboard::key::Code::Escape,
KeyCode::Fn => keyboard::key::Code::Fn,
KeyCode::FnLock => keyboard::key::Code::FnLock,
KeyCode::PrintScreen => keyboard::key::Code::PrintScreen,
KeyCode::ScrollLock => keyboard::key::Code::ScrollLock,
KeyCode::Pause => keyboard::key::Code::Pause,
KeyCode::BrowserBack => keyboard::key::Code::BrowserBack,
KeyCode::BrowserFavorites => keyboard::key::Code::BrowserFavorites,
KeyCode::BrowserForward => keyboard::key::Code::BrowserForward,
KeyCode::BrowserHome => keyboard::key::Code::BrowserHome,
KeyCode::BrowserRefresh => keyboard::key::Code::BrowserRefresh,
KeyCode::BrowserSearch => keyboard::key::Code::BrowserSearch,
KeyCode::BrowserStop => keyboard::key::Code::BrowserStop,
KeyCode::Eject => keyboard::key::Code::Eject,
KeyCode::LaunchApp1 => keyboard::key::Code::LaunchApp1,
KeyCode::LaunchApp2 => keyboard::key::Code::LaunchApp2,
KeyCode::LaunchMail => keyboard::key::Code::LaunchMail,
KeyCode::MediaPlayPause => keyboard::key::Code::MediaPlayPause,
KeyCode::MediaSelect => keyboard::key::Code::MediaSelect,
KeyCode::MediaStop => keyboard::key::Code::MediaStop,
KeyCode::MediaTrackNext => keyboard::key::Code::MediaTrackNext,
KeyCode::MediaTrackPrevious => keyboard::key::Code::MediaTrackPrevious,
KeyCode::Power => keyboard::key::Code::Power,
KeyCode::Sleep => keyboard::key::Code::Sleep,
KeyCode::AudioVolumeDown => keyboard::key::Code::AudioVolumeDown,
KeyCode::AudioVolumeMute => keyboard::key::Code::AudioVolumeMute,
KeyCode::AudioVolumeUp => keyboard::key::Code::AudioVolumeUp,
KeyCode::WakeUp => keyboard::key::Code::WakeUp,
KeyCode::Meta => keyboard::key::Code::Meta,
KeyCode::Hyper => keyboard::key::Code::Hyper,
KeyCode::Turbo => keyboard::key::Code::Turbo,
KeyCode::Abort => keyboard::key::Code::Abort,
KeyCode::Resume => keyboard::key::Code::Resume,
KeyCode::Suspend => keyboard::key::Code::Suspend,
KeyCode::Again => keyboard::key::Code::Again,
KeyCode::Copy => keyboard::key::Code::Copy,
KeyCode::Cut => keyboard::key::Code::Cut,
KeyCode::Find => keyboard::key::Code::Find,
KeyCode::Open => keyboard::key::Code::Open,
KeyCode::Paste => keyboard::key::Code::Paste,
KeyCode::Props => keyboard::key::Code::Props,
KeyCode::Select => keyboard::key::Code::Select,
KeyCode::Undo => keyboard::key::Code::Undo,
KeyCode::Hiragana => keyboard::key::Code::Hiragana,
KeyCode::Katakana => keyboard::key::Code::Katakana,
KeyCode::F1 => keyboard::key::Code::F1,
KeyCode::F2 => keyboard::key::Code::F2,
KeyCode::F3 => keyboard::key::Code::F3,
KeyCode::F4 => keyboard::key::Code::F4,
KeyCode::F5 => keyboard::key::Code::F5,
KeyCode::F6 => keyboard::key::Code::F6,
KeyCode::F7 => keyboard::key::Code::F7,
KeyCode::F8 => keyboard::key::Code::F8,
KeyCode::F9 => keyboard::key::Code::F9,
KeyCode::F10 => keyboard::key::Code::F10,
KeyCode::F11 => keyboard::key::Code::F11,
KeyCode::F12 => keyboard::key::Code::F12,
KeyCode::F13 => keyboard::key::Code::F13,
KeyCode::F14 => keyboard::key::Code::F14,
KeyCode::F15 => keyboard::key::Code::F15,
KeyCode::F16 => keyboard::key::Code::F16,
KeyCode::F17 => keyboard::key::Code::F17,
KeyCode::F18 => keyboard::key::Code::F18,
KeyCode::F19 => keyboard::key::Code::F19,
KeyCode::F20 => keyboard::key::Code::F20,
KeyCode::F21 => keyboard::key::Code::F21,
KeyCode::F22 => keyboard::key::Code::F22,
KeyCode::F23 => keyboard::key::Code::F23,
KeyCode::F24 => keyboard::key::Code::F24,
KeyCode::F25 => keyboard::key::Code::F25,
KeyCode::F26 => keyboard::key::Code::F26,
KeyCode::F27 => keyboard::key::Code::F27,
KeyCode::F28 => keyboard::key::Code::F28,
KeyCode::F29 => keyboard::key::Code::F29,
KeyCode::F30 => keyboard::key::Code::F30,
KeyCode::F31 => keyboard::key::Code::F31,
KeyCode::F32 => keyboard::key::Code::F32,
KeyCode::F33 => keyboard::key::Code::F33,
KeyCode::F34 => keyboard::key::Code::F34,
KeyCode::F35 => keyboard::key::Code::F35,
_ => None?,
})
}
/// Converts a `NativeKeyCode` from [`winit`] to an [`iced`] native key code.
///
/// [`winit`]: https://github.com/rust-windowing/winit
/// [`iced`]: https://github.com/iced-rs/iced/tree/0.12
pub fn native_key_code(
native_key_code: winit::keyboard::NativeKeyCode,
) -> keyboard::key::NativeCode {
use winit::keyboard::NativeKeyCode;
match native_key_code {
NativeKeyCode::Unidentified => keyboard::key::NativeCode::Unidentified,
NativeKeyCode::Android(code) => {
keyboard::key::NativeCode::Android(code)
}
NativeKeyCode::MacOS(code) => keyboard::key::NativeCode::MacOS(code),
NativeKeyCode::Windows(code) => {
keyboard::key::NativeCode::Windows(code)
}
NativeKeyCode::Xkb(code) => keyboard::key::NativeCode::Xkb(code),
}
}
/// Converts some [`UserAttention`] into its `winit` counterpart.
///
/// [`UserAttention`]: window::UserAttention
pub fn user_attention(
@ -858,7 +1147,31 @@ pub fn user_attention(
}
}
/// Converts some [`window::Icon`] into it's `winit` counterpart.
/// Converts some [`window::Direction`] into a [`winit::window::ResizeDirection`].
pub fn resize_direction(
resize_direction: window::Direction,
) -> winit::window::ResizeDirection {
match resize_direction {
window::Direction::North => winit::window::ResizeDirection::North,
window::Direction::South => winit::window::ResizeDirection::South,
window::Direction::East => winit::window::ResizeDirection::East,
window::Direction::West => winit::window::ResizeDirection::West,
window::Direction::NorthEast => {
winit::window::ResizeDirection::NorthEast
}
window::Direction::NorthWest => {
winit::window::ResizeDirection::NorthWest
}
window::Direction::SouthEast => {
winit::window::ResizeDirection::SouthEast
}
window::Direction::SouthWest => {
winit::window::ResizeDirection::SouthWest
}
}
}
/// Converts some [`window::Icon`] into its `winit` counterpart.
///
/// Returns `None` if there is an error during the conversion.
pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {
@ -867,6 +1180,17 @@ pub fn icon(icon: window::Icon) -> Option<winit::window::Icon> {
winit::window::Icon::from_rgba(pixels, size.width, size.height).ok()
}
/// Convertions some [`input_method::Purpose`] to its `winit` counterpart.
pub fn ime_purpose(
purpose: input_method::Purpose,
) -> winit::window::ImePurpose {
match purpose {
input_method::Purpose::Normal => winit::window::ImePurpose::Normal,
input_method::Purpose::Secure => winit::window::ImePurpose::Password,
input_method::Purpose::Terminal => winit::window::ImePurpose::Terminal,
}
}
// See: https://en.wikipedia.org/wiki/Private_Use_Areas
fn is_private_use(c: char) -> bool {
('\u{E000}'..='\u{F8FF}').contains(&c)

View file

@ -5,13 +5,13 @@
//! `iced_winit` offers some convenient abstractions on top of [`iced_runtime`]
//! to quickstart development when using [`winit`].
//!
//! It exposes a renderer-agnostic [`Application`] trait that can be implemented
//! It exposes a renderer-agnostic [`Program`] trait that can be implemented
//! and then run with a simple call. The use of this trait is optional.
//!
//! Additionally, a [`conversion`] module is available for users that decide to
//! implement a custom event loop.
//!
//! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.12/runtime
//! [`iced_runtime`]: https://github.com/iced-rs/iced/tree/0.13/runtime
//! [`winit`]: https://github.com/rust-windowing/winit
//! [`conversion`]: crate::conversion
#![doc(
@ -25,24 +25,23 @@ pub use iced_runtime::debug;
pub use iced_runtime::futures;
pub use winit;
#[cfg(feature = "multi-window")]
pub mod multi_window;
#[cfg(feature = "application")]
pub mod application;
pub mod clipboard;
pub mod conversion;
pub mod settings;
#[cfg(feature = "program")]
pub mod program;
#[cfg(feature = "system")]
pub mod system;
mod error;
mod proxy;
#[cfg(feature = "application")]
pub use application::Application;
pub use clipboard::Clipboard;
pub use error::Error;
pub use proxy::Proxy;
pub use settings::Settings;
#[cfg(feature = "program")]
pub use program::Program;

File diff suppressed because it is too large Load diff

View file

@ -1,157 +0,0 @@
use crate::core::mouse;
use crate::core::window::Id;
use crate::core::{Point, Size};
use crate::graphics::Compositor;
use crate::multi_window::{Application, DefaultStyle, State};
use std::collections::BTreeMap;
use std::sync::Arc;
use winit::monitor::MonitorHandle;
#[allow(missing_debug_implementations)]
pub struct WindowManager<A, C>
where
A: Application,
C: Compositor<Renderer = A::Renderer>,
A::Theme: DefaultStyle,
{
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::Theme: DefaultStyle,
{
pub fn new() -> Self {
Self {
aliases: BTreeMap::new(),
entries: BTreeMap::new(),
}
}
pub fn insert(
&mut self,
id: Id,
window: Arc<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.clone(),
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::None,
},
);
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::Theme: DefaultStyle,
{
fn default() -> Self {
Self::new()
}
}
#[allow(missing_debug_implementations)]
pub struct Window<A, C>
where
A: Application,
C: Compositor<Renderer = A::Renderer>,
A::Theme: DefaultStyle,
{
pub raw: Arc<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::Theme: DefaultStyle,
{
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)
}
}

1579
winit/src/program.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,18 @@
use crate::conversion;
use crate::core::{mouse, window};
use crate::core::{Color, Size};
use crate::core::{mouse, theme, window};
use crate::graphics::Viewport;
use crate::multi_window::{self, Application};
use std::fmt::{Debug, Formatter};
use crate::program::Program;
use winit::event::{Touch, WindowEvent};
use winit::window::Window;
/// The state of a multi-windowed [`Application`].
pub struct State<A: Application>
use std::fmt::{Debug, Formatter};
/// The state of a multi-windowed [`Program`].
pub struct State<P: Program>
where
A::Theme: multi_window::DefaultStyle,
P::Theme: theme::Base,
{
title: String,
scale_factor: f64,
@ -19,13 +20,13 @@ where
viewport_version: u64,
cursor_position: Option<winit::dpi::PhysicalPosition<f64>>,
modifiers: winit::keyboard::ModifiersState,
theme: A::Theme,
appearance: multi_window::Appearance,
theme: P::Theme,
style: theme::Style,
}
impl<A: Application> Debug for State<A>
impl<P: Program> Debug for State<P>
where
A::Theme: multi_window::DefaultStyle,
P::Theme: theme::Base,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("multi_window::State")
@ -34,25 +35,25 @@ where
.field("viewport", &self.viewport)
.field("viewport_version", &self.viewport_version)
.field("cursor_position", &self.cursor_position)
.field("appearance", &self.appearance)
.field("style", &self.style)
.finish()
}
}
impl<A: Application> State<A>
impl<P: Program> State<P>
where
A::Theme: multi_window::DefaultStyle,
P::Theme: theme::Base,
{
/// Creates a new [`State`] for the provided [`Application`]'s `window`.
/// Creates a new [`State`] for the provided [`Program`]'s `window`.
pub fn new(
application: &A,
application: &P,
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 = application.style(&theme);
let style = application.style(&theme);
let viewport = {
let physical_size = window.inner_size();
@ -71,7 +72,7 @@ where
cursor_position: None,
modifiers: winit::keyboard::ModifiersState::default(),
theme,
appearance,
style,
}
}
@ -121,18 +122,18 @@ where
}
/// Returns the current theme of the [`State`].
pub fn theme(&self) -> &A::Theme {
pub fn theme(&self) -> &P::Theme {
&self.theme
}
/// Returns the current background [`Color`] of the [`State`].
pub fn background_color(&self) -> Color {
self.appearance.background_color
self.style.background_color
}
/// Returns the current text [`Color`] of the [`State`].
pub fn text_color(&self) -> Color {
self.appearance.text_color
self.style.text_color
}
/// Processes the provided window event and updates the [`State`] accordingly.
@ -177,14 +178,14 @@ where
}
}
/// Synchronizes the [`State`] with its [`Application`] and its respective
/// Synchronizes the [`State`] with its [`Program`] and its respective
/// window.
///
/// Normally, an [`Application`] should be synchronized with its [`State`]
/// Normally, a [`Program`] should be synchronized with its [`State`]
/// and window after calling [`State::update`].
pub fn synchronize(
&mut self,
application: &A,
application: &P,
window_id: window::Id,
window: &Window,
) {
@ -216,6 +217,6 @@ where
// Update theme and appearance
self.theme = application.theme(window_id);
self.appearance = application.style(&self.theme);
self.style = application.style(&self.theme);
}
}

View file

@ -0,0 +1,422 @@
use crate::conversion;
use crate::core::alignment;
use crate::core::input_method;
use crate::core::mouse;
use crate::core::renderer;
use crate::core::text;
use crate::core::theme;
use crate::core::time::Instant;
use crate::core::window::{Id, RedrawRequest};
use crate::core::{
Color, InputMethod, Padding, Point, Rectangle, Size, Text, Vector,
};
use crate::graphics::Compositor;
use crate::program::{Program, State};
use winit::dpi::{LogicalPosition, LogicalSize};
use winit::monitor::MonitorHandle;
use std::collections::BTreeMap;
use std::sync::Arc;
#[allow(missing_debug_implementations)]
pub struct WindowManager<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
P::Theme: theme::Base,
{
aliases: BTreeMap<winit::window::WindowId, Id>,
entries: BTreeMap<Id, Window<P, C>>,
}
impl<P, C> WindowManager<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
P::Theme: theme::Base,
{
pub fn new() -> Self {
Self {
aliases: BTreeMap::new(),
entries: BTreeMap::new(),
}
}
pub fn insert(
&mut self,
id: Id,
window: Arc<winit::window::Window>,
application: &P,
compositor: &mut C,
exit_on_close_request: bool,
) -> &mut Window<P, 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.clone(),
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::None,
redraw_at: None,
preedit: None,
ime_state: None,
},
);
self.entries
.get_mut(&id)
.expect("Get window that was just inserted")
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn is_idle(&self) -> bool {
self.entries
.values()
.all(|window| window.redraw_at.is_none())
}
pub fn redraw_at(&self) -> Option<Instant> {
self.entries
.values()
.filter_map(|window| window.redraw_at)
.min()
}
pub fn first(&self) -> Option<&Window<P, C>> {
self.entries.first_key_value().map(|(_id, window)| window)
}
pub fn iter_mut(
&mut self,
) -> impl Iterator<Item = (Id, &mut Window<P, C>)> {
self.entries.iter_mut().map(|(k, v)| (*k, v))
}
pub fn get(&self, id: Id) -> Option<&Window<P, C>> {
self.entries.get(&id)
}
pub fn get_mut(&mut self, id: Id) -> Option<&mut Window<P, C>> {
self.entries.get_mut(&id)
}
pub fn get_mut_alias(
&mut self,
id: winit::window::WindowId,
) -> Option<(Id, &mut Window<P, 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<P, C>> {
let window = self.entries.remove(&id)?;
let _ = self.aliases.remove(&window.raw.id());
Some(window)
}
}
impl<P, C> Default for WindowManager<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
P::Theme: theme::Base,
{
fn default() -> Self {
Self::new()
}
}
#[allow(missing_debug_implementations)]
pub struct Window<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
P::Theme: theme::Base,
{
pub raw: Arc<winit::window::Window>,
pub state: State<P>,
pub viewport_version: u64,
pub exit_on_close_request: bool,
pub mouse_interaction: mouse::Interaction,
pub surface: C::Surface,
pub renderer: P::Renderer,
pub redraw_at: Option<Instant>,
preedit: Option<Preedit<P::Renderer>>,
ime_state: Option<(Point, input_method::Purpose)>,
}
impl<P, C> Window<P, C>
where
P: Program,
C: Compositor<Renderer = P::Renderer>,
P::Theme: theme::Base,
{
pub fn position(&self) -> Option<Point> {
self.raw
.outer_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)
}
pub fn request_redraw(&mut self, redraw_request: RedrawRequest) {
match redraw_request {
RedrawRequest::NextFrame => {
self.raw.request_redraw();
self.redraw_at = None;
}
RedrawRequest::At(at) => {
self.redraw_at = Some(at);
}
RedrawRequest::Wait => {}
}
}
pub fn request_input_method(&mut self, input_method: InputMethod) {
match input_method {
InputMethod::Disabled => {
self.disable_ime();
}
InputMethod::Enabled {
position,
purpose,
preedit,
} => {
self.enable_ime(position, purpose);
if let Some(preedit) = preedit {
if preedit.content.is_empty() {
self.preedit = None;
} else {
let mut overlay =
self.preedit.take().unwrap_or_else(Preedit::new);
overlay.update(
position,
&preedit,
self.state.background_color(),
&self.renderer,
);
self.preedit = Some(overlay);
}
} else {
self.preedit = None;
}
}
}
}
pub fn draw_preedit(&mut self) {
if let Some(preedit) = &self.preedit {
preedit.draw(
&mut self.renderer,
self.state.text_color(),
self.state.background_color(),
&Rectangle::new(
Point::ORIGIN,
self.state.viewport().logical_size(),
),
);
}
}
fn enable_ime(&mut self, position: Point, purpose: input_method::Purpose) {
if self.ime_state.is_none() {
self.raw.set_ime_allowed(true);
}
if self.ime_state != Some((position, purpose)) {
self.raw.set_ime_cursor_area(
LogicalPosition::new(position.x, position.y),
LogicalSize::new(10, 10), // TODO?
);
self.raw.set_ime_purpose(conversion::ime_purpose(purpose));
self.ime_state = Some((position, purpose));
}
}
fn disable_ime(&mut self) {
if self.ime_state.is_some() {
self.raw.set_ime_allowed(false);
self.ime_state = None;
}
self.preedit = None;
}
}
struct Preedit<Renderer>
where
Renderer: text::Renderer,
{
position: Point,
content: Renderer::Paragraph,
spans: Vec<text::Span<'static, (), Renderer::Font>>,
}
impl<Renderer> Preedit<Renderer>
where
Renderer: text::Renderer,
{
fn new() -> Self {
Self {
position: Point::ORIGIN,
spans: Vec::new(),
content: Renderer::Paragraph::default(),
}
}
fn update(
&mut self,
position: Point,
preedit: &input_method::Preedit,
background: Color,
renderer: &Renderer,
) {
self.position = position;
let spans = match &preedit.selection {
Some(selection) => {
vec![
text::Span::new(&preedit.content[..selection.start]),
text::Span::new(if selection.start == selection.end {
"\u{200A}"
} else {
&preedit.content[selection.start..selection.end]
})
.color(background),
text::Span::new(&preedit.content[selection.end..]),
]
}
_ => vec![text::Span::new(&preedit.content)],
};
if spans != self.spans.as_slice() {
use text::Paragraph as _;
self.content = Renderer::Paragraph::with_spans(Text {
content: &spans,
bounds: Size::INFINITY,
size: preedit
.text_size
.unwrap_or_else(|| renderer.default_size()),
line_height: text::LineHeight::default(),
font: renderer.default_font(),
horizontal_alignment: alignment::Horizontal::Left,
vertical_alignment: alignment::Vertical::Top,
shaping: text::Shaping::Advanced,
wrapping: text::Wrapping::None,
});
self.spans.clear();
self.spans
.extend(spans.into_iter().map(text::Span::to_static));
}
}
fn draw(
&self,
renderer: &mut Renderer,
color: Color,
background: Color,
viewport: &Rectangle,
) {
use text::Paragraph as _;
if self.content.min_width() < 1.0 {
return;
}
let mut bounds = Rectangle::new(
self.position - Vector::new(0.0, self.content.min_height()),
self.content.min_bounds(),
);
bounds.x = bounds
.x
.max(viewport.x)
.min(viewport.x + viewport.width - bounds.width);
bounds.y = bounds
.y
.max(viewport.y)
.min(viewport.y + viewport.height - bounds.height);
renderer.with_layer(bounds, |renderer| {
renderer.fill_quad(
renderer::Quad {
bounds,
..Default::default()
},
background,
);
renderer.fill_paragraph(
&self.content,
bounds.position(),
color,
bounds,
);
const UNDERLINE: f32 = 2.0;
renderer.fill_quad(
renderer::Quad {
bounds: bounds.shrink(Padding {
top: bounds.height - UNDERLINE,
..Default::default()
}),
..Default::default()
},
color,
);
for span_bounds in self.content.span_bounds(1) {
renderer.fill_quad(
renderer::Quad {
bounds: span_bounds
+ (bounds.position() - Point::ORIGIN),
..Default::default()
},
color,
);
}
});
}
}

View file

@ -1,20 +1,21 @@
use crate::futures::futures::{
Future, Sink, StreamExt,
channel::mpsc,
select,
task::{Context, Poll},
Future, Sink, StreamExt,
};
use crate::runtime::Action;
use std::pin::Pin;
/// An event loop proxy with backpressure that implements `Sink`.
#[derive(Debug)]
pub struct Proxy<Message: 'static> {
raw: winit::event_loop::EventLoopProxy<Message>,
sender: mpsc::Sender<Message>,
pub struct Proxy<T: 'static> {
raw: winit::event_loop::EventLoopProxy<Action<T>>,
sender: mpsc::Sender<Action<T>>,
notifier: mpsc::Sender<usize>,
}
impl<Message: 'static> Clone for Proxy<Message> {
impl<T: 'static> Clone for Proxy<T> {
fn clone(&self) -> Self {
Self {
raw: self.raw.clone(),
@ -24,12 +25,12 @@ impl<Message: 'static> Clone for Proxy<Message> {
}
}
impl<Message: 'static> Proxy<Message> {
impl<T: 'static> Proxy<T> {
const MAX_SIZE: usize = 100;
/// Creates a new [`Proxy`] from an `EventLoopProxy`.
pub fn new(
raw: winit::event_loop::EventLoopProxy<Message>,
raw: winit::event_loop::EventLoopProxy<Action<T>>,
) -> (Self, impl Future<Output = ()>) {
let (notifier, mut processed) = mpsc::channel(Self::MAX_SIZE);
let (sender, mut receiver) = mpsc::channel(Self::MAX_SIZE);
@ -72,16 +73,27 @@ impl<Message: 'static> Proxy<Message> {
)
}
/// Sends a `Message` to the event loop.
/// Sends a value to the event loop.
///
/// Note: This skips the backpressure mechanism with an unbounded
/// channel. Use sparingly!
pub fn send(&mut self, message: Message)
pub fn send(&mut self, value: T)
where
Message: std::fmt::Debug,
T: std::fmt::Debug,
{
self.send_action(Action::Output(value));
}
/// Sends an action to the event loop.
///
/// Note: This skips the backpressure mechanism with an unbounded
/// channel. Use sparingly!
pub fn send_action(&mut self, action: Action<T>)
where
T: std::fmt::Debug,
{
self.raw
.send_event(message)
.send_event(action)
.expect("Send message to event loop");
}
@ -92,7 +104,7 @@ impl<Message: 'static> Proxy<Message> {
}
}
impl<Message: 'static> Sink<Message> for Proxy<Message> {
impl<T: 'static> Sink<Action<T>> for Proxy<T> {
type Error = mpsc::SendError;
fn poll_ready(
@ -104,9 +116,9 @@ impl<Message: 'static> Sink<Message> for Proxy<Message> {
fn start_send(
mut self: Pin<&mut Self>,
message: Message,
action: Action<T>,
) -> Result<(), Self::Error> {
self.sender.start_send(message)
self.sender.start_send(action)
}
fn poll_flush(

View file

@ -1,25 +1,26 @@
//! Configure your application.
use crate::core::window;
use crate::core;
use std::borrow::Cow;
/// The settings of an application.
#[derive(Debug, Clone, Default)]
pub struct Settings<Flags> {
pub struct Settings {
/// The identifier of the application.
///
/// If provided, this identifier may be used to identify the application or
/// communicate with it through the windowing system.
pub id: Option<String>,
/// The [`window::Settings`].
pub window: window::Settings,
/// The data needed to initialize an [`Application`].
///
/// [`Application`]: crate::Application
pub flags: Flags,
/// The fonts to load on boot.
pub fonts: Vec<Cow<'static, [u8]>>,
}
impl From<core::Settings> for Settings {
fn from(settings: core::Settings) -> Self {
Self {
id: settings.id,
fonts: settings.fonts,
}
}
}

View file

@ -1,15 +1,13 @@
//! Access the native system.
use crate::graphics::compositor;
use crate::runtime::command::{self, Command};
use crate::runtime::system::{Action, Information};
use crate::runtime::{self, Task};
/// Query for available system information.
pub fn fetch_information<Message>(
f: impl Fn(Information) -> Message + Send + 'static,
) -> Command<Message> {
Command::single(command::Action::System(Action::QueryInformation(
Box::new(f),
)))
pub fn fetch_information() -> Task<Information> {
runtime::task::oneshot(|channel| {
runtime::Action::System(Action::QueryInformation(channel))
})
}
pub(crate) fn information(
@ -19,7 +17,11 @@ pub(crate) fn information(
let mut system = System::new_all();
system.refresh_all();
let cpu = system.global_cpu_info();
let cpu_brand = system
.cpus()
.first()
.map(|cpu| cpu.brand().to_string())
.unwrap_or_default();
let memory_used = sysinfo::get_current_pid()
.and_then(|pid| system.process(pid).ok_or("Process not found"))
@ -31,7 +33,7 @@ pub(crate) fn information(
system_kernel: System::kernel_version(),
system_version: System::long_os_version(),
system_short_version: System::os_version(),
cpu_brand: cpu.brand().into(),
cpu_brand,
cpu_cores: system.physical_core_count(),
memory_total: system.total_memory(),
memory_used,