123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- use anyhow::{anyhow, bail, Result};
- use gtk::prelude::WidgetExtManual;
- use gtk::{
- BoxExt, ButtonExt, ContainerExt, DialogExt, EditableSignals, EntryExt, FileChooserExt,
- GtkWindowExt, LabelExt, WidgetExt,
- };
- use std::cell::RefCell;
- use std::rc::Rc;
- mod dmc;
- struct AppData {
- symbol: char,
- dmc: Option<dmc::DMC>,
- }
- impl AppData {
- fn draw_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
- let f = std::fs::File::create(path)?;
- let surf = cairo::SvgSurface::for_stream(100.0, 100.0, f)?;
- self.render(&cairo::Context::new(&surf), 100.0, 100.0)?;
- if let Err(err) = surf.finish_output_stream() {
- bail!(err.error);
- };
- Ok(())
- }
- fn render(&self, ctx: &cairo::Context, w: f64, h: f64) -> Result<()> {
- let off_x = (w - 100.) / 2.0;
- let off_y = (h - 100.) / 2.0;
- let color = self.color();
- ctx.set_source_rgb(color.0, color.1, color.2);
- ctx.paint();
- ctx.set_source_rgb(0., 0., 0.);
- ctx.rectangle(off_x, off_y, 100., 100.);
- ctx.stroke();
- ctx.move_to(off_x, off_y - 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<RefCell<AppData>>,
- window: gtk::Window,
- }
- impl App {
- fn new(window: gtk::Window) -> App {
- let app_data = AppData {
- symbol: 'X',
- dmc: None,
- };
- let data = Rc::new(RefCell::new(app_data));
- App { data, window }
- }
- }
- fn mk_app() {
- let window = gtk::Window::new(gtk::WindowType::Toplevel);
- let app = App::new(window.clone());
- window.connect_delete_event(move |_, _| {
- gtk::main_quit();
- gtk::Inhibit(false)
- });
- window.set_title("I Am Legend");
- window.set_default_size(500, 200);
- 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 button = gtk::Button::with_label("EXPORT IT");
- {
- let color = gtk::Entry::new();
- let app = app.clone();
- let window = window.clone();
- let label = label.clone();
- let button = button.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);
- button.set_sensitive(true);
- } else {
- app.data.borrow_mut().dmc = None;
- label.set_text("");
- button.set_sensitive(false);
- }
- window.queue_draw_area(0, 0, 500, 200);
- });
- container.pack_start(&color, false, true, 0);
- }
- {
- button.set_sensitive(false);
- let app = app.clone();
- let window = window.clone();
- button.connect_clicked(move |_| {
- let dialog = gtk::FileChooserDialog::with_buttons(
- Some("Select filename"),
- Some(&window),
- gtk::FileChooserAction::Save,
- &[
- ("_Cancel", gtk::ResponseType::Cancel),
- ("_Open", gtk::ResponseType::Ok),
- ],
- );
- let filter = gtk::FileFilter::new();
- filter.add_mime_type("image/svg+xml");
- filter.add_pattern("*.svg");
- filter.set_name(Some("SVG image"));
- dialog.add_filter(&filter);
- if dialog.run() == gtk::ResponseType::Ok {
- if let Some(tgt) = dialog.get_filename() {
- let mut tgt = tgt.to_owned();
- if !tgt.ends_with(".svg") {
- tgt.set_extension("svg");
- }
- println!("saving to {:?}", tgt);
- if let Err(err) = app.data.borrow_mut().draw_to_file(tgt) {
- eprintln!("Error in rendering: {}", err);
- }
- }
- }
- unsafe {
- dialog.destroy();
- }
- });
- 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| {
- let w = cv.get_allocated_width();
- let h = cv.get_allocated_height();
- if let Err(err) = app.data.borrow_mut().render(ctx, w as f64, h as f64) {
- 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;
- cell.window.queue_draw_area(0, 0, 500, 200);
- });
- button
- }
- fn main() {
- if gtk::init().is_err() {
- eprintln!("Failed to initialize GTK application");
- } else {
- mk_app();
- gtk::main();
- }
- }
|