186 lines
5.7 KiB
Rust
186 lines
5.7 KiB
Rust
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<gtk::EventBox> 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<gtk::TargetEntry>,
|
|
}
|
|
|
|
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<gtk::EventBox> for CellWidget {
|
|
#[inline]
|
|
fn as_ref(&self) -> >k::EventBox {
|
|
&self.eventbox
|
|
}
|
|
}
|
|
|