use anyhow::{anyhow, bail, Result}; use gio::prelude::{ApplicationExt, ApplicationExtManual}; use gtk::{ BoxExt, ButtonExt, ContainerExt, EditableSignals, EntryExt, GtkWindowExt, LabelExt, WidgetExt, }; use std::cell::RefCell; use std::rc::Rc; mod dmc; struct AppData { symbol: char, dmc: Option, } impl AppData { fn draw_to_file(&self) -> Result<()> { let f = std::fs::File::create("samp.svg")?; let surf = cairo::SvgSurface::for_stream(100.0, 100.0, f)?; self.render(&cairo::Context::new(&surf))?; if let Err(err) = surf.finish_output_stream() { bail!(err.error); }; Ok(()) } fn render(&self, ctx: &cairo::Context) -> Result<()> { let color = self.color(); ctx.set_source_rgb(color.0, color.1, color.2); ctx.paint(); ctx.set_source_rgb(0., 0., 0.); ctx.move_to(0.0, -23.0); let layout = pangocairo::functions::create_layout(&ctx) .ok_or_else(|| anyhow!("Could not create layout"))?; layout.set_width(100 * 1024); layout.set_alignment(pango::Alignment::Center); let font = pango::FontDescription::from_string("Fira Sans 92"); layout.set_font_description(Some(&font)); layout.set_text(&self.symbol.to_string()); pangocairo::functions::show_layout(&ctx, &layout); Ok(()) } fn color(&self) -> (f64, f64, f64) { self.dmc .map(|dmc| dmc.color) .unwrap_or_else(|| (0.5, 0.5, 0.5)) } } #[derive(Clone)] struct App { data: Rc>, } impl App { fn new() -> App { let app_data = AppData { symbol: 'X', dmc: None, }; let data = Rc::new(RefCell::new(app_data)); App { data } } } fn mk_app() { let app = App::new(); let window = gtk::Window::new(gtk::WindowType::Toplevel); window.connect_delete_event(move |_, _| { gtk::main_quit(); gtk::Inhibit(false) }); window.set_title("I Am Legend"); window.set_default_size(500, 100); window.set_resizable(false); let container = gtk::Box::new(gtk::Orientation::Vertical, 4); container.pack_start(&mk_icon_choice('A', app.clone()), false, true, 0); container.pack_start(&mk_icon_choice('B', app.clone()), false, true, 0); container.pack_start(&mk_icon_choice('C', app.clone()), false, true, 0); container.pack_start(&mk_icon_choice('D', app.clone()), false, true, 0); let flow = gtk::Box::new(gtk::Orientation::Horizontal, 2); flow.pack_start(&container, false, true, 0); let container = gtk::Box::new(gtk::Orientation::Vertical, 2); let label = gtk::Label::new(None); { let color = gtk::Entry::new(); let app = app.clone(); let window = window.clone(); let label = label.clone(); color.connect_changed(move |s| { let str = s.get_text(); if let Some(color) = dmc::LOOKUP.get(str.as_str()) { app.data.borrow_mut().dmc = Some(*color.clone()); label.set_text(color.name); } else { app.data.borrow_mut().dmc = None; label.set_text(""); } window.queue_draw_area(0, 0, 500, 100); }); container.pack_start(&color, false, true, 0); } { let button = gtk::Button::with_label("EXPORT IT"); let app = app.clone(); button.connect_clicked(move |_| { if let Err(err) = app.data.borrow_mut().draw_to_file() { eprintln!("Error in rendering: {}", err); } }); container.pack_start(&button, false, true, 0); } container.pack_start(&label, false, true, 0); flow.pack_start(&container, false, true, 0); { let canvas = gtk::DrawingArea::new(); canvas.connect_draw(move |_cv, ctx| { if let Err(err) = app.data.borrow_mut().render(ctx) { eprintln!("Error in rendering: {}", err); } gtk::Inhibit(false) }); flow.pack_start(&canvas, true, true, 0); } window.add(&flow); window.show_all(); } fn mk_icon_choice(choice: char, cell: App) -> gtk::Button { let button = gtk::Button::with_label(&choice.to_string()); button.connect_clicked(move |_| { cell.data.borrow_mut().symbol = choice; println!("Choosing '{}'", choice); }); button } fn main() { if gtk::init().is_err() { eprintln!("Failed to initialize GTK application"); } else { mk_app(); gtk::main(); } }