Merge branch 'iters' into 'master'
layout: Place items using simple loops See merge request Librem5/squeekboard!140
This commit is contained in:
		@ -46,71 +46,8 @@ eek_layout_init (EekLayout *self)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const double row_spacing = 7.0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct place_data {
 | 
					 | 
				
			||||||
    double desired_width;
 | 
					 | 
				
			||||||
    double current_offset;
 | 
					 | 
				
			||||||
    // Needed for outline (bounds) retrieval
 | 
					 | 
				
			||||||
    LevelKeyboard *keyboard;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
row_placer(struct squeek_row *row, gpointer user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    struct place_data *data = (struct place_data*)user_data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    EekBounds row_bounds = {
 | 
					 | 
				
			||||||
        .x = 0,
 | 
					 | 
				
			||||||
        .y = 0,
 | 
					 | 
				
			||||||
        .width = data->desired_width,
 | 
					 | 
				
			||||||
        .height = 0,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    squeek_row_set_bounds(row, row_bounds);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Gather up all the keys in a row and adjust their bounds.
 | 
					 | 
				
			||||||
    squeek_row_place_buttons(row, data->keyboard);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    row_bounds = squeek_row_get_bounds(row);
 | 
					 | 
				
			||||||
    row_bounds.y = data->current_offset;
 | 
					 | 
				
			||||||
    squeek_row_set_bounds(row, row_bounds);
 | 
					 | 
				
			||||||
    data->current_offset += row_bounds.height + row_spacing;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void
 | 
					 | 
				
			||||||
row_counter(struct squeek_row *row, gpointer user_data) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    double *total_height = user_data;
 | 
					 | 
				
			||||||
    EekBounds row_bounds = squeek_row_get_bounds(row);
 | 
					 | 
				
			||||||
    *total_height += row_bounds.height + row_spacing;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					 | 
				
			||||||
eek_layout_place_rows(LevelKeyboard *keyboard, struct squeek_view *level)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /* Order rows */
 | 
					 | 
				
			||||||
    // This needs to be done after outlines, because outlines define key sizes
 | 
					 | 
				
			||||||
    // TODO: do this only for rows without bounds
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // The keyboard width is given by the user via screen size. The height will be given dynamically.
 | 
					 | 
				
			||||||
    // TODO: calculate max line width beforehand for button centering. Leave keyboard centering to the renderer later
 | 
					 | 
				
			||||||
    EekBounds view_bounds = squeek_view_get_bounds(level);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    struct place_data placer_data = {
 | 
					 | 
				
			||||||
        .desired_width = view_bounds.width,
 | 
					 | 
				
			||||||
        .current_offset = 0,
 | 
					 | 
				
			||||||
        .keyboard = keyboard,
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
    squeek_view_foreach(level, row_placer, &placer_data);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    double total_height = 0;
 | 
					 | 
				
			||||||
    squeek_view_foreach(level, row_counter, &total_height);
 | 
					 | 
				
			||||||
    view_bounds.height = total_height;
 | 
					 | 
				
			||||||
    squeek_view_set_bounds(level, view_bounds);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void
 | 
					void
 | 
				
			||||||
eek_layout_update_layout(LevelKeyboard *keyboard)
 | 
					eek_layout_update_layout(LevelKeyboard *keyboard)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    eek_layout_place_rows(keyboard, level_keyboard_current(keyboard));
 | 
					    squeek_view_place_contents(level_keyboard_current(keyboard), keyboard);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -929,7 +929,7 @@ eek_xml_layout_real_create_keyboard (EekLayout *self,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    for (uint i = 0; i < 4; i++) {
 | 
					    for (uint i = 0; i < 4; i++) {
 | 
				
			||||||
        if (views[i]) {
 | 
					        if (views[i]) {
 | 
				
			||||||
            eek_layout_place_rows(keyboard, views[i]);
 | 
					            squeek_view_place_contents(views[i], keyboard);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -71,5 +71,7 @@ void squeek_view_foreach(struct squeek_view*,
 | 
				
			|||||||
struct squeek_row *squeek_view_get_row(struct squeek_view *view,
 | 
					struct squeek_row *squeek_view_get_row(struct squeek_view *view,
 | 
				
			||||||
                                       struct squeek_button *button);
 | 
					                                       struct squeek_button *button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void squeek_row_place_buttons(struct squeek_row *row, LevelKeyboard *keyboard);
 | 
					
 | 
				
			||||||
 | 
					void
 | 
				
			||||||
 | 
					squeek_view_place_contents(struct squeek_view *view, LevelKeyboard *keyboard);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										150
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										150
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -41,6 +41,12 @@ pub mod c {
 | 
				
			|||||||
        pub height: f64
 | 
					        pub height: f64
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    impl Bounds {
 | 
				
			||||||
 | 
					        pub fn zero() -> Bounds {
 | 
				
			||||||
 | 
					            Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData);
 | 
					    type ButtonCallback = unsafe extern "C" fn(button: *mut ::layout::Button, data: *mut UserData);
 | 
				
			||||||
    type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData);
 | 
					    type RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -345,18 +351,22 @@ pub mod c {
 | 
				
			|||||||
        /// Places each button in order, starting from 0 on the left,
 | 
					        /// Places each button in order, starting from 0 on the left,
 | 
				
			||||||
        /// keeping the spacing.
 | 
					        /// keeping the spacing.
 | 
				
			||||||
        /// Sizes each button according to outline dimensions.
 | 
					        /// Sizes each button according to outline dimensions.
 | 
				
			||||||
        /// Sets the row size correctly
 | 
					        /// Places each row in order, starting from 0 on the top,
 | 
				
			||||||
 | 
					        /// keeping the spacing.
 | 
				
			||||||
 | 
					        /// Sets button and row sizes according to their contents.
 | 
				
			||||||
        #[no_mangle]
 | 
					        #[no_mangle]
 | 
				
			||||||
        pub extern "C"
 | 
					        pub extern "C"
 | 
				
			||||||
        fn squeek_row_place_buttons(
 | 
					        fn squeek_view_place_contents(
 | 
				
			||||||
            row: *mut ::layout::Row,
 | 
					            view: *mut ::layout::View,
 | 
				
			||||||
            keyboard: *const LevelKeyboard,
 | 
					            keyboard: *const LevelKeyboard, // source of outlines
 | 
				
			||||||
        ) {
 | 
					        ) {
 | 
				
			||||||
            let row = unsafe { &mut *row };
 | 
					            let view = unsafe { &mut *view };
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            let sizes = squeek_buttons_get_outlines(&row.buttons, keyboard);
 | 
					            let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row|
 | 
				
			||||||
 | 
					                squeek_buttons_get_outlines(&row.buttons, keyboard)
 | 
				
			||||||
 | 
					            ).collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            row.place_buttons_with_sizes(sizes);
 | 
					            view.place_buttons_with_sizes(sizes);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[no_mangle]
 | 
					        #[no_mangle]
 | 
				
			||||||
@ -538,6 +548,12 @@ pub mod c {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug)]
 | 
				
			||||||
 | 
					pub struct Size {
 | 
				
			||||||
 | 
					    pub width: f64,
 | 
				
			||||||
 | 
					    pub height: f64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The graphical representation of a button
 | 
					/// The graphical representation of a button
 | 
				
			||||||
#[derive(Clone, Debug)]
 | 
					#[derive(Clone, Debug)]
 | 
				
			||||||
pub struct Button {
 | 
					pub struct Button {
 | 
				
			||||||
@ -550,6 +566,7 @@ pub struct Button {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const BUTTON_SPACING: f64 = 4.0;
 | 
					const BUTTON_SPACING: f64 = 4.0;
 | 
				
			||||||
 | 
					const ROW_SPACING: f64 = 7.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// The graphical representation of a row of buttons
 | 
					/// The graphical representation of a row of buttons
 | 
				
			||||||
pub struct Row {
 | 
					pub struct Row {
 | 
				
			||||||
@ -567,53 +584,104 @@ impl Row {
 | 
				
			|||||||
            bounds: None,
 | 
					            bounds: None,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    fn place_buttons_with_sizes(&mut self, outlines: Vec<c::Bounds>) {
 | 
					    
 | 
				
			||||||
        let max_height = outlines.iter().map(
 | 
					    fn last(positions: &Vec<c::Bounds>) -> Option<&c::Bounds> {
 | 
				
			||||||
 | 
					        let len = positions.len();
 | 
				
			||||||
 | 
					        match len {
 | 
				
			||||||
 | 
					            0 => None,
 | 
				
			||||||
 | 
					            l => Some(&positions[l - 1])
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    fn calculate_button_positions(outlines: Vec<c::Bounds>)
 | 
				
			||||||
 | 
					        -> Vec<c::Bounds>
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut x_offset = 0f64;
 | 
				
			||||||
 | 
					        outlines.iter().map(|outline| {
 | 
				
			||||||
 | 
					            x_offset += outline.x; // account for offset outlines
 | 
				
			||||||
 | 
					            let position = c::Bounds {
 | 
				
			||||||
 | 
					                x: x_offset,
 | 
				
			||||||
 | 
					                ..outline.clone()
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            x_offset += outline.width + BUTTON_SPACING;
 | 
				
			||||||
 | 
					            position
 | 
				
			||||||
 | 
					        }).collect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    fn calculate_row_size(positions: &Vec<c::Bounds>) -> Size {
 | 
				
			||||||
 | 
					        let max_height = positions.iter().map(
 | 
				
			||||||
            |bounds| FloatOrd(bounds.height)
 | 
					            |bounds| FloatOrd(bounds.height)
 | 
				
			||||||
        ).max()
 | 
					        ).max()
 | 
				
			||||||
            .unwrap_or(FloatOrd(0f64))
 | 
					            .unwrap_or(FloatOrd(0f64))
 | 
				
			||||||
            .0;
 | 
					            .0;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        let mut acc = 0f64;
 | 
					        let total_width = match Row::last(positions) {
 | 
				
			||||||
        let x_offsets: Vec<f64> = outlines.iter().map(|outline| {
 | 
					            Some(position) => position.x + position.width,
 | 
				
			||||||
            acc += outline.x; // account for offset outlines
 | 
					            None => 0f64,
 | 
				
			||||||
            let out = acc;
 | 
					        };
 | 
				
			||||||
            acc += outline.width + BUTTON_SPACING;
 | 
					        Size { width: total_width, height: max_height }
 | 
				
			||||||
            out
 | 
					 | 
				
			||||||
        }).collect();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let total_width = acc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for ((mut button, outline), offset)
 | 
					 | 
				
			||||||
            in &mut self.buttons
 | 
					 | 
				
			||||||
                .iter_mut()
 | 
					 | 
				
			||||||
                .zip(outlines)
 | 
					 | 
				
			||||||
                .zip(x_offsets) {
 | 
					 | 
				
			||||||
            button.bounds = Some(c::Bounds {
 | 
					 | 
				
			||||||
                x: offset,
 | 
					 | 
				
			||||||
                ..outline
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let old_row_bounds = self.bounds.as_ref().unwrap().clone();
 | 
					 | 
				
			||||||
        self.bounds = Some(c::Bounds {
 | 
					 | 
				
			||||||
            // FIXME: do centering of each row based on keyboard dimensions,
 | 
					 | 
				
			||||||
            // one level up the iterators
 | 
					 | 
				
			||||||
            // now centering by comparing previous width to the new,
 | 
					 | 
				
			||||||
            // calculated one
 | 
					 | 
				
			||||||
            x: (old_row_bounds.width - total_width) / 2f64,
 | 
					 | 
				
			||||||
            width: total_width,
 | 
					 | 
				
			||||||
            height: max_height,
 | 
					 | 
				
			||||||
            ..old_row_bounds
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct View {
 | 
					pub struct View {
 | 
				
			||||||
 | 
					    /// Position relative to keyboard origin
 | 
				
			||||||
    bounds: c::Bounds,
 | 
					    bounds: c::Bounds,
 | 
				
			||||||
    rows: Vec<Box<Row>>,
 | 
					    rows: Vec<Box<Row>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl View {
 | 
				
			||||||
 | 
					    /// Determines the positions of rows based on their sizes
 | 
				
			||||||
 | 
					    /// Each row will be centered horizontally
 | 
				
			||||||
 | 
					    /// The collection of rows will not be altered vertically
 | 
				
			||||||
 | 
					    /// (TODO: make view bounds a constraint,
 | 
				
			||||||
 | 
					    /// and derive a scaling factor that lets contents fit into view)
 | 
				
			||||||
 | 
					    fn calculate_row_positions(&self, sizes: Vec<Size>) -> Vec<c::Bounds> {
 | 
				
			||||||
 | 
					        let mut y_offset = self.bounds.y;
 | 
				
			||||||
 | 
					        sizes.into_iter().map(|size| {
 | 
				
			||||||
 | 
					            let position = c::Bounds {
 | 
				
			||||||
 | 
					                x: (self.bounds.width - size.width) / 2f64,
 | 
				
			||||||
 | 
					                y: y_offset,
 | 
				
			||||||
 | 
					                width: size.width,
 | 
				
			||||||
 | 
					                height: size.height,
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            y_offset += size.height + ROW_SPACING;
 | 
				
			||||||
 | 
					            position
 | 
				
			||||||
 | 
					        }).collect()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Uses button outline information to place all buttons and rows inside.
 | 
				
			||||||
 | 
					    /// The view itself will not be affected by the sizes
 | 
				
			||||||
 | 
					    fn place_buttons_with_sizes(
 | 
				
			||||||
 | 
					        &mut self,
 | 
				
			||||||
 | 
					        button_outlines: Vec<Vec<c::Bounds>>
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        // Determine all positions
 | 
				
			||||||
 | 
					        let button_positions: Vec<_>
 | 
				
			||||||
 | 
					            = button_outlines.into_iter()
 | 
				
			||||||
 | 
					                .map(Row::calculate_button_positions)
 | 
				
			||||||
 | 
					                .collect();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        let row_sizes = button_positions.iter()
 | 
				
			||||||
 | 
					            .map(Row::calculate_row_size)
 | 
				
			||||||
 | 
					            .collect();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let row_positions = self.calculate_row_positions(row_sizes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Apply all positions
 | 
				
			||||||
 | 
					        for ((mut row, row_position), button_positions)
 | 
				
			||||||
 | 
					            in self.rows.iter_mut()
 | 
				
			||||||
 | 
					                .zip(row_positions)
 | 
				
			||||||
 | 
					                .zip(button_positions) {
 | 
				
			||||||
 | 
					            row.bounds = Some(row_position);
 | 
				
			||||||
 | 
					            for (mut button, button_position)
 | 
				
			||||||
 | 
					                in row.buttons.iter_mut()
 | 
				
			||||||
 | 
					                    .zip(button_positions) {
 | 
				
			||||||
 | 
					                button.bounds = Some(button_position);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod procedures {
 | 
					mod procedures {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user