yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
status_bar.cc
Go to the documentation of this file.
2
3#include <algorithm>
4
5#include "absl/strings/str_format.h"
14#include "imgui/imgui.h"
15#include "rom/rom.h"
16
17namespace yaze {
18namespace editor {
19
20namespace {
21
35
36const char* WorkflowStatusIcon(const ProjectWorkflowStatus& status,
37 const char* fallback) {
38 switch (status.state) {
40 return ICON_MD_SYNC;
44 return ICON_MD_ERROR;
46 default:
47 return fallback;
48 }
49}
50
52 const char* fallback_icon) {
53 if (!status.visible) {
54 return 0.0f;
55 }
56 const std::string label =
57 absl::StrFormat("%s %s", WorkflowStatusIcon(status, fallback_icon),
58 status.summary.empty() ? status.label : status.summary);
59 return ImGui::CalcTextSize(label.c_str()).x + 24.0f;
60}
61
62// Applies interactive affordances (hand cursor, click callback, tooltip) to the
63// most recently drawn segment item. Call immediately after the segment text
64// has been rendered so `ImGui::IsItemHovered()` / `IsItemClicked()` refer to it.
66 const bool interactive = static_cast<bool>(opts.on_click);
67 if (interactive && ImGui::IsItemHovered()) {
68 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
69 }
70 if (!opts.tooltip.empty() && ImGui::IsItemHovered()) {
71 ImGui::SetTooltip("%s", opts.tooltip.c_str());
72 }
73 if (interactive && ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
74 opts.on_click();
75 }
76}
77
78} // namespace
79
80float StatusBar::GetHeight() const {
81 if (!enabled_) {
82 return 0.0f;
83 }
86 }
87 return kStatusBarHeight;
88}
89
91 context_ = context;
92 if (context_) {
93 // Subscribe to UI status updates
95 [this](const StatusUpdateEvent& e) { HandleStatusUpdate(e); });
96
97 // Subscribe to selection changes from any editor
99 [this](const SelectionChangedEvent& e) {
100 if (e.IsEmpty()) {
102 } else {
103 SetSelection(static_cast<int>(e.Count()));
104 }
105 });
106
107 // Subscribe to zoom changes from any canvas
109 [this](const ZoomChangedEvent& e) { SetZoom(e.new_zoom); });
110 }
111}
112
114 switch (event.type) {
116 SetCursorPosition(event.x, event.y,
117 event.text.empty() ? nullptr : event.text.c_str());
118 break;
120 SetSelection(event.count, event.width, event.height);
121 break;
123 SetZoom(event.zoom);
124 break;
126 SetEditorMode(event.text);
127 break;
129 // Compatibility path: older callers may still emit Clear, but we no
130 // longer allow that event to wipe cursor/selection/zoom segments that can
131 // be owned by legacy event producers outside the new ContributeStatus()
132 // path.
134 break;
135 default:
136 break;
137 }
138}
139
140void StatusBar::SetSessionInfo(size_t session_id, size_t total_sessions) {
141 session_id_ = session_id;
142 total_sessions_ = total_sessions;
143}
144
145void StatusBar::SetCursorPosition(int x, int y, const char* label) {
146 has_cursor_ = true;
147 cursor_x_ = x;
148 cursor_y_ = y;
149 cursor_label_ = label ? label : "Pos";
150 cursor_options_ = {};
151}
152
153void StatusBar::SetCursorPosition(int x, int y, const char* label,
154 StatusBarSegmentOptions options) {
155 has_cursor_ = true;
156 cursor_x_ = x;
157 cursor_y_ = y;
158 cursor_label_ = label ? label : "Pos";
159 cursor_options_ = std::move(options);
160}
161
163 has_cursor_ = false;
164 cursor_options_ = {};
165}
166
167void StatusBar::SetSelection(int count, int width, int height) {
168 has_selection_ = true;
169 selection_count_ = count;
170 selection_width_ = width;
171 selection_height_ = height;
172}
173
175 has_selection_ = false;
176}
177
178void StatusBar::SetZoom(float level) {
179 has_zoom_ = true;
180 zoom_level_ = level;
181 zoom_options_ = {};
182}
183
184void StatusBar::SetZoom(float level, StatusBarSegmentOptions options) {
185 has_zoom_ = true;
186 zoom_level_ = level;
187 zoom_options_ = std::move(options);
188}
189
191 has_zoom_ = false;
192 zoom_options_ = {};
193}
194
195void StatusBar::SetEditorMode(const std::string& mode) {
196 has_mode_ = true;
197 editor_mode_ = mode;
198 mode_options_ = {};
199}
200
201void StatusBar::SetEditorMode(const std::string& mode,
202 StatusBarSegmentOptions options) {
203 has_mode_ = true;
204 editor_mode_ = mode;
205 mode_options_ = std::move(options);
206}
207
209 has_mode_ = false;
210 editor_mode_.clear();
211 mode_options_ = {};
212}
213
214void StatusBar::SetCustomSegment(const std::string& key,
215 const std::string& value) {
217}
218
219void StatusBar::SetCustomSegment(const std::string& key,
220 const std::string& value,
221 StatusBarSegmentOptions options) {
222 for (auto& seg : custom_segments_) {
223 if (seg.key == key) {
224 seg.value = value;
225 seg.options = std::move(options);
226 return;
227 }
228 }
229 custom_segments_.push_back({key, value, std::move(options)});
230}
231
232void StatusBar::ClearCustomSegment(const std::string& key) {
233 custom_segments_.erase(
234 std::remove_if(custom_segments_.begin(), custom_segments_.end(),
235 [&](const CustomSegment& s) { return s.key == key; }),
236 custom_segments_.end());
237}
238
246
251
252void StatusBar::SetAgentInfo(const std::string& provider,
253 const std::string& model, bool active) {
254 has_agent_ = true;
255 agent_provider_ = provider;
256 agent_model_ = model;
257 agent_active_ = active;
258}
259
261 has_agent_ = false;
262 agent_provider_.clear();
263 agent_model_.clear();
264 agent_active_ = false;
265}
266
268 if (!enabled_) {
269 return;
270 }
271
272 const ImGuiViewport* viewport = ImGui::GetMainViewport();
273
274 // Position at very bottom of viewport, above the safe area (home indicator).
275 // The dockspace is already reduced by GetBottomLayoutOffset() in controller.cc.
276 const float bar_height = GetHeight();
277 const float bottom_safe = gui::LayoutHelpers::GetSafeAreaInsets().bottom;
278 const float bar_y =
279 viewport->WorkPos.y + viewport->WorkSize.y - bar_height - bottom_safe;
280 const bool touch_mode = gui::LayoutHelpers::IsTouchDevice();
281 const ImVec2 panel_padding =
282 touch_mode ? ImVec2(14.0f, 7.0f) : ImVec2(8.0f, 4.0f);
283 const ImVec2 panel_spacing =
284 touch_mode ? ImVec2(12.0f, 0.0f) : ImVec2(8.0f, 0.0f);
285
286 // Status bar background - slightly elevated surface
287 ImVec4 bar_bg = gui::GetSurfaceContainerVec4();
288 ImVec4 bar_border = gui::GetOutlineVec4();
289
290 ImGuiWindowFlags extra_flags =
291 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
292 ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNavFocus |
293 ImGuiWindowFlags_NoBringToFrontOnFocus;
294
295 gui::FixedPanel bar("##StatusBar", ImVec2(viewport->WorkPos.x, bar_y),
296 ImVec2(viewport->WorkSize.x, bar_height),
297 {.bg = bar_bg,
298 .border = bar_border,
299 .padding = panel_padding,
300 .spacing = panel_spacing,
301 .border_size = 1.0f},
302 extra_flags);
303
304 if (bar) {
305 // Left section: ROM info, Session, Dirty status
307
308 if (total_sessions_ > 1) {
311 }
312
313 // Middle section: Editor context (cursor, selection)
314 if (has_cursor_) {
317 }
318
319 if (has_selection_) {
322 }
323
324 // Custom segments
326
327 // Right section: Zoom, Mode (right-aligned)
328 float right_section_width = 0.0f;
329 std::string agent_label;
330 if (has_agent_) {
331 agent_label = agent_model_.empty() ? agent_provider_ : agent_model_;
332 if (agent_label.empty()) {
333 agent_label = "Agent";
334 }
335 const size_t max_len = 20;
336 if (agent_label.size() > max_len) {
337 agent_label = agent_label.substr(0, max_len - 3) + "...";
338 }
339 std::string label = std::string(ICON_MD_SMART_TOY " ") + agent_label;
340 right_section_width += ImGui::CalcTextSize(label.c_str()).x +
341 ImGui::GetStyle().FramePadding.x * 2.0f + 10.0f;
342 }
343 if (has_zoom_) {
344 right_section_width += ImGui::CalcTextSize("100%").x + 20.0f;
345 }
346 if (has_mode_) {
347 right_section_width +=
348 ImGui::CalcTextSize(editor_mode_.c_str()).x + 30.0f;
349 }
350 right_section_width += WorkflowStatusWidth(build_status_, ICON_MD_BUILD);
351 right_section_width += WorkflowStatusWidth(run_status_, ICON_MD_PLAY_ARROW);
353 right_section_width += 16.0f;
354 }
355 if (run_status_.visible) {
356 right_section_width += 16.0f;
357 }
358
359 if (right_section_width > 0.0f) {
360 float available = ImGui::GetContentRegionAvail().x;
361 if (available > right_section_width + 20.0f) {
362 ImGui::SameLine(ImGui::GetWindowWidth() - right_section_width - 16.0f);
363
368 }
369 }
370
371 if (run_status_.visible) {
373 if (has_agent_ || has_zoom_ || has_mode_) {
375 }
376 }
377
378 if (has_agent_) {
380 if (has_zoom_ || has_mode_) {
382 }
383 }
384
385 if (has_zoom_) {
387 if (has_mode_) {
389 }
390 }
391
392 if (has_mode_) {
394 }
395 }
396 }
397 }
398}
399
401 std::string label = agent_model_.empty() ? agent_provider_ : agent_model_;
402 if (label.empty()) {
403 label = "Agent";
404 }
405 const size_t max_len = 20;
406 if (label.size() > max_len) {
407 label = label.substr(0, max_len - 3) + "...";
408 }
409 std::string button_label = std::string(ICON_MD_SMART_TOY " ") + label;
410
411 ImVec4 text_color =
413 gui::StyleColorGuard agent_colors({
414 {ImGuiCol_Text, text_color},
415 {ImGuiCol_Button, gui::GetSurfaceContainerHighVec4()},
416 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighestVec4()},
417 });
418 if (gui::ThemedButton(button_label.c_str())) {
421 }
422 }
423
424 if (ImGui::IsItemHovered()) {
425 ImGui::BeginTooltip();
426 ImGui::Text("%s Agent", ICON_MD_SMART_TOY);
427 ImGui::TextDisabled("Provider: %s", agent_provider_.empty()
428 ? "mock"
429 : agent_provider_.c_str());
430 ImGui::TextDisabled(
431 "Model: %s", agent_model_.empty() ? "not set" : agent_model_.c_str());
432 ImGui::TextDisabled("Toggle chat panel");
433 ImGui::EndTooltip();
434 }
435}
436
438 const char* default_icon) {
439 const std::string display =
440 status.summary.empty() ? status.label : status.summary;
441 gui::ColoredTextF(WorkflowStatusColor(status.state), "%s %s",
442 WorkflowStatusIcon(status, default_icon), display.c_str());
443
444 if (ImGui::IsItemHovered()) {
445 ImGui::BeginTooltip();
446 ImGui::Text("%s", status.label.c_str());
447 if (!status.summary.empty()) {
448 ImGui::TextDisabled("%s", status.summary.c_str());
449 }
450 if (!status.detail.empty()) {
451 ImGui::Separator();
452 ImGui::TextWrapped("%s", status.detail.c_str());
453 }
454 if (!status.output_tail.empty()) {
455 ImGui::Separator();
456 ImGui::TextWrapped("%s", status.output_tail.c_str());
457 }
458 ImGui::EndTooltip();
459 }
460}
461
463 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
464
465 if (rom_ && rom_->is_loaded()) {
466 // ROM name
467 gui::ColoredTextF(ImGui::GetStyleColorVec4(ImGuiCol_Text), "%s %s",
469
470 // Dirty indicator
471 if (rom_->dirty()) {
472 ImGui::SameLine();
474 gui::ConvertColorToImVec4(theme.warning));
475
476 if (ImGui::IsItemHovered()) {
477 ImGui::SetTooltip("Unsaved changes");
478 }
479 }
480 } else {
481 gui::ColoredTextF(gui::GetTextSecondaryVec4(), "%s No ROM loaded",
483 }
484}
485
489
490 if (ImGui::IsItemHovered()) {
491 ImGui::SetTooltip("Session %zu of %zu", session_id_ + 1, total_sessions_);
492 }
493}
494
498 ApplySegmentInteraction(cursor_options_);
499}
500
502 gui::StyleColorGuard selection_color(ImGuiCol_Text,
504
505 if (selection_width_ > 0 && selection_height_ > 0) {
506 ImGui::Text("%s %d (%dx%d)", ICON_MD_SELECT_ALL, selection_count_,
508 } else if (selection_count_ > 0) {
509 ImGui::Text("%s %d selected", ICON_MD_SELECT_ALL, selection_count_);
510 }
511}
512
514 int zoom_percent = static_cast<int>(zoom_level_ * 100.0f);
516 zoom_percent);
517
518 // When no explicit tooltip is configured, fall back to the legacy zoom
519 // readout so existing behavior is preserved.
521 if (ImGui::IsItemHovered()) {
522 ImGui::SetTooltip("Zoom: %d%%", zoom_percent);
523 }
524 } else {
525 ApplySegmentInteraction(zoom_options_);
526 }
527}
528
531 ApplySegmentInteraction(mode_options_);
532}
533
535 for (const auto& segment : custom_segments_) {
538 segment.key.c_str(), segment.value.c_str());
539 ApplySegmentInteraction(segment.options);
540 }
541}
542
544 ImGui::SameLine();
546 ImGui::SameLine();
547}
548
549} // namespace editor
550} // namespace yaze
HandlerId Subscribe(std::function< void(const T &)> handler)
Definition event_bus.h:22
bool dirty() const
Definition rom.h:133
auto short_name() const
Definition rom.h:147
bool is_loaded() const
Definition rom.h:132
Instance-based runtime context replacing ContentRegistry::Context.
static constexpr float kStatusBarHeight
Definition status_bar.h:204
void HandleStatusUpdate(const StatusUpdateEvent &event)
GlobalEditorContext * context_
Definition status_bar.h:222
float GetHeight() const
Get the height of the status bar.
Definition status_bar.cc:80
std::vector< CustomSegment > custom_segments_
Definition status_bar.h:259
void SetSessionInfo(size_t session_id, size_t total_sessions)
Set session information.
StatusBarSegmentOptions cursor_options_
Definition status_bar.h:235
static constexpr float kStatusBarTouchHeight
Definition status_bar.h:205
std::string agent_provider_
Definition status_bar.h:264
void ClearCursorPosition()
Clear cursor position (no cursor in editor)
std::function< void()> agent_toggle_callback_
Definition status_bar.h:266
void SetSelection(int count, int width=0, int height=0)
Set selection information.
StatusBarSegmentOptions zoom_options_
Definition status_bar.h:246
void ClearZoom()
Clear zoom display.
StatusBarSegmentOptions mode_options_
Definition status_bar.h:251
void SetCustomSegment(const std::string &key, const std::string &value)
Set a custom segment with key-value pair.
void SetZoom(float level)
Set current zoom level.
void DrawProjectWorkflowSegment(const ProjectWorkflowStatus &status, const char *default_icon)
std::string cursor_label_
Definition status_bar.h:234
void ClearSelection()
Clear selection info.
void ClearEditorMode()
Clear editor mode display.
void Initialize(GlobalEditorContext *context)
Definition status_bar.cc:90
void ClearEditorContributions()
Clear frame-scoped editor contributions.
void SetCursorPosition(int x, int y, const char *label="Pos")
Set cursor/mouse position in editor coordinates.
void SetEditorMode(const std::string &mode)
Set the current editor mode or tool.
ProjectWorkflowStatus build_status_
Definition status_bar.h:268
void Draw()
Draw the status bar.
void ClearAllContext()
Clear all context (cursor, selection, zoom, mode, custom)
void ClearCustomSegment(const std::string &key)
Remove a custom segment.
ProjectWorkflowStatus run_status_
Definition status_bar.h:269
void SetAgentInfo(const std::string &provider, const std::string &model, bool active)
RAII for fixed-position panels (activity bar, side panel, status bar).
static SafeAreaInsets GetSafeAreaInsets()
RAII guard for ImGui style colors.
Definition style_guard.h:27
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_PLAY_ARROW
Definition icons.h:1479
#define ICON_MD_ERROR
Definition icons.h:686
#define ICON_MD_LAYERS
Definition icons.h:1068
#define ICON_MD_CHECK_CIRCLE
Definition icons.h:400
#define ICON_MD_DESCRIPTION
Definition icons.h:539
#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_SYNC
Definition icons.h:1919
#define ICON_MD_FIBER_MANUAL_RECORD
Definition icons.h:739
#define ICON_MD_SMART_TOY
Definition icons.h:1781
const char * WorkflowStatusIcon(const ProjectWorkflowStatus &status, const char *fallback)
Definition status_bar.cc:36
ImVec4 WorkflowStatusColor(ProjectWorkflowState state)
Definition status_bar.cc:22
void ApplySegmentInteraction(const StatusBarSegmentOptions &opts)
Definition status_bar.cc:65
float WorkflowStatusWidth(const ProjectWorkflowStatus &status, const char *fallback_icon)
Definition status_bar.cc:51
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
void ColoredText(const char *text, const ImVec4 &color)
ImVec4 GetSurfaceContainerHighestVec4()
ImVec4 GetSuccessColor()
Definition ui_helpers.cc:48
bool ThemedButton(const char *label, const ImVec2 &size, const char *panel_id, const char *anim_id)
Draw a standard text button with theme colors.
ImVec4 GetPrimaryVec4()
ImVec4 GetTextSecondaryVec4()
void ColoredTextF(const ImVec4 &color, const char *fmt,...)
ImVec4 GetErrorColor()
Definition ui_helpers.cc:58
ImVec4 GetSurfaceContainerHighVec4()
ImVec4 GetInfoColor()
Definition ui_helpers.cc:63
ImVec4 GetOutlineVec4()
ImVec4 GetSurfaceContainerVec4()
Published when selection changes in any editor.
Optional behavior for an interactive status bar segment.
Definition status_bar.h:27
std::function< void()> on_click
Definition status_bar.h:28
Published when zoom level changes in any canvas/editor.