48 bool has_selection,
bool scratch_has_data,
Rom* rom,
52 if (!overworld || !overworld->
is_loaded() || !window_manager || !rom)
60 current_map, game_state);
61 const auto apply_property_edit =
64 return absl::FailedPreconditionError(
65 "Overworld property edit callback is not configured");
71 {{ImGuiStyleVar_FramePadding, ImVec2(6.0f, 5.0f)},
72 {ImGuiStyleVar_CellPadding, ImVec2(6.0f, 5.0f)}});
73 ImGui::PushID(
"OverworldToolbar");
76 if (BeginTable(
"OverworldCanvasToolbar", 8,
77 ImGuiTableFlags_Borders | ImGuiTableFlags_SizingStretchProp,
79 ImGui::TableSetupColumn(
"World", ImGuiTableColumnFlags_WidthFixed,
81 ImGui::TableSetupColumn(
"Map", ImGuiTableColumnFlags_WidthFixed,
83 ImGui::TableSetupColumn(
"Area Size", ImGuiTableColumnFlags_WidthFixed,
85 ImGui::TableSetupColumn(
"Lock", ImGuiTableColumnFlags_WidthFixed,
87 ImGui::TableSetupColumn(
"Mode", ImGuiTableColumnFlags_WidthFixed,
89 ImGui::TableSetupColumn(
"Context", ImGuiTableColumnFlags_WidthStretch);
90 ImGui::TableSetupColumn(
"Panels", ImGuiTableColumnFlags_WidthStretch);
91 ImGui::TableSetupColumn(
"Sidebar", ImGuiTableColumnFlags_WidthFixed, 44.0f);
94 const char* world_buttons[] = {
"LW",
"DW",
"SW"};
95 ImGui::PushID(
"WorldButtons");
96 for (
int world_index = 0; world_index < 3; ++world_index) {
97 if (world_index > 0) {
98 ImGui::SameLine(0, 2);
101 current_world == world_index,
102 ImVec2(30.0f, 0.0f))) {
106 current_world = world_index;
107 int local_map = current_map & 0x3F;
108 const int world_start = world_index * 0x40;
110 if (maps_remaining > 0) {
111 if (local_map >= maps_remaining) {
112 local_map = maps_remaining - 1;
114 current_map = world_start + local_map;
118 if (ImGui::IsItemHovered()) {
125 ImGui::TextUnformatted(metadata.map_id_label.c_str());
126 if (ImGui::IsItemHovered()) {
127 ImGui::SetTooltip(
"%s", metadata.map_title.c_str());
135 int current_area_size =
static_cast<int>(map->
area_size());
140 if (ImGui::Combo(
"##ToolbarAreaSize", ¤t_area_size,
kAreaSizeNames,
159 const char* limited_names[] = {
"Small (1x1)",
"Large (2x2)"};
160 int limited_size = (current_area_size == 0 || current_area_size == 1)
164 if (ImGui::Combo(
"##ToolbarAreaSize", &limited_size, limited_names, 2)) {
171 0,
static_cast<int>(size)})
183 HOVER_HINT(
"Small (1x1) and Large (2x2) maps. Wide/Tall require v3+");
190 current_map_lock ?
"Unpin Map" :
"Pin Map")) {
191 current_map_lock = !current_map_lock;
199 "Mouse Mode (1)\nNavigate, pan, and manage entities",
204 ImGui::SameLine(0, 2);
207 "Brush Mode (2/B)\nDraw tiles on the map\nRight-click or I to "
208 "sample tile16 under cursor",
213 ImGui::SameLine(0, 2);
216 "Fill Screen Mode (F)\nFill the 32x32 screen under the cursor "
217 "with the selected tile\nRight-click or I to sample tile16 under "
227 const float context_width = ImGui::GetContentRegionAvail().x;
229 const bool show_overlay_toggle = context_width >= 132.0f;
230 const bool has_metadata_clipboard =
234 ImVec4 version_color = theme.status_inactive;
235 bool show_upgrade =
false;
237 switch (rom_version) {
239 version_color = theme.text_warning_yellow;
243 version_color = theme.status_active;
246 version_color = theme.status_active;
249 version_color = theme.status_success;
254 const float map_summary_min_width =
255 show_upgrade ? 400.0f : (show_entity_context ? 340.0f : 280.0f);
256 const bool show_map_summary = context_width >= map_summary_min_width;
258 bool wrote_metadata_item =
false;
259 if (show_entity_context) {
260 const char* entity_icon =
"";
261 const char* entity_label =
"";
262 switch (entity_edit_mode) {
265 entity_label =
"Entrances";
269 entity_label =
"Exits";
273 entity_label =
"Items";
277 entity_label =
"Sprites";
281 entity_label =
"Transports";
285 entity_label =
"Music";
290 ImGui::TextColored(theme.selection_secondary,
"%s %s", entity_icon,
292 wrote_metadata_item =
true;
295 if (wrote_metadata_item) {
299 metadata.version_label.c_str());
300 wrote_metadata_item =
true;
301 if (ImGui::IsItemHovered()) {
303 tr(
"ROM version determines available overworld features.\n"
304 "v2+: Custom BG colors, main palettes\n"
305 "v3+: Wide/Tall maps, custom tile GFX, animated GFX"));
308 if (has_metadata_clipboard && context_width >= 440.0f) {
313 if (ImGui::IsItemHovered()) {
315 tr(
"%s\nPaste from the canvas context menu onto the hovered map."),
320 wrote_metadata_item =
true;
323 if (show_map_summary) {
325 ImGui::TextDisabled(
"%s | %s | %s | %s", metadata.map_name.c_str(),
326 metadata.area_gfx_label.c_str(),
327 metadata.area_palette_label.c_str(),
328 metadata.message_label.c_str());
329 if (ImGui::IsItemHovered()) {
330 ImGui::SetTooltip(
"%s\n%s\n%s\n%s\n%s\n%s", metadata.map_title.c_str(),
331 metadata.area_size_label.c_str(),
332 metadata.parent_label.c_str(),
333 metadata.sprite_gfx_label.c_str(),
334 metadata.sprite_palette_label.c_str(),
335 metadata.music_label.c_str());
337 wrote_metadata_item =
true;
340 static std::array<char, 128> rename_label_buffer{};
341 static int rename_map_id = -1;
342 static std::string rename_error;
343 ImGui::BeginDisabled(project ==
nullptr);
345 ICON_MD_EDIT, project ?
"Rename selected map label in project"
346 :
"Open a project to rename map labels")) {
347 rename_map_id = current_map;
348 rename_error.clear();
349 std::strncpy(rename_label_buffer.data(), metadata.map_name.c_str(),
350 rename_label_buffer.size() - 1);
351 rename_label_buffer[rename_label_buffer.size() - 1] =
'\0';
352 ImGui::OpenPopup(
"RenameOverworldMapLabel");
354 ImGui::EndDisabled();
356 if (ImGui::BeginPopup(
"RenameOverworldMapLabel")) {
358 ImGui::TextDisabled(tr(
"Map 0x%02X"), rename_map_id);
359 ImGui::SetNextItemWidth(260.0f);
360 ImGui::InputText(
"##OverworldMapLabel", rename_label_buffer.data(),
361 rename_label_buffer.size());
366 rename_label_buffer.data())
369 rename_label_buffer.data());
371 ImGui::CloseCurrentPopup();
373 rename_error = std::string(status.message());
384 rename_label_buffer[0] =
'\0';
385 ImGui::CloseCurrentPopup();
387 rename_error = std::string(status.message());
390 if (!rename_error.empty()) {
391 ImGui::TextWrapped(
"%s", rename_error.c_str());
399 "Edit selected map metadata\nGraphics, palettes, message, and "
401 ImGui::OpenPopup(
"OverworldToolbarMetadataEditor");
403 if (ImGui::BeginPopup(
"OverworldToolbarMetadataEditor")) {
404 static std::string metadata_error;
405 static std::array<char, 128> resource_label_buffer{};
406 static std::string resource_label_type;
407 static std::string resource_label_title;
408 static std::string resource_label_error;
409 static int resource_label_id = -1;
410 static int resource_label_hex_width = 2;
412 const auto status = apply_property_edit(edit);
414 metadata_error.clear();
416 metadata_error = std::string(status.message());
419 const auto begin_resource_label_edit =
420 [&](
const ToolbarResourceLabelTarget& target) {
421 resource_label_title = target.title;
422 resource_label_type = target.type;
423 resource_label_id = target.id;
424 resource_label_hex_width = target.hex_width;
425 resource_label_error.clear();
426 const std::string current_label =
428 std::strncpy(resource_label_buffer.data(), current_label.c_str(),
429 resource_label_buffer.size() - 1);
430 resource_label_buffer[resource_label_buffer.size() - 1] =
'\0';
431 ImGui::OpenPopup(
"OverworldMetadataResourceLabel");
433 auto apply_resource_label = [&](
const std::string& label) {
437 resource_label_id, label)
439 resource_label_id, label);
441 resource_label_error.clear();
442 ImGui::CloseCurrentPopup();
444 resource_label_error = std::string(status.message());
448 ImGui::Text(tr(
"%s Map 0x%02X Metadata"),
ICON_MD_TUNE, current_map);
451 if (ImGui::BeginTable(
"ToolbarMetadataEditor", 2,
452 ImGuiTableFlags_SizingFixedFit)) {
453 ImGui::TableSetupColumn(
"Property", ImGuiTableColumnFlags_WidthFixed,
455 ImGui::TableSetupColumn(
"Value", ImGuiTableColumnFlags_WidthFixed,
459 ImGui::TextUnformatted(tr(
"Area GFX"));
463 apply_toolbar_edit({current_map,
469 ImGui::TextUnformatted(tr(
"Area Palette"));
474 apply_toolbar_edit({current_map,
482 ImGui::TextUnformatted(tr(
"Main Palette"));
487 apply_toolbar_edit({current_map,
494 ImGui::TextUnformatted(tr(
"Sprite GFX"));
498 apply_toolbar_edit({current_map,
500 game_state, sprite_gfx});
504 ImGui::TextUnformatted(tr(
"Sprite Palette"));
509 apply_toolbar_edit({current_map,
511 game_state, sprite_palette});
517 ImGui::TextUnformatted(tr(
"Animated GFX"));
522 apply_toolbar_edit({current_map,
529 ImGui::TextUnformatted(tr(
"Message"));
537 for (
int i = 0; i < 4; ++i) {
539 ImGui::Text(tr(
"Music %d"), i);
542 const std::string
id = absl::StrFormat(
"##ToolbarMetaMusic%d", i);
554 ImGui::BeginDisabled(project ==
nullptr);
555 if (ImGui::BeginTable(
556 "ToolbarMetadataProjectLabels", 4,
557 ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_RowBg)) {
558 ImGui::TableSetupColumn(
"Resource", ImGuiTableColumnFlags_WidthFixed,
560 ImGui::TableSetupColumn(
"ID", ImGuiTableColumnFlags_WidthFixed,
562 ImGui::TableSetupColumn(
"Label", ImGuiTableColumnFlags_WidthFixed,
564 ImGui::TableSetupColumn(
"Edit", ImGuiTableColumnFlags_WidthFixed,
567 auto draw_label_row = [&](
const ToolbarResourceLabelTarget& target) {
569 ImGui::TextUnformatted(target.title.c_str());
572 "%s", FormatResourceId(target.id, target.hex_width).c_str());
574 const std::string project_label =
576 if (project_label.empty()) {
577 ImGui::TextDisabled(tr(
"default"));
579 ImGui::TextUnformatted(project_label.c_str());
582 const std::string row_id =
583 absl::StrFormat(
"Label%s%d", target.type, target.id);
584 ImGui::PushID(row_id.c_str());
586 begin_resource_label_edit(target);
588 if (ImGui::IsItemHovered()) {
590 tr(
"Rename %s %s in the open project"), target.title.c_str(),
591 FormatResourceId(target.id, target.hex_width).c_str());
596 draw_label_row({
"Area GFX",
"graphics", map->
area_graphics()});
597 draw_label_row({absl::StrFormat(
"Sprite GFX %d", game_state),
601 draw_label_row({
"Animated GFX",
"graphics", map->
animated_gfx()});
604 {
"Area Palette",
"overworld_area_palette", map->
area_palette()});
607 draw_label_row({
"Main Palette",
"overworld_main_palette",
610 draw_label_row({
"Sprite Palette",
"overworld_sprite_palette",
613 {
"Message",
"message", map->
message_id(), 4});
614 for (
int i = 0; i < 4; ++i) {
615 draw_label_row({absl::StrFormat(
"Music %d", i),
"overworld_music",
621 ImGui::EndDisabled();
623 ImGui::TextDisabled(tr(
"Open a project to store metadata labels."));
626 if (ImGui::BeginPopup(
"OverworldMetadataResourceLabel")) {
627 ImGui::Text(
"%s %s",
ICON_MD_LABEL, resource_label_title.c_str());
628 ImGui::TextDisabled(
"%s", FormatResourceId(resource_label_id,
629 resource_label_hex_width)
631 ImGui::SetNextItemWidth(260.0f);
632 ImGui::InputText(
"##OverworldMetadataResourceLabelInput",
633 resource_label_buffer.data(),
634 resource_label_buffer.size());
636 apply_resource_label(resource_label_buffer.data());
640 apply_resource_label(
"");
642 if (!resource_label_error.empty()) {
643 ImGui::TextWrapped(
"%s", resource_label_error.c_str());
648 if (!metadata_error.empty()) {
650 ImGui::TextWrapped(
"%s", metadata_error.c_str());
657 if (wrote_metadata_item) {
664 "Upgrade ROM to ZSCustomOverworld v3\n"
665 "Enables all advanced features");
666 }
else if (!show_map_summary && show_overlay_toggle &&
669 if (wrote_metadata_item) {
673 overlay_preview_enabled
674 ?
"Hide overlay preview"
675 :
"Show overlay preview",
676 overlay_preview_enabled)) {
684 const float panel_width = ImGui::GetContentRegionAvail().x;
685 const bool compact_panel_controls = panel_width < 320.0f;
686 const auto toggle_window = [&](
const char* panel_id) {
689 const auto popup_toggle_item = [&](
const char* label,
const char* panel_id,
690 const char* shortcut =
nullptr) {
691 const bool open = window_manager->
IsWindowOpen(panel_id);
692 if (ImGui::MenuItem(label, shortcut, open)) {
693 toggle_window(panel_id);
700 if (compact_panel_controls) {
702 ICON_MD_APPS,
"Overworld Windows\nOpen panel toggle menu")) {
703 ImGui::OpenPopup(
"OverworldWindowsPopup");
706 if (ImGui::BeginPopup(
"OverworldWindowsPopup")) {
709 popup_toggle_item(
"Tile16 Selector",
711 popup_toggle_item(
"Tile8 Selector",
718 popup_toggle_item(
"Scratch Workspace",
768 "Overworld Item List (Ctrl+Shift+I)\nFilter/select items and "
770 "duplicate/nudge shortcuts",