yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
editor_card_registry.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cstdio>
5
6#include "absl/strings/str_format.h"
9#include "imgui/imgui.h"
10
11namespace yaze {
12namespace editor {
13
14// ============================================================================
15// Session Lifecycle Management
16// ============================================================================
17
18void EditorCardRegistry::RegisterSession(size_t session_id) {
19 if (session_cards_.find(session_id) == session_cards_.end()) {
20 session_cards_[session_id] = std::vector<std::string>();
21 session_card_mapping_[session_id] = std::unordered_map<std::string, std::string>();
23 LOG_INFO("EditorCardRegistry", "Registered session %zu (total: %zu)", session_id, session_count_);
24 }
25}
26
28 auto it = session_cards_.find(session_id);
29 if (it != session_cards_.end()) {
30 UnregisterSessionCards(session_id);
31 session_cards_.erase(it);
32 session_card_mapping_.erase(session_id);
34
35 // Reset active session if it was the one being removed
36 if (active_session_ == session_id) {
38 if (!session_cards_.empty()) {
39 active_session_ = session_cards_.begin()->first;
40 }
41 }
42
43 LOG_INFO("EditorCardRegistry", "Unregistered session %zu (total: %zu)", session_id, session_count_);
44 }
45}
46
47void EditorCardRegistry::SetActiveSession(size_t session_id) {
48 if (session_cards_.find(session_id) != session_cards_.end()) {
49 active_session_ = session_id;
50 }
51}
52
53// ============================================================================
54// Card Registration
55// ============================================================================
56
57void EditorCardRegistry::RegisterCard(size_t session_id, const CardInfo& base_info) {
58 RegisterSession(session_id); // Ensure session exists
59
60 std::string prefixed_id = MakeCardId(session_id, base_info.card_id);
61
62 // Check if already registered to avoid duplicates
63 if (cards_.find(prefixed_id) != cards_.end()) {
64 LOG_WARN("EditorCardRegistry", "Card '%s' already registered, skipping duplicate", prefixed_id.c_str());
65 return;
66 }
67
68 // Create new CardInfo with prefixed ID
69 CardInfo prefixed_info = base_info;
70 prefixed_info.card_id = prefixed_id;
71
72 // If no visibility_flag provided, create centralized one
73 if (!prefixed_info.visibility_flag) {
74 centralized_visibility_[prefixed_id] = false; // Hidden by default
75 prefixed_info.visibility_flag = &centralized_visibility_[prefixed_id];
76 }
77
78 // Register the card
79 cards_[prefixed_id] = prefixed_info;
80
81 // Track in our session mapping
82 session_cards_[session_id].push_back(prefixed_id);
83 session_card_mapping_[session_id][base_info.card_id] = prefixed_id;
84
85 LOG_INFO("EditorCardRegistry", "Registered card %s -> %s for session %zu", base_info.card_id.c_str(), prefixed_id.c_str(), session_id);
86}
87
88void EditorCardRegistry::RegisterCard(size_t session_id,
89 const std::string& card_id,
90 const std::string& display_name,
91 const std::string& icon,
92 const std::string& category,
93 const std::string& shortcut_hint,
94 int priority,
95 std::function<void()> on_show,
96 std::function<void()> on_hide,
97 bool visible_by_default) {
98 CardInfo info;
99 info.card_id = card_id;
100 info.display_name = display_name;
101 info.icon = icon;
102 info.category = category;
103 info.shortcut_hint = shortcut_hint;
104 info.priority = priority;
105 info.visibility_flag = nullptr; // Will be created in RegisterCard
106 info.on_show = on_show;
107 info.on_hide = on_hide;
108
109 RegisterCard(session_id, info);
110
111 // Set initial visibility if requested
112 if (visible_by_default) {
113 ShowCard(session_id, card_id);
114 }
115}
116
117void EditorCardRegistry::UnregisterCard(size_t session_id, const std::string& base_card_id) {
118 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
119 if (prefixed_id.empty()) {
120 return;
121 }
122
123 auto it = cards_.find(prefixed_id);
124 if (it != cards_.end()) {
125 LOG_INFO("EditorCardRegistry", "Unregistered card: %s", prefixed_id.c_str());
126 cards_.erase(it);
127 centralized_visibility_.erase(prefixed_id);
128
129 // Remove from session tracking
130 auto& session_card_list = session_cards_[session_id];
131 session_card_list.erase(
132 std::remove(session_card_list.begin(), session_card_list.end(), prefixed_id),
133 session_card_list.end());
134
135 session_card_mapping_[session_id].erase(base_card_id);
136 }
137}
138
139void EditorCardRegistry::UnregisterCardsWithPrefix(const std::string& prefix) {
140 std::vector<std::string> to_remove;
141
142 // Find all cards with the given prefix
143 for (const auto& [card_id, card_info] : cards_) {
144 if (card_id.find(prefix) == 0) { // Starts with prefix
145 to_remove.push_back(card_id);
146 }
147 }
148
149 // Remove them
150 for (const auto& card_id : to_remove) {
151 cards_.erase(card_id);
152 centralized_visibility_.erase(card_id);
153 LOG_INFO("EditorCardRegistry", "Unregistered card with prefix '%s': %s", prefix.c_str(), card_id.c_str());
154 }
155
156 // Also clean up session tracking
157 for (auto& [session_id, card_list] : session_cards_) {
158 card_list.erase(
159 std::remove_if(card_list.begin(), card_list.end(),
160 [&prefix](const std::string& id) {
161 return id.find(prefix) == 0;
162 }),
163 card_list.end());
164 }
165}
166
168 cards_.clear();
170 session_cards_.clear();
171 session_card_mapping_.clear();
172 session_count_ = 0;
173 LOG_INFO("EditorCardRegistry", "Cleared all cards");
174}
175
176// ============================================================================
177// Card Control (Programmatic, No GUI)
178// ============================================================================
179
180bool EditorCardRegistry::ShowCard(size_t session_id, const std::string& base_card_id) {
181 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
182 if (prefixed_id.empty()) {
183 return false;
184 }
185
186 auto it = cards_.find(prefixed_id);
187 if (it != cards_.end()) {
188 if (it->second.visibility_flag) {
189 *it->second.visibility_flag = true;
190 }
191 if (it->second.on_show) {
192 it->second.on_show();
193 }
194 return true;
195 }
196 return false;
197}
198
199bool EditorCardRegistry::HideCard(size_t session_id, const std::string& base_card_id) {
200 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
201 if (prefixed_id.empty()) {
202 return false;
203 }
204
205 auto it = cards_.find(prefixed_id);
206 if (it != cards_.end()) {
207 if (it->second.visibility_flag) {
208 *it->second.visibility_flag = false;
209 }
210 if (it->second.on_hide) {
211 it->second.on_hide();
212 }
213 return true;
214 }
215 return false;
216}
217
218bool EditorCardRegistry::ToggleCard(size_t session_id, const std::string& base_card_id) {
219 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
220 if (prefixed_id.empty()) {
221 return false;
222 }
223
224 auto it = cards_.find(prefixed_id);
225 if (it != cards_.end() && it->second.visibility_flag) {
226 bool new_state = !(*it->second.visibility_flag);
227 *it->second.visibility_flag = new_state;
228
229 if (new_state && it->second.on_show) {
230 it->second.on_show();
231 } else if (!new_state && it->second.on_hide) {
232 it->second.on_hide();
233 }
234 return true;
235 }
236 return false;
237}
238
239bool EditorCardRegistry::IsCardVisible(size_t session_id, const std::string& base_card_id) const {
240 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
241 if (prefixed_id.empty()) {
242 return false;
243 }
244
245 auto it = cards_.find(prefixed_id);
246 if (it != cards_.end() && it->second.visibility_flag) {
247 return *it->second.visibility_flag;
248 }
249 return false;
250}
251
252bool* EditorCardRegistry::GetVisibilityFlag(size_t session_id, const std::string& base_card_id) {
253 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
254 if (prefixed_id.empty()) {
255 return nullptr;
256 }
257
258 auto it = cards_.find(prefixed_id);
259 if (it != cards_.end()) {
260 return it->second.visibility_flag;
261 }
262 return nullptr;
263}
264
265// ============================================================================
266// Batch Operations
267// ============================================================================
268
270 auto it = session_cards_.find(session_id);
271 if (it != session_cards_.end()) {
272 for (const auto& prefixed_card_id : it->second) {
273 auto card_it = cards_.find(prefixed_card_id);
274 if (card_it != cards_.end() && card_it->second.visibility_flag) {
275 *card_it->second.visibility_flag = true;
276 if (card_it->second.on_show) {
277 card_it->second.on_show();
278 }
279 }
280 }
281 }
282}
283
285 auto it = session_cards_.find(session_id);
286 if (it != session_cards_.end()) {
287 for (const auto& prefixed_card_id : it->second) {
288 auto card_it = cards_.find(prefixed_card_id);
289 if (card_it != cards_.end() && card_it->second.visibility_flag) {
290 *card_it->second.visibility_flag = false;
291 if (card_it->second.on_hide) {
292 card_it->second.on_hide();
293 }
294 }
295 }
296 }
297}
298
299void EditorCardRegistry::ShowAllCardsInCategory(size_t session_id, const std::string& category) {
300 auto it = session_cards_.find(session_id);
301 if (it != session_cards_.end()) {
302 for (const auto& prefixed_card_id : it->second) {
303 auto card_it = cards_.find(prefixed_card_id);
304 if (card_it != cards_.end() && card_it->second.category == category) {
305 if (card_it->second.visibility_flag) {
306 *card_it->second.visibility_flag = true;
307 }
308 if (card_it->second.on_show) {
309 card_it->second.on_show();
310 }
311 }
312 }
313 }
314}
315
316void EditorCardRegistry::HideAllCardsInCategory(size_t session_id, const std::string& category) {
317 auto it = session_cards_.find(session_id);
318 if (it != session_cards_.end()) {
319 for (const auto& prefixed_card_id : it->second) {
320 auto card_it = cards_.find(prefixed_card_id);
321 if (card_it != cards_.end() && card_it->second.category == category) {
322 if (card_it->second.visibility_flag) {
323 *card_it->second.visibility_flag = false;
324 }
325 if (card_it->second.on_hide) {
326 card_it->second.on_hide();
327 }
328 }
329 }
330 }
331}
332
333void EditorCardRegistry::ShowOnlyCard(size_t session_id, const std::string& base_card_id) {
334 // First get the category of the target card
335 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
336 if (prefixed_id.empty()) {
337 return;
338 }
339
340 auto target_it = cards_.find(prefixed_id);
341 if (target_it == cards_.end()) {
342 return;
343 }
344
345 std::string category = target_it->second.category;
346
347 // Hide all cards in the same category
348 HideAllCardsInCategory(session_id, category);
349
350 // Show the target card
351 ShowCard(session_id, base_card_id);
352}
353
354// ============================================================================
355// Query Methods
356// ============================================================================
357
358std::vector<std::string> EditorCardRegistry::GetCardsInSession(size_t session_id) const {
359 auto it = session_cards_.find(session_id);
360 if (it != session_cards_.end()) {
361 return it->second;
362 }
363 return {};
364}
365
366std::vector<CardInfo> EditorCardRegistry::GetCardsInCategory(size_t session_id,
367 const std::string& category) const {
368 std::vector<CardInfo> result;
369
370 auto it = session_cards_.find(session_id);
371 if (it != session_cards_.end()) {
372 for (const auto& prefixed_card_id : it->second) {
373 auto card_it = cards_.find(prefixed_card_id);
374 if (card_it != cards_.end() && card_it->second.category == category) {
375 result.push_back(card_it->second);
376 }
377 }
378 }
379
380 // Sort by priority
381 std::sort(result.begin(), result.end(),
382 [](const CardInfo& a, const CardInfo& b) {
383 return a.priority < b.priority;
384 });
385
386 return result;
387}
388
389std::vector<std::string> EditorCardRegistry::GetAllCategories(size_t session_id) const {
390 std::vector<std::string> categories;
391
392 auto it = session_cards_.find(session_id);
393 if (it != session_cards_.end()) {
394 for (const auto& prefixed_card_id : it->second) {
395 auto card_it = cards_.find(prefixed_card_id);
396 if (card_it != cards_.end()) {
397 if (std::find(categories.begin(), categories.end(),
398 card_it->second.category) == categories.end()) {
399 categories.push_back(card_it->second.category);
400 }
401 }
402 }
403 }
404 return categories;
405}
406
408 const std::string& base_card_id) const {
409 std::string prefixed_id = GetPrefixedCardId(session_id, base_card_id);
410 if (prefixed_id.empty()) {
411 return nullptr;
412 }
413
414 auto it = cards_.find(prefixed_id);
415 if (it != cards_.end()) {
416 return &it->second;
417 }
418 return nullptr;
419}
420
421std::vector<std::string> EditorCardRegistry::GetAllCategories() const {
422 std::vector<std::string> categories;
423 for (const auto& [card_id, card_info] : cards_) {
424 if (std::find(categories.begin(), categories.end(),
425 card_info.category) == categories.end()) {
426 categories.push_back(card_info.category);
427 }
428 }
429 return categories;
430}
431
432// ============================================================================
433// View Menu Integration
434// ============================================================================
435
436void EditorCardRegistry::DrawViewMenuSection(size_t session_id, const std::string& category) {
437 auto cards = GetCardsInCategory(session_id, category);
438
439 if (cards.empty()) {
440 return;
441 }
442
443 if (ImGui::BeginMenu(category.c_str())) {
444 for (const auto& card : cards) {
445 DrawCardMenuItem(card);
446 }
447 ImGui::EndMenu();
448 }
449}
450
451void EditorCardRegistry::DrawViewMenuAll(size_t session_id) {
452 auto categories = GetAllCategories(session_id);
453
454 for (const auto& category : categories) {
455 DrawViewMenuSection(session_id, category);
456 }
457}
458
459// ============================================================================
460// VSCode-Style Sidebar
461// ============================================================================
462
463void EditorCardRegistry::DrawSidebar(size_t session_id,
464 const std::string& category,
465 const std::vector<std::string>& active_categories,
466 std::function<void(const std::string&)> on_category_switch,
467 std::function<void()> on_collapse) {
468 // Use ThemeManager for consistent theming
469 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
470 const float sidebar_width = GetSidebarWidth();
471
472 // Fixed sidebar window on the left edge of screen - exactly like VSCode
473 // Positioned below menu bar, spans full height, fixed 48px width
474 ImGui::SetNextWindowPos(ImVec2(0, ImGui::GetFrameHeight()));
475 ImGui::SetNextWindowSize(ImVec2(sidebar_width, -1)); // Exactly 48px wide, full height
476
477 ImGuiWindowFlags sidebar_flags =
478 ImGuiWindowFlags_NoTitleBar |
479 ImGuiWindowFlags_NoResize |
480 ImGuiWindowFlags_NoMove |
481 ImGuiWindowFlags_NoCollapse |
482 ImGuiWindowFlags_NoDocking |
483 ImGuiWindowFlags_NoScrollbar |
484 ImGuiWindowFlags_NoScrollWithMouse |
485 ImGuiWindowFlags_NoFocusOnAppearing |
486 ImGuiWindowFlags_NoNavFocus;
487
488 // VSCode-style dark sidebar background with visible border
489 ImVec4 sidebar_bg = ImVec4(0.18f, 0.18f, 0.20f, 1.0f);
490 ImVec4 sidebar_border = ImVec4(0.4f, 0.4f, 0.45f, 1.0f);
491
492 ImGui::PushStyleColor(ImGuiCol_WindowBg, sidebar_bg);
493 ImGui::PushStyleColor(ImGuiCol_Border, sidebar_border);
494 ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(4.0f, 8.0f));
495 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 6.0f));
496 ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 2.0f);
497
498 if (ImGui::Begin("##EditorCardSidebar", nullptr, sidebar_flags)) {
499 // Category switcher buttons at top (only if multiple editors are active)
500 if (active_categories.size() > 1) {
501 ImVec4 accent = gui::ConvertColorToImVec4(theme.accent);
502 ImVec4 inactive = gui::ConvertColorToImVec4(theme.button);
503
504 for (const auto& cat : active_categories) {
505 bool is_current = (cat == category);
506
507 // Highlight current category with accent color
508 if (is_current) {
509 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(accent.x, accent.y, accent.z, 0.8f));
510 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(accent.x, accent.y, accent.z, 1.0f));
511 } else {
512 ImGui::PushStyleColor(ImGuiCol_Button, inactive);
513 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, gui::ConvertColorToImVec4(theme.button_hovered));
514 }
515
516 // Show first letter of category as button label
517 std::string btn_label = cat.empty() ? "?" : std::string(1, cat[0]);
518 if (ImGui::Button(btn_label.c_str(), ImVec2(40.0f, 32.0f))) {
519 // Switch to this category/editor
520 if (on_category_switch) {
521 on_category_switch(cat);
522 } else {
524 }
525 }
526
527 ImGui::PopStyleColor(2);
528
529 if (ImGui::IsItemHovered()) {
530 ImGui::SetTooltip("%s Editor\nClick to switch", cat.c_str());
531 }
532 }
533
534 ImGui::Dummy(ImVec2(0, 2.0f));
535 ImGui::Separator();
536 ImGui::Spacing();
537 }
538
539 // Get cards for current category
540 auto cards = GetCardsInCategory(session_id, category);
541
542 // Set this category as active when showing cards
543 if (!cards.empty()) {
544 SetActiveCategory(category);
545 }
546
547 // Close All and Show All buttons (only if cards exist)
548 if (!cards.empty()) {
549 ImVec4 error_color = gui::ConvertColorToImVec4(theme.error);
550 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
551 error_color.x * 0.6f, error_color.y * 0.6f, error_color.z * 0.6f, 0.9f));
552 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, error_color);
553 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(
554 error_color.x * 1.2f, error_color.y * 1.2f, error_color.z * 1.2f, 1.0f));
555
556 if (ImGui::Button(ICON_MD_CLOSE, ImVec2(40.0f, 36.0f))) {
557 HideAllCardsInCategory(session_id, category);
558 }
559
560 ImGui::PopStyleColor(3);
561
562 if (ImGui::IsItemHovered()) {
563 ImGui::SetTooltip("Close All %s Cards", category.c_str());
564 }
565
566 // Show All button
567 ImVec4 success_color = gui::ConvertColorToImVec4(theme.success);
568 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
569 success_color.x * 0.6f, success_color.y * 0.6f, success_color.z * 0.6f, 0.7f));
570 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, success_color);
571 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(
572 success_color.x * 1.2f, success_color.y * 1.2f, success_color.z * 1.2f, 1.0f));
573
574 if (ImGui::Button(ICON_MD_DONE_ALL, ImVec2(40.0f, 36.0f))) {
575 ShowAllCardsInCategory(session_id, category);
576 }
577
578 ImGui::PopStyleColor(3);
579
580 if (ImGui::IsItemHovered()) {
581 ImGui::SetTooltip("Show All %s Cards", category.c_str());
582 }
583
584 ImGui::Dummy(ImVec2(0, 2.0f));
585
586 // Draw individual card toggle buttons
587 ImVec4 accent_color = gui::ConvertColorToImVec4(theme.accent);
588 ImVec4 button_bg = gui::ConvertColorToImVec4(theme.button);
589
590 for (const auto& card : cards) {
591 ImGui::PushID(card.card_id.c_str());
592
593 bool is_active = card.visibility_flag && *card.visibility_flag;
594
595 // Highlight active cards with accent color
596 if (is_active) {
597 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(
598 accent_color.x, accent_color.y, accent_color.z, 0.5f));
599 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(
600 accent_color.x, accent_color.y, accent_color.z, 0.7f));
601 ImGui::PushStyleColor(ImGuiCol_ButtonActive, accent_color);
602 } else {
603 ImGui::PushStyleColor(ImGuiCol_Button, button_bg);
604 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, gui::ConvertColorToImVec4(theme.button_hovered));
605 ImGui::PushStyleColor(ImGuiCol_ButtonActive, gui::ConvertColorToImVec4(theme.button_active));
606 }
607
608 // Icon-only button for each card
609 if (ImGui::Button(card.icon.c_str(), ImVec2(40.0f, 40.0f))) {
610 ToggleCard(session_id, card.card_id);
611 SetActiveCategory(category);
612 }
613
614 ImGui::PopStyleColor(3);
615
616 // Show tooltip with card name and shortcut
617 if (ImGui::IsItemHovered() || ImGui::IsItemActive()) {
618 SetActiveCategory(category);
619
620 ImGui::SetTooltip("%s\n%s", card.display_name.c_str(),
621 card.shortcut_hint.empty() ? "" : card.shortcut_hint.c_str());
622 }
623
624 ImGui::PopID();
625 }
626 } // End if (!cards.empty())
627
628 // Card Browser and Collapse buttons at bottom
629 if (on_collapse) {
630 ImGui::Dummy(ImVec2(0, 10.0f));
631 ImGui::Separator();
632 ImGui::Spacing();
633
634 // Collapse button
635 ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.2f, 0.2f, 0.22f, 0.9f));
636 ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.3f, 0.3f, 0.32f, 1.0f));
637 ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.25f, 0.25f, 0.27f, 1.0f));
638
639 if (ImGui::Button(ICON_MD_KEYBOARD_ARROW_LEFT, ImVec2(40.0f, 36.0f))) {
640 on_collapse();
641 }
642
643 ImGui::PopStyleColor(3);
644
645 if (ImGui::IsItemHovered()) {
646 ImGui::SetTooltip("Hide Sidebar\nCtrl+B");
647 }
648 }
649 }
650 ImGui::End();
651
652 ImGui::PopStyleVar(3); // WindowPadding, ItemSpacing, WindowBorderSize
653 ImGui::PopStyleColor(2); // WindowBg, Border
654}
655
656// ============================================================================
657// Compact Controls for Menu Bar
658// ============================================================================
659
660void EditorCardRegistry::DrawCompactCardControl(size_t session_id, const std::string& category) {
661 auto cards = GetCardsInCategory(session_id, category);
662
663 if (cards.empty()) {
664 return;
665 }
666
667 // Compact dropdown
668 if (ImGui::BeginCombo("##CardControl", absl::StrFormat("%s Cards", ICON_MD_DASHBOARD).c_str())) {
669 for (const auto& card : cards) {
670 bool visible = card.visibility_flag ? *card.visibility_flag : false;
671 if (ImGui::MenuItem(absl::StrFormat("%s %s", card.icon.c_str(),
672 card.display_name.c_str()).c_str(),
673 nullptr, visible)) {
674 ToggleCard(session_id, card.card_id);
675 }
676 }
677 ImGui::EndCombo();
678 }
679}
680
681void EditorCardRegistry::DrawInlineCardToggles(size_t session_id, const std::string& category) {
682 auto cards = GetCardsInCategory(session_id, category);
683
684 size_t visible_count = 0;
685 for (const auto& card : cards) {
686 if (card.visibility_flag && *card.visibility_flag) {
687 visible_count++;
688 }
689 }
690
691 ImGui::Text("(%zu/%zu)", visible_count, cards.size());
692}
693
694// ============================================================================
695// Card Browser UI
696// ============================================================================
697
698void EditorCardRegistry::DrawCardBrowser(size_t session_id, bool* p_open) {
699 ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
700
701 if (ImGui::Begin(absl::StrFormat("%s Card Browser", ICON_MD_DASHBOARD).c_str(),
702 p_open)) {
703
704 static char search_filter[256] = "";
705 static std::string category_filter = "All";
706
707 // Search bar
708 ImGui::SetNextItemWidth(300);
709 ImGui::InputTextWithHint("##Search", absl::StrFormat("%s Search cards...",
710 ICON_MD_SEARCH).c_str(),
711 search_filter, sizeof(search_filter));
712
713 ImGui::SameLine();
714
715 // Category filter
716 if (ImGui::BeginCombo("##CategoryFilter", category_filter.c_str())) {
717 if (ImGui::Selectable("All", category_filter == "All")) {
718 category_filter = "All";
719 }
720
721 auto categories = GetAllCategories(session_id);
722 for (const auto& cat : categories) {
723 if (ImGui::Selectable(cat.c_str(), category_filter == cat)) {
724 category_filter = cat;
725 }
726 }
727 ImGui::EndCombo();
728 }
729
730 ImGui::Separator();
731
732 // Card table
733 if (ImGui::BeginTable("##CardTable", 4,
734 ImGuiTableFlags_ScrollY | ImGuiTableFlags_RowBg |
735 ImGuiTableFlags_Borders)) {
736
737 ImGui::TableSetupColumn("Visible", ImGuiTableColumnFlags_WidthFixed, 60);
738 ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
739 ImGui::TableSetupColumn("Category", ImGuiTableColumnFlags_WidthFixed, 120);
740 ImGui::TableSetupColumn("Shortcut", ImGuiTableColumnFlags_WidthFixed, 100);
741 ImGui::TableHeadersRow();
742
743 auto cards = (category_filter == "All")
744 ? GetCardsInSession(session_id)
745 : std::vector<std::string>{};
746
747 if (category_filter != "All") {
748 auto cat_cards = GetCardsInCategory(session_id, category_filter);
749 for (const auto& card : cat_cards) {
750 cards.push_back(card.card_id);
751 }
752 }
753
754 for (const auto& card_id : cards) {
755 auto card_it = cards_.find(card_id);
756 if (card_it == cards_.end()) continue;
757
758 const auto& card = card_it->second;
759
760 // Apply search filter
761 std::string search_str = search_filter;
762 if (!search_str.empty()) {
763 std::string card_lower = card.display_name;
764 std::transform(card_lower.begin(), card_lower.end(), card_lower.begin(), ::tolower);
765 std::transform(search_str.begin(), search_str.end(), search_str.begin(), ::tolower);
766 if (card_lower.find(search_str) == std::string::npos) {
767 continue;
768 }
769 }
770
771 ImGui::TableNextRow();
772
773 // Visibility toggle
774 ImGui::TableNextColumn();
775 if (card.visibility_flag) {
776 bool visible = *card.visibility_flag;
777 if (ImGui::Checkbox(absl::StrFormat("##vis_%s", card.card_id.c_str()).c_str(),
778 &visible)) {
779 *card.visibility_flag = visible;
780 if (visible && card.on_show) {
781 card.on_show();
782 } else if (!visible && card.on_hide) {
783 card.on_hide();
784 }
785 }
786 }
787
788 // Name with icon
789 ImGui::TableNextColumn();
790 ImGui::Text("%s %s", card.icon.c_str(), card.display_name.c_str());
791
792 // Category
793 ImGui::TableNextColumn();
794 ImGui::Text("%s", card.category.c_str());
795
796 // Shortcut
797 ImGui::TableNextColumn();
798 ImGui::TextDisabled("%s", card.shortcut_hint.c_str());
799 }
800
801 ImGui::EndTable();
802 }
803 }
804 ImGui::End();
805}
806
807// ============================================================================
808// Workspace Presets
809// ============================================================================
810
811void EditorCardRegistry::SavePreset(const std::string& name, const std::string& description) {
812 WorkspacePreset preset;
813 preset.name = name;
814 preset.description = description;
815
816 // Collect all visible cards across all sessions
817 for (const auto& [card_id, card_info] : cards_) {
818 if (card_info.visibility_flag && *card_info.visibility_flag) {
819 preset.visible_cards.push_back(card_id);
820 }
821 }
822
823 presets_[name] = preset;
825 LOG_INFO("EditorCardRegistry", "Saved preset: %s (%zu cards)", name.c_str(), preset.visible_cards.size());
826}
827
828bool EditorCardRegistry::LoadPreset(const std::string& name) {
829 auto it = presets_.find(name);
830 if (it == presets_.end()) {
831 return false;
832 }
833
834 // First hide all cards
835 for (auto& [card_id, card_info] : cards_) {
836 if (card_info.visibility_flag) {
837 *card_info.visibility_flag = false;
838 }
839 }
840
841 // Then show preset cards
842 for (const auto& card_id : it->second.visible_cards) {
843 auto card_it = cards_.find(card_id);
844 if (card_it != cards_.end() && card_it->second.visibility_flag) {
845 *card_it->second.visibility_flag = true;
846 if (card_it->second.on_show) {
847 card_it->second.on_show();
848 }
849 }
850 }
851
852 LOG_INFO("EditorCardRegistry", "Loaded preset: %s", name.c_str());
853 return true;
854}
855
856void EditorCardRegistry::DeletePreset(const std::string& name) {
857 presets_.erase(name);
859}
860
861std::vector<EditorCardRegistry::WorkspacePreset> EditorCardRegistry::GetPresets() const {
862 std::vector<WorkspacePreset> result;
863 for (const auto& [name, preset] : presets_) {
864 result.push_back(preset);
865 }
866 return result;
867}
868
869// ============================================================================
870// Quick Actions
871// ============================================================================
872
873void EditorCardRegistry::ShowAll(size_t session_id) {
874 ShowAllCardsInSession(session_id);
875}
876
877void EditorCardRegistry::HideAll(size_t session_id) {
878 HideAllCardsInSession(session_id);
879}
880
881void EditorCardRegistry::ResetToDefaults(size_t session_id) {
882 // Hide all cards first
883 HideAllCardsInSession(session_id);
884
885 // TODO: Load default visibility from config file or hardcoded defaults
886 LOG_INFO("EditorCardRegistry", "Reset to defaults for session %zu", session_id);
887}
888
889// ============================================================================
890// Statistics
891// ============================================================================
892
893size_t EditorCardRegistry::GetVisibleCardCount(size_t session_id) const {
894 size_t count = 0;
895 auto it = session_cards_.find(session_id);
896 if (it != session_cards_.end()) {
897 for (const auto& prefixed_card_id : it->second) {
898 auto card_it = cards_.find(prefixed_card_id);
899 if (card_it != cards_.end() && card_it->second.visibility_flag) {
900 if (*card_it->second.visibility_flag) {
901 count++;
902 }
903 }
904 }
905 }
906 return count;
907}
908
909// ============================================================================
910// Session Prefixing Utilities
911// ============================================================================
912
913std::string EditorCardRegistry::MakeCardId(size_t session_id, const std::string& base_id) const {
914 if (ShouldPrefixCards()) {
915 return absl::StrFormat("s%zu.%s", session_id, base_id);
916 }
917 return base_id;
918}
919
920// ============================================================================
921// Helper Methods (Private)
922// ============================================================================
923
927
928std::string EditorCardRegistry::GetPrefixedCardId(size_t session_id,
929 const std::string& base_id) const {
930 auto session_it = session_card_mapping_.find(session_id);
931 if (session_it != session_card_mapping_.end()) {
932 auto card_it = session_it->second.find(base_id);
933 if (card_it != session_it->second.end()) {
934 return card_it->second;
935 }
936 }
937
938 // Fallback: try unprefixed ID (for single session or direct access)
939 if (cards_.find(base_id) != cards_.end()) {
940 return base_id;
941 }
942
943 return ""; // Card not found
944}
945
947 auto it = session_cards_.find(session_id);
948 if (it != session_cards_.end()) {
949 for (const auto& prefixed_card_id : it->second) {
950 cards_.erase(prefixed_card_id);
951 centralized_visibility_.erase(prefixed_card_id);
952 }
953 }
954}
955
957 // TODO: Implement file I/O for presets
958 LOG_INFO("EditorCardRegistry", "SavePresetsToFile() - not yet implemented");
959}
960
962 // TODO: Implement file I/O for presets
963 LOG_INFO("EditorCardRegistry", "LoadPresetsFromFile() - not yet implemented");
964}
965
967 bool visible = info.visibility_flag ? *info.visibility_flag : false;
968
969 std::string label = absl::StrFormat("%s %s", info.icon.c_str(),
970 info.display_name.c_str());
971
972 const char* shortcut = info.shortcut_hint.empty() ? nullptr : info.shortcut_hint.c_str();
973
974 if (ImGui::MenuItem(label.c_str(), shortcut, visible)) {
975 if (info.visibility_flag) {
976 *info.visibility_flag = !visible;
977 if (*info.visibility_flag && info.on_show) {
978 info.on_show();
979 } else if (!*info.visibility_flag && info.on_hide) {
980 info.on_hide();
981 }
982 }
983 }
984}
985
986void EditorCardRegistry::DrawCardInSidebar(const CardInfo& info, bool is_active) {
987 if (is_active) {
988 ImGui::PushStyleColor(ImGuiCol_Button, gui::GetPrimaryVec4());
989 }
990
991 if (ImGui::Button(absl::StrFormat("%s %s", info.icon.c_str(),
992 info.display_name.c_str()).c_str(),
993 ImVec2(-1, 0))) {
994 if (info.visibility_flag) {
995 *info.visibility_flag = !*info.visibility_flag;
996 if (*info.visibility_flag && info.on_show) {
997 info.on_show();
998 } else if (!*info.visibility_flag && info.on_hide) {
999 info.on_hide();
1000 }
1001 }
1002 }
1003
1004 if (is_active) {
1005 ImGui::PopStyleColor();
1006 }
1007}
1008
1009} // namespace editor
1010} // namespace yaze
1011
bool * GetVisibilityFlag(size_t session_id, const std::string &base_card_id)
Get visibility flag pointer for a card.
size_t GetVisibleCardCount(size_t session_id) const
void DrawCardMenuItem(const CardInfo &info)
void HideAllCardsInSession(size_t session_id)
Hide all cards in a specific session.
std::unordered_map< std::string, bool > centralized_visibility_
void DrawSidebar(size_t session_id, const std::string &category, const std::vector< std::string > &active_categories={}, std::function< void(const std::string &)> on_category_switch=nullptr, std::function< void()> on_collapse=nullptr)
Draw sidebar for a category with session filtering.
bool IsCardVisible(size_t session_id, const std::string &base_card_id) const
Check if a card is currently visible.
std::vector< std::string > GetCardsInSession(size_t session_id) const
Get all cards registered for a session.
const CardInfo * GetCardInfo(size_t session_id, const std::string &base_card_id) const
Get card metadata.
void UnregisterSession(size_t session_id)
Unregister a session and all its cards.
std::string MakeCardId(size_t session_id, const std::string &base_id) const
Generate session-aware card ID.
void SetActiveSession(size_t session_id)
Set the currently active session.
std::unordered_map< std::string, CardInfo > cards_
bool HideCard(size_t session_id, const std::string &base_card_id)
Hide a card programmatically.
void ShowAllCardsInCategory(size_t session_id, const std::string &category)
Show all cards in a category for a session.
void ShowAll()
Show all cards for active session (convenience)
void DrawViewMenuSection(size_t session_id, const std::string &category)
Draw view menu section for a category.
bool LoadPreset(const std::string &name)
void ClearAllCards()
Remove all registered cards (use with caution)
void ShowOnlyCard(size_t session_id, const std::string &base_card_id)
Show only one card, hiding all others in its category.
void RegisterCard(size_t session_id, const CardInfo &base_info)
Register a card for a specific session.
void ShowAllCardsInSession(size_t session_id)
Show all cards in a specific session.
std::unordered_map< size_t, std::vector< std::string > > session_cards_
bool ToggleCard(size_t session_id, const std::string &base_card_id)
Toggle a card's visibility.
bool ShouldPrefixCards() const
Check if card IDs should be prefixed.
std::vector< CardInfo > GetCardsInCategory(size_t session_id, const std::string &category) const
Get cards in a specific category for a session.
void DrawViewMenuAll(size_t session_id)
Draw all categories as view menu submenus.
void DrawInlineCardToggles(size_t session_id, const std::string &category)
Draw minimal inline card toggles.
void UnregisterCardsWithPrefix(const std::string &prefix)
Unregister all cards with a given prefix.
void UnregisterCard(size_t session_id, const std::string &base_card_id)
Unregister a specific card.
void SavePreset(const std::string &name, const std::string &description="")
void RegisterSession(size_t session_id)
Register a new session in the registry.
void SetActiveCategory(const std::string &category)
Set active category (for sidebar)
void DeletePreset(const std::string &name)
std::string GetPrefixedCardId(size_t session_id, const std::string &base_id) const
void DrawCardBrowser(size_t session_id, bool *p_open)
Draw visual card browser/toggler.
std::vector< std::string > GetAllCategories() const
Get all registered categories across all sessions.
std::vector< WorkspacePreset > GetPresets() const
void DrawCardInSidebar(const CardInfo &info, bool is_active)
void HideAll()
Hide all cards for active session (convenience)
void DrawCompactCardControl(size_t session_id, const std::string &category)
Draw compact card control for active editor's cards.
std::unordered_map< std::string, WorkspacePreset > presets_
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > session_card_mapping_
void HideAllCardsInCategory(size_t session_id, const std::string &category)
Hide all cards in a category for a session.
bool ShowCard(size_t session_id, const std::string &base_card_id)
Show a card programmatically.
static constexpr float GetSidebarWidth()
void UnregisterSessionCards(size_t session_id)
static ThemeManager & Get()
const EnhancedTheme & GetCurrentTheme() const
#define ICON_MD_DONE_ALL
Definition icons.h:606
#define ICON_MD_SEARCH
Definition icons.h:1671
#define ICON_MD_DASHBOARD
Definition icons.h:515
#define ICON_MD_KEYBOARD_ARROW_LEFT
Definition icons.h:1029
#define ICON_MD_CLOSE
Definition icons.h:416
#define LOG_WARN(category, format,...)
Definition log.h:108
#define LOG_INFO(category, format,...)
Definition log.h:106
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:21
ImVec4 GetPrimaryVec4()
Main namespace for the application.
Definition controller.cc:20
Metadata for an editor card.
std::function< void()> on_hide
std::function< void()> on_show