yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dungeon_status_bar.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <cstdio>
6
15#include "imgui/imgui.h"
16
17namespace yaze::editor {
18
19namespace {
20
21float CalcStatusIconButtonWidth(const char* icon, float button_height) {
22 if (!icon || !*icon) {
23 return button_height;
24 }
25
26 const ImGuiStyle& style = ImGui::GetStyle();
27 const float glyph_width = ImGui::CalcTextSize(icon).x;
28 const float extra = std::max(2.0f, style.FramePadding.x);
29 return std::max(
30 button_height,
31 std::ceil(glyph_width + (style.FramePadding.x * 2.0f) + extra));
32}
33
34} // namespace
35
37 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
38
39 // Reserve a fixed-height bar at the bottom, respecting UIConfig minimum
40 const float font_bar =
41 ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f;
42 const float bar_height = std::max(font_bar, gui::UIConfig::kStatusBarHeight);
43
44 gui::StyleColorGuard bar_colors({
45 {ImGuiCol_ChildBg, gui::ConvertColorToImVec4(theme.frame_bg)},
46 });
47
48 ImGui::BeginChild(
49 "##DungeonStatusBar", ImVec2(-1, bar_height), false,
50 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
51
52 const float spacing = ImGui::GetStyle().ItemSpacing.x;
53
54 // Tool mode indicator
55 ImGui::AlignTextToFramePadding();
56 ImGui::TextDisabled(ICON_MD_BUILD);
57 ImGui::SameLine(0, 4);
58 ImGui::Text("%s", state.tool_mode);
59 if (state.workflow_mode != nullptr && state.workflow_mode[0] != '\0') {
60 ImGui::SameLine(0, spacing);
61 ImGui::TextDisabled("|");
62 ImGui::SameLine(0, spacing);
63 const ImVec4 workflow_color =
64 state.workflow_primary ? gui::ConvertColorToImVec4(theme.success)
65 : gui::ConvertColorToImVec4(theme.warning);
66 ImGui::TextColored(workflow_color, "%s %s", ICON_MD_WORKSPACES,
67 state.workflow_mode);
68 }
69 ImGui::SameLine(0, spacing * 2);
70
71 // Separator
72 ImGui::TextDisabled("|");
73 ImGui::SameLine(0, spacing * 2);
74
75 // Undo/Redo buttons with depth indicator
76 {
77 const float button_height =
78 std::max(gui::UIConfig::kIconButtonSmall + 4.0f, bar_height - 4.0f);
79 const ImVec2 undo_size(
80 CalcStatusIconButtonWidth(ICON_MD_UNDO, button_height), button_height);
81 const ImVec2 redo_size(
82 CalcStatusIconButtonWidth(ICON_MD_REDO, button_height), button_height);
83
84 if (!state.can_undo)
85 ImGui::BeginDisabled();
86
87 // Build tooltip string for undo
88 char undo_tip[128];
89 snprintf(undo_tip, sizeof(undo_tip), "Undo%s%s",
90 state.undo_desc ? ": " : "",
91 state.undo_desc ? state.undo_desc : "");
92 if (gui::ThemedIconButton(ICON_MD_UNDO, undo_tip, undo_size)) {
93 if (state.on_undo)
94 state.on_undo();
95 }
96 if (!state.can_undo)
97 ImGui::EndDisabled();
98 ImGui::SameLine(0, 4);
99
100 if (!state.can_redo)
101 ImGui::BeginDisabled();
102
103 // Build tooltip string for redo
104 char redo_tip[128];
105 snprintf(redo_tip, sizeof(redo_tip), "Redo%s%s",
106 state.redo_desc ? ": " : "",
107 state.redo_desc ? state.redo_desc : "");
108 if (gui::ThemedIconButton(ICON_MD_REDO, redo_tip, redo_size)) {
109 if (state.on_redo)
110 state.on_redo();
111 }
112 if (!state.can_redo)
113 ImGui::EndDisabled();
114
115 if (state.undo_depth > 0) {
116 ImGui::SameLine(0, 4);
117 ImGui::TextDisabled("(%d)", state.undo_depth);
118 }
119 }
120 ImGui::SameLine(0, spacing * 2);
121
122 // Separator
123 ImGui::TextDisabled("|");
124 ImGui::SameLine(0, spacing * 2);
125
126 // Selection summary
127 if (state.selection_count > 0) {
128 ImGui::TextDisabled(ICON_MD_SELECT_ALL);
129 ImGui::SameLine(0, 4);
130 ImGui::Text("%s", state.selection_summary.c_str());
131 } else {
132 ImGui::TextDisabled("%s", state.selection_summary.c_str());
133 }
134 if (state.selection_count > 0 && state.on_selection) {
135 if (ImGui::IsItemHovered()) {
136 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
137 ImGui::SetTooltip(tr("Open Selection inspector"));
138 }
139 if (ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
140 state.on_selection();
141 }
142 }
143 ImGui::SameLine(0, spacing * 2);
144
145 // Separator
146 ImGui::TextDisabled("|");
147 ImGui::SameLine(0, spacing * 2);
148
149 // Zoom level
150 ImGui::TextDisabled(ICON_MD_ZOOM_IN);
151 ImGui::SameLine(0, 4);
152 ImGui::Text("%d%%", state.zoom_percent);
153 // Right-aligned section: dirty indicator + room ID
154 {
155 char right_text[64];
156 if (state.room_id >= 0) {
157 if (state.room_dirty) {
158 snprintf(right_text, sizeof(right_text), ICON_MD_CIRCLE " Room 0x%03X",
159 state.room_id);
160 } else {
161 snprintf(right_text, sizeof(right_text), "Room 0x%03X", state.room_id);
162 }
163 } else {
164 snprintf(right_text, sizeof(right_text), "No room");
165 }
166
167 const float text_width = ImGui::CalcTextSize(right_text).x;
168 const float right_x = ImGui::GetWindowWidth() - text_width -
169 ImGui::GetStyle().WindowPadding.x;
170
171 ImGui::SameLine(std::max(ImGui::GetCursorPosX(), right_x));
172
173 if (state.room_dirty) {
174 ImGui::TextColored(gui::ConvertColorToImVec4(theme.warning), "%s",
175 right_text);
176 if (ImGui::IsItemHovered()) {
177 ImGui::SetTooltip(
178 tr("Room has pending editor changes. Apply Room writes them to the "
179 "loaded ROM buffer."));
180 }
181 } else {
182 ImGui::TextDisabled("%s", right_text);
183 }
184 }
185
186 ImGui::EndChild();
187}
188
190 const DungeonCanvasViewer& viewer, const char* tool_mode, bool room_dirty) {
192 state.tool_mode = tool_mode;
193 state.room_dirty = room_dirty;
194 state.room_id = viewer.current_room_id();
195
196 // Zoom from canvas
197 float scale = viewer.canvas().GetGlobalScale();
198 state.zoom_percent = static_cast<int>(scale * 100.0f + 0.5f);
199
200 // Selection from object interaction
201 const auto& interaction =
202 const_cast<DungeonCanvasViewer&>(viewer).object_interaction();
203 const auto snapshot = BuildDungeonSelectionSnapshot(
204 interaction, viewer.rooms(), viewer.current_room_id());
205 state.selection_count = static_cast<int>(snapshot.count);
206 state.selection_layer = snapshot.selection_layer;
208
209 return state;
210}
211
212} // namespace yaze::editor
static void Draw(const DungeonStatusBarState &state)
static DungeonStatusBarState BuildState(const DungeonCanvasViewer &viewer, const char *tool_mode, bool room_dirty)
float GetGlobalScale() const
Definition canvas.h:382
RAII guard for ImGui style colors.
Definition style_guard.h:27
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_CIRCLE
Definition icons.h:411
#define ICON_MD_REDO
Definition icons.h:1570
#define ICON_MD_BUILD
Definition icons.h:328
#define ICON_MD_ZOOM_IN
Definition icons.h:2194
#define ICON_MD_SELECT_ALL
Definition icons.h:1680
#define ICON_MD_WORKSPACES
Definition icons.h:2186
#define ICON_MD_UNDO
Definition icons.h:2039
float CalcStatusIconButtonWidth(const char *icon, float button_height)
Editors are the view controllers for the application.
DungeonSelectionSnapshot BuildDungeonSelectionSnapshot(const DungeonObjectInteraction &interaction, const DungeonRoomStore *rooms, int room_id)
std::string GetDungeonSelectionSummaryText(const DungeonSelectionSnapshot &snapshot)
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.
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
static constexpr float kStatusBarHeight
Definition ui_config.h:21
static constexpr float kIconButtonSmall
Definition ui_config.h:61