summaryrefslogblamecommitdiffstats
path: root/native/src/program/state.rs
blob: 2ddde2c2ea6af2afd2b3667046a766d0315ba114 (plain) (tree)
1
2
3
4
5
6
7
8
                       
                 
                    

                                                                    
 

                                                                     





                                       
                                         

                                     
                                          




                         
                                                                     
 

                                                                               





                                   
                                                  
                         
                                             




                     




                                                      

                                        
                                                        


         
                                                                



                                 

                                                                             
                                



                                                 

                                                                              
                                



                                                          
                                                                         



                                                                        




                                                                    




                                                                              

                  
                     
                               
                                   
                                                        
                                
                                      

                                      








                                                      

                                      


                                      
                     
                      

                          
 
                                                   
                                   



                                          
                                    
                                                                             














                                                                      
                                                               




                                            
                                                          







                                  
                                    
                                                                             










                                                           
                                 


                               

                                               
                                                                     
 









                                                                           
use crate::application;
use crate::mouse;
use crate::renderer;
use crate::user_interface::{self, UserInterface};
use crate::{Clipboard, Command, Debug, Event, Point, Program, Size};

/// The execution state of a [`Program`]. It leverages caching, event
/// processing, and rendering primitive storage.
#[allow(missing_debug_implementations)]
pub struct State<P>
where
    P: Program + 'static,
{
    program: P,
    cache: Option<user_interface::Cache>,
    queued_events: Vec<Event>,
    queued_messages: Vec<P::Message>,
    mouse_interaction: mouse::Interaction,
}

impl<P> State<P>
where
    P: Program + 'static,
    <P::Renderer as crate::Renderer>::Theme: application::StyleSheet,
{
    /// Creates a new [`State`] with the provided [`Program`], initializing its
    /// primitive with the given logical bounds and renderer.
    pub fn new(
        mut program: P,
        bounds: Size,
        renderer: &mut P::Renderer,
        debug: &mut Debug,
    ) -> Self {
        let user_interface = build_user_interface(
            &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::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 the [`Command`] obtained from [`Program`] after updating it,
    /// only if an update was necessary.
    pub fn update(
        &mut self,
        bounds: Size,
        cursor_position: Point,
        renderer: &mut P::Renderer,
        theme: &<P::Renderer as crate::Renderer>::Theme,
        style: &renderer::Style,
        clipboard: &mut dyn Clipboard,
        debug: &mut Debug,
    ) -> Option<Command<P::Message>> {
        let mut user_interface = build_user_interface(
            &mut self.program,
            self.cache.take().unwrap(),
            renderer,
            bounds,
            debug,
        );

        debug.event_processing_started();
        let mut messages = Vec::new();

        let _ = user_interface.update(
            &self.queued_events,
            cursor_position,
            renderer,
            clipboard,
            &mut messages,
        );

        messages.append(&mut self.queued_messages);
        self.queued_events.clear();
        debug.event_processing_finished();

        if messages.is_empty() {
            debug.draw_started();
            self.mouse_interaction =
                user_interface.draw(renderer, theme, style, cursor_position);
            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 commands =
                Command::batch(messages.into_iter().map(|message| {
                    debug.log_message(&message);

                    debug.update_started();
                    let command = self.program.update(message);
                    debug.update_finished();

                    command
                }));

            let mut user_interface = build_user_interface(
                &mut self.program,
                temp_cache,
                renderer,
                bounds,
                debug,
            );

            debug.draw_started();
            self.mouse_interaction =
                user_interface.draw(renderer, theme, style, cursor_position);
            debug.draw_finished();

            self.cache = Some(user_interface.into_cache());

            Some(commands)
        }
    }
}

fn build_user_interface<'a, P: Program>(
    program: &'a mut P,
    cache: user_interface::Cache,
    renderer: &mut P::Renderer,
    size: Size,
    debug: &mut Debug,
) -> UserInterface<'a, P::Message, P::Renderer>
where
    <P::Renderer as crate::Renderer>::Theme: application::StyleSheet,
{
    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
}