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
|
||||||
|
|||||||
154
src/layout.rs
154
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);
|
||||||
|
|
||||||
@ -341,22 +347,26 @@ pub mod c {
|
|||||||
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
|
unsafe { eek_get_outline_size(keyboard, button.oref.0) }
|
||||||
}).collect()
|
}).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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)
|
||||||
row.place_buttons_with_sizes(sizes);
|
).collect();
|
||||||
|
|
||||||
|
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 x_offsets: Vec<f64> = 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();
|
let total_width = match Row::last(positions) {
|
||||||
self.bounds = Some(c::Bounds {
|
Some(position) => position.x + position.width,
|
||||||
// FIXME: do centering of each row based on keyboard dimensions,
|
None => 0f64,
|
||||||
// one level up the iterators
|
};
|
||||||
// now centering by comparing previous width to the new,
|
Size { width: total_width, height: max_height }
|
||||||
// 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