yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
window_sidebar.cc
Go to the documentation of this file.
2#include "util/i18n/tr.h"
3
4#include <algorithm>
5#include <cctype>
6#include <map>
7#include <string>
8#include <vector>
9
10#include "absl/strings/str_format.h"
12#include "app/gui/core/icons.h"
19#include "core/color.h"
20#include "imgui/imgui.h"
21
22namespace yaze {
23namespace editor {
24
25namespace {
26
27std::string LowercaseCopy(std::string value) {
28 std::transform(
29 value.begin(), value.end(), value.begin(),
30 [](unsigned char c) { return static_cast<char>(::tolower(c)); });
31 return value;
32}
33
34bool MatchesWindowSearch(const std::string& query,
35 const WindowDescriptor& window) {
37 query, window.display_name, window.card_id, window.shortcut_hint);
38}
39
40bool IsDungeonRoomWindow(const std::string& window_id) {
41 constexpr char kPrefix[] = "dungeon.room_";
42 constexpr size_t kPrefixLength = sizeof(kPrefix) - 1;
43 if (window_id.rfind(kPrefix, 0) != 0 || window_id.size() <= kPrefixLength) {
44 return false;
45 }
46 // DungeonEditorV2 registers dynamic room windows with a decimal room number.
47 // Other room_* IDs, such as room_graphics and room_tags, are standalone tools.
48 return std::all_of(window_id.begin() + kPrefixLength, window_id.end(),
49 [](char ch) { return ch >= '0' && ch <= '9'; });
50}
51
52bool IsDungeonPanelModeWindow(const std::string& window_id) {
54}
55
56} // namespace
57
59 WorkspaceWindowManager& window_manager,
60 std::function<bool()> is_dungeon_workbench_mode,
61 std::function<void(bool)> set_dungeon_workflow_mode,
62 std::function<float()> get_bottom_reserved_height)
63 : window_manager_(window_manager),
64 is_dungeon_workbench_mode_(std::move(is_dungeon_workbench_mode)),
65 set_dungeon_workflow_mode_(std::move(set_dungeon_workflow_mode)),
66 get_bottom_reserved_height_(std::move(get_bottom_reserved_height)) {}
67
68bool WindowSidebar::MatchesWindowSearch(const std::string& query,
69 const std::string& display_name,
70 const std::string& window_id,
71 const std::string& shortcut_hint) {
72 if (query.empty()) {
73 return true;
74 }
75
76 const std::string search_str = LowercaseCopy(query);
77 return LowercaseCopy(display_name).find(search_str) != std::string::npos ||
78 LowercaseCopy(window_id).find(search_str) != std::string::npos ||
79 LowercaseCopy(shortcut_hint).find(search_str) != std::string::npos;
80}
81
82bool WindowSidebar::IsDungeonWindowModeTarget(const std::string& window_id) {
83 return window_id == "dungeon.room_selector" ||
84 window_id == "dungeon.room_matrix" || IsDungeonRoomWindow(window_id);
85}
86
88 const std::string& workflow_group) {
89 if (workflow_group.empty() || workflow_group == "Windows") {
90 return "Editors";
91 }
92 return workflow_group;
93}
94
96 if (!window.workflow_group.empty() && window.workflow_group != "Windows") {
97 return window.workflow_group;
98 }
99 // Dynamic per-room windows may omit workflow_group at registration time.
100 if (IsDungeonRoomWindow(window.card_id)) {
101 return "Rooms";
102 }
103 return SidebarSectionFor(window.workflow_group);
104}
105
106bool WindowSidebar::ShouldOmitWindowInSidebar(const std::string& window_id,
107 bool dungeon_workbench_mode) {
108 return dungeon_workbench_mode && IsDungeonWindowModeTarget(window_id);
109}
110
111void WindowSidebar::Draw(size_t session_id, const std::string& category,
112 std::function<bool()> has_rom) {
113 const auto& theme = gui::ThemeManager::Get().GetCurrentTheme();
114 const ImGuiViewport* viewport = ImGui::GetMainViewport();
115 const float bar_width = WorkspaceWindowManager::GetSidebarWidth();
116 const float panel_width =
117 window_manager_.GetActiveSidePanelWidth(viewport->WorkSize.x);
118
119 const float top_inset = gui::LayoutHelpers::GetTopInset();
120 const auto safe_area = gui::LayoutHelpers::GetSafeAreaInsets();
121 const float bottom_reserved =
123 ? std::max(0.0f, get_bottom_reserved_height_())
124 : 0.0f;
125 const float panel_height =
126 std::max(0.0f, viewport->WorkSize.y - top_inset - safe_area.bottom -
127 bottom_reserved);
128
129 gui::FixedPanel panel(
130 "##SidePanel",
131 ImVec2(viewport->WorkPos.x + bar_width, viewport->WorkPos.y + top_inset),
132 ImVec2(panel_width, panel_height),
133 {.bg = gui::ConvertColorToImVec4(theme.surface),
134 .padding = {8.0f, 8.0f},
135 .border_size = 1.0f,
136 .rounding = 0.0f},
137 ImGuiWindowFlags_NoFocusOnAppearing);
138
139 if (!panel) {
140 return;
141 }
142
143 ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[0]);
144 ImGui::Text("%s", category.c_str());
145 ImGui::PopFont();
146
147 float avail_width = ImGui::GetContentRegionAvail().x;
148 const ImVec2 chrome_icon_size = gui::IconSize::Small();
149 ImGui::SameLine(ImGui::GetCursorPosX() + avail_width - chrome_icon_size.x);
151 "Collapse Sidebar", false, ImVec4(0, 0, 0, 0),
152 "window_sidebar", "collapse_side_panel")) {
154 }
155
156 ImGui::Separator();
157 ImGui::Spacing();
158
159 const ImVec2 search_button_size = gui::IconSize::Small();
160 const float standard_spacing =
161 std::clamp(gui::LayoutHelpers::GetStandardSpacing(), 4.0f, 8.0f);
162 const float compact_spacing = std::max(4.0f, standard_spacing * 0.75f);
163 const float search_spacing = compact_spacing;
164
165 auto read_dungeon_workbench_mode = [&]() -> bool {
166 if (category != "Dungeon") {
167 return false;
168 }
171 }
172 return window_manager_.IsWindowOpen(session_id, "dungeon.workbench");
173 };
174 bool dungeon_workbench_mode = read_dungeon_workbench_mode();
175 const bool rom_loaded = has_rom ? has_rom() : true;
176 const bool disable_windows = !rom_loaded && category != "Emulator";
177 const bool bulk_actions_enabled =
178 !disable_windows && !(category == "Dungeon" && dungeon_workbench_mode);
179
180 const float search_width = std::max(96.0f, ImGui::GetContentRegionAvail().x -
181 (search_button_size.x * 2.0f) -
182 (search_spacing * 2.0f));
183 ImGui::SetNextItemWidth(search_width);
184 ImGui::InputTextWithHint("##SidebarSearch", ICON_MD_SEARCH " Filter...",
186 ImGui::SameLine(0.0f, search_spacing);
187 const bool filter_empty = sidebar_search_[0] == '\0';
188 if (filter_empty) {
189 ImGui::BeginDisabled();
190 }
191 if (gui::TransparentIconButton(ICON_MD_CLEAR, search_button_size,
192 "Clear filter", false,
193 gui::GetTextSecondaryVec4(), "window_sidebar",
194 "clear_sidebar_filter")) {
195 sidebar_search_[0] = '\0';
196 }
197 if (filter_empty) {
198 ImGui::EndDisabled();
199 }
200 ImGui::SameLine(0.0f, search_spacing);
201 if (gui::TransparentIconButton(ICON_MD_MORE_HORIZ, search_button_size,
202 "Window actions", false,
203 gui::GetTextSecondaryVec4(), "window_sidebar",
204 "open_sidebar_actions")) {
205 ImGui::OpenPopup("##WindowSidebarActions");
206 }
207 if (ImGui::BeginPopup("##WindowSidebarActions")) {
208 const auto category_windows =
209 window_manager_.GetWindowsInCategory(session_id, category);
210 int visible_windows = 0;
211 for (const auto& category_window : category_windows) {
212 if (category_window.visibility_flag && *category_window.visibility_flag) {
213 ++visible_windows;
214 }
215 }
216 ImGui::TextDisabled(tr("%d of %zu visible"), visible_windows,
217 category_windows.size());
218 ImGui::Separator();
219 if (ImGui::MenuItem(ICON_MD_APPS " Window Browser")) {
221 }
222 ImGui::Separator();
223 if (ImGui::MenuItem(ICON_MD_VISIBILITY " Show all", nullptr, false,
224 bulk_actions_enabled)) {
225 window_manager_.ShowAllWindowsInCategory(session_id, category);
226 }
227 const bool show_all_hovered =
228 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);
229 if (ImGui::MenuItem(ICON_MD_VISIBILITY_OFF " Hide all", nullptr, false,
230 bulk_actions_enabled)) {
231 window_manager_.HideAllWindowsInCategory(session_id, category);
232 }
233 const bool hide_all_hovered =
234 ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled);
235 if (!bulk_actions_enabled && (show_all_hovered || hide_all_hovered) &&
236 category == "Dungeon" && dungeon_workbench_mode) {
237 ImGui::SetTooltip(
238 tr("Switch to Window workflow to bulk-manage room windows."));
239 }
240 ImGui::EndPopup();
241 }
242
243 auto switch_to_dungeon_workbench_mode = [&]() -> bool {
244 if (category != "Dungeon" || dungeon_workbench_mode) {
245 return false;
246 }
249 dungeon_workbench_mode = true;
250 return true;
251 }
252 window_manager_.OpenWindow(session_id, "dungeon.workbench");
253 // Workbench-local tool windows (Object/Door/Sprite/Item Selector, Palette,
254 // Room Graphics/Tags, Custom Collision, Water Fill, Minecart) are NOT
255 // auto-closed here. Users can keep a standalone copy open alongside the
256 // Workbench drawer's embedded copy if they prefer that layout.
257 for (const auto& descriptor :
258 window_manager_.GetWindowsInCategory(session_id, "Dungeon")) {
259 const std::string& window_id = descriptor.card_id;
260 if (window_id == "dungeon.workbench") {
261 continue;
262 }
263 if (window_manager_.IsWindowPinned(session_id, window_id)) {
264 continue;
265 }
266 if (IsDungeonPanelModeWindow(window_id)) {
267 window_manager_.CloseWindow(session_id, window_id);
268 }
269 }
270 dungeon_workbench_mode = read_dungeon_workbench_mode();
271 return dungeon_workbench_mode;
272 };
273
274 auto switch_to_dungeon_window_mode = [&]() -> bool {
275 if (category != "Dungeon" || !dungeon_workbench_mode) {
276 return false;
277 }
280 dungeon_workbench_mode = false;
281 return true;
282 }
283 window_manager_.CloseWindow(session_id, "dungeon.workbench");
284 window_manager_.OpenWindow(session_id, "dungeon.room_selector");
285 window_manager_.OpenWindow(session_id, "dungeon.room_matrix");
286 dungeon_workbench_mode = read_dungeon_workbench_mode();
287 return !dungeon_workbench_mode;
288 };
289
290 auto ensure_dungeon_window_mode_for_window =
291 [&](const std::string& window_id) -> bool {
292 if (category != "Dungeon" || !dungeon_workbench_mode ||
293 !IsDungeonPanelModeWindow(window_id)) {
294 return false;
295 }
296 return switch_to_dungeon_window_mode();
297 };
298
299 if (category == "Dungeon") {
300 const float workflow_gap = compact_spacing;
301 const float workflow_min_button_width = 96.0f;
302 const float workflow_available_width =
303 std::max(1.0f, ImGui::GetContentRegionAvail().x);
304 const bool stack_workflow_buttons =
305 workflow_available_width <=
306 (workflow_min_button_width * 2.0f + workflow_gap);
307 const float workflow_button_width =
308 stack_workflow_buttons
309 ? workflow_available_width
310 : std::max(workflow_min_button_width,
311 (workflow_available_width - workflow_gap) * 0.5f);
312 const float workflow_button_height =
314 auto draw_workflow_button =
315 [&](const char* id, const char* icon, const char* label, bool active,
316 const char* tooltip, const ImVec2& button_size) -> bool {
317 const ImVec4 active_bg = gui::GetPrimaryVec4();
318 const ImVec4 inactive_bg = gui::GetSurfaceContainerHighVec4();
319 const ImVec4 inactive_hover = gui::GetSurfaceContainerHighestVec4();
321 {{ImGuiCol_Button, active ? active_bg : inactive_bg},
322 {ImGuiCol_ButtonHovered, active ? active_bg : inactive_hover},
323 {ImGuiCol_ButtonActive, active ? active_bg : inactive_hover}});
324 const std::string button_label =
325 absl::StrFormat("%s %s##%s", icon, label, id);
326 const bool clicked = ImGui::Button(button_label.c_str(), button_size);
327 if (ImGui::IsItemHovered() && tooltip && *tooltip) {
328 ImGui::SetTooltip("%s", tooltip);
329 }
330 return clicked;
331 };
332 const ImVec2 workflow_button_size(workflow_button_width,
333 workflow_button_height);
334
335 if (draw_workflow_button(
336 "workflow_workbench", ICON_MD_WORKSPACES, "Workbench",
337 dungeon_workbench_mode,
338 "Workbench mode: integrated room browser + inspector",
339 workflow_button_size)) {
340 switch_to_dungeon_workbench_mode();
341 }
342 if (!stack_workflow_buttons) {
343 ImGui::SameLine(0.0f, workflow_gap);
344 }
345 if (draw_workflow_button(
346 "workflow_windows", ICON_MD_VIEW_QUILT, "Windows",
347 !dungeon_workbench_mode,
348 "Window mode: standalone Room List + Room Matrix + room windows",
349 workflow_button_size)) {
350 switch_to_dungeon_window_mode();
351 }
352 ImGui::Separator();
353 }
354
355 ImGui::Spacing();
356
357 if (disable_windows) {
358 ImGui::TextUnformatted(ICON_MD_FOLDER_OPEN
359 " Open a ROM to enable this category");
360 ImGui::Spacing();
361 }
362
363 if (disable_windows) {
364 ImGui::BeginDisabled();
365 }
366
367 const auto pinned_windows = window_manager_.GetPinnedWindows();
368 const ImVec4 disabled_text = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
369 auto window_text_color = [&](bool visible) -> ImVec4 {
370 if (disable_windows) {
371 return disabled_text;
372 }
374 };
375 const float pin_button_side =
377 const ImVec2 pin_button_size(pin_button_side, pin_button_side);
378 auto draw_pin_toggle_button = [&](const std::string& widget_id,
379 bool pinned) -> bool {
380 ImGui::PushID(widget_id.c_str());
381 const ImVec4 pin_col = pinned ? gui::ConvertColorToImVec4(theme.primary)
383 const bool clicked = gui::TransparentIconButton(
384 pinned ? ICON_MD_PUSH_PIN : ICON_MD_PIN, pin_button_size,
385 pinned ? "Unpin window" : "Pin window", pinned, pin_col,
386 "window_sidebar", widget_id.c_str());
387 ImGui::PopID();
388 return clicked;
389 };
390
391 bool pinned_section_open = false;
392 if (sidebar_search_[0] == '\0' && !pinned_windows.empty()) {
393 bool has_pinned_in_category = false;
394 for (const auto& window_id : pinned_windows) {
395 const auto* window =
396 window_manager_.GetWindowDescriptor(session_id, window_id);
397 if (window && window->category == category) {
398 has_pinned_in_category = true;
399 break;
400 }
401 }
402
403 if (has_pinned_in_category) {
404 pinned_section_open = ImGui::CollapsingHeader(
405 ICON_MD_PUSH_PIN " Pinned Windows", ImGuiTreeNodeFlags_DefaultOpen);
406 if (pinned_section_open) {
407 for (const auto& window_id : pinned_windows) {
408 const auto* window =
409 window_manager_.GetWindowDescriptor(session_id, window_id);
410 if (!window || window->category != category) {
411 continue;
412 }
413
414 const bool visible =
415 window->visibility_flag ? *window->visibility_flag : false;
416
417 if (draw_pin_toggle_button("pin_" + window->card_id, true)) {
418 window_manager_.SetWindowPinned(session_id, window->card_id, false);
419 }
420
421 ImGui::SameLine(0.0f, compact_spacing);
422
423 std::string label = absl::StrFormat("%s %s", window->icon.c_str(),
424 window->display_name.c_str());
425 ImGui::PushID(
426 (std::string("pinned_select_") + window->card_id).c_str());
427 {
428 gui::StyleColorGuard text_color(ImGuiCol_Text,
429 window_text_color(visible));
430 ImVec2 item_size(ImGui::GetContentRegionAvail().x,
431 pin_button_size.y);
432 if (ImGui::Selectable(label.c_str(), visible,
433 ImGuiSelectableFlags_None, item_size)) {
434 const bool switched_mode =
435 ensure_dungeon_window_mode_for_window(window->card_id);
436 if (switched_mode) {
437 window_manager_.OpenWindow(session_id, window->card_id);
438 } else {
439 window_manager_.ToggleWindow(session_id, window->card_id);
440 }
441
442 const bool new_visible =
443 window->visibility_flag ? *window->visibility_flag : false;
444 if (new_visible) {
445 window_manager_.TriggerWindowClicked(window->category);
446 const std::string window_name =
448 if (!window_name.empty()) {
449 ImGui::SetWindowFocus(window_name.c_str());
450 }
451 }
452 }
453 }
454 ImGui::PopID();
455 }
456 ImGui::Spacing();
457 ImGui::Separator();
458 ImGui::Spacing();
459 }
460 }
461 }
462
463 auto windows = window_manager_.GetWindowsSortedByMRU(session_id, category);
464 const bool filtering = sidebar_search_[0] != '\0';
465
466 // Bucket windows into sidebar sections (Core / Editors / Rooms / Advanced /
467 // other). Pinned rows that already appear in the Pinned header are skipped.
468 std::map<std::string, std::vector<WindowDescriptor>> sections;
469 for (const auto& window : windows) {
470 if (ShouldOmitWindowInSidebar(window.card_id, dungeon_workbench_mode)) {
471 continue;
472 }
473 if (!MatchesWindowSearch(sidebar_search_, window.display_name,
474 window.card_id, window.shortcut_hint)) {
475 continue;
476 }
477 const bool is_pinned = window_manager_.IsWindowPinned(window.card_id);
478 if (pinned_section_open && is_pinned) {
479 continue;
480 }
481 sections[SidebarSectionFor(window)].push_back(window);
482 }
483
484 // Within each section: priority ascending, then display name.
485 for (auto& [section_name, section_windows] : sections) {
486 (void)section_name;
487 std::sort(section_windows.begin(), section_windows.end(),
488 [](const WindowDescriptor& a, const WindowDescriptor& b) {
489 if (a.priority != b.priority) {
490 return a.priority < b.priority;
491 }
492 return a.display_name < b.display_name;
493 });
494 }
495
496 constexpr const char* kPreferredSectionOrder[] = {
497 "Core",
498 "Editors",
499 "Rooms",
500 "Advanced",
501 };
502 std::vector<std::string> section_order;
503 for (const char* preferred : kPreferredSectionOrder) {
504 if (sections.count(preferred)) {
505 section_order.emplace_back(preferred);
506 }
507 }
508 for (const auto& [name, _] : sections) {
509 if (std::find(section_order.begin(), section_order.end(), name) ==
510 section_order.end()) {
511 section_order.push_back(name);
512 }
513 }
514
515 auto draw_window_row = [&](const WindowDescriptor& window) {
516 const bool is_pinned = window_manager_.IsWindowPinned(window.card_id);
517 const bool visible =
518 window.visibility_flag ? *window.visibility_flag : false;
519
520 if (draw_pin_toggle_button("pin_" + window.card_id, is_pinned)) {
521 window_manager_.SetWindowPinned(session_id, window.card_id, !is_pinned);
522 }
523 ImGui::SameLine(0.0f, compact_spacing);
524
525 std::string label = absl::StrFormat("%s %s", window.icon.c_str(),
526 window.display_name.c_str());
527 ImGui::PushID((std::string("window_select_") + window.card_id).c_str());
528 {
529 gui::StyleColorGuard text_color(ImGuiCol_Text,
530 window_text_color(visible));
531 ImVec2 item_size(ImGui::GetContentRegionAvail().x, pin_button_size.y);
532 if (ImGui::Selectable(label.c_str(), visible, ImGuiSelectableFlags_None,
533 item_size)) {
534 const bool switched_mode =
535 ensure_dungeon_window_mode_for_window(window.card_id);
536 if (switched_mode) {
537 window_manager_.OpenWindow(session_id, window.card_id);
538 } else {
539 window_manager_.ToggleWindow(session_id, window.card_id);
540 }
541
542 const bool new_visible =
543 window.visibility_flag ? *window.visibility_flag : false;
544 if (new_visible) {
545 window_manager_.MarkWindowRecentlyUsed(window.card_id);
546 window_manager_.TriggerWindowClicked(window.category);
547 const std::string window_name =
548 window_manager_.GetWorkspaceWindowName(window);
549 if (!window_name.empty()) {
550 ImGui::SetWindowFocus(window_name.c_str());
551 }
552 }
553 }
554 }
555 ImGui::PopID();
556
557 if (ImGui::IsItemHovered() && !window.shortcut_hint.empty()) {
558 ImGui::SetTooltip("%s", window.shortcut_hint.c_str());
559 }
560 };
561
562 const bool window_content_open = gui::LayoutHelpers::BeginContentChild(
563 "##WindowContent", ImVec2(0.0f, gui::UIConfig::kContentMinHeightList));
564 if (window_content_open) {
565 for (const std::string& section_name : section_order) {
566 auto it = sections.find(section_name);
567 if (it == sections.end() || it->second.empty()) {
568 continue;
569 }
570
571 // Search results are flat so a previously collapsed section cannot hide
572 // a match. Do not change the ordinary section's saved expansion state.
573 if (filtering) {
574 for (const auto& window : it->second) {
575 draw_window_row(window);
576 }
577 continue;
578 }
579
580 ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_None;
581 if (section_name == "Core") {
582 flags |= ImGuiTreeNodeFlags_DefaultOpen;
583 }
584
585 const std::string header = absl::StrFormat(
586 "%s##sidebar_section_%s", section_name.c_str(), section_name.c_str());
587 if (ImGui::CollapsingHeader(header.c_str(), flags)) {
588 for (const auto& window : it->second) {
589 draw_window_row(window);
590 }
591 }
592 }
593 }
595
596 if (disable_windows) {
597 ImGui::EndDisabled();
598 }
599
600 const float handle_width = 6.0f;
601 const ImVec2 panel_pos = ImGui::GetWindowPos();
602 const float panel_draw_height = ImGui::GetWindowHeight();
603 ImGui::SetCursorScreenPos(
604 ImVec2(panel_pos.x + panel_width - handle_width * 0.5f, panel_pos.y));
605 ImGui::InvisibleButton("##SidePanelResizeHandle",
606 ImVec2(handle_width, panel_draw_height));
607 const bool handle_hovered = ImGui::IsItemHovered();
608 const bool handle_active = ImGui::IsItemActive();
609 if (handle_hovered || handle_active) {
610 ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW);
611 }
612 if (handle_hovered && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) {
613 window_manager_.ResetSidePanelWidth();
614 }
615 if (handle_active) {
616 const float new_width = panel_width + ImGui::GetIO().MouseDelta.x;
617 window_manager_.SetActiveSidePanelWidth(new_width, viewport->WorkSize.x);
618 ImGui::SetTooltip(
619 tr("Width: %.0f px"),
620 window_manager_.GetActiveSidePanelWidth(viewport->WorkSize.x));
621 }
622
623 ImVec4 handle_color = gui::GetOutlineVec4();
624 handle_color.w = handle_active ? 0.95f : (handle_hovered ? 0.72f : 0.35f);
625 ImGui::GetWindowDrawList()->AddLine(
626 ImVec2(panel_pos.x + panel_width - 1.0f, panel_pos.y),
627 ImVec2(panel_pos.x + panel_width - 1.0f, panel_pos.y + panel_draw_height),
628 ImGui::GetColorU32(handle_color), handle_active ? 2.0f : 1.0f);
629}
630
631} // namespace editor
632} // namespace yaze
void Draw(size_t session_id, const std::string &category, std::function< bool()> has_rom)
std::function< void(bool)> set_dungeon_workflow_mode_
WindowSidebar(WorkspaceWindowManager &window_manager, std::function< bool()> is_dungeon_workbench_mode={}, std::function< void(bool)> set_dungeon_workflow_mode={}, std::function< float()> get_bottom_reserved_height={})
std::function< bool()> is_dungeon_workbench_mode_
static bool IsDungeonWindowModeTarget(const std::string &window_id)
WorkspaceWindowManager & window_manager_
std::function< float()> get_bottom_reserved_height_
static std::string SidebarSectionFor(const std::string &workflow_group)
static bool ShouldOmitWindowInSidebar(const std::string &window_id, bool dungeon_workbench_mode)
static bool MatchesWindowSearch(const std::string &query, const std::string &display_name, const std::string &window_id, const std::string &shortcut_hint)
Central registry for all editor cards with session awareness and dependency injection.
void HideAllWindowsInCategory(size_t session_id, const std::string &category)
std::string GetWorkspaceWindowName(size_t session_id, const std::string &base_window_id) const
Resolve the exact ImGui window name for a panel by base ID.
std::vector< WindowDescriptor > GetWindowsInCategory(size_t session_id, const std::string &category) const
const WindowDescriptor * GetWindowDescriptor(size_t session_id, const std::string &base_window_id) const
void SetWindowPinned(size_t session_id, const std::string &base_window_id, bool pinned)
bool CloseWindow(size_t session_id, const std::string &base_window_id)
void TriggerWindowClicked(const std::string &category)
std::vector< std::string > GetPinnedWindows(size_t session_id) const
bool IsWindowOpen(size_t session_id, const std::string &base_window_id) const
float GetActiveSidePanelWidth(float viewport_width) const
bool OpenWindow(size_t session_id, const std::string &base_window_id)
std::vector< WindowDescriptor > GetWindowsSortedByMRU(size_t session_id, const std::string &category) const
bool IsWindowPinned(size_t session_id, const std::string &base_window_id) const
bool ToggleWindow(size_t session_id, const std::string &base_window_id)
void SetSidebarExpanded(bool expanded, bool notify=true)
void ShowAllWindowsInCategory(size_t session_id, const std::string &category)
RAII for fixed-position panels (activity bar, side panel, status bar).
static bool BeginContentChild(const char *id, const ImVec2 &min_size, bool border=false, ImGuiWindowFlags flags=0)
static void EndContentChild()
static SafeAreaInsets GetSafeAreaInsets()
static float GetStandardWidgetHeight()
static float GetStandardSpacing()
RAII guard for ImGui style colors.
Definition style_guard.h:27
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
#define ICON_MD_FOLDER_OPEN
Definition icons.h:813
#define ICON_MD_VIEW_QUILT
Definition icons.h:2094
#define ICON_MD_APPS
Definition icons.h:168
#define ICON_MD_SEARCH
Definition icons.h:1673
#define ICON_MD_VISIBILITY
Definition icons.h:2101
#define ICON_MD_MORE_HORIZ
Definition icons.h:1241
#define ICON_MD_VISIBILITY_OFF
Definition icons.h:2102
#define ICON_MD_CHEVRON_LEFT
Definition icons.h:405
#define ICON_MD_PIN
Definition icons.h:1470
#define ICON_MD_CLEAR
Definition icons.h:416
#define ICON_MD_PUSH_PIN
Definition icons.h:1529
#define ICON_MD_WORKSPACES
Definition icons.h:2186
bool IsDungeonRoomWindow(const std::string &window_id)
bool IsDungeonPanelModeWindow(const std::string &window_id)
bool TransparentIconButton(const char *icon, const ImVec2 &size, const char *tooltip, bool is_active, const ImVec4 &active_color, const char *panel_id, const char *anim_id)
Draw a transparent icon button (hover effect only).
ImVec4 ConvertColorToImVec4(const Color &color)
Definition color.h:134
ImVec4 GetSurfaceContainerHighestVec4()
ImVec4 GetPrimaryVec4()
ImVec4 GetTextSecondaryVec4()
ImVec4 GetSurfaceContainerHighVec4()
ImVec4 GetOutlineVec4()
ImVec4 GetOnSurfaceVec4()
const char * tr(const char *source)
Definition translator.cc:50
Metadata for a dockable editor window (formerly PanelInfo)
static constexpr float kContentMinHeightList
Definition ui_config.h:54