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#include "util/i18n/tr.h"
3
4#include <algorithm>
5
6#include "absl/strings/str_format.h"
10#include "app/gui/core/style.h"
15#include "imgui/imgui.h"
16#include "rom/rom.h"
17
18namespace yaze {
19namespace editor {
20
21namespace {
22
36
37const char* WorkflowStatusIcon(const ProjectWorkflowStatus& status,
38 const char* fallback) {
39 switch (status.state) {
41 return ICON_MD_SYNC;
45 return ICON_MD_ERROR;
47 default:
48 return fallback;
49 }
50}
51
53 const char* fallback_icon) {
54 if (!status.visible) {
55 return 0.0f;
56 }
57 const std::string label =
58 absl::StrFormat("%s %s", WorkflowStatusIcon(status, fallback_icon),
59 status.summary.empty() ? status.label : status.summary);
60 return ImGui::CalcTextSize(label.c_str()).x + 24.0f;
61}
62
63// Applies interactive affordances (hand cursor, click callback, tooltip) to the
64// most recently drawn segment item. Call immediately after the segment text
65// has been rendered so `ImGui::IsItemHovered()` / `IsItemClicked()` refer to it.
67 const bool interactive = static_cast<bool>(opts.on_click);
68 if (interactive && ImGui::IsItemHovered()) {
69 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
70 }
71 if (!opts.tooltip.empty() && ImGui::IsItemHovered()) {
72 ImGui::SetTooltip("%s", opts.tooltip.c_str());
73 }
74 if (interactive && ImGui::IsItemClicked(ImGuiMouseButton_Left)) {
75 opts.on_click();
76 }
77}
78
79} // namespace
80
81float StatusBar::GetHeight() const {
82 if (!enabled_) {
83 return 0.0f;
84 }
87 }
88 return kStatusBarHeight;
89}
90
92 context_ = context;
93 if (context_) {
94 // Subscribe to UI status updates
96 [this](const StatusUpdateEvent& e) { HandleStatusUpdate(e); });
97
98 // Subscribe to selection changes from any editor
100 [this](const SelectionChangedEvent& e) {
101 if (e.IsEmpty()) {
103 } else {
104 SetSelection(static_cast<int>(e.Count()));
105 }
106 });
107
108 // Subscribe to zoom changes from any canvas
110 [this](const ZoomChangedEvent& e) { SetZoom(e.new_zoom); });
111 }
112}
113
115 switch (event.type) {
117 SetCursorPosition(event.x, event.y,
118 event.text.empty() ? nullptr : event.text.c_str());
119 break;
121 SetSelection(event.count, event.width, event.height);
122 break;
124 SetZoom(event.zoom);
125 break;
127 SetEditorMode(event.text);
128 break;
130 // Compatibility path: older callers may still emit Clear, but we no
131 // longer allow that event to wipe cursor/selection/zoom segments that can
132 // be owned by legacy event producers outside the new ContributeStatus()
133 // path.
135 break;
136 default:
137 break;
138 }
139}
140
141void StatusBar::SetSessionInfo(size_t session_id, size_t total_sessions) {
142 session_id_ = session_id;
143 total_sessions_ = total_sessions;
144}
145
146void StatusBar::SetCursorPosition(int x, int y, const char* label) {
147 has_cursor_ = true;
148 cursor_x_ = x;
149 cursor_y_ = y;
150 cursor_label_ = label ? label : "Pos";
151 cursor_options_ = {};
152}
153
154void StatusBar::SetCursorPosition(int x, int y, const char* label,
155 StatusBarSegmentOptions options) {
156 has_cursor_ = true;
157 cursor_x_ = x;
158 cursor_y_ = y;
159 cursor_label_ = label ? label : "Pos";
160 cursor_options_ = std::move(options);
161}
162
164 has_cursor_ = false;
165 cursor_options_ = {};
166}
167
168void StatusBar::SetSelection(int count, int width, int height) {
169 has_selection_ = true;
170 selection_count_ = count;
171 selection_width_ = width;
172 selection_height_ = height;
173}
174
176 has_selection_ = false;
177}
178
179void StatusBar::SetZoom(float level) {
180 has_zoom_ = true;
181 zoom_level_ = level;
182 zoom_options_ = {};
183}
184
185void StatusBar::SetZoom(float level, StatusBarSegmentOptions options) {
186 has_zoom_ = true;
187 zoom_level_ = level;
188 zoom_options_ = std::move(options);
189}
190
192 has_zoom_ = false;
193 zoom_options_ = {};
194}
195
196void StatusBar::SetEditorMode(const std::string& mode) {
197 has_mode_ = true;
198 editor_mode_ = mode;
199 mode_options_ = {};
200}
201
202void StatusBar::SetEditorMode(const std::string& mode,
203 StatusBarSegmentOptions options) {
204 has_mode_ = true;
205 editor_mode_ = mode;
206 mode_options_ = std::move(options);
207}
208
210 has_mode_ = false;
211 editor_mode_.clear();
212 mode_options_ = {};
213}
214
215void StatusBar::SetCustomSegment(const std::string& key,
216 const std::string& value) {
218}
219
220void StatusBar::SetCustomSegment(const std::string& key,
221 const std::string& value,
222 StatusBarSegmentOptions options) {
223 for (auto& seg : custom_segments_) {
224 if (seg.key == key) {
225 seg.value = value;
226 seg.options = std::move(options);
227 return;
228 }
229 }
230 custom_segments_.push_back({key, value, std::move(options)});
231}
232
233void StatusBar::ClearCustomSegment(const std::string& key) {
234 custom_segments_.erase(
235 std::remove_if(custom_segments_.begin(), custom_segments_.end(),
236 [&](const CustomSegment& s) { return s.key == key; }),
237 custom_segments_.end());
238}
239
247
252
253void StatusBar::SetAgentInfo(const std::string& provider,
254 const std::string& model, bool active) {
255 has_agent_ = true;
256 agent_provider_ = provider;
257 agent_model_ = model;
258 agent_active_ = active;
259}
260
262 has_agent_ = false;
263 agent_provider_.clear();
264 agent_model_.clear();
265 agent_active_ = false;
266}
267
269 if (!enabled_) {
270 return;
271 }
272
273 const ImGuiViewport* viewport = ImGui::GetMainViewport();
274
275 // Position at very bottom of viewport, above the safe area (home indicator).
276 // The dockspace is already reduced by GetBottomLayoutOffset() in controller.cc.
277 const float bar_height = GetHeight();
278 const float bottom_safe = gui::LayoutHelpers::GetSafeAreaInsets().bottom;
279 const float bar_y =
280 viewport->WorkPos.y + viewport->WorkSize.y - bar_height - bottom_safe;
281 const bool touch_mode = gui::LayoutHelpers::IsTouchDevice();
282 const ImVec2 panel_padding =
283 touch_mode ? ImVec2(14.0f, 7.0f) : ImVec2(8.0f, 4.0f);
284 const ImVec2 panel_spacing =
285 touch_mode ? ImVec2(12.0f, 0.0f) : ImVec2(8.0f, 0.0f);
286
287 // Status bar background - slightly elevated surface
288 ImVec4 bar_bg = gui::GetSurfaceContainerVec4();
289 ImVec4 bar_border = gui::GetOutlineVec4();
290
291 ImGuiWindowFlags extra_flags =
292 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse |
293 ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNavFocus |
294 ImGuiWindowFlags_NoBringToFrontOnFocus;
295
296 gui::FixedPanel bar("##StatusBar", ImVec2(viewport->WorkPos.x, bar_y),
297 ImVec2(viewport->WorkSize.x, bar_height),
298 {.bg = bar_bg,
299 .border = bar_border,
300 .padding = panel_padding,
301 .spacing = panel_spacing,
302 .border_size = 1.0f},
303 extra_flags);
304
305 if (bar) {
306 // Left section: ROM info, Session, Dirty status
308
309 if (total_sessions_ > 1) {
312 }
313
314 // Middle section: Editor context (cursor, selection)
315 if (has_cursor_) {
318 }
319
320 if (has_selection_) {
323 }
324
325 // Custom segments
327
328 // Right section: Zoom, Mode (right-aligned)
329 float right_section_width = 0.0f;
330 std::string agent_label;
331 if (has_agent_) {
332 agent_label = agent_model_.empty() ? agent_provider_ : agent_model_;
333 if (agent_label.empty()) {
334 agent_label = "Agent";
335 }
336 const size_t max_len = 20;
337 if (agent_label.size() > max_len) {
338 agent_label = agent_label.substr(0, max_len - 3) + "...";
339 }
340 std::string label = std::string(ICON_MD_SMART_TOY " ") + agent_label;
341 right_section_width += ImGui::CalcTextSize(label.c_str()).x +
342 ImGui::GetStyle().FramePadding.x * 2.0f + 10.0f;
343 }
344 if (has_zoom_) {
345 right_section_width += ImGui::CalcTextSize("100%").x + 20.0f;
346 }
347 if (has_mode_) {
348 right_section_width +=
349 ImGui::CalcTextSize(editor_mode_.c_str()).x + 30.0f;
350 }
351 right_section_width += WorkflowStatusWidth(build_status_, ICON_MD_BUILD);
352 right_section_width += WorkflowStatusWidth(run_status_, ICON_MD_PLAY_ARROW);
354 right_section_width += 16.0f;
355 }
356 if (run_status_.visible) {
357 right_section_width += 16.0f;
358 }
359
360 if (right_section_width > 0.0f) {
361 float available = ImGui::GetContentRegionAvail().x;
362 if (available > right_section_width + 20.0f) {
363 ImGui::SameLine(ImGui::GetWindowWidth() - right_section_width - 16.0f);
364
369 }
370 }
371
372 if (run_status_.visible) {
374 if (has_agent_ || has_zoom_ || has_mode_) {
376 }
377 }
378
379 if (has_agent_) {
381 if (has_zoom_ || has_mode_) {
383 }
384 }
385
386 if (has_zoom_) {
388 if (has_mode_) {
390 }
391 }
392
393 if (has_mode_) {
395 }
396 }
397 }
398 }
399}
400
402 std::string label = agent_model_.empty() ? agent_provider_ : agent_model_;
403 if (label.empty()) {
404 label = "Agent";
405 }
406 const size_t max_len = 20;
407 if (label.size() > max_len) {
408 label = label.substr(0, max_len - 3) + "...";
409 }
410 std::string button_label = std::string(ICON_MD_SMART_TOY " ") + label;
411
412 ImVec4 text_color =
414 gui::StyleColorGuard agent_colors({
415 {ImGuiCol_Text, text_color},
416 {ImGuiCol_Button, gui::GetSurfaceContainerHighVec4()},
417 {ImGuiCol_ButtonHovered, gui::GetSurfaceContainerHighestVec4()},
418 });
419 if (gui::ThemedButton(button_label.c_str())) {
422 }
423 }
424
425 if (ImGui::IsItemHovered()) {
426 ImGui::BeginTooltip();
427 ImGui::Text(tr("%s Agent"), ICON_MD_SMART_TOY);
428 ImGui::TextDisabled(tr("Provider: %s"), agent_provider_.empty()
429 ? "mock"
430 : agent_provider_.c_str());
431 ImGui::TextDisabled(tr("Model: %s"), agent_model_.empty()
432 ? "not set"
433 : agent_model_.c_str());
434 ImGui::TextDisabled(tr("Toggle chat panel"));
435 ImGui::EndTooltip();
436 }
437}
438
440 const char* default_icon) {
441 const std::string display =
442 status.summary.empty() ? status.label : status.summary;
443 gui::ColoredTextF(WorkflowStatusColor(status.state), "%s %s",
444 WorkflowStatusIcon(status, default_icon), display.c_str());
445
446 if (ImGui::IsItemHovered()) {
447 ImGui::BeginTooltip();
448 ImGui::Text("%s", status.label.c_str());
449 if (!status.summary.empty()) {
450 ImGui::TextDisabled("%s", status.summary.c_str());
451 }
452 if (!status.detail.empty()) {
453 ImGui::Separator();
454 ImGui::TextWrapped("%s", status.detail.c_str());
455 }
456 if (!status.output_tail.empty()) {
457 ImGui::Separator();
458 ImGui::TextWrapped("%s", status.output_tail.c_str());
459 }
460 ImGui::EndTooltip();
461 }
462}
463
465 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
466
467 if (rom_ && rom_->is_loaded()) {
468 // ROM name
469 gui::ColoredTextF(ImGui::GetStyleColorVec4(ImGuiCol_Text), "%s %s",
471
472 // Dirty indicator
473 if (rom_->dirty()) {
474 ImGui::SameLine();
476 gui::ConvertColorToImVec4(theme.warning));
477
478 if (ImGui::IsItemHovered()) {
479 ImGui::SetTooltip(tr("Unsaved changes"));
480 }
481 }
482 } else {
483 gui::ColoredTextF(gui::GetTextSecondaryVec4(), "%s No ROM loaded",
485 }
486}
487
491
492 if (ImGui::IsItemHovered()) {
493 ImGui::SetTooltip(tr("Session %zu of %zu"), session_id_ + 1,
495 }
496}
497
501 ApplySegmentInteraction(cursor_options_);
502}
503
505 gui::StyleColorGuard selection_color(ImGuiCol_Text,
507
508 if (selection_width_ > 0 && selection_height_ > 0) {
509 ImGui::Text(tr("%s %d (%dx%d)"), ICON_MD_SELECT_ALL, selection_count_,
511 } else if (selection_count_ > 0) {
512 ImGui::Text(tr("%s %d selected"), ICON_MD_SELECT_ALL, selection_count_);
513 }
514}
515
517 int zoom_percent = static_cast<int>(zoom_level_ * 100.0f);
519 zoom_percent);
520
521 // When no explicit tooltip is configured, fall back to the legacy zoom
522 // readout so existing behavior is preserved.
524 if (ImGui::IsItemHovered()) {
525 ImGui::SetTooltip(tr("Zoom: %d%%"), zoom_percent);
526 }
527 } else {
528 ApplySegmentInteraction(zoom_options_);
529 }
530}
531
534 ApplySegmentInteraction(mode_options_);
535}
536
538 for (const auto& segment : custom_segments_) {
541 segment.key.c_str(), segment.value.c_str());
542 ApplySegmentInteraction(segment.options);
543 }
544}
545
547 ImGui::SameLine();
549 ImGui::SameLine();
550}
551
552} // namespace editor
553} // namespace yaze
HandlerId Subscribe(std::function< void(const T &)> handler)
Definition event_bus.h:22
bool dirty() const
Definition rom.h:145
auto short_name() const
Definition rom.h:159
bool is_loaded() const
Definition rom.h:144
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:81
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:91
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:37
ImVec4 WorkflowStatusColor(ProjectWorkflowState state)
Definition status_bar.cc:23
void ApplySegmentInteraction(const StatusBarSegmentOptions &opts)
Definition status_bar.cc:66
float WorkflowStatusWidth(const ProjectWorkflowStatus &status, const char *fallback_icon)
Definition status_bar.cc:52
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:49
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:59
ImVec4 GetSurfaceContainerHighVec4()
ImVec4 GetInfoColor()
Definition ui_helpers.cc:64
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.