yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
canvas_interaction_handler.cc
Go to the documentation of this file.
2
3#include <cmath>
4
7#include "imgui/imgui.h"
8
9namespace yaze {
10namespace gui {
11
12namespace {
13
14// Helper function to align position to grid (local version)
15ImVec2 AlignToGridLocal(ImVec2 pos, float grid_step) {
16 return ImVec2(std::floor(pos.x / grid_step) * grid_step,
17 std::floor(pos.y / grid_step) * grid_step);
18}
19
20} // namespace
21
22void CanvasInteractionHandler::Initialize(const std::string& canvas_id) {
23 canvas_id_ = canvas_id;
24 ClearState();
25}
26
28 hover_points_.clear();
29 selected_points_.clear();
30 selected_tiles_.clear();
31 drawn_tile_pos_ = ImVec2(-1, -1);
32 mouse_pos_in_canvas_ = ImVec2(0, 0);
33 selected_tile_pos_ = ImVec2(-1, -1);
34 rect_select_active_ = false;
35}
36
38 ImVec2 canvas_p0, ImVec2 scrolling, float /*global_scale*/,
39 float /*tile_size*/, ImVec2 /*canvas_size*/, bool is_hovered) {
41
42 if (!is_hovered) {
43 hover_points_.clear();
44 return result;
45 }
46
47 const ImGuiIO& imgui_io = ImGui::GetIO();
48 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
50 ImVec2(imgui_io.MousePos.x - origin.x, imgui_io.MousePos.y - origin.y);
51
52 // Update based on current mode - each mode is handled by its specific Draw
53 // method This method exists for future state updates if needed
54 (void)current_mode_; // Suppress unused warning
55
56 return result;
57}
58
60 const gfx::Bitmap& bitmap, ImDrawList* draw_list, ImVec2 canvas_p0,
61 ImVec2 scrolling, float global_scale, float tile_size, bool is_hovered) {
62 const ImGuiIO& imgui_io = ImGui::GetIO();
63 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
64 const ImVec2 mouse_pos(imgui_io.MousePos.x - origin.x,
65 imgui_io.MousePos.y - origin.y);
66 const auto scaled_size = tile_size * global_scale;
67
68 // Clear hover when not hovering
69 if (!is_hovered) {
70 hover_points_.clear();
71 return false;
72 }
73
74 // Reset previous hover
75 hover_points_.clear();
76
77 // Calculate grid-aligned paint position
78 ImVec2 paint_pos = AlignToGridLocal(mouse_pos, scaled_size);
79 mouse_pos_in_canvas_ = paint_pos;
80 auto paint_pos_end =
81 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size);
82
83 hover_points_.push_back(paint_pos);
84 hover_points_.push_back(paint_pos_end);
85
86 // Draw preview of tile at hover position
87 if (bitmap.is_active() && draw_list) {
88 ScopedNearestSampling nearest_sampling(draw_list);
89 draw_list->AddImage(reinterpret_cast<ImTextureID>(bitmap.texture()),
90 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
91 ImVec2(origin.x + paint_pos.x + scaled_size,
92 origin.y + paint_pos.y + scaled_size),
93 ImVec2(0, 0), ImVec2(1, 1),
94 ImGui::GetColorU32(ImVec4(1, 1, 1, 1)));
95 }
96
97 // Check for paint action
98 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) &&
99 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
100 drawn_tile_pos_ = paint_pos;
101 return true;
102 }
103
104 return false;
105}
106
108 gfx::Tilemap& tilemap, int current_tile, ImDrawList* draw_list,
109 ImVec2 canvas_p0, ImVec2 scrolling, float global_scale, bool is_hovered) {
110 const ImGuiIO& imgui_io = ImGui::GetIO();
111 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
112 const ImVec2 mouse_pos(imgui_io.MousePos.x - origin.x,
113 imgui_io.MousePos.y - origin.y);
114
115 // Safety check
116 if (!tilemap.atlas.is_active() || tilemap.tile_size.x <= 0) {
117 return false;
118 }
119
120 const auto scaled_size = tilemap.tile_size.x * global_scale;
121
122 if (!is_hovered) {
123 hover_points_.clear();
124 return false;
125 }
126
127 hover_points_.clear();
128
129 ImVec2 paint_pos = AlignToGrid(mouse_pos, scaled_size);
130 mouse_pos_in_canvas_ = paint_pos;
131
132 hover_points_.push_back(paint_pos);
133 hover_points_.push_back(
134 ImVec2(paint_pos.x + scaled_size, paint_pos.y + scaled_size));
135
136 // Draw tile preview from atlas
137 if (tilemap.atlas.is_active() && tilemap.atlas.texture() && draw_list) {
138 int tiles_per_row = tilemap.atlas.width() / tilemap.tile_size.x;
139 if (tiles_per_row > 0) {
140 int tile_x = (current_tile % tiles_per_row) * tilemap.tile_size.x;
141 int tile_y = (current_tile / tiles_per_row) * tilemap.tile_size.y;
142
143 if (tile_x >= 0 && tile_x < tilemap.atlas.width() && tile_y >= 0 &&
144 tile_y < tilemap.atlas.height()) {
145 ImVec2 uv0 =
146 ImVec2(static_cast<float>(tile_x) / tilemap.atlas.width(),
147 static_cast<float>(tile_y) / tilemap.atlas.height());
148 ImVec2 uv1 = ImVec2(static_cast<float>(tile_x + tilemap.tile_size.x) /
149 tilemap.atlas.width(),
150 static_cast<float>(tile_y + tilemap.tile_size.y) /
151 tilemap.atlas.height());
152
153 ScopedNearestSampling nearest_sampling(draw_list);
154 draw_list->AddImage(
155 reinterpret_cast<ImTextureID>(tilemap.atlas.texture()),
156 ImVec2(origin.x + paint_pos.x, origin.y + paint_pos.y),
157 ImVec2(origin.x + paint_pos.x + scaled_size,
158 origin.y + paint_pos.y + scaled_size),
159 uv0, uv1, ImGui::GetColorU32(ImVec4(1, 1, 1, 1)));
160 }
161 }
162 }
163
164 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) ||
165 ImGui::IsMouseDragging(ImGuiMouseButton_Left)) {
166 drawn_tile_pos_ = paint_pos;
167 return true;
168 }
169
170 return false;
171}
172
174 const ImVec4& color, ImDrawList* draw_list, ImVec2 canvas_p0,
175 ImVec2 scrolling, float global_scale, float tile_size, bool is_hovered) {
176 const ImGuiIO& imgui_io = ImGui::GetIO();
177 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
178 const ImVec2 mouse_pos(imgui_io.MousePos.x - origin.x,
179 imgui_io.MousePos.y - origin.y);
180 auto scaled_tile_size = tile_size * global_scale;
181 static bool is_dragging = false;
182 static ImVec2 start_drag_pos;
183
184 if (!is_hovered) {
185 hover_points_.clear();
186 return false;
187 }
188
189 hover_points_.clear();
190
191 ImVec2 paint_pos = AlignToGrid(mouse_pos, scaled_tile_size);
192 mouse_pos_in_canvas_ = paint_pos;
193
194 // Clamp to canvas bounds (assuming canvas_size from Update)
195 // For now, skip clamping as we don't have canvas_size here
196
197 hover_points_.push_back(paint_pos);
198 hover_points_.push_back(
199 ImVec2(paint_pos.x + scaled_tile_size, paint_pos.y + scaled_tile_size));
200
201 if (draw_list) {
202 draw_list->AddRectFilled(
203 ImVec2(origin.x + paint_pos.x + 1, origin.y + paint_pos.y + 1),
204 ImVec2(origin.x + paint_pos.x + scaled_tile_size,
205 origin.y + paint_pos.y + scaled_tile_size),
206 IM_COL32(color.x * 255, color.y * 255, color.z * 255, 255));
207 }
208
209 if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
210 is_dragging = true;
211 start_drag_pos = paint_pos;
212 }
213
214 if (is_dragging && ImGui::IsMouseReleased(ImGuiMouseButton_Left)) {
215 is_dragging = false;
216 drawn_tile_pos_ = start_drag_pos;
217 return true;
218 }
219
220 return false;
221}
222
223bool CanvasInteractionHandler::DrawTileSelector(ImDrawList* /*draw_list*/,
224 ImVec2 canvas_p0,
225 ImVec2 scrolling,
226 float tile_size,
227 bool is_hovered) {
228 const ImGuiIO& imgui_io = ImGui::GetIO();
229 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
230 const ImVec2 mouse_pos(imgui_io.MousePos.x - origin.x,
231 imgui_io.MousePos.y - origin.y);
232
233 if (is_hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
234 hover_points_.clear();
235 ImVec2 painter_pos = AlignToGridLocal(mouse_pos, tile_size);
236
237 hover_points_.push_back(painter_pos);
238 hover_points_.push_back(
239 ImVec2(painter_pos.x + tile_size, painter_pos.y + tile_size));
240 mouse_pos_in_canvas_ = painter_pos;
241 }
242
243 if (is_hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
244 return true;
245 }
246
247 return false;
248}
249
251 int current_map, ImDrawList* draw_list, ImVec2 canvas_p0, ImVec2 scrolling,
252 float global_scale, float tile_size, bool is_hovered) {
253 if (!is_hovered) {
254 return false;
255 }
256
257 // Create CanvasGeometry from parameters
258 CanvasGeometry geometry;
259 geometry.canvas_p0 = canvas_p0;
260 geometry.scrolling = scrolling;
261 geometry.scaled_size =
262 ImVec2(tile_size * global_scale, tile_size * global_scale);
263 geometry.canvas_sz =
264 ImVec2(tile_size, tile_size); // Will be updated if needed
265
266 // Call new event-based function
268 geometry, current_map, tile_size, draw_list, ImGuiMouseButton_Right);
269
270 // Update internal state for backward compatibility
271 if (event.is_complete) {
272 selected_tiles_ = event.selected_tiles;
273 selected_points_.clear();
274 selected_points_.push_back(event.start_pos);
275 selected_points_.push_back(event.end_pos);
276 rect_select_active_ = true;
277 return true;
278 }
279
280 if (!event.selected_tiles.empty() && !event.is_complete) {
281 // Single tile selection
282 selected_tile_pos_ = event.selected_tiles[0];
283 selected_points_.clear();
284 rect_select_active_ = false;
285 }
286
287 rect_select_active_ = event.is_active;
288
289 return false;
290}
291
292// Helper methods - these are thin wrappers that could be static but kept as
293// instance methods for potential future state access
294ImVec2 CanvasInteractionHandler::AlignPosToGrid(ImVec2 pos, float grid_step) {
295 return AlignToGridLocal(pos, grid_step);
296}
297
299 ImVec2 scrolling) {
300 const ImGuiIO& imgui_io = ImGui::GetIO();
301 const ImVec2 origin(canvas_p0.x + scrolling.x, canvas_p0.y + scrolling.y);
302 return ImVec2(imgui_io.MousePos.x - origin.x, imgui_io.MousePos.y - origin.y);
303}
304
305bool CanvasInteractionHandler::IsMouseClicked(ImGuiMouseButton button) {
306 return ImGui::IsMouseClicked(button);
307}
308
310 return ImGui::IsMouseDoubleClicked(button);
311}
312
313bool CanvasInteractionHandler::IsMouseDragging(ImGuiMouseButton button) {
314 return ImGui::IsMouseDragging(button);
315}
316
317bool CanvasInteractionHandler::IsMouseReleased(ImGuiMouseButton button) {
318 return ImGui::IsMouseReleased(button);
319}
320
321} // namespace gui
322} // namespace yaze
Free functions for canvas interaction handling.
Represents a bitmap image optimized for SNES ROM hacking.
Definition bitmap.h:70
TextureHandle texture() const
Definition bitmap.h:410
bool is_active() const
Definition bitmap.h:414
int height() const
Definition bitmap.h:399
int width() const
Definition bitmap.h:398
bool DrawSelectRect(int current_map, ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, float global_scale, float tile_size, bool is_hovered)
Draw rectangle selector (multi-tile selection)
void ClearState()
Clear all interaction state.
bool DrawTilemapPainter(gfx::Tilemap &tilemap, int current_tile, ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, float global_scale, bool is_hovered)
Draw tilemap painter (preview + interaction)
ImVec2 AlignPosToGrid(ImVec2 pos, float grid_step)
TileInteractionResult Update(ImVec2 canvas_p0, ImVec2 scrolling, float global_scale, float tile_size, ImVec2 canvas_size, bool is_hovered)
Update interaction state (call once per frame)
bool DrawTileSelector(ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, float tile_size, bool is_hovered)
Draw tile selector (single tile selection)
bool DrawSolidTilePainter(const ImVec4 &color, ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, float global_scale, float tile_size, bool is_hovered)
Draw solid color painter.
bool IsMouseDoubleClicked(ImGuiMouseButton button)
bool DrawTilePainter(const gfx::Bitmap &bitmap, ImDrawList *draw_list, ImVec2 canvas_p0, ImVec2 scrolling, float global_scale, float tile_size, bool is_hovered)
Draw tile painter (preview + interaction)
ImVec2 GetMousePosition(ImVec2 canvas_p0, ImVec2 scrolling)
void Initialize(const std::string &canvas_id)
Initialize the interaction handler.
RAII scope that samples the images drawn inside it with nearest-neighbor filtering.
ImVec2 AlignToGrid(ImVec2 pos, float grid_step)
Align position to grid.
RectSelectionEvent HandleRectangleSelection(const CanvasGeometry &geometry, int current_map, float tile_size, ImDrawList *draw_list, ImGuiMouseButton mouse_button)
Handle rectangle selection interaction.
int y
Y coordinate or height.
Definition tilemap.h:21
int x
X coordinate or width.
Definition tilemap.h:20
Tilemap structure for SNES tile-based graphics management.
Definition tilemap.h:118
Pair tile_size
Size of individual tiles (8x8 or 16x16)
Definition tilemap.h:123
Bitmap atlas
Master bitmap containing all tiles.
Definition tilemap.h:119
Canvas geometry calculated per-frame.
Event payload for rectangle selection operations.
Result of a tile interaction operation.