mirror of https://github.com/xzeldon/htop.git
Improve discoverability of the expand/collapse feature.
It is now accessible via F6 when on tree view (as a bonus, it is now also reachable via the mouse). The function bar now dynamically changes to reflect the toggle nature of the tree-view mode (F5) and the F6 key serves as expand/collapse when on tree mode, and its previous behavior of bringing up the "Sort By" menu (which only made sense on non-tree mode). Users wishing to go to the "Sort By" menu straight from Tree View can still do so with the "<" and ">" keys (the top-compatible keys for sort selection).
This commit is contained in:
parent
af4c412ebf
commit
19b438de10
|
@ -40,15 +40,19 @@ ObjectClass FunctionBar_class = {
|
|||
|
||||
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events) {
|
||||
FunctionBar* this = AllocThis(FunctionBar);
|
||||
this->functions = (char**) functions;
|
||||
this->functions = calloc(16, sizeof(char*));
|
||||
if (!functions) {
|
||||
functions = FunctionBar_FLabels;
|
||||
}
|
||||
for (int i = 0; i < 15 && functions[i]; i++) {
|
||||
this->functions[i] = strdup(functions[i]);
|
||||
}
|
||||
if (keys && events) {
|
||||
this->staticData = false;
|
||||
this->functions = malloc(sizeof(char*) * 15);
|
||||
this->keys = malloc(sizeof(char*) * 15);
|
||||
this->events = malloc(sizeof(int) * 15);
|
||||
int i = 0;
|
||||
while (i < 15 && functions[i]) {
|
||||
this->functions[i] = strdup(functions[i]);
|
||||
this->keys[i] = strdup(keys[i]);
|
||||
this->events[i] = events[i];
|
||||
i++;
|
||||
|
@ -56,7 +60,6 @@ FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* eve
|
|||
this->size = i;
|
||||
} else {
|
||||
this->staticData = true;
|
||||
this->functions = (char**)( functions ? functions : FunctionBar_FLabels );
|
||||
this->keys = (char**) FunctionBar_FKeys;
|
||||
this->events = FunctionBar_FEvents;
|
||||
this->size = 10;
|
||||
|
@ -66,12 +69,14 @@ FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* eve
|
|||
|
||||
void FunctionBar_delete(Object* cast) {
|
||||
FunctionBar* this = (FunctionBar*) cast;
|
||||
for (int i = 0; i < 15 && this->functions[i]; i++) {
|
||||
free(this->functions[i]);
|
||||
}
|
||||
free(this->functions);
|
||||
if (!this->staticData) {
|
||||
for (int i = 0; i < this->size; i++) {
|
||||
free(this->functions[i]);
|
||||
free(this->keys[i]);
|
||||
}
|
||||
free(this->functions);
|
||||
free(this->keys);
|
||||
free(this->events);
|
||||
}
|
||||
|
@ -79,7 +84,6 @@ void FunctionBar_delete(Object* cast) {
|
|||
}
|
||||
|
||||
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
|
||||
assert(!this->staticData);
|
||||
for (int i = 0; i < this->size; i++) {
|
||||
if (this->events[i] == event) {
|
||||
free(this->functions[i]);
|
||||
|
|
|
@ -91,9 +91,11 @@ between them as a tree. Toggling the key will switch between tree and
|
|||
your previously selected sort view. Selecting a sort view will exit
|
||||
tree view.
|
||||
.TP
|
||||
.B F6, <, >
|
||||
Select a field for sorting. The current sort field is indicated by a
|
||||
highlight in the header.
|
||||
.B F6
|
||||
On sorted view, select a field for sorting, also accessible through < and >.
|
||||
The current sort field is indicated by a highlight in the header.
|
||||
On tree view, expand or collapse the current subtree. A "+" indicator in the
|
||||
tree node indicates that it is collapsed.
|
||||
.TP
|
||||
.B F7, ]
|
||||
Increase the selected process's priority (subtract from 'nice' value).
|
||||
|
|
133
htop.c
133
htop.c
|
@ -48,6 +48,8 @@ static void printVersionFlag() {
|
|||
exit(0);
|
||||
}
|
||||
|
||||
static const char* defaultFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
|
||||
|
||||
static void printHelpFlag() {
|
||||
fputs("htop " VERSION " - " COPYRIGHT "\n"
|
||||
"Released under the GNU GPL.\n\n"
|
||||
|
@ -76,7 +78,7 @@ static struct { const char* key; const char* info; } helpLeft[] = {
|
|||
{ .key = " H: ", .info = "hide/show user threads" },
|
||||
{ .key = " K: ", .info = "hide/show kernel threads" },
|
||||
{ .key = " F: ", .info = "cursor follows process" },
|
||||
{ .key = " + -: ", .info = "expand/collapse tree" },
|
||||
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
|
||||
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
|
||||
{ .key = " I: ", .info = "invert sort order" },
|
||||
{ .key = " F6 >: ", .info = "select sort column" },
|
||||
|
@ -268,10 +270,24 @@ static bool setUserOnly(const char* userName, bool* userOnly, uid_t* userId) {
|
|||
return false;
|
||||
}
|
||||
|
||||
static inline void setSortKey(ProcessList* pl, ProcessField sortKey, Panel* panel, Settings* settings) {
|
||||
static void setTreeView(ProcessList* pl, FunctionBar* fuBar, bool mode) {
|
||||
if (mode) {
|
||||
FunctionBar_setLabel(fuBar, KEY_F(5), "Sorted");
|
||||
FunctionBar_setLabel(fuBar, KEY_F(6), "Collap");
|
||||
} else {
|
||||
FunctionBar_setLabel(fuBar, KEY_F(5), "Tree ");
|
||||
FunctionBar_setLabel(fuBar, KEY_F(6), "SortBy");
|
||||
}
|
||||
if (mode != pl->treeView) {
|
||||
FunctionBar_draw(fuBar, NULL);
|
||||
}
|
||||
pl->treeView = mode;
|
||||
}
|
||||
|
||||
static inline void setSortKey(ProcessList* pl, FunctionBar* fuBar, ProcessField sortKey, Panel* panel, Settings* settings) {
|
||||
pl->sortKey = sortKey;
|
||||
pl->direction = 1;
|
||||
pl->treeView = false;
|
||||
setTreeView(pl, fuBar, false);
|
||||
settings->changed = true;
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
}
|
||||
|
@ -294,6 +310,35 @@ static void tagAllChildren(Panel* panel, Process* parent) {
|
|||
}
|
||||
}
|
||||
|
||||
static bool expandCollapse(Panel* panel) {
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (!p) return false;
|
||||
p->showChildren = !p->showChildren;
|
||||
return true;
|
||||
}
|
||||
|
||||
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header) {
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
|
||||
Panel_setHeader(sortPanel, "Sort by");
|
||||
const char* fuFunctions[] = {"Sort ", "Cancel ", NULL};
|
||||
ProcessField* fields = pl->fields;
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
char* name = String_trim(Process_fieldNames[fields[i]]);
|
||||
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
|
||||
if (fields[i] == pl->sortKey)
|
||||
Panel_setSelected(sortPanel, i);
|
||||
free(name);
|
||||
}
|
||||
ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
|
||||
if (field) {
|
||||
settings->changed = true;
|
||||
setSortKey(pl, defaultBar, field->key, panel, settings);
|
||||
} else {
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
}
|
||||
Object_delete(sortPanel);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
int delay = -1;
|
||||
|
@ -437,16 +482,15 @@ int main(int argc, char** argv) {
|
|||
Panel* panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, false, &Process_class);
|
||||
ProcessList_setPanel(pl, panel);
|
||||
|
||||
FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
|
||||
setTreeView(pl, defaultBar, pl->treeView);
|
||||
|
||||
if (sortKey > 0) {
|
||||
pl->sortKey = sortKey;
|
||||
pl->treeView = false;
|
||||
setTreeView(pl, defaultBar, false);
|
||||
pl->direction = 1;
|
||||
}
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
|
||||
const char* defaultFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ",
|
||||
"SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
|
||||
FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
|
||||
|
||||
IncSet* inc = IncSet_new(defaultBar);
|
||||
|
||||
|
@ -468,6 +512,8 @@ int main(int argc, char** argv) {
|
|||
|
||||
bool idle = false;
|
||||
|
||||
bool collapsed = false;
|
||||
|
||||
while (!quit) {
|
||||
gettimeofday(&tv, NULL);
|
||||
newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
|
||||
|
@ -490,9 +536,24 @@ int main(int argc, char** argv) {
|
|||
idle = false;
|
||||
}
|
||||
doRefresh = true;
|
||||
|
||||
if (pl->treeView) {
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (p) {
|
||||
if (!p->showChildren && !collapsed) {
|
||||
FunctionBar_setLabel(defaultBar, KEY_F(6), "Expand");
|
||||
FunctionBar_draw(defaultBar, NULL);
|
||||
} else if (p->showChildren && collapsed) {
|
||||
FunctionBar_setLabel(defaultBar, KEY_F(6), "Collap");
|
||||
FunctionBar_draw(defaultBar, NULL);
|
||||
}
|
||||
collapsed = !p->showChildren;
|
||||
}
|
||||
}
|
||||
|
||||
if (!idle)
|
||||
if (!idle) {
|
||||
Panel_draw(panel, true);
|
||||
}
|
||||
|
||||
int prev = ch;
|
||||
if (inc->active)
|
||||
|
@ -524,9 +585,9 @@ int main(int argc, char** argv) {
|
|||
ProcessField field = ProcessList_keyAt(pl, x);
|
||||
if (field == pl->sortKey) {
|
||||
ProcessList_invertSortOrder(pl);
|
||||
pl->treeView = false;
|
||||
setTreeView(pl, defaultBar, false);
|
||||
} else {
|
||||
setSortKey(pl, field, panel, settings);
|
||||
setSortKey(pl, defaultBar, field, panel, settings);
|
||||
}
|
||||
refreshTimeout = 0;
|
||||
continue;
|
||||
|
@ -580,13 +641,13 @@ int main(int argc, char** argv) {
|
|||
case 'M':
|
||||
{
|
||||
refreshTimeout = 0;
|
||||
setSortKey(pl, PERCENT_MEM, panel, settings);
|
||||
setSortKey(pl, defaultBar, PERCENT_MEM, panel, settings);
|
||||
break;
|
||||
}
|
||||
case 'T':
|
||||
{
|
||||
refreshTimeout = 0;
|
||||
setSortKey(pl, TIME, panel, settings);
|
||||
setSortKey(pl, defaultBar, TIME, panel, settings);
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
|
@ -608,7 +669,7 @@ int main(int argc, char** argv) {
|
|||
case 'P':
|
||||
{
|
||||
refreshTimeout = 0;
|
||||
setSortKey(pl, PERCENT_CPU, panel, settings);
|
||||
setSortKey(pl, defaultBar, PERCENT_CPU, panel, settings);
|
||||
break;
|
||||
}
|
||||
case KEY_F(1):
|
||||
|
@ -704,11 +765,10 @@ int main(int argc, char** argv) {
|
|||
case '=':
|
||||
case '-':
|
||||
{
|
||||
Process* p = (Process*) Panel_getSelected(panel);
|
||||
if (!p) break;
|
||||
p->showChildren = !p->showChildren;
|
||||
refreshTimeout = 0;
|
||||
doRecalculate = true;
|
||||
if (expandCollapse(panel)) {
|
||||
doRecalculate = true;
|
||||
refreshTimeout = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KEY_F(9):
|
||||
|
@ -764,31 +824,25 @@ int main(int argc, char** argv) {
|
|||
break;
|
||||
case '<':
|
||||
case ',':
|
||||
case KEY_F(18):
|
||||
case '>':
|
||||
case '.':
|
||||
{
|
||||
sortBy(panel, pl, settings, headerHeight, defaultBar, header);
|
||||
refreshTimeout = 0;
|
||||
break;
|
||||
}
|
||||
case KEY_F(18):
|
||||
case KEY_F(6):
|
||||
{
|
||||
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
|
||||
Panel_setHeader(sortPanel, "Sort by");
|
||||
const char* fuFunctions[] = {"Sort ", "Cancel ", NULL};
|
||||
ProcessField* fields = pl->fields;
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
char* name = String_trim(Process_fieldNames[fields[i]]);
|
||||
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
|
||||
if (fields[i] == pl->sortKey)
|
||||
Panel_setSelected(sortPanel, i);
|
||||
free(name);
|
||||
}
|
||||
ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
|
||||
if (field) {
|
||||
settings->changed = true;
|
||||
setSortKey(pl, field->key, panel, settings);
|
||||
if (pl->treeView) {
|
||||
if (expandCollapse(panel)) {
|
||||
doRecalculate = true;
|
||||
refreshTimeout = 0;
|
||||
}
|
||||
} else {
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
sortBy(panel, pl, settings, headerHeight, defaultBar, header);
|
||||
refreshTimeout = 0;
|
||||
}
|
||||
Object_delete(sortPanel);
|
||||
refreshTimeout = 0;
|
||||
break;
|
||||
}
|
||||
case 'i':
|
||||
|
@ -842,7 +896,8 @@ int main(int argc, char** argv) {
|
|||
case 't':
|
||||
case KEY_F(5):
|
||||
refreshTimeout = 0;
|
||||
pl->treeView = !pl->treeView;
|
||||
collapsed = false;
|
||||
setTreeView(pl, defaultBar, !pl->treeView);
|
||||
if (pl->treeView) pl->direction = 1;
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
ProcessList_expandTree(pl);
|
||||
|
|
Loading…
Reference in New Issue