yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tile_selector_widget.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <cmath>
6#include <cstdio>
7
8#include "absl/strings/str_format.h"
11#include "app/gui/core/icons.h"
12
13namespace yaze::gui {
14
15namespace {
16
17std::string_view TrimAsciiWhitespace(std::string_view s) {
18 while (!s.empty() && (s.front() == ' ' || s.front() == '\t' ||
19 s.front() == '\n' || s.front() == '\r')) {
20 s.remove_prefix(1);
21 }
22 while (!s.empty() && (s.back() == ' ' || s.back() == '\t' ||
23 s.back() == '\n' || s.back() == '\r')) {
24 s.remove_suffix(1);
25 }
26 return s;
27}
28
29// Parse tile-id text using hex by default, with explicit decimal support via
30// "d:<value>".
31bool ParseTileIdText(std::string_view input, int* out_value) {
32 if (!out_value) {
33 return false;
34 }
35
36 std::string_view trimmed = TrimAsciiWhitespace(input);
37 if (trimmed.empty()) {
38 return false;
39 }
40
41 bool decimal_mode = false;
42 if (trimmed.size() >= 2 && (trimmed[0] == 'd' || trimmed[0] == 'D') &&
43 trimmed[1] == ':') {
44 decimal_mode = true;
45 trimmed.remove_prefix(2);
46 } else if (trimmed.size() >= 2 && trimmed[0] == '0' &&
47 (trimmed[1] == 'x' || trimmed[1] == 'X')) {
48 trimmed.remove_prefix(2);
49 }
50
51 if (trimmed.empty()) {
52 return false;
53 }
54
55 std::string text(trimmed);
56 unsigned int parsed = 0;
57 char trailing = '\0';
58 const char* format = decimal_mode ? "%u%c" : "%x%c";
59 if (std::sscanf(text.c_str(), format, &parsed, &trailing) != 1) {
60 return false;
61 }
62
63 *out_value = static_cast<int>(parsed);
64 return true;
65}
66
67} // namespace
68
70 : config_(),
71 total_tiles_(config_.total_tiles),
72 widget_id_(std::move(widget_id)) {}
73
74TileSelectorWidget::TileSelectorWidget(std::string widget_id, Config config)
75 : config_(config),
76 total_tiles_(config.total_tiles),
77 widget_id_(std::move(widget_id)) {
79}
80
82 canvas_ = canvas;
83}
84
85void TileSelectorWidget::SetTileCount(int total_tiles) {
86 const int previous_total = total_tiles_;
87 total_tiles_ = std::max(total_tiles, 0);
88 if (display_order_active_ && total_tiles_ != previous_total) {
90 }
93 }
97 }
98}
99
100void TileSelectorWidget::SetDisplayScale(float display_scale) {
101 if (display_scale <= 0.0f || config_.tile_size <= 0) {
102 return;
103 }
104 // Whole-pixel cells keep the grid, highlight and hit testing on the atlas's
105 // tile edges at every row; fractional cells drift as the grid scrolls.
106 const float tile_size = static_cast<float>(config_.tile_size);
107 const float cell = std::max(1.0f, std::floor(tile_size * display_scale));
108 config_.display_scale = cell / tile_size;
109}
110
114
115void TileSelectorWidget::SetTilesPerRow(int tiles_per_row) {
116 if (tiles_per_row > 0) {
118 }
119}
120
122 if (IsValidTileId(tile_id)) {
123 selected_tile_id_ = tile_id;
125 }
126}
127
128namespace {
129
131 const float cell =
132 std::max(1.0f, std::floor(config.tile_size * config.display_scale));
133 return config.tiles_per_row * cell + config.draw_offset.x * 2;
134}
135
136} // namespace
137
138void TileSelectorWidget::SetDisplayOrder(std::vector<int> order) {
139 cell_of_tile_.assign(total_tiles_, -1);
140 display_order_.clear();
141 display_order_.reserve(order.size());
142 for (const int tile_id : order) {
143 if (IsValidTileId(tile_id) && cell_of_tile_[tile_id] < 0) {
144 cell_of_tile_[tile_id] = static_cast<int>(display_order_.size());
145 display_order_.push_back(tile_id);
146 }
147 }
149}
150
156
158 return display_order_active_ ? static_cast<int>(display_order_.size())
159 : total_tiles_;
160}
161
163 if (cell < 0 || cell >= CellCount()) {
164 return -1;
165 }
166 return display_order_active_ ? display_order_[cell] : cell;
167}
168
169int TileSelectorWidget::CellOfTile(int tile_id) const {
170 if (!IsValidTileId(tile_id)) {
171 return -1;
172 }
173 return display_order_active_ ? cell_of_tile_[tile_id] : tile_id;
174}
175
177 const int num_rows =
179 return ImVec2(GridWidthFor(config_),
180 num_rows * TileCellSize() + config_.draw_offset.y * 2);
181}
182
184 // ImGuiStyle's default; used when no context exists (layout unit tests).
185 constexpr float kImGuiDefaultScrollbarSize = 14.0f;
186 return ImGui::GetCurrentContext() ? ImGui::GetStyle().ScrollbarSize
187 : kImGuiDefaultScrollbarSize;
188}
189
191 float scrollbar_size) {
192 // Filter-bar controls wrap compactly when the available width is tight
193 // (see DrawFilterBar); they must not inflate the preferred width.
194 return GridWidthFor(config) + scrollbar_size;
195}
196
200
202 bool atlas_ready) {
203 RenderResult result;
204
205 if (!canvas_) {
206 return result;
207 }
208
209 const float tile_display_size = TileCellSize();
210 const ImVec2 content_size = GetGridContentSize();
211
212 // Set content size for ImGui child window (must be called before
213 // DrawBackground)
214 ImGui::SetCursorPos(ImVec2(0, 0));
215 ImGui::Dummy(content_size);
216 ImGui::SetCursorPos(ImVec2(0, 0));
217
218 // Handle pending scroll (deferred from ScrollToTile call outside render
219 // context)
220 if (pending_scroll_tile_id_ >= 0) {
222 const ImVec2 target = TileOrigin(pending_scroll_tile_id_);
224 const ImVec2 window_size = ImGui::GetWindowSize();
225 float scroll_x =
226 target.x - (window_size.x / 2.0f) + (tile_display_size / 2.0f);
227 float scroll_y =
228 target.y - (window_size.y / 2.0f) + (tile_display_size / 2.0f);
229 scroll_x = std::max(0.0f, scroll_x);
230 scroll_y = std::max(0.0f, scroll_y);
231 ImGui::SetScrollX(scroll_x);
232 ImGui::SetScrollY(scroll_y);
233 }
234 }
235 pending_scroll_tile_id_ = -1; // Clear pending scroll
236 }
237
239
240 if (atlas_ready && atlas.is_active()) {
242 DrawOrderedTiles(atlas, tile_display_size);
243 } else {
244 canvas_->DrawBitmap(atlas, static_cast<int>(config_.draw_offset.x),
245 static_cast<int>(config_.draw_offset.y),
247 }
248
249 result = HandleInteraction(tile_display_size);
250
251 // Hover tooltip: show tile ID and zoomed preview
252 if (config_.show_hover_tooltip && ImGui::IsItemHovered()) {
253 int hovered_tile = ResolveTileAtCursor(tile_display_size);
254 if (IsValidTileId(hovered_tile)) {
255 ImGui::BeginTooltip();
256 ImGui::Text(tr("Tile %d (0x%03X)"), hovered_tile, hovered_tile);
257 if (hover_detail_) {
258 const std::string detail = hover_detail_(hovered_tile);
259 if (!detail.empty()) {
260 ImGui::TextUnformatted(detail.c_str());
261 }
262 }
263
264 // Extract and draw a zoomed preview of the hovered tile
265 int tile_col = hovered_tile % config_.tiles_per_row;
266 int tile_row = hovered_tile / config_.tiles_per_row;
267 int src_x = tile_col * config_.tile_size;
268 int src_y = tile_row * config_.tile_size;
269
270 // Use ImGui texture coords for the atlas to show the tile zoomed
271 auto* texture_id = atlas.texture();
272 if (texture_id != nullptr) {
273 float atlas_w = static_cast<float>(atlas.width());
274 float atlas_h = static_cast<float>(atlas.height());
275 if (atlas_w > 0 && atlas_h > 0) {
276 ImVec2 uv0(src_x / atlas_w, src_y / atlas_h);
277 ImVec2 uv1((src_x + config_.tile_size) / atlas_w,
278 (src_y + config_.tile_size) / atlas_h);
279 float preview_size = config_.tile_size * 4.0f;
280 ImGui::Image((ImTextureID)(intptr_t)texture_id,
281 ImVec2(preview_size, preview_size), uv0, uv1);
282 }
283 }
284
285 ImGui::EndTooltip();
286 }
287 }
288
290 DrawTileIdLabels(tile_display_size);
291 }
292
293 DrawGridLines(tile_display_size);
294 DrawHighlight(tile_display_size);
295 }
296
299
300 return result;
301}
302
304 CanvasMenuItem item("Copy Tile ID", ICON_MD_CONTENT_COPY, [this]() {
306 ImGui::SetClipboardText(
307 absl::StrFormat("0x%03X", selected_tile_id_).c_str());
308 }
309 });
310 item.enabled_condition = [this]() {
312 };
313 return item;
314}
315
317 float tile_display_size) {
318 RenderResult result;
319
320 if (!ImGui::IsItemHovered()) {
321 return result;
322 }
323
324 const bool clicked = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
325 const bool double_clicked =
326 ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left);
327 const bool right_clicked = ImGui::IsMouseClicked(ImGuiMouseButton_Right);
328
329 if (clicked || double_clicked || right_clicked) {
330 const int hovered_tile = ResolveTileAtCursor(tile_display_size);
331 if (IsValidTileId(hovered_tile) && IsInFilterRange(hovered_tile)) {
332 result.tile_clicked = clicked;
333 result.tile_double_clicked = double_clicked;
334 result.tile_right_clicked = right_clicked;
335 if (hovered_tile != selected_tile_id_) {
336 selected_tile_id_ = hovered_tile;
337 result.selection_changed = true;
338 }
340 }
341 }
342
343 // Emit drag source for the selected tile when dragging is enabled
345 result.tile_dragging =
347 }
348
349 return result;
350}
351
352int TileSelectorWidget::ResolveTileAtCursor(float tile_display_size) const {
353 if (!canvas_ || tile_display_size <= 0.0f) {
354 return -1;
355 }
356
357 const ImVec2 screen_pos = ImGui::GetIO().MousePos;
358 const ImVec2 origin = canvas_->zero_point();
359 const ImVec2 scroll = canvas_->scrolling();
360
361 // Convert screen position to canvas content position (accounting for scroll)
362 ImVec2 local =
363 ImVec2(screen_pos.x - origin.x - config_.draw_offset.x - scroll.x,
364 screen_pos.y - origin.y - config_.draw_offset.y - scroll.y);
365
366 if (local.x < 0.0f || local.y < 0.0f) {
367 return -1;
368 }
369
370 const int column = static_cast<int>(local.x / tile_display_size);
371 const int row = static_cast<int>(local.y / tile_display_size);
372 if (column >= config_.tiles_per_row) {
373 return -1;
374 }
375
376 return TileAtCell(row * config_.tiles_per_row + column);
377}
378
380 const ImVec2 origin = canvas_->zero_point();
381 const ImVec2 scroll = canvas_->scrolling();
382 return ImVec2(origin.x + scroll.x + config_.draw_offset.x,
383 origin.y + scroll.y + config_.draw_offset.y);
384}
385
387 float tile_display_size) const {
388 ImDrawList* draw_list = canvas_ ? canvas_->draw_list() : nullptr;
389 if (!draw_list || tile_display_size <= 0.0f || config_.tiles_per_row <= 0) {
390 return {0, 0};
391 }
392 const int num_rows =
394 const float grid_top = GridScreenOrigin().y;
395 const int first_row = std::clamp(
396 static_cast<int>(std::floor((draw_list->GetClipRectMin().y - grid_top) /
397 tile_display_size)),
398 0, num_rows);
399 const int last_row = std::clamp(
400 static_cast<int>(std::ceil((draw_list->GetClipRectMax().y - grid_top) /
401 tile_display_size)),
402 0, num_rows);
403 return {first_row, last_row};
404}
405
407 float tile_display_size) const {
408 ImDrawList* draw_list = canvas_ ? canvas_->draw_list() : nullptr;
409 if (!draw_list || atlas.texture() == nullptr || atlas.width() <= 0 ||
410 atlas.height() <= 0 || config_.tile_size <= 0) {
411 return;
412 }
413 const int atlas_columns = std::max(1, atlas.width() / config_.tile_size);
414 const float atlas_w = static_cast<float>(atlas.width());
415 const float atlas_h = static_cast<float>(atlas.height());
416 const ImVec2 grid_min = GridScreenOrigin();
417 const auto [first_row, last_row] = VisibleRows(tile_display_size);
418 const int last_cell = std::min(last_row * config_.tiles_per_row, CellCount());
419 for (int cell = first_row * config_.tiles_per_row; cell < last_cell; ++cell) {
420 const int tile_id = display_order_[cell];
421 const float src_x =
422 static_cast<float>((tile_id % atlas_columns) * config_.tile_size);
423 const float src_y =
424 static_cast<float>((tile_id / atlas_columns) * config_.tile_size);
425 const ImVec2 dst_min(
426 grid_min.x + (cell % config_.tiles_per_row) * tile_display_size,
427 grid_min.y + (cell / config_.tiles_per_row) * tile_display_size);
428 draw_list->AddImage(
429 (ImTextureID)(intptr_t)atlas.texture(), dst_min,
430 ImVec2(dst_min.x + tile_display_size, dst_min.y + tile_display_size),
431 ImVec2(src_x / atlas_w, src_y / atlas_h),
432 ImVec2((src_x + config_.tile_size) / atlas_w,
433 (src_y + config_.tile_size) / atlas_h));
434 }
435}
436
437void TileSelectorWidget::DrawGridLines(float tile_display_size) const {
438 ImDrawList* draw_list = canvas_ ? canvas_->draw_list() : nullptr;
439 if (!draw_list || !canvas_->GetConfig().enable_grid ||
440 tile_display_size <= 0.0f || config_.tiles_per_row <= 0) {
441 return;
442 }
443
444 const int num_rows =
446 const ImVec2 grid_min = GridScreenOrigin();
447 const ImVec2 grid_max(grid_min.x + config_.tiles_per_row * tile_display_size,
448 grid_min.y + num_rows * tile_display_size);
449
450 // Only the rows inside the scrolling child's clip rect are emitted.
451 const auto [first_row, last_row] = VisibleRows(tile_display_size);
452 if (first_row >= last_row) {
453 return;
454 }
455
456 constexpr ImU32 kGridColor = IM_COL32(200, 200, 200, 50);
457 const float top = grid_min.y + first_row * tile_display_size;
458 const float bottom = grid_min.y + last_row * tile_display_size;
459 for (int column = 0; column <= config_.tiles_per_row; ++column) {
460 const float x = grid_min.x + column * tile_display_size;
461 draw_list->AddLine(ImVec2(x, top), ImVec2(x, bottom), kGridColor);
462 }
463 for (int row = first_row; row <= last_row; ++row) {
464 const float y = grid_min.y + row * tile_display_size;
465 draw_list->AddLine(ImVec2(grid_min.x, y), ImVec2(grid_max.x, y),
466 kGridColor);
467 }
468}
469
470void TileSelectorWidget::DrawHighlight(float tile_display_size) const {
471 ImDrawList* draw_list = canvas_ ? canvas_->draw_list() : nullptr;
472 const int cell = CellOfTile(selected_tile_id_);
473 if (!draw_list || cell < 0) {
474 return;
475 }
476
477 const int column = cell % config_.tiles_per_row;
478 const int row = cell / config_.tiles_per_row;
479 const ImVec2 grid_min = GridScreenOrigin();
480 const ImVec2 tile_min(grid_min.x + column * tile_display_size,
481 grid_min.y + row * tile_display_size);
482 const ImVec2 tile_max(tile_min.x + tile_display_size,
483 tile_min.y + tile_display_size);
484 draw_list->AddRect(tile_min, tile_max,
485 ImGui::ColorConvertFloat4ToU32(config_.highlight_color),
486 0.0f, 0, 1.5f);
487}
488
490 // Future enhancement: draw ImGui text overlay with tile indices.
491}
492
493void TileSelectorWidget::ScrollToTile(int tile_id, bool use_imgui_scroll) {
494 if (!canvas_ || !IsValidTileId(tile_id)) {
495 return;
496 }
497
498 // Defer scroll until next render (when we're in the correct ImGui window
499 // context)
500 pending_scroll_tile_id_ = tile_id;
501 pending_scroll_use_imgui_ = use_imgui_scroll;
502}
503
504ImVec2 TileSelectorWidget::TileOrigin(int tile_id) const {
505 const int cell = CellOfTile(tile_id);
506 if (cell < 0) {
507 return ImVec2(-1, -1);
508 }
509 const float tile_display_size = TileCellSize();
510 const int column = cell % config_.tiles_per_row;
511 const int row = cell / config_.tiles_per_row;
512 return ImVec2(config_.draw_offset.x + column * tile_display_size,
513 config_.draw_offset.y + row * tile_display_size);
514}
515
517 std::string_view input) {
518 int tile_id = -1;
519 if (!ParseTileIdText(input, &tile_id)) {
521 return last_jump_result_;
522 }
523
524 if (!IsValidTileId(tile_id)) {
526 return last_jump_result_;
527 }
528
529 selected_tile_id_ = tile_id;
530 ScrollToTile(tile_id, true);
532 return last_jump_result_;
533}
534
536 ImGui::PushID(widget_id_.c_str());
537 const bool compact = ImGui::GetContentRegionAvail().x < 115.0f;
538 const char* label =
540 : (filter_range_active_ ? "Find / Filter*" : "Find / Filter");
541 if (ImGui::Button(label)) {
542 ImGui::OpenPopup("TileLookup");
543 }
545 "button", ImGui::GetItemID());
546 if (ImGui::IsItemHovered()) {
547 ImGui::SetTooltip("Jump to a tile ID or filter a range%s",
548 filter_range_active_ ? " (range filter active)" : "");
549 }
550 bool jumped = false;
551 ImGui::SetNextWindowSizeConstraints(ImVec2(300.0f, 0.0f),
552 ImVec2(380.0f, 500.0f));
553 if (ImGui::BeginPopup("TileLookup")) {
554 jumped = DrawFilterControls();
555 if (jumped) {
556 ImGui::CloseCurrentPopup();
557 }
558 ImGui::EndPopup();
559 }
560 ImGui::PopID();
561 return jumped;
562}
563
565 bool jumped = false;
566 const int max_tile_id = GetMaxTileId();
567 const float available_width =
568 std::max(ImGui::GetContentRegionAvail().x, 1.0f);
569 const bool compact_layout = available_width < 420.0f;
570 const float jump_input_width =
571 compact_layout ? std::clamp(available_width * 0.28f, 56.0f, 88.0f)
572 : 64.0f;
573 const float range_input_width =
574 compact_layout
575 ? std::clamp((available_width - 110.0f) * 0.5f, 56.0f, 88.0f)
576 : 64.0f;
577
578 constexpr ImGuiInputTextFlags kInputFlags =
579 ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll;
580
581 ImGui::PushID(widget_id_.c_str());
582
583 // Jump-to-ID input
584 ImGui::AlignTextToFramePadding();
585 ImGui::TextUnformatted(tr("Go:"));
586 ImGui::SameLine();
587
588 ImGui::SetNextItemWidth(jump_input_width);
589 if (ImGui::InputText("##TileFilterID", filter_buf_, sizeof(filter_buf_),
590 kInputFlags)) {
593 jumped = true;
594 break;
597 break;
598 }
599 }
601 "input", ImGui::GetItemID());
602 if (ImGui::IsItemHovered()) {
603 ImGui::SetTooltip(
604 tr("Enter tile ID and press Enter:\n"
605 "hex: 1A or 0x1A\n"
606 "decimal: d:26"));
607 }
608
609 ImGui::SameLine();
610 ImGui::TextDisabled(tr("/ 0x%03X"), max_tile_id);
611
613 ImGui::TextUnformatted(tr("Invalid tile ID (hex or d:decimal)"));
615 ImGui::Text(tr("Out of range (max: 0x%03X)"), max_tile_id);
616 }
617
618 // Range filter inputs
619 if (!compact_layout) {
620 ImGui::SameLine(0, 12.0f);
621 }
622 ImGui::AlignTextToFramePadding();
623 ImGui::TextUnformatted(tr("Range:"));
624 ImGui::SameLine();
625
626 bool range_changed = false;
627 ImGui::SetNextItemWidth(range_input_width);
628 if (ImGui::InputText("##RangeMin", filter_min_buf_, sizeof(filter_min_buf_),
629 kInputFlags)) {
630 range_changed = true;
631 }
633 "input", ImGui::GetItemID());
634 if (ImGui::IsItemHovered()) {
635 ImGui::SetTooltip(
636 tr("Min tile ID. Press Enter to apply.\n"
637 "hex: 1A or 0x1A\n"
638 "decimal: d:26"));
639 }
640 ImGui::SameLine();
641 ImGui::TextUnformatted("-");
642 ImGui::SameLine();
643 ImGui::SetNextItemWidth(range_input_width);
644 if (ImGui::InputText("##RangeMax", filter_max_buf_, sizeof(filter_max_buf_),
645 kInputFlags)) {
646 range_changed = true;
647 }
649 "input", ImGui::GetItemID());
650 if (ImGui::IsItemHovered()) {
651 ImGui::SetTooltip(
652 tr("Max tile ID. Press Enter to apply.\n"
653 "hex: 1A or 0x1A\n"
654 "decimal: d:26"));
655 }
656 if (!compact_layout) {
657 ImGui::SameLine();
658 ImGui::TextDisabled(tr("(hex, d:dec)"));
659 } // Compact: the input tooltips carry the format hint.
660
661 if (range_changed) {
662 int parsed_min = 0;
663 int parsed_max = 0;
664 bool has_min = ParseTileIdText(filter_min_buf_, &parsed_min);
665 bool has_max = ParseTileIdText(filter_max_buf_, &parsed_max);
666
667 if (has_min && has_max && parsed_min <= parsed_max) {
668 SetRangeFilter(parsed_min, parsed_max);
669 filter_range_error_ = false;
671 filter_out_of_range_ = false;
673 } else {
674 // SetRangeFilter returned early: both values exceeded total_tiles_.
676 }
677 } else if (!has_min && !has_max) {
679 filter_range_error_ = false;
680 filter_out_of_range_ = false;
681 } else if (has_min && has_max && parsed_min > parsed_max) {
682 // Invalid range: min must be ≤ max
683 filter_range_error_ = true;
684 filter_out_of_range_ = false;
685 }
686 }
687
688 // Clear button when range is active
690 if (!compact_layout) {
691 ImGui::SameLine();
692 }
693 if (ImGui::SmallButton(tr("X##ClearRange"))) {
695 filter_min_buf_[0] = '\0';
696 filter_max_buf_[0] = '\0';
697 filter_range_error_ = false;
698 }
699 if (ImGui::IsItemHovered()) {
700 ImGui::SetTooltip(tr("Clear range filter"));
701 }
702 }
703
704 // Validation feedback (shown inline after the filter inputs)
706 if (!compact_layout) {
707 ImGui::SameLine(0, 8.0f);
708 }
709 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
710 tr("Min must be <= Max"));
711 } else if (filter_out_of_range_) {
712 if (!compact_layout) {
713 ImGui::SameLine(0, 8.0f);
714 }
715 ImGui::TextColored(ImVec4(1.0f, 0.4f, 0.4f, 1.0f),
716 tr("Out of range (max: 0x%03X)"), GetMaxTileId());
717 } else if (filter_range_active_) {
718 int range_count = filter_range_max_ - filter_range_min_ + 1;
719 if (range_count <= 0) {
720 if (!compact_layout) {
721 ImGui::SameLine(0, 8.0f);
722 }
723 ImGui::TextDisabled(tr("(no tiles in range)"));
724 }
725 }
726
727 ImGui::PopID();
728
729 return jumped;
730}
731
732void TileSelectorWidget::SetRangeFilter(int min_id, int max_id) {
733 if (min_id < 0)
734 min_id = 0;
735 if (max_id >= total_tiles_)
736 max_id = total_tiles_ - 1;
737 if (min_id > max_id)
738 return;
739
741 filter_range_min_ = min_id;
742 filter_range_max_ = max_id;
743 filter_range_error_ = false;
744}
745
753
754bool TileSelectorWidget::IsValidTileId(int tile_id) const {
755 return tile_id >= 0 && tile_id < total_tiles_;
756}
757
758bool TileSelectorWidget::IsInFilterRange(int tile_id) const {
760 return true;
761 return tile_id >= filter_range_min_ && tile_id <= filter_range_max_;
762}
763
764} // namespace yaze::gui
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
Modern, robust canvas for drawing and manipulating graphics.
Definition canvas.h:64
void DrawBitmap(Bitmap &bitmap, int border_offset, float scale)
Definition canvas.cc:1295
void DrawContextMenu()
Definition canvas.cc:824
auto draw_list() const
Definition canvas.h:363
auto zero_point() const
Definition canvas.h:364
CanvasConfig & GetConfig()
Definition canvas.h:243
void DrawBackground(ImVec2 canvas_size=ImVec2(0, 0), std::optional< ImVec2 > viewport_size=std::nullopt)
Definition canvas.cc:703
auto scrolling() const
Definition canvas.h:366
static float PreferredViewportWidth(const Config &config, float scrollbar_size)
JumpToTileResult JumpToTileFromInput(std::string_view input)
int CellOfTile(int tile_id) const
Grid cell showing a tile, or -1 when the display order hides it.
void SetDisplayScale(float display_scale)
Snaps down so each tile cell is a whole number of screen pixels.
RenderResult Render(gfx::Bitmap &atlas, bool atlas_ready)
std::function< std::string(int)> hover_detail_
void DrawGridLines(float tile_display_size) const
int ResolveTileAtCursor(float tile_display_size) const
static float CurrentScrollbarSize()
Current style scrollbar size, or ImGui's default without a context.
bool IsValidTileId(int tile_id) const
void SetRangeFilter(int min_id, int max_id)
ImVec2 TileOrigin(int tile_id) const
void DrawTileIdLabels(float tile_display_size) const
RenderResult HandleInteraction(float tile_display_size)
void SetDisplayOrder(std::vector< int > order)
void ScrollToTile(int tile_id, bool use_imgui_scroll=true)
CanvasMenuItem CopyTileIdMenuItem() const
void DrawHighlight(float tile_display_size) const
bool IsInFilterRange(int tile_id) const
void DrawOrderedTiles(gfx::Bitmap &atlas, float tile_display_size) const
int TileAtCell(int cell) const
Tile shown in a grid cell, or -1 past the last cell.
void SetTilesPerRow(int tiles_per_row)
TileSelectorWidget(std::string widget_id)
std::pair< int, int > VisibleRows(float tile_display_size) const
void RegisterWidget(const std::string &full_path, const std::string &type, ImGuiID imgui_id, const std::string &description="", const WidgetMetadata &metadata=WidgetMetadata())
static WidgetIdRegistry & Instance()
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_CONTENT_COPY
Definition icons.h:465
#define ICON_MD_FILTER_ALT
Definition icons.h:761
bool ParseTileIdText(std::string_view input, int *out_value)
float GridWidthFor(const TileSelectorWidget::Config &config)
Graphical User Interface (GUI) components for the application.
bool BeginTileDragSource(int tile_id, int map_id)
Definition drag_drop.h:65
Declarative menu item definition.
Definition canvas_menu.h:64
std::function< bool()> enabled_condition
Definition canvas_menu.h:85