6#include "absl/strings/str_format.h"
29 "Entrances (3)\nOnly entrances respond to hover and drag"},
31 "Exits (4)\nOnly exits respond to hover and drag"},
33 "Items (5)\nOnly items respond to hover and drag.\n"
34 "Arrows nudge the selected item (Shift = 16px), Cmd/Ctrl+D duplicates"},
36 "Sprites (6)\nOnly sprites respond to hover and drag"},
39bool ToolButton(
const char* label,
const char* tooltip,
bool active,
40 const char* automation_id =
nullptr) {
44 std::string(
"OverworldToolbar/button:") + automation_id,
"button",
47 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
48 ImGui::SetTooltip(
"%s", tooltip);
54 ImGui::SameLine(0, 6.0f);
55 const ImVec2 pos = ImGui::GetCursorScreenPos();
56 const float height = ImGui::GetFrameHeight();
57 ImGui::GetWindowDrawList()->AddLine(ImVec2(pos.x, pos.y + 3.0f),
58 ImVec2(pos.x, pos.y + height - 3.0f),
59 ImGui::GetColorU32(ImGuiCol_Separator));
60 ImGui::Dummy(ImVec2(1.0f, 0.0f));
61 ImGui::SameLine(0, 6.0f);
67 const char* shortcut_name)
const {
76 std::string result = text;
77 const size_t newline = result.find(
'\n');
78 const std::string hint =
" (" + keys +
")";
79 if (newline == std::string::npos) {
82 return result.insert(newline, hint);
86 const auto toggle = [&](
const char* icon,
const char* label,
87 const char* shortcut,
const std::function<bool()>& on,
88 const std::function<void()>& flip) {
92 const std::string tip =
WithHint(label, shortcut);
94 const std::string keys =
96 const std::string title =
97 std::string(label).substr(0, std::string(label).find(
'\n'));
98 const std::string item = absl::StrFormat(
"%s %s", icon, title);
99 if (ImGui::MenuItem(item.c_str(), keys.empty() ?
nullptr : keys.c_str(),
103 if (ImGui::IsItemHovered()) {
104 ImGui::SetTooltip(
"%s", tip.c_str());
111 ImGui::SameLine(0, 2);
113 const auto action = [&](
const char* icon,
const char* label,
114 const char* shortcut,
115 const std::function<void()>& run) {
120 const std::string keys =
122 const std::string title =
123 std::string(label).substr(0, std::string(label).find(
'\n'));
124 const std::string item = absl::StrFormat(
"%s %s", icon, title);
125 if (ImGui::MenuItem(item.c_str(),
126 keys.empty() ?
nullptr : keys.c_str())) {
129 if (ImGui::IsItemHovered()) {
130 ImGui::SetTooltip(
"%s",
WithHint(label, shortcut).c_str());
134 const std::string tip =
WithHint(label, shortcut);
138 ImGui::SameLine(0, 2);
143 toggle(
ICON_MD_PLACE,
"Entities\nShow entrances, exits, items and sprites",
153 ImGui::AlignTextToFramePadding();
154 ImGui::TextDisabled(
"%3.0f%%",
get_zoom() * 100.0f);
155 ImGui::SameLine(0, 2);
171 const std::function<
void()>& draw_appearance) {
172 if (!overworld || !overworld->
is_loaded() || !window_manager || !rom) {
175 const bool project_terrain = !overworld->
is_lyra_preview() && project &&
177 current_map >= 0xA0 && current_map < 0xC0;
178 if (!project_terrain && !overworld->
overworld_map(current_map)) {
182 const float avail = ImGui::GetContentRegionAvail().x;
183 const float right_edge = ImGui::GetCursorScreenPos().x + avail;
184 const bool tiny = avail < ImGui::GetFontSize() * 38.0f;
187 {{ImGuiStyleVar_FramePadding, ImVec2(4.0f, 3.0f)},
188 {ImGuiStyleVar_ItemSpacing, ImVec2(4.0f, 4.0f)}});
189 ImGui::PushID(
"OverworldToolbar");
192 static constexpr const char* kWorldButtons[] = {
"LW",
"DW",
"SW"};
193 static constexpr const char* kWorldShortcuts[] = {
"overworld.world_light",
194 "overworld.world_dark",
195 "overworld.world_special"};
196 for (
int world_index = 0; !tiny && world_index < 3; ++world_index) {
197 if (world_index > 0) {
198 ImGui::SameLine(0, 2);
201 current_world == world_index, ImVec2(30.0f, 0.0f)) &&
206 absl::StrFormat(
"OverworldToolbar/world:%d", world_index),
"button",
208 if (ImGui::IsItemHovered()) {
219 current_map, game_state);
223 if (current_map >= 0xA0 && current_map < 0xC0) {
224 ImGui::SameLine(0, 2);
225 ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.3f, 1.0f),
ICON_MD_INFO);
226 if (ImGui::IsItemHovered()) {
228 "Tail Map ($A0-$BF) • Entity Limits:\n"
229 "• Items: Disabled (SRAM event storage $7EF000-$7EF4FF unallocated)\n"
230 "• Exits: 1 free slot (full links need exit table v2)\n"
231 "• Entrances: Trigger table v2 in development (Phase P2)\n"
232 "• Sprites: Draft state (actor compilation needed for ROM)");
235 if (ImGui::IsItemHovered()) {
237 "%s\n%s\nClick this button or double-click the canvas map for "
239 "%s / %s: adjacent map",
240 metadata.map_title.c_str(), metadata.area_size_label.c_str(),
241 WithHint(
"Previous",
"overworld.map_left").c_str(),
242 WithHint(
"Next",
"overworld.map_right").c_str());
244 ImGui::SameLine(0, 2);
245 const std::string pin_tip =
247 ?
WithHint(
"Unpin map\nResume following the cursor",
248 "overworld.toggle_lock")
249 :
WithHint(
"Pin map\nKeep properties on this map while navigating",
250 "overworld.toggle_lock");
254 ImVec2(ImGui::GetFrameHeight(), ImGui::GetFrameHeight()),
256 current_map_lock = !current_map_lock;
260 ImGui::BeginDisabled(project_terrain);
261 ImGui::SameLine(0, 4);
262 const std::string story_label = absl::StrFormat(
"Story %d", game_state);
263 ImGui::SetNextItemWidth(ImGui::CalcTextSize(
"Story 0").x +
264 ImGui::GetFrameHeight() + 8.0f);
265 const bool story_open = ImGui::BeginCombo(
"##Story", story_label.c_str());
267 "OverworldToolbar/combo:Story",
"combobox", ImGui::GetItemID());
268 if (ImGui::IsItemHovered()) {
270 "Story part to preview. Changes the active sprite set, "
271 "sprite graphics and sprite palettes. "
272 "Shared item placements stay the same.");
275 for (
int state = 0; state < 3; ++state) {
276 const std::string label =
278 if (ImGui::Selectable(label.c_str(), game_state == state) &&
283 absl::StrFormat(
"OverworldToolbar/story:%d", state),
"selectable",
288 if (draw_appearance) {
289 ImGui::SameLine(0, 4);
292 ImGui::EndDisabled();
294 const char* entity_label =
"Entities";
295 for (
const auto& button : kEntityModeButtons) {
296 if (entity_edit_mode == button.mode) {
297 entity_label = button.label;
300 const auto button_width = [](
const char* label) {
301 return ImGui::CalcTextSize(label,
nullptr,
true).x +
302 ImGui::GetStyle().FramePadding.x * 2.0f;
306 const float drawing_width =
310 const auto tools_width = [&](
bool compact_labels) {
311 return 3.0f * 13.0f + drawing_width +
312 button_width(compact_labels ?
ICON_MD_PLACE : entity_label) +
316 const float remaining = right_edge - ImGui::GetItemRectMax().x;
317 const bool compact = tools_width(
false) > remaining;
319 const bool overflow = tiny || tools_width(
true) > remaining;
321 ImGui::SameLine(0, 4);
324 ImGui::OpenPopup(
"ToolsOverflow");
326 if (!ImGui::BeginPopup(
"ToolsOverflow")) {
331 for (
int world = 0; world < 3; ++world) {
333 current_world == world) &&
352 "Select (1)\nLeft-drag empty map: pan Middle-drag: pan\n"
353 "Drag entities to move them Right-click: map menu\n"
354 "Double-click a map: Map Properties",
358 ImGui::SameLine(0, 2);
359 const std::string brush_tip =
WithHint(
360 "Brush (2)\nLeft-drag: paint the selected tile16\n"
361 "Right-click: sample tile16 (or I) Right-drag: capture a brush\n"
362 "[ / ]: previous / next tile16 Shift+Right-click: map menu",
363 "overworld.brush_toggle");
368 ImGui::SameLine(0, 2);
369 const std::string fill_tip =
WithHint(
370 "Fill screen\nClick fills the 32x32 screen under the cursor\n"
371 "Right-click: sample tile16 Shift+Right-click: map menu",
377 ImGui::BeginDisabled(project_terrain);
378 ImGui::SameLine(0, 2);
380 "Pick an item from the list, then click the map to place it",
381 false,
"Insert Item")) {
390 ImGui::OpenPopup(
"EntityTools");
393 "OverworldToolbar/button:Entities",
"button", ImGui::GetItemID());
394 if (ImGui::IsItemHovered()) {
396 "Choose which entities respond to hover and drag.\n"
397 "Entrances (3), exits (4), items (5), sprites (6).");
399 if (ImGui::BeginPopup(
"EntityTools")) {
404 entity_edit_mode = mode;
407 if (ImGui::MenuItem(
"All entities",
nullptr,
411 for (
const auto& button : kEntityModeButtons) {
412 const bool disabled_for_tail =
413 (current_map >= 0xA0 && current_map < 0xC0) &&
415 if (disabled_for_tail) {
416 ImGui::BeginDisabled();
418 if (ImGui::MenuItem(button.label,
nullptr,
419 entity_edit_mode == button.mode)) {
422 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
423 if (disabled_for_tail) {
425 "Items unavailable on Tail Maps (SRAM event storage "
428 ImGui::SetTooltip(
"%s", button.tooltip);
431 if (disabled_for_tail) {
432 ImGui::EndDisabled();
437 ImGui::EndDisabled();
443 "Grid, entities, overlay and zoom",
false,
"View")) {
444 ImGui::OpenPopup(
"OverworldViewMenu");
446 if (ImGui::BeginPopup(
"OverworldViewMenu")) {
450 ImGui::SameLine(0, 2);
452 "Open overworld editing windows",
false,
"Windows")) {
453 ImGui::OpenPopup(
"OverworldWindowsPopup");
455 if (ImGui::BeginPopup(
"OverworldWindowsPopup")) {
456 const auto item = [&](
const char* label,
const char* panel_id,
457 const char* shortcut_name) {
458 const std::string keys =
shortcuts && shortcut_name
461 if (ImGui::MenuItem(label, keys.empty() ?
nullptr : keys.c_str(),
463 const bool opening = !window_manager->
IsWindowOpen(panel_id);
476 "overworld.toggle_tile16_editor");
483 "overworld.toggle_item_list");
493 " Upgrade to ZSCustomOverworld v3...")) {
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
std::string GetDisplayString(const std::string &name) const
Live, platform-formatted binding for a shortcut name.
Central registry for all editor cards with session awareness and dependency injection.
size_t GetActiveSessionId() const
bool IsWindowOpen(size_t session_id, const std::string &base_window_id) const
bool ToggleWindow(size_t session_id, const std::string &base_window_id)
RAII guard for ImGui style vars.
static OverworldVersion GetVersion(const Rom &rom)
Detect ROM version from ASM marker byte.
bool is_lyra_preview() const
auto overworld_map(int i) const
#define ICON_MD_GRID_VIEW
#define ICON_MD_CENTER_FOCUS_STRONG
#define ICON_MD_FIT_SCREEN
#define ICON_MD_GPS_FIXED
#define ICON_MD_VISIBILITY
#define ICON_MD_FORMAT_COLOR_FILL
#define ICON_MD_MORE_HORIZ
#define ICON_MD_AUTO_FIX_HIGH
#define ICON_MD_ANALYTICS
Editors are the view controllers for the application.
constexpr const char * kWorldNames[]
OverworldMapMetadata BuildOverworldMapMetadata(const zelda3::Overworld &overworld, const Rom *rom, const project::YazeProject *project, int map_id, int game_state)
constexpr const char * kGameStateNames[]
bool ThemedIconButton(const char *icon, const char *tooltip, const ImVec2 &size, bool is_active, bool is_disabled, const char *panel_id, const char *anim_id)
Draw a standard icon button with theme-aware colors.
bool ToggleButton(const char *label, bool active, const ImVec2 &size)
bool ToolbarIconButton(const char *icon, const char *tooltip, bool is_active)
Convenience wrapper for toolbar-sized icon buttons.
@ kVanilla
0xFF in ROM, no ZScream ASM applied
static constexpr const char * kMapProperties
static constexpr const char * kTile8Selector
static constexpr const char * kAreaGraphics
static constexpr const char * kItemList
static constexpr const char * kScratchSpace
static constexpr const char * kTile16Editor
static constexpr const char * kTile16Selector
static constexpr const char * kGfxGroups
static constexpr const char * kUsageStats
Modern project structure with comprehensive settings consolidation.
std::string lyra_terrain_file