renderer: Bring button drawing closer to Rust

This commit is contained in:
Dorota Czaplejewicz
2020-12-04 14:07:02 +00:00
parent 8f62520648
commit 9522d4e302
4 changed files with 138 additions and 91 deletions

View File

@ -18,8 +18,6 @@
* 02110-1301 USA * 02110-1301 USA
*/ */
#include "config.h"
#include <math.h> #include <math.h>
#include <string.h> #include <string.h>
#include <gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf/gdk-pixbuf.h>
@ -60,21 +58,21 @@ render_outline (cairo_t *cr,
position.x, position.y, position.width, position.height); position.x, position.y, position.width, position.height);
} }
static void render_button_in_context(gint scale_factor, /// Rust interface
void eek_render_button_in_context(uint32_t scale_factor,
cairo_t *cr, cairo_t *cr,
GtkStyleContext *ctx, GtkStyleContext *ctx,
const struct squeek_button *button) { EekBounds bounds,
const char *icon_name,
const gchar *label) {
/* blank background */ /* blank background */
cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0);
cairo_paint (cr); cairo_paint (cr);
EekBounds bounds = squeek_button_get_bounds(button);
render_outline (cr, ctx, bounds); render_outline (cr, ctx, bounds);
cairo_paint (cr); cairo_paint (cr);
/* render icon (if any) */ /* render icon (if any) */
const char *icon_name = squeek_button_get_icon_name(button);
if (icon_name) { if (icon_name) {
cairo_surface_t *icon_surface = cairo_surface_t *icon_surface =
eek_renderer_get_icon_surface (icon_name, 16, scale_factor); eek_renderer_get_icon_surface (icon_name, 16, scale_factor);
@ -104,25 +102,27 @@ static void render_button_in_context(gint scale_factor,
} }
} }
const gchar *label = squeek_button_get_label(button);
if (label) { if (label) {
render_button_label (cr, ctx, label, squeek_button_get_bounds(button)); render_button_label (cr, ctx, label, bounds);
} }
} }
void /// Prepare context for drawing the button.
eek_render_button (EekRenderer *self, /// The context MUST be released using the corresponing "put" procedure
cairo_t *cr, /// before drawing the next button.
const struct squeek_button *button, /// Interface for Rust.
gboolean pressed, GtkStyleContext *
gboolean locked) eek_get_style_context_for_button (EekRenderer *self,
const char *name,
const char *outline_name,
uint64_t pressed,
uint64_t locked)
{ {
GtkStyleContext *ctx = self->button_context; GtkStyleContext *ctx = self->button_context;
/* Set the name of the button on the widget path, using the name obtained /* Set the name of the button on the widget path, using the name obtained
from the button's symbol. */ from the button's symbol. */
g_autoptr (GtkWidgetPath) path = NULL; g_autoptr (GtkWidgetPath) path = NULL;
path = gtk_widget_path_copy (gtk_style_context_get_path (ctx)); path = gtk_widget_path_copy (gtk_style_context_get_path (ctx));
const char *name = squeek_button_get_name(button);
gtk_widget_path_iter_set_name (path, -1, name); gtk_widget_path_iter_set_name (path, -1, name);
/* Update the style context with the updated widget path. */ /* Update the style context with the updated widget path. */
@ -131,14 +131,17 @@ eek_render_button (EekRenderer *self,
(pressed) or normal. */ (pressed) or normal. */
gtk_style_context_set_state(ctx, gtk_style_context_set_state(ctx,
pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL); pressed ? GTK_STATE_FLAG_ACTIVE : GTK_STATE_FLAG_NORMAL);
const char *outline_name = squeek_button_get_outline_name(button);
if (locked) { if (locked) {
gtk_style_context_add_class(ctx, "locked"); gtk_style_context_add_class(ctx, "locked");
} }
gtk_style_context_add_class(ctx, outline_name); gtk_style_context_add_class(ctx, outline_name);
return ctx;
}
render_button_in_context(self->scale_factor, cr, ctx, button); /// Interface for Rust.
void eek_put_style_context_for_button(GtkStyleContext *ctx,
const char *outline_name,
uint64_t locked) {
// Save and restore functions don't work if gtk_render_* was used in between // Save and restore functions don't work if gtk_render_* was used in between
gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL); gtk_style_context_set_state(ctx, GTK_STATE_FLAG_NORMAL);
gtk_style_context_remove_class(ctx, outline_name); gtk_style_context_remove_class(ctx, outline_name);
@ -333,6 +336,11 @@ eek_renderer_set_scale_factor (EekRenderer *renderer, gint scale)
renderer->scale_factor = scale; renderer->scale_factor = scale;
} }
/// Rust interface.
uint32_t eek_renderer_get_scale_factor(EekRenderer *renderer) {
return renderer->scale_factor;
}
cairo_surface_t * cairo_surface_t *
eek_renderer_get_icon_surface (const gchar *icon_name, eek_renderer_get_icon_surface (const gchar *icon_name,
gint size, gint size,

View File

@ -5,18 +5,21 @@ use std::cell::RefCell;
use ::action::Action; use ::action::Action;
use ::keyboard; use ::keyboard;
use ::layout::{ Button, Layout }; use ::layout::{ Button, Label, Layout };
use ::layout::c::{ EekGtkKeyboard, Point }; use ::layout::c::{ Bounds, EekGtkKeyboard, Point };
use ::submission::Submission; use ::submission::Submission;
use glib::translate::FromGlibPtrNone; use glib::translate::FromGlibPtrNone;
use gtk::WidgetExt; use gtk::WidgetExt;
use std::ffi::CStr;
use std::ptr;
mod c { mod c {
use super::*; use super::*;
use cairo_sys; 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 // This is constructed only in C, no need for warnings
#[allow(dead_code)] #[allow(dead_code)]
@ -24,18 +27,45 @@ mod c {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub struct EekRenderer(*const c_void); 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] #[no_mangle]
extern "C" { 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)] #[allow(improper_ctypes)]
pub fn eek_render_button( pub fn eek_renderer_get_scale_factor(
renderer: EekRenderer, renderer: EekRenderer,
) -> u32;
#[allow(improper_ctypes)]
pub fn eek_render_button_in_context(
scale_factor: u32,
cr: *mut cairo_sys::cairo_t, 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, pressed: u64,
locked: 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 button.size.width, button.size.height
); );
cr.clip(); 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, renderer,
cairo::Context::to_raw_none(&cr), button.name.as_ptr(),
button as *const Button, outline_name_c,
pressed as u64, pressed as u64,
locked 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) { pub fn queue_redraw(keyboard: EekGtkKeyboard) {

View File

@ -26,12 +26,6 @@ struct squeek_layout_state {
struct squeek_layout; 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); void squeek_button_print(const struct squeek_button* button);
struct transformation squeek_layout_calculate_transformation( struct transformation squeek_layout_calculate_transformation(

View File

@ -41,9 +41,7 @@ pub mod c {
use super::*; use super::*;
use gtk_sys; use gtk_sys;
use std::ffi::CStr;
use std::os::raw::{ c_char, c_void }; use std::os::raw::{ c_char, c_void };
use std::ptr;
use std::ops::{ Add, Sub }; use std::ops::{ Add, Sub };
@ -163,57 +161,6 @@ pub mod c {
// The following defined in Rust. TODO: wrap naked pointers to Rust data inside RefCells to prevent multiple writers // 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] #[no_mangle]
pub extern "C" pub extern "C"
fn squeek_button_print(button: *const ::layout::Button) { fn squeek_button_print(button: *const ::layout::Button) {
@ -485,6 +432,15 @@ pub struct Button {
pub state: Rc<RefCell<KeyState>>, 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 /// The graphical representation of a row of buttons
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Row { pub struct Row {