extern crate gdk; extern crate gio; extern crate glib; extern crate gtk; use std::cell::RefCell; use std::rc::Rc; use gio::prelude::*; use gtk::prelude::*; use std::env::args; use std::process; mod grid; mod pawn; /// Content of the Application state /// #[derive(Debug, Clone, Default)] pub struct AppData { /// A slot for any pawn waiting for placement pending: Option, pawns: Option, } #[derive(Debug,Clone)] struct NewPawnDialog { root: gtk::Window, name: gtk::Entry, desc: gtk::TextBuffer, confirm: gtk::Button, abort: gtk::Button, } impl NewPawnDialog { fn init(builder: >k::Builder, app_state: AppState) -> Self { let dialog = NewPawnDialog { root: builder.get_object("new_pawn_dialog").unwrap(), name: builder.get_object("name_entry").unwrap(), desc: builder.get_object("pawn_desc").unwrap(), confirm: builder.get_object("confirm").unwrap(), abort: builder.get_object("abort").unwrap(), }; let d = dialog.clone(); dialog.abort.connect_clicked(move |_| { d.reset(); d.hide(); }); let d = dialog.clone(); dialog.confirm.connect_clicked(move |_| { if let Some(data) = d.take_data() { let pawn = pawn::Pawn::from_data(data); println!("Create {:?}", pawn); app_state.add_pawn(&pawn); d.reset(); d.hide(); }; }); dialog } fn reset(&self) { self.name.set_text(""); self.desc.set_text(""); } fn take_data(&self) -> Option { let name = String::from(self.name.get_text().unwrap().as_str()); let description = { let (ref start, ref end) = self.desc.get_bounds(); String::from( self.desc.get_text(start, end, true).unwrap().as_str() ) }; if name.is_empty() { None } else { Some(pawn::PawnData { name, description, position: None, }) } } fn show(&self) { self.root.show_all(); } fn hide(&self) { self.root.set_visible(false); } } /// A sharable state for the whole application #[derive(Debug, Clone, Default)] pub struct AppState(Rc>); impl std::ops::Deref for AppState { type Target = Rc>; fn deref(&self) -> &Self::Target { &self.0 } } impl AppState { fn add_pawn(&self, pawn: &pawn::Pawn) { pawn.connect_place(self.clone()); pawn.connect_stats(); self.borrow_mut() .pawns .as_ref() .expect("PawnList was not initialized !") .add(&pawn); dbg!(pawn); } } struct App { inner: gtk::Application, state: AppState, } impl App { fn new<'a>() -> Result { let app = App { inner: gtk::Application::new( "home.local.PlayMat", gio::ApplicationFlags::FLAGS_NONE, ) .expect("Failed to build Application"), state: Default::default(), }; app.connect_all(); Ok(app) } fn connect_all(&self) { let app_state = self.state.clone(); self.inner.connect_startup(|_| {}); self.inner.connect_activate(move |app| { println!("Activate App"); let main_src = include_str!("../res/main.glade"); let builder = gtk::Builder::new_from_string(main_src); let win: gtk::ApplicationWindow = builder.get_object("app").unwrap(); win.set_application(app); // Set up a simple switch for the Pawn list panel let panel: gtk::Paned = builder.get_object("panel").unwrap(); Self::new_action(app, "panel_switch", move |_, _| { if panel.get_position() == 0 { panel.set_position(360); } else { panel.set_position(0); } }); // Pawn list let pawn_list = builder.get_object("pawn_list").unwrap(); let pawn_list = pawn::PawnList::init(pawn_list); app_state.borrow_mut().pawns.replace(pawn_list); win.show_all(); // Before grid because we want to hide some widgets there // Initialize grid let grid: gtk::Grid = builder.get_object("map").unwrap(); // TODO: implement drag-drop events // * From pawn_list to cell : place pawn on dest cell // * From cell to cell : move pawn from source to dest cell let _grid = grid::Grid::init(grid, 10, app_state.clone()); let dialog = NewPawnDialog::init(&builder, app_state.clone()); let new_pawn_btn: gtk::Button = builder.get_object("add_pawn").unwrap(); new_pawn_btn.connect_clicked(move |_| { Self::on_add_pawn(&dialog)}); }); } fn on_add_pawn(win: &NewPawnDialog) { win.show(); } /// Creates a simple action and connects the given handler to its activate signal. fn new_action) + 'static>( app: >k::Application, name: &str, f: F, ) { let action = gio::SimpleAction::new(name, None); action.connect_activate(f); app.add_action(&action); } fn run(&self, args: &[String]) -> i32 { println!("{:#?}", &self.state); self.inner.run(args) } } fn main() { if gtk::init().is_err() { println!("Failed to initialize Gtk"); return; } let exit_code = match App::new() { Err(e) => { println!("Error !\n{}", e); 1 } Ok(app) => app.run(&args().collect::>()), }; process::exit(exit_code); }