209 lines
5.9 KiB
Rust
209 lines
5.9 KiB
Rust
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<pawn::PawnData>,
|
|
pawns: Option<pawn::PawnList>,
|
|
}
|
|
|
|
#[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<pawn::PawnData> {
|
|
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<RefCell<AppData>>);
|
|
|
|
impl std::ops::Deref for AppState {
|
|
type Target = Rc<RefCell<AppData>>;
|
|
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<Self, &'a str> {
|
|
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<F: Fn(&gio::SimpleAction, &Option<glib::Variant>) + '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::<Vec<_>>()),
|
|
};
|
|
process::exit(exit_code);
|
|
}
|