1#define IMGUI_DEFINE_MATH_OPERATORS
7#include "imgui/imgui.h"
15WindowDescriptor BuildDescriptorFromPanel(
const WindowContent& panel) {
16 WindowDescriptor descriptor;
17 auto* panel_ptr =
const_cast<WindowContent*
>(&panel);
18 descriptor.
card_id = panel.GetId();
19 descriptor.display_name = panel.GetDisplayName();
20 descriptor.icon = panel.GetIcon();
21 descriptor.category = panel.GetEditorCategory();
22 descriptor.priority = panel.GetPriority();
23 descriptor.workflow_group = panel.GetWorkflowGroup();
24 descriptor.workflow_label = panel.GetWorkflowLabel();
25 descriptor.workflow_description = panel.GetWorkflowDescription();
26 descriptor.workflow_priority = panel.GetWorkflowPriority();
27 descriptor.shortcut_hint = panel.GetShortcutHint();
28 descriptor.scope = panel.GetScope();
29 descriptor.window_lifecycle = panel.GetWindowLifecycle();
30 descriptor.context_scope = panel.GetContextScope();
31 descriptor.enabled_condition = [panel_ptr]() {
32 return panel_ptr->IsEnabled();
34 descriptor.disabled_tooltip = panel.GetDisabledTooltip();
35 descriptor.visibility_flag =
nullptr;
36 descriptor.window_title = panel.GetIcon() +
" " + panel.GetDisplayName();
46 std::unordered_map<std::string, std::string>();
48 std::unordered_map<std::string, std::string>();
53 LOG_INFO(
"WorkspaceWindowManager",
"Registered session %zu (total: %zu)",
74 LOG_INFO(
"WorkspaceWindowManager",
"Unregistered session %zu (total: %zu)",
88 const std::string old_key =
89 (session_map.find(scope) != session_map.end()) ? session_map[scope] :
"";
93 session_map[scope] = std::move(key);
103 const auto& session_map = sit->second;
104 auto it = session_map.find(scope);
105 if (it == session_map.end()) {
112 const std::string& legacy_base_id,
const std::string& canonical_base_id) {
113 if (legacy_base_id.empty() || canonical_base_id.empty() ||
114 legacy_base_id == canonical_base_id) {
121 const std::string& panel_id)
const {
126 const std::string& panel_id)
const {
127 if (panel_id.empty()) {
131 std::string resolved = panel_id;
132 std::unordered_set<std::string> visited;
133 visited.insert(resolved);
135 for (
int depth = 0; depth < 16; ++depth) {
141 const std::string& next = alias_it->second;
142 if (next == resolved || visited.count(next) > 0) {
147 visited.insert(resolved);
155 const std::string& old_key,
156 const std::string& new_key) {
158 if (!new_key.empty()) {
163 if (!session_windows) {
167 for (
const auto& prefixed_id : *session_windows) {
172 if (desc->context_scope != scope) {
175 if (!desc->visibility_flag || !*desc->visibility_flag) {
183 if (!base_id.empty()) {
190 size_t session_id,
const std::string& prefixed_id)
const {
195 auto it = reverse->find(prefixed_id);
196 if (it == reverse->end()) {
203 size_t session_id,
const std::vector<std::string>& panel_ids) {
205 if (!window_mapping) {
209 std::unordered_set<std::string> visible_set;
210 visible_set.reserve(panel_ids.size());
211 for (
const auto& panel_id : panel_ids) {
215 for (
const auto& [base_id, prefixed_id] : *window_mapping) {
217 if (!descriptor->visibility_flag) {
220 *descriptor->visibility_flag = visible_set.count(base_id) > 0;
224 LOG_INFO(
"WorkspaceWindowManager",
"Set %zu panels visible for session %zu",
225 panel_ids.size(), session_id);
228std::unordered_map<std::string, bool>
230 std::unordered_map<std::string, bool> state;
233 if (!session_mapping) {
237 for (
const auto& [base_id, prefixed_id] : *session_mapping) {
239 if (descriptor->visibility_flag) {
240 state[base_id] = *descriptor->visibility_flag;
249 size_t session_id,
const std::unordered_map<std::string, bool>& state,
250 bool publish_events) {
252 if (!session_mapping) {
254 "Cannot restore visibility: session %zu not found", session_id);
259 for (
const auto& [base_id, visible] : state) {
261 auto mapping_it = session_mapping->find(canonical_base_id);
262 if (mapping_it == session_mapping->end()) {
266 if (!descriptor->visibility_flag) {
269 *descriptor->visibility_flag = visible;
270 if (publish_events) {
272 canonical_base_id, descriptor->category,
280 "Restored visibility for %zu/%zu panels in session %zu", restored,
281 state.size(), session_id);
284std::unordered_map<std::string, bool>
286 std::unordered_map<std::string, bool> state;
289 std::string base_id = prefixed_id;
290 if (prefixed_id.size() > 2 && prefixed_id[0] ==
's') {
291 size_t dot_pos = prefixed_id.find(
'.');
292 if (dot_pos != std::string::npos && dot_pos + 1 < prefixed_id.size()) {
293 base_id = prefixed_id.substr(dot_pos + 1);
296 state[base_id] = pinned;
303 state.try_emplace(base_id, pinned);
310 const std::unordered_map<std::string, bool>& state) {
311 std::unordered_map<std::string, bool> canonical_state;
312 canonical_state.reserve(state.size());
313 for (
const auto& [base_id, pinned] : state) {
323 for (
const auto& [base_id, prefixed_id] : card_mapping) {
324 auto state_it = canonical_state.find(base_id);
325 if (state_it != canonical_state.end()) {
333 "Restored pinned state for %zu panels (%zu still pending "
340 size_t session_id,
const std::string& base_card_id)
const {
341 const WindowDescriptor* descriptor =
350 const WindowDescriptor& descriptor)
const {
351 return descriptor.GetImGuiWindowName();
355 size_t session_id,
const std::vector<WindowDescriptor>& cards) {
357 ImGui::IsWindowHovered(ImGuiHoveredFlags_None) &&
358 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
367 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
373 int card_count =
static_cast<int>(cards.size());
374 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) ||
375 ImGui::IsKeyPressed(ImGuiKey_J)) {
379 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) ||
380 ImGui::IsKeyPressed(ImGuiKey_K)) {
384 if (ImGui::IsKeyPressed(ImGuiKey_Home)) {
387 if (ImGui::IsKeyPressed(ImGuiKey_End)) {
393 if (ImGui::IsKeyPressed(ImGuiKey_Enter) ||
394 ImGui::IsKeyPressed(ImGuiKey_Space)) {
402 const std::string& card_id) {
407 size_t session_id,
const std::string& category)
const {
411 panels.begin(), panels.end(),
412 [
this, session_id](
const WindowDescriptor& a,
const WindowDescriptor& b) {
413 bool a_pinned = IsWindowPinnedImpl(session_id, a.card_id);
414 bool b_pinned = IsWindowPinnedImpl(session_id, b.card_id);
415 if (a_pinned != b_pinned) {
416 return a_pinned > b_pinned;
421 uint64_t a_time = (a_it !=
last_used_at_.end()) ? a_it->second : 0;
422 uint64_t b_time = (b_it !=
last_used_at_.end()) ? b_it->second : 0;
423 if (a_time != b_time) {
424 return a_time > b_time;
427 return a.priority < b.priority;
433size_t WorkspaceWindowManager::GetVisibleWindowCount(
size_t session_id)
const {
435 const auto* session_windows = FindSessionWindowIds(session_id);
436 if (!session_windows) {
439 for (
const auto& prefixed_card_id : *session_windows) {
440 if (
const auto* descriptor = FindDescriptorByPrefixedId(prefixed_card_id)) {
441 if (descriptor->visibility_flag && *descriptor->visibility_flag) {
449void WorkspaceWindowManager::UpdateSessionCount() {
450 session_count_ = session_cards_.size();
453std::string WorkspaceWindowManager::GetPrefixedWindowId(
454 size_t session_id,
const std::string& base_id)
const {
455 const std::string resolved_base_id = ResolveBaseWindowId(base_id);
457 const auto* session_mapping = FindSessionWindowMapping(session_id);
458 if (session_mapping) {
459 auto card_it = session_mapping->find(resolved_base_id);
460 if (card_it != session_mapping->end()) {
461 return card_it->second;
465 if (cards_.find(resolved_base_id) != cards_.end()) {
466 return resolved_base_id;
472void WorkspaceWindowManager::RegisterPanelDescriptorForSession(
473 size_t session_id,
const WindowContent& panel) {
474 RegisterSession(session_id);
475 std::string panel_id =
476 MakeWindowId(session_id, panel.GetId(), panel.GetScope());
477 bool already_registered = (cards_.find(panel_id) != cards_.end());
478 WindowDescriptor descriptor = BuildDescriptorFromPanel(panel);
479 RegisterPanel(session_id, descriptor);
480 if (!already_registered && panel.IsVisibleByDefault()) {
481 OpenWindowImpl(session_id, panel.GetId());
485void WorkspaceWindowManager::TrackPanelForSession(
size_t session_id,
486 const std::string& base_id,
487 const std::string& panel_id) {
488 const std::string canonical_base_id = ResolveBaseWindowId(base_id);
490 auto& card_list = session_cards_[session_id];
491 if (std::find(card_list.begin(), card_list.end(), panel_id) ==
493 card_list.push_back(panel_id);
495 session_card_mapping_[session_id][canonical_base_id] = panel_id;
496 session_reverse_card_mapping_[session_id][panel_id] = canonical_base_id;
502 auto pending_it = pending_pinned_base_ids_.find(canonical_base_id);
503 if (pending_it != pending_pinned_base_ids_.end()) {
504 pinned_panels_[panel_id] = pending_it->second;
505 pending_pinned_base_ids_.erase(pending_it);
509void WorkspaceWindowManager::UnregisterSessionPanels(
size_t session_id) {
510 const auto* session_windows = FindSessionWindowIds(session_id);
511 if (!session_windows) {
515 for (
const auto& prefixed_card_id : *session_windows) {
516 if (global_panel_ids_.find(prefixed_card_id) != global_panel_ids_.end()) {
519 const std::string base_card_id =
520 GetBaseIdForPrefixedId(session_id, prefixed_card_id);
521 if (!base_card_id.empty()) {
522 RememberPinnedStateForRemovedWindow(session_id, base_card_id,
525 cards_.erase(prefixed_card_id);
526 centralized_visibility_.erase(prefixed_card_id);
527 pinned_panels_.erase(prefixed_card_id);
bool IsWindowPinnedImpl(size_t session_id, const std::string &base_card_id) const
bool CloseWindowImpl(size_t session_id, const std::string &base_card_id)
std::string GetContextKey(size_t session_id, WindowContextScope scope) const
std::unordered_map< std::string, std::string > * FindSessionWindowMapping(size_t session_id)
std::string ResolveBaseWindowId(const std::string &panel_id) const
WindowBrowserState browser_state_
void RestoreVisibilityState(size_t session_id, const std::unordered_map< std::string, bool > &state, bool publish_events=false)
Restore panel visibility state from persistence.
std::vector< std::string > * FindSessionWindowIds(size_t session_id)
void ApplyContextPolicy(size_t session_id, WindowContextScope scope, const std::string &old_key, const std::string &new_key)
const WindowDescriptor * GetWindowDescriptorImpl(size_t session_id, const std::string &base_card_id) const
std::string GetBaseIdForPrefixedId(size_t session_id, const std::string &prefixed_id) const
std::vector< WindowDescriptor > GetWindowsInCategoryImpl(size_t session_id, const std::string &category) const
std::unordered_map< size_t, std::unordered_map< std::string, std::string > > & session_card_mapping_
std::unordered_map< std::string, bool > & pending_pinned_base_ids_
void TrackPanelForSession(size_t session_id, const std::string &base_id, const std::string &panel_id)
WindowSessionState session_state_
WindowDescriptor * FindDescriptorByPrefixedId(const std::string &prefixed_id)
void SetVisibleWindowsImpl(size_t session_id, const std::vector< std::string > &panel_ids)
void MarkWindowRecentlyUsedImpl(const std::string &card_id)
bool ToggleWindowImpl(size_t session_id, const std::string &base_card_id)
std::string ResolvePanelAlias(const std::string &panel_id) const
Resolve a panel ID through the alias table.
std::unordered_map< std::string, std::string > * FindSessionReverseWindowMapping(size_t session_id)
void SetActiveSession(size_t session_id)
std::string GetWindowNameImpl(size_t session_id, const std::string &base_card_id) const
std::unordered_map< std::string, uint64_t > & last_used_at_
std::vector< WindowDescriptor > GetWindowsSortedByMRUImpl(size_t session_id, const std::string &category) const
std::unordered_map< std::string, bool > SerializeVisibilityState(size_t session_id) const
Serialize panel visibility state for persistence.
void HandleSidebarKeyboardNav(size_t session_id, const std::vector< WindowDescriptor > &cards)
Handle keyboard navigation in sidebar (click-to-focus modal)
void UnregisterSession(size_t session_id)
void UpdateSessionCount()
std::unordered_map< std::string, std::string > & panel_id_aliases_
std::unordered_map< std::string, bool > & pinned_panels_
void RegisterSession(size_t session_id)
std::unordered_map< std::string, bool > SerializePinnedState() const
Serialize pinned panel state for persistence.
void UnregisterSessionPanels(size_t session_id)
void SetContextKey(size_t session_id, WindowContextScope scope, std::string key)
Set a string key for a given context scope (room/selection/etc)
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 RestorePinnedState(const std::unordered_map< std::string, bool > &state)
Restore pinned panel state from persistence.
void RegisterPanelAlias(const std::string &legacy_base_id, const std::string &canonical_base_id)
Register a legacy panel ID alias that resolves to a canonical ID.
std::unordered_set< std::string > & global_panel_ids_
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
WindowContextScope
Optional context binding for a window's behavior within an editor.
std::unordered_map< size_t, std::unordered_map< WindowContextScope, std::string, WindowContextScopeHash > > session_context_keys
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