use crate::*; pub struct Grid { inner: gtk::Grid, } /// Position of a cell as (x,y) tuple. #[derive(Debug, Copy, Clone)] pub struct CellPosition(i32, i32); impl Grid { /// 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)); cell.inner.connect_clicked(app_state.clone()); inner.attach(cell.as_ref(), x, y, 1, 1); } Grid { inner } } } /// A single cell of the grid struct Cell { /// The inner widget inner: CellWidget, position: CellPosition, } impl Cell { fn new(position: CellPosition) -> Self { Cell { inner: CellWidget::new(position), position, } } } impl AsRef for Cell { #[inline] fn as_ref(&self) -> >k::EventBox { self.inner.as_ref() } } /// The cell widget is either empty or has a pawn /// Pawn can be moved around cells using drag-n-drop. However, due to my inability /// to dynamically change the behavior with set/unset methods, all cells are always /// drag and drop at once. /// It is usable though, just weird. When you grab an empty cell you'll make a button appear. /// Know that if you drop a cell on itself, it will empty itself ! #[derive(Debug, Clone)] struct CellWidget { eventbox: gtk::EventBox, position: gtk::Label, name: gtk::Label, desc_btn: gtk::Button, targets: Vec, } impl CellWidget { fn new(pos: CellPosition) -> Self { let cell_src = include_str!("../res/cell.glade"); let builder = gtk::Builder::new_from_string(cell_src); // Retrieve children let cell = CellWidget { eventbox: builder.get_object("cell").unwrap(), position: builder.get_object("position").unwrap(), name: builder.get_object("name").unwrap(), desc_btn: builder.get_object("desc_btn").unwrap(), targets: vec![ gtk::TargetEntry::new("text/plain;charset=utf-8", gtk::TargetFlags::SAME_APP, 0), ], }; cell.desc_btn.set_visible(false); cell.position.set_text(&format!("{}x{}", pos.0, pos.1)); cell.set_drag(); cell.set_drop(); Self::set_content(&cell, None); cell } fn unset_drag(&self) { dbg!("Unset drag"); self.eventbox.drag_source_unset(); } fn set_drag(&self) { dbg!("Set drag"); // Acting as source self.eventbox.drag_source_set( gdk::ModifierType::MODIFIER_MASK, &self.targets, gdk::DragAction::MOVE, ); let c = self.clone(); // Send data to the drop site self.eventbox .connect_drag_data_get(move |_, _, data, info, time| { println!("Send..."); // TODO: Refactoring, this is the inverse of 'placing', // building a PawnData instead of destructuring it. if let Some(to_send) = c.name .get_text() { dbg!(&to_send); data.set_text(&to_send); } else { dbg!("Should not happen !!"); } }); // Empty the cell on successfull move let c = self.clone(); self.eventbox.connect_drag_data_delete(move |w, drag| { println!("Cleaning... {:#?}", (&drag.drag_drop_succeeded())); Self::set_content(&c, None); }); } fn unset_drop(&self) { dbg!("Unset drop"); self.eventbox.drag_dest_unset(); } fn set_drop(&self) { dbg!("Set drop"); // Acting as destination self.eventbox.drag_dest_set( gtk::DestDefaults::ALL, &self.targets, gdk::DragAction::MOVE, ); // Retrieve data from the source let c = self.clone(); self.eventbox .connect_drag_data_received(move |w, _, _, _, data, _, _| { // Check if cell is not already occupied if !(c.name.get_text().unwrap().as_str() == "") { // TODO: Find what to do to abort, since // DragContext methods don't seem to work... println!("Overriding !!"); } else { println!("Dropped !"); if let Some(text) = data.get_text() { Self::set_content(&c, Some(text.as_str())); } else { eprintln!("No data !"); } }; }); } /// Updates content of the Cell fn set_content(&self, data: Option<&str>) { println!("Set content to {:?}", &data); // TODO: there is surely something cleaner... if let Some(name) = data { self.name.set_text(name); self.desc_btn.set_visible(true); } else { self.name.set_text(""); self.desc_btn.set_visible(false); }; } pub fn connect_clicked(&self, state: AppState) { let c = self.clone(); self.eventbox.connect_button_press_event(move |_, _| { let mut state = state.borrow_mut(); if let Some(ref data) = state.pending.take() { println!("{:?}", data); Self::set_content(&c, Some(&data.name)); }; gtk::Inhibit(true) }); } } impl AsRef for CellWidget { #[inline] fn as_ref(&self) -> >k::EventBox { &self.eventbox } }