62 const std::vector<std::string>& input,
63 const std::vector<std::string>& order,
64 const std::unordered_set<std::string>& pinned,
65 const std::unordered_set<std::string>& hidden) {
67 std::vector<std::string> visible;
68 visible.reserve(input.size());
69 std::unordered_set<std::string> visible_set;
70 visible_set.reserve(input.size());
71 for (
const auto& c : input) {
75 visible_set.insert(c);
79 std::vector<std::string> pinned_visible;
80 std::unordered_set<std::string> pinned_visible_set;
81 for (
const auto& c : input) {
82 if (visible_set.count(c) && pinned.count(c)) {
83 pinned_visible.push_back(c);
84 pinned_visible_set.insert(c);
91 std::vector<std::string> ordered;
92 std::vector<std::string> newcomers;
95 for (
const auto& c : visible) {
96 if (pinned_visible_set.count(c))
101 std::unordered_set<std::string> ordered_set;
102 for (
const auto& c : order) {
103 if (!visible_set.count(c))
105 if (pinned_visible_set.count(c))
107 ordered.push_back(c);
108 ordered_set.insert(c);
110 std::unordered_set<std::string> order_set(order.begin(), order.end());
111 for (
const auto& c : visible) {
112 if (pinned_visible_set.count(c))
114 if (order_set.count(c))
116 newcomers.push_back(c);
118 std::sort(newcomers.begin(), newcomers.end());
121 std::vector<std::string> result;
122 result.reserve(pinned_visible.size() + ordered.size() + newcomers.size());
123 result.insert(result.end(), pinned_visible.begin(), pinned_visible.end());
124 result.insert(result.end(), ordered.begin(), ordered.end());
125 result.insert(result.end(), newcomers.begin(), newcomers.end());
247 size_t session_id,
const std::string& active_category,
248 const std::vector<std::string>& all_categories,
249 const std::unordered_set<std::string>& active_editor_categories,
250 std::function<
bool()> has_rom, std::function<
bool()> is_rom_dirty,
251 std::function<
int()> pending_dungeon_rooms) {
254 const ImGuiViewport* viewport = ImGui::GetMainViewport();
258 const float viewport_height =
259 std::max(0.0f, viewport->WorkSize.y - top_inset - safe_area.bottom -
263 constexpr ImGuiWindowFlags kExtraFlags =
264 ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoFocusOnAppearing |
265 ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoBringToFrontOnFocus;
269 ImVec2(viewport->WorkPos.x, viewport->WorkPos.y + top_inset),
270 ImVec2(bar_width, viewport_height),
273 .padding = {0.0f, 8.0f},
274 .spacing = {0.0f, 8.0f},
275 .border_size = 1.0f},
282 "Global Search (Ctrl+Shift+F)",
false,
283 ImVec4(0, 0, 0, 0),
"activity_bar",
290 ImVec2 sep_p1 = ImGui::GetCursorScreenPos();
293 ImGui::GetWindowDrawList()->AddLine(
299 bool rom_loaded = has_rom ? has_rom() :
false;
303 std::vector<std::string> filtered_input;
304 filtered_input.reserve(all_categories.size());
305 for (
const auto& cat : all_categories) {
308 filtered_input.push_back(cat);
311 std::vector<std::string> effective = filtered_input;
315 prefs.sidebar_pinned, prefs.sidebar_hidden);
319 if (effective.empty() && !filtered_input.empty()) {
322 effective = filtered_input;
327 for (
const auto& cat : effective) {
328 bool is_selected = (cat == active_category);
330 bool has_active_editor = active_editor_categories.count(cat) > 0;
333 bool category_enabled =
334 rom_loaded || (cat ==
"Emulator") || (cat ==
"Agent");
338 ImVec4 cat_color(cat_theme.r, cat_theme.g, cat_theme.b, cat_theme.a);
339 ImVec4 glow_color(cat_theme.glow_r, cat_theme.glow_g, cat_theme.glow_b,
343 if (is_selected && category_enabled && panel_expanded) {
344 ImVec2 pos = ImGui::GetCursorScreenPos();
347 ImVec4 outer_glow = glow_color;
348 outer_glow.w = 0.15f;
349 ImGui::GetWindowDrawList()->AddRectFilled(
350 ImVec2(pos.x - 1.0f, pos.y - 1.0f),
351 ImVec2(pos.x + 49.0f, pos.y + 41.0f),
352 ImGui::ColorConvertFloat4ToU32(outer_glow), 4.0f);
355 ImVec4 highlight = glow_color;
357 ImGui::GetWindowDrawList()->AddRectFilled(
358 pos, ImVec2(pos.x + 48.0f, pos.y + 40.0f),
359 ImGui::ColorConvertFloat4ToU32(highlight), 2.0f);
362 ImGui::GetWindowDrawList()->AddRectFilled(
363 pos, ImVec2(pos.x + 4.0f, pos.y + 40.0f),
364 ImGui::ColorConvertFloat4ToU32(cat_color));
370 if (is_selected && category_enabled && !panel_expanded) {
371 ImVec2 pos = ImGui::GetCursorScreenPos();
372 ImVec4 highlight = glow_color;
374 ImGui::GetWindowDrawList()->AddRectFilled(
375 pos, ImVec2(pos.x + 48.0f, pos.y + 40.0f),
376 ImGui::ColorConvertFloat4ToU32(highlight), 2.0f);
377 ImVec4 accent = cat_color;
379 ImGui::GetWindowDrawList()->AddRectFilled(
380 pos, ImVec2(pos.x + 3.0f, pos.y + 40.0f),
381 ImGui::ColorConvertFloat4ToU32(accent));
387 if (!is_selected && category_enabled && has_active_editor) {
388 ImVec2 pos = ImGui::GetCursorScreenPos();
389 ImVec4 dim_accent = cat_color;
390 dim_accent.w = 0.35f;
391 ImGui::GetWindowDrawList()->AddRectFilled(
392 ImVec2(pos.x + 45.0f, pos.y + 8.0f),
393 ImVec2(pos.x + 48.0f, pos.y + 32.0f),
394 ImGui::ColorConvertFloat4ToU32(dim_accent), 1.5f);
402 ImVec4 icon_color = cat_color;
403 if (!category_enabled) {
404 ImGui::BeginDisabled();
407 nullptr, is_selected, icon_color,
408 "activity_bar", cat.c_str())) {
409 if (category_enabled) {
410 if (cat == active_category && panel_expanded) {
420 if (!category_enabled) {
421 ImGui::EndDisabled();
429 ImVec2 pin_min = ImGui::GetItemRectMin();
430 ImVec4 pin_color = cat_color;
432 ImGui::GetWindowDrawList()->AddCircleFilled(
433 ImVec2(pin_min.x + 6.0f, pin_min.y + 6.0f), 2.5f,
434 ImGui::ColorConvertFloat4ToU32(pin_color));
437 const int pending_rooms =
438 pending_dungeon_rooms ? pending_dungeon_rooms() : 0;
439 const bool dungeon_pending = cat ==
"Dungeon" && pending_rooms > 0;
443 const bool rom_dirty = is_rom_dirty ? is_rom_dirty() :
false;
444 if (is_selected && category_enabled && rom_dirty) {
445 ImVec2 last_min = ImGui::GetItemRectMin();
446 ImVec2 last_max = ImGui::GetItemRectMax();
447 ImVec2 dot_center(last_max.x - 7.0f, last_min.y + 7.0f);
449 ImGui::GetWindowDrawList()->AddCircleFilled(
450 dot_center, 3.5f, ImGui::ColorConvertFloat4ToU32(dot_color));
453 if (category_enabled && dungeon_pending) {
454 ImVec2 last_min = ImGui::GetItemRectMin();
455 ImVec2 pending_center(last_min.x + 7.0f, last_min.y + 7.0f);
457 ImGui::GetWindowDrawList()->AddCircleFilled(
458 pending_center, 3.0f,
459 ImGui::ColorConvertFloat4ToU32(pending_color));
463 if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
464 ImGui::BeginTooltip();
465 ImGui::Text(
"%s %s", icon.c_str(), cat.c_str());
466 if (!category_enabled) {
469 }
else if (has_active_editor) {
471 is_selected ?
"Active editor" :
"Editor open (click to focus)",
484 if (is_selected && rom_dirty) {
488 if (dungeon_pending) {
490 "%d dungeon room%s pending apply", pending_rooms,
491 pending_rooms == 1 ?
"" :
"s");
499 ImGui::SetCursorPosY(viewport_height - 48.0f);
502 nullptr,
false, ImVec4(0, 0, 0, 0),
503 "activity_bar",
"more_actions")) {
504 ImGui::OpenPopup(
"ActivityBarMoreMenu");
507 if (ImGui::BeginPopup(
"ActivityBarMoreMenu")) {
511 if (action.
icon !=
nullptr) {
512 label = absl::StrFormat(
"%s %s", action.icon, action.label);
514 label = action.label;
517 if (ImGui::MenuItem(label.c_str(),
nullptr,
524 ImGui::TextDisabled(tr(
"No actions available"));