Merge branch 'positioning' into 'master'
Positioning See merge request Librem5/squeekboard!274
This commit is contained in:
		@ -498,53 +498,6 @@ eek_renderer_get_icon_surface (const gchar *icon_name,
 | 
				
			|||||||
    return surface;
 | 
					    return surface;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static gboolean
 | 
					 | 
				
			||||||
sign (EekPoint *p1, EekPoint *p2, EekPoint *p3)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    // FIXME: what is this actually checking?
 | 
					 | 
				
			||||||
    return (p1->x - p3->x) * (p2->y - p3->y) -
 | 
					 | 
				
			||||||
        (p2->x - p3->x) * (p1->y - p3->y);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
uint32_t
 | 
					 | 
				
			||||||
eek_are_bounds_inside (EekBounds bounds, EekPoint point, EekPoint origin, int32_t angle)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    EekPoint points[4];
 | 
					 | 
				
			||||||
    gboolean b1, b2, b3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    points[0].x = bounds.x;
 | 
					 | 
				
			||||||
    points[0].y = bounds.y;
 | 
					 | 
				
			||||||
    points[1].x = points[0].x + bounds.width;
 | 
					 | 
				
			||||||
    points[1].y = points[0].y;
 | 
					 | 
				
			||||||
    points[2].x = points[1].x;
 | 
					 | 
				
			||||||
    points[2].y = points[1].y + bounds.height;
 | 
					 | 
				
			||||||
    points[3].x = points[0].x;
 | 
					 | 
				
			||||||
    points[3].y = points[2].y;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (unsigned i = 0; i < G_N_ELEMENTS(points); i++) {
 | 
					 | 
				
			||||||
        eek_point_rotate (&points[i], angle);
 | 
					 | 
				
			||||||
        points[i].x += origin.x;
 | 
					 | 
				
			||||||
        points[i].y += origin.y;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    b1 = sign (&point, &points[0], &points[1]) < 0.0;
 | 
					 | 
				
			||||||
    b2 = sign (&point, &points[1], &points[2]) < 0.0;
 | 
					 | 
				
			||||||
    b3 = sign (&point, &points[2], &points[0]) < 0.0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (b1 == b2 && b2 == b3) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    b1 = sign (&point, &points[2], &points[3]) < 0.0;
 | 
					 | 
				
			||||||
    b2 = sign (&point, &points[3], &points[0]) < 0.0;
 | 
					 | 
				
			||||||
    b3 = sign (&point, &points[0], &points[2]) < 0.0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (b1 == b2 && b2 == b3) {
 | 
					 | 
				
			||||||
        return 1;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct transformation
 | 
					struct transformation
 | 
				
			||||||
eek_renderer_get_transformation (EekRenderer *renderer) {
 | 
					eek_renderer_get_transformation (EekRenderer *renderer) {
 | 
				
			||||||
    struct transformation failed = {0};
 | 
					    struct transformation failed = {0};
 | 
				
			||||||
 | 
				
			|||||||
@ -34,6 +34,5 @@ eek_xml_layout_real_create_keyboard (const char *keyboard_type,
 | 
				
			|||||||
                                     enum squeek_arrangement_kind t)
 | 
					                                     enum squeek_arrangement_kind t)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct squeek_layout *layout = squeek_load_layout(keyboard_type, t);
 | 
					    struct squeek_layout *layout = squeek_load_layout(keyboard_type, t);
 | 
				
			||||||
    squeek_layout_place_contents(layout);
 | 
					 | 
				
			||||||
    return level_keyboard_new(manager, layout);
 | 
					    return level_keyboard_new(manager, layout);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										79
									
								
								src/data.rs
									
									
									
									
									
								
							
							
						
						
									
										79
									
								
								src/data.rs
									
									
									
									
									
								
							@ -17,6 +17,7 @@ use ::keyboard::{
 | 
				
			|||||||
    KeyState, PressType,
 | 
					    KeyState, PressType,
 | 
				
			||||||
    generate_keymap, generate_keycodes, FormattingError
 | 
					    generate_keymap, generate_keycodes, FormattingError
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					use ::layout;
 | 
				
			||||||
use ::layout::ArrangementKind;
 | 
					use ::layout::ArrangementKind;
 | 
				
			||||||
use ::resources;
 | 
					use ::resources;
 | 
				
			||||||
use ::util::c::as_str;
 | 
					use ::util::c::as_str;
 | 
				
			||||||
@ -215,6 +216,7 @@ fn load_layout_data_with_fallback(
 | 
				
			|||||||
#[derive(Debug, Deserialize, PartialEq)]
 | 
					#[derive(Debug, Deserialize, PartialEq)]
 | 
				
			||||||
#[serde(deny_unknown_fields)]
 | 
					#[serde(deny_unknown_fields)]
 | 
				
			||||||
pub struct Layout {
 | 
					pub struct Layout {
 | 
				
			||||||
 | 
					    /// FIXME: deprecate in favor of margins
 | 
				
			||||||
    bounds: Bounds,
 | 
					    bounds: Bounds,
 | 
				
			||||||
    views: HashMap<String, Vec<ButtonIds>>,
 | 
					    views: HashMap<String, Vec<ButtonIds>>,
 | 
				
			||||||
    #[serde(default)] 
 | 
					    #[serde(default)] 
 | 
				
			||||||
@ -269,6 +271,7 @@ enum Action {
 | 
				
			|||||||
#[derive(Debug, Clone, Deserialize, PartialEq)]
 | 
					#[derive(Debug, Clone, Deserialize, PartialEq)]
 | 
				
			||||||
#[serde(deny_unknown_fields)]
 | 
					#[serde(deny_unknown_fields)]
 | 
				
			||||||
struct Outline {
 | 
					struct Outline {
 | 
				
			||||||
 | 
					    /// FIXME: replace with Size
 | 
				
			||||||
    bounds: Bounds,
 | 
					    bounds: Bounds,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -303,6 +306,20 @@ impl From<io::Error> for Error {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn add_offsets<'a, I: 'a, T, F: 'a>(iterator: I, get_size: F)
 | 
				
			||||||
 | 
					    -> impl Iterator<Item=(f64, T)> + 'a
 | 
				
			||||||
 | 
					    where I: Iterator<Item=T>,
 | 
				
			||||||
 | 
					        F: Fn(&T) -> f64,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    let mut offset = 0.0;
 | 
				
			||||||
 | 
					    iterator.map(move |item| {
 | 
				
			||||||
 | 
					        let size = get_size(&item);
 | 
				
			||||||
 | 
					        let value = (offset, item);
 | 
				
			||||||
 | 
					        offset += size;
 | 
				
			||||||
 | 
					        value
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Layout {
 | 
					impl Layout {
 | 
				
			||||||
    pub fn from_resource(name: &str) -> Result<Layout, LoadError> {
 | 
					    pub fn from_resource(name: &str) -> Result<Layout, LoadError> {
 | 
				
			||||||
        let data = resources::get_keyboard(name)
 | 
					        let data = resources::get_keyboard(name)
 | 
				
			||||||
@ -403,20 +420,10 @@ impl Layout {
 | 
				
			|||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let views = HashMap::from_iter(
 | 
					        let views = HashMap::from_iter(
 | 
				
			||||||
            self.views.iter().map(|(name, view)| {(
 | 
					            self.views.iter().map(|(name, view)| {
 | 
				
			||||||
                name.clone(),
 | 
					                let rows = view.iter().map(|row| {
 | 
				
			||||||
                ::layout::View {
 | 
					                    let buttons = row.split_ascii_whitespace()
 | 
				
			||||||
                    bounds: ::layout::c::Bounds {
 | 
					                        .map(|name| {
 | 
				
			||||||
                        x: self.bounds.x,
 | 
					 | 
				
			||||||
                        y: self.bounds.y,
 | 
					 | 
				
			||||||
                        width: self.bounds.width,
 | 
					 | 
				
			||||||
                        height: self.bounds.height,
 | 
					 | 
				
			||||||
                    },
 | 
					 | 
				
			||||||
                    rows: view.iter().map(|row| {
 | 
					 | 
				
			||||||
                        ::layout::Row {
 | 
					 | 
				
			||||||
                            angle: 0,
 | 
					 | 
				
			||||||
                            bounds: None,
 | 
					 | 
				
			||||||
                            buttons: row.split_ascii_whitespace().map(|name| {
 | 
					 | 
				
			||||||
                            Box::new(create_button(
 | 
					                            Box::new(create_button(
 | 
				
			||||||
                                &self.buttons,
 | 
					                                &self.buttons,
 | 
				
			||||||
                                &self.outlines,
 | 
					                                &self.outlines,
 | 
				
			||||||
@ -426,11 +433,22 @@ impl Layout {
 | 
				
			|||||||
                                    .clone(),
 | 
					                                    .clone(),
 | 
				
			||||||
                                &mut warning_handler,
 | 
					                                &mut warning_handler,
 | 
				
			||||||
                            ))
 | 
					                            ))
 | 
				
			||||||
                            }).collect(),
 | 
					                        });
 | 
				
			||||||
 | 
					                    ::layout::Row {
 | 
				
			||||||
 | 
					                        angle: 0,
 | 
				
			||||||
 | 
					                        buttons: add_offsets(
 | 
				
			||||||
 | 
					                            buttons,
 | 
				
			||||||
 | 
					                            |button| button.size.width,
 | 
				
			||||||
 | 
					                        ).collect()
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    }).collect(),
 | 
					                });
 | 
				
			||||||
                }
 | 
					                let rows = add_offsets(rows, |row| row.get_height())
 | 
				
			||||||
            )})
 | 
					                    .collect();
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    name.clone(),
 | 
				
			||||||
 | 
					                    layout::View::new(rows)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        (
 | 
					        (
 | 
				
			||||||
@ -440,6 +458,13 @@ impl Layout {
 | 
				
			|||||||
                    CString::new(keymap_str)
 | 
					                    CString::new(keymap_str)
 | 
				
			||||||
                        .expect("Invalid keymap string generated")
 | 
					                        .expect("Invalid keymap string generated")
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
 | 
					                // FIXME: use a dedicated field
 | 
				
			||||||
 | 
					                margins: layout::Margins {
 | 
				
			||||||
 | 
					                    top: self.bounds.x,
 | 
				
			||||||
 | 
					                    left: self.bounds.y,
 | 
				
			||||||
 | 
					                    bottom: 0.0,
 | 
				
			||||||
 | 
					                    right: self.bounds.y,
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
            }),
 | 
					            }),
 | 
				
			||||||
            warning_handler,
 | 
					            warning_handler,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@ -629,13 +654,11 @@ fn create_button<H: WarningHandler>(
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ::layout::Button {
 | 
					    layout::Button {
 | 
				
			||||||
        name: cname,
 | 
					        name: cname,
 | 
				
			||||||
        outline_name: CString::new(outline_name).expect("Bad outline"),
 | 
					        outline_name: CString::new(outline_name).expect("Bad outline"),
 | 
				
			||||||
        // TODO: do layout before creating buttons
 | 
					        // TODO: do layout before creating buttons
 | 
				
			||||||
        bounds: ::layout::c::Bounds {
 | 
					        size: layout::Size {
 | 
				
			||||||
            x: outline.bounds.x,
 | 
					 | 
				
			||||||
            y: outline.bounds.y,
 | 
					 | 
				
			||||||
            width: outline.bounds.width,
 | 
					            width: outline.bounds.width,
 | 
				
			||||||
            height: outline.bounds.height,
 | 
					            height: outline.bounds.height,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
@ -734,8 +757,8 @@ mod tests {
 | 
				
			|||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            out.views["base"]
 | 
					            out.views["base"]
 | 
				
			||||||
                .rows[0]
 | 
					                .get_rows()[0].1
 | 
				
			||||||
                .buttons[0]
 | 
					                .buttons[0].1
 | 
				
			||||||
                .label,
 | 
					                .label,
 | 
				
			||||||
            ::layout::Label::Text(CString::new("test").unwrap())
 | 
					            ::layout::Label::Text(CString::new("test").unwrap())
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -749,8 +772,8 @@ mod tests {
 | 
				
			|||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            out.views["base"]
 | 
					            out.views["base"]
 | 
				
			||||||
                .rows[0]
 | 
					                .get_rows()[0].1
 | 
				
			||||||
                .buttons[0]
 | 
					                .buttons[0].1
 | 
				
			||||||
                .label,
 | 
					                .label,
 | 
				
			||||||
            ::layout::Label::Text(CString::new("test").unwrap())
 | 
					            ::layout::Label::Text(CString::new("test").unwrap())
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@ -765,8 +788,8 @@ mod tests {
 | 
				
			|||||||
            .unwrap();
 | 
					            .unwrap();
 | 
				
			||||||
        assert_eq!(
 | 
					        assert_eq!(
 | 
				
			||||||
            out.views["base"]
 | 
					            out.views["base"]
 | 
				
			||||||
                .rows[0]
 | 
					                .get_rows()[0].1
 | 
				
			||||||
                .buttons[0]
 | 
					                .buttons[0].1
 | 
				
			||||||
                .state.borrow()
 | 
					                .state.borrow()
 | 
				
			||||||
                .keycodes.len(),
 | 
					                .keycodes.len(),
 | 
				
			||||||
            2
 | 
					            2
 | 
				
			||||||
 | 
				
			|||||||
@ -48,17 +48,14 @@ mod c {
 | 
				
			|||||||
        let cr = unsafe { cairo::Context::from_raw_none(cr) };
 | 
					        let cr = unsafe { cairo::Context::from_raw_none(cr) };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let view = layout.get_current_view();
 | 
					        let view = layout.get_current_view();
 | 
				
			||||||
        let view_position = view.bounds.get_position();
 | 
					        for (row_offset, row) in &view.get_rows() {
 | 
				
			||||||
        for row in &view.rows {
 | 
					            for (x_offset, button) in &row.buttons {
 | 
				
			||||||
            for button in &row.buttons {
 | 
					 | 
				
			||||||
                let state = RefCell::borrow(&button.state).clone();
 | 
					                let state = RefCell::borrow(&button.state).clone();
 | 
				
			||||||
                if state.pressed == keyboard::PressType::Pressed || state.locked {
 | 
					                if state.pressed == keyboard::PressType::Pressed || state.locked {
 | 
				
			||||||
                    let position = &view_position
 | 
					 | 
				
			||||||
                        + row.bounds.clone().unwrap().get_position()
 | 
					 | 
				
			||||||
                        + button.bounds.get_position();
 | 
					 | 
				
			||||||
                    render_button_at_position(
 | 
					                    render_button_at_position(
 | 
				
			||||||
                        renderer, &cr,
 | 
					                        renderer, &cr,
 | 
				
			||||||
                        position, button.as_ref(),
 | 
					                        row_offset + Point { x: *x_offset, y: 0.0 },
 | 
				
			||||||
 | 
					                        button.as_ref(),
 | 
				
			||||||
                        state.pressed, state.locked,
 | 
					                        state.pressed, state.locked,
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -76,15 +73,12 @@ mod c {
 | 
				
			|||||||
        let layout = unsafe { &mut *layout };
 | 
					        let layout = unsafe { &mut *layout };
 | 
				
			||||||
        let cr = unsafe { cairo::Context::from_raw_none(cr) };
 | 
					        let cr = unsafe { cairo::Context::from_raw_none(cr) };
 | 
				
			||||||
        let view = layout.get_current_view();
 | 
					        let view = layout.get_current_view();
 | 
				
			||||||
        let view_position = view.bounds.get_position();
 | 
					        for (row_offset, row) in &view.get_rows() {
 | 
				
			||||||
        for row in &view.rows {
 | 
					            for (x_offset, button) in &row.buttons {
 | 
				
			||||||
            for button in &row.buttons {
 | 
					 | 
				
			||||||
                let position = &view_position
 | 
					 | 
				
			||||||
                    + row.bounds.clone().unwrap().get_position()
 | 
					 | 
				
			||||||
                    + button.bounds.get_position();
 | 
					 | 
				
			||||||
                render_button_at_position(
 | 
					                render_button_at_position(
 | 
				
			||||||
                    renderer, &cr,
 | 
					                    renderer, &cr,
 | 
				
			||||||
                    position, button.as_ref(),
 | 
					                    row_offset + Point { x: *x_offset, y: 0.0 },
 | 
				
			||||||
 | 
					                    button.as_ref(),
 | 
				
			||||||
                    keyboard::PressType::Released, false,
 | 
					                    keyboard::PressType::Released, false,
 | 
				
			||||||
                );
 | 
					                );
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -105,7 +99,7 @@ pub fn render_button_at_position(
 | 
				
			|||||||
    cr.translate(position.x, position.y);
 | 
					    cr.translate(position.x, position.y);
 | 
				
			||||||
    cr.rectangle(
 | 
					    cr.rectangle(
 | 
				
			||||||
        0.0, 0.0,
 | 
					        0.0, 0.0,
 | 
				
			||||||
        button.bounds.width, button.bounds.height
 | 
					        button.size.width, button.size.height
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
    cr.clip();
 | 
					    cr.clip();
 | 
				
			||||||
    unsafe {
 | 
					    unsafe {
 | 
				
			||||||
 | 
				
			|||||||
@ -28,9 +28,6 @@ struct transformation squeek_layout_calculate_transformation(
 | 
				
			|||||||
        const struct squeek_layout *layout,
 | 
					        const struct squeek_layout *layout,
 | 
				
			||||||
        double allocation_width, double allocation_size);
 | 
					        double allocation_width, double allocation_size);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void
 | 
					 | 
				
			||||||
squeek_layout_place_contents(struct squeek_layout*);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
 | 
					struct squeek_layout *squeek_load_layout(const char *name, uint32_t type);
 | 
				
			||||||
const char *squeek_layout_get_keymap(const struct squeek_layout*);
 | 
					const char *squeek_layout_get_keymap(const struct squeek_layout*);
 | 
				
			||||||
enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
 | 
					enum squeek_arrangement_kind squeek_layout_get_kind(const struct squeek_layout *);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										454
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										454
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -25,9 +25,9 @@ use std::vec::Vec;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use ::action::Action;
 | 
					use ::action::Action;
 | 
				
			||||||
use ::drawing;
 | 
					use ::drawing;
 | 
				
			||||||
use ::float_ord::FloatOrd;
 | 
					 | 
				
			||||||
use ::keyboard::{ KeyState, PressType };
 | 
					use ::keyboard::{ KeyState, PressType };
 | 
				
			||||||
use ::submission::{ Timestamp, VirtualKeyboard };
 | 
					use ::submission::{ Timestamp, VirtualKeyboard };
 | 
				
			||||||
 | 
					use ::util::find_max_double;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::borrow::Borrow;
 | 
					use std::borrow::Borrow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -40,7 +40,7 @@ pub mod c {
 | 
				
			|||||||
    use std::os::raw::{ c_char, c_void };
 | 
					    use std::os::raw::{ c_char, c_void };
 | 
				
			||||||
    use std::ptr;
 | 
					    use std::ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    use std::ops::Add;
 | 
					    use std::ops::{ Add, Sub };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // The following defined in C
 | 
					    // The following defined in C
 | 
				
			||||||
    #[repr(transparent)]
 | 
					    #[repr(transparent)]
 | 
				
			||||||
@ -49,7 +49,7 @@ pub mod c {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /// Defined in eek-types.h
 | 
					    /// Defined in eek-types.h
 | 
				
			||||||
    #[repr(C)]
 | 
					    #[repr(C)]
 | 
				
			||||||
    #[derive(Clone, Debug)]
 | 
					    #[derive(Clone, Debug, PartialEq)]
 | 
				
			||||||
    pub struct Point {
 | 
					    pub struct Point {
 | 
				
			||||||
        pub x: f64,
 | 
					        pub x: f64,
 | 
				
			||||||
        pub y: f64,
 | 
					        pub y: f64,
 | 
				
			||||||
@ -72,6 +72,16 @@ pub mod c {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    impl Sub<&Point> for Point {
 | 
				
			||||||
 | 
					        type Output = Point;
 | 
				
			||||||
 | 
					        fn sub(self, other: &Point) -> Point {
 | 
				
			||||||
 | 
					            Point {
 | 
				
			||||||
 | 
					                x: self.x - other.x,
 | 
				
			||||||
 | 
					                y: self.y - other.y,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Defined in eek-types.h
 | 
					    /// Defined in eek-types.h
 | 
				
			||||||
    #[repr(C)]
 | 
					    #[repr(C)]
 | 
				
			||||||
    #[derive(Clone, Debug, PartialEq)]
 | 
					    #[derive(Clone, Debug, PartialEq)]
 | 
				
			||||||
@ -83,11 +93,9 @@ pub mod c {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    impl Bounds {
 | 
					    impl Bounds {
 | 
				
			||||||
        pub fn get_position(&self) -> Point {
 | 
					        pub fn contains(&self, point: &Point) -> bool {
 | 
				
			||||||
            Point {
 | 
					            (point.x > self.x && point.x < self.x + self.width
 | 
				
			||||||
                x: self.x,
 | 
					                && point.y > self.y && point.y < self.y + self.height)
 | 
				
			||||||
                y: self.y,
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -133,7 +141,10 @@ pub mod c {
 | 
				
			|||||||
    pub extern "C"
 | 
					    pub extern "C"
 | 
				
			||||||
    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
					    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
				
			||||||
        let button = unsafe { &*button };
 | 
					        let button = unsafe { &*button };
 | 
				
			||||||
        button.bounds.clone()
 | 
					        Bounds {
 | 
				
			||||||
 | 
					            x: 0.0, y: 0.0,
 | 
				
			||||||
 | 
					            width: button.size.width, height: button.size.height
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[no_mangle]
 | 
					    #[no_mangle]
 | 
				
			||||||
@ -193,13 +204,13 @@ pub mod c {
 | 
				
			|||||||
        allocation_height: f64,
 | 
					        allocation_height: f64,
 | 
				
			||||||
    ) -> Transformation {
 | 
					    ) -> Transformation {
 | 
				
			||||||
        let layout = unsafe { &*layout };
 | 
					        let layout = unsafe { &*layout };
 | 
				
			||||||
        let bounds = &layout.get_current_view().bounds;
 | 
					        let size = layout.calculate_size();
 | 
				
			||||||
        let h_scale = allocation_width / bounds.width;
 | 
					        let h_scale = allocation_width / size.width;
 | 
				
			||||||
        let v_scale = allocation_height / bounds.height;
 | 
					        let v_scale = allocation_height / size.height;
 | 
				
			||||||
        let scale = if h_scale > v_scale { h_scale } else { v_scale };
 | 
					        let scale = if h_scale < v_scale { h_scale } else { v_scale };
 | 
				
			||||||
        Transformation {
 | 
					        Transformation {
 | 
				
			||||||
            origin_x: allocation_width - (scale * bounds.width) / 2.0,
 | 
					            origin_x: (allocation_width - (scale * size.width)) / 2.0,
 | 
				
			||||||
            origin_y: allocation_height - (scale * bounds.height) / 2.0,
 | 
					            origin_y: (allocation_height - (scale * size.height)) / 2.0,
 | 
				
			||||||
            scale: scale,
 | 
					            scale: scale,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -230,58 +241,11 @@ pub mod c {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        use ::submission::c::ZwpVirtualKeyboardV1;
 | 
					        use ::submission::c::ZwpVirtualKeyboardV1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[repr(C)]
 | 
					 | 
				
			||||||
        #[derive(PartialEq, Debug)]
 | 
					 | 
				
			||||||
        pub struct CButtonPlace {
 | 
					 | 
				
			||||||
            row: *const Row,
 | 
					 | 
				
			||||||
            button: *const Button,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        impl<'a> From<ButtonPlace<'a>> for CButtonPlace {
 | 
					 | 
				
			||||||
            fn from(value: ButtonPlace<'a>) -> CButtonPlace {
 | 
					 | 
				
			||||||
                CButtonPlace {
 | 
					 | 
				
			||||||
                    row: value.row as *const Row,
 | 
					 | 
				
			||||||
                    button: value.button as *const Button,
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // This is constructed only in C, no need for warnings
 | 
					        // This is constructed only in C, no need for warnings
 | 
				
			||||||
        #[allow(dead_code)]
 | 
					        #[allow(dead_code)]
 | 
				
			||||||
        #[repr(transparent)]
 | 
					        #[repr(transparent)]
 | 
				
			||||||
        pub struct LevelKeyboard(*const c_void);
 | 
					        pub struct LevelKeyboard(*const c_void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        #[no_mangle]
 | 
					 | 
				
			||||||
        extern "C" {
 | 
					 | 
				
			||||||
            /// Checks if point falls within bounds,
 | 
					 | 
				
			||||||
            /// which are relative to origin and rotated by angle (I think)
 | 
					 | 
				
			||||||
            pub fn eek_are_bounds_inside (bounds: Bounds,
 | 
					 | 
				
			||||||
                point: Point,
 | 
					 | 
				
			||||||
                origin: Point,
 | 
					 | 
				
			||||||
                angle: i32
 | 
					 | 
				
			||||||
            ) -> u32;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// Places each button in order, starting from 0 on the left,
 | 
					 | 
				
			||||||
        /// keeping the spacing.
 | 
					 | 
				
			||||||
        /// Sizes each button according to outline dimensions.
 | 
					 | 
				
			||||||
        /// 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_layout_place_contents(layout: *mut Layout) {
 | 
					 | 
				
			||||||
            let layout = unsafe { &mut *layout };
 | 
					 | 
				
			||||||
            for view in layout.views.values_mut() {
 | 
					 | 
				
			||||||
                let sizes: Vec<Vec<Bounds>> = view.rows.iter().map(|row| {
 | 
					 | 
				
			||||||
                    row.buttons.iter()
 | 
					 | 
				
			||||||
                        .map(|button| button.bounds.clone())
 | 
					 | 
				
			||||||
                        .collect()
 | 
					 | 
				
			||||||
                }).collect();
 | 
					 | 
				
			||||||
                view.place_buttons_with_sizes(sizes);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// Release pointer in the specified position
 | 
					        /// Release pointer in the specified position
 | 
				
			||||||
        #[no_mangle]
 | 
					        #[no_mangle]
 | 
				
			||||||
        pub extern "C"
 | 
					        pub extern "C"
 | 
				
			||||||
@ -348,8 +312,13 @@ pub mod c {
 | 
				
			|||||||
                Point { x: x_widget, y: y_widget }
 | 
					                Point { x: x_widget, y: y_widget }
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if let Some(position) = layout.get_button_at_point(point) {
 | 
					            let state = {
 | 
				
			||||||
                let mut state = position.button.state.clone();
 | 
					                let view = layout.get_current_view();
 | 
				
			||||||
 | 
					                view.find_button_by_position(point)
 | 
				
			||||||
 | 
					                    .map(|place| place.button.state.clone())
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if let Some(mut state) = state {
 | 
				
			||||||
                layout.press_key(
 | 
					                layout.press_key(
 | 
				
			||||||
                    &VirtualKeyboard(virtual_keyboard),
 | 
					                    &VirtualKeyboard(virtual_keyboard),
 | 
				
			||||||
                    &mut state,
 | 
					                    &mut state,
 | 
				
			||||||
@ -357,7 +326,7 @@ pub mod c {
 | 
				
			|||||||
                );
 | 
					                );
 | 
				
			||||||
                // maybe TODO: draw on the display buffer here
 | 
					                // maybe TODO: draw on the display buffer here
 | 
				
			||||||
                drawing::queue_redraw(ui_keyboard);
 | 
					                drawing::queue_redraw(ui_keyboard);
 | 
				
			||||||
            }
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // FIXME: this will work funny
 | 
					        // FIXME: this will work funny
 | 
				
			||||||
@ -388,9 +357,7 @@ pub mod c {
 | 
				
			|||||||
                place.map(|place| {(
 | 
					                place.map(|place| {(
 | 
				
			||||||
                    place.button.state.clone(),
 | 
					                    place.button.state.clone(),
 | 
				
			||||||
                    place.button.clone(),
 | 
					                    place.button.clone(),
 | 
				
			||||||
                    view.bounds.get_position()
 | 
					                    place.offset,
 | 
				
			||||||
                        + place.row.bounds.clone().unwrap().get_position()
 | 
					 | 
				
			||||||
                        + place.button.bounds.get_position(),
 | 
					 | 
				
			||||||
                )})
 | 
					                )})
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -455,18 +422,12 @@ pub mod c {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Relative to `View`
 | 
					 | 
				
			||||||
struct ButtonPosition {
 | 
					 | 
				
			||||||
    view_position: c::Point,
 | 
					 | 
				
			||||||
    button: Button,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct ButtonPlace<'a> {
 | 
					pub struct ButtonPlace<'a> {
 | 
				
			||||||
    button: &'a Button,
 | 
					    button: &'a Button,
 | 
				
			||||||
    row: &'a Row,
 | 
					    offset: c::Point,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug, Clone)]
 | 
				
			||||||
pub struct Size {
 | 
					pub struct Size {
 | 
				
			||||||
    pub width: f64,
 | 
					    pub width: f64,
 | 
				
			||||||
    pub height: f64,
 | 
					    pub height: f64,
 | 
				
			||||||
@ -487,9 +448,7 @@ pub struct Button {
 | 
				
			|||||||
    pub name: CString,
 | 
					    pub name: CString,
 | 
				
			||||||
    /// Label to display to the user
 | 
					    /// Label to display to the user
 | 
				
			||||||
    pub label: Label,
 | 
					    pub label: Label,
 | 
				
			||||||
    /// TODO: position the buttons before they get initial bounds
 | 
					    pub size: Size,
 | 
				
			||||||
    /// Position relative to some origin (i.e. parent/row)
 | 
					 | 
				
			||||||
    pub bounds: c::Bounds,
 | 
					 | 
				
			||||||
    /// The name of the visual class applied
 | 
					    /// The name of the visual class applied
 | 
				
			||||||
    pub outline_name: CString,
 | 
					    pub outline_name: CString,
 | 
				
			||||||
    /// current state, shared with other buttons
 | 
					    /// current state, shared with other buttons
 | 
				
			||||||
@ -498,66 +457,39 @@ pub struct Button {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/// The graphical representation of a row of buttons
 | 
					/// The graphical representation of a row of buttons
 | 
				
			||||||
pub struct Row {
 | 
					pub struct Row {
 | 
				
			||||||
    pub buttons: Vec<Box<Button>>,
 | 
					    /// Buttons together with their offset from the left
 | 
				
			||||||
 | 
					    pub buttons: Vec<(f64, Box<Button>)>,
 | 
				
			||||||
    /// Angle is not really used anywhere...
 | 
					    /// Angle is not really used anywhere...
 | 
				
			||||||
    pub angle: i32,
 | 
					    pub angle: i32,
 | 
				
			||||||
    /// Position relative to some origin (i.e. parent/view origin)
 | 
					 | 
				
			||||||
    pub bounds: Option<c::Bounds>,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Row {    
 | 
					impl Row {    
 | 
				
			||||||
    fn last(positions: &Vec<c::Bounds>) -> Option<&c::Bounds> {
 | 
					    pub fn get_height(&self) -> f64 {
 | 
				
			||||||
        let len = positions.len();
 | 
					        find_max_double(
 | 
				
			||||||
        match len {
 | 
					            self.buttons.iter(),
 | 
				
			||||||
            0 => None,
 | 
					            |(_offset, button)| button.size.height,
 | 
				
			||||||
            l => Some(&positions[l - 1])
 | 
					        )
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn calculate_button_positions(outlines: Vec<c::Bounds>) -> Vec<c::Bounds> {
 | 
					    fn get_width(&self) -> f64 {
 | 
				
			||||||
        let mut x_offset = 0f64;
 | 
					        self.buttons.iter().next_back()
 | 
				
			||||||
        outlines.iter().map(|outline| {
 | 
					            .map(|(x_offset, button)| button.size.width + x_offset)
 | 
				
			||||||
            x_offset += outline.x; // account for offset outlines
 | 
					            .unwrap_or(0.0)
 | 
				
			||||||
            let position = c::Bounds {
 | 
					 | 
				
			||||||
                x: x_offset,
 | 
					 | 
				
			||||||
                ..outline.clone()
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            x_offset += outline.width;
 | 
					 | 
				
			||||||
            position
 | 
					 | 
				
			||||||
        }).collect()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    fn calculate_row_size(positions: &Vec<c::Bounds>) -> Size {
 | 
					 | 
				
			||||||
        let max_height = positions.iter().map(
 | 
					 | 
				
			||||||
            |bounds| FloatOrd(bounds.height)
 | 
					 | 
				
			||||||
        ).max()
 | 
					 | 
				
			||||||
            .unwrap_or(FloatOrd(0f64))
 | 
					 | 
				
			||||||
            .0;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        let total_width = match Row::last(positions) {
 | 
					 | 
				
			||||||
            Some(position) => position.x + position.width,
 | 
					 | 
				
			||||||
            None => 0f64,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        Size { width: total_width, height: max_height }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Finds the first button that covers the specified point
 | 
					    /// Finds the first button that covers the specified point
 | 
				
			||||||
    /// relative to row's position's origin
 | 
					    /// relative to row's position's origin
 | 
				
			||||||
    fn find_button_by_position(&self, point: c::Point)
 | 
					    fn find_button_by_position(&self, point: c::Point)
 | 
				
			||||||
        -> Option<&Box<Button>>
 | 
					        -> Option<(f64, &Box<Button>)>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        let row_bounds = self.bounds.as_ref().expect("Missing bounds on row");
 | 
					        self.buttons.iter().find(|(x_offset, button)| {
 | 
				
			||||||
        let origin = c::Point {
 | 
					            c::Bounds {
 | 
				
			||||||
            x: row_bounds.x,
 | 
					                x: *x_offset, y: 0.0,
 | 
				
			||||||
            y: row_bounds.y,
 | 
					                width: button.size.width,
 | 
				
			||||||
        };
 | 
					                height: button.size.height,
 | 
				
			||||||
        let angle = self.angle;
 | 
					            }.contains(&point)
 | 
				
			||||||
        self.buttons.iter().find(|button| {
 | 
					 | 
				
			||||||
            let bounds = button.bounds.clone();
 | 
					 | 
				
			||||||
            let point = point.clone();
 | 
					 | 
				
			||||||
            let origin = origin.clone();
 | 
					 | 
				
			||||||
            procedures::is_point_inside(bounds, point, origin, angle)
 | 
					 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
 | 
					            .map(|(x_offset, button)| (*x_offset, button))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -568,84 +500,56 @@ pub struct Spacing {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct View {
 | 
					pub struct View {
 | 
				
			||||||
    /// Position relative to keyboard origin
 | 
					    /// Rows together with their offsets from the top
 | 
				
			||||||
    pub bounds: c::Bounds,
 | 
					    rows: Vec<(f64, Row)>,
 | 
				
			||||||
    pub rows: Vec<Row>,
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl View {
 | 
					impl View {
 | 
				
			||||||
    /// Determines the positions of rows based on their sizes
 | 
					    pub fn new(rows: Vec<(f64, Row)>) -> View {
 | 
				
			||||||
    /// Each row will be centered horizontally
 | 
					        View { rows }
 | 
				
			||||||
    /// The collection of rows will not be altered vertically
 | 
					 | 
				
			||||||
    /// (TODO: maybe make view bounds a constraint,
 | 
					 | 
				
			||||||
    /// and derive a scaling factor that lets contents fit into view)
 | 
					 | 
				
			||||||
    /// (or TODO: blow up view bounds to match contents
 | 
					 | 
				
			||||||
    /// and then scale the entire thing)
 | 
					 | 
				
			||||||
    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;
 | 
					 | 
				
			||||||
            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(|outlines| {
 | 
					 | 
				
			||||||
                    Row::calculate_button_positions(outlines)
 | 
					 | 
				
			||||||
                })
 | 
					 | 
				
			||||||
                .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 = button_position;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Finds the first button that covers the specified point
 | 
					    /// Finds the first button that covers the specified point
 | 
				
			||||||
    /// relative to view's position's origin
 | 
					    /// relative to view's position's origin
 | 
				
			||||||
    fn find_button_by_position(&self, point: c::Point)
 | 
					    fn find_button_by_position(&self, point: c::Point)
 | 
				
			||||||
        -> Option<ButtonPlace>
 | 
					        -> Option<ButtonPlace>
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        // make point relative to the inside of the view,
 | 
					        self.get_rows().iter().find_map(|(row_offset, row)| {
 | 
				
			||||||
        // which is the origin of all rows
 | 
					            // make point relative to the inside of the row
 | 
				
			||||||
        let point = c::Point {
 | 
					            row.find_button_by_position({
 | 
				
			||||||
            x: point.x - self.bounds.x,
 | 
					                c::Point { x: point.x, y: point.y } - row_offset
 | 
				
			||||||
            y: point.y - self.bounds.y,
 | 
					            }).map(|(button_x_offset, button)| ButtonPlace {
 | 
				
			||||||
        };
 | 
					                button,
 | 
				
			||||||
 | 
					                offset: row_offset + c::Point {
 | 
				
			||||||
        self.rows.iter().find_map(|row| {
 | 
					                    x: button_x_offset,
 | 
				
			||||||
            row.find_button_by_position(point.clone())
 | 
					                    y: 0.0,
 | 
				
			||||||
                .map(|button| ButtonPlace {row, button})
 | 
					                },
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    fn get_width(&self) -> f64 {
 | 
				
			||||||
 | 
					        // No need to call `get_rows()`,
 | 
				
			||||||
 | 
					        // as the biggest row is the most far reaching in both directions
 | 
				
			||||||
 | 
					        // because they are all centered.
 | 
				
			||||||
 | 
					        find_max_double(self.rows.iter(), |(_offset, row)| row.get_width())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    fn get_height(&self) -> f64 {
 | 
				
			||||||
 | 
					        self.rows.iter().next_back()
 | 
				
			||||||
 | 
					            .map(|(y_offset, row)| row.get_height() + y_offset)
 | 
				
			||||||
 | 
					            .unwrap_or(0.0)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /// Returns positioned rows, with appropriate x offsets (centered)
 | 
				
			||||||
 | 
					    pub fn get_rows(&self) -> Vec<(c::Point, &Row)> {
 | 
				
			||||||
 | 
					        let available_width = self.get_width();
 | 
				
			||||||
 | 
					        self.rows.iter().map(|(y_offset, row)| {(
 | 
				
			||||||
 | 
					            c::Point {
 | 
				
			||||||
 | 
					                x: (available_width - row.get_width()) / 2.0,
 | 
				
			||||||
 | 
					                y: *y_offset,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            row,
 | 
				
			||||||
 | 
					        )}).collect()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -656,10 +560,18 @@ pub enum ArrangementKind {
 | 
				
			|||||||
    Wide = 1,
 | 
					    Wide = 1,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub struct Margins {
 | 
				
			||||||
 | 
					    pub top: f64,
 | 
				
			||||||
 | 
					    pub bottom: f64,
 | 
				
			||||||
 | 
					    pub left: f64,
 | 
				
			||||||
 | 
					    pub right: f64,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: split into sth like
 | 
					// TODO: split into sth like
 | 
				
			||||||
// Arrangement (views) + details (keymap) + State (keys)
 | 
					// Arrangement (views) + details (keymap) + State (keys)
 | 
				
			||||||
/// State of the UI, contains the backend as well
 | 
					/// State of the UI, contains the backend as well
 | 
				
			||||||
pub struct Layout {
 | 
					pub struct Layout {
 | 
				
			||||||
 | 
					    pub margins: Margins,
 | 
				
			||||||
    pub kind: ArrangementKind,
 | 
					    pub kind: ArrangementKind,
 | 
				
			||||||
    pub current_view: String,
 | 
					    pub current_view: String,
 | 
				
			||||||
    // Views own the actual buttons which have state
 | 
					    // Views own the actual buttons which have state
 | 
				
			||||||
@ -686,6 +598,7 @@ pub struct Layout {
 | 
				
			|||||||
pub struct LayoutData {
 | 
					pub struct LayoutData {
 | 
				
			||||||
    pub views: HashMap<String, View>,
 | 
					    pub views: HashMap<String, View>,
 | 
				
			||||||
    pub keymap_str: CString,
 | 
					    pub keymap_str: CString,
 | 
				
			||||||
 | 
					    pub margins: Margins,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct NoSuchView;
 | 
					struct NoSuchView;
 | 
				
			||||||
@ -703,6 +616,7 @@ impl Layout {
 | 
				
			|||||||
            keymap_str: data.keymap_str,
 | 
					            keymap_str: data.keymap_str,
 | 
				
			||||||
            pressed_keys: HashSet::new(),
 | 
					            pressed_keys: HashSet::new(),
 | 
				
			||||||
            locked_keys: HashSet::new(),
 | 
					            locked_keys: HashSet::new(),
 | 
				
			||||||
 | 
					            margins: data.margins,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -799,54 +713,46 @@ impl Layout {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn get_button_at_point(&self, point: c::Point) -> Option<ButtonPosition> {
 | 
					    fn calculate_size(&self) -> Size {
 | 
				
			||||||
        let view = self.get_current_view();
 | 
					        Size {
 | 
				
			||||||
        let place = view.find_button_by_position(point);
 | 
					            height: find_max_double(
 | 
				
			||||||
        place.map(|place| ButtonPosition {
 | 
					                self.views.iter(),
 | 
				
			||||||
            button: place.button.clone(),
 | 
					                |(_name, view)| view.get_height(),
 | 
				
			||||||
            // Rows have no business being inside a view
 | 
					            ),
 | 
				
			||||||
            // if they have no valid bounds.
 | 
					            width: find_max_double(
 | 
				
			||||||
            view_position: place.row.bounds.clone().unwrap().get_position()
 | 
					                self.views.iter(),
 | 
				
			||||||
                + place.button.bounds.get_position(),
 | 
					                |(_name, view)| view.get_width(),
 | 
				
			||||||
        })
 | 
					            ),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
mod procedures {
 | 
					mod procedures {
 | 
				
			||||||
    use super::*;
 | 
					    use super::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    type Path<'v> = (&'v Row, &'v Box<Button>);
 | 
					    type Place<'v> = (c::Point, &'v Box<Button>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Finds all `(row, button)` paths that refer to the specified key `state`
 | 
					    /// Finds all buttons referring to the key in `state`,
 | 
				
			||||||
    pub fn find_key_paths<'v, 's>(
 | 
					    /// together with their offsets within the view.
 | 
				
			||||||
 | 
					    pub fn find_key_places<'v, 's>(
 | 
				
			||||||
        view: &'v View,
 | 
					        view: &'v View,
 | 
				
			||||||
        state: &'s Rc<RefCell<KeyState>>
 | 
					        state: &'s Rc<RefCell<KeyState>>
 | 
				
			||||||
    ) -> Vec<Path<'v>> {
 | 
					    ) -> Vec<Place<'v>> {
 | 
				
			||||||
        view.rows.iter().flat_map(|row| {
 | 
					        view.get_rows().iter().flat_map(|(row_offset, row)| {
 | 
				
			||||||
            let row_paths: Vec<Path> = row.buttons.iter().filter_map(|button| {
 | 
					            row.buttons.iter()
 | 
				
			||||||
 | 
					                .filter_map(move |(x_offset, button)| {
 | 
				
			||||||
                    if Rc::ptr_eq(&button.state, state) {
 | 
					                    if Rc::ptr_eq(&button.state, state) {
 | 
				
			||||||
                    Some((row, button))
 | 
					                        Some((
 | 
				
			||||||
 | 
					                            row_offset + c::Point { x: *x_offset, y: 0.0 },
 | 
				
			||||||
 | 
					                            button,
 | 
				
			||||||
 | 
					                        ))
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        None
 | 
					                        None
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
            }).collect(); // collecting not to let row references outlive the function
 | 
					                })
 | 
				
			||||||
            row_paths.into_iter()
 | 
					 | 
				
			||||||
        }).collect()
 | 
					        }).collect()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /// Checks if point is inside bounds which are rotated by angle.
 | 
					 | 
				
			||||||
    /// FIXME: what's origin about?
 | 
					 | 
				
			||||||
    pub fn is_point_inside(
 | 
					 | 
				
			||||||
        bounds: c::Bounds,
 | 
					 | 
				
			||||||
        point: c::Point,
 | 
					 | 
				
			||||||
        origin: c::Point,
 | 
					 | 
				
			||||||
        angle: i32
 | 
					 | 
				
			||||||
    ) -> bool {
 | 
					 | 
				
			||||||
        (unsafe {
 | 
					 | 
				
			||||||
            c::procedures::eek_are_bounds_inside(bounds, point, origin, angle)
 | 
					 | 
				
			||||||
        }) == 1
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    #[cfg(test)]
 | 
					    #[cfg(test)]
 | 
				
			||||||
    mod test {
 | 
					    mod test {
 | 
				
			||||||
        use super::*;
 | 
					        use super::*;
 | 
				
			||||||
@ -868,62 +774,33 @@ mod procedures {
 | 
				
			|||||||
            let button = make_button_with_state("1".into(), state);
 | 
					            let button = make_button_with_state("1".into(), state);
 | 
				
			||||||
            let button_ptr = as_ptr(&button);
 | 
					            let button_ptr = as_ptr(&button);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let row_bounds = Some(c::Bounds {
 | 
					 | 
				
			||||||
                x: 0.1, y: 2.3,
 | 
					 | 
				
			||||||
                width: 4.5, height: 6.7,
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
            let row = Row {
 | 
					            let row = Row {
 | 
				
			||||||
                buttons: vec!(button),
 | 
					                buttons: vec!((0.1, button)),
 | 
				
			||||||
                angle: 0,
 | 
					                angle: 0,
 | 
				
			||||||
                bounds: row_bounds.clone(),
 | 
					 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let view = View {
 | 
					            let view = View {
 | 
				
			||||||
                bounds: c::Bounds {
 | 
					                rows: vec!((1.2, row)),
 | 
				
			||||||
                    x: 0f64, y: 0f64,
 | 
					 | 
				
			||||||
                    width: 0f64, height: 0f64
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                rows: vec!(row),
 | 
					 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            assert_eq!(
 | 
					            assert_eq!(
 | 
				
			||||||
                find_key_paths(&view, &state_clone.clone()).iter()
 | 
					                find_key_places(&view, &state_clone.clone()).into_iter()
 | 
				
			||||||
                    .map(|(row, button)| { (row.bounds.clone(), as_ptr(button)) })
 | 
					                    .map(|(place, button)| { (place, as_ptr(button)) })
 | 
				
			||||||
                    .collect::<Vec<_>>(),
 | 
					                    .collect::<Vec<_>>(),
 | 
				
			||||||
                vec!(
 | 
					                vec!(
 | 
				
			||||||
                    (row_bounds, button_ptr)
 | 
					                    (c::Point { x: 0.1, y: 1.2 }, button_ptr)
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let view = View {
 | 
					            let view = View {
 | 
				
			||||||
                bounds: c::Bounds {
 | 
					 | 
				
			||||||
                    x: 0f64, y: 0f64,
 | 
					 | 
				
			||||||
                    width: 0f64, height: 0f64
 | 
					 | 
				
			||||||
                },
 | 
					 | 
				
			||||||
                rows: Vec::new(),
 | 
					                rows: Vec::new(),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            assert_eq!(
 | 
					            assert_eq!(
 | 
				
			||||||
                find_key_paths(&view, &state_clone.clone()).is_empty(),
 | 
					                find_key_places(&view, &state_clone.clone()).is_empty(),
 | 
				
			||||||
                true
 | 
					                true
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    pub fn get_button_bounds(
 | 
					 | 
				
			||||||
        view: &View,
 | 
					 | 
				
			||||||
        row: &Row,
 | 
					 | 
				
			||||||
        button: &Button
 | 
					 | 
				
			||||||
    ) -> Option<c::Bounds> {
 | 
					 | 
				
			||||||
        match &row.bounds {
 | 
					 | 
				
			||||||
            Some(row) => Some(c::Bounds {
 | 
					 | 
				
			||||||
                x: view.bounds.x + row.x + button.bounds.x,
 | 
					 | 
				
			||||||
                y: view.bounds.y + row.y + button.bounds.y,
 | 
					 | 
				
			||||||
                width: button.bounds.width,
 | 
					 | 
				
			||||||
                height: button.bounds.height,
 | 
					 | 
				
			||||||
            }),
 | 
					 | 
				
			||||||
            _ => None,
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Top level procedures, dispatching to everything
 | 
					/// Top level procedures, dispatching to everything
 | 
				
			||||||
@ -944,19 +821,18 @@ mod seat {
 | 
				
			|||||||
        let view = layout.get_current_view();
 | 
					        let view = layout.get_current_view();
 | 
				
			||||||
        let action = RefCell::borrow(key).action.clone();
 | 
					        let action = RefCell::borrow(key).action.clone();
 | 
				
			||||||
        if let Action::ShowPreferences = action {
 | 
					        if let Action::ShowPreferences = action {
 | 
				
			||||||
            let paths = ::layout::procedures::find_key_paths(
 | 
					            let places = ::layout::procedures::find_key_places(
 | 
				
			||||||
                view, key
 | 
					                view, key
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            // getting first item will cause mispositioning
 | 
					            // getting first item will cause mispositioning
 | 
				
			||||||
            // with more than one button with the same key
 | 
					            // with more than one button with the same key
 | 
				
			||||||
            // on the keyboard
 | 
					            // on the keyboard
 | 
				
			||||||
            if let Some((row, button)) = paths.get(0) {
 | 
					            if let Some((offset, button)) = places.get(0) {
 | 
				
			||||||
                let bounds = ::layout::procedures::get_button_bounds(
 | 
					                let bounds = c::Bounds {
 | 
				
			||||||
                    view, row, button
 | 
					                    x: offset.x, y: offset.y,
 | 
				
			||||||
                ).unwrap_or_else(|| {
 | 
					                    width: button.size.width,
 | 
				
			||||||
                    eprintln!("BUG: Clicked button has no position?");
 | 
					                    height: button.size.height,
 | 
				
			||||||
                    c::Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
 | 
					                };
 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                ::popover::show(
 | 
					                ::popover::show(
 | 
				
			||||||
                    ui_keyboard,
 | 
					                    ui_keyboard,
 | 
				
			||||||
                    widget_to_layout.reverse_bounds(bounds)
 | 
					                    widget_to_layout.reverse_bounds(bounds)
 | 
				
			||||||
@ -987,12 +863,48 @@ mod test {
 | 
				
			|||||||
    ) -> Box<Button> {
 | 
					    ) -> Box<Button> {
 | 
				
			||||||
        Box::new(Button {
 | 
					        Box::new(Button {
 | 
				
			||||||
            name: CString::new(name.clone()).unwrap(),
 | 
					            name: CString::new(name.clone()).unwrap(),
 | 
				
			||||||
            bounds: c::Bounds {
 | 
					            size: Size { width: 0f64, height: 0f64 },
 | 
				
			||||||
                x: 0f64, y: 0f64, width: 0f64, height: 0f64
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            outline_name: CString::new("test").unwrap(),
 | 
					            outline_name: CString::new("test").unwrap(),
 | 
				
			||||||
            label: Label::Text(CString::new(name).unwrap()),
 | 
					            label: Label::Text(CString::new(name).unwrap()),
 | 
				
			||||||
            state: state,
 | 
					            state: state,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    #[test]
 | 
				
			||||||
 | 
					    fn check_centering() {
 | 
				
			||||||
 | 
					        //    foo
 | 
				
			||||||
 | 
					        // ---bar---
 | 
				
			||||||
 | 
					        let view = View::new(vec![
 | 
				
			||||||
 | 
					            (
 | 
				
			||||||
 | 
					                0.0,
 | 
				
			||||||
 | 
					                Row {
 | 
				
			||||||
 | 
					                    angle: 0,
 | 
				
			||||||
 | 
					                    buttons: vec![(
 | 
				
			||||||
 | 
					                        0.0,
 | 
				
			||||||
 | 
					                        Box::new(Button {
 | 
				
			||||||
 | 
					                            size: Size { width: 10.0, height: 10.0 },
 | 
				
			||||||
 | 
					                            ..*make_button_with_state("foo".into(), make_state())
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                    )]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            (
 | 
				
			||||||
 | 
					                10.0,
 | 
				
			||||||
 | 
					                Row {
 | 
				
			||||||
 | 
					                    angle: 0,
 | 
				
			||||||
 | 
					                    buttons: vec![(
 | 
				
			||||||
 | 
					                        0.0,
 | 
				
			||||||
 | 
					                        Box::new(Button {
 | 
				
			||||||
 | 
					                            size: Size { width: 30.0, height: 10.0 },
 | 
				
			||||||
 | 
					                            ..*make_button_with_state("bar".into(), make_state())
 | 
				
			||||||
 | 
					                        }),
 | 
				
			||||||
 | 
					                    )]
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					        assert!(
 | 
				
			||||||
 | 
					            view.find_button_by_position(c::Point { x: 5.0, y: 5.0 })
 | 
				
			||||||
 | 
					                .is_none()
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -56,8 +56,8 @@ fn check_layout(layout: Layout) {
 | 
				
			|||||||
    
 | 
					    
 | 
				
			||||||
    // "Press" each button with keysyms
 | 
					    // "Press" each button with keysyms
 | 
				
			||||||
    for view in layout.views.values() {
 | 
					    for view in layout.views.values() {
 | 
				
			||||||
        for row in &view.rows {
 | 
					        for (_y, row) in &view.get_rows() {
 | 
				
			||||||
            for button in &row.buttons {
 | 
					            for (_x, button) in &row.buttons {
 | 
				
			||||||
                let keystate = button.state.borrow();
 | 
					                let keystate = button.state.borrow();
 | 
				
			||||||
                for keycode in &keystate.keycodes {
 | 
					                for keycode in &keystate.keycodes {
 | 
				
			||||||
                    match state.key_get_one_sym(*keycode) {
 | 
					                    match state.key_get_one_sym(*keycode) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										12
									
								
								src/util.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/util.rs
									
									
									
									
									
								
							@ -2,6 +2,8 @@
 | 
				
			|||||||
use std::collections::HashMap;
 | 
					use std::collections::HashMap;
 | 
				
			||||||
use std::rc::Rc;
 | 
					use std::rc::Rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use ::float_ord::FloatOrd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use std::borrow::Borrow;
 | 
					use std::borrow::Borrow;
 | 
				
			||||||
use std::hash::{ Hash, Hasher };
 | 
					use std::hash::{ Hash, Hasher };
 | 
				
			||||||
use std::iter::FromIterator;
 | 
					use std::iter::FromIterator;
 | 
				
			||||||
@ -142,6 +144,16 @@ pub fn hash_map_map<K, V, F, K1, V1>(map: HashMap<K, V>, mut f: F)
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn find_max_double<T, I, F>(iterator: I, get: F)
 | 
				
			||||||
 | 
					    -> f64
 | 
				
			||||||
 | 
					    where I: Iterator<Item=T>,
 | 
				
			||||||
 | 
					        F: Fn(&T) -> f64
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    iterator.map(|value| FloatOrd(get(&value)))
 | 
				
			||||||
 | 
					        .max().unwrap_or(FloatOrd(0f64))
 | 
				
			||||||
 | 
					        .0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Compares pointers but not internal values of Rc
 | 
					/// Compares pointers but not internal values of Rc
 | 
				
			||||||
pub struct Pointer<T>(pub Rc<T>);
 | 
					pub struct Pointer<T>(pub Rc<T>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user