view.rs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. use gtk::prelude::WidgetExtManual;
  2. use gtk::{
  3. BoxExt, ButtonExt, ContainerExt, DialogExt, EditableSignals, EntryExt, FileChooserExt,
  4. GtkWindowExt, LabelExt, WidgetExt,
  5. };
  6. use std::cell::RefCell;
  7. use std::collections::HashMap;
  8. use std::rc::Rc;
  9. use crate::{app, dmc};
  10. const SYMBOLS: &[char] = &['A', 'B', 'C', 'D'];
  11. #[derive(Clone)]
  12. struct App {
  13. data: Rc<RefCell<app::Data>>,
  14. }
  15. impl App {
  16. fn new() -> App {
  17. let app_data = app::Data {
  18. symbol: 'X',
  19. dmc: None,
  20. };
  21. let data = Rc::new(RefCell::new(app_data));
  22. App { data }
  23. }
  24. }
  25. #[derive(Clone)]
  26. pub struct View {
  27. app: App,
  28. window: gtk::Window,
  29. icon_buttons: HashMap<char, gtk::Button>,
  30. color_input: gtk::Entry,
  31. color_name: gtk::Label,
  32. submit: gtk::Button,
  33. canvas: gtk::DrawingArea,
  34. }
  35. impl View {
  36. /// Setup and run the program
  37. pub fn run() {
  38. View::create().setup().show()
  39. }
  40. /// Create and arrange the widgets that will be used for drawing
  41. /// the UI
  42. fn create() -> View {
  43. let window = gtk::Window::new(gtk::WindowType::Toplevel);
  44. let app = App::new();
  45. // left pane: the icon choices
  46. let left_pane = gtk::Box::new(gtk::Orientation::Vertical, 4);
  47. // HKHK
  48. let mut icon_buttons = HashMap::new();
  49. for ch in SYMBOLS {
  50. let button = gtk::Button::with_label(&ch.to_string());
  51. left_pane.pack_start(&button, false, true, 0);
  52. icon_buttons.insert(*ch, button);
  53. }
  54. // center pane
  55. let center_pane = gtk::Box::new(gtk::Orientation::Vertical, 2);
  56. let color_input = gtk::Entry::new();
  57. let color_name = gtk::Label::new(None);
  58. let submit = gtk::Button::with_label("EXPORT IT");
  59. center_pane.pack_start(&color_input, false, true, 0);
  60. center_pane.pack_start(&color_name, false, true, 0);
  61. center_pane.pack_start(&submit, false, true, 0);
  62. let canvas = gtk::DrawingArea::new();
  63. // the overall container
  64. let flow = gtk::Box::new(gtk::Orientation::Horizontal, 2);
  65. flow.pack_start(&left_pane, false, true, 0);
  66. flow.pack_start(&center_pane, false, true, 0);
  67. flow.pack_start(&canvas, true, true, 0);
  68. window.add(&flow);
  69. View {
  70. app,
  71. window,
  72. icon_buttons,
  73. color_input,
  74. color_name,
  75. submit,
  76. canvas,
  77. }
  78. }
  79. /// Bind callbacks and set up default configuration values for the
  80. /// provided widgets
  81. fn setup(&self) -> &Self {
  82. // setup window properties and callbacks
  83. self.window.connect_delete_event(move |_, _| {
  84. gtk::main_quit();
  85. gtk::Inhibit(false)
  86. });
  87. self.window.set_title("I Am Legend");
  88. self.window.set_default_size(500, 200);
  89. self.window.set_resizable(false);
  90. // setup all the icon button callbacks
  91. for (ch, button) in self.icon_buttons.iter() {
  92. self.clone().icon_button_clicked(*ch, button);
  93. }
  94. self.clone().color_entry_changed(&self.color_input);
  95. self.clone().submit_clicked(&self.submit);
  96. self.clone().canvas_draw(&self.canvas);
  97. self
  98. }
  99. /// Setup the callback for an icon-chooser button
  100. fn icon_button_clicked(self, ch: char, button: &gtk::Button) {
  101. button.connect_clicked(move |_| {
  102. self.app.data.borrow_mut().symbol = ch;
  103. self.window.queue_draw_area(0, 0, 500, 200);
  104. });
  105. }
  106. /// Setup the callback that fires when the text changes for the
  107. /// DMC thread input box
  108. fn color_entry_changed(self, entry: &gtk::Entry) {
  109. entry.connect_changed(move |s| {
  110. let str = s.get_text();
  111. if let Some(color) = dmc::LOOKUP.get(str.as_str()) {
  112. self.app.data.borrow_mut().dmc = Some(*color.clone());
  113. self.color_name.set_text(color.name);
  114. self.submit.set_sensitive(true);
  115. } else {
  116. self.app.data.borrow_mut().dmc = None;
  117. self.color_name.set_text("");
  118. self.submit.set_sensitive(false);
  119. }
  120. self.window.queue_draw_area(0, 0, 500, 200);
  121. });
  122. }
  123. /// Setup the callback that fires when the 'EXPORT IT' button is
  124. /// clicked
  125. fn submit_clicked(self, button: &gtk::Button) {
  126. button.connect_clicked(move |_| {
  127. let dialog = gtk::FileChooserDialog::with_buttons(
  128. Some("Select filename"),
  129. Some(&self.window),
  130. gtk::FileChooserAction::Save,
  131. &[
  132. ("_Cancel", gtk::ResponseType::Cancel),
  133. ("_Open", gtk::ResponseType::Ok),
  134. ],
  135. );
  136. let filter = gtk::FileFilter::new();
  137. filter.add_mime_type("image/svg+xml");
  138. filter.add_pattern("*.svg");
  139. filter.set_name(Some("SVG image"));
  140. dialog.add_filter(&filter);
  141. if dialog.run() == gtk::ResponseType::Ok {
  142. if let Some(tgt) = dialog.get_filename() {
  143. let mut tgt = tgt.to_owned();
  144. if !tgt.ends_with(".svg") {
  145. tgt.set_extension("svg");
  146. }
  147. if let Err(err) = self.app.data.borrow_mut().draw_to_file(tgt) {
  148. eprintln!("Error in rendering: {}", err);
  149. }
  150. }
  151. }
  152. unsafe {
  153. dialog.destroy();
  154. }
  155. });
  156. }
  157. /// Setup the callback that handles drawing the canvas
  158. fn canvas_draw(self, canvas: &gtk::DrawingArea) {
  159. canvas.connect_draw(move |cv, ctx| {
  160. let w = cv.get_allocated_width();
  161. let h = cv.get_allocated_height();
  162. if let Err(err) = self.app.data.borrow_mut().render(ctx, w as f64, h as f64) {
  163. eprintln!("Error in rendering: {}", err);
  164. }
  165. gtk::Inhibit(false)
  166. });
  167. }
  168. /// Show the whole thing
  169. fn show(&self) {
  170. self.window.show_all();
  171. }
  172. }