renderer: Bring button drawing closer to Rust
This commit is contained in:
		
							
								
								
									
										115
									
								
								src/drawing.rs
									
									
									
									
									
								
							
							
						
						
									
										115
									
								
								src/drawing.rs
									
									
									
									
									
								
							@ -5,18 +5,21 @@ use std::cell::RefCell;
 | 
			
		||||
 | 
			
		||||
use ::action::Action;
 | 
			
		||||
use ::keyboard;
 | 
			
		||||
use ::layout::{ Button, Layout };
 | 
			
		||||
use ::layout::c::{ EekGtkKeyboard, Point };
 | 
			
		||||
use ::layout::{ Button, Label, Layout };
 | 
			
		||||
use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
 | 
			
		||||
use ::submission::Submission;
 | 
			
		||||
 | 
			
		||||
use glib::translate::FromGlibPtrNone;
 | 
			
		||||
use gtk::WidgetExt;
 | 
			
		||||
 | 
			
		||||
use std::ffi::CStr;
 | 
			
		||||
use std::ptr;
 | 
			
		||||
 | 
			
		||||
mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use cairo_sys;
 | 
			
		||||
    use std::os::raw::c_void;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    
 | 
			
		||||
    // This is constructed only in C, no need for warnings
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
@ -24,18 +27,45 @@ mod c {
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct EekRenderer(*const c_void);
 | 
			
		||||
 | 
			
		||||
    // This is constructed only in C, no need for warnings
 | 
			
		||||
    /// Just don't clone this for no reason.
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    #[repr(transparent)]
 | 
			
		||||
    #[derive(Clone, Copy)]
 | 
			
		||||
    pub struct GtkStyleContext(*const c_void);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    extern "C" {
 | 
			
		||||
        // Button and View inside CButtonPlace are safe to pass to C
 | 
			
		||||
        // as long as they don't outlive the call
 | 
			
		||||
        // and nothing dereferences them
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_render_button(
 | 
			
		||||
        pub fn eek_renderer_get_scale_factor(
 | 
			
		||||
            renderer: EekRenderer,
 | 
			
		||||
        ) -> u32;
 | 
			
		||||
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_render_button_in_context(
 | 
			
		||||
            scale_factor: u32,
 | 
			
		||||
            cr: *mut cairo_sys::cairo_t,
 | 
			
		||||
            button: *const Button,
 | 
			
		||||
            ctx: GtkStyleContext,
 | 
			
		||||
            bounds: Bounds,
 | 
			
		||||
            icon_name: *const c_char,
 | 
			
		||||
            label: *const c_char,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_get_style_context_for_button(
 | 
			
		||||
            renderer: EekRenderer,
 | 
			
		||||
            name: *const c_char,
 | 
			
		||||
            outline_name: *const c_char,
 | 
			
		||||
            pressed: u64,
 | 
			
		||||
            locked: u64,
 | 
			
		||||
        ) -> GtkStyleContext;
 | 
			
		||||
 | 
			
		||||
        #[allow(improper_ctypes)]
 | 
			
		||||
        pub fn eek_put_style_context_for_button(
 | 
			
		||||
            ctx: GtkStyleContext,
 | 
			
		||||
            outline_name: *const c_char,
 | 
			
		||||
            locked: u64,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -109,16 +139,75 @@ pub fn render_button_at_position(
 | 
			
		||||
        button.size.width, button.size.height
 | 
			
		||||
    );
 | 
			
		||||
    cr.clip();
 | 
			
		||||
    unsafe {
 | 
			
		||||
        c::eek_render_button(
 | 
			
		||||
 | 
			
		||||
    let scale_factor = unsafe {
 | 
			
		||||
        c::eek_renderer_get_scale_factor(renderer)
 | 
			
		||||
    };
 | 
			
		||||
    let bounds = button.get_bounds();
 | 
			
		||||
    let (label_c, icon_name_c) = match &button.label {
 | 
			
		||||
        Label::Text(text) => (text.as_ptr(), ptr::null()),
 | 
			
		||||
        Label::IconName(name) => {
 | 
			
		||||
            let l = unsafe {
 | 
			
		||||
                // CStr doesn't allocate anything, so it only points to
 | 
			
		||||
                // the 'static str, avoiding a memory leak
 | 
			
		||||
                CStr::from_bytes_with_nul_unchecked(b"icon\0")
 | 
			
		||||
            };
 | 
			
		||||
            (l.as_ptr(), name.as_ptr())
 | 
			
		||||
        },
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    with_button_context(
 | 
			
		||||
        renderer,
 | 
			
		||||
        button,
 | 
			
		||||
        pressed,
 | 
			
		||||
        locked,
 | 
			
		||||
        |ctx| unsafe {
 | 
			
		||||
            // TODO: split into separate procedures:
 | 
			
		||||
            // draw outline, draw label, draw icon.
 | 
			
		||||
            c::eek_render_button_in_context(
 | 
			
		||||
                scale_factor,
 | 
			
		||||
                cairo::Context::to_raw_none(&cr),
 | 
			
		||||
                *ctx,
 | 
			
		||||
                bounds,
 | 
			
		||||
                icon_name_c,
 | 
			
		||||
                label_c,
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    cr.restore();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn with_button_context<R, F: FnOnce(&c::GtkStyleContext) -> R>(
 | 
			
		||||
    renderer: c::EekRenderer,
 | 
			
		||||
    button: &Button,
 | 
			
		||||
    pressed: keyboard::PressType,
 | 
			
		||||
    locked: bool,
 | 
			
		||||
    operation: F,
 | 
			
		||||
) -> R {
 | 
			
		||||
    let outline_name_c = button.outline_name.as_ptr();
 | 
			
		||||
    
 | 
			
		||||
    let ctx = unsafe {
 | 
			
		||||
        c::eek_get_style_context_for_button(
 | 
			
		||||
            renderer,
 | 
			
		||||
            cairo::Context::to_raw_none(&cr),
 | 
			
		||||
            button as *const Button,
 | 
			
		||||
            button.name.as_ptr(),
 | 
			
		||||
            outline_name_c,
 | 
			
		||||
            pressed as u64,
 | 
			
		||||
            locked as u64,
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
    cr.restore();
 | 
			
		||||
    
 | 
			
		||||
    let r = operation(&ctx);
 | 
			
		||||
 | 
			
		||||
    unsafe {
 | 
			
		||||
        c::eek_put_style_context_for_button(
 | 
			
		||||
            ctx,
 | 
			
		||||
            outline_name_c,
 | 
			
		||||
            locked as u64,
 | 
			
		||||
        )
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn queue_redraw(keyboard: EekGtkKeyboard) {
 | 
			
		||||
 | 
			
		||||
@ -26,12 +26,6 @@ struct squeek_layout_state {
 | 
			
		||||
 | 
			
		||||
struct squeek_layout;
 | 
			
		||||
 | 
			
		||||
EekBounds squeek_button_get_bounds(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_label(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_icon_name(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_name(const struct squeek_button*);
 | 
			
		||||
const char *squeek_button_get_outline_name(const struct squeek_button*);
 | 
			
		||||
 | 
			
		||||
void squeek_button_print(const struct squeek_button* button);
 | 
			
		||||
 | 
			
		||||
struct transformation squeek_layout_calculate_transformation(
 | 
			
		||||
 | 
			
		||||
@ -41,9 +41,7 @@ pub mod c {
 | 
			
		||||
    use super::*;
 | 
			
		||||
 | 
			
		||||
    use gtk_sys;
 | 
			
		||||
    use std::ffi::CStr;
 | 
			
		||||
    use std::os::raw::{ c_char, c_void };
 | 
			
		||||
    use std::ptr;
 | 
			
		||||
 | 
			
		||||
    use std::ops::{ Add, Sub };
 | 
			
		||||
 | 
			
		||||
@ -162,57 +160,6 @@ pub mod c {
 | 
			
		||||
    pub struct LevelKeyboard(*const c_void);
 | 
			
		||||
 | 
			
		||||
    // The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_bounds(button: *const ::layout::Button) -> Bounds {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        Bounds {
 | 
			
		||||
            x: 0.0, y: 0.0,
 | 
			
		||||
            width: button.size.width, height: button.size.height
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_label(
 | 
			
		||||
        button: *const ::layout::Button
 | 
			
		||||
    ) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        match &button.label {
 | 
			
		||||
            Label::Text(text) => text.as_ptr(),
 | 
			
		||||
            // returning static strings to C is a bit cumbersome
 | 
			
		||||
            Label::IconName(_) => unsafe {
 | 
			
		||||
                // CStr doesn't allocate anything, so it only points to
 | 
			
		||||
                // the 'static str, avoiding a memory leak
 | 
			
		||||
                CStr::from_bytes_with_nul_unchecked(b"icon\0")
 | 
			
		||||
            }.as_ptr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_icon_name(button: *const Button) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        match &button.label {
 | 
			
		||||
            Label::Text(_) => ptr::null(),
 | 
			
		||||
            Label::IconName(name) => name.as_ptr(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_name(button: *const Button) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        button.name.as_ptr()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
    fn squeek_button_get_outline_name(button: *const Button) -> *const c_char {
 | 
			
		||||
        let button = unsafe { &*button };
 | 
			
		||||
        button.outline_name.as_ptr()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    #[no_mangle]
 | 
			
		||||
    pub extern "C"
 | 
			
		||||
@ -485,6 +432,15 @@ pub struct Button {
 | 
			
		||||
    pub state: Rc<RefCell<KeyState>>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Button {
 | 
			
		||||
    pub fn get_bounds(&self) -> c::Bounds {
 | 
			
		||||
        c::Bounds {
 | 
			
		||||
            x: 0.0, y: 0.0,
 | 
			
		||||
            width: self.size.width, height: self.size.height,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The graphical representation of a row of buttons
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct Row {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user