From c1edb8108430973c6fda79950c611398a990f144 Mon Sep 17 00:00:00 2001 From: Ashley Wulber Date: Thu, 19 Feb 2026 17:01:13 -0500 Subject: [PATCH] fixes: various fixes and cleanup --- Cargo.lock | 19 + Cargo.toml | 4 - core/src/length.rs | 6 + core/src/mouse/click.rs | 1 - core/src/widget/tree.rs | 16 +- examples/multi_window/Cargo.toml | 1 - examples/sctk_drag/src/dnd_destination.rs | 18 +- examples/sctk_drag/src/dnd_source.rs | 10 +- examples/sctk_subsurface/src/main.rs | 21 +- program/src/lib.rs | 133 ----- runtime/Cargo.toml | 1 - runtime/src/multi_window/state.rs | 279 ---------- runtime/src/program/state.rs | 237 -------- src/lib.rs | 23 +- widget/src/lazy/component.rs | 1 - widget/src/lazy/responsive.rs | 522 ------------------ widget/src/mouse_area.rs | 73 ++- widget/src/pane_grid.rs | 14 +- widget/src/scrollable.rs | 2 - winit/Cargo.toml | 1 - winit/src/lib.rs | 339 +++++++----- winit/src/platform_specific/mod.rs | 37 +- .../wayland/event_loop/state.rs | 1 - winit/src/platform_specific/wayland/keymap.rs | 192 +++++++ winit/src/platform_specific/wayland/mod.rs | 44 +- .../platform_specific/wayland/sctk_event.rs | 134 ++++- winit/src/window.rs | 2 + 27 files changed, 676 insertions(+), 1455 deletions(-) delete mode 100644 runtime/src/multi_window/state.rs delete mode 100644 runtime/src/program/state.rs delete mode 100644 widget/src/lazy/responsive.rs diff --git a/Cargo.lock b/Cargo.lock index f951991a..870359d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "accesskit" version = "0.22.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "uuid", ] @@ -28,6 +29,7 @@ dependencies = [ [[package]] name = "accesskit_atspi_common" version = "0.15.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "accesskit", "accesskit_consumer", @@ -39,6 +41,7 @@ dependencies = [ [[package]] name = "accesskit_consumer" version = "0.32.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "accesskit", "hashbrown 0.16.1", @@ -47,6 +50,7 @@ dependencies = [ [[package]] name = "accesskit_macos" version = "0.23.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "accesskit", "accesskit_consumer", @@ -59,6 +63,7 @@ dependencies = [ [[package]] name = "accesskit_unix" version = "0.18.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -77,6 +82,7 @@ dependencies = [ [[package]] name = "accesskit_windows" version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "accesskit", "accesskit_consumer", @@ -89,6 +95,7 @@ dependencies = [ [[package]] name = "accesskit_winit" version = "0.30.0" +source = "git+https://github.com/wash2/accesskit?branch=iced-0.14#1f893441ec3dc5923f5430c02bba762f5444bbea" dependencies = [ "accesskit", "accesskit_macos", @@ -1637,6 +1644,7 @@ dependencies = [ [[package]] name = "dpi" version = "0.1.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" [[package]] name = "drm" @@ -8434,6 +8442,7 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winit" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "cfg_aliases", @@ -8459,6 +8468,7 @@ dependencies = [ [[package]] name = "winit-android" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "android-activity", "bitflags 2.10.0", @@ -8473,6 +8483,7 @@ dependencies = [ [[package]] name = "winit-appkit" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "block2 0.6.2", @@ -8494,6 +8505,7 @@ dependencies = [ [[package]] name = "winit-common" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "memmap2 0.9.9", "objc2 0.6.3", @@ -8508,6 +8520,7 @@ dependencies = [ [[package]] name = "winit-core" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "cursor-icon", @@ -8521,6 +8534,7 @@ dependencies = [ [[package]] name = "winit-orbital" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "dpi", @@ -8536,6 +8550,7 @@ dependencies = [ [[package]] name = "winit-uikit" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "block2 0.6.2", @@ -8555,6 +8570,7 @@ dependencies = [ [[package]] name = "winit-wayland" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "ahash", "bitflags 2.10.0", @@ -8580,6 +8596,7 @@ dependencies = [ [[package]] name = "winit-web" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "atomic-waker", "bitflags 2.10.0", @@ -8601,6 +8618,7 @@ dependencies = [ [[package]] name = "winit-win32" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "cursor-icon", @@ -8616,6 +8634,7 @@ dependencies = [ [[package]] name = "winit-x11" version = "0.31.0-beta.2" +source = "git+https://github.com/pop-os/winit.git?branch=iced-0.14-rebase#a610ac9c7a72b39ff102ed4d946291618dc725b6" dependencies = [ "bitflags 2.10.0", "bytemuck", diff --git a/Cargo.toml b/Cargo.toml index f2d9029d..63dc4c71 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,9 +90,6 @@ advanced = [ "iced_core/advanced", "iced_widget/advanced", ] # Enables the advanced module -multi-window = [ - "iced_winit?/multi-window", -] # Enables experimental multi-window support. basic-shaping = ["iced_core/basic-shaping"] # Enables advanced text shaping by default advanced-shaping = ["iced_core/advanced-shaping"] @@ -336,4 +333,3 @@ useless_conversion = "deny" [workspace.lints.rustdoc] broken_intra_doc_links = "forbid" - diff --git a/core/src/length.rs b/core/src/length.rs index 2259af19..d30061e9 100644 --- a/core/src/length.rs +++ b/core/src/length.rs @@ -78,6 +78,12 @@ impl From for Length { } } +impl From for Length { + fn from(amount: i32) -> Self { + Length::Fixed(amount as f32) + } +} + impl From for Length { fn from(amount: u16) -> Self { Length::Fixed(amount as f32) diff --git a/core/src/mouse/click.rs b/core/src/mouse/click.rs index 5424d25f..51f5fc9d 100644 --- a/core/src/mouse/click.rs +++ b/core/src/mouse/click.rs @@ -46,7 +46,6 @@ impl Click { previous: Option, ) -> Click { let time = Instant::now(); - let kind = if let Some(previous) = previous { if previous.is_consecutive(position, time) && button == previous.button diff --git a/core/src/widget/tree.rs b/core/src/widget/tree.rs index ff93c41a..58da733b 100644 --- a/core/src/widget/tree.rs +++ b/core/src/widget/tree.rs @@ -147,7 +147,6 @@ impl Tree { None } - /// Reconciliates the current tree with the provided [`Widget`]. /// /// If the tag of the [`Widget`] matches the tag of the [`Tree`], then the @@ -166,9 +165,10 @@ impl Tree { new.borrow_mut(); let mut tag_match = self.tag == borrowed.tag(); + if tag_match { if let Some(Id(Internal::Custom(_, n))) = borrowed.id() { - let mut named = NAMED + if let Some((mut state, children)) = NAMED .with(|named| named.borrow_mut().remove(&n)) .or_else(|| { //check self.id @@ -199,14 +199,14 @@ impl Tree { } else { None } - }); - if let Some((mut state, children)) = named { + }) + { std::mem::swap(&mut self.state, &mut state); let widget_children = borrowed.children(); if !tag_match || self.children.len() != widget_children.len() { - self.children = borrowed.children(); + self.children = widget_children; } else { for (old_i, mut old) in children { let Some(my_state) = self.children.get_mut(old_i) @@ -244,8 +244,10 @@ impl Tree { if let Some(id) = self.id.clone() { borrowed.set_id(id); } - if self.children.len() != borrowed.children().len() { - self.children = borrowed.children(); + let borrowed_children = borrowed.children(); + + if self.children.len() != borrowed_children.len() { + self.children = borrowed_children; } } } diff --git a/examples/multi_window/Cargo.toml b/examples/multi_window/Cargo.toml index cf2c5394..8593d86b 100644 --- a/examples/multi_window/Cargo.toml +++ b/examples/multi_window/Cargo.toml @@ -11,6 +11,5 @@ iced = { path = "../..", default-features = false, features = [ "tokio", "debug", "winit", - "multi-window", "tiny-skia", ] } diff --git a/examples/sctk_drag/src/dnd_destination.rs b/examples/sctk_drag/src/dnd_destination.rs index b9564357..1081d8bb 100644 --- a/examples/sctk_drag/src/dnd_destination.rs +++ b/examples/sctk_drag/src/dnd_destination.rs @@ -341,7 +341,7 @@ impl<'a, Message: 'static> Widget ); if matches!(s, event::Status::Captured) { shell.capture_event(); -return; + return; } let state = tree.state.downcast_mut::>(); @@ -383,7 +383,7 @@ return; ); } shell.capture_event(); -return; + return; } Event::Dnd(DndEvent::Offer(id, OfferEvent::Leave)) if id == Some(my_id) => @@ -407,7 +407,7 @@ return; ); } shell.capture_event(); -return; + return; } Event::Dnd(DndEvent::Offer(id, OfferEvent::Motion { x, y })) if id == Some(my_id) => @@ -441,7 +441,7 @@ return; ); } shell.capture_event(); -return; + return; } Event::Dnd(DndEvent::Offer(id, OfferEvent::LeaveDestination)) if id == Some(my_id) => @@ -452,7 +452,7 @@ return; shell.publish(msg); } shell.capture_event(); -return; + return; } Event::Dnd(DndEvent::Offer(id, OfferEvent::Drop)) if id == Some(my_id) => @@ -463,7 +463,7 @@ return; shell.publish(msg); } shell.capture_event(); -return; + return; } Event::Dnd(DndEvent::Offer( id, @@ -478,13 +478,12 @@ return; shell.publish(msg); } shell.capture_event(); -return; + return; } Event::Dnd(DndEvent::Offer( id, OfferEvent::Data { data, mime_type }, )) if id == Some(my_id) => { - dbg!("got data"); if let (Some(msg), ret) = state.on_data_received( mime_type, data, @@ -497,7 +496,7 @@ return; return ret; } shell.capture_event(); -return; + return; } _ => {} } @@ -705,7 +704,6 @@ impl State { impl Fn(String, Vec, DndAction, f64, f64) -> Message, >, ) -> (Option, event::Status) { - dbg!("data received"); let Some(dnd) = self.drag_offer.as_ref() else { self.drag_offer = None; return (None, event::Status::Ignored); diff --git a/examples/sctk_drag/src/dnd_source.rs b/examples/sctk_drag/src/dnd_source.rs index d5bbf94d..1d8b85c4 100644 --- a/examples/sctk_drag/src/dnd_source.rs +++ b/examples/sctk_drag/src/dnd_source.rs @@ -217,9 +217,8 @@ impl< } state.left_pressed_position = Some(position); - // dbg!(&state, &self.id); shell.capture_event(); -return; + return; } } mouse::Event::ButtonReleased(mouse::Button::Left) @@ -227,7 +226,7 @@ return; { state.left_pressed_position = None; shell.capture_event(); -return; + return; } mouse::Event::CursorMoved { .. } => { if let Some(position) = cursor.position() { @@ -240,7 +239,6 @@ return; if let Some(left_pressed_position) = state.left_pressed_position { - // dbg!(&state); if position.distance(left_pressed_position) > self.drag_threshold { @@ -261,7 +259,7 @@ return; state.hovered = true; } shell.capture_event(); -return; + return; } } _ => return ret, @@ -272,7 +270,7 @@ return; if state.is_dragging { state.is_dragging = false; shell.capture_event(); -return; + return; } return ret; } diff --git a/examples/sctk_subsurface/src/main.rs b/examples/sctk_subsurface/src/main.rs index 94380e9d..c00f7225 100644 --- a/examples/sctk_subsurface/src/main.rs +++ b/examples/sctk_subsurface/src/main.rs @@ -7,6 +7,7 @@ use cctk::sctk::reexports::{ use iced::platform_specific::shell::commands::subsurface::get_subsurface; use iced::{ + Element, Length, Subscription, Task, event::wayland::Event as WaylandEvent, platform_specific::{ runtime::wayland::subsurface::SctkSubsurfaceSettings, @@ -14,7 +15,6 @@ use iced::{ }, widget::{button, column, text, text_input}, window::{self, Id, Settings}, - Element, Length, Subscription, Task, }; use std::sync::{Arc, Mutex}; @@ -70,20 +70,17 @@ impl SubsurfaceApp { fn update(&mut self, message: Message) -> Task { match message { - Message::WaylandEvent(evt) => { - dbg!(&evt); - match evt { - WaylandEvent::Output(_evt, output) => { - if self.connection.is_none() { - if let Some(backend) = output.backend().upgrade() { - self.connection = - Some(Connection::from_backend(backend)); - } + Message::WaylandEvent(evt) => match evt { + WaylandEvent::Output(_evt, output) => { + if self.connection.is_none() { + if let Some(backend) = output.backend().upgrade() { + self.connection = + Some(Connection::from_backend(backend)); } } - _ => {} } - } + _ => {} + }, Message::Wayland(evt) => match evt { wayland::Event::RedBuffer(buffer) => { self.red_buffer = Some(buffer); diff --git a/program/src/lib.rs b/program/src/lib.rs index bb710e73..818300f3 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -116,139 +116,6 @@ pub trait Program: Sized { fn presets(&self) -> &[Preset] { &[] } - - // #[cfg(feature = "winit")] - // /// Runs the [`Program`]. - // /// - // /// The state of the [`Program`] must implement [`Default`]. - // /// If your state does not implement [`Default`], use [`run_with`] - // /// instead. - // /// - // /// [`run_with`]: Self::run_with - // fn run( - // self, - // settings: crate::Settings, - // window_settings: Option, - // ) -> crate::Result - // where - // Self: 'static, - // Self::State: Default, - // { - // self.run_with(settings, window_settings, || { - // (Self::State::default(), Task::none()) - // }) - // } - - // #[cfg(feature = "winit")] - // /// Runs the [`Program`] with the given [`Settings`] and a closure that creates the initial state. - // fn run_with( - // self, - // settings: crate::Settings, - // window_settings: Option, - // initialize: I, - // ) -> crate::Result - // where - // Self: 'static, - // I: FnOnce() -> (Self::State, Task) + 'static, - // { - // use std::marker::PhantomData; - - // struct Instance { - // program: P, - // state: P::State, - // _initialize: PhantomData, - // } - - // impl (P::State, Task)> - // shell::Program for Instance - // { - // type Message = P::Message; - // type Theme = P::Theme; - // type Renderer = P::Renderer; - // type Flags = (P, I); - // type Executor = P::Executor; - - // fn new( - // (program, initialize): Self::Flags, - // ) -> (Self, Task) { - // let (state, task) = initialize(); - - // ( - // Self { - // program, - // state, - // _initialize: PhantomData, - // }, - // task, - // ) - // } - - // fn title(&self, window: window::Id) -> String { - // self.program.title(&self.state, window) - // } - - // fn update( - // &mut self, - // message: Self::Message, - // ) -> Task { - // self.program.update(&mut self.state, message) - // } - - // fn view( - // &self, - // window: window::Id, - // ) -> crate::Element<'_, Self::Message, Self::Theme, Self::Renderer> - // { - // self.program.view(&self.state, window) - // } - - // fn subscription(&self) -> Subscription { - // self.program.subscription(&self.state) - // } - - // fn theme(&self, window: window::Id) -> Self::Theme { - // self.program.theme(&self.state, window) - // } - - // fn style(&self, theme: &Self::Theme) -> Appearance { - // self.program.style(&self.state, theme) - // } - - // fn scale_factor(&self, window: window::Id) -> f64 { - // self.program.scale_factor(&self.state, window) - // } - // } - - // #[allow(clippy::needless_update)] - // let renderer_settings = crate::graphics::Settings { - // default_font: settings.default_font, - // default_text_size: settings.default_text_size, - // antialiasing: if settings.antialiasing { - // Some(crate::graphics::Antialiasing::MSAAx4) - // } else { - // None - // }, - // ..crate::graphics::Settings::default() - // }; - - // Ok(shell::program::run::< - // Instance, - // ::Compositor, - // >( - // crate::Settings { - // id: settings.id, - // fonts: settings.fonts, - // default_font: settings.default_font, - // default_text_size: settings.default_text_size, - // antialiasing: settings.antialiasing, - // exit_on_close_request: settings.exit_on_close_request, - // } - // .into(), - // renderer_settings, - // window_settings, - // (self, initialize), - // )?) - // } } /// Decorates a [`Program`] with the given title function. diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index eaa4031b..90ce50eb 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -16,7 +16,6 @@ workspace = true [features] debug = [] selector = ["dep:iced_selector"] -multi-window = [] a11y = ["iced_accessibility", "iced_core/a11y"] wayland = ["iced_core/wayland", "cctk"] diff --git a/runtime/src/multi_window/state.rs b/runtime/src/multi_window/state.rs deleted file mode 100644 index 4cb8d7cc..00000000 --- a/runtime/src/multi_window/state.rs +++ /dev/null @@ -1,279 +0,0 @@ -//! The internal state of a multi-window [`Program`]. -use crate::core::event::{self, Event}; -use crate::core::mouse; -use crate::core::renderer; -use crate::core::widget::operation::{self, Operation}; -use crate::core::{Clipboard, Size}; -use crate::user_interface::{self, UserInterface}; -use crate::{Debug, Program, Task}; - -/// The execution state of a multi-window [`Program`]. It leverages caching, event -/// processing, and rendering primitive storage. -#[allow(missing_debug_implementations)] -pub struct State

-where - P: Program + 'static, -{ - program: P, - caches: Option>, - queued_events: Vec, - queued_messages: Vec, - mouse_interaction: mouse::Interaction, -} - -impl

State

-where - P: Program + 'static, -{ - /// Creates a new [`State`] with the provided [`Program`], initializing its - /// primitive with the given logical bounds and renderer. - pub fn new( - program: P, - bounds: Size, - renderer: &mut P::Renderer, - debug: &mut Debug, - ) -> Self { - let user_interface = build_user_interface( - &program, - user_interface::Cache::default(), - renderer, - bounds, - debug, - ); - - let caches = Some(vec![user_interface.into_cache()]); - - State { - program, - caches, - queued_events: Vec::new(), - queued_messages: Vec::new(), - mouse_interaction: mouse::Interaction::None, - } - } - - /// Returns a reference to the [`Program`] of the [`State`]. - pub fn program(&self) -> &P { - &self.program - } - - /// Queues an event in the [`State`] for processing during an [`update`]. - /// - /// [`update`]: Self::update - pub fn queue_event(&mut self, event: Event) { - self.queued_events.push(event); - } - - /// Queues a message in the [`State`] for processing during an [`update`]. - /// - /// [`update`]: Self::update - pub fn queue_message(&mut self, message: P::Message) { - self.queued_messages.push(message); - } - - /// Returns whether the event queue of the [`State`] is empty or not. - pub fn is_queue_empty(&self) -> bool { - self.queued_events.is_empty() && self.queued_messages.is_empty() - } - - /// Returns the current [`mouse::Interaction`] of the [`State`]. - pub fn mouse_interaction(&self) -> mouse::Interaction { - self.mouse_interaction - } - - /// Processes all the queued events and messages, rebuilding and redrawing - /// the widgets of the linked [`Program`] if necessary. - /// - /// Returns a list containing the instances of [`Event`] that were not - /// captured by any widget, and the [`Task`] obtained from [`Program`] - /// after updating it, only if an update was necessary. - pub fn update( - &mut self, - bounds: Size, - cursor: mouse::Cursor, - renderer: &mut P::Renderer, - theme: &P::Theme, - style: &renderer::Style, - clipboard: &mut dyn Clipboard, - debug: &mut Debug, - ) -> (Vec, Option>) { - let mut user_interfaces = build_user_interfaces( - &self.program, - self.caches.take().unwrap(), - renderer, - bounds, - debug, - ); - - debug.event_processing_started(); - let mut messages = Vec::new(); - - let uncaptured_events = user_interfaces.iter_mut().fold( - vec![], - |mut uncaptured_events, ui| { - let (_, event_statuses) = ui.update( - &self.queued_events, - cursor, - renderer, - clipboard, - &mut messages, - ); - - uncaptured_events.extend( - self.queued_events - .iter() - .zip(event_statuses) - .filter_map(|(event, status)| { - matches!(status, event::Status::Ignored) - .then_some(event) - }) - .cloned(), - ); - uncaptured_events - }, - ); - - self.queued_events.clear(); - messages.append(&mut self.queued_messages); - debug.event_processing_finished(); - - let commands = if messages.is_empty() { - debug.draw_started(); - - for ui in &mut user_interfaces { - self.mouse_interaction = - ui.draw(renderer, theme, style, cursor); - } - - debug.draw_finished(); - - self.caches = Some( - user_interfaces - .drain(..) - .map(UserInterface::into_cache) - .collect(), - ); - - None - } else { - let temp_caches = user_interfaces - .drain(..) - .map(UserInterface::into_cache) - .collect(); - - drop(user_interfaces); - - let commands = Task::batch(messages.into_iter().map(|msg| { - debug.log_message(&msg); - - debug.update_started(); - let task = self.program.update(msg); - debug.update_finished(); - - task - })); - - let mut user_interfaces = build_user_interfaces( - &self.program, - temp_caches, - renderer, - bounds, - debug, - ); - - debug.draw_started(); - for ui in &mut user_interfaces { - self.mouse_interaction = - ui.draw(renderer, theme, style, cursor); - } - debug.draw_finished(); - - self.caches = Some( - user_interfaces - .drain(..) - .map(UserInterface::into_cache) - .collect(), - ); - - Some(commands) - }; - - (uncaptured_events, commands) - } - - /// Applies widget [`Operation`]s to the [`State`]. - pub fn operate( - &mut self, - renderer: &mut P::Renderer, - operations: impl Iterator>, - bounds: Size, - debug: &mut Debug, - ) { - let mut user_interfaces = build_user_interfaces( - &self.program, - self.caches.take().unwrap(), - renderer, - bounds, - debug, - ); - - for operation in operations { - let mut current_operation = Some(operation); - - while let Some(mut operation) = current_operation.take() { - for ui in &mut user_interfaces { - ui.operate(renderer, operation.as_mut()); - } - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(()) => {} - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } - _ => {} - }; - } - } - - self.caches = Some( - user_interfaces - .drain(..) - .map(UserInterface::into_cache) - .collect(), - ); - } -} - -fn build_user_interfaces<'a, P: Program>( - program: &'a P, - mut caches: Vec, - renderer: &mut P::Renderer, - size: Size, - debug: &mut Debug, -) -> Vec> { - caches - .drain(..) - .map(|cache| { - build_user_interface(program, cache, renderer, size, debug) - }) - .collect() -} - -fn build_user_interface<'a, P: Program>( - program: &'a P, - cache: user_interface::Cache, - renderer: &mut P::Renderer, - size: Size, - debug: &mut Debug, -) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> { - debug.view_started(); - let view = program.view(); - debug.view_finished(); - - debug.layout_started(); - let user_interface = UserInterface::build(view, size, cache, renderer); - debug.layout_finished(); - - user_interface -} diff --git a/runtime/src/program/state.rs b/runtime/src/program/state.rs deleted file mode 100644 index 1eb2d770..00000000 --- a/runtime/src/program/state.rs +++ /dev/null @@ -1,237 +0,0 @@ -use crate::core::event::{self, Event}; -use crate::core::mouse; -use crate::core::renderer; -use crate::core::widget::operation::{self, Operation}; -use crate::core::{Clipboard, Size}; -use crate::user_interface::{self, UserInterface}; -use crate::{Debug, Program, Task}; - -/// The execution state of a [`Program`]. It leverages caching, event -/// processing, and rendering primitive storage. -#[allow(missing_debug_implementations)] -pub struct State

-where - P: Program + 'static, -{ - program: P, - cache: Option, - queued_events: Vec, - queued_messages: Vec, - mouse_interaction: mouse::Interaction, -} - -impl

State

-where - P: Program + 'static, -{ - /// Creates a new [`State`] with the provided [`Program`], initializing its - /// primitive with the given logical bounds and renderer. - pub fn new( - id: iced_core::id::Id, - mut program: P, - bounds: Size, - renderer: &mut P::Renderer, - debug: &mut Debug, - ) -> Self { - let user_interface = build_user_interface( - id, - &mut program, - user_interface::Cache::default(), - renderer, - bounds, - debug, - ); - - let cache = Some(user_interface.into_cache()); - - State { - program, - cache, - queued_events: Vec::new(), - queued_messages: Vec::new(), - mouse_interaction: mouse::Interaction::None, - } - } - - /// Returns a reference to the [`Program`] of the [`State`]. - pub fn program(&self) -> &P { - &self.program - } - - /// Queues an event in the [`State`] for processing during an [`update`]. - /// - /// [`update`]: Self::update - pub fn queue_event(&mut self, event: Event) { - self.queued_events.push(event); - } - - /// Queues a message in the [`State`] for processing during an [`update`]. - /// - /// [`update`]: Self::update - pub fn queue_message(&mut self, message: P::Message) { - self.queued_messages.push(message); - } - - /// Returns whether the event queue of the [`State`] is empty or not. - pub fn is_queue_empty(&self) -> bool { - self.queued_events.is_empty() && self.queued_messages.is_empty() - } - - /// Returns the current [`mouse::Interaction`] of the [`State`]. - pub fn mouse_interaction(&self) -> mouse::Interaction { - self.mouse_interaction - } - - /// Processes all the queued events and messages, rebuilding and redrawing - /// the widgets of the linked [`Program`] if necessary. - /// - /// Returns a list containing the instances of [`Event`] that were not - /// captured by any widget, and the [`Task`] obtained from [`Program`] - /// after updating it, only if an update was necessary. - pub fn update( - &mut self, - id: iced_core::id::Id, - bounds: Size, - cursor: mouse::Cursor, - renderer: &mut P::Renderer, - theme: &P::Theme, - style: &renderer::Style, - clipboard: &mut dyn Clipboard, - debug: &mut Debug, - ) -> (Vec, Option>) { - let mut user_interface = build_user_interface( - id.clone(), - &mut self.program, - self.cache.take().unwrap(), - renderer, - bounds, - debug, - ); - - debug.event_processing_started(); - let mut messages = Vec::new(); - - let (_, event_statuses) = user_interface.update( - &self.queued_events, - cursor, - renderer, - clipboard, - &mut messages, - ); - - let uncaptured_events = self - .queued_events - .iter() - .zip(event_statuses) - .filter_map(|(event, status)| { - matches!(status, event::Status::Ignored).then_some(event) - }) - .cloned() - .collect(); - - self.queued_events.clear(); - messages.append(&mut self.queued_messages); - debug.event_processing_finished(); - - let task = if messages.is_empty() { - debug.draw_started(); - self.mouse_interaction = - user_interface.draw(renderer, theme, style, cursor); - debug.draw_finished(); - - self.cache = Some(user_interface.into_cache()); - - None - } else { - // When there are messages, we are forced to rebuild twice - // for now :^) - let temp_cache = user_interface.into_cache(); - - let tasks = Task::batch(messages.into_iter().map(|message| { - debug.log_message(&message); - - debug.update_started(); - let task = self.program.update(message); - debug.update_finished(); - - task - })); - - let mut user_interface = build_user_interface( - id, - &mut self.program, - temp_cache, - renderer, - bounds, - debug, - ); - - debug.draw_started(); - self.mouse_interaction = - user_interface.draw(renderer, theme, style, cursor); - debug.draw_finished(); - - self.cache = Some(user_interface.into_cache()); - - Some(tasks) - }; - - (uncaptured_events, task) - } - - /// Applies [`Operation`]s to the [`State`] - pub fn operate( - &mut self, - id: iced_core::id::Id, - renderer: &mut P::Renderer, - operations: impl Iterator>, - bounds: Size, - debug: &mut Debug, - ) { - let mut user_interface = build_user_interface( - id, - &mut self.program, - self.cache.take().unwrap(), - renderer, - bounds, - debug, - ); - - for operation in operations { - let mut current_operation = Some(operation); - - while let Some(mut operation) = current_operation.take() { - user_interface.operate(renderer, operation.as_mut()); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(()) => {} - operation::Outcome::Chain(next) => { - current_operation = Some(next); - } - }; - } - } - - self.cache = Some(user_interface.into_cache()); - } -} - -fn build_user_interface<'a, P: Program>( - _id: iced_core::id::Id, - program: &'a mut P, - cache: user_interface::Cache, - renderer: &mut P::Renderer, - size: Size, - debug: &mut Debug, -) -> UserInterface<'a, P::Message, P::Theme, P::Renderer> { - debug.view_started(); - let view = program.view(); - debug.view_finished(); - - debug.layout_started(); - let user_interface = UserInterface::build(view, size, cache, renderer); - debug.layout_finished(); - - user_interface -} diff --git a/src/lib.rs b/src/lib.rs index cc5a4465..fbc6bac2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -492,17 +492,17 @@ pub use iced_widget::core; #[cfg(feature = "winit")] use iced_winit as shell; -#[cfg(not(any( - target_arch = "wasm32", - feature = "thread-pool", - feature = "tokio", - feature = "smol" -)))] -compile_error!( - "No futures executor has been enabled! You must enable an \ - executor feature.\n\ - Available options: thread-pool, tokio, or smol." -); +// #[cfg(not(any( +// target_arch = "wasm32", +// feature = "thread-pool", +// feature = "tokio", +// feature = "smol" +// )))] +// compile_error!( +// "No futures executor has been enabled! You must enable an \ +// executor feature.\n\ +// Available options: thread-pool, tokio, or smol." +// ); // #[cfg(all( // target_family = "unix", @@ -515,7 +515,6 @@ compile_error!( // display server feature.\n\ // Available options: x11, wayland." // ); - #[cfg(feature = "highlighter")] pub use iced_highlighter as highlighter; diff --git a/widget/src/lazy/component.rs b/widget/src/lazy/component.rs index ca259407..a810e117 100644 --- a/widget/src/lazy/component.rs +++ b/widget/src/lazy/component.rs @@ -373,7 +373,6 @@ where } .build(), )); - shell.invalidate_layout(); shell.request_redraw(); } diff --git a/widget/src/lazy/responsive.rs b/widget/src/lazy/responsive.rs deleted file mode 100644 index 0f25101f..00000000 --- a/widget/src/lazy/responsive.rs +++ /dev/null @@ -1,522 +0,0 @@ -use crate::core::event::{self, Event}; -use crate::core::layout::{self, Layout}; -use crate::core::mouse; -use crate::core::overlay; -use crate::core::renderer; -use crate::core::widget::tree::{self, Tree}; -use crate::core::{ - self, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, - Widget, -}; -use crate::horizontal_space; -use crate::runtime::overlay::Nested; - -use iced_renderer::core::widget::Operation; -use ouroboros::self_referencing; -use std::cell::{RefCell, RefMut}; -use std::marker::PhantomData; -use std::ops::Deref; - -/// A widget that is aware of its dimensions. -/// -/// A [`Responsive`] widget will always try to fill all the available space of -/// its parent. -#[cfg(feature = "lazy")] -#[allow(missing_debug_implementations)] -pub struct Responsive< - 'a, - Message, - Theme = crate::Theme, - Renderer = crate::Renderer, -> { - view: Box Element<'a, Message, Theme, Renderer> + 'a>, - content: RefCell>, -} - -impl<'a, Message, Theme, Renderer> Responsive<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - /// Creates a new [`Responsive`] widget with a closure that produces its - /// contents. - /// - /// The `view` closure will be provided with the current [`Size`] of - /// the [`Responsive`] widget and, therefore, can be used to build the - /// contents of the widget in a responsive way. - pub fn new( - view: impl Fn(Size) -> Element<'a, Message, Theme, Renderer> + 'a, - ) -> Self { - Self { - view: Box::new(view), - content: RefCell::new(Content { - size: Size::ZERO, - layout: None, - element: Element::new(horizontal_space().width(0)), - }), - } - } -} - -struct Content<'a, Message, Theme, Renderer> { - size: Size, - layout: Option, - element: Element<'a, Message, Theme, Renderer>, -} - -impl<'a, Message, Theme, Renderer> Content<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn layout(&mut self, tree: &mut Tree, renderer: &Renderer) { - if self.layout.is_none() { - self.layout = Some(self.element.as_widget().layout( - tree, - renderer, - &layout::Limits::new(Size::ZERO, self.size), - )); - } - } - - fn update( - &mut self, - tree: &mut Tree, - new_size: Size, - view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, - ) { - if self.size == new_size { - return; - } - - self.element = view(new_size); - self.size = new_size; - self.layout = None; - - tree.diff(&mut self.element); - } - - fn resolve( - &mut self, - tree: &mut Tree, - renderer: R, - layout: Layout<'_>, - view: &dyn Fn(Size) -> Element<'a, Message, Theme, Renderer>, - f: impl FnOnce( - &mut Tree, - R, - Layout<'_>, - &mut Element<'a, Message, Theme, Renderer>, - ) -> T, - ) -> T - where - R: Deref, - { - self.update(tree, layout.bounds().size(), view); - self.layout(tree, renderer.deref()); - - let content_layout = Layout::with_offset( - layout.position() - Point::ORIGIN, - self.layout.as_ref().unwrap(), - ); - - f(tree, renderer, content_layout, &mut self.element) - } -} - -struct State { - tree: RefCell, -} - -impl<'a, Message, Theme, Renderer> Widget - for Responsive<'a, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn tag(&self) -> tree::Tag { - tree::Tag::of::() - } - - fn state(&self) -> tree::State { - tree::State::new(State { - tree: RefCell::new(Tree::empty()), - }) - } - - fn size(&self) -> Size { - Size { - width: Length::Fill, - height: Length::Fill, - } - } - - fn layout( - &self, - _tree: &mut Tree, - _renderer: &Renderer, - limits: &layout::Limits, - ) -> layout::Node { - layout::Node::new(limits.max()) - } - - fn operate( - &self, - tree: &mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn crate::core::widget::Operation, - ) { - let state = tree.state.downcast_mut::(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element - .as_widget() - .operate(tree, layout, renderer, operation); - }, - ); - } - - fn update( - &mut self, - tree: &mut Tree, - event: Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - viewport: &Rectangle, - ) -> event::Status { - let state = tree.state.downcast_mut::(); - let mut content = self.content.borrow_mut(); - - let mut local_messages = vec![]; - let mut local_shell = Shell::new(&mut local_messages); - - let status = content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget_mut().on_event( - tree, - event, - layout, - cursor, - renderer, - clipboard, - &mut local_shell, - viewport, - ) - }, - ); - - if local_shell.is_layout_invalid() { - content.layout = None; - } - - shell.merge(local_shell, std::convert::identity); - - status - } - - fn draw( - &self, - tree: &Tree, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - ) { - let state = tree.state.downcast_ref::(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element.as_widget().draw( - tree, renderer, theme, style, layout, cursor, viewport, - ); - }, - ); - } - - fn mouse_interaction( - &self, - tree: &Tree, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - let state = tree.state.downcast_ref::(); - let mut content = self.content.borrow_mut(); - - content.resolve( - &mut state.tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, renderer, layout, element| { - element - .as_widget() - .mouse_interaction(tree, layout, cursor, viewport, renderer) - }, - ) - } - - fn overlay<'b>( - &'b mut self, - tree: &'b mut Tree, - layout: Layout<'_>, - renderer: &Renderer, - translation: Vector, - ) -> Option> { - use std::ops::DerefMut; - - let state = tree.state.downcast_ref::(); - - let overlay = OverlayBuilder { - content: self.content.borrow_mut(), - tree: state.tree.borrow_mut(), - types: PhantomData, - overlay_builder: |content: &mut RefMut< - '_, - Content<'_, _, _, _>, - >, - tree| { - content.update(tree, layout.bounds().size(), &self.view); - content.layout(tree, renderer); - - let Content { - element, - layout: content_layout_node, - .. - } = content.deref_mut(); - - let content_layout = Layout::with_offset( - layout.bounds().position() - Point::ORIGIN, - content_layout_node.as_ref().unwrap(), - ); - - ( - element - .as_widget_mut() - .overlay(tree, content_layout, renderer, translation) - .map(|overlay| RefCell::new(Nested::new(overlay))), - content_layout_node, - ) - }, - } - .build(); - - if overlay.with_overlay(|(overlay, _layout)| overlay.is_some()) { - Some(overlay::Element::new(Box::new(overlay))) - } else { - None - } - } - - #[cfg(feature = "a11y")] - fn a11y_nodes( - &self, - layout: Layout<'_>, - tree: &Tree, - cursor_position: mouse::Cursor, - ) -> iced_accessibility::A11yTree { - let state = tree.state.downcast_ref::().tree.borrow(); - self.content.borrow().element.as_widget().a11y_nodes( - layout, - &*state, - cursor_position, - ) - } - - fn id(&self) -> Option { - self.content.borrow().element.as_widget().id() - } - - fn set_id(&mut self, _id: iced_runtime::core::id::Id) { - self.content - .borrow_mut() - .element - .as_widget_mut() - .set_id(_id); - } - - fn drag_destinations( - &self, - state: &Tree, - layout: Layout<'_>, - renderer: &Renderer, - dnd_rectangles: &mut core::clipboard::DndDestinationRectangles, - ) { - let ret = self.content.borrow_mut().resolve( - &mut state.state.downcast_ref::().tree.borrow_mut(), - renderer, - layout, - &self.view, - |tree, r, layout, element| { - element.as_widget().drag_destinations( - tree, - layout, - r, - dnd_rectangles, - ); - }, - ); - ret - } -} - -impl<'a, Message, Theme, Renderer> - From> - for Element<'a, Message, Theme, Renderer> -where - Message: 'a, - Theme: 'a, - Renderer: core::Renderer + 'a, -{ - fn from(responsive: Responsive<'a, Message, Theme, Renderer>) -> Self { - Self::new(responsive) - } -} - -#[self_referencing] -struct Overlay<'a, 'b, Message, Theme, Renderer> { - content: RefMut<'a, Content<'b, Message, Theme, Renderer>>, - tree: RefMut<'a, Tree>, - types: PhantomData, - - #[borrows(mut content, mut tree)] - #[not_covariant] - overlay: ( - Option>>, - &'this mut Option, - ), -} - -impl<'a, 'b, Message, Theme, Renderer> - Overlay<'a, 'b, Message, Theme, Renderer> -{ - fn with_overlay_maybe( - &self, - f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T, - ) -> Option { - self.with_overlay(|(overlay, _layout)| { - overlay.as_ref().map(|nested| (f)(&mut nested.borrow_mut())) - }) - } - - fn with_overlay_mut_maybe( - &mut self, - f: impl FnOnce(&mut Nested<'_, Message, Theme, Renderer>) -> T, - ) -> Option { - self.with_overlay_mut(|(overlay, _layout)| { - overlay.as_mut().map(|nested| (f)(nested.get_mut())) - }) - } -} - -impl<'a, 'b, Message, Theme, Renderer> - overlay::Overlay - for Overlay<'a, 'b, Message, Theme, Renderer> -where - Renderer: core::Renderer, -{ - fn layout(&mut self, renderer: &Renderer, bounds: Size) -> layout::Node { - self.with_overlay_maybe(|overlay| overlay.layout(renderer, bounds)) - .unwrap_or_default() - } - - fn draw( - &self, - renderer: &mut Renderer, - theme: &Theme, - style: &renderer::Style, - layout: Layout<'_>, - cursor: mouse::Cursor, - ) { - let _ = self.with_overlay_maybe(|overlay| { - overlay.draw(renderer, theme, style, layout, cursor); - }); - } - - fn mouse_interaction( - &self, - layout: Layout<'_>, - cursor: mouse::Cursor, - viewport: &Rectangle, - renderer: &Renderer, - ) -> mouse::Interaction { - self.with_overlay_maybe(|overlay| { - overlay.mouse_interaction(layout, cursor, viewport, renderer) - }) - .unwrap_or_default() - } - - fn update( - &mut self, - event: Event, - layout: Layout<'_>, - cursor: mouse::Cursor, - renderer: &Renderer, - clipboard: &mut dyn Clipboard, - shell: &mut Shell<'_, Message>, - ) -> event::Status { - let mut is_layout_invalid = false; - - let event_status = self - .with_overlay_mut_maybe(|overlay| { - let event_status = overlay.on_event( - event, layout, cursor, renderer, clipboard, shell, - ); - - is_layout_invalid = shell.is_layout_invalid(); - - event_status - }) - .unwrap_or(event::Status::Ignored); - - if is_layout_invalid { - self.with_overlay_mut(|(_overlay, layout)| { - **layout = None; - }); - } - - event_status - } - - fn is_over( - &self, - layout: Layout<'_>, - renderer: &Renderer, - cursor_position: Point, - ) -> bool { - self.with_overlay_maybe(|overlay| { - overlay.is_over(layout, renderer, cursor_position) - }) - .unwrap_or_default() - } - - fn operate( - &mut self, - layout: Layout<'_>, - renderer: &Renderer, - operation: &mut dyn Operation, - ) { - let _ = self.with_overlay_mut_maybe(|overlay| { - overlay.operate(layout, renderer, operation); - }); - } -} diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs index 1265a309..a5182b40 100644 --- a/widget/src/mouse_area.rs +++ b/widget/src/mouse_area.rs @@ -158,7 +158,7 @@ struct State { // TODO: Support on_enter and on_exit drag_initiated: Option, is_out_of_bounds: bool, - last_click: Option, + last_press: Option, } impl Default for State { fn default() -> Self { @@ -166,7 +166,7 @@ impl Default for State { is_hovered: Default::default(), drag_initiated: None, is_out_of_bounds: true, - last_click: None, + last_press: None, cursor_position: None, bounds: Rectangle::default(), previous_click: None, @@ -456,44 +456,46 @@ fn update( return; } - if let Some(message) = widget.on_double_press.as_ref() { - if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) = - event - { - if let Some(cursor_position) = cursor.position() { - let click = mouse::Click::new( - cursor_position, - mouse::Button::Left, - state.last_click, - ); - state.last_click = Some(click); - if let mouse::click::Kind::Double = click.kind() { - state.drag_initiated = None; - shell.publish(message.clone()); - shell.capture_event(); - - return; - } - } - } - } - if let Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) | Event::Touch(touch::Event::FingerPressed { .. }) = event { - let mut captured = false; + if let Some(position) = cursor_position { + let new_click = mouse::Click::new( + position, + mouse::Button::Left, + state.last_press, + ); + if new_click.kind() == mouse::click::Kind::Double + && let Some(double_press) = widget.on_double_press.as_ref() + { + shell.publish(double_press.clone()); + } else if let Some(on_press_message) = widget.on_press.as_ref() { + shell.publish(on_press_message.clone()); + } - if let Some(position) = cursor_position - && let Some(message) = widget.on_double_click.as_ref() - { + state.last_press = Some(new_click); + + // Even if this is not a double click, but the press is nevertheless + // processed by us and should not be popup to parent widgets. + shell.capture_event(); + } + } + + if let Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) + | Event::Touch(touch::Event::FingerLifted { .. }) = event + { + if let Some(position) = cursor_position { let new_click = mouse::Click::new( position, mouse::Button::Left, state.previous_click, ); - - if new_click.kind() == mouse::click::Kind::Double { - shell.publish(message.clone()); + if new_click.kind() == mouse::click::Kind::Double + && let Some(double_press) = widget.on_double_click.as_ref() + { + shell.publish(double_press.clone()); + } else if let Some(on_press_message) = widget.on_release.as_ref() { + shell.publish(on_press_message.clone()); } state.previous_click = Some(new_click); @@ -505,15 +507,6 @@ fn update( } match event { - Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) - | Event::Touch(touch::Event::FingerLifted { .. }) => { - state.drag_initiated = None; - if let Some(message) = widget.on_release.as_ref() { - shell.publish(message.clone()); - } - shell.capture_event(); - return; - } Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Right)) => { if let Some(message) = widget.on_right_release.as_ref() { shell.publish(message.clone()); diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 2d4683eb..ed765190 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -400,13 +400,13 @@ where retain }); - // let ids = self.contents.iter().map(|_| None).collect(); // TODO - // tree.diff_children_custom( - // &mut self.contents, - // ids, - // |state, (_, content): (_, _)| content.diff(state), - // |(_, content): (_, _)| content.state(), - // ); + let ids = self.contents.iter().map(|_| None).collect(); // TODO + tree.diff_children_custom( + &mut self.contents, + ids, + |state, content: _| content.diff(state), + |content: _| content.state(), + ); let Memory { order, .. } = tree.state.downcast_mut(); order.clone_from(&self.panes); diff --git a/widget/src/scrollable.rs b/widget/src/scrollable.rs index 6af5303d..d88e2139 100644 --- a/widget/src/scrollable.rs +++ b/widget/src/scrollable.rs @@ -1188,7 +1188,6 @@ where if delta.y.abs() < AUTOSCROLL_DEADZONE { delta.y = 0.0; } - if delta.x != 0.0 || delta.y != 0.0 { let time_delta = if let Some(last_frame) = last_frame { @@ -2007,7 +2006,6 @@ fn notify_viewport( } state.last_notified = Some(viewport); - if let Some(on_scroll) = on_scroll { shell.publish(on_scroll(viewport)); } diff --git a/winit/Cargo.toml b/winit/Cargo.toml index db936217..c6e32e94 100644 --- a/winit/Cargo.toml +++ b/winit/Cargo.toml @@ -36,7 +36,6 @@ system = ["sysinfo"] program = [] wayland-dlopen = ["winit/wayland-dlopen"] wayland-csd-adwaita = ["winit/wayland-csd-adwaita"] -multi-window = ["iced_runtime/multi-window"] a11y = ["iced_accessibility", "iced_runtime/a11y"] diff --git a/winit/src/lib.rs b/winit/src/lib.rs index 33944c1b..2dc57391 100644 --- a/winit/src/lib.rs +++ b/winit/src/lib.rs @@ -27,6 +27,7 @@ pub use program::runtime; pub use runtime::futures; use window_clipboard::mime::ClipboardStoreData; pub use winit; +use winit::event::WindowEvent; use winit::raw_window_handle::HasWindowHandle; #[cfg(feature = "a11y")] @@ -76,6 +77,7 @@ use window::WindowManager; use rustc_hash::FxHashMap; use std::borrow::Cow; use std::mem::ManuallyDrop; +use std::ops::ControlFlow; use std::slice; use std::sync::Arc; @@ -130,7 +132,13 @@ where let task = if let Some(window_settings) = window_settings { let mut task = Some(task); - let (_id, open) = runtime::window::open(window_settings); + let open = iced_runtime::task::oneshot(|channel| { + iced_runtime::Action::Window(iced_runtime::window::Action::Open( + window::Id::RESERVED, + window_settings, + channel, + )) + }); open.then(move |_| task.take().unwrap_or_else(Task::none)) } else { @@ -532,52 +540,49 @@ where .start_send(Event::StartDnd) .expect("Send event"); } + #[cfg(feature = "a11y")] Control::InitAdapter(id, window) => { - #[cfg(feature = "a11y")] - { - use crate::a11y::*; - use iced_accessibility::accesskit::{ - ActivationHandler, Node, NodeId, Role, - Tree, TreeUpdate, + use crate::a11y::*; + use iced_accessibility::accesskit::{ + ActivationHandler, Node, NodeId, Role, + Tree, TreeUpdate, + }; + use iced_accessibility::accesskit_winit::Adapter; + + let node_id = + iced_runtime::core::id::window_node_id(); + + let activation_handler = + WinitActivationHandler { + proxy: self.control_sender.clone(), + title: String::new(), }; - use iced_accessibility::accesskit_winit::Adapter; - let node_id = - iced_runtime::core::id::window_node_id( - ); + let action_handler = WinitActionHandler { + id, + proxy: self.control_sender.clone(), + }; - let activation_handler = - WinitActivationHandler { - proxy: self.control_sender.clone(), - title: String::new(), - }; - - let action_handler = WinitActionHandler { - id, + let deactivation_handler = + WinitDeactivationHandler { proxy: self.control_sender.clone(), }; - let deactivation_handler = - WinitDeactivationHandler { - proxy: self.control_sender.clone(), - }; - - self.sender - .start_send(Event::A11yAdapter( - id, - ( - node_id, - Adapter::with_direct_handlers( - event_loop, - window.as_ref(), - activation_handler, - action_handler, - deactivation_handler, - ), + self.sender + .start_send(Event::A11yAdapter( + id, + ( + node_id, + Adapter::with_direct_handlers( + event_loop, + window.as_ref(), + activation_handler, + action_handler, + deactivation_handler, ), - )) - .expect("send event"); - } + ), + )) + .expect("send event"); } }, _ => { @@ -776,69 +781,32 @@ async fn run_instance

( control_sender .start_send(Control::InitAdapter(id, window.clone())) .expect("Send control message"); - if compositor.is_none() { - let (compositor_sender, compositor_receiver) = - oneshot::channel(); - - let create_compositor = { - let window = window.clone(); - let display_handle = display_handle.clone(); - let proxy = proxy.clone(); - let default_fonts = default_fonts.clone(); - - async move { - let shell = Shell::new(proxy.clone()); - - let mut compositor = - ::Compositor::new( - graphics_settings, - display_handle, - window, - shell, - ).await; - - if let Ok(compositor) = &mut compositor { - for font in default_fonts { - compositor.load_font(font.clone()); - } - } - - compositor_sender - .send(compositor) - .ok() - .expect("Send compositor"); - - // HACK! Send a proxy event on completion to trigger - // a runtime re-poll - // TODO: Send compositor through proxy (?) - { - let (sender, _receiver) = oneshot::channel(); - - proxy.send_action(Action::Window( - runtime::window::Action::GetLatest(sender), - )); - } - } - }; - - #[cfg(target_arch = "wasm32")] - wasm_bindgen_futures::spawn_local(create_compositor); - - #[cfg(not(target_arch = "wasm32"))] - runtime.block_on(create_compositor); - - match compositor_receiver - .await - .expect("Wait for compositor") - { - Ok(new_compositor) => { - compositor = Some(new_compositor); - } - Err(error) => { - let _ = control_sender - .start_send(Control::Crash(error.into())); - continue; - } + #[cfg(feature = "wayland")] + platform_specific_handler.send_wayland( + platform_specific::Action::TrackWindow(window.clone(), id), + ); + match create_compositor::

( + window.clone(), + CreateCompositor { + proxy: &proxy, + runtime: &mut runtime, + display_handle: &display_handle, + graphics_settings: &graphics_settings, + default_fonts: &default_fonts, + }, + ) + .await + { + Ok(comp) => { + compositor = Some(comp); + } + Err(error) => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed(error), + )) + .expect("Send control message"); + continue; } } @@ -982,6 +950,22 @@ async fn run_instance

( else { continue; }; + // XX must force update to corner radius before the surface is committed. + #[cfg(feature = "wayland")] + if window.surface_version != window.state.surface_version() + || window.logical_size() != window.state.logical_size() + { + platform_specific_handler.send_wayland( + platform_specific::Action::ResizeWindow(id), + ); + window.redraw_requested = true; + control_sender + .start_send(Control::Winit( + window.raw.id(), + WindowEvent::RedrawRequested, + )) + .expect("Send redraw event"); + } window.redraw_requested = false; let physical_size = window.state.physical_size(); @@ -1330,7 +1314,6 @@ async fn run_instance

( true } }); - let no_window_events = window_events.is_empty(); #[cfg(feature = "wayland")] window_events.push(core::Event::PlatformSpecific( @@ -1376,6 +1359,14 @@ async fn run_instance

( _ = window.raw.request_surface_size(s); window.raw.set_min_surface_size(Some(s)); window.raw.set_max_surface_size(Some(s)); + + window.state.update( + &program, + window.raw.as_ref(), + &WindowEvent::SurfaceResized( + requested_physical_size, + ), + ); window.state.synchronize( &program, id, @@ -1499,7 +1490,9 @@ async fn run_instance

( .start_send(Control::ChangeFlow(ControlFlow::Wait)); } } - Event::Exit => break, + Event::Exit => { + break; + } Event::Dnd(e) => { use winit::raw_window_handle::HasWindowHandle; @@ -1569,23 +1562,26 @@ async fn run_instance

( a11y_enabled = enabled; } Event::PlatformSpecific(e) => { - if let Some(c) = compositor.as_mut() { - crate::platform_specific::handle_event( - e, - &mut events, - &mut platform_specific_handler, - &program, - c, - &mut window_manager, - &mut user_interfaces, - &mut clipboard, - #[cfg(feature = "a11y")] - &mut adapters, - ); - } else { - // TODO should we break? - break; - } + crate::platform_specific::handle_event( + e, + &mut events, + &mut platform_specific_handler, + &program, + &mut compositor, + &mut window_manager, + &mut user_interfaces, + &mut clipboard, + #[cfg(feature = "a11y")] + &mut adapters, + CreateCompositor { + proxy: &proxy, + display_handle: &display_handle, + graphics_settings: &graphics_settings, + default_fonts: &default_fonts, + runtime: &mut runtime, + }, + ) + .await; } Event::StartDnd => { let compositor = match compositor.as_mut() { @@ -1614,12 +1610,15 @@ async fn run_instance

( Some(s) } core::clipboard::DndSource::Widget(w) => { - log::debug!( + log::trace!( "start_dnd: searching for widget {:?}", w ); - // search windows for widget with operation - let result = user_interfaces.iter_mut().find_map( + // if user intefaces is just 1 len, use the single id instead of wasting time searching + let result = if user_interfaces.len() ==1 { + user_interfaces.keys().next().cloned() + } else { + user_interfaces.iter_mut().find_map( |(ui_id, ui)| { let Some(ui_renderer) = window_manager .get_mut(ui_id.clone()) @@ -1666,7 +1665,11 @@ async fn run_instance

( } None }, - ); + ) + }; + + // search windows for widget with operation + if result.is_none() { log::warn!( "start_dnd: widget {:?} not found; drag will fail", @@ -1817,6 +1820,88 @@ async fn run_instance

( let _ = ManuallyDrop::into_inner(user_interfaces); } +struct CreateCompositor<'a, P: Program> { + proxy: &'a Proxy<

::Message>, + display_handle: &'a winit::event_loop::OwnedDisplayHandle, + graphics_settings: &'a iced_graphics::Settings, + default_fonts: &'a [Cow<'static, [u8]>], + runtime: &'a mut Runtime< +

::Executor, + Proxy<

::Message>, + Action<

::Message>, + >, +} + +async fn create_compositor<'a, P>( + window: Arc, + CreateCompositor { + proxy, + display_handle, + graphics_settings, + default_fonts, + runtime, + }: CreateCompositor<'a, P>, +) -> Result< + <

::Renderer as compositor::Default>::Compositor, + iced_graphics::Error, +> +where + P: Program, +{ + let (compositor_sender, compositor_receiver) = oneshot::channel(); + + let create_compositor = { + let display_handle = display_handle.clone(); + let proxy = proxy.clone(); + let graphics_settings = (*graphics_settings).clone(); + let window = window.clone(); + let default_fonts = default_fonts.to_vec(); + + async move { + let shell = Shell::new(proxy.clone()); + + let mut compositor = + ::Compositor::new( + graphics_settings, + display_handle, + window, + shell, + ) + .await; + + if let Ok(compositor) = &mut compositor { + for font in default_fonts { + compositor.load_font(font.clone()); + } + } + + compositor_sender + .send(compositor) + .ok() + .expect("Send compositor"); + + // HACK! Send a proxy event on completion to trigger + // a runtime re-poll + // TODO: Send compositor through proxy (?) + { + let (sender, _receiver) = oneshot::channel(); + + proxy.send_action(Action::Window( + runtime::window::Action::GetLatest(sender), + )); + } + } + }; + + #[cfg(target_arch = "wasm32")] + wasm_bindgen_futures::spawn_local(create_compositor); + + #[cfg(not(target_arch = "wasm32"))] + runtime.block_on(create_compositor); + + compositor_receiver.await.expect("Wait for compositor") +} + /// Builds a window's [`UserInterface`] for the [`Program`]. fn build_user_interface<'a, P: Program>( program: &'a program::Instance

, @@ -1965,7 +2050,9 @@ where let _ = ui_caches.remove(&id); let _ = interfaces.remove(&id); let proxy = clipboard.proxy(); - + #[cfg(feature = "wayland")] + platform_specific + .send_wayland(platform_specific::Action::RemoveWindow(id)); if let Some(window) = window_manager.remove(id) { if clipboard.window_id() == Some(window.raw.id()) { *clipboard = window_manager diff --git a/winit/src/platform_specific/mod.rs b/winit/src/platform_specific/mod.rs index af233fed..3f02b08f 100644 --- a/winit/src/platform_specific/mod.rs +++ b/winit/src/platform_specific/mod.rs @@ -1,7 +1,7 @@ //! Wayland specific shell //! -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap, sync::Arc}; #[cfg(all(feature = "wayland", target_os = "linux"))] use cctk::sctk::reexports::client::Connection; @@ -20,7 +20,7 @@ pub use wayland::*; #[cfg(all(feature = "wayland", target_os = "linux"))] use wayland_backend::client::Backend; -use crate::{Program, WindowManager}; +use crate::{CreateCompositor, Program, WindowManager}; pub type UserInterfaces<'a, P> = HashMap< window::Id, @@ -176,12 +176,14 @@ impl PlatformSpecific { } } -pub(crate) fn handle_event<'a, P>( +pub(crate) async fn handle_event<'a, 'b, P>( e: Event, events: &mut Vec<(Option, iced_runtime::core::Event)>, platform_specific: &mut PlatformSpecific, program: &'a crate::program::Instance

, - compositor: &mut <

::Renderer as compositor::Default>::Compositor, + compositor: &mut Option< + <

::Renderer as compositor::Default>::Compositor, + >, window_manager: &mut WindowManager< P, <

::Renderer as compositor::Default>::Compositor, @@ -192,23 +194,28 @@ pub(crate) fn handle_event<'a, P>( window::Id, (u64, iced_accessibility::accesskit_winit::Adapter), >, + create_compositor: CreateCompositor<'b, P>, ) where P: Program, { match e { #[cfg(all(feature = "wayland", target_os = "linux"))] Event::Wayland(e) => { - platform_specific.wayland.handle_event( - e, - events, - program, - compositor, - window_manager, - user_interfaces, - clipboard, - #[cfg(feature = "a11y")] - adapters, - ); + platform_specific + .wayland + .handle_event( + e, + events, + program, + compositor, + window_manager, + user_interfaces, + clipboard, + #[cfg(feature = "a11y")] + adapters, + create_compositor, + ) + .await; } } } diff --git a/winit/src/platform_specific/wayland/event_loop/state.rs b/winit/src/platform_specific/wayland/event_loop/state.rs index 1b1c9e6a..bd45659c 100644 --- a/winit/src/platform_specific/wayland/event_loop/state.rs +++ b/winit/src/platform_specific/wayland/event_loop/state.rs @@ -1232,7 +1232,6 @@ impl SctkState { found |= p.data.id == settings.parent; parent_mismatch |= found && p.data.id != settings.parent; } - parent_mismatch |= !found; if !self.destroyed.is_empty() || parent_mismatch { if parent_mismatch { let mut found = false; diff --git a/winit/src/platform_specific/wayland/keymap.rs b/winit/src/platform_specific/wayland/keymap.rs index 562981f0..ea4ea977 100644 --- a/winit/src/platform_specific/wayland/keymap.rs +++ b/winit/src/platform_specific/wayland/keymap.rs @@ -893,3 +893,195 @@ pub fn keysym_location(keysym: u32) -> Location { _ => Location::Standard, } } + +pub fn key_to_keysym( + unmodified_key: Key, + location: Location, +) -> Option { + use xkbcommon_dl::keysyms; + + let raw = match unmodified_key { + Key::Named(named) => Some(match (named, location) { + (Named::Escape, _) => keysyms::Escape, + (Named::Backspace, _) => keysyms::BackSpace, + (Named::Tab, _) => keysyms::Tab, + (Named::Enter, _) => keysyms::Return, + + (Named::Compose, _) => keysyms::Multi_key, + + (Named::KanjiMode, _) => keysyms::Kanji, + (Named::Eisu, _) => keysyms::Eisu_toggle, + (Named::NonConvert, _) => keysyms::Muhenkan, + (Named::Convert, _) => keysyms::Henkan_Mode, + (Named::Romaji, _) => keysyms::Romaji, + (Named::Hiragana, _) => keysyms::Hiragana, + (Named::Katakana, _) => keysyms::Katakana, + (Named::HiraganaKatakana, _) => keysyms::Hiragana_Katakana, + (Named::Zenkaku, _) => keysyms::Zenkaku, + (Named::Hankaku, _) => keysyms::Hankaku, + (Named::ZenkakuHankaku, _) => keysyms::Zenkaku_Hankaku, + (Named::KanaMode, _) => keysyms::Kana_Lock, + (Named::Alphanumeric, _) => keysyms::Eisu_toggle, + (Named::CodeInput, _) => keysyms::Codeinput, + (Named::AllCandidates, _) => keysyms::MultipleCandidate, + (Named::PreviousCandidate, _) => keysyms::PreviousCandidate, + (Named::SingleCandidate, _) => keysyms::SingleCandidate, + + (Named::ArrowUp, _) => keysyms::Up, + (Named::ArrowDown, _) => keysyms::Down, + (Named::ArrowLeft, _) => keysyms::Left, + (Named::ArrowRight, _) => keysyms::Right, + (Named::PageUp, _) => keysyms::Page_Up, + (Named::PageDown, _) => keysyms::Page_Down, + (Named::Home, _) => keysyms::Home, + (Named::End, _) => keysyms::End, + (Named::Insert, _) => keysyms::Insert, + (Named::Delete, _) => keysyms::Delete, + + (Named::Undo, _) => keysyms::Undo, + (Named::Redo, _) => keysyms::Redo, + (Named::ContextMenu, _) => keysyms::Menu, + (Named::Find, _) => keysyms::Find, + (Named::Cancel, _) => keysyms::Cancel, + (Named::Help, _) => keysyms::Help, + (Named::Pause, _) => keysyms::Break, + (Named::ModeChange, _) => keysyms::Mode_switch, + (Named::NumLock, _) => keysyms::Num_Lock, + (Named::CapsLock, _) => keysyms::Caps_Lock, + (Named::ScrollLock, _) => keysyms::Scroll_Lock, + (Named::PrintScreen, _) => keysyms::Print, + + (Named::F1, _) => keysyms::F1, + (Named::F2, _) => keysyms::F2, + (Named::F3, _) => keysyms::F3, + (Named::F4, _) => keysyms::F4, + (Named::F5, _) => keysyms::F5, + (Named::F6, _) => keysyms::F6, + (Named::F7, _) => keysyms::F7, + (Named::F8, _) => keysyms::F8, + (Named::F9, _) => keysyms::F9, + (Named::F10, _) => keysyms::F10, + (Named::F11, _) => keysyms::F11, + (Named::F12, _) => keysyms::F12, + (Named::F13, _) => keysyms::F13, + (Named::F14, _) => keysyms::F14, + (Named::F15, _) => keysyms::F15, + (Named::F16, _) => keysyms::F16, + (Named::F17, _) => keysyms::F17, + (Named::F18, _) => keysyms::F18, + (Named::F19, _) => keysyms::F19, + (Named::F20, _) => keysyms::F20, + + (Named::Shift, _) => keysyms::Shift_L, + (Named::Control, _) => keysyms::Control_L, + (Named::Alt, _) => keysyms::Alt_L, + (Named::Super, _) => keysyms::Super_L, + (Named::Hyper, _) => keysyms::Hyper_L, + (Named::Meta, _) => keysyms::Meta_L, + + (Named::AltGraph, _) => keysyms::ISO_Level3_Shift, + (Named::GroupNext, _) => keysyms::ISO_Next_Group, + (Named::GroupPrevious, _) => keysyms::ISO_Prev_Group, + (Named::GroupFirst, _) => keysyms::ISO_First_Group, + (Named::GroupLast, _) => keysyms::ISO_Last_Group, + + (Named::EraseEof, _) => keysyms::_3270_EraseEOF, + (Named::Attn, _) => keysyms::_3270_Attn, + (Named::Play, _) => keysyms::_3270_Play, + (Named::ExSel, _) => keysyms::_3270_ExSelect, + (Named::CrSel, _) => keysyms::_3270_CursorSelect, + + (Named::Space, _) => keysyms::space, + + // XF86 multimedia / internet / power keys (subset shown) + (Named::BrightnessUp, _) => keysyms::XF86_MonBrightnessUp, + (Named::BrightnessDown, _) => keysyms::XF86_MonBrightnessDown, + (Named::Standby, _) => keysyms::XF86_Standby, + (Named::AudioVolumeDown, _) => keysyms::XF86_AudioLowerVolume, + (Named::AudioVolumeUp, _) => keysyms::XF86_AudioRaiseVolume, + (Named::MediaPlay, _) => keysyms::XF86_AudioPlay, + (Named::MediaStop, _) => keysyms::XF86_AudioStop, + (Named::MediaTrackPrevious, _) => keysyms::XF86_AudioPrev, + (Named::MediaTrackNext, _) => keysyms::XF86_AudioNext, + (Named::BrowserHome, _) => keysyms::XF86_HomePage, + (Named::LaunchMail, _) => keysyms::XF86_Mail, + (Named::BrowserSearch, _) => keysyms::XF86_Search, + (Named::MediaRecord, _) => keysyms::XF86_AudioRecord, + (Named::LaunchApplication2, _) => keysyms::XF86_Calculator, + (Named::LaunchCalendar, _) => keysyms::XF86_Calendar, + (Named::Power, _) => keysyms::XF86_PowerDown, + (Named::BrowserBack, _) => keysyms::XF86_Back, + (Named::BrowserForward, _) => keysyms::XF86_Forward, + (Named::BrowserRefresh, _) => keysyms::XF86_Refresh, + (Named::WakeUp, _) => keysyms::XF86_WakeUp, + (Named::Eject, _) => keysyms::XF86_Eject, + (Named::LaunchScreenSaver, _) => keysyms::XF86_ScreenSaver, + (Named::LaunchWebBrowser, _) => keysyms::XF86_WWW, + (Named::BrowserFavorites, _) => keysyms::XF86_Favorites, + (Named::MediaPause, _) => keysyms::XF86_AudioPause, + (Named::LaunchApplication1, _) => keysyms::XF86_MyComputer, + (Named::Close, _) => keysyms::XF86_Close, + (Named::Copy, _) => keysyms::XF86_Copy, + (Named::Cut, _) => keysyms::XF86_Cut, + (Named::LaunchSpreadsheet, _) => keysyms::XF86_Excel, + (Named::LogOff, _) => keysyms::XF86_LogOff, + (Named::New, _) => keysyms::XF86_New, + (Named::Open, _) => keysyms::XF86_Open, + (Named::Paste, _) => keysyms::XF86_Paste, + (Named::LaunchPhone, _) => keysyms::XF86_Phone, + (Named::MailReply, _) => keysyms::XF86_Reply, + (Named::Save, _) => keysyms::XF86_Save, + (Named::MailSend, _) => keysyms::XF86_Send, + (Named::SpellCheck, _) => keysyms::XF86_Spell, + (Named::SplitScreenToggle, _) => keysyms::XF86_SplitScreen, + (Named::LaunchMediaPlayer, _) => keysyms::XF86_Video, + (Named::LaunchWordProcessor, _) => keysyms::XF86_Word, + (Named::ZoomIn, _) => keysyms::XF86_ZoomIn, + (Named::ZoomOut, _) => keysyms::XF86_ZoomOut, + (Named::LaunchWebCam, _) => keysyms::XF86_WebCam, + (Named::MailForward, _) => keysyms::XF86_MailForward, + (Named::LaunchMusicPlayer, _) => keysyms::XF86_Music, + (Named::MediaFastForward, _) => keysyms::XF86_AudioForward, + (Named::RandomToggle, _) => keysyms::XF86_AudioRandomPlay, + (Named::Subtitle, _) => keysyms::XF86_Subtitle, + (Named::MediaAudioTrack, _) => keysyms::XF86_AudioCycleTrack, + (Named::Hibernate, _) => keysyms::XF86_Hibernate, + (Named::AudioVolumeMute, _) => keysyms::XF86_AudioMute, + (Named::VideoModeNext, _) => keysyms::XF86_Next_VMode, + + _ => return None, + }), + Key::Character(c) if c.chars().count() == 1 => { + Some(match (c.chars().next(), location) { + (Some('0'), Location::Numpad) => keysyms::KP_0, + (Some('1'), Location::Numpad) => keysyms::KP_1, + (Some('2'), Location::Numpad) => keysyms::KP_2, + (Some('3'), Location::Numpad) => keysyms::KP_3, + (Some('4'), Location::Numpad) => keysyms::KP_4, + (Some('5'), Location::Numpad) => keysyms::KP_5, + (Some('6'), Location::Numpad) => keysyms::KP_6, + (Some('7'), Location::Numpad) => keysyms::KP_7, + (Some('8'), Location::Numpad) => keysyms::KP_8, + (Some('9'), Location::Numpad) => keysyms::KP_9, + (Some('.'), Location::Numpad) => keysyms::KP_Decimal, + (Some('+'), Location::Numpad) => keysyms::KP_Add, + (Some('-'), Location::Numpad) => keysyms::KP_Subtract, + (Some('*'), Location::Numpad) => keysyms::KP_Multiply, + (Some('/'), Location::Numpad) => keysyms::KP_Divide, + (Some('='), Location::Numpad) => keysyms::KP_Equal, + + (Some(c), _) => unsafe { + let keysym = + xkbcommon::xkb::ffi::xkb_utf32_to_keysym(c as u32); + if keysym == keysyms::NoSymbol { + return None; + } + keysym + }, + _ => return None, + }) + } + _ => None, + }; + raw.map(|k| xkeysym::Keysym::new(k)) +} diff --git a/winit/src/platform_specific/wayland/mod.rs b/winit/src/platform_specific/wayland/mod.rs index 2994191e..2361ecb0 100644 --- a/winit/src/platform_specific/wayland/mod.rs +++ b/winit/src/platform_specific/wayland/mod.rs @@ -8,7 +8,7 @@ pub mod subsurface_widget; pub mod winit_window; use super::{PlatformSpecific, SurfaceIdWrapper}; -use crate::{Control, Program, WindowManager}; +use crate::{Control, CreateCompositor, Program, WindowManager}; use crate::platform_specific::UserInterfaces; use cctk::sctk::reexports::calloop; @@ -150,12 +150,14 @@ impl WaylandSpecific { self.conn.as_ref() } - pub(crate) fn handle_event<'a, P>( + pub(crate) async fn handle_event<'a, 'b, P>( &mut self, e: SctkEvent, events: &mut Vec<(Option, iced_runtime::core::Event)>, program: &'a crate::program::Instance

, - compositor: &mut <

::Renderer as compositor::Default>::Compositor, + compositor: &mut Option< + <

::Renderer as compositor::Default>::Compositor, + >, window_manager: &mut WindowManager< P, <

::Renderer as compositor::Default>::Compositor, @@ -166,6 +168,7 @@ impl WaylandSpecific { window::Id, (u64, iced_accessibility::accesskit_winit::Adapter), >, + create_compositor: CreateCompositor<'b, P>, ) where P: Program, { @@ -196,22 +199,25 @@ impl WaylandSpecific { return Default::default(); }; - sctk_event.process( - modifiers, - program, - compositor, - window_manager, - surface_ids, - sender, - event_sender, - proxy, - user_interfaces, - events, - clipboard, - subsurface_state, - #[cfg(feature = "a11y")] - adapters, - ); + sctk_event + .process( + modifiers, + program, + compositor, + window_manager, + surface_ids, + sender, + event_sender, + proxy, + user_interfaces, + events, + clipboard, + subsurface_state, + #[cfg(feature = "a11y")] + adapters, + create_compositor, + ) + .await; } }; } diff --git a/winit/src/platform_specific/wayland/sctk_event.rs b/winit/src/platform_specific/wayland/sctk_event.rs index 108bdc11..eaf1cea5 100755 --- a/winit/src/platform_specific/wayland/sctk_event.rs +++ b/winit/src/platform_specific/wayland/sctk_event.rs @@ -1,5 +1,5 @@ use crate::{ - Clipboard, Control, Program, + Clipboard, Control, CreateCompositor, Error, Program, create_compositor, platform_specific::{ SurfaceIdWrapper, UserInterfaces, wayland::{ @@ -342,11 +342,13 @@ pub struct SurfaceCompositorUpdate { } impl SctkEvent { - pub(crate) fn process<'a, P>( + pub(crate) async fn process<'a, 'b, P>( self, modifiers: &mut Modifiers, program: &'a crate::program::Instance

, - compositor: &mut <

::Renderer as compositor::Default>::Compositor, + compositor: &mut Option< + <

::Renderer as compositor::Default>::Compositor, + >, window_manager: &mut crate::WindowManager< P, <

::Renderer as compositor::Default>::Compositor, @@ -363,6 +365,7 @@ impl SctkEvent { window::Id, (u64, iced_accessibility::accesskit_winit::Adapter), >, + create_compositor_data: CreateCompositor<'b, P>, ) where P: Program, { @@ -766,7 +769,25 @@ impl SctkEvent { sctk_winit.clone(), )) .expect("Send control message"); - + if compositor.is_none() { + match create_compositor( + sctk_winit.clone(), + create_compositor_data, + ) + .await + { + Ok(c) => *compositor = Some(c), + Err(error) => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed(error), + )) + .expect("Send control message"); + return; + } + }; + } + let compositor = compositor.as_mut().unwrap(); let window = window_manager.insert( surface_id, sctk_winit, @@ -976,6 +997,10 @@ impl SctkEvent { }, ); } + let Some(compositor) = compositor.as_mut() else { + log::error!("compositor missing"); + return; + }; let window = window_manager.insert( surface_id, @@ -1190,6 +1215,25 @@ impl SctkEvent { }, ); } + if compositor.is_none() { + match create_compositor( + sctk_winit.clone(), + create_compositor_data, + ) + .await + { + Ok(c) => *compositor = Some(c), + Err(error) => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed(error), + )) + .expect("Send control message"); + return; + } + }; + } + let compositor = compositor.as_mut().unwrap(); let window = window_manager.insert( surface_id, @@ -1201,20 +1245,70 @@ impl SctkEvent { ); _ = surface_ids.insert(object_id, wrapper.clone()); let logical_size = window.logical_size(); - todo!() - // let _ = user_interfaces.insert( - // surface_id, - // crate::build_user_interface( - // program, - // user_interface::Cache::default(), - // &mut window.renderer, - // logical_size, - // surface_id, - // window.raw.clone(), - // window.prev_dnd_destination_rectangles_count, - // clipboard, - // ), - // ); + let mut ui = crate::build_user_interface( + program, + user_interface::Cache::default(), + &mut window.renderer, + logical_size, + surface_id, + window.raw.clone(), + window.prev_dnd_destination_rectangles_count, + clipboard, + ); + + _ = ui.update( + &vec![iced_runtime::core::Event::PlatformSpecific( + iced_runtime::core::event::PlatformSpecific::Wayland( + iced_runtime::core::event::wayland::Event::RequestResize, + ), + )], + window.state.cursor(), + &mut window.renderer, + clipboard, + &mut Vec::new(), + ); + + if let Some(requested_size) = + clipboard.requested_logical_size.lock().unwrap().take() + { + let requested_physical_size = winit::dpi::PhysicalSize::new( + (requested_size.width as f64 + * window.state.scale_factor()) + .ceil() as u32, + (requested_size.height as f64 + * window.state.scale_factor()) + .ceil() as u32, + ); + let physical_size = window.state.physical_size(); + if requested_physical_size.width != physical_size.width + || requested_physical_size.height + != physical_size.height + { + // FIXME what to do when we are stuck in a configure event/resize request loop + // We don't have control over how winit handles this. + window.resize_enabled = true; + + let s = + winit::dpi::Size::Physical(requested_physical_size); + _ = window.raw.request_surface_size(s); + window.raw.set_min_surface_size(Some(s)); + window.raw.set_max_surface_size(Some(s)); + window.state.synchronize( + &program, + surface_id, + window.raw.as_ref(), + ); + } + } + events.push(( + Some(surface_id), + iced_runtime::core::Event::PlatformSpecific( + PlatformSpecific::Wayland(wayland::Event::Subsurface( + wayland::SubsurfaceEvent::Created, + )), + ), + )); + _ = user_interfaces.insert(surface_id, ui); } SctkEvent::SessionLockSurfaceConfigure { surface, @@ -1434,6 +1528,10 @@ impl SctkEvent { }, ); } + let Some(compositor) = compositor.as_mut() else { + log::error!("compositor missing"); + return; + }; let window = window_manager.insert( surface_id, diff --git a/winit/src/window.rs b/winit/src/window.rs index 804103d0..9ba3b774 100644 --- a/winit/src/window.rs +++ b/winit/src/window.rs @@ -72,8 +72,10 @@ where ); let renderer = compositor.create_renderer(); + self.aliases.retain(|w, i| *w != window.id() && *i != id); let _ = self.aliases.insert(window.id(), id); + self.entries.retain(|old, _| id != *old); let _ = self.entries.insert( id, Window {