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
3#include <algorithm>
4#include <cstdio>
5
13#include "imgui/imgui.h"
14
15namespace yaze::editor {
16
17namespace {
18
19float CalcStatusIconButtonWidth(const char* icon, float button_height) {
20 if (!icon || !*icon) {
21 return button_height;
22 }
23
24 const ImGuiStyle& style = ImGui::GetStyle();
25 const float glyph_width = ImGui::CalcTextSize(icon).x;
26 const float extra = std::max(2.0f, style.FramePadding.x);
27 return std::max(button_height,
28 std::ceil(glyph_width + (style.FramePadding.x * 2.0f) +
29 extra));
30}
31
32} // namespace
33
35 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
36
37 // Reserve a fixed-height bar at the bottom, respecting UIConfig minimum
38 const float font_bar =
39 ImGui::GetFontSize() + ImGui::GetStyle().FramePadding.y * 2.0f;
40 const float bar_height = std::max(font_bar, gui::UIConfig::kStatusBarHeight);
41
42 gui::StyleColorGuard bar_colors({
43 {ImGuiCol_ChildBg, gui::ConvertColorToImVec4(theme.frame_bg)},
44 });
45
46 ImGui::BeginChild(
47 "##DungeonStatusBar", ImVec2(-1, bar_height), false,
48 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
49
50 const float spacing = ImGui::GetStyle().ItemSpacing.x;
51
52 // Tool mode indicator
53 ImGui::AlignTextToFramePadding();
54 ImGui::TextDisabled(ICON_MD_BUILD);
55 ImGui::SameLine(0, 4);
56 ImGui::Text("%s", state.tool_mode);
57 if (state.workflow_mode != nullptr && state.workflow_mode[0] != '\0') {
58 ImGui::SameLine(0, spacing);
59 ImGui::TextDisabled("|");
60 ImGui::SameLine(0, spacing);
61 const ImVec4 workflow_color =
62 state.workflow_primary ? gui::ConvertColorToImVec4(theme.success)
63 : gui::ConvertColorToImVec4(theme.warning);
64 ImGui::TextColored(workflow_color, "%s %s", ICON_MD_WORKSPACES,
65 state.workflow_mode);
66 }
67 ImGui::SameLine(0, spacing * 2);
68
69 // Separator
70 ImGui::TextDisabled("|");
71 ImGui::SameLine(0, spacing * 2);
72
73 // Undo/Redo buttons with depth indicator
74 {
75 const float button_height =
76 std::max(gui::UIConfig::kIconButtonSmall + 4.0f, bar_height - 4.0f);
77 const ImVec2 undo_size(
78 CalcStatusIconButtonWidth(ICON_MD_UNDO, button_height), button_height);
79 const ImVec2 redo_size(
80 CalcStatusIconButtonWidth(ICON_MD_REDO, button_height), button_height);
81
82 if (!state.can_undo)
83 ImGui::BeginDisabled();
84
85 // Build tooltip string for undo
86 char undo_tip[128];
87 snprintf(undo_tip, sizeof(undo_tip), "Undo%s%s",
88 state.undo_desc ? ": " : "",
89 state.undo_desc ? state.undo_desc : "");
90 if (gui::ThemedIconButton(ICON_MD_UNDO, undo_tip, undo_size)) {
91 if (state.on_undo)
92 state.on_undo();
93 }
94 if (!state.can_undo)
95 ImGui::EndDisabled();
96 ImGui::SameLine(0, 4);
97
98 if (!state.can_redo)
99 ImGui::BeginDisabled();
100
101 // Build tooltip string for redo
102 char redo_tip[128];
103 snprintf(redo_tip, sizeof(redo_tip), "Redo%s%s",
104 state.redo_desc ? ": " : "",
105 state.redo_desc ? state.redo_desc : "");
106 if (gui::ThemedIconButton(ICON_MD_REDO, redo_tip, redo_size)) {
107 if (state.on_redo)
108 state.on_redo();
109 }
110 if (!state.can_redo)
111 ImGui::EndDisabled();
112
113 if (state.undo_depth > 0) {
114 ImGui::SameLine(0, 4);
115 ImGui::TextDisabled("(%d)", state.undo_depth);
116 }
117 }
118 ImGui::SameLine(0, spacing * 2);
119
120 // Separator
121 ImGui::TextDisabled("|");
122 ImGui::SameLine(0, spacing * 2);
123
124 // Selection summary
125 if (state.selection_count > 0) {
126 ImGui::TextDisabled(ICON_MD_SELECT_ALL);
127 ImGui::SameLine(0, 4);
128 if (state.selection_layer >= 0) {
129 ImGui::Text("%d obj, L%d", state.selection_count,
130 state.selection_layer + 1);
131 } else {
132 ImGui::Text("%d obj", state.selection_count);
133 }
134 } else {
135 ImGui::TextDisabled("No selection");
136 }
137 ImGui::SameLine(0, spacing * 2);
138
139 // Separator
140 ImGui::TextDisabled("|");
141 ImGui::SameLine(0, spacing * 2);
142
143 // Zoom level
144 ImGui::TextDisabled(ICON_MD_ZOOM_IN);
145 ImGui::SameLine(0, 4);
146 ImGui::Text("%d%%", state.zoom_percent);
147 ImGui::SameLine(0, spacing * 2);
148
149 // Separator
150 ImGui::TextDisabled("|");
151 ImGui::SameLine(0, spacing * 2);
152
153 // Cursor tile coordinates
154 if (state.cursor_tile_x >= 0 && state.cursor_tile_y >= 0) {
155 ImGui::TextDisabled(ICON_MD_MY_LOCATION);
156 ImGui::SameLine(0, 4);
157 ImGui::Text("(%d, %d)", state.cursor_tile_x, state.cursor_tile_y);
158 } else {
159 ImGui::TextDisabled(ICON_MD_MY_LOCATION " --");
160 }
161
162 // Right-aligned section: dirty indicator + room ID
163 {
164 char right_text[64];
165 if (state.room_id >= 0) {
166 if (state.room_dirty) {
167 snprintf(right_text, sizeof(right_text), ICON_MD_CIRCLE " Room 0x%03X",
168 state.room_id);
169 } else {
170 snprintf(right_text, sizeof(right_text), "Room 0x%03X", state.room_id);
171 }
172 } else {
173 snprintf(right_text, sizeof(right_text), "No room");
174 }
175
176 const float text_width = ImGui::CalcTextSize(right_text).x;
177 const float right_x = ImGui::GetWindowWidth() - text_width -
178 ImGui::GetStyle().WindowPadding.x;
179
180 ImGui::SameLine(std::max(ImGui::GetCursorPosX(), right_x));
181
182 if (state.room_dirty) {
183 ImGui::TextColored(gui::ConvertColorToImVec4(theme.warning), "%s",
184 right_text);
185 if (ImGui::IsItemHovered()) {
186 ImGui::SetTooltip("Room has unsaved changes");
187 }
188 } else {
189 ImGui::TextDisabled("%s", right_text);
190 }
191 }
192
193 ImGui::EndChild();
194}
195
197 const DungeonCanvasViewer& viewer, const char* tool_mode, bool room_dirty) {
199 state.tool_mode = tool_mode;
200 state.room_dirty = room_dirty;
201 state.room_id = viewer.current_room_id();
202
203 // Zoom from canvas
204 float scale = viewer.canvas().GetGlobalScale();
205 state.zoom_percent = static_cast<int>(scale * 100.0f + 0.5f);
206
207 // Selection from object interaction
208 const auto& interaction =
209 const_cast<DungeonCanvasViewer&>(viewer).object_interaction();
210 auto selected = interaction.GetSelectedObjectIndices();
211 state.selection_count = static_cast<int>(selected.size());
212
213 // Determine layer from first selected object (if any)
214 if (!selected.empty() && viewer.rooms() && viewer.current_room_id() >= 0 &&
215 viewer.current_room_id() < static_cast<int>(viewer.rooms()->size())) {
216 const auto& objects =
217 (*viewer.rooms())[viewer.current_room_id()].GetTileObjects();
218 if (selected[0] < objects.size()) {
219 state.selection_layer = objects[selected[0]].layer_;
220 }
221 }
222
223 // Cursor coordinates from ImGui hover state on canvas
224 // Note: the actual tile coordinates are derived from the canvas hover
225 // position. Since we don't have direct access to the canvas mouse pos
226 // from outside the draw call, we leave these at -1 (the canvas viewer
227 // itself could populate this if we add a public accessor later).
228 state.cursor_tile_x = -1;
229 state.cursor_tile_y = -1;
230
231 return state;
232}
233
234} // 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:470
RAII guard for ImGui style colors.
Definition style_guard.h:27
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_MY_LOCATION
Definition icons.h:1270
#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.
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