Add a popover menu to switch languages
This commit is contained in:
		
							
								
								
									
										159
									
								
								src/layout.rs
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								src/layout.rs
									
									
									
									
									
								
							@ -37,6 +37,7 @@ pub mod c {
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
    use gtk_sys;
 | 
			
		||||
 | 
			
		||||
    // The following defined in C
 | 
			
		||||
 | 
			
		||||
@ -45,7 +46,7 @@ pub mod c {
 | 
			
		||||
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Copy, Clone)]
 | 
			
		||||
    pub struct EekGtkKeyboard(*const c_void);
 | 
			
		||||
    pub struct EekGtkKeyboard(pub *const gtk_sys::GtkWidget);
 | 
			
		||||
 | 
			
		||||
    /// Defined in eek-types.h
 | 
			
		||||
    #[repr(C)]
 | 
			
		||||
@ -245,7 +246,7 @@ pub mod c {
 | 
			
		||||
            origin_y: f64,
 | 
			
		||||
            scale: f64,
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        impl Transformation {
 | 
			
		||||
            fn forward(&self, p: Point) -> Point {
 | 
			
		||||
                Point {
 | 
			
		||||
@ -253,6 +254,25 @@ pub mod c {
 | 
			
		||||
                    y: (p.y - self.origin_y) / self.scale,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            fn reverse(&self, p: Point) -> Point {
 | 
			
		||||
                Point {
 | 
			
		||||
                    x: p.x * self.scale + self.origin_x,
 | 
			
		||||
                    y: p.y * self.scale + self.origin_y,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            pub fn reverse_bounds(&self, b: Bounds) -> Bounds {
 | 
			
		||||
                let start = self.reverse(Point { x: b.x, y: b.y });
 | 
			
		||||
                let end = self.reverse(Point {
 | 
			
		||||
                    x: b.x + b.width,
 | 
			
		||||
                    y: b.y + b.height,
 | 
			
		||||
                });
 | 
			
		||||
                Bounds {
 | 
			
		||||
                    x: start.x,
 | 
			
		||||
                    y: start.y,
 | 
			
		||||
                    width: end.x - start.x,
 | 
			
		||||
                    height: end.y - start.y,
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // This is constructed only in C, no need for warnings
 | 
			
		||||
@ -319,28 +339,30 @@ pub mod c {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// Release pointer in the specified position
 | 
			
		||||
        #[no_mangle]
 | 
			
		||||
        pub extern "C"
 | 
			
		||||
        fn squeek_layout_release(
 | 
			
		||||
            layout: *mut Layout,
 | 
			
		||||
            virtual_keyboard: ZwpVirtualKeyboardV1, // TODO: receive a reference to the backend
 | 
			
		||||
            widget_to_layout: Transformation,
 | 
			
		||||
            time: u32,
 | 
			
		||||
            ui_keyboard: EekGtkKeyboard,
 | 
			
		||||
        ) {
 | 
			
		||||
            let time = Timestamp(time);
 | 
			
		||||
            let layout = unsafe { &mut *layout };
 | 
			
		||||
            let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
 | 
			
		||||
            // The list must be copied,
 | 
			
		||||
            // because it will be mutated in the loop
 | 
			
		||||
            for key in layout.pressed_keys.clone() {
 | 
			
		||||
                let key: &Rc<RefCell<KeyState>> = key.borrow();
 | 
			
		||||
                layout.release_key(
 | 
			
		||||
                ui::release_key(
 | 
			
		||||
                    layout,
 | 
			
		||||
                    &virtual_keyboard,
 | 
			
		||||
                    &mut key.clone(),
 | 
			
		||||
                    Timestamp(time)
 | 
			
		||||
                );
 | 
			
		||||
                let view = layout.get_current_view();
 | 
			
		||||
                ::layout::procedures::release_ui_buttons(
 | 
			
		||||
                    &view, key, ui_keyboard,
 | 
			
		||||
                    &widget_to_layout,
 | 
			
		||||
                    time,
 | 
			
		||||
                    ui_keyboard,
 | 
			
		||||
                    key
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@ -418,6 +440,7 @@ pub mod c {
 | 
			
		||||
            time: u32,
 | 
			
		||||
            ui_keyboard: EekGtkKeyboard,
 | 
			
		||||
        ) {
 | 
			
		||||
            let time = Timestamp(time);
 | 
			
		||||
            let layout = unsafe { &mut *layout };
 | 
			
		||||
            let virtual_keyboard = VirtualKeyboard(virtual_keyboard);
 | 
			
		||||
 | 
			
		||||
@ -442,36 +465,30 @@ pub mod c {
 | 
			
		||||
                    if Rc::ptr_eq(&state, &wrapped_key.0) {
 | 
			
		||||
                        found = true;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        layout.release_key(
 | 
			
		||||
                        ui::release_key(
 | 
			
		||||
                            layout,
 | 
			
		||||
                            &virtual_keyboard,
 | 
			
		||||
                            &mut key.clone(), 
 | 
			
		||||
                            Timestamp(time),
 | 
			
		||||
                        );
 | 
			
		||||
                        let view = layout.get_current_view();
 | 
			
		||||
                        ::layout::procedures::release_ui_buttons(
 | 
			
		||||
                            &view, key, ui_keyboard,
 | 
			
		||||
                            &widget_to_layout,
 | 
			
		||||
                            time,
 | 
			
		||||
                            ui_keyboard,
 | 
			
		||||
                            key,
 | 
			
		||||
                        );
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if !found {
 | 
			
		||||
                    layout.press_key(
 | 
			
		||||
                        &virtual_keyboard,
 | 
			
		||||
                        &mut state,
 | 
			
		||||
                        Timestamp(time),
 | 
			
		||||
                    );
 | 
			
		||||
                    layout.press_key(&virtual_keyboard, &mut state, time);
 | 
			
		||||
                    unsafe { eek_gtk_on_button_pressed(c_place, ui_keyboard) };
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                for wrapped_key in pressed {
 | 
			
		||||
                    let key: &Rc<RefCell<KeyState>> = wrapped_key.borrow();
 | 
			
		||||
                    layout.release_key(
 | 
			
		||||
                    ui::release_key(
 | 
			
		||||
                        layout,
 | 
			
		||||
                        &virtual_keyboard,
 | 
			
		||||
                        &mut key.clone(), 
 | 
			
		||||
                        Timestamp(time),
 | 
			
		||||
                    );
 | 
			
		||||
                    let view = layout.get_current_view();
 | 
			
		||||
                    ::layout::procedures::release_ui_buttons(
 | 
			
		||||
                        &view, key, ui_keyboard,
 | 
			
		||||
                        &widget_to_layout,
 | 
			
		||||
                        time,
 | 
			
		||||
                        ui_keyboard,
 | 
			
		||||
                        key,
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
@ -503,6 +520,28 @@ pub mod c {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #[cfg(test)]
 | 
			
		||||
        mod test {
 | 
			
		||||
            use super::*;
 | 
			
		||||
            
 | 
			
		||||
            fn near(a: f64, b: f64) -> bool {
 | 
			
		||||
                (a - b).abs() < ((a + b) * 0.001f64).abs()
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn transform_back() {
 | 
			
		||||
                let transform = Transformation {
 | 
			
		||||
                    origin_x: 10f64,
 | 
			
		||||
                    origin_y: 11f64,
 | 
			
		||||
                    scale: 12f64,
 | 
			
		||||
                };
 | 
			
		||||
                let point = Point { x: 1f64, y: 1f64 };
 | 
			
		||||
                let transformed = transform.reverse(transform.forward(point.clone()));
 | 
			
		||||
                assert!(near(point.x, transformed.x));
 | 
			
		||||
                assert!(near(point.y, transformed.y));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -717,6 +756,12 @@ pub struct Layout {
 | 
			
		||||
    pub keymap_str: CString,
 | 
			
		||||
    // Changeable state
 | 
			
		||||
    // a Vec would be enough, but who cares, this will be small & fast enough
 | 
			
		||||
    // TODO: turn those into per-input point *_buttons to track dragging.
 | 
			
		||||
    // The renderer doesn't need the list of pressed keys any more,
 | 
			
		||||
    // because it needs to iterate
 | 
			
		||||
    // through all buttons of the current view anyway.
 | 
			
		||||
    // When the list tracks actual location,
 | 
			
		||||
    // it becomes possible to place popovers and other UI accurately.
 | 
			
		||||
    pub pressed_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
 | 
			
		||||
    pub locked_keys: HashSet<::util::Pointer<RefCell<KeyState>>>,
 | 
			
		||||
}
 | 
			
		||||
@ -949,6 +994,64 @@ mod procedures {
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    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 UI procedures
 | 
			
		||||
mod ui {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    // TODO: turn into release_button
 | 
			
		||||
    pub fn release_key(
 | 
			
		||||
        layout: &mut Layout,
 | 
			
		||||
        virtual_keyboard: &VirtualKeyboard,
 | 
			
		||||
        widget_to_layout: &c::procedures::Transformation,
 | 
			
		||||
        time: Timestamp,
 | 
			
		||||
        ui_keyboard: c::EekGtkKeyboard,
 | 
			
		||||
        key: &Rc<RefCell<KeyState>>,
 | 
			
		||||
    ) {
 | 
			
		||||
        layout.release_key(virtual_keyboard, &mut key.clone(), time);
 | 
			
		||||
 | 
			
		||||
        let view = layout.get_current_view();
 | 
			
		||||
        let action = RefCell::borrow(key).action.clone();
 | 
			
		||||
        if let Action::ShowPreferences = action {
 | 
			
		||||
            let paths = ::layout::procedures::find_key_paths(
 | 
			
		||||
                view, key
 | 
			
		||||
            );
 | 
			
		||||
            // getting first item will cause mispositioning
 | 
			
		||||
            // with more than one button with the same key
 | 
			
		||||
            // on the keyboard
 | 
			
		||||
            if let Some((row, button)) = paths.get(0) {
 | 
			
		||||
                let bounds = ::layout::procedures::get_button_bounds(
 | 
			
		||||
                    view, row, button
 | 
			
		||||
                ).unwrap_or_else(|| {
 | 
			
		||||
                    eprintln!("BUG: Clicked button has no position?");
 | 
			
		||||
                    c::Bounds { x: 0f64, y: 0f64, width: 0f64, height: 0f64 }
 | 
			
		||||
                });
 | 
			
		||||
                ::popover::show(
 | 
			
		||||
                    ui_keyboard,
 | 
			
		||||
                    widget_to_layout.reverse_bounds(bounds)
 | 
			
		||||
                );
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        procedures::release_ui_buttons(view, key, ui_keyboard);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user