23#include "imgui/imgui.h"
24#include "imgui/imgui_internal.h"
58 "Switch to standalone panel workflow";
60 "Visit another room to seed compare history.";
62 "Visit another room first to seed compare history.";
64 "Visit another room to build room history.";
68 float button_size = 0.0f;
77 bool pane_toggles =
true;
78 bool recent_rooms =
true;
79 bool stitched_rooms =
true;
81 bool apply_room =
false;
82 bool dungeon_map =
false;
83 bool view_options =
false;
84 bool panel_mode =
false;
85 bool overflow =
false;
90 layout.
width = toolbar_width;
91 layout.
spacing = ImGui::GetStyle().ItemSpacing.x;
98bool DrawToolbarActionButton(
const char*
id,
const char* label,
99 const ImVec2& size,
const char* tooltip,
100 bool active =
false);
103 int current_room,
int previous_room,
104 const std::function<
const std::deque<int>&()>& get_recent_rooms) {
105 if (previous_room >= 0 && previous_room != current_room) {
106 return {
true, previous_room};
108 if (get_recent_rooms) {
109 const auto& mru = get_recent_rooms();
110 for (
int rid : mru) {
111 if (rid != current_room) {
120 bool* value,
float btn_size,
const char* tooltip_on,
121 const char* tooltip_off) {
126 const float btn = btn_size;
129 const bool active = *value;
131 const ImVec4 col_btn = ImGui::GetStyleColorVec4(ImGuiCol_Button);
132 const ImVec4 col_active = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
136 active ? col_active : col_btn);
138 ImGui::Button(active ? icon_on : icon_off, ImVec2(btn_w, btn));
140 if (ImGui::IsItemHovered()) {
141 ImGui::SetTooltip(
"%s", active ? tooltip_on : tooltip_off);
156 ImGui::TextDisabled(tr(
"Canvas"));
158 if (ImGui::Checkbox(tr(
"Grid (8x8)"), &v)) {
162 if (ImGui::Checkbox(tr(
"Object Bounds"), &v)) {
166 if (ImGui::Checkbox(tr(
"Hover Coordinates"), &v)) {
170 if (ImGui::Checkbox(tr(
"Camera Quadrants"), &v)) {
178 ImGui::TextDisabled(tr(
"Authoring"));
180 if (ImGui::Checkbox(tr(
"Track Collision"), &v)) {
184 if (ImGui::Checkbox(tr(
"Custom Collision"), &v)) {
188 if (ImGui::Checkbox(tr(
"Water Fill (Oracle)"), &v)) {
192 if (ImGui::Checkbox(tr(
"Minecart Pathing"), &v)) {
196 if (ImGui::Checkbox(tr(
"Track Gaps"), &v)) {
200 if (ImGui::Checkbox(tr(
"Track Routes"), &v)) {
204 if (ImGui::Checkbox(tr(
"Custom Objects (Oracle)"), &v)) {
207 if (ImGui::IsItemHovered()) {
209 tr(
"Highlight registered custom-draw objects\n"
210 "with a cyan overlay showing position and subtype."));
224 "Canvas view options")) {
240 const float mode_height = toolbar_layout.
button_size;
241 const float connected_width =
244 ImVec2(connected_width, mode_height),
246 ?
"Return to single-room canvas"
247 :
"Stitched Rooms: browse the current room "
248 "and its neighbors as one canvas",
255 const ImVec2& size,
const char* tooltip,
260 if (tooltip && *tooltip && ImGui::IsItemHovered()) {
261 ImGui::SetTooltip(
"%s", tooltip);
267 int current_room_id,
int* compare_room_id,
268 const std::function<
const std::deque<int>&()>& get_recent_rooms,
269 char* search_buf,
size_t search_buf_size,
271 if (!compare_room_id || *compare_room_id < 0) {
274 const bool can_search = search_buf !=
nullptr && search_buf_size > 1;
275 const char* filter = can_search ? search_buf :
"";
280 snprintf(preview,
sizeof(preview),
"[%03X] %s", *compare_room_id,
283 auto to_lower = [](
unsigned char c) {
284 return static_cast<char>(std::tolower(c));
286 auto icontains = [&](
const std::string& haystack,
287 const char* needle) ->
bool {
288 if (!needle || *needle ==
'\0') {
291 const size_t nlen = std::strlen(needle);
292 for (
size_t i = 0; i + nlen <= haystack.size(); ++i) {
294 for (
size_t j = 0; j < nlen; ++j) {
295 if (to_lower(
static_cast<unsigned char>(haystack[i + j])) !=
296 to_lower(
static_cast<unsigned char>(needle[j]))) {
308 ImGui::SetNextItemWidth(std::clamp(ImGui::GetContentRegionAvail().x, 120.0f,
310 if (ImGui::BeginCombo(
"##CompareRoomPicker", preview,
311 ImGuiComboFlags_HeightLarge)) {
313 if (get_recent_rooms) {
314 const auto& mru = get_recent_rooms();
315 for (
int rid : mru) {
316 if (rid == current_room_id) {
320 const auto rid_label =
322 snprintf(item,
sizeof(item),
"[%03X] %s", rid, rid_label.c_str());
323 const bool is_selected = (rid == *compare_room_id);
324 if (ImGui::Selectable(item, is_selected)) {
325 *compare_room_id = rid;
332 ImGui::SetNextItemWidth(-1.0f);
335 search_buf, search_buf_size);
337 ImGui::TextDisabled(tr(
"Search unavailable"));
341 std::vector<int> filtered_rooms;
344 if (rid == current_room_id) {
349 snprintf(hex_buf,
sizeof(hex_buf),
"%03X", rid);
350 if (!icontains(rid_label, filter) && !icontains(hex_buf, filter)) {
353 filtered_rooms.push_back(rid);
358 ImGuiListClipper clipper;
359 clipper.Begin(
static_cast<int>(filtered_rooms.size()));
360 while (clipper.Step()) {
361 for (
int idx = clipper.DisplayStart; idx < clipper.DisplayEnd; ++idx) {
362 const int rid = filtered_rooms[idx];
363 const auto rid_label =
366 snprintf(item,
sizeof(item),
"[%03X] %s", rid, rid_label.c_str());
367 const bool is_selected = (rid == *compare_room_id);
368 if (ImGui::Selectable(item, is_selected)) {
369 *compare_room_id = rid;
377 if (ImGui::IsItemHovered()) {
383 bool show_direct_room_id) {
384 ImGui::TextDisabled(tr(
"Compare Room"));
388 if (!show_direct_room_id) {
392 uint16_t cmp =
static_cast<uint16_t
>(
399 if (ImGui::IsItemHovered()) {
416 const char* tooltip = compare_active ?
"Compare settings" :
"Start compare";
428 if (!compare_active) {
430 ImGui::TextDisabled(tr(
"No recent room to compare"));
434 ImGui::BeginDisabled();
436 if (ImGui::MenuItem(tr(
"Start Compare"))) {
442 ImGui::EndDisabled();
449 if (ImGui::MenuItem(tr(
"Swap Rooms"))) {
462 if (ImGui::MenuItem(tr(
"End Compare"))) {
473 ImGui::TextDisabled(tr(
"Recent Rooms"));
480 std::vector<int> recent_ids;
483 recent_ids.assign(recent.begin(), recent.end());
485 std::vector<int> to_forget;
487 for (
int room_id : recent_ids) {
488 ImGui::PushID(room_id);
490 const bool room_dirty =
493 const auto room_name =
495 char item_label[160];
496 snprintf(item_label,
sizeof(item_label),
"[%03X]%s %s##RecentRoom", room_id,
497 room_dirty ?
"*" :
"", room_name.c_str());
499 if (ImGui::Selectable(item_label, is_current) && !is_current) {
506 if (ImGui::IsItemHovered()) {
507 ImGui::SetTooltip(
"Click to open; right-click for actions%s",
508 room_dirty ?
"\nPending room changes" :
"");
511 if (ImGui::BeginPopupContextItem(
"##RecentRoomActions")) {
530 to_forget.push_back(room_id);
538 for (
int room_id : to_forget) {
550 ImGui::BeginDisabled();
557 ImGui::EndDisabled();
574 auto append = [&](
float item_width) {
598 float left_width = 0.0f;
599 auto append_left = [&](
float item_width,
float gap) {
600 if (left_width > 0.0f) {
603 left_width += item_width;
615 const float navigation_width =
616 4.0f * ImGui::GetFrameHeight() + 3.0f * ImGui::GetStyle().ItemSpacing.x;
654 constexpr float kSafetyMargin = 4.0f;
663 auto collapse = [&](
bool& item) {
682 if (collapse(visibility.
compare))
698 if (!compare_active) {
706 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
714 if (ImGui::MenuItem(tr(
"Swap Rooms"))) {
727 if (ImGui::MenuItem(tr(
"End Compare"))) {
735 const float button_width =
739 "More dungeon actions")) {
743 bool request_panel_mode =
false;
745 return request_panel_mode;
749 if (ImGui::MenuItem(
ICON_MD_LIST " Room Browser",
nullptr,
761 const bool has_history =
768 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
783 const bool has_hidden_room_action =
787 if (has_hidden_room_action) {
808 request_panel_mode =
true;
810 if (ImGui::IsItemHovered()) {
816 return request_panel_mode;
829 ImGui::TextDisabled(tr(
"Workbench toolbar not wired"));
833 const ToolbarLayout layout =
834 ResolveToolbarLayout(std::max(ImGui::GetContentRegionAvail().x, 1.0f));
835 bool request_panel_mode =
false;
839 const ImVec2 frame_pad = ImGui::GetStyle().FramePadding;
841 ImGuiStyleVar_FramePadding, ImVec2(std::max(4.0f, frame_pad.x - 1.0f),
842 std::max(3.0f, frame_pad.y - 1.0f)));
844 ImGuiStyleVar_ItemSpacing,
845 ImVec2(std::max(layout.spacing, 4.0f),
846 std::max(3.0f, ImGui::GetStyle().ItemSpacing.y - 1.0f)));
847 const ToolbarVisibility visibility = ResolveToolbarVisibility(p, layout);
848 const float right_actions_width =
849 ToolbarRightActionsWidth(p, layout, visibility);
861 if (visibility.pane_toggles) {
864 "Hide room browser",
"Show room browser");
865 ImGui::SameLine(0.0f, kToolbarActionGap);
868 "Hide inspector",
"Show inspector");
869 ImGui::SameLine(0.0f, kToolbarClusterGap);
874 if (visibility.recent_rooms) {
875 ImGui::SameLine(0.0f, kToolbarActionGap);
876 DrawRecentRoomsMenu(p, layout);
879 if (visibility.apply_room) {
880 ImGui::SameLine(0.0f, kToolbarActionGap);
883 "Apply this room into the loaded ROM buffer "
884 "(File > Save ROM persists to disk)")) {
888 if (visibility.dungeon_map) {
889 ImGui::SameLine(0.0f, kToolbarActionGap);
892 "Open the Dungeon Map popup")) {
897 if (visibility.stitched_rooms) {
898 ImGui::SameLine(0.0f, kToolbarClusterGap);
899 DrawCanvasModeSelector(p.
layout, layout);
902 if (visibility.compare) {
903 ImGui::SameLine(0.0f, kToolbarClusterGap);
904 DrawCompareMenu(p, layout);
907 if (right_actions_width > 0.0f) {
908 const float last_left_edge =
909 ImGui::GetItemRectMax().x - ImGui::GetWindowPos().x;
910 const float right_start =
911 std::max(last_left_edge + kToolbarActionGap,
912 ImGui::GetWindowContentRegionMax().x - right_actions_width);
913 ImGui::SameLine(right_start, 0.0f);
915 bool drew_right_action =
false;
916 if (visibility.view_options) {
918 drew_right_action =
true;
920 if (visibility.panel_mode) {
921 if (drew_right_action) {
922 ImGui::SameLine(0.0f, kToolbarActionGap);
926 kToolbarPanelWorkflowTooltip)) {
927 request_panel_mode =
true;
929 drew_right_action =
true;
931 if (visibility.overflow) {
932 if (drew_right_action) {
933 ImGui::SameLine(0.0f, kToolbarActionGap);
935 request_panel_mode |= DrawToolbarOverflowMenu(p, layout, visibility);
938 return request_panel_mode;
bool show_object_bounds() const
DungeonRoomStore * rooms() const
void set_show_water_fill_overlay(bool show)
void set_show_custom_collision_overlay(bool show)
void set_show_track_route_overlay(bool show)
void SetConnectedControlsInline(bool inline_controls)
bool show_track_gap_overlay() const
void set_show_custom_objects_overlay(bool show)
bool show_custom_objects_overlay() const
void set_show_track_collision_overlay(bool show)
void set_show_minecart_sprite_overlay(bool show)
bool show_coordinate_overlay() const
bool show_track_collision_overlay() const
bool show_minecart_sprite_overlay() const
bool show_water_fill_overlay() const
bool show_camera_quadrant_overlay() const
void set_show_object_bounds(bool show)
void set_show_coordinate_overlay(bool show)
const project::YazeProject * project() const
bool show_track_route_overlay() const
void set_show_track_gap_overlay(bool show)
void set_show_camera_quadrant_overlay(bool show)
bool show_custom_collision_overlay() const
void set_show_grid(bool show)
zelda3::Room * GetIfMaterialized(int room_id)
static float GetStandardWidgetHeight()
RAII guard for ImGui style colors.
RAII guard for ImGui style vars.
bool HasUnsavedChanges() const
#define ICON_MD_VIEW_QUILT
#define ICON_MD_COMPARE_ARROWS
#define ICON_MD_VISIBILITY
#define ICON_MD_MORE_HORIZ
#define ICON_MD_OPEN_IN_NEW
std::string GetRoomLabel(const project::YazeProject *project, int room_id)
float CalcIconButtonWidth(const char *icon, float button_height)
float CalcIconToggleButtonWidth(const char *icon_on, const char *icon_off, float button_height)
bool DrawHeaderIconAction(const char *id, const char *icon, float button_size, const char *tooltip, bool active=false)
Editors are the view controllers for the application.
bool ToggleButton(const char *label, bool active, const ImVec2 &size)
InputHexResult InputHexWordEx(const char *label, uint16_t *data, float input_width, bool no_step)
bool show_connected_canvas_view
bool show_right_inspector
static constexpr float kIconButtonSmall
Modern project structure with comprehensive settings consolidation.