2023-01-16 09:53:52 +00:00
|
|
|
use crate::prelude::*;
|
|
|
|
use anyhow::Result;
|
|
|
|
|
|
|
|
use crate::builder::validator::gridinfo::GridInfo;
|
|
|
|
|
2023-01-18 06:28:32 +00:00
|
|
|
use std::sync::{
|
|
|
|
Arc, RwLock, RwLockReadGuard,
|
2025-03-04 10:37:45 +00:00
|
|
|
atomic::{AtomicBool, AtomicU32, Ordering::SeqCst},
|
2023-01-16 09:53:52 +00:00
|
|
|
};
|
|
|
|
use std::{ops::Deref, sync::Weak};
|
|
|
|
|
|
|
|
use super::fill_type::FillType;
|
|
|
|
|
2024-05-25 07:42:05 +00:00
|
|
|
pub enum ConnectDirection {
|
|
|
|
East,
|
|
|
|
West,
|
|
|
|
South,
|
|
|
|
North,
|
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
#[derive(Clone)]
|
2025-03-04 11:25:02 +00:00
|
|
|
enum ChildState {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some {
|
2025-03-04 11:25:02 +00:00
|
|
|
child: Arc<dyn GuiElementTraits>,
|
2023-01-16 09:53:52 +00:00
|
|
|
x: u32,
|
|
|
|
y: u32,
|
|
|
|
},
|
|
|
|
Extend {
|
2025-03-04 11:25:02 +00:00
|
|
|
weak_child: Weak<dyn GuiElementTraits>,
|
2023-01-16 09:53:52 +00:00
|
|
|
x: u32,
|
|
|
|
y: u32,
|
|
|
|
},
|
|
|
|
None,
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl ChildState {
|
2023-01-16 09:53:52 +00:00
|
|
|
fn downgrade(&self) -> Self {
|
|
|
|
match self {
|
|
|
|
Self::Some { child, x, y } => Self::Extend {
|
|
|
|
weak_child: Arc::downgrade(child),
|
|
|
|
x: *x,
|
|
|
|
y: *y,
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => panic!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_some(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
ChildState::Some { .. } => true,
|
|
|
|
ChildState::Extend { .. } => false,
|
|
|
|
ChildState::None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_extend(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
ChildState::Some { .. } => false,
|
|
|
|
ChildState::Extend { .. } => true,
|
|
|
|
ChildState::None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn get_some(&self) -> Option<(&Arc<dyn GuiElementTraits>, u32, u32)> {
|
2023-01-16 09:53:52 +00:00
|
|
|
match self {
|
|
|
|
ChildState::Some { child, x, y } => Some((child, *x, *y)),
|
|
|
|
ChildState::Extend { .. } => None,
|
|
|
|
ChildState::None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn get(&self) -> Option<(Arc<dyn GuiElementTraits>, u32, u32)> {
|
2023-01-16 09:53:52 +00:00
|
|
|
match self {
|
|
|
|
ChildState::Some { child, x, y } => Some((child.clone(), *x, *y)),
|
2023-01-18 06:28:32 +00:00
|
|
|
ChildState::Extend { weak_child, x, y } => {
|
|
|
|
weak_child.upgrade().map(|child| (child, *x, *y))
|
|
|
|
}
|
2023-01-16 09:53:52 +00:00
|
|
|
ChildState::None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn map<C, R>(&self, f: C) -> Option<R>
|
|
|
|
where
|
2025-03-04 11:25:02 +00:00
|
|
|
C: FnOnce(&Arc<dyn GuiElementTraits>) -> R,
|
2023-01-16 09:53:52 +00:00
|
|
|
{
|
|
|
|
match self {
|
|
|
|
ChildState::Some { child, .. } => Some(f(child)),
|
|
|
|
ChildState::Extend { weak_child, .. } => weak_child.upgrade().map(|child| f(&child)),
|
|
|
|
ChildState::None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub struct Grid {
|
2023-01-16 09:53:52 +00:00
|
|
|
pub(crate) framable: Arc<Framable>,
|
|
|
|
background: RwLock<Option<FillType>>,
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
children: RwLock<Vec<Vec<ChildState>>>,
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
dim_x: usize,
|
|
|
|
dim_y: usize,
|
|
|
|
|
|
|
|
padding: AtomicU32,
|
|
|
|
margin: AtomicU32,
|
|
|
|
|
|
|
|
visible: AtomicBool,
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl Grid {
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn new(
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &GuiHandler,
|
2023-01-16 09:53:52 +00:00
|
|
|
dim_x: usize,
|
|
|
|
dim_y: usize,
|
|
|
|
top_level: bool,
|
|
|
|
) -> Result<Arc<Self>> {
|
|
|
|
let grid = Arc::new(Grid {
|
2025-03-04 10:37:45 +00:00
|
|
|
framable: Framable::new(gui_handler.width(), gui_handler.height(), top_level)?,
|
2023-01-16 09:53:52 +00:00
|
|
|
background: RwLock::new(None),
|
|
|
|
|
|
|
|
children: RwLock::new(vec![vec![ChildState::None; dim_y]; dim_x]),
|
|
|
|
|
|
|
|
dim_x,
|
|
|
|
dim_y,
|
|
|
|
|
|
|
|
padding: AtomicU32::new(0),
|
|
|
|
margin: AtomicU32::new(10),
|
|
|
|
|
|
|
|
visible: AtomicBool::new(false),
|
|
|
|
});
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
// if top_level {
|
|
|
|
// let weak_grid = Arc::downgrade(&grid);
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
// grid.framable.add_callback(
|
|
|
|
// weak_grid.clone(),
|
|
|
|
// Box::new(move |gui_handler| {
|
|
|
|
// if let Some(grid) = weak_grid.upgrade() {
|
|
|
|
// grid.calculate_child_positions(gui_handler)?;
|
|
|
|
// }
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
// Ok(())
|
|
|
|
// }),
|
|
|
|
// );
|
|
|
|
// }
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(grid)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn dimensions(&self) -> (usize, usize) {
|
|
|
|
(self.dim_x, self.dim_y)
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_background(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 10:37:45 +00:00
|
|
|
background: impl Into<FillTypeInfo>,
|
|
|
|
) -> Result<()> {
|
|
|
|
super::set_background(
|
|
|
|
gui_handler,
|
|
|
|
self.visible(),
|
|
|
|
&self.framable,
|
|
|
|
&self.background,
|
|
|
|
background,
|
|
|
|
)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn background(&self) -> RwLockReadGuard<'_, Option<FillType>> {
|
|
|
|
self.background.read().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_margin(&self, margin: u32) {
|
|
|
|
self.margin.store(margin, SeqCst);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_padding(&self, padding: u32) {
|
|
|
|
self.padding.store(padding, SeqCst);
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn child_at(&self, x: usize, y: usize) -> Result<Option<Arc<dyn GuiElementTraits>>> {
|
2023-01-18 06:28:32 +00:00
|
|
|
if x >= self.dim_x {
|
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
"Tried to access Grid at {} while only being {} wide",
|
|
|
|
x,
|
|
|
|
self.dim_x
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
if y >= self.dim_y {
|
|
|
|
return Err(anyhow::anyhow!(
|
|
|
|
"Tried to access Grid at {} while only being {} tall",
|
|
|
|
y,
|
|
|
|
self.dim_y
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
Ok(self.children.read().unwrap()[x][y].map(|c| c.clone()))
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn detach(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 10:37:45 +00:00
|
|
|
pos_x: usize,
|
|
|
|
pos_y: usize,
|
2025-03-04 11:25:02 +00:00
|
|
|
) -> Result<Option<Arc<dyn GuiElementTraits>>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
if pos_x >= self.dim_x as usize {
|
|
|
|
panic!(
|
|
|
|
"x position ({}) must not be bigger than grid x-dimension ({})",
|
|
|
|
pos_x, self.dim_x
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if pos_y >= self.dim_y as usize {
|
|
|
|
panic!(
|
|
|
|
"y position ({}) must not be bigger than grid y-dimension ({})",
|
|
|
|
pos_y, self.dim_y
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let current = self.children.write().unwrap()[pos_x][pos_y].clone();
|
|
|
|
|
|
|
|
if current.is_extend() {
|
|
|
|
todo!();
|
|
|
|
}
|
|
|
|
|
|
|
|
self.children.write().unwrap()[pos_x][pos_y] = ChildState::None;
|
|
|
|
|
|
|
|
match current.get() {
|
|
|
|
Some((child, _x, _y)) => {
|
|
|
|
if self.visible() {
|
|
|
|
if let Some(child_visibility) = child.visibility() {
|
2025-03-04 10:37:45 +00:00
|
|
|
child_visibility.set_visibility(gui_handler, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Some(child))
|
|
|
|
}
|
|
|
|
None => Ok(None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if item got detached
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn detach_item(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
|
|
|
item: Arc<dyn GuiElementTraits>,
|
2025-03-04 10:37:45 +00:00
|
|
|
) -> Result<bool> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let mut grid = self.children.write().unwrap();
|
|
|
|
let mut removed = false;
|
|
|
|
|
|
|
|
'outer: for column in grid.iter_mut() {
|
|
|
|
for child_opt in column.iter_mut() {
|
|
|
|
let mut remove = false;
|
|
|
|
|
|
|
|
if let Some((child, _x, _y)) = child_opt.get() {
|
|
|
|
// ugly workaround because Arc::ptr_eq does not work (7th, April 2020)
|
|
|
|
|
|
|
|
let item_ptr = Arc::into_raw(item.clone());
|
|
|
|
let child_ptr = Arc::into_raw(child.clone());
|
|
|
|
|
|
|
|
if std::ptr::eq(item_ptr, child_ptr) {
|
|
|
|
remove = true;
|
|
|
|
|
|
|
|
if self.visible() {
|
|
|
|
if let Some(child_visibility) = child.visibility() {
|
2025-03-04 10:37:45 +00:00
|
|
|
child_visibility.set_visibility(gui_handler, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
Arc::from_raw(item_ptr);
|
|
|
|
Arc::from_raw(child_ptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if remove {
|
|
|
|
*child_opt = ChildState::None;
|
|
|
|
removed = true;
|
|
|
|
|
|
|
|
break 'outer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(removed)
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn set_position(&self, gui_handler: &mut GuiHandler, x: i32, y: i32) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
// update own position
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.change_position(gui_handler, x, y)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.update_frame(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.calculate_child_positions(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn calculate_child_positions(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
// recalculate positions of the children
|
|
|
|
let children = self.children.read().unwrap();
|
|
|
|
|
|
|
|
for (x, row) in children.iter().enumerate() {
|
|
|
|
for (y, child_opt) in row.iter().enumerate() {
|
|
|
|
if let Some((child, dim_x, dim_y)) = child_opt.get_some() {
|
|
|
|
if let Some(child_gridable) = child.gridable() {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.child_position(gui_handler, child_gridable, x, y, dim_x, dim_y)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn position(&self) -> (i32, i32) {
|
|
|
|
(self.framable.left(), self.framable.top())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn extents(&self) -> (u32, u32) {
|
|
|
|
let width = (self.framable.right() as i32 - self.framable.left()) as u32;
|
|
|
|
let height = (self.framable.bottom() as i32 - self.framable.top()) as u32;
|
|
|
|
|
|
|
|
(width, height)
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn disallow_position_scale(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.allow_position_scale(gui_handler, false)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn disallow_size_scale(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.allow_size_scale(gui_handler, false)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn connect(
|
2024-05-25 07:42:05 +00:00
|
|
|
&self,
|
|
|
|
direction: ConnectDirection,
|
2025-03-04 11:25:02 +00:00
|
|
|
elements: impl IntoIterator<Item = (usize, &Arc<Selectable>)>,
|
2024-05-25 07:42:05 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
for (index, selectable) in elements {
|
|
|
|
match direction {
|
|
|
|
ConnectDirection::East => {
|
|
|
|
let y = index;
|
|
|
|
|
|
|
|
for x in (0..self.dim_x).rev() {
|
|
|
|
if let Some(child) = self.child_at(x, y)? {
|
|
|
|
if let Some(gridable) = child.gridable() {
|
|
|
|
if let Some(grid_selectable) = gridable.selectable() {
|
|
|
|
Selectable::connect_horizontally(grid_selectable, selectable);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ConnectDirection::West => {
|
|
|
|
let y = index;
|
|
|
|
|
|
|
|
for x in 0..self.dim_x {
|
|
|
|
if let Some(child) = self.child_at(x, y)? {
|
|
|
|
if let Some(gridable) = child.gridable() {
|
|
|
|
if let Some(grid_selectable) = gridable.selectable() {
|
|
|
|
Selectable::connect_horizontally(selectable, grid_selectable);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ConnectDirection::South => {
|
|
|
|
let x = index;
|
|
|
|
|
|
|
|
for y in (0..self.dim_y).rev() {
|
|
|
|
if let Some(child) = self.child_at(x, y)? {
|
|
|
|
if let Some(gridable) = child.gridable() {
|
|
|
|
if let Some(grid_selectable) = gridable.selectable() {
|
|
|
|
Selectable::connect_vertically(grid_selectable, selectable);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ConnectDirection::North => {
|
|
|
|
let x = index;
|
|
|
|
|
|
|
|
for y in (0..self.dim_y).rev() {
|
|
|
|
if let Some(child) = self.child_at(x, y)? {
|
|
|
|
if let Some(gridable) = child.gridable() {
|
|
|
|
if let Some(grid_selectable) = gridable.selectable() {
|
|
|
|
Selectable::connect_vertically(grid_selectable, selectable);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn attach(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
|
|
|
child: Arc<dyn GuiElementTraits>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos_x: usize,
|
|
|
|
pos_y: usize,
|
|
|
|
dim_x: u32,
|
|
|
|
dim_y: u32,
|
|
|
|
) -> Result<()> {
|
|
|
|
if pos_x >= self.dim_x as usize {
|
|
|
|
panic!(
|
|
|
|
"x postion ({}) must not be bigger than grid x-dimension ({})",
|
|
|
|
pos_x, self.dim_x
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if pos_y >= self.dim_y as usize {
|
|
|
|
panic!(
|
|
|
|
"y postion ({}) must not be bigger than grid y-dimension ({})",
|
|
|
|
pos_y, self.dim_y
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_ne!(dim_x, 0);
|
|
|
|
assert_ne!(dim_y, 0);
|
|
|
|
|
|
|
|
// get grid
|
|
|
|
let mut grid = self.children.write().unwrap();
|
|
|
|
|
|
|
|
if let Some(child_gridable) = child.gridable() {
|
|
|
|
// check for neighbourships
|
|
|
|
if let Some(current_selectable) = child_gridable.selectable() {
|
|
|
|
Self::set_neighbours(
|
|
|
|
&*grid,
|
|
|
|
current_selectable,
|
|
|
|
(pos_x, pos_y),
|
|
|
|
(self.dim_x, self.dim_y),
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.framable.is_framed() {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.child_position(gui_handler, child_gridable, pos_x, pos_y, dim_x, dim_y)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
child_gridable.set_layer(self.framable.ui_layer())?;
|
|
|
|
}
|
|
|
|
|
2024-04-22 07:48:32 +00:00
|
|
|
// disable the old element
|
|
|
|
match &grid[pos_x][pos_y] {
|
|
|
|
ChildState::Some { child, .. } => {
|
|
|
|
if self.visible() {
|
|
|
|
if let Some(child_visibility) = child.visibility() {
|
2025-03-04 10:37:45 +00:00
|
|
|
child_visibility.set_visibility(gui_handler, false)?;
|
2024-04-22 07:48:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ChildState::Extend { .. } => todo!(),
|
|
|
|
ChildState::None => (),
|
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
if self.visible() {
|
|
|
|
if let Some(child_visibility) = child.visibility() {
|
2025-03-04 10:37:45 +00:00
|
|
|
child_visibility.set_visibility(gui_handler, true)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let child_state = ChildState::Some {
|
|
|
|
child,
|
|
|
|
x: dim_x,
|
|
|
|
y: dim_y,
|
|
|
|
};
|
|
|
|
|
|
|
|
let weak = child_state.downgrade();
|
|
|
|
|
|
|
|
// insert the element
|
|
|
|
for x in pos_x..(pos_x + dim_x as usize) {
|
|
|
|
for y in pos_y..(pos_y + dim_y as usize) {
|
|
|
|
grid[x][y] = weak.clone();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
grid[pos_x][pos_y] = child_state;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn has_attachment_at(&self, x: usize, y: usize) -> Result<bool> {
|
|
|
|
let children = self.children.read().unwrap();
|
|
|
|
|
|
|
|
Ok(children[x][y].is_some())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn try_from(
|
|
|
|
grid_info: &GridInfo,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2023-01-16 09:53:52 +00:00
|
|
|
top_level: bool,
|
|
|
|
) -> Result<Arc<Self>> {
|
|
|
|
let grid = Grid::new(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2023-01-16 09:53:52 +00:00
|
|
|
grid_info.x_dimension.get()?,
|
|
|
|
grid_info.y_dimension.get()?,
|
|
|
|
top_level,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
grid.set_margin(grid_info.margin);
|
|
|
|
grid.set_padding(grid_info.padding);
|
|
|
|
|
|
|
|
if let Some(background) = &grid_info.background_type {
|
2025-03-04 10:37:45 +00:00
|
|
|
grid.set_background(gui_handler, background.clone())?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(grid)
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn change_position_unscaled(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 10:37:45 +00:00
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
) -> Result<()> {
|
|
|
|
self.framable.change_position_unscaled(gui_handler, x, y)?;
|
2024-04-21 06:28:17 +00:00
|
|
|
|
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.update_frame(gui_handler)?;
|
2024-04-21 06:28:17 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.calculate_child_positions(gui_handler)?;
|
2024-04-21 06:28:17 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
fn child_position(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
|
|
|
child: &dyn Gridable,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos_x: usize,
|
|
|
|
pos_y: usize,
|
|
|
|
dim_x: u32,
|
|
|
|
dim_y: u32,
|
|
|
|
) -> Result<()> {
|
|
|
|
// calculate the extents for the new child
|
|
|
|
let single_width = ((self.framable.right() as i32 - self.framable.left())
|
|
|
|
- (2 * self.padding.load(SeqCst) as i32
|
|
|
|
+ (self.dim_x as i32 - 1) * self.margin.load(SeqCst) as i32))
|
|
|
|
/ self.dim_x as i32;
|
|
|
|
let single_height = ((self.framable.bottom() as i32 - self.framable.top())
|
|
|
|
- (2 * self.padding.load(SeqCst) as i32
|
|
|
|
+ (self.dim_y as i32 - 1) * self.margin.load(SeqCst) as i32))
|
|
|
|
/ self.dim_y as i32;
|
|
|
|
|
|
|
|
let child_width = if dim_x == 1 {
|
|
|
|
single_width
|
|
|
|
} else {
|
|
|
|
single_width + (dim_x - 1) as i32 * (single_width + self.margin.load(SeqCst) as i32)
|
|
|
|
};
|
|
|
|
|
|
|
|
let child_height = if dim_y == 1 {
|
|
|
|
single_height
|
|
|
|
} else {
|
|
|
|
single_height + (dim_y - 1) as i32 * (single_height + self.margin.load(SeqCst) as i32)
|
|
|
|
};
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let win_width = gui_handler.width();
|
|
|
|
let win_height = gui_handler.height();
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let y_align = match self.framable.vertical_alignment() {
|
|
|
|
VerticalAlign::Top => 0,
|
|
|
|
VerticalAlign::Middle => win_height / 2,
|
|
|
|
VerticalAlign::Bottom => win_height,
|
|
|
|
VerticalAlign::NotSet => panic!("grid: vertical alignment is not set"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let x_align = match self.framable.horizontal_alignment() {
|
|
|
|
HorizontalAlign::Left => 0,
|
|
|
|
HorizontalAlign::Middle => win_width / 2,
|
|
|
|
HorizontalAlign::Right => win_width,
|
|
|
|
HorizontalAlign::NotSet => panic!("grid: horizontal alignment is not set"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let child_x = self.framable.left()
|
|
|
|
+ self.padding.load(SeqCst) as i32
|
|
|
|
+ pos_x as i32 * (single_width + self.margin.load(SeqCst) as i32);
|
|
|
|
let child_y = self.framable.top()
|
|
|
|
+ self.padding.load(SeqCst) as i32
|
|
|
|
+ pos_y as i32 * (single_height + self.margin.load(SeqCst) as i32);
|
|
|
|
|
|
|
|
child.set_frame(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2023-01-16 09:53:52 +00:00
|
|
|
child_x - x_align as i32,
|
|
|
|
child_y - y_align as i32,
|
|
|
|
child_width as u32,
|
|
|
|
child_height as u32,
|
|
|
|
self.framable.vertical_alignment(),
|
|
|
|
self.framable.horizontal_alignment(),
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn select(&self, gui_handler: &mut GuiHandler, x: u32, y: u32) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
match self.children.read().unwrap()[x as usize][y as usize].get_some() {
|
|
|
|
Some((child, ..)) => match child.gridable() {
|
|
|
|
Some(gridable) => match gridable.selectable() {
|
|
|
|
Some(selectable) => {
|
2025-03-04 10:37:45 +00:00
|
|
|
selectable.select(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
None => panic!("gridable at position ({}, {}), is not selectable", x, y),
|
|
|
|
},
|
|
|
|
None => panic!("element at position ({}, {}) is not a gridable", x, y),
|
|
|
|
},
|
|
|
|
|
|
|
|
None => panic!("no element at position ({}, {})", x, y),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn disable_tree(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.delete(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.disable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.set_tree_visibility(gui_handler, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn set_tree_visibility(&self, gui_handler: &mut GuiHandler, visible: bool) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let tree = self.children.read().unwrap();
|
|
|
|
|
|
|
|
for row in tree.deref() {
|
|
|
|
for child_state in row {
|
|
|
|
if let Some((child, ..)) = child_state.get_some() {
|
|
|
|
if let Some(visibility) = child.visibility() {
|
2025-03-04 10:37:45 +00:00
|
|
|
visibility.set_visibility(gui_handler, visible)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_neighbours(
|
2025-03-04 11:25:02 +00:00
|
|
|
grid: &Vec<Vec<ChildState>>,
|
|
|
|
current_child: &Arc<Selectable>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos: (usize, usize),
|
|
|
|
dim: (usize, usize),
|
|
|
|
) -> Result<()> {
|
|
|
|
// set east neighbour
|
|
|
|
Self::set_east_neighbour(grid, current_child, pos, dim)?;
|
|
|
|
|
|
|
|
// set west neighbour
|
|
|
|
Self::set_west_neighbour(grid, current_child, pos, dim)?;
|
|
|
|
|
|
|
|
// set north neighbour
|
|
|
|
Self::set_north_neighbour(grid, current_child, pos, dim)?;
|
|
|
|
|
|
|
|
// set south neighbour
|
|
|
|
Self::set_south_neighbour(grid, current_child, pos, dim)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_north_neighbour(
|
2025-03-04 11:25:02 +00:00
|
|
|
grid: &Vec<Vec<ChildState>>,
|
|
|
|
current_child: &Arc<Selectable>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos: (usize, usize),
|
|
|
|
dim: (usize, usize),
|
|
|
|
) -> Result<bool> {
|
|
|
|
match Self::search_neighbour_in_direction(grid, pos, (0, 1), dim) {
|
2024-05-18 07:39:15 +00:00
|
|
|
Some(neighbour) => {
|
|
|
|
current_child.set_south_neighbour(Some(&neighbour));
|
|
|
|
neighbour.set_north_neighbour(Some(current_child));
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
None => Ok(false),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_south_neighbour(
|
2025-03-04 11:25:02 +00:00
|
|
|
grid: &Vec<Vec<ChildState>>,
|
|
|
|
current_child: &Arc<Selectable>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos: (usize, usize),
|
|
|
|
dim: (usize, usize),
|
|
|
|
) -> Result<bool> {
|
|
|
|
match Self::search_neighbour_in_direction(grid, pos, (0, -1), dim) {
|
2024-05-18 07:39:15 +00:00
|
|
|
Some(neighbour) => {
|
|
|
|
current_child.set_north_neighbour(Some(&neighbour));
|
|
|
|
neighbour.set_south_neighbour(Some(current_child));
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
None => Ok(false),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_east_neighbour(
|
2025-03-04 11:25:02 +00:00
|
|
|
grid: &Vec<Vec<ChildState>>,
|
|
|
|
current_child: &Arc<Selectable>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos: (usize, usize),
|
|
|
|
dim: (usize, usize),
|
|
|
|
) -> Result<bool> {
|
|
|
|
match Self::search_neighbour_in_direction(grid, pos, (1, 0), dim) {
|
2024-05-18 07:39:15 +00:00
|
|
|
Some(neighbour) => {
|
|
|
|
current_child.set_east_neighbour(Some(&neighbour));
|
|
|
|
neighbour.set_west_neighbour(Some(current_child));
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
None => Ok(false),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn set_west_neighbour(
|
2025-03-04 11:25:02 +00:00
|
|
|
grid: &Vec<Vec<ChildState>>,
|
|
|
|
current_child: &Arc<Selectable>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos: (usize, usize),
|
|
|
|
dim: (usize, usize),
|
|
|
|
) -> Result<bool> {
|
|
|
|
match Self::search_neighbour_in_direction(grid, pos, (-1, 0), dim) {
|
2024-05-18 07:39:15 +00:00
|
|
|
Some(neighbour) => {
|
|
|
|
current_child.set_west_neighbour(Some(&neighbour));
|
|
|
|
neighbour.set_east_neighbour(Some(current_child));
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(true)
|
|
|
|
}
|
|
|
|
None => Ok(false),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn search_neighbour_in_direction(
|
2025-03-04 11:25:02 +00:00
|
|
|
grid: &Vec<Vec<ChildState>>,
|
2023-01-16 09:53:52 +00:00
|
|
|
pos: (usize, usize),
|
|
|
|
dir: (i32, i32),
|
|
|
|
dim: (usize, usize),
|
2025-03-04 11:25:02 +00:00
|
|
|
) -> Option<Arc<Selectable>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let (mut x, mut y) = pos;
|
|
|
|
let (x_step, y_step) = dir;
|
|
|
|
let (dim_x, dim_y) = dim;
|
|
|
|
|
|
|
|
while ((x as i32 + x_step) < dim_x as i32 && (x as i32 + x_step) >= 0)
|
|
|
|
&& ((y as i32 + y_step) < dim_y as i32 && (y as i32 + y_step) >= 0)
|
|
|
|
{
|
|
|
|
x = (x as i32 + x_step) as usize;
|
|
|
|
y = (y as i32 + y_step) as usize;
|
|
|
|
|
|
|
|
if let Some((neighbour, ..)) = grid[x][y].get() {
|
|
|
|
if let Some(gridable) = neighbour.gridable() {
|
|
|
|
if let Some(selectable) = gridable.selectable() {
|
|
|
|
return Some(selectable.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl GuiElementTraits for Grid {
|
|
|
|
fn gridable(&self) -> Option<&dyn Gridable> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visibility(&self) -> Option<&dyn Visibility> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(GuiElement::Grid(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl Visibility for Grid {
|
2023-01-16 09:53:52 +00:00
|
|
|
fn visible(&self) -> bool {
|
|
|
|
self.visible.load(SeqCst)
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn set_visibility(&self, gui_handler: &mut GuiHandler, visibility: bool) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
if visibility != self.visible.load(SeqCst) {
|
|
|
|
self.visible.store(visibility, SeqCst);
|
|
|
|
|
|
|
|
if visibility {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.add(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.enable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.set_tree_visibility(gui_handler, true)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
} else {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.disable_tree(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl Gridable for Grid {
|
2023-01-16 09:53:52 +00:00
|
|
|
fn set_frame(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2023-01-16 09:53:52 +00:00
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
w: u32,
|
|
|
|
h: u32,
|
|
|
|
vert_align: VerticalAlign,
|
|
|
|
hori_align: HorizontalAlign,
|
|
|
|
) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable
|
|
|
|
.set_frame(gui_handler, x, y, w, h, vert_align, hori_align);
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.update_frame(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.calculate_child_positions(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn selectable(&self) -> Option<&Arc<Selectable>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
"Grid"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_layer(&self, layer: i32) -> Result<()> {
|
|
|
|
self.framable.set_ui_layer(layer);
|
|
|
|
|
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
|
|
|
background.set_ui_layer(layer);
|
|
|
|
}
|
|
|
|
|
|
|
|
for row in self.children.read().unwrap().iter() {
|
|
|
|
for child_state in row.iter() {
|
|
|
|
if let Some((child, ..)) = child_state.get_some() {
|
|
|
|
if let Some(gridable) = child.gridable() {
|
|
|
|
gridable.set_layer(layer)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-04-21 05:41:08 +00:00
|
|
|
|
|
|
|
fn position_extent(&self) -> (i32, i32, u32, u32) {
|
|
|
|
self.framable.position()
|
|
|
|
}
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|