yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
panel_manager.cc
Go to the documentation of this file.
1#define IMGUI_DEFINE_MATH_OPERATORS
2
4
5#include <algorithm>
6#include <cstdio>
7#include <fstream>
8
9#include "absl/strings/str_format.h"
14#include "app/gui/core/icons.h"
15#include "imgui/imgui.h"
16#include "imgui/imgui_internal.h" // For ImGuiWindow and FindWindowByName
17#include "util/json.h"
18#include "util/log.h"
19#include "util/platform_paths.h"
20
21namespace yaze {
22namespace editor {
23
24// ============================================================================
25// Category Icon Mapping
26// ============================================================================
27
28std::string PanelManager::GetCategoryIcon(const std::string& category) {
29 if (category == "Dungeon") return ICON_MD_CASTLE;
30 if (category == "Overworld") return ICON_MD_MAP;
31 if (category == "Graphics") return ICON_MD_IMAGE;
32 if (category == "Palette") return ICON_MD_PALETTE;
33 if (category == "Sprite") return ICON_MD_PERSON;
34 if (category == "Music") return ICON_MD_MUSIC_NOTE;
35 if (category == "Message") return ICON_MD_MESSAGE;
36 if (category == "Screen") return ICON_MD_TV;
37 if (category == "Emulator") return ICON_MD_VIDEOGAME_ASSET;
38 if (category == "Assembly") return ICON_MD_CODE;
39 if (category == "Settings") return ICON_MD_SETTINGS;
40 if (category == "Memory") return ICON_MD_MEMORY;
41 if (category == "Agent") return ICON_MD_SMART_TOY;
42 return ICON_MD_FOLDER; // Default for unknown categories
43}
44
45// ============================================================================
46// Category Theme Colors (Expressive Icon Theming)
47// ============================================================================
48
50 const std::string& category) {
51 // Expressive colors for each category - vibrant when active
52 // Format: {icon_r, icon_g, icon_b, icon_a, glow_r, glow_g, glow_b}
53
54 if (category == "Dungeon") {
55 // Castle gold - warm, regal
56 return {0.95f, 0.75f, 0.20f, 1.0f, 0.95f, 0.75f, 0.20f};
57 }
58 if (category == "Overworld") {
59 // Forest green - natural, expansive
60 return {0.30f, 0.85f, 0.45f, 1.0f, 0.30f, 0.85f, 0.45f};
61 }
62 if (category == "Graphics") {
63 // Image blue - creative, visual
64 return {0.40f, 0.70f, 0.95f, 1.0f, 0.40f, 0.70f, 0.95f};
65 }
66 if (category == "Palette") {
67 // Rainbow pink/magenta - colorful, artistic
68 return {0.90f, 0.40f, 0.70f, 1.0f, 0.90f, 0.40f, 0.70f};
69 }
70 if (category == "Sprite") {
71 // Character cyan - lively, animated
72 return {0.30f, 0.85f, 0.85f, 1.0f, 0.30f, 0.85f, 0.85f};
73 }
74 if (category == "Music") {
75 // Note purple - creative, rhythmic
76 return {0.70f, 0.40f, 0.90f, 1.0f, 0.70f, 0.40f, 0.90f};
77 }
78 if (category == "Message") {
79 // Text yellow - communicative, bright
80 return {0.95f, 0.90f, 0.40f, 1.0f, 0.95f, 0.90f, 0.40f};
81 }
82 if (category == "Screen") {
83 // TV white/silver - display, clean
84 return {0.90f, 0.92f, 0.95f, 1.0f, 0.90f, 0.92f, 0.95f};
85 }
86 if (category == "Emulator") {
87 // Game red - playful, active
88 return {0.90f, 0.35f, 0.40f, 1.0f, 0.90f, 0.35f, 0.40f};
89 }
90 if (category == "Assembly") {
91 // Code green - technical, precise
92 return {0.40f, 0.90f, 0.50f, 1.0f, 0.40f, 0.90f, 0.50f};
93 }
94 if (category == "Settings") {
95 // Gear gray/blue - utility, system
96 return {0.60f, 0.70f, 0.80f, 1.0f, 0.60f, 0.70f, 0.80f};
97 }
98 if (category == "Memory") {
99 // Memory orange - data, technical
100 return {0.95f, 0.60f, 0.25f, 1.0f, 0.95f, 0.60f, 0.25f};
101 }
102 if (category == "Agent") {
103 // AI purple/violet - intelligent, futuristic
104 return {0.60f, 0.40f, 0.95f, 1.0f, 0.60f, 0.40f, 0.95f};
105 }
106
107 // Default - neutral blue
108 return {0.50f, 0.60f, 0.80f, 1.0f, 0.50f, 0.60f, 0.80f};
109}
110
111// ============================================================================
112// Session Lifecycle Management
113// ============================================================================
114
115void PanelManager::RegisterSession(size_t session_id) {
116 if (session_cards_.find(session_id) == session_cards_.end()) {
117 session_cards_[session_id] = std::vector<std::string>();
118 session_card_mapping_[session_id] =
119 std::unordered_map<std::string, std::string>();
121 LOG_INFO("PanelManager", "Registered session %zu (total: %zu)",
122 session_id, session_count_);
123 }
124}
125
126void PanelManager::UnregisterSession(size_t session_id) {
127 auto it = session_cards_.find(session_id);
128 if (it != session_cards_.end()) {
129 UnregisterSessionPanels(session_id);
130 session_cards_.erase(it);
131 session_card_mapping_.erase(session_id);
133
134 // Reset active session if it was the one being removed
135 if (active_session_ == session_id) {
136 active_session_ = 0;
137 if (!session_cards_.empty()) {
138 active_session_ = session_cards_.begin()->first;
139 }
140 }
141
142 LOG_INFO("PanelManager", "Unregistered session %zu (total: %zu)",
143 session_id, session_count_);
144 }
145}
146
147void PanelManager::SetActiveSession(size_t session_id) {
148 if (session_cards_.find(session_id) != session_cards_.end()) {
149 active_session_ = session_id;
150 }
151}
152
153// ============================================================================
154// Panel Registration
155// ============================================================================
156
157void PanelManager::RegisterPanel(size_t session_id,
158 const PanelDescriptor& base_info) {
159 RegisterSession(session_id); // Ensure session exists
160
161 std::string prefixed_id = MakePanelId(session_id, base_info.card_id);
162
163 // Check if already registered to avoid duplicates
164 if (cards_.find(prefixed_id) != cards_.end()) {
165 LOG_WARN("PanelManager",
166 "Panel '%s' already registered, skipping duplicate",
167 prefixed_id.c_str());
168 return;
169 }
170
171 // Create new PanelDescriptor with prefixed ID
172 PanelDescriptor prefixed_info = base_info;
173 prefixed_info.card_id = prefixed_id;
174
175 // If no visibility_flag provided, create centralized one
176 if (!prefixed_info.visibility_flag) {
177 centralized_visibility_[prefixed_id] = false; // Hidden by default
178 prefixed_info.visibility_flag = &centralized_visibility_[prefixed_id];
179 }
180
181 // Register the card
182 cards_[prefixed_id] = prefixed_info;
183
184 // Track in our session mapping
185 session_cards_[session_id].push_back(prefixed_id);
186 session_card_mapping_[session_id][base_info.card_id] = prefixed_id;
187
188 LOG_INFO("PanelManager", "Registered card %s -> %s for session %zu",
189 base_info.card_id.c_str(), prefixed_id.c_str(), session_id);
190}
191
193 size_t session_id, const std::string& card_id,
194 const std::string& display_name, const std::string& icon,
195 const std::string& category, const std::string& shortcut_hint, int priority,
196 std::function<void()> on_show, std::function<void()> on_hide,
197 bool visible_by_default) {
198 PanelDescriptor info;
199 info.card_id = card_id;
200 info.display_name = display_name;
201 info.icon = icon;
202 info.category = category;
203 info.shortcut_hint = shortcut_hint;
204 info.priority = priority;
205 info.visibility_flag = nullptr; // Will be created in RegisterPanel
206 info.on_show = on_show;
207 info.on_hide = on_hide;
208
209 RegisterPanel(session_id, info);
210
211 // Set initial visibility if requested
212 if (visible_by_default) {
213 ShowPanel(session_id, card_id);
214 }
215}
216
217void PanelManager::UnregisterPanel(size_t session_id,
218 const std::string& base_card_id) {
219 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
220 if (prefixed_id.empty()) {
221 return;
222 }
223
224 auto it = cards_.find(prefixed_id);
225 if (it != cards_.end()) {
226 LOG_INFO("PanelManager", "Unregistered card: %s",
227 prefixed_id.c_str());
228 cards_.erase(it);
229 centralized_visibility_.erase(prefixed_id);
230 pinned_panels_.erase(prefixed_id);
231
232 // Remove from session tracking
233 auto& session_card_list = session_cards_[session_id];
234 session_card_list.erase(std::remove(session_card_list.begin(),
235 session_card_list.end(), prefixed_id),
236 session_card_list.end());
237
238 session_card_mapping_[session_id].erase(base_card_id);
239 }
240}
241
242void PanelManager::UnregisterPanelsWithPrefix(const std::string& prefix) {
243 std::vector<std::string> to_remove;
244
245 // Find all cards with the given prefix
246 for (const auto& [card_id, card_info] : cards_) {
247 if (card_id.find(prefix) == 0) { // Starts with prefix
248 to_remove.push_back(card_id);
249 }
250 }
251
252 // Remove them
253 for (const auto& card_id : to_remove) {
254 cards_.erase(card_id);
255 centralized_visibility_.erase(card_id);
256 pinned_panels_.erase(card_id);
257 LOG_INFO("PanelManager", "Unregistered card with prefix '%s': %s",
258 prefix.c_str(), card_id.c_str());
259 }
260
261 // Also clean up session tracking
262 for (auto& [session_id, card_list] : session_cards_) {
263 card_list.erase(std::remove_if(card_list.begin(), card_list.end(),
264 [&prefix](const std::string& id) {
265 return id.find(prefix) == 0;
266 }),
267 card_list.end());
268 }
269}
270
272 cards_.clear();
274 pinned_panels_.clear();
275 session_cards_.clear();
276 session_card_mapping_.clear();
277 panel_instances_.clear();
278 session_count_ = 0;
279 LOG_INFO("PanelManager", "Cleared all cards");
280}
281
282// ============================================================================
283// EditorPanel Instance Management (Phase 4)
284// ============================================================================
285
286void PanelManager::RegisterEditorPanel(std::unique_ptr<EditorPanel> panel) {
287 if (!panel) {
288 LOG_ERROR("PanelManager", "Attempted to register null EditorPanel");
289 return;
290 }
291
292 // Phase 6: Resource Panel Limits
293 auto* resource_panel = dynamic_cast<ResourcePanel*>(panel.get());
294 if (resource_panel) {
295 EnforceResourceLimits(resource_panel->GetResourceType());
296 }
297
298 std::string panel_id = panel->GetId();
299
300 // Check if already registered
301 if (panel_instances_.find(panel_id) != panel_instances_.end()) {
302 LOG_WARN("PanelManager", "EditorPanel '%s' already registered, skipping",
303 panel_id.c_str());
304 return;
305 }
306
307 // Auto-register PanelDescriptor for sidebar/menu visibility
308 PanelDescriptor descriptor;
309 descriptor.card_id = panel_id;
310 descriptor.display_name = panel->GetDisplayName();
311 descriptor.icon = panel->GetIcon();
312 descriptor.category = panel->GetEditorCategory();
313 descriptor.priority = panel->GetPriority();
314 descriptor.shortcut_hint = panel->GetShortcutHint();
315 descriptor.visibility_flag = nullptr; // Will be created by RegisterPanel
316 descriptor.window_title = panel->GetIcon() + " " + panel->GetDisplayName();
317
318 // Check if panel should be visible by default
319 bool visible_by_default = panel->IsVisibleByDefault();
320
321 // Register the descriptor (creates visibility flag)
322 RegisterPanel(active_session_, descriptor);
323
324 // Set initial visibility if panel should be visible by default
325 if (visible_by_default) {
326 ShowPanel(active_session_, panel_id);
327 }
328
329 // Store the EditorPanel instance
330 panel_instances_[panel_id] = std::move(panel);
331
332 // Phase 6: Track resource panel usage
333 if (resource_panel) {
334 std::string type = resource_panel->GetResourceType();
335 resource_panels_[type].push_back(panel_id);
336 panel_resource_types_[panel_id] = type;
337 }
338
339 LOG_INFO("PanelManager", "Registered EditorPanel: %s (%s)",
340 panel_id.c_str(), descriptor.display_name.c_str());
341}
342
343// ============================================================================
344// Resource Management (Phase 6)
345// ============================================================================
346
347void PanelManager::EnforceResourceLimits(const std::string& resource_type) {
348 auto it = resource_panels_.find(resource_type);
349 if (it == resource_panels_.end()) return;
350
351 auto& panel_list = it->second;
352 size_t limit = ResourcePanelLimits::kMaxTotalResourcePanels; // Default fallback
353
354 // Determine limit based on type
355 if (resource_type == "room") limit = ResourcePanelLimits::kMaxRoomPanels;
356 else if (resource_type == "song") limit = ResourcePanelLimits::kMaxSongPanels;
357 else if (resource_type == "sheet") limit = ResourcePanelLimits::kMaxSheetPanels;
358 else if (resource_type == "map") limit = ResourcePanelLimits::kMaxMapPanels;
359
360 // Evict panels until we have room for one more (current count < limit)
361 // Prioritize evicting non-pinned panels first, then oldest pinned ones
362 while (panel_list.size() >= limit) {
363 // First pass: find oldest non-pinned panel
364 std::string panel_to_evict;
365 for (const auto& panel_id : panel_list) {
366 if (!IsPanelPinned(panel_id)) {
367 panel_to_evict = panel_id;
368 break;
369 }
370 }
371
372 // If all are pinned, evict the oldest (front of list) anyway
373 if (panel_to_evict.empty()) {
374 panel_to_evict = panel_list.front();
375 LOG_INFO("PanelManager", "All %s panels pinned, evicting oldest: %s",
376 resource_type.c_str(), panel_to_evict.c_str());
377 } else {
378 LOG_INFO("PanelManager", "Evicting non-pinned resource panel: %s (type: %s)",
379 panel_to_evict.c_str(), resource_type.c_str());
380 }
381
382 // Remove from LRU list first to avoid iterator issues
383 panel_list.remove(panel_to_evict);
384
385 UnregisterEditorPanel(panel_to_evict);
386 }
387}
388
389void PanelManager::MarkPanelUsed(const std::string& panel_id) {
390 auto type_it = panel_resource_types_.find(panel_id);
391 if (type_it == panel_resource_types_.end()) return;
392
393 std::string type = type_it->second;
394 auto& list = resource_panels_[type];
395
396 // Move to back (MRU)
397 // std::list::remove is slow (linear), but list size is small (<10)
398 list.remove(panel_id);
399 list.push_back(panel_id);
400}
401
402void PanelManager::UnregisterEditorPanel(const std::string& panel_id) {
403 auto it = panel_instances_.find(panel_id);
404 if (it != panel_instances_.end()) {
405 // Call OnClose before removing
406 it->second->OnClose();
407 panel_instances_.erase(it);
408 LOG_INFO("PanelManager", "Unregistered EditorPanel: %s", panel_id.c_str());
409 }
410
411 // Also unregister the descriptor
413}
414
415EditorPanel* PanelManager::GetEditorPanel(const std::string& panel_id) {
416 auto it = panel_instances_.find(panel_id);
417 if (it != panel_instances_.end()) {
418 return it->second.get();
419 }
420 return nullptr;
421}
422
424 // Suppress panel drawing when dashboard is active (no editor selected yet)
425 // This ensures panels don't appear until user selects an editor
427 return;
428 }
429
430 for (auto& [panel_id, panel] : panel_instances_) {
431 // Check visibility via PanelDescriptor
432 if (!IsPanelVisible(panel_id)) {
433 continue;
434 }
435
436 // Category filtering: only draw if matches active category, pinned, or persistent
437 bool should_draw = false;
438 if (panel->GetEditorCategory() == active_category_) {
439 should_draw = true;
440 } else if (IsPanelPinned(panel_id)) {
441 should_draw = true;
442 } else if (panel->GetPanelCategory() == PanelCategory::Persistent) {
443 should_draw = true;
444 }
445
446 if (!should_draw) {
447 continue;
448 }
449
450 // Get visibility flag for the panel window
451 bool* visibility_flag = GetVisibilityFlag(panel_id);
452
453 // Get display name without icon - PanelWindow will add the icon
454 // This fixes the double-icon issue where both descriptor and PanelWindow added icons
455 std::string display_name = panel->GetDisplayName();
456
457 // Create PanelWindow and draw content
458 gui::PanelWindow window(display_name.c_str(), panel->GetIcon().c_str(),
459 visibility_flag);
460
461 // Use preferred width from EditorPanel if specified
462 float preferred_width = panel->GetPreferredWidth();
463 if (preferred_width > 0.0f) {
464 window.SetDefaultSize(preferred_width, 0); // 0 height = auto
465 }
466
467 // Enable pin functionality for cross-editor persistence
468 window.SetPinnable(true);
469 window.SetPinned(IsPanelPinned(panel_id));
470
471 // Wire up pin state change callback to persist to PanelManager
472 window.SetPinChangedCallback([this, panel_id](bool pinned) {
473 SetPanelPinned(panel_id, pinned);
474 });
475
476 if (window.Begin(visibility_flag)) {
477 panel->Draw(visibility_flag);
478 }
479 window.End();
480
481 // Handle visibility change (window closed via X button)
482 if (visibility_flag && !*visibility_flag) {
483 panel->OnClose();
484 }
485 }
486}
487
488void PanelManager::OnEditorSwitch(const std::string& from_category,
489 const std::string& to_category) {
490 if (from_category == to_category) {
491 return; // No switch needed
492 }
493
494 LOG_INFO("PanelManager", "Switching from category '%s' to '%s'",
495 from_category.c_str(), to_category.c_str());
496
497 // Hide non-pinned, non-persistent panels from previous category
498 for (const auto& [panel_id, panel] : panel_instances_) {
499 if (panel->GetEditorCategory() == from_category &&
500 !IsPanelPinned(panel_id) &&
501 panel->GetPanelCategory() != PanelCategory::Persistent) {
502 HidePanel(panel_id);
503 }
504 }
505
506 // Show default panels for new category
507 EditorType editor_type = EditorRegistry::GetEditorTypeFromCategory(to_category);
508 auto defaults = LayoutPresets::GetDefaultPanels(editor_type);
509 for (const auto& panel_id : defaults) {
510 ShowPanel(panel_id);
511 }
512
513 // Update active category
514 SetActiveCategory(to_category);
515}
516
517// ============================================================================
518// Panel Control (Programmatic, No GUI)
519// ============================================================================
520
521bool PanelManager::ShowPanel(size_t session_id,
522 const std::string& base_card_id) {
523 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
524 if (prefixed_id.empty()) {
525 return false;
526 }
527
528 auto it = cards_.find(prefixed_id);
529 if (it != cards_.end()) {
530 if (it->second.visibility_flag) {
531 *it->second.visibility_flag = true;
532 }
533 if (it->second.on_show) {
534 it->second.on_show();
535 }
536 return true;
537 }
538 return false;
539}
540
541bool PanelManager::HidePanel(size_t session_id,
542 const std::string& base_card_id) {
543 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
544 if (prefixed_id.empty()) {
545 return false;
546 }
547
548 auto it = cards_.find(prefixed_id);
549 if (it != cards_.end()) {
550 if (it->second.visibility_flag) {
551 *it->second.visibility_flag = false;
552 }
553 if (it->second.on_hide) {
554 it->second.on_hide();
555 }
556 return true;
557 }
558 return false;
559}
560
561bool PanelManager::TogglePanel(size_t session_id,
562 const std::string& base_card_id) {
563 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
564 if (prefixed_id.empty()) {
565 return false;
566 }
567
568 auto it = cards_.find(prefixed_id);
569 if (it != cards_.end() && it->second.visibility_flag) {
570 bool new_state = !(*it->second.visibility_flag);
571 *it->second.visibility_flag = new_state;
572
573 if (new_state && it->second.on_show) {
574 it->second.on_show();
575 } else if (!new_state && it->second.on_hide) {
576 it->second.on_hide();
577 }
578 return true;
579 }
580 return false;
581}
582
583bool PanelManager::IsPanelVisible(size_t session_id,
584 const std::string& base_card_id) const {
585 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
586 if (prefixed_id.empty()) {
587 return false;
588 }
589
590 auto it = cards_.find(prefixed_id);
591 if (it != cards_.end() && it->second.visibility_flag) {
592 return *it->second.visibility_flag;
593 }
594 return false;
595}
596
597bool* PanelManager::GetVisibilityFlag(size_t session_id,
598 const std::string& base_card_id) {
599 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
600 if (prefixed_id.empty()) {
601 return nullptr;
602 }
603
604 auto it = cards_.find(prefixed_id);
605 if (it != cards_.end()) {
606 return it->second.visibility_flag;
607 }
608 return nullptr;
609}
610
611// ============================================================================
612// Batch Operations
613// ============================================================================
614
616 auto it = session_cards_.find(session_id);
617 if (it != session_cards_.end()) {
618 for (const auto& prefixed_card_id : it->second) {
619 auto card_it = cards_.find(prefixed_card_id);
620 if (card_it != cards_.end() && card_it->second.visibility_flag) {
621 *card_it->second.visibility_flag = true;
622 if (card_it->second.on_show) {
623 card_it->second.on_show();
624 }
625 }
626 }
627 }
628}
629
631 auto it = session_cards_.find(session_id);
632 if (it != session_cards_.end()) {
633 for (const auto& prefixed_card_id : it->second) {
634 auto card_it = cards_.find(prefixed_card_id);
635 if (card_it != cards_.end() && card_it->second.visibility_flag) {
636 *card_it->second.visibility_flag = false;
637 if (card_it->second.on_hide) {
638 card_it->second.on_hide();
639 }
640 }
641 }
642 }
643}
644
646 const std::string& category) {
647 auto it = session_cards_.find(session_id);
648 if (it != session_cards_.end()) {
649 for (const auto& prefixed_card_id : it->second) {
650 auto card_it = cards_.find(prefixed_card_id);
651 if (card_it != cards_.end() && card_it->second.category == category) {
652 if (card_it->second.visibility_flag) {
653 *card_it->second.visibility_flag = true;
654 }
655 if (card_it->second.on_show) {
656 card_it->second.on_show();
657 }
658 }
659 }
660 }
661}
662
664 const std::string& category) {
665 auto it = session_cards_.find(session_id);
666 if (it != session_cards_.end()) {
667 for (const auto& prefixed_card_id : it->second) {
668 auto card_it = cards_.find(prefixed_card_id);
669 if (card_it != cards_.end() && card_it->second.category == category) {
670 if (card_it->second.visibility_flag) {
671 *card_it->second.visibility_flag = false;
672 }
673 if (card_it->second.on_hide) {
674 card_it->second.on_hide();
675 }
676 }
677 }
678 }
679}
680
681void PanelManager::ShowOnlyPanel(size_t session_id,
682 const std::string& base_card_id) {
683 // First get the category of the target card
684 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
685 if (prefixed_id.empty()) {
686 return;
687 }
688
689 auto target_it = cards_.find(prefixed_id);
690 if (target_it == cards_.end()) {
691 return;
692 }
693
694 std::string category = target_it->second.category;
695
696 // Hide all cards in the same category
697 HideAllPanelsInCategory(session_id, category);
698
699 // Show the target card
700 ShowPanel(session_id, base_card_id);
701}
702
703// ============================================================================
704// Query Methods
705// ============================================================================
706
707std::vector<std::string> PanelManager::GetPanelsInSession(
708 size_t session_id) const {
709 auto it = session_cards_.find(session_id);
710 if (it != session_cards_.end()) {
711 return it->second;
712 }
713 return {};
714}
715
716std::vector<PanelDescriptor> PanelManager::GetPanelsInCategory(
717 size_t session_id, const std::string& category) const {
718 std::vector<PanelDescriptor> result;
719
720 auto it = session_cards_.find(session_id);
721 if (it != session_cards_.end()) {
722 for (const auto& prefixed_card_id : it->second) {
723 auto card_it = cards_.find(prefixed_card_id);
724 if (card_it != cards_.end() && card_it->second.category == category) {
725 result.push_back(card_it->second);
726 }
727 }
728 }
729
730 // Sort by priority
731 std::sort(result.begin(), result.end(),
732 [](const PanelDescriptor& a, const PanelDescriptor& b) {
733 return a.priority < b.priority;
734 });
735
736 return result;
737}
738
739std::vector<std::string> PanelManager::GetAllCategories(
740 size_t session_id) const {
741 std::vector<std::string> categories;
742
743 auto it = session_cards_.find(session_id);
744 if (it != session_cards_.end()) {
745 for (const auto& prefixed_card_id : it->second) {
746 auto card_it = cards_.find(prefixed_card_id);
747 if (card_it != cards_.end()) {
748 if (std::find(categories.begin(), categories.end(),
749 card_it->second.category) == categories.end()) {
750 categories.push_back(card_it->second.category);
751 }
752 }
753 }
754 }
755 return categories;
756}
757
759 size_t session_id, const std::string& base_card_id) const {
760 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
761 if (prefixed_id.empty()) {
762 return nullptr;
763 }
764
765 auto it = cards_.find(prefixed_id);
766 if (it != cards_.end()) {
767 return &it->second;
768 }
769 return nullptr;
770}
771
772std::vector<std::string> PanelManager::GetAllCategories() const {
773 std::vector<std::string> categories;
774 for (const auto& [card_id, card_info] : cards_) {
775 if (std::find(categories.begin(), categories.end(), card_info.category) ==
776 categories.end()) {
777 categories.push_back(card_info.category);
778 }
779 }
780 return categories;
781}
782
783// ============================================================================
784// Sidebar Keyboard Navigation
785// ============================================================================
786
788 size_t session_id, const std::vector<PanelDescriptor>& cards) {
789 // Click to focus - only focus if sidebar window is hovered and mouse clicked
790 if (!sidebar_has_focus_ && ImGui::IsWindowHovered(ImGuiHoveredFlags_None) &&
791 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
792 sidebar_has_focus_ = true;
793 focused_card_index_ = cards.empty() ? -1 : 0;
794 }
795
796 // No navigation if not focused or no cards
797 if (!sidebar_has_focus_ || cards.empty()) {
798 return;
799 }
800
801 // Escape to unfocus
802 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
803 sidebar_has_focus_ = false;
805 return;
806 }
807
808 int card_count = static_cast<int>(cards.size());
809
810 // Arrow keys / vim keys navigation
811 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) ||
812 ImGui::IsKeyPressed(ImGuiKey_J)) {
813 focused_card_index_ = std::min(focused_card_index_ + 1, card_count - 1);
814 }
815 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) ||
816 ImGui::IsKeyPressed(ImGuiKey_K)) {
817 focused_card_index_ = std::max(focused_card_index_ - 1, 0);
818 }
819
820 // Home/End for quick navigation
821 if (ImGui::IsKeyPressed(ImGuiKey_Home)) {
823 }
824 if (ImGui::IsKeyPressed(ImGuiKey_End)) {
825 focused_card_index_ = card_count - 1;
826 }
827
828 // Enter/Space to toggle card visibility
829 if (focused_card_index_ >= 0 && focused_card_index_ < card_count) {
830 if (ImGui::IsKeyPressed(ImGuiKey_Enter) ||
831 ImGui::IsKeyPressed(ImGuiKey_Space)) {
832 const auto& card = cards[focused_card_index_];
833 TogglePanel(session_id, card.card_id);
834 }
835 }
836}
837
838// ============================================================================
839// Favorites and Recent
840// ============================================================================
841
842void PanelManager::ToggleFavorite(const std::string& card_id) {
843 if (favorite_cards_.find(card_id) != favorite_cards_.end()) {
844 favorite_cards_.erase(card_id);
845 } else {
846 favorite_cards_.insert(card_id);
847 }
848 // TODO: Persist favorites to user settings
849}
850
851bool PanelManager::IsFavorite(const std::string& card_id) const {
852 return favorite_cards_.find(card_id) != favorite_cards_.end();
853}
854
855void PanelManager::AddToRecent(const std::string& card_id) {
856 // Remove if already exists (to move to front)
857 auto it = std::find(recent_cards_.begin(), recent_cards_.end(), card_id);
858 if (it != recent_cards_.end()) {
859 recent_cards_.erase(it);
860 }
861
862 // Add to front
863 recent_cards_.insert(recent_cards_.begin(), card_id);
864
865 // Trim if needed
866 if (recent_cards_.size() > kMaxRecentPanels) {
868 }
869}
870
871// ============================================================================
872// Workspace Presets
873// ============================================================================
874
875void PanelManager::SavePreset(const std::string& name,
876 const std::string& description) {
877 WorkspacePreset preset;
878 preset.name = name;
879 preset.description = description;
880
881 // Collect all visible cards across all sessions
882 for (const auto& [card_id, card_info] : cards_) {
883 if (card_info.visibility_flag && *card_info.visibility_flag) {
884 preset.visible_cards.push_back(card_id);
885 }
886 }
887
888 presets_[name] = preset;
890 LOG_INFO("PanelManager", "Saved preset: %s (%zu cards)", name.c_str(),
891 preset.visible_cards.size());
892}
893
894bool PanelManager::LoadPreset(const std::string& name) {
895 auto it = presets_.find(name);
896 if (it == presets_.end()) {
897 return false;
898 }
899
900 // First hide all cards
901 for (auto& [card_id, card_info] : cards_) {
902 if (card_info.visibility_flag) {
903 *card_info.visibility_flag = false;
904 }
905 }
906
907 // Then show preset cards
908 for (const auto& card_id : it->second.visible_cards) {
909 auto card_it = cards_.find(card_id);
910 if (card_it != cards_.end() && card_it->second.visibility_flag) {
911 *card_it->second.visibility_flag = true;
912 if (card_it->second.on_show) {
913 card_it->second.on_show();
914 }
915 }
916 }
917
918 LOG_INFO("PanelManager", "Loaded preset: %s", name.c_str());
919 return true;
920}
921
922void PanelManager::DeletePreset(const std::string& name) {
923 presets_.erase(name);
925}
926
927std::vector<PanelManager::WorkspacePreset>
929 std::vector<WorkspacePreset> result;
930 for (const auto& [name, preset] : presets_) {
931 result.push_back(preset);
932 }
933 return result;
934}
935
936// ============================================================================
937// Quick Actions
938// ============================================================================
939
940void PanelManager::ShowAll(size_t session_id) {
941 ShowAllPanelsInSession(session_id);
942}
943
944void PanelManager::HideAll(size_t session_id) {
945 HideAllPanelsInSession(session_id);
946}
947
948void PanelManager::ResetToDefaults(size_t session_id) {
949 // Hide all cards first
950 HideAllPanelsInSession(session_id);
951
952 // TODO: Load default visibility from config file or hardcoded defaults
953 LOG_INFO("PanelManager", "Reset to defaults for session %zu",
954 session_id);
955}
956
957void PanelManager::ResetToDefaults(size_t session_id,
958 EditorType editor_type) {
959 // Get category for this editor
960 std::string category = EditorRegistry::GetEditorCategory(editor_type);
961 if (category.empty()) {
962 LOG_WARN("PanelManager",
963 "No category found for editor type %d, skipping reset",
964 static_cast<int>(editor_type));
965 return;
966 }
967
968 // Hide all cards in this category first
969 HideAllPanelsInCategory(session_id, category);
970
971 // Get default cards from LayoutPresets
972 auto default_panels = LayoutPresets::GetDefaultPanels(editor_type);
973
974 // Show each default card
975 for (const auto& card_id : default_panels) {
976 if (ShowPanel(session_id, card_id)) {
977 LOG_INFO("PanelManager", "Showing default card: %s",
978 card_id.c_str());
979 }
980 }
981
982 LOG_INFO("PanelManager",
983 "Reset %s editor to defaults (%zu cards visible)", category.c_str(),
984 default_panels.size());
985}
986
987// ============================================================================
988// Statistics
989// ============================================================================
990
991size_t PanelManager::GetVisiblePanelCount(size_t session_id) const {
992 size_t count = 0;
993 auto it = session_cards_.find(session_id);
994 if (it != session_cards_.end()) {
995 for (const auto& prefixed_card_id : it->second) {
996 auto card_it = cards_.find(prefixed_card_id);
997 if (card_it != cards_.end() && card_it->second.visibility_flag) {
998 if (*card_it->second.visibility_flag) {
999 count++;
1000 }
1001 }
1002 }
1003 }
1004 return count;
1005}
1006
1007// ============================================================================
1008// Session Prefixing Utilities
1009// ============================================================================
1010
1011std::string PanelManager::MakePanelId(size_t session_id,
1012 const std::string& base_id) const {
1013 if (ShouldPrefixPanels()) {
1014 return absl::StrFormat("s%zu.%s", session_id, base_id);
1015 }
1016 return base_id;
1017}
1018
1019// ============================================================================
1020// Helper Methods (Private)
1021// ============================================================================
1022
1026
1028 size_t session_id, const std::string& base_id) const {
1029 auto session_it = session_card_mapping_.find(session_id);
1030 if (session_it != session_card_mapping_.end()) {
1031 auto card_it = session_it->second.find(base_id);
1032 if (card_it != session_it->second.end()) {
1033 return card_it->second;
1034 }
1035 }
1036
1037 // Fallback: try unprefixed ID (for single session or direct access)
1038 if (cards_.find(base_id) != cards_.end()) {
1039 return base_id;
1040 }
1041
1042 return ""; // Panel not found
1043}
1044
1046 auto it = session_cards_.find(session_id);
1047 if (it != session_cards_.end()) {
1048 for (const auto& prefixed_card_id : it->second) {
1049 cards_.erase(prefixed_card_id);
1050 centralized_visibility_.erase(prefixed_card_id);
1051 pinned_panels_.erase(prefixed_card_id);
1052 }
1053 }
1054}
1055
1057 auto config_dir_result = util::PlatformPaths::GetConfigDirectory();
1058 if (!config_dir_result.ok()) {
1059 LOG_ERROR("PanelManager", "Failed to get config directory: %s",
1060 config_dir_result.status().ToString().c_str());
1061 return;
1062 }
1063
1064 std::filesystem::path presets_file = *config_dir_result / "layout_presets.json";
1065
1066 try {
1067 yaze::Json j;
1068 j["version"] = 1;
1069 j["presets"] = yaze::Json::object();
1070
1071 for (const auto& [name, preset] : presets_) {
1072 yaze::Json preset_json;
1073 preset_json["name"] = preset.name;
1074 preset_json["description"] = preset.description;
1075 preset_json["visible_cards"] = preset.visible_cards;
1076 j["presets"][name] = preset_json;
1077 }
1078
1079 std::ofstream file(presets_file);
1080 if (!file.is_open()) {
1081 LOG_ERROR("PanelManager", "Failed to open file for writing: %s",
1082 presets_file.string().c_str());
1083 return;
1084 }
1085
1086 file << j.dump(2);
1087 file.close();
1088
1089 LOG_INFO("PanelManager", "Saved %zu presets to %s", presets_.size(),
1090 presets_file.string().c_str());
1091 } catch (const std::exception& e) {
1092 LOG_ERROR("PanelManager", "Error saving presets: %s", e.what());
1093 }
1094}
1095
1097 auto config_dir_result = util::PlatformPaths::GetConfigDirectory();
1098 if (!config_dir_result.ok()) {
1099 LOG_WARN("PanelManager", "Failed to get config directory: %s",
1100 config_dir_result.status().ToString().c_str());
1101 return;
1102 }
1103
1104 std::filesystem::path presets_file = *config_dir_result / "layout_presets.json";
1105
1106 if (!util::PlatformPaths::Exists(presets_file)) {
1107 LOG_INFO("PanelManager", "No presets file found at %s",
1108 presets_file.string().c_str());
1109 return;
1110 }
1111
1112 try {
1113 std::ifstream file(presets_file);
1114 if (!file.is_open()) {
1115 LOG_WARN("PanelManager", "Failed to open presets file: %s",
1116 presets_file.string().c_str());
1117 return;
1118 }
1119
1120 yaze::Json j;
1121 file >> j;
1122 file.close();
1123
1124 if (!j.contains("presets")) {
1125 LOG_WARN("PanelManager", "Invalid presets file format");
1126 return;
1127 }
1128
1129 size_t loaded_count = 0;
1130 // Note: iterating over yaze::Json or nlohmann::json requires standard loop if using alias
1131 // However, yaze::Json alias is just nlohmann::json when enabled.
1132 // When disabled, the loop will just not execute or stub loop.
1133 // But wait, nlohmann::json iterators return key/value pair or special iterator.
1134 // Let's check how the loop was written: for (auto& [name, preset_json] : j["presets"].items())
1135 // My stub has items(), but nlohmann::json uses items() too.
1136 for (auto& [name, preset_json] : j["presets"].items()) {
1137 WorkspacePreset preset;
1138 preset.name = preset_json.value("name", name);
1139 preset.description = preset_json.value("description", "");
1140
1141 if (preset_json.contains("visible_cards")) {
1142 yaze::Json visible_cards = preset_json["visible_cards"];
1143 if (visible_cards.is_array()) {
1144 for (const auto& card : visible_cards) {
1145 if (card.is_string()) {
1146 preset.visible_cards.push_back(card.get<std::string>());
1147 }
1148 }
1149 }
1150 }
1151
1152 presets_[name] = preset;
1153 loaded_count++;
1154 }
1155
1156 LOG_INFO("PanelManager", "Loaded %zu presets from %s", loaded_count,
1157 presets_file.string().c_str());
1158 } catch (const std::exception& e) {
1159 LOG_ERROR("PanelManager", "Error loading presets: %s", e.what());
1160 }
1161}
1162
1163// =============================================================================
1164// File Browser Integration
1165// =============================================================================
1166
1167FileBrowser* PanelManager::GetFileBrowser(const std::string& category) {
1168 auto it = category_file_browsers_.find(category);
1169 if (it != category_file_browsers_.end()) {
1170 return it->second.get();
1171 }
1172 return nullptr;
1173}
1174
1175void PanelManager::EnableFileBrowser(const std::string& category,
1176 const std::string& root_path) {
1177 if (category_file_browsers_.find(category) == category_file_browsers_.end()) {
1178 auto browser = std::make_unique<FileBrowser>();
1179
1180 // Set callback to forward file clicks
1181 browser->SetFileClickedCallback(
1182 [this, category](const std::string& path) {
1183 if (on_file_clicked_) {
1184 on_file_clicked_(category, path);
1185 }
1186 // Also activate the editor for this category
1187 if (on_card_clicked_) {
1188 on_card_clicked_(category);
1189 }
1190 });
1191
1192 if (!root_path.empty()) {
1193 browser->SetRootPath(root_path);
1194 }
1195
1196 // Set defaults for Assembly file browser
1197 if (category == "Assembly") {
1198 browser->SetFileFilter({".asm", ".s", ".65c816", ".inc", ".h"});
1199 }
1200
1201 category_file_browsers_[category] = std::move(browser);
1202 LOG_INFO("PanelManager", "Enabled file browser for category: %s",
1203 category.c_str());
1204 }
1205}
1206
1207void PanelManager::DisableFileBrowser(const std::string& category) {
1208 category_file_browsers_.erase(category);
1209}
1210
1211bool PanelManager::HasFileBrowser(const std::string& category) const {
1212 return category_file_browsers_.find(category) !=
1214}
1215
1216void PanelManager::SetFileBrowserPath(const std::string& category,
1217 const std::string& path) {
1218 auto it = category_file_browsers_.find(category);
1219 if (it != category_file_browsers_.end()) {
1220 it->second->SetRootPath(path);
1221 }
1222}
1223
1224// ============================================================================
1225// Pinning (Phase 3 scaffold)
1226// ============================================================================
1227
1228void PanelManager::SetPanelPinned(size_t session_id,
1229 const std::string& base_card_id,
1230 bool pinned) {
1231 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
1232 if (prefixed_id.empty()) {
1233 prefixed_id = MakePanelId(session_id, base_card_id);
1234 }
1235 pinned_panels_[prefixed_id] = pinned;
1236}
1237
1238bool PanelManager::IsPanelPinned(size_t session_id,
1239 const std::string& base_card_id) const {
1240 std::string prefixed_id = GetPrefixedPanelId(session_id, base_card_id);
1241 if (prefixed_id.empty()) {
1242 prefixed_id = MakePanelId(session_id, base_card_id);
1243 }
1244 auto it = pinned_panels_.find(prefixed_id);
1245 return it != pinned_panels_.end() && it->second;
1246}
1247
1248std::vector<std::string> PanelManager::GetPinnedPanels(
1249 size_t session_id) const {
1250 std::vector<std::string> result;
1251 const std::string prefix =
1252 ShouldPrefixPanels() ? absl::StrFormat("s%zu.", session_id) : "";
1253
1254 for (const auto& [panel_id, pinned] : pinned_panels_) {
1255 if (!pinned) continue;
1256 if (prefix.empty() || panel_id.rfind(prefix, 0) == 0) {
1257 result.push_back(panel_id);
1258 }
1259 }
1260 return result;
1261}
1262
1263void PanelManager::SetPanelPinned(const std::string& base_card_id,
1264 bool pinned) {
1265 SetPanelPinned(active_session_, base_card_id, pinned);
1266}
1267
1268bool PanelManager::IsPanelPinned(const std::string& base_card_id) const {
1269 return IsPanelPinned(active_session_, base_card_id);
1270}
1271
1272std::vector<std::string> PanelManager::GetPinnedPanels() const {
1274}
1275
1276
1277
1278// =============================================================================
1279// Panel Validation
1280// =============================================================================
1281
1283 const std::string& card_id) const {
1284 PanelValidationResult result;
1285 result.card_id = card_id;
1286
1287 auto it = cards_.find(card_id);
1288 if (it == cards_.end()) {
1289 result.expected_title = "";
1290 result.found_in_imgui = false;
1291 result.message = "Panel not registered";
1292 return result;
1293 }
1294
1295 const PanelDescriptor& info = it->second;
1296 result.expected_title = info.GetWindowTitle();
1297
1298 if (result.expected_title.empty()) {
1299 result.found_in_imgui = false;
1300 result.message = "FAIL - Missing window title";
1301 return result;
1302 }
1303
1304 // Check if ImGui has a window with this title
1305 ImGuiWindow* window = ImGui::FindWindowByName(result.expected_title.c_str());
1306 result.found_in_imgui = (window != nullptr);
1307
1308 if (result.found_in_imgui) {
1309 result.message = "OK - Window found";
1310 } else {
1311 result.message = "FAIL - No window with title: " + result.expected_title;
1312 }
1313
1314 return result;
1315}
1316
1317
1318std::vector<PanelManager::PanelValidationResult>
1320 std::vector<PanelValidationResult> results;
1321 results.reserve(cards_.size());
1322
1323 for (const auto& [card_id, info] : cards_) {
1324 results.push_back(ValidatePanel(card_id));
1325 }
1326
1327 return results;
1328}
1329
1330
1331
1332} // namespace editor
1333} // namespace yaze
bool is_array() const
Definition json.h:58
static Json object()
Definition json.h:34
items_view items()
Definition json.h:88
std::string dump(int=-1, char=' ', bool=false, int=0) const
Definition json.h:91
bool contains(const std::string &) const
Definition json.h:53
Base interface for all logical panel components.
static EditorType GetEditorTypeFromCategory(const std::string &category)
static std::string GetEditorCategory(EditorType type)
File system browser for the sidebar.
static std::vector< std::string > GetDefaultPanels(EditorType type)
Get default visible panels for an editor.
std::vector< std::string > GetPinnedPanels() const
size_t GetVisiblePanelCount(size_t session_id) const
void ShowOnlyPanel(size_t session_id, const std::string &base_card_id)
std::unordered_map< std::string, bool > centralized_visibility_
void AddToRecent(const std::string &card_id)
std::unordered_map< std::string, std::unique_ptr< EditorPanel > > panel_instances_
void RegisterSession(size_t session_id)
void SetFileBrowserPath(const std::string &category, const std::string &path)
EditorPanel * GetEditorPanel(const std::string &panel_id)
Get an EditorPanel instance by ID.
void OnEditorSwitch(const std::string &from_category, const std::string &to_category)
Handle editor/category switching for panel visibility.
void UnregisterSessionPanels(size_t session_id)
void SetActiveSession(size_t session_id)
bool TogglePanel(size_t session_id, const std::string &base_card_id)
const PanelDescriptor * GetPanelDescriptor(size_t session_id, const std::string &base_card_id) const
std::unordered_map< std::string, std::string > panel_resource_types_
std::unordered_map< size_t, std::vector< std::string > > session_cards_
std::vector< std::string > GetPanelsInSession(size_t session_id) const
void HandleSidebarKeyboardNav(size_t session_id, const std::vector< PanelDescriptor > &cards)
Handle keyboard navigation in sidebar (click-to-focus modal)
std::vector< std::string > GetAllCategories() const
static constexpr size_t kMaxRecentPanels
bool * GetVisibilityFlag(size_t session_id, const std::string &base_card_id)
void UnregisterSession(size_t session_id)
void HideAllPanelsInSession(size_t session_id)
bool HasFileBrowser(const std::string &category) const
void UnregisterPanelsWithPrefix(const std::string &prefix)
std::function< void(const std::string &, const std::string &) on_file_clicked_)
std::vector< std::string > recent_cards_
std::vector< PanelDescriptor > GetPanelsInCategory(size_t session_id, const std::string &category) const
bool ShowPanel(size_t session_id, const std::string &base_card_id)
std::string MakePanelId(size_t session_id, const std::string &base_id) const
std::unordered_map< std::string, PanelDescriptor > cards_
std::unordered_map< std::string, bool > pinned_panels_
static CategoryTheme GetCategoryTheme(const std::string &category)
void RegisterPanel(size_t session_id, const PanelDescriptor &base_info)
bool IsPanelVisible(size_t session_id, const std::string &base_card_id) const
bool LoadPreset(const std::string &name)
void EnableFileBrowser(const std::string &category, const std::string &root_path="")
PanelValidationResult ValidatePanel(const std::string &card_id) const
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > session_card_mapping_
void DeletePreset(const std::string &name)
void DisableFileBrowser(const std::string &category)
std::function< void(const std::string &) on_card_clicked_)
std::unordered_map< std::string, std::unique_ptr< FileBrowser > > category_file_browsers_
std::string GetPrefixedPanelId(size_t session_id, const std::string &base_id) const
void UnregisterPanel(size_t session_id, const std::string &base_card_id)
void DrawAllVisiblePanels()
Draw all visible EditorPanel instances (central drawing)
void ResetToDefaults(size_t session_id)
void UnregisterEditorPanel(const std::string &panel_id)
Unregister and destroy an EditorPanel instance.
void ShowAllPanelsInCategory(size_t session_id, const std::string &category)
std::vector< PanelValidationResult > ValidatePanels() const
void MarkPanelUsed(const std::string &panel_id)
Mark a panel as recently used (for LRU)
std::unordered_map< std::string, std::list< std::string > > resource_panels_
void EnforceResourceLimits(const std::string &resource_type)
Enforce limits on resource panels (LRU eviction)
static std::string GetCategoryIcon(const std::string &category)
static constexpr const char * kDashboardCategory
bool IsPanelPinned(size_t session_id, const std::string &base_card_id) const
FileBrowser * GetFileBrowser(const std::string &category)
void ToggleFavorite(const std::string &card_id)
void SetActiveCategory(const std::string &category)
void RegisterEditorPanel(std::unique_ptr< EditorPanel > panel)
Register an EditorPanel instance for central drawing.
void SavePreset(const std::string &name, const std::string &description="")
bool HidePanel(size_t session_id, const std::string &base_card_id)
std::vector< WorkspacePreset > GetPresets() const
std::unordered_set< std::string > favorite_cards_
void SetPanelPinned(size_t session_id, const std::string &base_card_id, bool pinned)
bool IsFavorite(const std::string &card_id) const
void HideAllPanelsInCategory(size_t session_id, const std::string &category)
std::unordered_map< std::string, WorkspacePreset > presets_
void ShowAllPanelsInSession(size_t session_id)
Base class for panels that edit specific ROM resources.
Draggable, dockable panel for editor sub-windows.
void SetPinChangedCallback(std::function< void(bool)> callback)
void SetPinned(bool pinned)
void SetPinnable(bool pinnable)
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
static absl::StatusOr< std::filesystem::path > GetConfigDirectory()
Get the user-specific configuration directory for YAZE.
static bool Exists(const std::filesystem::path &path)
Check if a file or directory exists.
#define ICON_MD_SETTINGS
Definition icons.h:1699
#define ICON_MD_MEMORY
Definition icons.h:1195
#define ICON_MD_MAP
Definition icons.h:1173
#define ICON_MD_CODE
Definition icons.h:434
#define ICON_MD_VIDEOGAME_ASSET
Definition icons.h:2076
#define ICON_MD_MESSAGE
Definition icons.h:1201
#define ICON_MD_CASTLE
Definition icons.h:380
#define ICON_MD_MUSIC_NOTE
Definition icons.h:1264
#define ICON_MD_IMAGE
Definition icons.h:982
#define ICON_MD_PERSON
Definition icons.h:1415
#define ICON_MD_FOLDER
Definition icons.h:809
#define ICON_MD_PALETTE
Definition icons.h:1370
#define ICON_MD_TV
Definition icons.h:2032
#define ICON_MD_SMART_TOY
Definition icons.h:1781
#define LOG_ERROR(category, format,...)
Definition log.h:109
#define LOG_WARN(category, format,...)
Definition log.h:107
#define LOG_INFO(category, format,...)
Definition log.h:105
constexpr size_t kMaxTotalResourcePanels
Maximum total resource panels across all types.
constexpr size_t kMaxSongPanels
Maximum open song panels (music editor)
constexpr size_t kMaxSheetPanels
Maximum open graphics sheet panels.
constexpr size_t kMaxRoomPanels
Maximum open room panels (dungeon editor)
constexpr size_t kMaxMapPanels
Maximum open map panels (overworld editor)
@ Persistent
Always visible once shown.
Metadata for an editor panel (formerly PanelInfo)
std::function< void()> on_show
std::string GetWindowTitle() const
Get the effective window title for DockBuilder.
std::function< void()> on_hide
Get the expressive theme color for a category.