diff --git a/eek/eek-layout.c b/eek/eek-layout.c index af27e885..738151c4 100644 --- a/eek/eek-layout.c +++ b/eek/eek-layout.c @@ -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 eek_layout_update_layout(LevelKeyboard *keyboard) { - eek_layout_place_rows(keyboard, level_keyboard_current(keyboard)); + squeek_view_place_contents(level_keyboard_current(keyboard), keyboard); } diff --git a/eek/eek-xml-layout.c b/eek/eek-xml-layout.c index 5dacce55..7ba0e153 100644 --- a/eek/eek-xml-layout.c +++ b/eek/eek-xml-layout.c @@ -929,7 +929,7 @@ eek_xml_layout_real_create_keyboard (EekLayout *self, for (uint i = 0; i < 4; i++) { if (views[i]) { - eek_layout_place_rows(keyboard, views[i]); + squeek_view_place_contents(views[i], keyboard); } } diff --git a/src/layout.h b/src/layout.h index 1b01af52..91a5a173 100644 --- a/src/layout.h +++ b/src/layout.h @@ -71,5 +71,7 @@ void squeek_view_foreach(struct squeek_view*, struct squeek_row *squeek_view_get_row(struct squeek_view *view, 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 diff --git a/src/layout.rs b/src/layout.rs index a676c3fb..58d635b7 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -41,6 +41,12 @@ pub mod c { 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 RowCallback = unsafe extern "C" fn(row: *mut ::layout::Row, data: *mut UserData); @@ -341,22 +347,26 @@ pub mod c { unsafe { eek_get_outline_size(keyboard, button.oref.0) } }).collect() } - + /// Places each button in order, starting from 0 on the left, /// keeping the spacing. /// 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] pub extern "C" - fn squeek_row_place_buttons( - row: *mut ::layout::Row, - keyboard: *const LevelKeyboard, + fn squeek_view_place_contents( + view: *mut ::layout::View, + 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); - - row.place_buttons_with_sizes(sizes); + let sizes: Vec> = view.rows.iter().map(|row| + squeek_buttons_get_outlines(&row.buttons, keyboard) + ).collect(); + + view.place_buttons_with_sizes(sizes); } #[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 #[derive(Clone, Debug)] pub struct Button { @@ -550,6 +566,7 @@ pub struct Button { const BUTTON_SPACING: f64 = 4.0; +const ROW_SPACING: f64 = 7.0; /// The graphical representation of a row of buttons pub struct Row { @@ -567,53 +584,104 @@ impl Row { bounds: None, } } - fn place_buttons_with_sizes(&mut self, outlines: Vec) { - let max_height = outlines.iter().map( + + fn last(positions: &Vec) -> Option<&c::Bounds> { + let len = positions.len(); + match len { + 0 => None, + l => Some(&positions[l - 1]) + } + } + + fn calculate_button_positions(outlines: Vec) + -> Vec + { + 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) -> Size { + let max_height = positions.iter().map( |bounds| FloatOrd(bounds.height) ).max() .unwrap_or(FloatOrd(0f64)) .0; - - let mut acc = 0f64; - let x_offsets: Vec = outlines.iter().map(|outline| { - acc += outline.x; // account for offset outlines - let out = acc; - acc += outline.width + BUTTON_SPACING; - 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 - }); + let total_width = match Row::last(positions) { + Some(position) => position.x + position.width, + None => 0f64, + }; + Size { width: total_width, height: max_height } } } pub struct View { + /// Position relative to keyboard origin bounds: c::Bounds, rows: Vec>, } +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) -> Vec { + 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> + ) { + // 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 { use super::*;