antoyo / relm

Idiomatic, GTK+-based, GUI library, inspired by Elm, written in Rust
MIT License
2.42k stars 79 forks source link

Drawing via DrawHandler prevents gtk::render_* functions from working #229

Open SolarLiner opened 4 years ago

SolarLiner commented 4 years ago

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(); } ```
antoyo commented 4 years ago

This drawing system is not ready yet, but I'll look at this issue when I finish this system.