1#define IMGUI_DEFINE_MATH_OPERATORS
9#include "imgui/imgui.h"
35 return panel_ptr->IsEnabled();
49 std::unordered_map<std::string, std::string>();
51 std::unordered_map<std::string, std::string>();
56 LOG_INFO(
"WorkspaceWindowManager",
"Registered session %zu (total: %zu)",
77 LOG_INFO(
"WorkspaceWindowManager",
"Unregistered session %zu (total: %zu)",
91 const std::string old_key =
92 (session_map.find(scope) != session_map.end()) ? session_map[scope] :
"";
96 session_map[scope] = std::move(key);
106 const auto& session_map = sit->second;
107 auto it = session_map.find(scope);
108 if (it == session_map.end()) {
115 const std::string& legacy_base_id,
const std::string& canonical_base_id) {
116 if (legacy_base_id.empty() || canonical_base_id.empty() ||
117 legacy_base_id == canonical_base_id) {
124 const std::string& panel_id)
const {
129 const std::string& panel_id)
const {
130 if (panel_id.empty()) {
134 std::string resolved = panel_id;
135 std::unordered_set<std::string> visited;
136 visited.insert(resolved);
138 for (
int depth = 0; depth < 16; ++depth) {
144 const std::string& next = alias_it->second;
145 if (next == resolved || visited.count(next) > 0) {
150 visited.insert(resolved);
158 const std::string& old_key,
159 const std::string& new_key) {
161 if (!new_key.empty()) {
166 if (!session_windows) {
170 for (
const auto& prefixed_id : *session_windows) {
175 if (desc->context_scope != scope) {
178 if (!desc->visibility_flag || !*desc->visibility_flag) {
186 if (!base_id.empty()) {
193 size_t session_id,
const std::string& prefixed_id)
const {
198 auto it = reverse->find(prefixed_id);
199 if (it == reverse->end()) {
206 size_t session_id,
const std::vector<std::string>& panel_ids) {
208 if (!window_mapping) {
212 std::unordered_set<std::string> visible_set;
213 visible_set.reserve(panel_ids.size());
214 for (
const auto& panel_id : panel_ids) {
218 for (
const auto& [base_id, prefixed_id] : *window_mapping) {
220 if (!descriptor->visibility_flag) {
223 *descriptor->visibility_flag = visible_set.count(base_id) > 0;
227 LOG_INFO(
"WorkspaceWindowManager",
"Set %zu panels visible for session %zu",
228 panel_ids.size(), session_id);
231std::unordered_map<std::string, bool>
233 std::unordered_map<std::string, bool> state;
236 if (!session_mapping) {
240 for (
const auto& [base_id, prefixed_id] : *session_mapping) {
245 if (descriptor->visibility_flag) {
246 state[base_id] = *descriptor->visibility_flag;
255 size_t session_id,
const std::unordered_map<std::string, bool>& state,
256 bool publish_events) {
258 if (!session_mapping) {
260 "Cannot restore visibility: session %zu not found", session_id);
265 for (
const auto& [base_id, visible] : state) {
270 auto mapping_it = session_mapping->find(canonical_base_id);
271 if (mapping_it == session_mapping->end()) {
275 if (!descriptor->visibility_flag) {
280 *descriptor->visibility_flag = visible;
281 if (publish_events) {
283 canonical_base_id, descriptor->category,
291 "Restored visibility for %zu/%zu panels in session %zu", restored,
292 state.size(), session_id);
295std::unordered_map<std::string, bool>
297 std::unordered_map<std::string, bool> state;
300 std::string base_id = prefixed_id;
301 if (prefixed_id.size() > 2 && prefixed_id[0] ==
's') {
302 size_t dot_pos = prefixed_id.find(
'.');
303 if (dot_pos != std::string::npos && dot_pos + 1 < prefixed_id.size()) {
304 base_id = prefixed_id.substr(dot_pos + 1);
307 state[base_id] = pinned;
314 state.try_emplace(base_id, pinned);
321 const std::unordered_map<std::string, bool>& state) {
322 std::unordered_map<std::string, bool> canonical_state;
323 canonical_state.reserve(state.size());
324 for (
const auto& [base_id, pinned] : state) {
334 for (
const auto& [base_id, prefixed_id] : card_mapping) {
335 auto state_it = canonical_state.find(base_id);
336 if (state_it != canonical_state.end()) {
344 "Restored pinned state for %zu panels (%zu still pending "
351 size_t session_id,
const std::string& base_card_id)
const {
366 size_t session_id,
const std::vector<WindowDescriptor>& cards) {
368 ImGui::IsWindowHovered(ImGuiHoveredFlags_None) &&
369 ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
378 if (ImGui::IsKeyPressed(ImGuiKey_Escape)) {
384 int card_count =
static_cast<int>(cards.size());
385 if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) ||
386 ImGui::IsKeyPressed(ImGuiKey_J)) {
390 if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) ||
391 ImGui::IsKeyPressed(ImGuiKey_K)) {
395 if (ImGui::IsKeyPressed(ImGuiKey_Home)) {
398 if (ImGui::IsKeyPressed(ImGuiKey_End)) {
404 if (ImGui::IsKeyPressed(ImGuiKey_Enter) ||
405 ImGui::IsKeyPressed(ImGuiKey_Space)) {
413 const std::string& card_id) {
418 size_t session_id,
const std::string& category)
const {
422 panels.begin(), panels.end(),
424 bool a_pinned = IsWindowPinnedImpl(session_id, a.card_id);
425 bool b_pinned = IsWindowPinnedImpl(session_id, b.card_id);
426 if (a_pinned != b_pinned) {
427 return a_pinned > b_pinned;
432 uint64_t a_time = (a_it !=
last_used_at_.end()) ? a_it->second : 0;
433 uint64_t b_time = (b_it !=
last_used_at_.end()) ? b_it->second : 0;
434 if (a_time != b_time) {
435 return a_time > b_time;
444size_t WorkspaceWindowManager::GetVisibleWindowCount(
size_t session_id)
const {
446 const auto* session_windows = FindSessionWindowIds(session_id);
447 if (!session_windows) {
450 for (
const auto& prefixed_card_id : *session_windows) {
451 if (
const auto* descriptor = FindDescriptorByPrefixedId(prefixed_card_id)) {
452 if (descriptor->visibility_flag && *descriptor->visibility_flag) {
460void WorkspaceWindowManager::UpdateSessionCount() {
461 session_count_ = session_cards_.size();
464std::string WorkspaceWindowManager::GetPrefixedWindowId(
465 size_t session_id,
const std::string& base_id)
const {
466 const std::string resolved_base_id = ResolveBaseWindowId(base_id);
468 const auto* session_mapping = FindSessionWindowMapping(session_id);
469 if (session_mapping) {
470 auto card_it = session_mapping->find(resolved_base_id);
471 if (card_it != session_mapping->end()) {
472 return card_it->second;
476 if (cards_.find(resolved_base_id) != cards_.end()) {
477 return resolved_base_id;
483void WorkspaceWindowManager::RegisterPanelDescriptorForSession(
485 RegisterSession(session_id);
486 std::string panel_id =
488 bool already_registered = (cards_.find(panel_id) != cards_.end());
490 RegisterPanel(session_id, descriptor);
492 OpenWindowImpl(session_id, panel.
GetId());
496void WorkspaceWindowManager::TrackPanelForSession(
size_t session_id,
497 const std::string& base_id,
498 const std::string& panel_id) {
499 const std::string canonical_base_id = ResolveBaseWindowId(base_id);
501 auto& card_list = session_cards_[session_id];
502 if (std::find(card_list.begin(), card_list.end(), panel_id) ==
504 card_list.push_back(panel_id);
506 session_card_mapping_[session_id][canonical_base_id] = panel_id;
507 session_reverse_card_mapping_[session_id][panel_id] = canonical_base_id;
513 auto pending_it = pending_pinned_base_ids_.find(canonical_base_id);
514 if (pending_it != pending_pinned_base_ids_.end()) {
515 pinned_panels_[panel_id] = pending_it->second;
516 pending_pinned_base_ids_.erase(pending_it);
520void WorkspaceWindowManager::UnregisterSessionPanels(
size_t session_id) {
521 const auto* session_windows = FindSessionWindowIds(session_id);
522 if (!session_windows) {
526 for (
const auto& prefixed_card_id : *session_windows) {
527 if (global_panel_ids_.find(prefixed_card_id) != global_panel_ids_.end()) {
530 const std::string base_card_id =
531 GetBaseIdForPrefixedId(session_id, prefixed_card_id);
534 if (registry_panel_ids_.find(base_card_id) == registry_panel_ids_.end()) {
535 if (
auto instance_it = panel_instances_.find(prefixed_card_id);
536 instance_it != panel_instances_.end()) {
537 instance_it->second->OnClose();
539 UntrackResourceWindow(prefixed_card_id);
540 panel_instances_.erase(instance_it);
543 if (!base_card_id.empty()) {
544 RememberPinnedStateForRemovedWindow(session_id, base_card_id,
547 pending_float_window_ids_.erase(prefixed_card_id);
548 cards_.erase(prefixed_card_id);
549 centralized_visibility_.erase(prefixed_card_id);
550 pinned_panels_.erase(prefixed_card_id);
Base interface for all logical window content components.
virtual std::string GetWorkflowDescription() const
Optional workflow description for menus/command palette.
virtual std::string GetDisabledTooltip() const
Get tooltip text when panel is disabled.
virtual int GetWorkflowPriority() const
Optional workflow ordering priority (lower sorts first).
virtual bool IsVisibleByDefault() const
Whether this panel should be visible by default.
virtual std::string GetDisplayName() const =0
Human-readable name shown in menus and title bars.
virtual WindowLifecycle GetWindowLifecycle() const
Get the lifecycle category for this window.
virtual std::string GetEditorCategory() const =0
Editor category this panel belongs to.
virtual WindowPresentationPolicy GetPresentationPolicy() const
How this content is hosted and advertised by workspace chrome.
virtual int GetPriority() const
Get display priority for menu ordering.
virtual WindowScope GetScope() const
Get the registration scope for this window.
virtual std::string GetIcon() const =0
Material Design icon for this panel.
virtual WindowContextScope GetContextScope() const
Optional context binding for this window (room/selection/etc)
virtual std::string GetWorkflowLabel() const
Optional workflow label for menus/command palette.
virtual std::string GetId() const =0
Unique identifier for this panel.
virtual std::string GetWorkflowGroup() const
Optional workflow group for hack-centric actions.
virtual std::string GetShortcutHint() const
Get keyboard shortcut hint for display.
std::unordered_set< std::string > & pending_float_window_ids_
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_
void ClearAnimationsForPanel(const std::string &panel_id)
#define LOG_WARN(category, format,...)
#define LOG_INFO(category, format,...)
WindowContextScope
Optional context binding for a window's behavior within an editor.
bool IsTransientPanelVisibilityId(absl::string_view panel_id)
Metadata for a dockable editor window (formerly PanelInfo)
std::string shortcut_hint
std::string workflow_group
WindowContextScope context_scope
std::string disabled_tooltip
std::string workflow_description
WindowPresentationPolicy presentation
std::string workflow_label
std::string GetImGuiWindowName() const
Build the exact ImGui window name used by PanelWindow::Begin.
std::function< bool()> enabled_condition
WindowLifecycle window_lifecycle
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