Using a DrawHandler<_> to perform drawing "the relm way" makes the gtk::render_* functions useless, as they do not draw anything. Furthermore, the StyleContext struct returned by cairo::Context::get_style_context() seems zeroed as it only returns transparent black colors for get_*_color.
For example, gtk::render_background renders the background with respect for the user's GTK theme, which works fine with regular GTK and hooking manually into connnect_draw, does not draw anything when used with relm, which results in the drawing area not being cleared at the beginning of each draw.
gtk-rs example code (works)
```rust
use gio::prelude::*;
use gtk::{prelude::*, StateFlags, Window, WindowType};
use std::ops::Deref;
use std::{rc::Rc, time::Instant};
fn main() {
gtk::init().unwrap();
let start = Instant::now();
let window = Window::new(WindowType::Toplevel);
window.set_title("GTK DrawingArea test");
let draw_area = gtk::DrawingArea::new();
let draw_area = Rc::from(draw_area);
draw_area.connect_draw({
move |da, cr| {
let alloc = da.get_allocation();
let width = alloc.width as f64;
let height = alloc.height as f64;
let style = da.get_style_context();
gtk::render_background(&style, cr, 0.0, 0.0, width, height);
cr.set_source_rgb(1.0, 0.5, 0.1);
let t = Instant::now() - start;
let x = t.as_secs_f64().sin().abs() * width;
cr.rectangle(x, 0.0, 10., 10.);
cr.fill();
let col = style.get_color(StateFlags::NORMAL);
cr.set_source_rgb(col.red, col.green, col.blue);
let text = format!("Elapsed: {}.{} s", t.as_secs(), t.subsec_millis());
cr.move_to(15., 10.);
cr.show_text(&text);
gtk::Inhibit(false)
}
});
gtk::timeout_add(16, {
let draw_area = draw_area.clone();
move || {
draw_area.queue_draw();
Continue(true)
}
});
window.connect_destroy_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
window.add(draw_area.deref());
window.show_all();
gtk::main();
}
```
relm code (doesn't work)
```rust
fn main() {
use relm::{interval, DrawHandler, Update, Widget};
use relm_derive::Msg;
#[derive(Msg)]
enum Messages {
Redraw,
}
struct App {
win: gtk::Window,
draw_area: gtk::DrawingArea,
draw_handler: DrawHandler,
start: Instant,
}
impl Update for App {
type Model = Instant;
type ModelParam = ();
type Msg = Messages;
fn model(relm: &Relm, param: Self::ModelParam) -> Self::Model {
Instant::now()
}
fn subscriptions(&mut self, _relm: &Relm) {
interval(_relm.stream(), 16, || Messages::Redraw);
}
fn update(&mut self, event: Self::Msg) {
match event {
Messages::Redraw => {
let ctx = self.draw_handler.get_context();
let style = self.draw_area.get_style_context();
let alloc = self.draw_area.get_allocation();
let width = alloc.width as f64;
let height = alloc.height as f64;
gtk::render_background(&style, &ctx, 0.0, 0.0, width, height);
ctx.set_source_rgb(1.0, 0.5, 0.1);
let t = Instant::now() - self.start;
let x = t.as_secs_f64().sin().abs() * width;
ctx.rectangle(x, 0.0, 10., 10.);
ctx.fill();
let col = style.get_color(StateFlags::NORMAL);
ctx.set_source_rgb(col.red, col.green, col.blue);
let text = format!("Elapsed: {}.{} s", t.as_secs(), t.subsec_millis());
ctx.move_to(15., 10.);
ctx.show_text(&text);
}
}
}
}
impl Widget for App {
type Root = gtk::Window;
fn init_view(&mut self) {
self.win.show_all();
self.draw_handler.init(&self.draw_area);
}
fn root(&self) -> Self::Root {
self.win.clone()
}
fn view(relm: &Relm, start: Self::Model) -> Self {
let draw_area = gtk::DrawingArea::new();
let win = gtk::Window::new(gtk::WindowType::Toplevel);
win.add(&draw_area);
Self {
win,
draw_area,
draw_handler: DrawHandler::new().unwrap(),
start,
}
}
}
App::run(()).unwrap();
}
```
Workaround for relm
```rust
fn main() {
use relm::{interval, DrawHandler, Update, Widget};
use relm_derive::Msg;
#[derive(Msg)]
enum Messages {
Redraw,
}
struct App {
win: gtk::Window,
draw_area: gtk::DrawingArea,
//draw_handler: DrawHandler,
start: Instant,
}
impl Update for App {
type Model = Instant;
type ModelParam = ();
type Msg = Messages;
fn model(relm: &Relm, param: Self::ModelParam) -> Self::Model {
Instant::now()
}
fn subscriptions(&mut self, _relm: &Relm) {
interval(_relm.stream(), 16, || Messages::Redraw);
}
fn update(&mut self, event: Self::Msg) {
match event {
Messages::Redraw => {
self.draw_area.queue_draw();
/*let ctx = self.draw_handler.get_context();
let style = self.draw_area.get_style_context();
let alloc = self.draw_area.get_allocation();
let width = alloc.width as f64;
let height = alloc.height as f64;
gtk::render_background(&style, &ctx, 0.0, 0.0, width, height);
ctx.set_source_rgb(1.0, 0.5, 0.1);
let t = Instant::now() - self.start;
let x = t.as_secs_f64().sin().abs() * width;
ctx.rectangle(x, 0.0, 10., 10.);
ctx.fill();
let col = style.get_color(StateFlags::NORMAL);
ctx.set_source_rgb(col.red, col.green, col.blue);
let text = format!("Elapsed: {}.{} s", t.as_secs(), t.subsec_millis());
ctx.move_to(15., 10.);
ctx.show_text(&text);*/
}
}
}
}
impl Widget for App {
type Root = gtk::Window;
fn init_view(&mut self) {
self.win.show_all();
let start = self.start.clone();
//self.draw_handler.init(&self.draw_area);
self.draw_area.connect_draw(move |da, cr| {
let alloc = da.get_allocation();
let width = alloc.width as f64;
let height = alloc.height as f64;
let style = da.get_style_context();
gtk::render_background(&style, cr, 0.0, 0.0, width, height);
cr.set_source_rgb(1.0, 0.5, 0.1);
let t = Instant::now() - start;
let x = t.as_secs_f64().sin().abs() * width;
cr.rectangle(x, 0.0, 10., 10.);
cr.fill();
let col = style.get_color(StateFlags::NORMAL);
cr.set_source_rgb(col.red, col.green, col.blue);
let text = format!("Elapsed: {}.{} s", t.as_secs(), t.subsec_millis());
cr.move_to(15., 10.);
cr.show_text(&text);
gtk::Inhibit(false)
});
}
fn root(&self) -> Self::Root {
self.win.clone()
}
fn view(relm: &Relm, start: Self::Model) -> Self {
let draw_area = gtk::DrawingArea::new();
let win = gtk::Window::new(gtk::WindowType::Toplevel);
win.add(&draw_area);
Self {
win,
draw_area,
//draw_handler: DrawHandler::new().unwrap(),
start,
}
}
}
App::run(()).unwrap();
}
```
Using a
DrawHandler<_>
to perform drawing "the relm way" makes thegtk::render_*
functions useless, as they do not draw anything. Furthermore, theStyleContext
struct returned bycairo::Context::get_style_context()
seems zeroed as it only returns transparent black colors forget_*_color
.For example,
gtk::render_background
renders the background with respect for the user's GTK theme, which works fine with regular GTK and hooking manually intoconnnect_draw
, does not draw anything when used with relm, which results in the drawing area not being cleared at the beginning of each draw.gtk-rs example code (works)
```rust use gio::prelude::*; use gtk::{prelude::*, StateFlags, Window, WindowType}; use std::ops::Deref; use std::{rc::Rc, time::Instant}; fn main() { gtk::init().unwrap(); let start = Instant::now(); let window = Window::new(WindowType::Toplevel); window.set_title("GTK DrawingArea test"); let draw_area = gtk::DrawingArea::new(); let draw_area = Rc::from(draw_area); draw_area.connect_draw({ move |da, cr| { let alloc = da.get_allocation(); let width = alloc.width as f64; let height = alloc.height as f64; let style = da.get_style_context(); gtk::render_background(&style, cr, 0.0, 0.0, width, height); cr.set_source_rgb(1.0, 0.5, 0.1); let t = Instant::now() - start; let x = t.as_secs_f64().sin().abs() * width; cr.rectangle(x, 0.0, 10., 10.); cr.fill(); let col = style.get_color(StateFlags::NORMAL); cr.set_source_rgb(col.red, col.green, col.blue); let text = format!("Elapsed: {}.{} s", t.as_secs(), t.subsec_millis()); cr.move_to(15., 10.); cr.show_text(&text); gtk::Inhibit(false) } }); gtk::timeout_add(16, { let draw_area = draw_area.clone(); move || { draw_area.queue_draw(); Continue(true) } }); window.connect_destroy_event(|_, _| { gtk::main_quit(); Inhibit(true) }); window.add(draw_area.deref()); window.show_all(); gtk::main(); } ```relm code (doesn't work)
```rust fn main() { use relm::{interval, DrawHandler, Update, Widget}; use relm_derive::Msg; #[derive(Msg)] enum Messages { Redraw, } struct App { win: gtk::Window, draw_area: gtk::DrawingArea, draw_handler: DrawHandlerWorkaround for relm
```rust fn main() { use relm::{interval, DrawHandler, Update, Widget}; use relm_derive::Msg; #[derive(Msg)] enum Messages { Redraw, } struct App { win: gtk::Window, draw_area: gtk::DrawingArea, //draw_handler: DrawHandler