renderer: Bring button drawing closer to Rust
This commit is contained in:
@ -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,
|
||||||
|
|||||||
113
src/drawing.rs
113
src/drawing.rs
@ -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,
|
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),
|
cairo::Context::to_raw_none(&cr),
|
||||||
button as *const Button,
|
*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,
|
||||||
|
button.name.as_ptr(),
|
||||||
|
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) {
|
||||||
|
|||||||
@ -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(
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user