feat(proc): add E hotkey to collapse/expand all tree nodes

Pressing E in tree mode collapses all processes if any parent is
currently expanded, or expands all if everything is already collapsed.
This gives a quick per-application resource summary without having
to manually toggle each node.

Implemented across all platforms (Linux, macOS, FreeBSD, OpenBSD, NetBSD).
This commit is contained in:
jam
2026-02-27 15:22:34 +09:00
committed by Jakob P. Liljenberg
parent 5cd645f482
commit f0cfe0980c
8 changed files with 87 additions and 6 deletions

View File

@@ -346,6 +346,11 @@ namespace Input {
no_update = false;
Config::set("update_following", true);
}
else if (key == "E" and Config::getB("proc_tree")) {
atomic_wait(Runner::active);
Proc::collapse_all = 1;
no_update = false;
}
else if (is_in(key, "u")) {
Config::flip("pause_proc_list");
}

View File

@@ -205,6 +205,7 @@ namespace Menu {
{"c", "Toggle per-core cpu usage of processes."},
{"r", "Reverse sorting order in processes box."},
{"e", "Toggle processes tree view."},
{"E", "Collapse/expand all processes in tree view."},
{"%", "Toggles memory display mode in processes box."},
{"Selected +, -", "Expand/collapse the selected process in tree view."},
{"Selected t", "Terminate selected process with SIGTERM - 15."},

View File

@@ -360,7 +360,7 @@ namespace Proc {
extern bool shown, redraw;
extern int select_max;
extern atomic<int> detailed_pid;
extern int selected_pid, start, selected, collapse, expand, filter_found, selected_depth, toggle_children;
extern int selected_pid, start, selected, collapse, expand, filter_found, selected_depth, toggle_children, collapse_all;
extern int scroll_pos;
extern string selected_name;
extern atomic<bool> resized;

View File

@@ -1007,7 +1007,7 @@ namespace Proc {
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1, toggle_children = -1;
int collapse = -1, expand = -1, toggle_children = -1, collapse_all = -1;
uint64_t old_cputimes = 0;
atomic<int> numpids = 0;
int filter_found = 0;
@@ -1301,6 +1301,21 @@ namespace Proc {
}
collapse = expand = -1;
}
if (collapse_all != -1) {
//? If any parent process is currently expanded, collapse all; otherwise expand all
std::unordered_set<size_t> parent_pids;
for (const auto& p : current_procs)
parent_pids.insert(static_cast<size_t>(p.ppid));
const bool do_collapse = rng::any_of(current_procs, [&parent_pids](const proc_info& p) {
return parent_pids.contains(p.pid) and not p.collapsed;
});
for (auto& p : current_procs)
p.collapsed = do_collapse;
collapse_all = -1;
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<tree_proc> tree_procs;

View File

@@ -2879,7 +2879,7 @@ namespace Proc {
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1, toggle_children = -1;
int collapse = -1, expand = -1, toggle_children = -1, collapse_all = -1;
uint64_t old_cputimes{};
atomic<int> numpids{};
int filter_found{};
@@ -3354,6 +3354,21 @@ namespace Proc {
}
collapse = expand = -1;
}
if (collapse_all != -1) {
//? If any parent process is currently expanded, collapse all; otherwise expand all
std::unordered_set<size_t> parent_pids;
for (const auto& p : current_procs)
parent_pids.insert(static_cast<size_t>(p.ppid));
const bool do_collapse = rng::any_of(current_procs, [&parent_pids](const proc_info& p) {
return parent_pids.contains(p.pid) and not p.collapsed;
});
for (auto& p : current_procs)
p.collapsed = do_collapse;
collapse_all = -1;
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<tree_proc> tree_procs;

View File

@@ -1100,7 +1100,7 @@ namespace Proc {
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1, toggle_children = -1;
int collapse = -1, expand = -1, toggle_children = -1, collapse_all = -1;
uint64_t old_cputimes = 0;
atomic<int> numpids = 0;
int filter_found = 0;
@@ -1362,6 +1362,21 @@ namespace Proc {
}
collapse = expand = -1;
}
if (collapse_all != -1) {
//? If any parent process is currently expanded, collapse all; otherwise expand all
std::unordered_set<size_t> parent_pids;
for (const auto& p : current_procs)
parent_pids.insert(static_cast<size_t>(p.ppid));
const bool do_collapse = rng::any_of(current_procs, [&parent_pids](const proc_info& p) {
return parent_pids.contains(p.pid) and not p.collapsed;
});
for (auto& p : current_procs)
p.collapsed = do_collapse;
collapse_all = -1;
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<tree_proc> tree_procs;

View File

@@ -991,7 +991,7 @@ namespace Proc {
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1, toggle_children = -1;
int collapse = -1, expand = -1, toggle_children = -1, collapse_all = -1;
uint64_t old_cputimes = 0;
atomic<int> numpids = 0;
int filter_found = 0;
@@ -1265,6 +1265,21 @@ namespace Proc {
}
collapse = expand = -1;
}
if (collapse_all != -1) {
//? If any parent process is currently expanded, collapse all; otherwise expand all
std::unordered_set<size_t> parent_pids;
for (const auto& p : current_procs)
parent_pids.insert(static_cast<size_t>(p.ppid));
const bool do_collapse = rng::any_of(current_procs, [&parent_pids](const proc_info& p) {
return parent_pids.contains(p.pid) and not p.collapsed;
});
for (auto& p : current_procs)
p.collapsed = do_collapse;
collapse_all = -1;
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<tree_proc> tree_procs;

View File

@@ -1590,7 +1590,7 @@ namespace Proc {
fs::file_time_type passwd_time;
uint64_t cputimes;
int collapse = -1, expand = -1, toggle_children = -1;
int collapse = -1, expand = -1, toggle_children = -1, collapse_all = -1;
uint64_t old_cputimes = 0;
atomic<int> numpids = 0;
int filter_found = 0;
@@ -1922,6 +1922,21 @@ namespace Proc {
}
collapse = expand = -1;
}
if (collapse_all != -1) {
//? If any parent process is currently expanded, collapse all; otherwise expand all
std::unordered_set<size_t> parent_pids;
for (const auto& p : current_procs)
parent_pids.insert(static_cast<size_t>(p.ppid));
const bool do_collapse = rng::any_of(current_procs, [&parent_pids](const proc_info& p) {
return parent_pids.contains(p.pid) and not p.collapsed;
});
for (auto& p : current_procs)
p.collapsed = do_collapse;
collapse_all = -1;
if (Config::ints.at("proc_selected") > 0) locate_selection = true;
}
if (should_filter or not filter.empty()) filter_found = 0;
vector<tree_proc> tree_procs;