diff options
Diffstat (limited to '')
-rw-r--r-- | examples/multi_window/src/main.rs | 50 | ||||
-rw-r--r-- | glutin/src/multi_window.rs | 116 | ||||
-rw-r--r-- | native/src/window.rs | 3 | ||||
-rw-r--r-- | winit/src/multi_window.rs | 117 |
4 files changed, 171 insertions, 115 deletions
diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs index 0d0a809b..23f08217 100644 --- a/examples/multi_window/src/main.rs +++ b/examples/multi_window/src/main.rs @@ -12,6 +12,7 @@ use iced::{Color, Command, Element, Length, Settings, Size, Subscription}; use iced_lazy::responsive; use iced_native::{event, subscription, Event}; +use iced_native::widget::scrollable::{Properties, RelativeOffset}; use iced_native::window::Id; use std::collections::HashMap; @@ -56,6 +57,7 @@ enum WindowMessage { CloseFocused, SelectedWindow(pane_grid::Pane, SelectableWindow), CloseWindow, + SnapToggle, } impl Application for Example { @@ -94,6 +96,25 @@ impl Application for Example { fn update(&mut self, message: Message) -> Command<Message> { let Message::Window(id, message) = message; match message { + WindowMessage::SnapToggle => { + let window = self.windows.get_mut(&id).unwrap(); + + if let Some(focused) = &window.focus { + let pane = window.panes.get_mut(focused).unwrap(); + + let cmd = scrollable::snap_to( + pane.scrollable_id.clone(), + if pane.snapped { + RelativeOffset::START + } else { + RelativeOffset::END + }, + ); + + pane.snapped = !pane.snapped; + return cmd; + } + } WindowMessage::Split(axis, pane) => { let window = self.windows.get_mut(&id).unwrap(); let result = window.panes.split( @@ -311,7 +332,13 @@ impl Application for Example { }); pane_grid::Content::new(responsive(move |size| { - view_content(id, total_panes, pane.is_pinned, size) + view_content( + id, + pane.scrollable_id.clone(), + total_panes, + pane.is_pinned, + size, + ) })) .title_bar(title_bar) .style(if is_focused { @@ -403,24 +430,29 @@ impl std::fmt::Display for SelectableWindow { #[derive(Debug)] struct Pane { id: usize, + pub scrollable_id: scrollable::Id, pub axis: pane_grid::Axis, pub is_pinned: bool, pub is_moving: bool, + pub snapped: bool, } impl Pane { fn new(id: usize, axis: pane_grid::Axis) -> Self { Self { id, + scrollable_id: scrollable::Id::new(format!("{:?}", id)), axis, is_pinned: false, is_moving: false, + snapped: false, } } } fn view_content<'a>( pane: pane_grid::Pane, + scrollable_id: scrollable::Id, total_panes: usize, is_pinned: bool, size: Size, @@ -445,7 +477,8 @@ fn view_content<'a>( button( "Split vertically", WindowMessage::Split(pane_grid::Axis::Vertical, pane), - ) + ), + button("Snap", WindowMessage::SnapToggle,) ] .spacing(5) .max_width(150); @@ -462,15 +495,22 @@ fn view_content<'a>( controls, ] .width(Length::Fill) + .height(Length::Units(800)) .spacing(10) .align_items(Alignment::Center); - container(scrollable(content)) + Element::from( + container( + scrollable(content) + .vertical_scroll(Properties::new()) + .id(scrollable_id), + ) .width(Length::Fill) .height(Length::Fill) .padding(5) - .center_y() - .into() + .center_y(), + ) + .explain(Color::default()) } fn view_controls<'a>( diff --git a/glutin/src/multi_window.rs b/glutin/src/multi_window.rs index a2e0581a..33fe60ff 100644 --- a/glutin/src/multi_window.rs +++ b/glutin/src/multi_window.rs @@ -34,6 +34,7 @@ use std::num::NonZeroU32; #[cfg(feature = "tracing")] use tracing::{info_span, instrument::Instrument}; +use iced_native::widget::operation; #[allow(unsafe_code)] const ONE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) }; @@ -294,7 +295,7 @@ where ); #[cfg(feature = "tracing")] - let run_instance = + let run_instance = run_instance.instrument(info_span!("Application", "LOOP")); run_instance @@ -380,7 +381,7 @@ async fn run_instance<A, E, C>( let mut clipboard = Clipboard::connect(windows.values().next().expect("No window found")); - let mut cache = user_interface::Cache::default(); + let mut caches = HashMap::new(); let mut current_context_window = None; let mut window_ids: HashMap<_, _> = windows .iter() @@ -422,23 +423,20 @@ async fn run_instance<A, E, C>( let _ = interfaces.insert(id, user_interface); } - { - let state = states.values().next().expect("No state found."); + run_command( + &application, + &mut caches, + &states, + &mut renderer, + init_command, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &windows, + || compositor.fetch_information(), + ); - run_command( - &application, - &mut cache, - state, - &mut renderer, - init_command, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &windows, - || compositor.fetch_information(), - ); - } runtime.track(application.subscription().map(Event::Application)); let mut mouse_interaction = mouse::Interaction::default(); @@ -501,8 +499,7 @@ async fn run_instance<A, E, C>( user_interface::State::Outdated ) { - let state = &mut states.get_mut(&id).unwrap(); - let pure_states: HashMap<_, _> = + let user_interfaces: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() .map(|(id, interface)| { @@ -513,8 +510,8 @@ async fn run_instance<A, E, C>( // Update application update( &mut application, - &mut cache, - state, + &mut caches, + &states, &mut renderer, &mut runtime, &mut clipboard, @@ -526,7 +523,7 @@ async fn run_instance<A, E, C>( ); // Update window - state.synchronize( + states.get_mut(&id).unwrap().synchronize( &application, id, windows.get(&id).expect("No window found with ID."), @@ -539,7 +536,7 @@ async fn run_instance<A, E, C>( &mut renderer, &mut debug, &states, - pure_states, + user_interfaces, )); if should_exit { @@ -590,7 +587,8 @@ async fn run_instance<A, E, C>( event::Event::UserEvent(event) => match event { Event::Application(message) => messages.push(message), Event::WindowCreated(id, window) => { - let state = multi_window::State::new(&application, id, &window); + let state = + multi_window::State::new(&application, id, &window); let user_interface = multi_window::build_user_interface( &application, user_interface::Cache::default(), @@ -768,7 +766,10 @@ async fn run_instance<A, E, C>( )); } } else { - log::error!("Window state not found for id: {:?}", window_id); + log::error!( + "Window state not found for id: {:?}", + window_id + ); } } else { log::error!("Window not found for id: {:?}", window_id); @@ -786,8 +787,8 @@ async fn run_instance<A, E, C>( /// resulting [`Command`], and tracking its [`Subscription`]. pub fn update<A: Application, E: Executor>( application: &mut A, - cache: &mut user_interface::Cache, - state: &multi_window::State<A>, + caches: &mut HashMap<window::Id, user_interface::Cache>, + states: &HashMap<window::Id, multi_window::State<A>>, renderer: &mut A::Renderer, runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, clipboard: &mut Clipboard, @@ -797,6 +798,7 @@ pub fn update<A: Application, E: Executor>( windows: &HashMap<window::Id, winit::window::Window>, graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy, ) where + A: Application + 'static, <A::Renderer as crate::Renderer>::Theme: StyleSheet, { for message in messages.drain(..) { @@ -808,8 +810,8 @@ pub fn update<A: Application, E: Executor>( run_command( application, - cache, - state, + caches, + &states, renderer, command, runtime, @@ -828,8 +830,8 @@ pub fn update<A: Application, E: Executor>( /// Runs the actions of a [`Command`]. pub fn run_command<A, E>( application: &A, - cache: &mut user_interface::Cache, - state: &multi_window::State<A>, + caches: &mut HashMap<window::Id, user_interface::Cache>, + states: &HashMap<window::Id, multi_window::State<A>>, renderer: &mut A::Renderer, command: Command<A::Message>, runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, @@ -839,7 +841,7 @@ pub fn run_command<A, E>( windows: &HashMap<window::Id, winit::window::Window>, _graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy, ) where - A: Application, + A: Application + 'static, E: Executor, <A::Renderer as crate::Renderer>::Theme: StyleSheet, { @@ -967,38 +969,42 @@ pub fn run_command<A, E>( } }, command::Action::Widget(action) => { - use crate::widget::operation; - - let mut current_cache = std::mem::take(cache); + let mut current_caches = std::mem::take(caches); let mut current_operation = Some(action.into_operation()); - let mut user_interface = multi_window::build_user_interface( + let mut user_interfaces = multi_window::build_user_interfaces( application, - current_cache, renderer, - state.logical_size(), debug, - window::Id::MAIN, // TODO(derezzedex): run the operation on every widget tree + states, + current_caches, ); while let Some(mut operation) = current_operation.take() { - user_interface.operate(renderer, operation.as_mut()); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(message) => { - proxy - .send_event(Event::Application(message)) - .expect("Send message to event loop"); - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); + for user_interface in user_interfaces.values_mut() { + user_interface.operate(renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(message) => { + proxy + .send_event(Event::Application(message)) + .expect("Send message to event loop"); + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } } } } - current_cache = user_interface.into_cache(); - *cache = current_cache; + let user_interfaces: HashMap<_, _> = user_interfaces + .drain() + .map(|(id, interface)| (id, interface.into_cache())) + .collect(); + + current_caches = user_interfaces; + *caches = current_caches; } } } @@ -1010,7 +1016,7 @@ pub fn build_user_interfaces<'a, A>( renderer: &mut A::Renderer, debug: &mut Debug, states: &HashMap<window::Id, multi_window::State<A>>, - mut pure_states: HashMap<window::Id, user_interface::Cache>, + mut user_interfaces: HashMap<window::Id, user_interface::Cache>, ) -> HashMap< window::Id, iced_winit::UserInterface< @@ -1025,7 +1031,7 @@ where { let mut interfaces = HashMap::new(); - for (id, pure_state) in pure_states.drain() { + for (id, pure_state) in user_interfaces.drain() { let state = &states.get(&id).unwrap(); let user_interface = multi_window::build_user_interface( diff --git a/native/src/window.rs b/native/src/window.rs index aa11756f..e768ed6d 100644 --- a/native/src/window.rs +++ b/native/src/window.rs @@ -21,6 +21,7 @@ pub use user_attention::UserAttention; use crate::subscription::{self, Subscription}; use crate::time::Instant; +use crate::window; /// Subscribes to the frames of the window of the running application. /// @@ -42,6 +43,8 @@ pub fn frames() -> Subscription<Frame> { /// The returned `Frame` for a framerate subscription. #[derive(Debug)] pub struct Frame { + /// The `window::Id` that the `Frame` was produced in. pub id: Id, + /// The `Instant` at which the frame was produced. pub at: Instant, } diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs index 430e6706..bd7e9d44 100644 --- a/winit/src/multi_window.rs +++ b/winit/src/multi_window.rs @@ -4,12 +4,12 @@ mod state; pub use state::State; use crate::clipboard::{self, Clipboard}; -use crate::conversion; use crate::mouse; use crate::renderer; use crate::settings; use crate::widget::operation; use crate::window; +use crate::{conversion, multi_window}; use crate::{ Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime, Settings, Size, Subscription, @@ -254,7 +254,7 @@ where ); #[cfg(feature = "trace")] - let run_instance = + let run_instance = run_instance.instrument(info_span!("Application", "LOOP")); run_instance @@ -335,7 +335,7 @@ async fn run_instance<A, E, C>( let mut clipboard = Clipboard::connect(windows.values().next().expect("No window found")); - let mut cache = user_interface::Cache::default(); + let mut caches = HashMap::new(); let mut window_ids: HashMap<_, _> = windows .iter() .map(|(&id, window)| (window.id(), id)) @@ -368,26 +368,23 @@ async fn run_instance<A, E, C>( let _ = states.insert(id, state); let _ = surfaces.insert(id, surface); let _ = interfaces.insert(id, user_interface); + let _ = caches.insert(id, user_interface::Cache::default()); } - { - // TODO(derezzedex) - let state = states.values().next().expect("No state found"); + run_command( + &application, + &mut caches, + &states, + &mut renderer, + init_command, + &mut runtime, + &mut clipboard, + &mut proxy, + &mut debug, + &windows, + || compositor.fetch_information(), + ); - run_command( - &application, - &mut cache, - state, - &mut renderer, - init_command, - &mut runtime, - &mut clipboard, - &mut proxy, - &mut debug, - &windows, - || compositor.fetch_information(), - ); - } runtime.track(application.subscription().map(Event::Application)); let mut mouse_interaction = mouse::Interaction::default(); @@ -455,8 +452,7 @@ async fn run_instance<A, E, C>( user_interface::State::Outdated, ) { - let state = states.get_mut(&id).unwrap(); - let pure_states: HashMap<_, _> = + let user_interfaces: HashMap<_, _> = ManuallyDrop::into_inner(interfaces) .drain() .map( @@ -472,8 +468,8 @@ async fn run_instance<A, E, C>( // Update application update( &mut application, - &mut cache, - state, + &mut caches, + &states, &mut renderer, &mut runtime, &mut clipboard, @@ -485,7 +481,7 @@ async fn run_instance<A, E, C>( ); // Update window - state.synchronize( + states.get_mut(&id).unwrap().synchronize( &application, id, windows.get(&id).expect("No window found with ID."), @@ -498,7 +494,7 @@ async fn run_instance<A, E, C>( &mut renderer, &mut debug, &states, - pure_states, + user_interfaces, )); if should_exit { @@ -579,6 +575,7 @@ async fn run_instance<A, E, C>( let _ = interfaces.insert(id, user_interface); let _ = window_ids.insert(window.id(), id); let _ = windows.insert(id, window); + let _ = caches.insert(id, user_interface::Cache::default()); } Event::CloseWindow(id) => { if let Some(window) = windows.get(&id) { @@ -764,7 +761,10 @@ async fn run_instance<A, E, C>( )); } } else { - log::error!("No window state found for id: {:?}", window_id); + log::error!( + "No window state found for id: {:?}", + window_id + ); } } else { log::error!("No window found with id: {:?}", window_id); @@ -840,8 +840,8 @@ where /// resulting [`Command`], and tracking its [`Subscription`]. pub fn update<A: Application, E: Executor>( application: &mut A, - cache: &mut user_interface::Cache, - state: &State<A>, + caches: &mut HashMap<window::Id, user_interface::Cache>, + states: &HashMap<window::Id, State<A>>, renderer: &mut A::Renderer, runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, clipboard: &mut Clipboard, @@ -851,6 +851,7 @@ pub fn update<A: Application, E: Executor>( windows: &HashMap<window::Id, winit::window::Window>, graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where + A: Application + 'static, <A::Renderer as crate::Renderer>::Theme: StyleSheet, { for message in messages.drain(..) { @@ -867,8 +868,8 @@ pub fn update<A: Application, E: Executor>( run_command( application, - cache, - state, + caches, + states, renderer, command, runtime, @@ -887,8 +888,8 @@ pub fn update<A: Application, E: Executor>( /// Runs the actions of a [`Command`]. pub fn run_command<A, E>( application: &A, - cache: &mut user_interface::Cache, - state: &State<A>, + caches: &mut HashMap<window::Id, user_interface::Cache>, + states: &HashMap<window::Id, State<A>>, renderer: &mut A::Renderer, command: Command<A::Message>, runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>, @@ -898,7 +899,7 @@ pub fn run_command<A, E>( windows: &HashMap<window::Id, winit::window::Window>, _graphics_info: impl FnOnce() -> compositor::Information + Copy, ) where - A: Application, + A: Application + 'static, E: Executor, <A::Renderer as crate::Renderer>::Theme: StyleSheet, { @@ -1024,36 +1025,42 @@ pub fn run_command<A, E>( } }, command::Action::Widget(action) => { - let mut current_cache = std::mem::take(cache); + let mut current_caches = std::mem::take(caches); let mut current_operation = Some(action.into_operation()); - let mut user_interface = build_user_interface( + let mut user_interfaces = build_user_interfaces( application, - current_cache, renderer, - state.logical_size(), debug, - window::Id::MAIN, // TODO(derezzedex): run the operation on every widget tree + states, + current_caches, ); while let Some(mut operation) = current_operation.take() { - user_interface.operate(renderer, operation.as_mut()); - - match operation.finish() { - operation::Outcome::None => {} - operation::Outcome::Some(message) => { - proxy - .send_event(Event::Application(message)) - .expect("Send message to event loop"); - } - operation::Outcome::Chain(next) => { - current_operation = Some(next); + for user_interface in user_interfaces.values_mut() { + user_interface.operate(renderer, operation.as_mut()); + + match operation.finish() { + operation::Outcome::None => {} + operation::Outcome::Some(message) => { + proxy + .send_event(Event::Application(message)) + .expect("Send message to event loop"); + } + operation::Outcome::Chain(next) => { + current_operation = Some(next); + } } } } - current_cache = user_interface.into_cache(); - *cache = current_cache; + let user_interfaces: HashMap<_, _> = user_interfaces + .drain() + .map(|(id, interface)| (id, interface.into_cache())) + .collect(); + + current_caches = user_interfaces; + *caches = current_caches; } } } @@ -1065,7 +1072,7 @@ pub fn build_user_interfaces<'a, A>( renderer: &mut A::Renderer, debug: &mut Debug, states: &HashMap<window::Id, State<A>>, - mut pure_states: HashMap<window::Id, user_interface::Cache>, + mut cached_user_interfaces: HashMap<window::Id, user_interface::Cache>, ) -> HashMap< window::Id, UserInterface< @@ -1080,12 +1087,12 @@ where { let mut interfaces = HashMap::new(); - for (id, pure_state) in pure_states.drain() { + for (id, cache) in cached_user_interfaces.drain() { let state = &states.get(&id).unwrap(); let user_interface = build_user_interface( application, - pure_state, + cache, renderer, state.logical_size(), debug, |