rustapi/editor_tools/
tile_eraser.rs1use crate::docks::tiles_editor_undo::TileEditorUndoAtom;
2use crate::prelude::*;
3
4pub struct TileEraserTool {
5 id: TheId,
6 before_tile: Option<rusterix::Tile>,
8 before_snapshot: Option<(PixelEditingContext, rusterix::Texture)>,
10 changed: bool,
11}
12
13impl EditorTool for TileEraserTool {
14 fn new() -> Self
15 where
16 Self: Sized,
17 {
18 Self {
19 id: TheId::named_with_id("Tile Eraser Tool", Uuid::new_v4()),
20 before_tile: None,
21 before_snapshot: None,
22 changed: false,
23 }
24 }
25
26 fn id(&self) -> TheId {
27 self.id.clone()
28 }
29
30 fn info(&self) -> String {
31 "Eraser Tool (E). Click and drag to clear pixels to transparent.".to_string()
32 }
33
34 fn icon_name(&self) -> String {
35 "eraser".to_string()
36 }
37
38 fn rgba_view_mode(&self) -> Option<TheRGBAViewMode> {
39 Some(TheRGBAViewMode::TileEditor)
40 }
41
42 fn accel(&self) -> Option<char> {
43 Some('E')
44 }
45
46 fn handle_event(
47 &mut self,
48 event: &TheEvent,
49 ui: &mut TheUI,
50 ctx: &mut TheContext,
51 project: &mut Project,
52 server_ctx: &mut ServerContext,
53 ) -> bool {
54 let mut redraw = false;
55
56 match event {
57 TheEvent::TileEditorClicked(id, coord) => {
58 if id.name == "Tile Editor Dock RGBA Layout View" {
59 match server_ctx.editing_ctx {
60 PixelEditingContext::Tile(tile_id, _) => {
61 if let Some(tile) = project.tiles.get(&tile_id) {
62 self.before_tile = Some(tile.clone());
63 }
64 }
65 _ => {
66 if let Some(texture) =
67 project.get_editing_texture(&server_ctx.editing_ctx)
68 {
69 self.before_snapshot =
70 Some((server_ctx.editing_ctx, texture.clone()));
71 }
72 }
73 }
74
75 self.erase_pixel(*coord, ui, ctx, project, server_ctx);
76 redraw = true;
77 }
78 }
79 TheEvent::TileEditorDragged(id, coord) => {
80 if id.name == "Tile Editor Dock RGBA Layout View" {
81 self.erase_pixel(*coord, ui, ctx, project, server_ctx);
82 redraw = true;
83 }
84 }
85 TheEvent::TileEditorUp(_) => {
86 if self.changed {
87 if matches!(server_ctx.editing_ctx, PixelEditingContext::Tile(..)) {
88 ctx.ui.send(TheEvent::Custom(
89 TheId::named("Update Tilepicker"),
90 TheValue::Empty,
91 ));
92 }
93 ctx.ui.send(TheEvent::Custom(
94 TheId::named("Tile Editor Undo Available"),
95 TheValue::Empty,
96 ));
97 self.changed = false;
98 }
99 }
100 _ => {}
101 }
102
103 redraw
104 }
105
106 fn get_undo_atom(&mut self, project: &Project) -> Option<Box<dyn std::any::Any>> {
107 if let Some(before) = self.before_tile.take() {
108 if let Some(tile) = project.tiles.get(&before.id) {
109 if !tile.textures.is_empty() {
110 let after = tile.clone();
111 let atom = TileEditorUndoAtom::TileEdit(before.id, before, after);
112 return Some(Box::new(atom));
113 }
114 }
115 return None;
116 }
117
118 if let Some((editing_ctx, before)) = self.before_snapshot.take() {
119 if let Some(after) = project.get_editing_texture(&editing_ctx) {
120 let atom = TileEditorUndoAtom::TextureEdit(editing_ctx, before, after.clone());
121 return Some(Box::new(atom));
122 }
123 }
124 None
125 }
126}
127
128impl TileEraserTool {
129 fn erase_pixel(
130 &mut self,
131 pos: Vec2<i32>,
132 ui: &mut TheUI,
133 ctx: &mut TheContext,
134 project: &mut Project,
135 server_ctx: &mut ServerContext,
136 ) {
137 let editing_ctx = server_ctx.editing_ctx;
138
139 if matches!(editing_ctx, PixelEditingContext::AvatarFrame(..))
140 && server_ctx.avatar_anchor_slot != AvatarAnchorEditSlot::None
141 {
142 return;
143 }
144 if let Some(editor) = ui.get_rgba_layout("Tile Editor Dock RGBA Layout")
145 && let Some(rgba_view) = editor.rgba_view_mut().as_rgba_view()
146 {
147 if rgba_view.has_paste_preview() {
148 return;
149 }
150 let selection = rgba_view.selection();
151 if !selection.is_empty() && !selection.contains(&(pos.x, pos.y)) {
152 return;
153 }
154 }
155
156 if let Some(texture) = project.get_editing_texture_mut(&editing_ctx) {
157 let width = texture.width as i32;
158 let height = texture.height as i32;
159
160 if pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height {
161 let current = texture.get_pixel(pos.x as u32, pos.y as u32);
162 if current == [0, 0, 0, 0] {
163 return;
164 }
165 texture.set_pixel(pos.x as u32, pos.y as u32, [0, 0, 0, 0]);
166 texture.generate_normals(true);
167
168 match editing_ctx {
169 PixelEditingContext::Tile(tile_id, _) => {
170 ctx.ui.send(TheEvent::Custom(
171 TheId::named("Tile Updated"),
172 TheValue::Id(tile_id),
173 ));
174 }
175 PixelEditingContext::AvatarFrame(..) => {
176 ctx.ui.send(TheEvent::Custom(
177 TheId::named("Editing Texture Updated"),
178 TheValue::Empty,
179 ));
180 }
181 PixelEditingContext::None => {}
182 }
183
184 self.changed = true;
185 }
186 }
187 }
188}