yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
workspace_window_manager.cc
Go to the documentation of this file.
1#define IMGUI_DEFINE_MATH_OPERATORS
2
4
5#include <algorithm>
6#include <cmath>
7#include <cstdio>
8#include <fstream>
9
10#include "absl/strings/str_format.h"
18#include "app/gui/core/icons.h"
21#include "imgui/imgui.h"
22#include "imgui/imgui_internal.h" // For ImGuiWindow and FindWindowByName
23#include "util/json.h"
24#include "util/log.h"
25#include "util/platform_paths.h"
26
27namespace yaze {
28namespace editor {
29
30namespace {
31
33 WindowDescriptor descriptor;
34 auto* panel_ptr = const_cast<WindowContent*>(&panel);
35 descriptor.card_id = panel.GetId();
36 descriptor.display_name = panel.GetDisplayName();
37 descriptor.icon = panel.GetIcon();
38 descriptor.category = panel.GetEditorCategory();
39 descriptor.priority = panel.GetPriority();
40 descriptor.workflow_group = panel.GetWorkflowGroup();
41 descriptor.workflow_label = panel.GetWorkflowLabel();
43 descriptor.workflow_priority = panel.GetWorkflowPriority();
44 descriptor.shortcut_hint = panel.GetShortcutHint();
45 descriptor.scope = panel.GetScope();
46 descriptor.window_lifecycle = panel.GetWindowLifecycle();
47 descriptor.context_scope = panel.GetContextScope();
48 descriptor.enabled_condition = [panel_ptr]() {
49 return panel_ptr->IsEnabled();
50 };
51 descriptor.disabled_tooltip = panel.GetDisabledTooltip();
52 descriptor.visibility_flag = nullptr; // Created by RegisterPanel
53 descriptor.window_title = panel.GetIcon() + " " + panel.GetDisplayName();
54 return descriptor;
55}
56
57} // namespace
58
59// ============================================================================
60// Category Icon Mapping
61// ============================================================================
62
64 const std::string& category) {
65 if (category == "Dungeon")
66 return ICON_MD_CASTLE;
67 if (category == "Overworld")
68 return ICON_MD_MAP;
69 if (category == "Graphics")
70 return ICON_MD_IMAGE;
71 if (category == "Palette")
72 return ICON_MD_PALETTE;
73 if (category == "Sprite")
74 return ICON_MD_PERSON;
75 if (category == "Music")
76 return ICON_MD_MUSIC_NOTE;
77 if (category == "Message")
78 return ICON_MD_MESSAGE;
79 if (category == "Screen")
80 return ICON_MD_TV;
81 if (category == "Emulator")
83 if (category == "Assembly")
84 return ICON_MD_CODE;
85 if (category == "Settings")
86 return ICON_MD_SETTINGS;
87 if (category == "Memory")
88 return ICON_MD_MEMORY;
89 if (category == "Agent")
90 return ICON_MD_SMART_TOY;
91 return ICON_MD_FOLDER; // Default for unknown categories
92}
93
94// ============================================================================
95// Category Theme Colors (Expressive Icon Theming)
96// ============================================================================
97
99 const std::string& category) {
100 // Expressive colors for each category - vibrant when active
101 // Format: {icon_r, icon_g, icon_b, icon_a, glow_r, glow_g, glow_b}
102
103 if (category == "Dungeon") {
104 // Castle gold - warm, regal
105 return {0.95f, 0.75f, 0.20f, 1.0f, 0.95f, 0.75f, 0.20f};
106 }
107 if (category == "Overworld") {
108 // Forest green - natural, expansive
109 return {0.30f, 0.85f, 0.45f, 1.0f, 0.30f, 0.85f, 0.45f};
110 }
111 if (category == "Graphics") {
112 // Image blue - creative, visual
113 return {0.40f, 0.70f, 0.95f, 1.0f, 0.40f, 0.70f, 0.95f};
114 }
115 if (category == "Palette") {
116 // Rainbow pink/magenta - colorful, artistic
117 return {0.90f, 0.40f, 0.70f, 1.0f, 0.90f, 0.40f, 0.70f};
118 }
119 if (category == "Sprite") {
120 // Character cyan - lively, animated
121 return {0.30f, 0.85f, 0.85f, 1.0f, 0.30f, 0.85f, 0.85f};
122 }
123 if (category == "Music") {
124 // Note purple - creative, rhythmic
125 return {0.70f, 0.40f, 0.90f, 1.0f, 0.70f, 0.40f, 0.90f};
126 }
127 if (category == "Message") {
128 // Text yellow - communicative, bright
129 return {0.95f, 0.90f, 0.40f, 1.0f, 0.95f, 0.90f, 0.40f};
130 }
131 if (category == "Screen") {
132 // TV white/silver - display, clean
133 return {0.90f, 0.92f, 0.95f, 1.0f, 0.90f, 0.92f, 0.95f};
134 }
135 if (category == "Emulator") {
136 // Game red - playful, active
137 return {0.90f, 0.35f, 0.40f, 1.0f, 0.90f, 0.35f, 0.40f};
138 }
139 if (category == "Assembly") {
140 // Code green - technical, precise
141 return {0.40f, 0.90f, 0.50f, 1.0f, 0.40f, 0.90f, 0.50f};
142 }
143 if (category == "Settings") {
144 // Gear gray/blue - utility, system
145 return {0.60f, 0.70f, 0.80f, 1.0f, 0.60f, 0.70f, 0.80f};
146 }
147 if (category == "Memory") {
148 // Memory orange - data, technical
149 return {0.95f, 0.60f, 0.25f, 1.0f, 0.95f, 0.60f, 0.25f};
150 }
151 if (category == "Agent") {
152 // AI purple/violet - intelligent, futuristic
153 return {0.60f, 0.40f, 0.95f, 1.0f, 0.60f, 0.40f, 0.95f};
154 }
155
156 // Default - neutral blue
157 return {0.50f, 0.60f, 0.80f, 1.0f, 0.50f, 0.60f, 0.80f};
158}
159
162 const float fallback = GetSidePanelWidthForViewport(viewport_width);
163 if (viewport_width <= 0.0f) {
164 SidePanelWidthBounds bounds{};
165 bounds.min_width = 220.0f;
166 bounds.max_width = std::max(520.0f, fallback);
167 return bounds;
168 }
169
170 const float min_width = std::max(220.0f, viewport_width * 0.18f);
171 const float max_width = std::max(min_width + 20.0f, viewport_width * 0.62f);
172 SidePanelWidthBounds bounds{};
173 bounds.min_width = min_width;
174 bounds.max_width = max_width;
175 return bounds;
176}
177
179 float viewport_width) const {
180 const float default_width = GetSidePanelWidthForViewport(viewport_width);
181 if (browser_state_.sidebar_width <= 0.0f) {
182 return default_width;
183 }
184 const auto bounds = GetSidePanelWidthBounds(viewport_width);
185 return std::clamp(browser_state_.sidebar_width, bounds.min_width,
186 bounds.max_width);
187}
188
190 float viewport_width,
191 bool notify) {
192 if (width <= 0.0f) {
196 }
197 return;
198 }
199
200 const auto bounds = GetSidePanelWidthBounds(viewport_width);
201 const float clamped = std::clamp(width, bounds.min_width, bounds.max_width);
202 if (std::abs(browser_state_.sidebar_width - clamped) < 0.5f) {
203 return;
204 }
208 }
209}
210
220
222 bool notify) {
223 const float fallback = GetDefaultPanelBrowserCategoryWidth();
224 const float clamped = std::max(180.0f, width > 0.0f ? width : fallback);
225 if (std::abs(browser_state_.window_browser_category_width - clamped) < 0.5f) {
226 return;
227 }
232 }
233}
234
236 const std::string& prefixed_id) {
237 auto it = registry_state_.descriptors.find(prefixed_id);
238 return it != registry_state_.descriptors.end() ? &it->second : nullptr;
239}
240
242 const std::string& prefixed_id) const {
243 auto it = registry_state_.descriptors.find(prefixed_id);
244 return it != registry_state_.descriptors.end() ? &it->second : nullptr;
245}
246
248 size_t session_id) {
249 auto it = session_state_.session_windows.find(session_id);
250 return it != session_state_.session_windows.end() ? &it->second : nullptr;
251}
252
253const std::vector<std::string>* WorkspaceWindowManager::FindSessionWindowIds(
254 size_t session_id) const {
255 auto it = session_state_.session_windows.find(session_id);
256 return it != session_state_.session_windows.end() ? &it->second : nullptr;
257}
258
259std::unordered_map<std::string, std::string>*
261 auto it = session_state_.session_window_mapping.find(session_id);
262 return it != session_state_.session_window_mapping.end() ? &it->second
263 : nullptr;
264}
265
266const std::unordered_map<std::string, std::string>*
268 auto it = session_state_.session_window_mapping.find(session_id);
269 return it != session_state_.session_window_mapping.end() ? &it->second
270 : nullptr;
271}
272
273std::unordered_map<std::string, std::string>*
275 auto it = session_state_.session_reverse_window_mapping.find(session_id);
276 return it != session_state_.session_reverse_window_mapping.end() ? &it->second
277 : nullptr;
278}
279
280const std::unordered_map<std::string, std::string>*
282 size_t session_id) const {
283 auto it = session_state_.session_reverse_window_mapping.find(session_id);
284 return it != session_state_.session_reverse_window_mapping.end() ? &it->second
285 : nullptr;
286}
287
289 size_t session_id, const std::string& prefixed_window_id,
290 const std::string& base_window_id, const std::string& category,
291 bool visible) const {
292 if (auto* bus = ContentRegistry::Context::event_bus()) {
294 prefixed_window_id, base_window_id, category, visible, session_id));
295 }
296}
297
299 const std::string& resource_type) const {
300 if (resource_type == "room") {
302 }
303 if (resource_type == "song") {
305 }
306 if (resource_type == "sheet") {
308 }
309 if (resource_type == "map") {
311 }
313}
314
316 const std::string& panel_id, ResourceWindowContent* resource_panel) {
317 if (!resource_panel) {
318 return;
319 }
320 const std::string resource_type = resource_panel->GetResourceType();
321 resource_panels_[resource_type].push_back(panel_id);
322 panel_resource_types_[panel_id] = resource_type;
323}
324
326 const std::string& panel_id) {
327 auto type_it = panel_resource_types_.find(panel_id);
328 if (type_it == panel_resource_types_.end()) {
329 return;
330 }
331
332 const std::string resource_type = type_it->second;
333 auto panels_it = resource_panels_.find(resource_type);
334 if (panels_it != resource_panels_.end()) {
335 panels_it->second.remove(panel_id);
336 if (panels_it->second.empty()) {
337 resource_panels_.erase(panels_it);
338 }
339 }
340 panel_resource_types_.erase(type_it);
341}
342
344 const std::list<std::string>& panel_ids) const {
345 for (const auto& panel_id : panel_ids) {
346 if (!IsWindowPinnedImpl(panel_id)) {
347 return panel_id;
348 }
349 }
350
351 return panel_ids.empty() ? std::string{} : panel_ids.front();
352}
353
354// ============================================================================
355// Session Lifecycle Management
356// ============================================================================
357
358// ============================================================================
359// Panel Registration
360// ============================================================================
361
363 const WindowDescriptor& base_info) {
364 RegisterSession(session_id); // Ensure session exists
365
366 WindowDescriptor canonical_info = base_info;
367 canonical_info.card_id = ResolveBaseWindowId(base_info.card_id);
368
369 std::string panel_id =
370 MakeWindowId(session_id, canonical_info.card_id, canonical_info.scope);
371
372 bool already_registered = (cards_.find(panel_id) != cards_.end());
373 if (already_registered && canonical_info.scope != WindowScope::kGlobal) {
374 LOG_WARN("WorkspaceWindowManager",
375 "Panel '%s' already registered, skipping duplicate",
376 panel_id.c_str());
377 }
378
379 if (!already_registered) {
380 // Create new WindowDescriptor with final ID
381 WindowDescriptor panel_info = canonical_info;
382 panel_info.card_id = panel_id;
383
384 // If no visibility_flag provided, create centralized one
385 if (!panel_info.visibility_flag) {
386 centralized_visibility_[panel_id] = false; // Hidden by default
387 panel_info.visibility_flag = &centralized_visibility_[panel_id];
388 }
389
390 // Register the card
391 cards_[panel_id] = panel_info;
392
393 LOG_INFO("WorkspaceWindowManager",
394 "Registered card %s -> %s for session %zu",
395 canonical_info.card_id.c_str(), panel_id.c_str(), session_id);
396 }
397
398 if (canonical_info.scope == WindowScope::kGlobal) {
399 global_panel_ids_.insert(panel_id);
400 for (const auto& [mapped_session, _] : session_cards_) {
401 TrackPanelForSession(mapped_session, canonical_info.card_id, panel_id);
402 }
403 } else {
404 TrackPanelForSession(session_id, canonical_info.card_id, panel_id);
405 }
406}
407
409 size_t session_id, const std::string& card_id,
410 const std::string& display_name, const std::string& icon,
411 const std::string& category, const std::string& shortcut_hint, int priority,
412 std::function<void()> on_show, std::function<void()> on_hide,
413 bool visible_by_default) {
414 WindowDescriptor info;
415 info.card_id = ResolveBaseWindowId(card_id);
416 info.display_name = display_name;
417 info.icon = icon;
418 info.category = category;
419 info.shortcut_hint = shortcut_hint;
420 info.priority = priority;
421 info.visibility_flag = nullptr; // Will be created in RegisterPanel
422 info.on_show = on_show;
423 info.on_hide = on_hide;
424
425 RegisterPanel(session_id, info);
426
427 // Set initial visibility if requested
428 if (visible_by_default) {
429 OpenWindowImpl(session_id, info.card_id);
430 }
431}
432
434 const std::string& base_card_id) {
435 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
436 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
437 if (prefixed_id.empty()) {
438 return;
439 }
440
441 auto it = cards_.find(prefixed_id);
442 if (it != cards_.end()) {
443 LOG_INFO("WorkspaceWindowManager", "Unregistered card: %s",
444 prefixed_id.c_str());
445 RememberPinnedStateForRemovedWindow(session_id, canonical_base_id,
446 prefixed_id);
447 UntrackResourceWindow(prefixed_id);
448 cards_.erase(it);
449 centralized_visibility_.erase(prefixed_id);
450 pinned_panels_.erase(prefixed_id);
451 if (global_panel_ids_.find(prefixed_id) != global_panel_ids_.end()) {
452 global_panel_ids_.erase(prefixed_id);
453 for (auto& [mapped_session, card_list] : session_cards_) {
454 card_list.erase(
455 std::remove(card_list.begin(), card_list.end(), prefixed_id),
456 card_list.end());
457 }
458 for (auto& [mapped_session, mapping] : session_card_mapping_) {
459 mapping.erase(canonical_base_id);
460 }
461 for (auto& [mapped_session, reverse_mapping] :
463 reverse_mapping.erase(prefixed_id);
464 }
465 return;
466 }
467
468 // Remove from session tracking
469 auto& session_card_list = session_cards_[session_id];
470 session_card_list.erase(std::remove(session_card_list.begin(),
471 session_card_list.end(), prefixed_id),
472 session_card_list.end());
473
474 session_card_mapping_[session_id].erase(canonical_base_id);
475 }
476}
477
479 const std::string& prefix) {
480 struct RemovalInfo {
481 std::string prefixed_id;
482 size_t session_id = 0;
483 std::string base_id;
484 };
485 std::vector<RemovalInfo> to_remove;
486
487 // Find all cards with the given prefix
488 for (const auto& [card_id, card_info] : cards_) {
489 (void)card_info;
490 if (card_id.find(prefix) == 0) { // Starts with prefix
491 RemovalInfo info;
492 info.prefixed_id = card_id;
493 for (const auto& [session_id, reverse_mapping] :
495 auto reverse_it = reverse_mapping.find(card_id);
496 if (reverse_it != reverse_mapping.end()) {
497 info.session_id = session_id;
498 info.base_id = reverse_it->second;
499 break;
500 }
501 }
502 to_remove.push_back(std::move(info));
503 }
504 }
505
506 // Remove them
507 for (const auto& info : to_remove) {
508 if (!info.base_id.empty()) {
509 RememberPinnedStateForRemovedWindow(info.session_id, info.base_id,
510 info.prefixed_id);
511 }
512 UntrackResourceWindow(info.prefixed_id);
513 cards_.erase(info.prefixed_id);
514 centralized_visibility_.erase(info.prefixed_id);
515 pinned_panels_.erase(info.prefixed_id);
516 LOG_INFO("WorkspaceWindowManager", "Unregistered card with prefix '%s': %s",
517 prefix.c_str(), info.prefixed_id.c_str());
518 }
519
520 // Also clean up session tracking
521 for (auto& [session_id, card_list] : session_cards_) {
522 card_list.erase(std::remove_if(card_list.begin(), card_list.end(),
523 [&prefix](const std::string& id) {
524 return id.find(prefix) == 0;
525 }),
526 card_list.end());
527 }
528}
529
531 cards_.clear();
533 pinned_panels_.clear();
534 session_cards_.clear();
535 session_card_mapping_.clear();
537 session_context_keys_.clear();
538 panel_instances_.clear();
539 registry_panel_ids_.clear();
540 global_panel_ids_.clear();
541 resource_panels_.clear();
542 panel_resource_types_.clear();
543 session_count_ = 0;
544 LOG_INFO("WorkspaceWindowManager", "Cleared all cards");
545}
546
547// ============================================================================
548// WindowContent Instance Management (Phase 4)
549// ============================================================================
550
552 std::unique_ptr<WindowContent> panel) {
553 if (!panel) {
554 LOG_ERROR("WorkspaceWindowManager",
555 "Attempted to register null WindowContent");
556 return;
557 }
558
559 auto* resource_panel = dynamic_cast<ResourceWindowContent*>(panel.get());
560 if (resource_panel) {
561 EnforceResourceWindowLimits(resource_panel->GetResourceType());
562 }
563
564 std::string panel_id = panel->GetId();
565
566 // Check if already registered
567 if (panel_instances_.find(panel_id) != panel_instances_.end()) {
568 LOG_WARN("WorkspaceWindowManager",
569 "WindowContent '%s' already registered, skipping registry add",
570 panel_id.c_str());
571 return;
572 }
573
574 if (panel->GetScope() == WindowScope::kGlobal) {
575 global_panel_ids_.insert(panel_id);
576 }
577 registry_panel_ids_.insert(panel_id);
578
579 // Store the WindowContent instance
580 panel_instances_[panel_id] = std::move(panel);
581
582 TrackResourceWindow(panel_id, resource_panel);
583
584 LOG_INFO("WorkspaceWindowManager", "Registered registry WindowContent: %s",
585 panel_id.c_str());
586}
587
589 size_t session_id) {
590 RegisterSession(session_id);
591 for (const auto& panel_id : registry_panel_ids_) {
592 auto it = panel_instances_.find(panel_id);
593 if (it == panel_instances_.end()) {
594 continue;
595 }
596 RegisterPanelDescriptorForSession(session_id, *it->second);
597 }
598}
599
601 std::unique_ptr<WindowContent> panel) {
602 if (!panel) {
603 LOG_ERROR("WorkspaceWindowManager",
604 "Attempted to register null WindowContent");
605 return;
606 }
607
608 auto* resource_panel = dynamic_cast<ResourceWindowContent*>(panel.get());
609 if (resource_panel) {
610 EnforceResourceWindowLimits(resource_panel->GetResourceType());
611 }
612
613 std::string panel_id = panel->GetId();
614
615 // Check if already registered
616 if (panel_instances_.find(panel_id) != panel_instances_.end()) {
617 LOG_WARN("WorkspaceWindowManager",
618 "WindowContent '%s' already registered, skipping",
619 panel_id.c_str());
620 return;
621 }
622
623 // Auto-register WindowDescriptor for sidebar/menu visibility
624 WindowDescriptor descriptor = BuildDescriptorFromPanel(*panel);
625
626 // Check if panel should be visible by default
627 bool visible_by_default = panel->IsVisibleByDefault();
628
629 // Register the descriptor (creates visibility flag)
630 RegisterPanel(active_session_, descriptor);
631
632 // Set initial visibility if panel should be visible by default
633 if (visible_by_default) {
635 }
636
637 // Store the WindowContent instance
638 panel_instances_[panel_id] = std::move(panel);
639
640 TrackResourceWindow(panel_id, resource_panel);
641
642 LOG_INFO("WorkspaceWindowManager", "Registered WindowContent: %s (%s)",
643 panel_id.c_str(), descriptor.display_name.c_str());
644}
645
646// ============================================================================
647// Resource Management (Phase 6)
648// ============================================================================
649
651 const std::string& resource_type) {
652 auto it = resource_panels_.find(resource_type);
653 if (it == resource_panels_.end())
654 return;
655
656 auto& panel_list = it->second;
657 const size_t limit = GetResourceWindowLimit(resource_type);
658
659 // Evict panels until we have room for one more (current count < limit)
660 while (panel_list.size() >= limit) {
661 std::string panel_to_evict = SelectResourceWindowForEviction(panel_list);
662 if (panel_to_evict.empty()) {
663 break;
664 }
665
666 // Remove from LRU list first to avoid iterator issues
667 panel_list.remove(panel_to_evict);
668
669 UnregisterWindowContent(panel_to_evict);
670 }
671}
672
673void WorkspaceWindowManager::MarkPanelUsed(const std::string& panel_id) {
674 auto type_it = panel_resource_types_.find(panel_id);
675 if (type_it == panel_resource_types_.end())
676 return;
677
678 std::string type = type_it->second;
679 auto& list = resource_panels_[type];
680
681 // Move to back (MRU)
682 // std::list::remove is slow (linear), but list size is small (<10)
683 list.remove(panel_id);
684 list.push_back(panel_id);
685}
686
688 const std::string& panel_id) {
689 UntrackResourceWindow(panel_id);
690 auto it = panel_instances_.find(panel_id);
691 if (it != panel_instances_.end()) {
692 // Call OnClose before removing
693 it->second->OnClose();
695 panel_instances_.erase(it);
696 registry_panel_ids_.erase(panel_id);
697 global_panel_ids_.erase(panel_id);
698 LOG_INFO("WorkspaceWindowManager", "Unregistered WindowContent: %s",
699 panel_id.c_str());
700 }
701
702 // Also unregister the descriptor
704}
705
707 const std::string& panel_id) {
708 auto it = panel_instances_.find(panel_id);
709 if (it != panel_instances_.end()) {
710 return it->second.get();
711 }
712 return nullptr;
713}
714
716 const std::string& prefixed_panel_id, const std::string& base_panel_id) {
717 auto prefixed_it = panel_instances_.find(prefixed_panel_id);
718 if (prefixed_it != panel_instances_.end()) {
719 return prefixed_it->second.get();
720 }
721 auto base_it = panel_instances_.find(base_panel_id);
722 if (base_it != panel_instances_.end()) {
723 return base_it->second.get();
724 }
725 return nullptr;
726}
727
729 const std::string& prefixed_panel_id,
730 const std::string& base_panel_id) const {
731 auto prefixed_it = panel_instances_.find(prefixed_panel_id);
732 if (prefixed_it != panel_instances_.end()) {
733 return prefixed_it->second.get();
734 }
735 auto base_it = panel_instances_.find(base_panel_id);
736 if (base_it != panel_instances_.end()) {
737 return base_it->second.get();
738 }
739 return nullptr;
740}
741
743 // Suppress panel drawing when dashboard is active (no editor selected yet)
744 // This ensures panels don't appear until user selects an editor
745 if (browser_state_.active_category.empty() ||
747 return;
748 }
749
750 auto session_it = session_cards_.find(active_session_);
751 if (session_it == session_cards_.end()) {
752 return;
753 }
754
755 auto& animator = gui::GetAnimator();
756 bool animations_enabled = animator.IsEnabled();
757 const bool touch_device = gui::LayoutHelpers::IsTouchDevice();
758
759 const auto resolve_switch_speed = [&](float snappy, float standard,
760 float relaxed) {
761 switch (animator.motion_profile()) {
763 return snappy;
765 return relaxed;
767 default:
768 return standard;
769 }
770 };
771 const float category_transition_speed =
772 resolve_switch_speed(8.5f, 6.0f, 4.5f);
773 const float panel_fade_speed = resolve_switch_speed(11.0f, 8.0f, 6.0f);
774
775 // Animate global category transition alpha
776 float global_alpha = 1.0f;
777 if (animations_enabled) {
778 global_alpha = animator.Animate("global", "category_transition", 1.0f,
779 category_transition_speed);
780 }
781
782 for (const auto& prefixed_panel_id : session_it->second) {
783 auto descriptor_it = cards_.find(prefixed_panel_id);
784 if (descriptor_it == cards_.end()) {
785 continue;
786 }
787 WindowDescriptor& descriptor = descriptor_it->second;
788
789 std::string base_panel_id =
790 GetBaseIdForPrefixedId(active_session_, prefixed_panel_id);
791 if (base_panel_id.empty()) {
792 base_panel_id = prefixed_panel_id;
793 }
794
795 WindowContent* panel = FindPanelInstance(prefixed_panel_id, base_panel_id);
796 if (!panel) {
797 continue;
798 }
799
800 bool is_visible = descriptor.visibility_flag && *descriptor.visibility_flag;
801
802 // Category filtering: draw if matches active category or user has pinned.
803 // Previously a Persistent lifecycle was a third branch here; collapsed into
804 // CrossEditor + default-pin via UserSettings revision-7 migration.
805 bool should_draw = false;
806 if (is_visible) {
809 should_draw = true;
810 } else if (IsWindowPinnedImpl(active_session_, base_panel_id)) {
811 should_draw = true;
812 }
813 }
814
815 // Compute target alpha: 1.0 if should draw, 0.0 if should hide
816 float target_alpha = should_draw ? 1.0f : 0.0f;
817
818 // Animate alpha towards target (or snap if animations disabled)
819 float current_alpha = 1.0f;
820 if (animations_enabled) {
821 current_alpha = animator.Animate(prefixed_panel_id, "panel_alpha",
822 target_alpha, panel_fade_speed);
823 current_alpha *= global_alpha; // Apply global category fade
824 } else {
825 current_alpha = target_alpha;
826 }
827
828 // Skip drawing if alpha is effectively zero
829 if (current_alpha < 0.01f) {
830 continue;
831 }
832
833 // Get visibility flag for the panel window
834 bool* visibility_flag = descriptor.visibility_flag;
835
836 // Get display name without icon - PanelWindow will add the icon
837 // This fixes the double-icon issue where both descriptor and PanelWindow added icons
838 std::string display_name = descriptor.display_name.empty()
839 ? panel->GetDisplayName()
840 : descriptor.display_name;
841 std::string icon =
842 descriptor.icon.empty() ? panel->GetIcon() : descriptor.icon;
843
844 if (editor_resolver_) {
845 if (Editor* editor = editor_resolver_(panel->GetEditorCategory())) {
847 }
848 }
849
850 // Create PanelWindow and draw content
851 gui::PanelWindow window(display_name.c_str(), icon.c_str(),
852 visibility_flag);
853 window.SetStableId(prefixed_panel_id);
854 if (touch_device) {
856 }
857
858 // Use preferred width from WindowContent if specified
859 float preferred_width = panel->GetPreferredWidth();
860 if (preferred_width > 0.0f) {
861 window.SetDefaultSize(preferred_width, 0); // 0 height = auto
862 }
863
864 // Enable pin functionality for cross-editor persistence
865 window.SetPinnable(true);
866 window.SetPinned(IsWindowPinnedImpl(active_session_, base_panel_id));
867
868 // Wire up pin state change callback to persist to WorkspaceWindowManager
869 window.SetPinChangedCallback([this, base_panel_id](bool pinned) {
870 SetWindowPinnedImpl(base_panel_id, pinned);
871 });
872
873 // Apply fade alpha for smooth transitions
874 std::optional<gui::StyleVarGuard> alpha_guard;
875 if (current_alpha < 1.0f) {
876 alpha_guard.emplace(ImGuiStyleVar_Alpha, current_alpha);
877 }
878
879 animator.ApplyPanelTransitionPre(prefixed_panel_id);
880 if (window.Begin(visibility_flag)) {
881 panel->DrawWithLazyInit(visibility_flag);
882 }
883 window.End();
884 animator.ApplyPanelTransitionPost(prefixed_panel_id);
885
886 alpha_guard.reset();
887
888 // Handle visibility change (window closed via X button)
889 if (visibility_flag && !*visibility_flag) {
890 panel->OnClose();
891 }
892 }
893}
894
895void WorkspaceWindowManager::OnEditorSwitch(const std::string& from_category,
896 const std::string& to_category) {
897 if (from_category == to_category) {
898 return; // No switch needed
899 }
900
901 LOG_INFO("WorkspaceWindowManager", "Switching from category '%s' to '%s'",
902 from_category.c_str(), to_category.c_str());
903
904 // IMPORTANT:
905 // Editor switching must *not* be treated as a user-initiated hide/show.
906 //
907 // Historically this function toggled visibility flags (CloseWindowImpl/OpenWindowImpl),
908 // which publishes PanelVisibilityChangedEvent and permanently overwrote user
909 // prefs. Worse, dynamic "resource windows" (rooms/songs) interpret
910 // IsWindowVisibleImpl()==false as "user closed", unregistering themselves.
911 //
912 // Reset global category transition to trigger fade-in
913 if (gui::GetAnimator().IsEnabled()) {
916
917 // Also begin a per-panel fade for every visible window in the incoming
918 // category. Without this, individual panels use whatever alpha the
919 // animator happened to have for their workspace fade state from the
920 // previous session, which was the source of the "old bitmap still
921 // visible during switch" ghosting. EditorActivator clears that narrow
922 // transition state just before this call, so each transition starts from
923 // alpha=0.
924 const auto incoming =
926 for (const auto& descriptor : incoming) {
927 if (descriptor.visibility_flag && *descriptor.visibility_flag) {
929 GetPrefixedWindowId(active_session_, descriptor.card_id),
931 }
932 }
933 }
934
935 SetActiveCategory(to_category);
936}
937
938// ============================================================================
939// Panel Control (Programmatic, No GUI)
940// ============================================================================
941
943 const std::string& base_card_id) {
944 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
945 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
946 if (prefixed_id.empty()) {
947 return false;
948 }
949
950 auto* descriptor = FindDescriptorByPrefixedId(prefixed_id);
951 if (descriptor) {
952 const bool was_visible =
953 (descriptor->visibility_flag && *descriptor->visibility_flag);
954 if (descriptor->visibility_flag) {
955 *descriptor->visibility_flag = true;
956 }
957 if (descriptor->on_show) {
958 descriptor->on_show();
959 }
960 if (!was_visible) {
961 if (WindowContent* panel =
962 FindPanelInstance(prefixed_id, canonical_base_id)) {
963 panel->OnOpen();
964 }
965 }
966
967 PublishWindowVisibilityChanged(session_id, prefixed_id, canonical_base_id,
968 descriptor->category, true);
969 return true;
970 }
971 return false;
972}
973
975 const std::string& base_card_id) {
976 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
977 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
978 if (prefixed_id.empty()) {
979 return false;
980 }
981
982 auto* descriptor = FindDescriptorByPrefixedId(prefixed_id);
983 if (descriptor) {
984 const bool was_visible =
985 (descriptor->visibility_flag && *descriptor->visibility_flag);
986 if (descriptor->visibility_flag) {
987 *descriptor->visibility_flag = false;
988 }
989 if (descriptor->on_hide) {
990 descriptor->on_hide();
991 }
992 if (was_visible) {
993 if (WindowContent* panel =
994 FindPanelInstance(prefixed_id, canonical_base_id)) {
995 panel->OnClose();
996 }
997 }
998
999 PublishWindowVisibilityChanged(session_id, prefixed_id, canonical_base_id,
1000 descriptor->category, false);
1001 return true;
1002 }
1003 return false;
1004}
1005
1007 const std::string& base_card_id) {
1008 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
1009 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
1010 if (prefixed_id.empty()) {
1011 return false;
1012 }
1013
1014 auto* descriptor = FindDescriptorByPrefixedId(prefixed_id);
1015 if (descriptor && descriptor->visibility_flag) {
1016 bool new_state = !(*descriptor->visibility_flag);
1017 *descriptor->visibility_flag = new_state;
1018
1019 if (new_state && descriptor->on_show) {
1020 descriptor->on_show();
1021 } else if (!new_state && descriptor->on_hide) {
1022 descriptor->on_hide();
1023 }
1024 if (WindowContent* panel =
1025 FindPanelInstance(prefixed_id, canonical_base_id)) {
1026 if (new_state) {
1027 panel->OnOpen();
1028 } else {
1029 panel->OnClose();
1030 }
1031 }
1032
1033 PublishWindowVisibilityChanged(session_id, prefixed_id, canonical_base_id,
1034 descriptor->category, new_state);
1035 return true;
1036 }
1037 return false;
1038}
1039
1041 size_t session_id, const std::string& base_card_id) const {
1042 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
1043 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
1044 if (prefixed_id.empty()) {
1045 return false;
1046 }
1047
1048 const auto* descriptor = FindDescriptorByPrefixedId(prefixed_id);
1049 if (descriptor && descriptor->visibility_flag) {
1050 return *descriptor->visibility_flag;
1051 }
1052 return false;
1053}
1054
1056 size_t session_id, const std::string& base_card_id) {
1057 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
1058 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
1059 if (prefixed_id.empty()) {
1060 return nullptr;
1061 }
1062
1063 auto* descriptor = FindDescriptorByPrefixedId(prefixed_id);
1064 if (descriptor) {
1065 return descriptor->visibility_flag;
1066 }
1067 return nullptr;
1068}
1069
1070// ============================================================================
1071// Batch Operations
1072// ============================================================================
1073
1075 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1076 for (const auto& prefixed_card_id : *session_windows) {
1077 if (auto* descriptor = FindDescriptorByPrefixedId(prefixed_card_id)) {
1078 if (descriptor->visibility_flag) {
1079 *descriptor->visibility_flag = true;
1080 }
1081 if (descriptor->on_show) {
1082 descriptor->on_show();
1083 }
1084 }
1085 }
1086 }
1087}
1088
1090 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1091 for (const auto& prefixed_card_id : *session_windows) {
1092 if (auto* descriptor = FindDescriptorByPrefixedId(prefixed_card_id)) {
1093 if (descriptor->visibility_flag) {
1094 *descriptor->visibility_flag = false;
1095 }
1096 if (descriptor->on_hide) {
1097 descriptor->on_hide();
1098 }
1099 }
1100 }
1101 }
1102}
1103
1105 size_t session_id, const std::string& category) {
1106 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1107 for (const auto& prefixed_card_id : *session_windows) {
1108 if (auto* descriptor = FindDescriptorByPrefixedId(prefixed_card_id)) {
1109 if (descriptor->category != category) {
1110 continue;
1111 }
1112 if (descriptor->visibility_flag) {
1113 *descriptor->visibility_flag = true;
1114 }
1115 if (descriptor->on_show) {
1116 descriptor->on_show();
1117 }
1118 }
1119 }
1120 }
1121}
1122
1124 size_t session_id, const std::string& category) {
1125 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1126 for (const auto& prefixed_card_id : *session_windows) {
1127 if (auto* descriptor = FindDescriptorByPrefixedId(prefixed_card_id)) {
1128 if (descriptor->category != category) {
1129 continue;
1130 }
1131 if (descriptor->visibility_flag) {
1132 *descriptor->visibility_flag = false;
1133 }
1134 if (descriptor->on_hide) {
1135 descriptor->on_hide();
1136 }
1137 }
1138 }
1139 }
1140}
1141
1143 const std::string& base_card_id) {
1144 // First get the category of the target card
1145 std::string prefixed_id = GetPrefixedWindowId(session_id, base_card_id);
1146 if (prefixed_id.empty()) {
1147 return;
1148 }
1149
1150 const auto* target = FindDescriptorByPrefixedId(prefixed_id);
1151 if (!target) {
1152 return;
1153 }
1154
1155 std::string category = target->category;
1156
1157 // Hide all cards in the same category
1158 HideAllWindowsInCategory(session_id, category);
1159
1160 // Show the target card
1161 OpenWindowImpl(session_id, base_card_id);
1162}
1163
1164// ============================================================================
1165// Query Methods
1166// ============================================================================
1167
1169 size_t session_id) const {
1170 std::vector<std::string> result;
1171 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1172 result.reserve(session_windows->size());
1173 for (const auto& prefixed_window_id : *session_windows) {
1174 const std::string base_window_id =
1175 GetBaseIdForPrefixedId(session_id, prefixed_window_id);
1176 result.push_back(base_window_id.empty() ? prefixed_window_id
1177 : base_window_id);
1178 }
1179 }
1180 return result;
1181}
1182
1184 size_t session_id, const std::string& category) const {
1185 std::vector<WindowDescriptor> result;
1186
1187 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1188 for (const auto& prefixed_window_id : *session_windows) {
1189 if (const auto* descriptor =
1190 FindDescriptorByPrefixedId(prefixed_window_id)) {
1191 if (descriptor->category == category) {
1192 WindowDescriptor window = *descriptor;
1193 const std::string base_window_id =
1194 GetBaseIdForPrefixedId(session_id, prefixed_window_id);
1195 if (!base_window_id.empty()) {
1196 window.card_id = base_window_id;
1197 }
1198 result.push_back(std::move(window));
1199 }
1200 }
1201 }
1202 }
1203
1204 // Sort by priority
1205 std::sort(result.begin(), result.end(),
1206 [](const WindowDescriptor& a, const WindowDescriptor& b) {
1207 return a.priority < b.priority;
1208 });
1209
1210 return result;
1211}
1212
1214 size_t session_id) const {
1215 std::vector<std::string> categories;
1216
1217 if (const auto* session_windows = FindSessionWindowIds(session_id)) {
1218 for (const auto& prefixed_window_id : *session_windows) {
1219 if (const auto* descriptor =
1220 FindDescriptorByPrefixedId(prefixed_window_id)) {
1221 if (std::find(categories.begin(), categories.end(),
1222 descriptor->category) == categories.end()) {
1223 categories.push_back(descriptor->category);
1224 }
1225 }
1226 }
1227 }
1228 return categories;
1229}
1230
1232 size_t session_id, const std::string& base_card_id) const {
1233 const std::string canonical_base_id = ResolveBaseWindowId(base_card_id);
1234 std::string prefixed_id = GetPrefixedWindowId(session_id, canonical_base_id);
1235 if (prefixed_id.empty()) {
1236 return nullptr;
1237 }
1238
1239 return FindDescriptorByPrefixedId(prefixed_id);
1240}
1241
1242std::vector<std::string> WorkspaceWindowManager::GetAllCategories() const {
1243 std::vector<std::string> categories;
1244 for (const auto& [card_id, card_info] : registry_state_.descriptors) {
1245 if (std::find(categories.begin(), categories.end(), card_info.category) ==
1246 categories.end()) {
1247 categories.push_back(card_info.category);
1248 }
1249 }
1250 return categories;
1251}
1252
1253// ============================================================================
1254// State Persistence
1255// ============================================================================
1256
1258 size_t session_id) const {
1259 std::vector<std::string> visible_panels;
1260
1261 const auto* window_mapping = FindSessionWindowMapping(session_id);
1262 if (!window_mapping) {
1263 return visible_panels;
1264 }
1265
1266 for (const auto& [base_id, prefixed_id] : *window_mapping) {
1267 if (const auto* descriptor = FindDescriptorByPrefixedId(prefixed_id)) {
1268 if (descriptor->visibility_flag && *descriptor->visibility_flag) {
1269 visible_panels.push_back(base_id);
1270 }
1271 }
1272 }
1273
1274 return visible_panels;
1275}
1276
1277// ============================================================================
1278// Workspace Presets
1279// ============================================================================
1280
1281void WorkspaceWindowManager::SavePreset(const std::string& name,
1282 const std::string& description) {
1283 WorkspacePreset preset;
1284 preset.name = name;
1285 preset.description = description;
1286
1287 // Collect all visible cards across all sessions
1288 for (const auto& [card_id, card_info] : cards_) {
1289 if (card_info.visibility_flag && *card_info.visibility_flag) {
1290 preset.visible_cards.push_back(card_id);
1291 }
1292 }
1293
1294 presets_[name] = preset;
1296 LOG_INFO("WorkspaceWindowManager", "Saved preset: %s (%zu cards)",
1297 name.c_str(), preset.visible_cards.size());
1298}
1299
1300bool WorkspaceWindowManager::LoadPreset(const std::string& name) {
1301 auto it = presets_.find(name);
1302 if (it == presets_.end()) {
1303 return false;
1304 }
1305
1306 // First hide all cards
1307 for (auto& [card_id, card_info] : cards_) {
1308 if (card_info.visibility_flag) {
1309 *card_info.visibility_flag = false;
1310 }
1311 }
1312
1313 // Then show preset cards
1314 for (const auto& card_id : it->second.visible_cards) {
1315 auto card_it = cards_.find(card_id);
1316 if (card_it != cards_.end() && card_it->second.visibility_flag) {
1317 *card_it->second.visibility_flag = true;
1318 if (card_it->second.on_show) {
1319 card_it->second.on_show();
1320 }
1321 }
1322 }
1323
1324 LOG_INFO("WorkspaceWindowManager", "Loaded preset: %s", name.c_str());
1325 return true;
1326}
1327
1328void WorkspaceWindowManager::DeletePreset(const std::string& name) {
1329 presets_.erase(name);
1331}
1332
1333std::vector<WorkspaceWindowManager::WorkspacePreset>
1335 std::vector<WorkspacePreset> result;
1336 for (const auto& [name, preset] : presets_) {
1337 result.push_back(preset);
1338 }
1339 return result;
1340}
1341
1342// ============================================================================
1343// Quick Actions
1344// ============================================================================
1345
1346void WorkspaceWindowManager::ShowAll(size_t session_id) {
1347 ShowAllWindowsInSession(session_id);
1348}
1349
1350void WorkspaceWindowManager::HideAll(size_t session_id) {
1351 HideAllWindowsInSession(session_id);
1352}
1353
1355 // Hide all cards first
1356 HideAllWindowsInSession(session_id);
1357
1358 // TODO: Load default visibility from config file or hardcoded defaults
1359 LOG_INFO("WorkspaceWindowManager", "Reset to defaults for session %zu",
1360 session_id);
1361}
1362
1364 EditorType editor_type) {
1365 // Get category for this editor
1366 std::string category = EditorRegistry::GetEditorCategory(editor_type);
1367 if (category.empty()) {
1368 LOG_WARN("WorkspaceWindowManager",
1369 "No category found for editor type %d, skipping reset",
1370 static_cast<int>(editor_type));
1371 return;
1372 }
1373
1374 // Hide all cards in this category first
1375 HideAllWindowsInCategory(session_id, category);
1376
1377 // Get default cards from LayoutPresets
1378 auto default_panels = LayoutPresets::GetDefaultPanels(editor_type);
1379
1380 // Show each default card
1381 for (const auto& card_id : default_panels) {
1382 if (OpenWindowImpl(session_id, card_id)) {
1383 LOG_INFO("WorkspaceWindowManager", "Showing default card: %s",
1384 card_id.c_str());
1385 }
1386 }
1387
1388 LOG_INFO("WorkspaceWindowManager",
1389 "Reset %s editor to defaults (%zu cards visible)", category.c_str(),
1390 default_panels.size());
1391}
1392
1393// ============================================================================
1394// Session Prefixing Utilities
1395// ============================================================================
1396
1398 size_t session_id, const std::string& base_id) const {
1399 return MakeWindowId(session_id, base_id, WindowScope::kSession);
1400}
1401
1402std::string WorkspaceWindowManager::MakeWindowId(size_t session_id,
1403 const std::string& base_id,
1404 WindowScope scope) const {
1405 const std::string canonical_base_id = ResolveBaseWindowId(base_id);
1406 if (scope == WindowScope::kGlobal) {
1407 return canonical_base_id;
1408 }
1409 if (ShouldPrefixPanels()) {
1410 return absl::StrFormat("s%zu.%s", session_id, canonical_base_id);
1411 }
1412 return canonical_base_id;
1413}
1414
1415} // namespace editor
1416} // namespace yaze
static std::string GetEditorCategory(EditorType type)
Interface for editor classes.
Definition editor.h:240
static std::vector< std::string > GetDefaultPanels(EditorType type)
Get default visible panels for an editor.
Base class for windows that edit specific ROM resources.
virtual std::string GetResourceType() const =0
The resource type name.
Base interface for all logical window content components.
virtual std::string GetWorkflowDescription() const
Optional workflow description for menus/command palette.
virtual std::string GetDisabledTooltip() const
Get tooltip text when panel is disabled.
virtual int GetWorkflowPriority() const
Optional workflow ordering priority (lower sorts first).
virtual std::string GetDisplayName() const =0
Human-readable name shown in menus and title bars.
virtual WindowLifecycle GetWindowLifecycle() const
Get the lifecycle category for this window.
virtual std::string GetEditorCategory() const =0
Editor category this panel belongs to.
virtual int GetPriority() const
Get display priority for menu ordering.
virtual void OnClose()
Called when panel is hidden.
virtual WindowScope GetScope() const
Get the registration scope for this window.
virtual std::string GetIcon() const =0
Material Design icon for this panel.
virtual float GetPreferredWidth() const
Get preferred width for this panel (optional)
virtual WindowContextScope GetContextScope() const
Optional context binding for this window (room/selection/etc)
virtual std::string GetWorkflowLabel() const
Optional workflow label for menus/command palette.
void DrawWithLazyInit(bool *p_open)
Execute lazy initialization if needed, then call Draw()
virtual std::string GetId() const =0
Unique identifier for this panel.
virtual std::string GetWorkflowGroup() const
Optional workflow group for hack-centric actions.
virtual std::string GetShortcutHint() const
Get keyboard shortcut hint for display.
void RegisterPanel(size_t session_id, const WindowDescriptor &base_info)
bool IsWindowPinnedImpl(size_t session_id, const std::string &base_card_id) const
void SavePreset(const std::string &name, const std::string &description="")
std::vector< std::string > GetWindowsInSessionImpl(size_t session_id) const
void SetActiveSidePanelWidth(float width, float viewport_width=0.0f, bool notify=true)
void HideAllWindowsInCategory(size_t session_id, const std::string &category)
bool CloseWindowImpl(size_t session_id, const std::string &base_card_id)
size_t GetResourceWindowLimit(const std::string &resource_type) const
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > & session_reverse_card_mapping_
static SidePanelWidthBounds GetSidePanelWidthBounds(float viewport_width)
std::vector< WorkspacePreset > GetPresets() const
void UnregisterWindowContent(const std::string &window_id)
Unregister and destroy a WindowContent instance.
std::unordered_map< std::string, std::string > * FindSessionWindowMapping(size_t session_id)
std::string SelectResourceWindowForEviction(const std::list< std::string > &panel_ids) const
bool * GetVisibilityFlag(size_t session_id, const std::string &base_card_id)
void UntrackResourceWindow(const std::string &panel_id)
std::string ResolveBaseWindowId(const std::string &panel_id) const
std::unordered_map< std::string, WindowDescriptor > & cards_
std::string GetPrefixedWindowId(size_t session_id, const std::string &base_id) const
static float GetSidePanelWidthForViewport(float viewport_width)
std::vector< std::string > * FindSessionWindowIds(size_t session_id)
const WindowDescriptor * GetWindowDescriptorImpl(size_t session_id, const std::string &base_card_id) const
void RememberPinnedStateForRemovedWindow(size_t session_id, const std::string &base_card_id, const std::string &prefixed_id)
std::string GetBaseIdForPrefixedId(size_t session_id, const std::string &prefixed_id) const
static CategoryTheme GetCategoryTheme(const std::string &category)
std::unordered_map< std::string, std::string > & panel_resource_types_
std::vector< WindowDescriptor > GetWindowsInCategoryImpl(size_t session_id, const std::string &category) const
std::unordered_map< std::string, bool > & centralized_visibility_
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > & session_card_mapping_
void MarkPanelUsed(const std::string &panel_id)
Mark a panel as recently used (for LRU)
void TrackPanelForSession(size_t session_id, const std::string &base_id, const std::string &panel_id)
std::unordered_map< std::string, std::list< std::string > > & resource_panels_
void TrackResourceWindow(const std::string &panel_id, ResourceWindowContent *resource_panel)
std::vector< std::string > GetAllCategories() const
void SetActiveCategory(const std::string &category, bool notify=true)
void RegisterRegistryWindowContent(std::unique_ptr< WindowContent > window)
Register a ContentRegistry-managed WindowContent instance.
static std::string GetCategoryIcon(const std::string &category)
void UnregisterPanel(size_t session_id, const std::string &base_card_id)
void ShowOnlyWindow(size_t session_id, const std::string &base_window_id)
void RegisterWindowContent(std::unique_ptr< WindowContent > window)
Register a WindowContent instance for central drawing.
std::function< Editor *(const std::string &) editor_resolver_)
static constexpr const char * kDashboardCategory
std::unordered_map< std::string, WorkspacePreset > presets_
void EnforceResourceLimits(const std::string &resource_type)
Enforce limits on resource panels (LRU eviction)
WindowDescriptor * FindDescriptorByPrefixedId(const std::string &prefixed_id)
void SetPanelBrowserCategoryWidth(float width, bool notify=true)
bool IsWindowVisibleImpl(size_t session_id, const std::string &base_card_id) const
void DrawAllVisiblePanels()
Draw all visible WindowContent instances (central drawing)
std::unordered_map< std::string, std::unique_ptr< WindowContent > > & panel_instances_
std::unordered_set< std::string > & registry_panel_ids_
std::vector< std::string > GetVisibleWindowIdsImpl(size_t session_id) const
void SetWindowPinnedImpl(size_t session_id, const std::string &base_card_id, bool pinned)
bool ToggleWindowImpl(size_t session_id, const std::string &base_card_id)
std::unordered_map< size_t, std::vector< std::string > > & session_cards_
void EnforceResourceWindowLimits(const std::string &resource_type)
std::unordered_map< std::string, std::string > * FindSessionReverseWindowMapping(size_t session_id)
float GetActiveSidePanelWidth(float viewport_width) const
std::string MakeWindowId(size_t session_id, const std::string &base_id) const
void OnEditorSwitch(const std::string &from_category, const std::string &to_category)
Handle editor/category switching for panel visibility.
std::unordered_map< std::string, bool > & pinned_panels_
WindowContent * GetWindowContent(const std::string &window_id)
Get a WindowContent instance by ID.
void RegisterRegistryWindowContentsForSession(size_t session_id)
Register descriptors for all registry window contents in a session.
std::unordered_map< size_t, std::unordered_map< WindowContextScope, std::string, WindowContextScopeHash > > & session_context_keys_
WindowContent * FindPanelInstance(const std::string &prefixed_panel_id, const std::string &base_panel_id)
void UnregisterPanelsWithPrefix(const std::string &prefix)
void PublishWindowVisibilityChanged(size_t session_id, const std::string &prefixed_window_id, const std::string &base_window_id, const std::string &category, bool visible) const
void RegisterPanelDescriptorForSession(size_t session_id, const WindowContent &panel)
bool OpenWindowImpl(size_t session_id, const std::string &base_card_id)
void ShowAllWindowsInCategory(size_t session_id, const std::string &category)
std::unordered_set< std::string > & global_panel_ids_
void ClearAnimationsForPanel(const std::string &panel_id)
Definition animator.cc:83
void BeginPanelTransition(const std::string &panel_id, TransitionType type)
Definition animator.cc:126
Draggable, dockable panel for editor sub-windows.
void SetPinChangedCallback(std::function< void(bool)> callback)
void SetPinned(bool pinned)
void SetStableId(const std::string &stable_id)
void SetPinnable(bool pinnable)
void SetPosition(Position pos)
bool Begin(bool *p_open=nullptr)
void SetDefaultSize(float width, float height)
#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
::yaze::EventBus * event_bus()
Get the current EventBus instance.
void SetCurrentEditor(Editor *editor)
Set the currently active editor.
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)
WindowDescriptor BuildDescriptorFromPanel(const WindowContent &panel)
WindowScope
Defines whether a window is session-scoped or global.
Animator & GetAnimator()
Definition animator.cc:318
static PanelVisibilityChangedEvent Create(const std::string &id, const std::string &base_id, const std::string &cat, bool vis, size_t session=0)
Metadata for a dockable editor window (formerly PanelInfo)
Get the expressive theme color for a category.
std::unordered_map< std::string, WindowDescriptor > descriptors
std::unordered_map< size_t, std::vector< std::string > > session_windows
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > session_reverse_window_mapping
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > session_window_mapping