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
32WindowDescriptor BuildDescriptorFromPanel(const WindowContent& panel) {
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();
42 descriptor.workflow_description = panel.GetWorkflowDescription();
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
98WorkspaceWindowManager::CategoryTheme WorkspaceWindowManager::GetCategoryTheme(
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
160WorkspaceWindowManager::SidePanelWidthBounds
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
212 if (browser_state_.sidebar_width <= 0.0f) {
213 return;
214 }
218 }
219}
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
247std::vector<std::string>* WorkspaceWindowManager::FindSessionWindowIds(
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
362void WorkspaceWindowManager::RegisterPanel(size_t session_id,
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
433void WorkspaceWindowManager::UnregisterPanel(size_t session_id,
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) {
807 if (panel->GetEditorCategory() == browser_state_.active_category ||
808 descriptor.category == browser_state_.active_category) {
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) {
855 window.SetPosition(gui::PanelWindow::Position::Center);
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
942bool WorkspaceWindowManager::OpenWindowImpl(size_t session_id,
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
974bool WorkspaceWindowManager::CloseWindowImpl(size_t session_id,
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
1006bool WorkspaceWindowManager::ToggleWindowImpl(size_t session_id,
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
1142void WorkspaceWindowManager::ShowOnlyWindow(size_t session_id,
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
1183std::vector<WindowDescriptor> WorkspaceWindowManager::GetWindowsInCategoryImpl(
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
1213std::vector<std::string> WorkspaceWindowManager::GetAllCategories(
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
1354void WorkspaceWindowManager::ResetToDefaults(size_t session_id) {
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
1363void WorkspaceWindowManager::ResetToDefaults(size_t session_id,
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)
static std::vector< std::string > GetDefaultPanels(EditorType type)
Get default visible panels for an editor.
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
#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)
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)
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