use crate::editor::DOCKMANAGER;
use crate::prelude::*;
use rusterix::{TileGroup, TileGroupMemberRef, TileSource};
pub struct DuplicateTile {
id: TheId,
nodeui: TheNodeUI,
}
impl DuplicateTile {
fn append_board_position(project: &Project, span: Vec2<i32>) -> Vec2<i32> {
let pack_cols = (project.tile_board_cols - 1).max(1);
let mut occupied: FxHashSet<(i32, i32)> = FxHashSet::default();
for pos in project.tile_board_tiles.values() {
occupied.insert((pos.x, pos.y));
}
for pos in project.tile_board_groups.values() {
let group_span = project
.tile_groups
.iter()
.find(|(id, _)| project.tile_board_groups.get(*id) == Some(pos))
.map(|(_, group)| Vec2::new(group.width as i32, group.height as i32))
.unwrap_or(Vec2::new(1, 1));
for dy in 0..group_span.y.max(1) {
for dx in 0..group_span.x.max(1) {
occupied.insert((pos.x + dx, pos.y + dy));
}
}
}
for pos in project.tile_board_empty_slots() {
occupied.insert((pos.x, pos.y));
}
for y in 0..(project.tile_board_rows.max(8) + 32) {
for x in 0..=(pack_cols - span.x).max(0) {
let mut fits = true;
for dy in 0..span.y.max(1) {
for dx in 0..span.x.max(1) {
if occupied.contains(&(x + dx, y + dy)) {
fits = false;
break;
}
}
if !fits {
break;
}
}
if fits {
return Vec2::new(x, y);
}
}
}
Vec2::zero()
}
fn clone_tile_group(
&self,
project: &mut Project,
group_id: Uuid,
server_ctx: &mut ServerContext,
ctx: &mut TheContext,
) {
let Some(group) = project.tile_groups.get(&group_id).cloned() else {
return;
};
let mut cloned_group = TileGroup::new(group.width, group.height);
cloned_group.name = if group.name.is_empty() {
"New Group".to_string()
} else {
format!("{} Copy", group.name)
};
cloned_group.tags = group.tags.clone();
for member in &group.members {
if let Some(mut tile) = project.tiles.get(&member.tile_id).cloned() {
let new_tile_id = Uuid::new_v4();
tile.id = new_tile_id;
project.tiles.insert(new_tile_id, tile);
cloned_group.members.push(TileGroupMemberRef {
tile_id: new_tile_id,
x: member.x,
y: member.y,
});
}
}
let new_group_id = cloned_group.id;
project.add_tile_group(cloned_group);
if let Some(node_group) = project.tile_node_groups.get(&group_id).cloned() {
let mut cloned_node_group = node_group;
cloned_node_group.group_id = new_group_id;
cloned_node_group.graph_id = Uuid::new_v4();
if !cloned_node_group.graph_name.is_empty() {
cloned_node_group.graph_name = format!("{} Copy", cloned_node_group.graph_name);
}
project.add_tile_node_group(cloned_node_group);
}
let pos = Self::append_board_position(
project,
Vec2::new(group.width as i32, group.height as i32),
);
project.ensure_tile_board_space(pos + Vec2::new(group.width as i32, group.height as i32));
project.set_tile_board_position(TileSource::TileGroup(new_group_id), pos);
server_ctx.curr_tile_source = Some(TileSource::TileGroup(new_group_id));
server_ctx.tile_node_group_id = project
.is_tile_node_group(&new_group_id)
.then_some(new_group_id);
server_ctx.curr_tile_id = project
.tile_groups
.get(&new_group_id)
.and_then(|group| group.members.first())
.map(|member| member.tile_id);
ctx.ui.send(TheEvent::Custom(
TheId::named("Update Tilepicker"),
TheValue::Empty,
));
ctx.ui.send(TheEvent::Custom(
TheId::named("Update Tiles"),
TheValue::Empty,
));
}
}
impl Action for DuplicateTile {
fn new() -> Self
where
Self: Sized,
{
let mut nodeui: TheNodeUI = TheNodeUI::default();
let item = TheNodeUIItem::Markdown("desc".into(), fl!("action_duplicate_tile_desc"));
nodeui.add_item(item);
Self {
id: TheId::named(&fl!("action_duplicate_tile")),
nodeui,
}
}
fn id(&self) -> TheId {
self.id.clone()
}
fn info(&self) -> String {
fl!("action_duplicate_tile_desc")
}
fn role(&self) -> ActionRole {
ActionRole::Dock
}
fn accel(&self) -> Option<TheAccelerator> {
None
}
fn is_applicable(&self, _map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
DOCKMANAGER.read().unwrap().dock == "Tiles"
&& (server_ctx.curr_tile_source.is_some() || server_ctx.curr_tile_id.is_some())
}
fn apply_project(
&self,
project: &mut Project,
_ui: &mut TheUI,
ctx: &mut TheContext,
server_ctx: &mut ServerContext,
) {
if let Some(source) = server_ctx.curr_tile_source {
match source {
TileSource::TileGroup(group_id) => {
self.clone_tile_group(project, group_id, server_ctx, ctx);
return;
}
TileSource::TileGroupMember { group_id, .. } => {
if project.is_tile_node_group(&group_id) {
self.clone_tile_group(project, group_id, server_ctx, ctx);
return;
}
}
_ => {}
}
}
if let Some(tile_id) = server_ctx.curr_tile_id {
if let Some(mut tile) = project.tiles.get(&tile_id).cloned() {
tile.id = Uuid::new_v4();
project.tiles.insert(tile.id, tile);
ctx.ui.send(TheEvent::Custom(
TheId::named("Update Tilepicker"),
TheValue::Empty,
));
ctx.ui.send(TheEvent::Custom(
TheId::named("Update Tiles"),
TheValue::Empty,
));
}
}
}
fn params(&self) -> TheNodeUI {
self.nodeui.clone()
}
fn handle_event(
&mut self,
event: &TheEvent,
_project: &mut Project,
_ui: &mut TheUI,
_ctx: &mut TheContext,
_server_ctx: &mut ServerContext,
) -> bool {
self.nodeui.handle_event(event)
}
}