//! 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::{Command, Debug, Program}; /// 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::Idle, } } /// 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 [`Command`] 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: &::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 = Command::batch(messages.into_iter().map(|msg| { debug.log_message(&msg); debug.update_started(); let command = self.program.update(msg); debug.update_finished(); command })); 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(message) => { self.queued_messages.push(message) } 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::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 }