summaryrefslogblamecommitdiffstats
path: root/core/src/shell.rs
blob: 7a92a2bef9b63839b6d97604387dec335d47a36c (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                         
                  
 








                                                                               
                                                  
                            
                              






                                                                     
                                 
                                     
                                       


         




                                                                    



                                                                     
 






                                                                     
                                   
                     
                                                                          
             

                                                                          

                   


         
                                                           

                                                                   

     




                                                             






                                                        





                                                                                
                








                                                                        






                                                            






                                                                               





                                                                             

         

                                                              



                                                                  
 
use crate::time::Instant;
use crate::window;

/// A connection to the state of a shell.
///
/// A [`Widget`] can leverage a [`Shell`] to trigger changes in an application,
/// like publishing messages or invalidating the current layout.
///
/// [`Widget`]: crate::Widget
#[derive(Debug)]
pub struct Shell<'a, Message> {
    messages: &'a mut Vec<Message>,
    redraw_request: Option<window::RedrawRequest>,
    is_layout_invalid: bool,
    are_widgets_invalid: bool,
}

impl<'a, Message> Shell<'a, Message> {
    /// Creates a new [`Shell`] with the provided buffer of messages.
    pub fn new(messages: &'a mut Vec<Message>) -> Self {
        Self {
            messages,
            redraw_request: None,
            is_layout_invalid: false,
            are_widgets_invalid: false,
        }
    }

    /// Returns true if the [`Shell`] contains no published messages
    pub fn is_empty(&self) -> bool {
        self.messages.is_empty()
    }

    /// Publish the given `Message` for an application to process it.
    pub fn publish(&mut self, message: Message) {
        self.messages.push(message);
    }

    /// Requests a new frame to be drawn as soon as possible.
    pub fn request_redraw(&mut self) {
        self.redraw_request = Some(window::RedrawRequest::NextFrame);
    }

    /// Requests a new frame to be drawn at the given [`Instant`].
    pub fn request_redraw_at(&mut self, at: Instant) {
        match self.redraw_request {
            None => {
                self.redraw_request = Some(window::RedrawRequest::At(at));
            }
            Some(window::RedrawRequest::At(current)) if at < current => {
                self.redraw_request = Some(window::RedrawRequest::At(at));
            }
            _ => {}
        }
    }

    /// Returns the request a redraw should happen, if any.
    pub fn redraw_request(&self) -> Option<window::RedrawRequest> {
        self.redraw_request
    }

    /// Returns whether the current layout is invalid or not.
    pub fn is_layout_invalid(&self) -> bool {
        self.is_layout_invalid
    }

    /// Invalidates the current application layout.
    ///
    /// The shell will relayout the application widgets.
    pub fn invalidate_layout(&mut self) {
        self.is_layout_invalid = true;
    }

    /// Triggers the given function if the layout is invalid, cleaning it in the
    /// process.
    pub fn revalidate_layout(&mut self, f: impl FnOnce()) {
        if self.is_layout_invalid {
            self.is_layout_invalid = false;

            f();
        }
    }

    /// Returns whether the widgets of the current application have been
    /// invalidated.
    pub fn are_widgets_invalid(&self) -> bool {
        self.are_widgets_invalid
    }

    /// Invalidates the current application widgets.
    ///
    /// The shell will rebuild and relayout the widget tree.
    pub fn invalidate_widgets(&mut self) {
        self.are_widgets_invalid = true;
    }

    /// Merges the current [`Shell`] with another one by applying the given
    /// function to the messages of the latter.
    ///
    /// This method is useful for composition.
    pub fn merge<B>(&mut self, other: Shell<'_, B>, f: impl Fn(B) -> Message) {
        self.messages.extend(other.messages.drain(..).map(f));

        if let Some(new) = other.redraw_request {
            self.redraw_request = Some(
                self.redraw_request
                    .map(|current| if current < new { current } else { new })
                    .unwrap_or(new),
            );
        }

        self.is_layout_invalid =
            self.is_layout_invalid || other.is_layout_invalid;

        self.are_widgets_invalid =
            self.are_widgets_invalid || other.are_widgets_invalid;
    }
}