summaryrefslogblamecommitdiffstats
path: root/graphics/src/overlay/menu.rs
blob: 9e91a0ef64700190e608e59fb891b9ba86376f50 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                  
                                    

                                 

                                                                     

  

                                
                                               
     
                               
 

                       



                                
                      






                                                 


                                                         
                                           











                               
                             

                                      
                         

                       
                      



                                                             
                                                                      


                                        








                                                                                



                                                        
                                                         
                                    
                                                                  




                                                 
                                                          
                                                     

                                       





                                             
                                                      




                                           
                     
                                       
                                             
                        
                                    















                                                                
//! Build and show dropdown menus.
use crate::backend::{self, Backend};
use crate::{Primitive, Renderer};
use iced_native::{
    mouse, overlay, Color, Font, HorizontalAlignment, Padding, Point,
    Rectangle, VerticalAlignment,
};

pub use iced_style::menu::Style;

impl<B> overlay::menu::Renderer for Renderer<B>
where
    B: Backend + backend::Text,
{
    type Style = Style;

    fn decorate(
        &mut self,
        bounds: Rectangle,
        _cursor_position: Point,
        style: &Style,
        (primitives, mouse_cursor): Self::Output,
    ) -> Self::Output {
        (
            Primitive::Group {
                primitives: vec![
                    Primitive::Quad {
                        bounds,
                        background: style.background,
                        border_color: style.border_color,
                        border_width: style.border_width,
                        border_radius: 0.0,
                    },
                    primitives,
                ],
            },
            mouse_cursor,
        )
    }

    fn draw<T: ToString>(
        &mut self,
        bounds: Rectangle,
        cursor_position: Point,
        viewport: &Rectangle,
        options: &[T],
        hovered_option: Option<usize>,
        padding: Padding,
        text_size: u16,
        font: Font,
        style: &Style,
    ) -> Self::Output {
        use std::f32;

        let is_mouse_over = bounds.contains(cursor_position);
        let option_height = (text_size + padding.vertical()) as usize;

        let mut primitives = Vec::new();

        let offset = viewport.y - bounds.y;
        let start = (offset / option_height as f32) as usize;
        let end =
            ((offset + viewport.height) / option_height as f32).ceil() as usize;

        let visible_options = &options[start..end.min(options.len())];

        for (i, option) in visible_options.iter().enumerate() {
            let i = start + i;
            let is_selected = hovered_option == Some(i);

            let bounds = Rectangle {
                x: bounds.x,
                y: bounds.y + (option_height * i) as f32,
                width: bounds.width,
                height: f32::from(text_size + padding.vertical()),
            };

            if is_selected {
                primitives.push(Primitive::Quad {
                    bounds,
                    background: style.selected_background,
                    border_color: Color::TRANSPARENT,
                    border_width: 0.0,
                    border_radius: 0.0,
                });
            }

            primitives.push(Primitive::Text {
                content: option.to_string(),
                bounds: Rectangle {
                    x: bounds.x + padding.left as f32,
                    y: bounds.center_y(),
                    width: f32::INFINITY,
                    ..bounds
                },
                size: f32::from(text_size),
                font,
                color: if is_selected {
                    style.selected_text_color
                } else {
                    style.text_color
                },
                horizontal_alignment: HorizontalAlignment::Left,
                vertical_alignment: VerticalAlignment::Center,
            });
        }

        (
            Primitive::Group { primitives },
            if is_mouse_over {
                mouse::Interaction::Pointer
            } else {
                mouse::Interaction::default()
            },
        )
    }
}