rustapi/editor_tools/
tile_draw.rs1use crate::docks::tiles_editor_undo::TileEditorUndoAtom;
2use crate::prelude::*;
3
4pub struct TileDrawTool {
5 id: TheId,
6 before_tile: Option<rusterix::Tile>,
8 before_snapshot: Option<(PixelEditingContext, rusterix::Texture)>,
10 changed: bool,
11}
12
13impl EditorTool for TileDrawTool {
14 fn new() -> Self
15 where
16 Self: Sized,
17 {
18 Self {
19 id: TheId::named_with_id("Tile Draw 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 "Draw Tool (D). Click and drag to draw pixels with the selected palette color.".to_string()
32 }
33
34 fn icon_name(&self) -> String {
35 "paint-brush".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('D')
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 {
61 PixelEditingContext::Tile(tile_id, _) => {
62 if let Some(tile) = project.tiles.get(&tile_id) {
63 self.before_tile = Some(tile.clone());
64 }
65 }
66 _ => {
67 if let Some(texture) =
68 project.get_editing_texture(&server_ctx.editing_ctx)
69 {
70 self.before_snapshot =
71 Some((server_ctx.editing_ctx, texture.clone()));
72 }
73 }
74 }
75
76 self.draw_pixel(*coord, ui, ctx, project, server_ctx);
77 redraw = true;
78 }
79 }
80 TheEvent::TileEditorDragged(id, coord) => {
81 if id.name == "Tile Editor Dock RGBA Layout View" {
82 self.draw_pixel(*coord, ui, ctx, project, server_ctx);
83 redraw = true;
84 }
85 }
86 TheEvent::TileEditorUp(_) => {
87 if self.changed {
88 if matches!(server_ctx.editing_ctx, PixelEditingContext::Tile(..)) {
90 ctx.ui.send(TheEvent::Custom(
91 TheId::named("Update Tilepicker"),
92 TheValue::Empty,
93 ));
94 }
95 ctx.ui.send(TheEvent::Custom(
96 TheId::named("Tile Editor Undo Available"),
97 TheValue::Empty,
98 ));
99 self.changed = false;
100 }
101 }
102 _ => {}
103 }
104
105 redraw
106 }
107
108 fn get_undo_atom(&mut self, project: &Project) -> Option<Box<dyn std::any::Any>> {
109 if let Some(before) = self.before_tile.take() {
111 if let Some(tile) = project.tiles.get(&before.id) {
112 if !tile.textures.is_empty() {
113 let after = tile.clone();
114 let atom = TileEditorUndoAtom::TileEdit(before.id, before, after);
115 return Some(Box::new(atom));
116 }
117 }
118 return None;
119 }
120
121 if let Some((editing_ctx, before)) = self.before_snapshot.take() {
123 if let Some(after) = project.get_editing_texture(&editing_ctx) {
124 let atom = TileEditorUndoAtom::TextureEdit(editing_ctx, before, after.clone());
125 return Some(Box::new(atom));
126 }
127 }
128 None
129 }
130}
131
132impl TileDrawTool {
133 fn draw_pixel(
134 &mut self,
135 pos: Vec2<i32>,
136 ui: &mut TheUI,
137 ctx: &mut TheContext,
138 project: &mut Project,
139 server_ctx: &mut ServerContext,
140 ) {
141 let editing_ctx = server_ctx.editing_ctx;
142
143 let color_array = if ui.shift {
145 Some([0, 0, 0, 0])
146 } else {
147 editing_ctx.get_draw_color(
148 &project.palette,
149 server_ctx.palette_opacity,
150 server_ctx.body_marker_color,
151 )
152 };
153
154 if let Some(color_array) = color_array {
155 if matches!(editing_ctx, PixelEditingContext::AvatarFrame(..))
156 && server_ctx.avatar_anchor_slot != AvatarAnchorEditSlot::None
157 {
158 return;
159 }
160 if let Some(editor) = ui.get_rgba_layout("Tile Editor Dock RGBA Layout")
161 && let Some(rgba_view) = editor.rgba_view_mut().as_rgba_view()
162 {
163 if rgba_view.has_paste_preview() {
164 return;
165 }
166 let selection = rgba_view.selection();
167 if !selection.is_empty() && !selection.contains(&(pos.x, pos.y)) {
168 return;
169 }
170 }
171
172 if let Some(texture) = project.get_editing_texture_mut(&editing_ctx) {
173 let width = texture.width as i32;
174 let height = texture.height as i32;
175
176 if pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height {
177 texture.set_pixel(pos.x as u32, pos.y as u32, color_array);
178 texture.generate_normals(true);
179
180 match editing_ctx {
182 PixelEditingContext::Tile(tile_id, _) => {
183 ctx.ui.send(TheEvent::Custom(
184 TheId::named("Tile Updated"),
185 TheValue::Id(tile_id),
186 ));
187 }
188 PixelEditingContext::AvatarFrame(..) => {
189 ctx.ui.send(TheEvent::Custom(
190 TheId::named("Editing Texture Updated"),
191 TheValue::Empty,
192 ));
193 }
194 PixelEditingContext::None => {}
195 }
196
197 self.changed = true;
198 }
199 }
200 }
201 }
202}