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