diff --git a/src/grid.rs b/src/grid.rs index 8829780..85b781c 100644 --- a/src/grid.rs +++ b/src/grid.rs @@ -4,18 +4,21 @@ pub struct Grid { inner: gtk::Grid, } +#[derive(Debug,Copy,Clone)] pub struct CellPosition(i32, i32); impl Grid { - // Initialize a grid, populating its cells - // Only square grid for now - pub fn init(inner: gtk::Grid, size: i32) -> Self { + /// Initialize a grid, populating its cells + /// Only square grid for now + pub(crate) fn init(inner: gtk::Grid, size: i32, app_state: AppState) -> Self { for i in 0..(size * size) { let x = i % size; let y = i / size; - let cell = grid::Cell::new(CellPosition(x,y)); + let cell = grid::Cell::new( + CellPosition(x,y) + ); + cell.connect_clicked(app_state.clone()); inner.attach(cell.as_ref(), x, y, 1, 1); - // Signals } Grid { inner } } @@ -26,6 +29,7 @@ struct CellWidget { eventbox: gtk::EventBox, position: gtk::Label, header : gtk::Label, + content: gtk::Label, } impl CellWidget { @@ -68,25 +72,28 @@ impl CellWidget { .unwrap(); position.set_text(&format!("{}x{}", pos.0, pos.1)); - let header_clone = header.clone(); - eventbox.connect_button_press_event(Self::on_click(header_clone)); - - CellWidget { + let cell = CellWidget { eventbox, position, header, - } + content: builder.get_object("content").unwrap(), + }; + cell } - // Handler for button press events - fn on_click( - data: gtk::Label - ) -> impl Fn(>k::EventBox, &gdk::EventButton) -> gtk::Inhibit - { - move |_,_| { - data.set_text("Clicked."); - gtk::Inhibit(true) - } + pub fn connect_clicked(&self, state: AppState) { + let header = self.header.clone(); + let content = self.content.clone(); + self.eventbox + .connect_button_press_event(move |_,_| { + let mut state = state.borrow_mut(); + if let Some(data) = state.pending.take() { + println!("{:?}", data); + content.set_text(&data.name); + }; + header.set_text("Is Clicked."); + gtk::Inhibit(true) + }); } } @@ -97,21 +104,22 @@ impl AsRef for CellWidget { } } -pub struct Cell { +struct Cell { inner: CellWidget, - is_active: bool, - is_occupied: bool, + position: CellPosition, } impl Cell { - pub fn new(pos: CellPosition) -> Self { - let inner = CellWidget::new(pos, "Empty cell"); + fn new(position: CellPosition) -> Self { Cell { - inner, - is_active: false, - is_occupied: false, + inner: CellWidget::new(position, ""), + position } } + + fn connect_clicked(&self, state: AppState) { + self.inner.connect_clicked(state); + } } impl AsRef for Cell { diff --git a/src/main.rs b/src/main.rs index f30b863..f25ba1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,109 +3,44 @@ extern crate gio; extern crate gdk; extern crate glib; -use gtk::BoxExt; +use std::cell::RefCell; +use std::rc::Rc; + use gtk::prelude::*; use gio::prelude::*; use std::env::args; use std::process; mod grid; +mod pawn; -mod pawn { - use super::*; - - #[derive(Debug,Clone)] - pub struct PawnList { - inner: gtk::ListBox, - } - - impl PawnList { - pub fn init(inner: gtk::ListBox) -> Self { - PawnList{ inner } - } - - pub fn add(&self, pawn: &Pawn) { - let row = gtk::ListBoxRow::new(); - row.add(pawn.as_ref()); - self.inner.add(&row); - } - } - - struct PawnData { - name: String, - } - - - pub struct Pawn { - data: PawnData, - widget: gtk::Box, - } - - impl Pawn { - - pub fn new>(name: S) -> Self { - let pawn_src = include_str!("../res/pawn.glade"); - let builder = gtk::Builder::new_from_string(pawn_src); - let name = name.into(); - let label: gtk::Label = - builder - .get_object("name") - .unwrap(); - label.set_text(&name); - let place_btn: gtk::Button = - builder - .get_object("place_btn") - .unwrap(); - place_btn.connect_clicked(|_| { - println!("Placing..."); - }); - let stats_btn: gtk::Button = - builder - .get_object("stats_btn") - .unwrap(); - stats_btn.connect_clicked(|_| { - println!("Showing stats..."); - }); - - let widget: gtk::Box = builder.get_object("pawn").unwrap(); - Pawn { - data: PawnData{ name }, - widget, - } - } - } - - impl AsRef for Pawn { - fn as_ref(&self) -> >k::Box { - &self.widget - } - } - - pub fn pawn_factory() -> Vec { - let mut pawns = Vec::with_capacity(3); - for name in &["Lomion", "Oilosse", "Fefi"] { - pawns.push(Pawn::new(*name)); - } - pawns - } - - #[cfg(test)] - mod tests { - #[test] - fn test_pawn_factory() { - let pawns = pawn_factory(); - assert_eq!(pawns.len(), 3); - assert_eq!(pawns.get(2).unwrap(), "Fefi"); - } - } +/// Content of the Application state +/// +#[derive(Debug,Clone,Default)] +pub struct AppData { + /// A slot for any pawn waiting for placement + pending: Option, + /// Owned list of pawns + pawns: Vec, } +/// 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 + } +} struct App { inner: gtk::Application, - pawns: Vec, + state: AppState, } + impl App { fn new<'a>() -> Result { let app = @@ -114,55 +49,63 @@ impl App { "home.local.PlayMat", gio::ApplicationFlags::FLAGS_NONE ).expect("Failed to build Application"), - pawns: Vec::new(), + state: Default::default(), }; - app.inner.connect_startup(|_| {}); - app.inner.connect_activate(Self::on_activate); + app.connect_all(); Ok(app) } - fn on_activate(app: >k::Application) { - 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); + 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); + // 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); + } + }); + + // 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()); + + // TODO: + // * Display creature description when hovering over its position (cell) + + // TODO: Pawn list + let pawn_list = builder.get_object("pawn_list").unwrap(); + let pawn_list = pawn::PawnList::init(pawn_list); + + for pawn in pawn::pawn_factory() { + pawn_list.add(&pawn); + pawn.connect_place(app_state.clone()); + pawn.connect_stats(); + app_state.borrow_mut().pawns.push(pawn); + } + win.show_all(); } - }); - - // 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); - - // TODO: - // * Display creature description when hovering over its position (cell) - - // TODO: Pawn list - let pawn_list = builder.get_object("pawn_list").unwrap(); - let pawn_list = pawn::PawnList::init(pawn_list); - - for pawn in &pawn::pawn_factory() { - pawn_list.add(pawn); - } - win.show_all(); + ); } /// Creates a simple action and connects the given handler to its activate signal. - pub fn new_action) + 'static>( + fn new_action) + 'static>( app: >k::Application, name: &str, f: F, @@ -173,6 +116,7 @@ impl App { } fn run(&self, args: &[String]) -> i32 { + println!("{:#?}", &self.state); self.inner.run(args) } } diff --git a/src/pawn.rs b/src/pawn.rs new file mode 100644 index 0000000..f500795 --- /dev/null +++ b/src/pawn.rs @@ -0,0 +1,99 @@ + +use super::*; + +#[derive(Debug,Clone)] +pub struct PawnList { + inner: gtk::ListBox, +} + +impl PawnList { + pub fn init(inner: gtk::ListBox) -> Self { + PawnList{ inner } + } + + pub fn add(&self, pawn: &Pawn) { + let row = gtk::ListBoxRow::new(); + row.add(pawn.as_ref()); + self.inner.add(&row); + } +} + +#[derive(Debug,Clone)] +pub struct PawnData { + pub name: String, + pub position: Option, // Content of label from CellWidget +} + +#[derive(Debug, Clone)] +pub struct Pawn { + data: PawnData, + widget: gtk::Box, + place_btn: gtk::Button, + stats_btn: gtk::Button, +} + +impl Pawn { + pub fn new>(name: S) -> Self { + let pawn_src = include_str!("../res/pawn.glade"); + let builder = gtk::Builder::new_from_string(pawn_src); + let name = name.into(); + let label: gtk::Label = + builder + .get_object("name") + .unwrap(); + label.set_text(&name); + let widget: gtk::Box = builder.get_object("pawn").unwrap(); + Pawn { + data: PawnData{ name, position: None, }, + widget, + place_btn: builder.get_object("place_btn").unwrap(), + stats_btn: builder.get_object("stats_btn").unwrap(), + } + } + + pub fn connect_place(&self, state: AppState) { + let name = self.data.name.clone(); + let data = self.data.clone(); + self.place_btn + .connect_clicked( + move |_| { + println!("Placing {}...", name); + let mut state = state.0.borrow_mut(); + state.pending = Some(data.clone()); // ??? + } + ); + } + + pub fn connect_stats(&self) { + self.stats_btn + .connect_clicked( + move |_| { + println!("Showing stats..."); + } + ); + } +} + +impl AsRef for Pawn { + fn as_ref(&self) -> >k::Box { + &self.widget + } +} + +pub fn pawn_factory() -> Vec { + let mut pawns = Vec::with_capacity(3); + for name in &["Lomion", "Oilosse", "Fefi"] { + pawns.push(Pawn::new(*name)); + } + pawns +} + +#[cfg(test)] +mod tests { + #[test] + fn test_pawn_factory() { + let pawns = pawn_factory(); + assert_eq!(pawns.len(), 3); + assert_eq!(pawns.get(2).unwrap(), "Fefi"); + } +}