main.rs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. use anyhow::{anyhow, bail, Result};
  2. use gtk::prelude::WidgetExtManual;
  3. use gtk::{
  4. BoxExt, ButtonExt, ContainerExt, DialogExt, EditableSignals, EntryExt, FileChooserExt,
  5. GtkWindowExt, LabelExt, WidgetExt,
  6. };
  7. use std::cell::RefCell;
  8. use std::rc::Rc;
  9. mod dmc;
  10. struct AppData {
  11. symbol: char,
  12. dmc: Option<dmc::DMC>,
  13. }
  14. impl AppData {
  15. fn draw_to_file<P: AsRef<std::path::Path>>(&self, path: P) -> Result<()> {
  16. let f = std::fs::File::create(path)?;
  17. let surf = cairo::SvgSurface::for_stream(100.0, 100.0, f)?;
  18. self.render(&cairo::Context::new(&surf), 100.0, 100.0)?;
  19. if let Err(err) = surf.finish_output_stream() {
  20. bail!(err.error);
  21. };
  22. Ok(())
  23. }
  24. fn render(&self, ctx: &cairo::Context, w: f64, h: f64) -> Result<()> {
  25. let off_x = (w - 100.) / 2.0;
  26. let off_y = (h - 100.) / 2.0;
  27. let color = self.color();
  28. ctx.set_source_rgb(color.0, color.1, color.2);
  29. ctx.paint();
  30. ctx.set_source_rgb(0., 0., 0.);
  31. ctx.rectangle(off_x, off_y, 100., 100.);
  32. ctx.stroke();
  33. ctx.move_to(off_x, off_y - 23.0);
  34. let layout = pangocairo::functions::create_layout(&ctx)
  35. .ok_or_else(|| anyhow!("Could not create layout"))?;
  36. layout.set_width(100 * 1024);
  37. layout.set_alignment(pango::Alignment::Center);
  38. let font = pango::FontDescription::from_string("Fira Sans 92");
  39. layout.set_font_description(Some(&font));
  40. layout.set_text(&self.symbol.to_string());
  41. pangocairo::functions::show_layout(&ctx, &layout);
  42. Ok(())
  43. }
  44. fn color(&self) -> (f64, f64, f64) {
  45. self.dmc
  46. .map(|dmc| dmc.color)
  47. .unwrap_or_else(|| (0.5, 0.5, 0.5))
  48. }
  49. }
  50. #[derive(Clone)]
  51. struct App {
  52. data: Rc<RefCell<AppData>>,
  53. window: gtk::Window,
  54. }
  55. impl App {
  56. fn new(window: gtk::Window) -> App {
  57. let app_data = AppData {
  58. symbol: 'X',
  59. dmc: None,
  60. };
  61. let data = Rc::new(RefCell::new(app_data));
  62. App { data, window }
  63. }
  64. }
  65. fn mk_app() {
  66. let window = gtk::Window::new(gtk::WindowType::Toplevel);
  67. let app = App::new(window.clone());
  68. window.connect_delete_event(move |_, _| {
  69. gtk::main_quit();
  70. gtk::Inhibit(false)
  71. });
  72. window.set_title("I Am Legend");
  73. window.set_default_size(500, 200);
  74. window.set_resizable(false);
  75. let container = gtk::Box::new(gtk::Orientation::Vertical, 4);
  76. container.pack_start(&mk_icon_choice('A', app.clone()), false, true, 0);
  77. container.pack_start(&mk_icon_choice('B', app.clone()), false, true, 0);
  78. container.pack_start(&mk_icon_choice('C', app.clone()), false, true, 0);
  79. container.pack_start(&mk_icon_choice('D', app.clone()), false, true, 0);
  80. let flow = gtk::Box::new(gtk::Orientation::Horizontal, 2);
  81. flow.pack_start(&container, false, true, 0);
  82. let container = gtk::Box::new(gtk::Orientation::Vertical, 2);
  83. let label = gtk::Label::new(None);
  84. let button = gtk::Button::with_label("EXPORT IT");
  85. {
  86. let color = gtk::Entry::new();
  87. let app = app.clone();
  88. let window = window.clone();
  89. let label = label.clone();
  90. let button = button.clone();
  91. color.connect_changed(move |s| {
  92. let str = s.get_text();
  93. if let Some(color) = dmc::LOOKUP.get(str.as_str()) {
  94. app.data.borrow_mut().dmc = Some(*color.clone());
  95. label.set_text(color.name);
  96. button.set_sensitive(true);
  97. } else {
  98. app.data.borrow_mut().dmc = None;
  99. label.set_text("");
  100. button.set_sensitive(false);
  101. }
  102. window.queue_draw_area(0, 0, 500, 200);
  103. });
  104. container.pack_start(&color, false, true, 0);
  105. }
  106. {
  107. button.set_sensitive(false);
  108. let app = app.clone();
  109. let window = window.clone();
  110. button.connect_clicked(move |_| {
  111. let dialog = gtk::FileChooserDialog::with_buttons(
  112. Some("Select filename"),
  113. Some(&window),
  114. gtk::FileChooserAction::Save,
  115. &[
  116. ("_Cancel", gtk::ResponseType::Cancel),
  117. ("_Open", gtk::ResponseType::Ok),
  118. ],
  119. );
  120. let filter = gtk::FileFilter::new();
  121. filter.add_mime_type("image/svg+xml");
  122. filter.add_pattern("*.svg");
  123. filter.set_name(Some("SVG image"));
  124. dialog.add_filter(&filter);
  125. if dialog.run() == gtk::ResponseType::Ok {
  126. if let Some(tgt) = dialog.get_filename() {
  127. let mut tgt = tgt.to_owned();
  128. if !tgt.ends_with(".svg") {
  129. tgt.set_extension("svg");
  130. }
  131. println!("saving to {:?}", tgt);
  132. if let Err(err) = app.data.borrow_mut().draw_to_file(tgt) {
  133. eprintln!("Error in rendering: {}", err);
  134. }
  135. }
  136. }
  137. unsafe {
  138. dialog.destroy();
  139. }
  140. });
  141. container.pack_start(&button, false, true, 0);
  142. }
  143. container.pack_start(&label, false, true, 0);
  144. flow.pack_start(&container, false, true, 0);
  145. {
  146. let canvas = gtk::DrawingArea::new();
  147. canvas.connect_draw(move |cv, ctx| {
  148. let w = cv.get_allocated_width();
  149. let h = cv.get_allocated_height();
  150. if let Err(err) = app.data.borrow_mut().render(ctx, w as f64, h as f64) {
  151. eprintln!("Error in rendering: {}", err);
  152. }
  153. gtk::Inhibit(false)
  154. });
  155. flow.pack_start(&canvas, true, true, 0);
  156. }
  157. window.add(&flow);
  158. window.show_all();
  159. }
  160. fn mk_icon_choice(choice: char, cell: App) -> gtk::Button {
  161. let button = gtk::Button::with_label(&choice.to_string());
  162. button.connect_clicked(move |_| {
  163. cell.data.borrow_mut().symbol = choice;
  164. cell.window.queue_draw_area(0, 0, 500, 200);
  165. });
  166. button
  167. }
  168. fn main() {
  169. if gtk::init().is_err() {
  170. eprintln!("Failed to initialize GTK application");
  171. } else {
  172. mk_app();
  173. gtk::main();
  174. }
  175. }