7#include <unordered_set>
13#include "imgui/imgui.h"
14#include "imgui/imgui_internal.h"
17#include <TargetConditionals.h>
38 for (
const auto& window_id : default_windows) {
42 LOG_INFO(
"LayoutManager",
"Showing %zu default windows for editor type %d",
43 default_windows.size(),
static_cast<int>(type));
48 for (
const auto& [key, value] : map) {
55 std::unordered_map<std::string, bool>* map) {
60 for (
const auto& [key, value] : obj.
items()) {
61 if (value.is_boolean()) {
62 (*map)[key] = value.get<
bool>();
68 std::unordered_map<std::string, bool>* windows) {
84 const std::string& project_key) {
86 if (!layouts_dir.ok()) {
91 std::filesystem::path projects_dir = *layouts_dir /
"projects";
93 return projects_dir / (project_key +
".json");
100 return *layouts_dir /
"layouts.json";
109 if (preset_name ==
"Minimal") {
113 if (preset_name ==
"Developer") {
117 if (preset_name ==
"Designer") {
121 if (preset_name ==
"Modder") {
125 if (preset_name ==
"Overworld Expert") {
129 if (preset_name ==
"Dungeon Expert") {
133 if (preset_name ==
"Testing") {
137 if (preset_name ==
"Audio") {
147 if (profile_id ==
"mapping") {
149 return "Dungeon Expert";
151 return "Overworld Expert";
153 if (profile_id ==
"code") {
156 if (profile_id ==
"debug") {
159 if (profile_id ==
"chat") {
168 ImGuiID dockspace_id) {
172 "Layout for editor type %d already initialized, skipping",
173 static_cast<int>(type));
181 LOG_INFO(
"LayoutManager",
"Initializing layout for editor type %d",
182 static_cast<int>(type));
185 ImGui::DockBuilderRemoveNode(dockspace_id);
186 ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
188 ImVec2 dockspace_size = ImVec2(1280, 720);
189 if (
auto* viewport = ImGui::GetMainViewport()) {
190 dockspace_size = viewport->WorkSize;
194 if (last_size.x > 0.0f && last_size.y > 0.0f) {
195 dockspace_size = last_size;
197 ImGui::DockBuilderSetNodeSize(dockspace_id, dockspace_size);
206 ImGui::DockBuilderFinish(dockspace_id);
214 ImGuiDockNode* node = ImGui::DockBuilderGetNode(dockspace_id);
217 "Cannot rebuild layout: dockspace ID %u not found", dockspace_id);
221 LOG_INFO(
"LayoutManager",
"Forcing rebuild of layout for editor type %d",
222 static_cast<int>(type));
232 ImGui::DockBuilderRemoveNode(dockspace_id);
233 ImGui::DockBuilderAddNode(dockspace_id, ImGuiDockNodeFlags_DockSpace);
234 ImVec2 dockspace_size = ImGui::GetMainViewport()->WorkSize;
236 if (last_size.x > 0.0f && last_size.y > 0.0f) {
237 dockspace_size = last_size;
239 ImGui::DockBuilderSetNodeSize(dockspace_id, dockspace_size);
248 ImGui::DockBuilderFinish(dockspace_id);
253 LOG_INFO(
"LayoutManager",
"Layout rebuild complete for editor type %d",
254 static_cast<int>(type));
262 float bottom = 0.22f;
264 float vertical_split = 0.52f;
270 case EditorType::kDungeon:
277 case EditorType::kOverworld:
283 case EditorType::kGraphics:
288 case EditorType::kPalette:
293 case EditorType::kSprite:
298 case EditorType::kScreen:
303 case EditorType::kMessage:
308 case EditorType::kAssembly:
313 case EditorType::kEmulator:
318 case EditorType::kAgent:
337 ImGuiID left_top = 0;
338 ImGuiID left_bottom = 0;
339 ImGuiID right_top = 0;
340 ImGuiID right_bottom = 0;
348 bool left_top =
false;
349 bool left_bottom =
false;
350 bool right_top =
false;
351 bool right_bottom =
false;
355 const std::string& panel_id) {
366 std::vector<std::pair<std::string, DockPosition>> docked_panels;
369 std::unordered_set<std::string> seen_panels;
372 auto append_ordered = [&](
const std::vector<std::string>& ordered_ids) {
373 for (
const auto& panel_id : ordered_ids) {
374 if (seen_panels.contains(panel_id) ||
382 docked_panels.emplace_back(it->first, it->second);
383 seen_panels.insert(panel_id);
390 std::vector<std::pair<std::string, DockPosition>> remaining_panels;
393 if (seen_panels.contains(panel_id) ||
397 remaining_panels.emplace_back(panel_id, position);
400 std::sort(remaining_panels.begin(), remaining_panels.end(),
401 [](
const auto& lhs,
const auto& rhs) {
402 if (lhs.second != rhs.second) {
403 return static_cast<int>(lhs.second) <
404 static_cast<int>(rhs.second);
406 return lhs.first < rhs.first;
408 docked_panels.insert(docked_panels.end(), remaining_panels.begin(),
409 remaining_panels.end());
411 return docked_panels;
415 const std::vector<std::pair<std::string, DockPosition>>& docked_panels) {
417 for (
const auto& [_, pos] : docked_panels) {
433 needs.left_top =
true;
437 needs.left_bottom =
true;
441 needs.right_top =
true;
445 needs.right_bottom =
true;
467 const std::vector<std::pair<std::string, DockPosition>>& docked_panels,
469 if (!window_manager) {
473 float preferred_width = 0.0f;
474 for (
const auto& [panel_id, position] : docked_panels) {
475 if (!matches_region(position)) {
479 preferred_width = std::max(preferred_width, panel->GetPreferredWidth());
482 return preferred_width;
488 const std::vector<std::pair<std::string, DockPosition>>& docked_panels) {
489 if (!cfg || !window_manager || viewport_width <= 0.0f) {
496 if (preferred_left > 0.0f) {
497 cfg->
left = std::clamp(preferred_left / viewport_width, 0.14f, 0.36f);
504 if (preferred_right > 0.0f) {
505 cfg->
right = std::clamp(preferred_right / viewport_width, 0.18f, 0.42f);
513 ids.
center = dockspace_id;
517 ids.left = ImGui::DockBuilderSplitNode(ids.center, ImGuiDir_Left, cfg.
left,
518 nullptr, &ids.center);
521 ids.right = ImGui::DockBuilderSplitNode(ids.center, ImGuiDir_Right,
522 cfg.
right,
nullptr, &ids.center);
525 ids.bottom = ImGui::DockBuilderSplitNode(ids.center, ImGuiDir_Down,
526 cfg.
bottom,
nullptr, &ids.center);
529 ids.top = ImGui::DockBuilderSplitNode(ids.center, ImGuiDir_Up, cfg.
top,
530 nullptr, &ids.center);
536 ids.left_bottom = ImGui::DockBuilderSplitNode(
537 ids.left, ImGuiDir_Down, cfg.
vertical_split,
nullptr, &ids.left_top);
545 ids.right_bottom = ImGui::DockBuilderSplitNode(
546 ids.right, ImGuiDir_Down, cfg.
vertical_split,
nullptr, &ids.right_top);
555 ImGuiID dockspace_id) {
556 auto preset = LayoutPresets::GetDefaultPreset(type);
558 if (!window_manager_) {
560 "WorkspaceWindowManager not available, skipping dock layout for "
562 static_cast<int>(type));
566 const size_t session_id =
567 window_manager_ ? window_manager_->GetActiveSessionId() : 0;
568 const auto docked_panels = CollectDockedPanels(preset);
573 const ImGuiViewport* viewport = ImGui::GetMainViewport();
574 const float viewport_width = viewport ? viewport->WorkSize.x : 0.0f;
575 const bool is_compact =
576#if defined(__APPLE__) && TARGET_OS_IOS == 1
578 static bool compact_mode =
true;
579 constexpr float kEnterCompactWidth = 900.0f;
580 constexpr float kExitCompactWidth = 940.0f;
581 if (viewport_width <= 0.0f) {
584 compact_mode = compact_mode ? (viewport_width < kExitCompactWidth)
585 : (viewport_width < kEnterCompactWidth);
589 (viewport_width > 0.0f && viewport_width < 900.0f);
592 DockSplitNeeds needs{};
593 DockSplitConfig cfg{};
595 needs = ComputeSplitNeeds(docked_panels);
596 cfg = DockSplitConfig::ForEditor(type);
597 ApplyPreferredSplitWidths(&cfg, needs, viewport_width, window_manager_,
601 DockNodeIds ids = BuildDockTree(dockspace_id, needs, cfg);
605 case DockPosition::Left:
606 if (ids.left_top || ids.left_bottom) {
608 return ids.left_top ? ids.left_top : ids.left_bottom;
610 return ids.left ? ids.left : ids.center;
611 case DockPosition::Right:
612 if (ids.right_top || ids.right_bottom) {
613 return ids.right_top ? ids.right_top : ids.right_bottom;
615 return ids.right ? ids.right : ids.center;
616 case DockPosition::Bottom:
617 return ids.bottom ? ids.bottom : ids.center;
618 case DockPosition::Top:
619 return ids.top ? ids.top : ids.center;
620 case DockPosition::LeftTop:
621 return ids.left_top ? ids.left_top : (ids.left ? ids.left : ids.center);
622 case DockPosition::LeftBottom:
623 return ids.left_bottom ? ids.left_bottom
624 : (ids.left ? ids.left : ids.center);
625 case DockPosition::RightTop:
626 return ids.right_top ? ids.right_top
627 : (ids.right ? ids.right : ids.center);
628 case DockPosition::RightBottom:
629 return ids.right_bottom ? ids.right_bottom
630 : (ids.right ? ids.right : ids.center);
631 case DockPosition::Center:
637 std::vector<ImGuiID> auto_hide_nodes;
640 for (
const auto& [panel_id, position] : docked_panels) {
643 ? window_manager_->GetWindowDescriptor(session_id, panel_id)
647 "Preset references window '%s' that is not registered (session "
649 panel_id.c_str(), session_id);
653 std::string window_title = window_manager_->GetWorkspaceWindowName(*desc);
654 if (window_title.empty()) {
656 "Cannot dock window '%s': missing window name (session %zu)",
657 panel_id.c_str(), session_id);
661 const ImGuiID dock_id = get_dock_id(position);
662 ImGui::DockBuilderDockWindow(window_title.c_str(), dock_id);
664 if (
WindowContent* panel = window_manager_->GetWindowContent(panel_id);
665 panel && panel->PreferAutoHideTabBar() &&
666 std::find(auto_hide_nodes.begin(), auto_hide_nodes.end(), dock_id) ==
667 auto_hide_nodes.end()) {
668 if (ImGuiDockNode* node = ImGui::DockBuilderGetNode(dock_id)) {
669 node->LocalFlags |= ImGuiDockNodeFlags_AutoHideTabBar;
670 auto_hide_nodes.push_back(dock_id);
677void LayoutManager::BuildOverworldLayout(ImGuiID dockspace_id) {
678 BuildLayoutFromPreset(EditorType::kOverworld, dockspace_id);
680void LayoutManager::BuildDungeonLayout(ImGuiID dockspace_id) {
681 BuildLayoutFromPreset(EditorType::kDungeon, dockspace_id);
683void LayoutManager::BuildGraphicsLayout(ImGuiID dockspace_id) {
684 BuildLayoutFromPreset(EditorType::kGraphics, dockspace_id);
686void LayoutManager::BuildPaletteLayout(ImGuiID dockspace_id) {
687 BuildLayoutFromPreset(EditorType::kPalette, dockspace_id);
689void LayoutManager::BuildScreenLayout(ImGuiID dockspace_id) {
690 BuildLayoutFromPreset(EditorType::kScreen, dockspace_id);
692void LayoutManager::BuildMusicLayout(ImGuiID dockspace_id) {
693 BuildLayoutFromPreset(EditorType::kMusic, dockspace_id);
695void LayoutManager::BuildSpriteLayout(ImGuiID dockspace_id) {
696 BuildLayoutFromPreset(EditorType::kSprite, dockspace_id);
698void LayoutManager::BuildMessageLayout(ImGuiID dockspace_id) {
699 BuildLayoutFromPreset(EditorType::kMessage, dockspace_id);
701void LayoutManager::BuildAssemblyLayout(ImGuiID dockspace_id) {
702 BuildLayoutFromPreset(EditorType::kAssembly, dockspace_id);
704void LayoutManager::BuildSettingsLayout(ImGuiID dockspace_id) {
705 BuildLayoutFromPreset(EditorType::kSettings, dockspace_id);
707void LayoutManager::BuildEmulatorLayout(ImGuiID dockspace_id) {
708 BuildLayoutFromPreset(EditorType::kEmulator, dockspace_id);
711void LayoutManager::SaveCurrentLayout(
const std::string& name,
bool persist) {
712 if (!window_manager_) {
714 "Cannot save layout '%s': WorkspaceWindowManager not available",
722 size_t session_id = window_manager_->GetActiveSessionId();
723 auto visibility_state = window_manager_->SerializeVisibilityState(session_id);
726 saved_layouts_[name] = visibility_state;
727 saved_pinned_layouts_[name] = window_manager_->SerializePinnedState();
728 layout_scopes_[name] = scope;
732 const char* ini_data = ImGui::SaveIniSettingsToMemory(&ini_size);
733 if (ini_data && ini_size > 0) {
734 saved_imgui_layouts_[name] = std::string(ini_data, ini_size);
738 SaveLayoutsToDisk(scope);
741 LOG_INFO(
"LayoutManager",
"Saved layout '%s' with %zu window states%s",
742 name.c_str(), visibility_state.size(),
743 persist ?
"" :
" (session-only)");
746void LayoutManager::LoadLayout(
const std::string& name) {
747 if (!window_manager_) {
749 "Cannot load layout '%s': WorkspaceWindowManager not available",
755 auto layout_it = saved_layouts_.find(name);
756 if (layout_it == saved_layouts_.end()) {
757 LOG_WARN(
"LayoutManager",
"Layout '%s' not found", name.c_str());
762 size_t session_id = window_manager_->GetActiveSessionId();
763 window_manager_->RestoreVisibilityState(session_id, layout_it->second,
766 auto pinned_it = saved_pinned_layouts_.find(name);
767 if (pinned_it != saved_pinned_layouts_.end()) {
768 window_manager_->RestorePinnedState(pinned_it->second);
772 auto imgui_it = saved_imgui_layouts_.find(name);
773 if (imgui_it != saved_imgui_layouts_.end() && !imgui_it->second.empty()) {
774 ImGui::LoadIniSettingsFromMemory(imgui_it->second.c_str(),
775 imgui_it->second.size());
778 LOG_INFO(
"LayoutManager",
"Loaded layout '%s'", name.c_str());
781void LayoutManager::CaptureTemporarySessionLayout(
size_t session_id) {
782 if (!window_manager_) {
786 temp_session_id_ = session_id;
787 temp_session_visibility_ =
788 window_manager_->SerializeVisibilityState(session_id);
789 temp_session_pinned_ = window_manager_->SerializePinnedState();
792 const char* ini_data = ImGui::SaveIniSettingsToMemory(&ini_size);
793 if (ini_data && ini_size > 0) {
794 temp_session_imgui_layout_ = std::string(ini_data, ini_size);
796 temp_session_imgui_layout_.clear();
799 has_temp_session_layout_ =
true;
802 "Captured temporary session layout for session %zu (%zu panel states)",
803 session_id, temp_session_visibility_.size());
806bool LayoutManager::RestoreTemporarySessionLayout(
size_t session_id,
807 bool clear_after_restore) {
808 if (!window_manager_ || !has_temp_session_layout_) {
812 if (session_id != temp_session_id_) {
814 "Session layout snapshot belongs to session %zu, requested %zu",
815 temp_session_id_, session_id);
819 window_manager_->RestoreVisibilityState(session_id, temp_session_visibility_,
821 window_manager_->RestorePinnedState(temp_session_pinned_);
823 if (!temp_session_imgui_layout_.empty()) {
824 ImGui::LoadIniSettingsFromMemory(temp_session_imgui_layout_.c_str(),
825 temp_session_imgui_layout_.size());
828 if (clear_after_restore) {
829 ClearTemporarySessionLayout();
832 LOG_INFO(
"LayoutManager",
"Restored temporary session layout for session %zu",
837void LayoutManager::ClearTemporarySessionLayout() {
838 has_temp_session_layout_ =
false;
839 temp_session_id_ = 0;
840 temp_session_visibility_.clear();
841 temp_session_pinned_.clear();
842 temp_session_imgui_layout_.clear();
845bool LayoutManager::SaveNamedSnapshot(
const std::string& name,
847 if (!window_manager_ || name.empty()) {
852 snapshot.
visibility = window_manager_->SerializeVisibilityState(session_id);
853 snapshot.
pinned = window_manager_->SerializePinnedState();
855 const char* ini_data = ImGui::SaveIniSettingsToMemory(&ini_size);
856 if (ini_data && ini_size > 0) {
859 named_snapshots_[name] = std::move(snapshot);
860 LOG_INFO(
"LayoutManager",
"Saved named snapshot '%s' for session %zu",
861 name.c_str(), session_id);
865bool LayoutManager::RestoreNamedSnapshot(
const std::string& name,
867 bool remove_after_restore) {
868 if (!window_manager_) {
871 auto it = named_snapshots_.find(name);
872 if (it == named_snapshots_.end()) {
879 window_manager_->RestoreVisibilityState(session_id, snapshot.
visibility,
881 window_manager_->RestorePinnedState(snapshot.
pinned);
883 ImGui::LoadIniSettingsFromMemory(snapshot.
imgui_layout.c_str(),
886 if (remove_after_restore) {
887 named_snapshots_.erase(it);
892bool LayoutManager::DeleteNamedSnapshot(
const std::string& name) {
893 auto it = named_snapshots_.find(name);
894 if (it == named_snapshots_.end()) {
897 named_snapshots_.erase(it);
901std::vector<std::string> LayoutManager::ListNamedSnapshots(
902 size_t session_id)
const {
903 std::vector<std::string> names;
904 names.reserve(named_snapshots_.size());
905 for (
const auto& [name, snapshot] : named_snapshots_) {
906 if (snapshot.session_id == session_id) {
907 names.push_back(name);
910 std::sort(names.begin(), names.end());
914bool LayoutManager::HasNamedSnapshot(
const std::string& name)
const {
915 return named_snapshots_.find(name) != named_snapshots_.end();
918bool LayoutManager::DeleteLayout(
const std::string& name) {
919 auto layout_it = saved_layouts_.find(name);
920 if (layout_it == saved_layouts_.end()) {
921 LOG_WARN(
"LayoutManager",
"Cannot delete layout '%s': not found",
927 auto scope_it = layout_scopes_.find(name);
928 if (scope_it != layout_scopes_.end()) {
929 scope = scope_it->second;
932 saved_layouts_.erase(layout_it);
933 saved_imgui_layouts_.erase(name);
934 saved_pinned_layouts_.erase(name);
935 layout_scopes_.erase(name);
937 SaveLayoutsToDisk(scope);
939 LOG_INFO(
"LayoutManager",
"Deleted layout '%s'", name.c_str());
943std::vector<LayoutProfile> LayoutManager::GetBuiltInProfiles() {
947 .description =
"Focused editing workspace with minimal panel noise",
948 .preset_name =
"Minimal",
949 .open_agent_chat =
false},
952 .description =
"Debugger-first workspace for tracing and memory tools",
953 .preset_name =
"Developer",
954 .open_agent_chat =
false},
957 .description =
"Map-centric layout for overworld or dungeon workflows",
958 .preset_name =
"Overworld Expert",
959 .open_agent_chat =
false},
962 .description =
"Collaboration-heavy layout with agent-centric tooling",
963 .preset_name =
"Modder",
964 .open_agent_chat =
true},
968bool LayoutManager::ApplyBuiltInProfile(
const std::string& profile_id,
972 if (!window_manager_) {
974 "Cannot apply profile '%s': WorkspaceWindowManager not available",
981 for (
const auto& profile : GetBuiltInProfiles()) {
982 if (profile.id == profile_id) {
983 matched_profile = profile;
990 LOG_WARN(
"LayoutManager",
"Unknown layout profile id: %s",
995 const std::string resolved_preset =
996 ResolveProfilePresetName(profile_id, editor_type);
997 if (!resolved_preset.empty()) {
1002 if (!TryGetNamedPreset(matched_profile.
preset_name, &preset)) {
1003 LOG_WARN(
"LayoutManager",
"Unable to resolve preset '%s' for profile '%s'",
1004 matched_profile.
preset_name.c_str(), profile_id.c_str());
1008 window_manager_->HideAllWindowsInSession(session_id);
1010 window_manager_->OpenWindow(session_id, panel_id);
1016 *out_profile = matched_profile;
1019 LOG_INFO(
"LayoutManager",
"Applied profile '%s' via preset '%s'",
1020 profile_id.c_str(), matched_profile.
preset_name.c_str());
1024std::vector<std::string> LayoutManager::GetSavedLayoutNames()
const {
1025 std::vector<std::string> names;
1026 names.reserve(saved_layouts_.size());
1027 for (
const auto& [name, _] : saved_layouts_) {
1028 names.push_back(name);
1033bool LayoutManager::HasLayout(
const std::string& name)
const {
1034 return saved_layouts_.find(name) != saved_layouts_.end();
1037void LayoutManager::LoadLayoutsFromDisk() {
1038 saved_layouts_.clear();
1039 saved_imgui_layouts_.clear();
1040 saved_pinned_layouts_.clear();
1041 layout_scopes_.clear();
1043 LoadLayoutsFromDiskInternal(LayoutScope::kGlobal,
false);
1044 if (!project_layout_key_.empty()) {
1045 LoadLayoutsFromDiskInternal(LayoutScope::kProject,
true);
1049void LayoutManager::SetProjectLayoutKey(
const std::string& key) {
1054 project_layout_key_ = key;
1055 LoadLayoutsFromDisk();
1058void LayoutManager::UseGlobalLayouts() {
1059 project_layout_key_.clear();
1060 LoadLayoutsFromDisk();
1064 return project_layout_key_.empty() ? LayoutScope::kGlobal
1065 : LayoutScope::kProject;
1068void LayoutManager::LoadLayoutsFromDiskInternal(
LayoutScope scope,
bool merge) {
1069 std::filesystem::path layout_path =
1070 GetLayoutsFilePath(scope, project_layout_key_);
1071 if (layout_path.empty()) {
1075 if (!std::filesystem::exists(layout_path)) {
1077 LOG_INFO(
"LayoutManager",
"No layouts file at %s",
1078 layout_path.string().c_str());
1084 std::ifstream file(layout_path);
1085 if (!file.is_open()) {
1086 LOG_WARN(
"LayoutManager",
"Failed to open layouts file: %s",
1087 layout_path.string().c_str());
1095 LOG_WARN(
"LayoutManager",
"Layouts file missing 'layouts' object: %s",
1096 layout_path.string().c_str());
1100 for (
auto& [name, entry] : root[
"layouts"].
items()) {
1101 if (!entry.is_object()) {
1105 std::unordered_map<std::string, bool> windows;
1106 std::unordered_map<std::string, bool> pinned;
1108 JsonToWindowMap(entry, &windows);
1109 if (entry.contains(
"pinned")) {
1110 JsonToBoolMap(entry[
"pinned"], &pinned);
1113 saved_layouts_[name] = std::move(windows);
1114 saved_pinned_layouts_[name] = std::move(pinned);
1115 layout_scopes_[name] = scope;
1117 if (entry.contains(
"imgui_ini") && entry[
"imgui_ini"].is_string()) {
1118 saved_imgui_layouts_[name] = entry[
"imgui_ini"].get<std::string>();
1120 saved_imgui_layouts_.erase(name);
1124 LOG_INFO(
"LayoutManager",
"Loaded layouts from %s",
1125 layout_path.string().c_str());
1126 }
catch (
const std::exception& e) {
1127 LOG_WARN(
"LayoutManager",
"Failed to load layouts: %s", e.what());
1132 std::filesystem::path layout_path =
1133 GetLayoutsFilePath(scope, project_layout_key_);
1134 if (layout_path.empty()) {
1135 LOG_WARN(
"LayoutManager",
"No layout path resolved for scope");
1142 LOG_WARN(
"LayoutManager",
"Failed to create layout directory: %s",
1143 status.ToString().c_str());
1149 root[
"version"] = 2;
1152 for (
const auto& [name, windows] : saved_layouts_) {
1153 auto scope_it = layout_scopes_.find(name);
1154 if (scope_it != layout_scopes_.end() && scope_it->second != scope) {
1159 entry[kWindowsKey] = BoolMapToJson(windows);
1161 auto pinned_it = saved_pinned_layouts_.find(name);
1162 if (pinned_it != saved_pinned_layouts_.end()) {
1163 entry[
"pinned"] = BoolMapToJson(pinned_it->second);
1166 auto imgui_it = saved_imgui_layouts_.find(name);
1167 if (imgui_it != saved_imgui_layouts_.end()) {
1168 entry[
"imgui_ini"] = imgui_it->second;
1171 root[
"layouts"][name] = entry;
1174 std::ofstream file(layout_path);
1175 if (!file.is_open()) {
1176 LOG_WARN(
"LayoutManager",
"Failed to open layouts file for write: %s",
1177 layout_path.string().c_str());
1180 file << root.
dump(2);
1182 }
catch (
const std::exception& e) {
1183 LOG_WARN(
"LayoutManager",
"Failed to save layouts: %s", e.what());
1188 layouts_initialized_[type] =
false;
1189 LOG_INFO(
"LayoutManager",
"Reset layout for editor type %d",
1190 static_cast<int>(type));
1194 auto it = layouts_initialized_.find(type);
1195 return it != layouts_initialized_.end() && it->second;
1199 layouts_initialized_[type] =
true;
1200 LOG_INFO(
"LayoutManager",
"Marked layout for editor type %d as initialized",
1201 static_cast<int>(type));
1204void LayoutManager::ClearInitializationFlags() {
1205 layouts_initialized_.clear();
1206 LOG_INFO(
"LayoutManager",
"Cleared all layout initialization flags");
1209std::string LayoutManager::GetWindowTitle(
const std::string& card_id)
const {
1210 if (!window_manager_) {
1214 const size_t session_id = window_manager_->GetActiveSessionId();
1215 return window_manager_->GetWorkspaceWindowName(session_id, card_id);
std::string dump(int=-1, char=' ', bool=false, int=0) const
bool contains(const std::string &) const
EditorType current_editor_type_
ImGuiID last_dockspace_id_
WorkspaceWindowManager * window_manager_
void RebuildLayout(EditorType type, ImGuiID dockspace_id)
Force rebuild of layout for a specific editor type.
std::unordered_map< EditorType, bool > layouts_initialized_
bool IsLayoutInitialized(EditorType type) const
Check if a layout has been initialized for an editor.
void MarkLayoutInitialized(EditorType type)
Mark a layout as initialized.
void InitializeEditorLayout(EditorType type, ImGuiID dockspace_id)
Initialize the default layout for a specific editor type.
void BuildLayoutFromPreset(EditorType type, ImGuiID dockspace_id)
static PanelLayoutPreset GetLogicDebuggerPreset()
Get the "logic debugger" workspace preset (QA and debug focused)
static PanelLayoutPreset GetDungeonMasterPreset()
Get the "dungeon master" workspace preset.
static PanelLayoutPreset GetAudioEngineerPreset()
Get the "audio engineer" workspace preset (music focused)
static PanelLayoutPreset GetDesignerPreset()
Get the "designer" workspace preset (visual-focused)
static std::vector< std::string > GetDefaultWindows(EditorType type)
static PanelLayoutPreset GetOverworldArtistPreset()
Get the "overworld artist" workspace preset.
static PanelLayoutPreset GetModderPreset()
Get the "modder" workspace preset (full-featured)
static PanelLayoutPreset GetMinimalPreset()
Get the "minimal" workspace preset (minimal cards)
static PanelLayoutPreset GetDeveloperPreset()
Get the "developer" workspace preset (debug-focused)
Base interface for all logical window content components.
Central registry for all editor cards with session awareness and dependency injection.
bool OpenWindow(size_t session_id, const std::string &base_window_id)
WindowContent * GetWindowContent(const std::string &window_id)
Get a WindowContent instance by ID.
static ImVec2 GetLastDockspaceSize()
#define LOG_ERROR(category, format,...)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
bool IsLeftDockPosition(DockPosition pos)
std::vector< std::pair< std::string, DockPosition > > CollectDockedPanels(const PanelLayoutPreset &preset)
float ResolvePreferredRegionWidth(WorkspaceWindowManager *window_manager, const std::vector< std::pair< std::string, DockPosition > > &docked_panels, bool(*matches_region)(DockPosition))
bool IsRightDockPosition(DockPosition pos)
std::string ResolveProfilePresetName(const std::string &profile_id, EditorType editor_type)
bool TryGetNamedPreset(const std::string &preset_name, PanelLayoutPreset *preset_out)
yaze::Json BoolMapToJson(const std::unordered_map< std::string, bool > &map)
void ApplyPreferredSplitWidths(DockSplitConfig *cfg, const DockSplitNeeds &needs, float viewport_width, WorkspaceWindowManager *window_manager, const std::vector< std::pair< std::string, DockPosition > > &docked_panels)
constexpr char kLegacyPanelsKey[]
void JsonToWindowMap(const yaze::Json &entry, std::unordered_map< std::string, bool > *windows)
std::filesystem::path GetLayoutsFilePath(LayoutScope scope, const std::string &project_key)
void JsonToBoolMap(const yaze::Json &obj, std::unordered_map< std::string, bool > *map)
bool ShouldDockPanelInDefaultLayout(const PanelLayoutPreset &preset, const std::string &panel_id)
constexpr char kWindowsKey[]
DockSplitNeeds ComputeSplitNeeds(const std::vector< std::pair< std::string, DockPosition > > &docked_panels)
void ShowDefaultWindowsForEditor(WorkspaceWindowManager *registry, EditorType type)
DockNodeIds BuildDockTree(ImGuiID dockspace_id, const DockSplitNeeds &needs, const DockSplitConfig &cfg)
LayoutScope
Storage scope for saved layouts.
DockPosition
Preferred dock position for a card in a layout.
std::unordered_map< std::string, bool > visibility
std::unordered_map< std::string, bool > pinned
Built-in workflow-oriented layout profiles.
Defines default panel visibility for an editor type.
std::vector< std::string > optional_panels
std::unordered_map< std::string, DockPosition > panel_positions
std::vector< std::string > default_visible_panels
bool dock_only_default_visible_panels
Metadata for a dockable editor window (formerly PanelInfo)
static DockSplitConfig ForEditor(EditorType type)