Skip to content

Commit 19b438d

Browse files
committed
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).
1 parent af4c412 commit 19b438d

File tree

4 files changed

+112
-49
lines changed

4 files changed

+112
-49
lines changed

FunctionBar.c

+11-7
Original file line numberDiff line numberDiff line change
@@ -40,23 +40,26 @@ ObjectClass FunctionBar_class = {
4040

4141
FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* events) {
4242
FunctionBar* this = AllocThis(FunctionBar);
43-
this->functions = (char**) functions;
43+
this->functions = calloc(16, sizeof(char*));
44+
if (!functions) {
45+
functions = FunctionBar_FLabels;
46+
}
47+
for (int i = 0; i < 15 && functions[i]; i++) {
48+
this->functions[i] = strdup(functions[i]);
49+
}
4450
if (keys && events) {
4551
this->staticData = false;
46-
this->functions = malloc(sizeof(char*) * 15);
4752
this->keys = malloc(sizeof(char*) * 15);
4853
this->events = malloc(sizeof(int) * 15);
4954
int i = 0;
5055
while (i < 15 && functions[i]) {
51-
this->functions[i] = strdup(functions[i]);
5256
this->keys[i] = strdup(keys[i]);
5357
this->events[i] = events[i];
5458
i++;
5559
}
5660
this->size = i;
5761
} else {
5862
this->staticData = true;
59-
this->functions = (char**)( functions ? functions : FunctionBar_FLabels );
6063
this->keys = (char**) FunctionBar_FKeys;
6164
this->events = FunctionBar_FEvents;
6265
this->size = 10;
@@ -66,20 +69,21 @@ FunctionBar* FunctionBar_new(const char** functions, const char** keys, int* eve
6669

6770
void FunctionBar_delete(Object* cast) {
6871
FunctionBar* this = (FunctionBar*) cast;
72+
for (int i = 0; i < 15 && this->functions[i]; i++) {
73+
free(this->functions[i]);
74+
}
75+
free(this->functions);
6976
if (!this->staticData) {
7077
for (int i = 0; i < this->size; i++) {
71-
free(this->functions[i]);
7278
free(this->keys[i]);
7379
}
74-
free(this->functions);
7580
free(this->keys);
7681
free(this->events);
7782
}
7883
free(this);
7984
}
8085

8186
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
82-
assert(!this->staticData);
8387
for (int i = 0; i < this->size; i++) {
8488
if (this->events[i] == event) {
8589
free(this->functions[i]);

htop.1.in

+5-3
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ between them as a tree. Toggling the key will switch between tree and
9191
your previously selected sort view. Selecting a sort view will exit
9292
tree view.
9393
.TP
94-
.B F6, <, >
95-
Select a field for sorting. The current sort field is indicated by a
96-
highlight in the header.
94+
.B F6
95+
On sorted view, select a field for sorting, also accessible through < and >.
96+
The current sort field is indicated by a highlight in the header.
97+
On tree view, expand or collapse the current subtree. A "+" indicator in the
98+
tree node indicates that it is collapsed.
9799
.TP
98100
.B F7, ]
99101
Increase the selected process's priority (subtract from 'nice' value).

htop.c

+94-39
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ static void printVersionFlag() {
4848
exit(0);
4949
}
5050

51+
static const char* defaultFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ", "SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
52+
5153
static void printHelpFlag() {
5254
fputs("htop " VERSION " - " COPYRIGHT "\n"
5355
"Released under the GNU GPL.\n\n"
@@ -76,7 +78,7 @@ static struct { const char* key; const char* info; } helpLeft[] = {
7678
{ .key = " H: ", .info = "hide/show user threads" },
7779
{ .key = " K: ", .info = "hide/show kernel threads" },
7880
{ .key = " F: ", .info = "cursor follows process" },
79-
{ .key = " + -: ", .info = "expand/collapse tree" },
81+
{ .key = " F6 + -: ", .info = "expand/collapse tree" },
8082
{ .key = " P M T: ", .info = "sort by CPU%, MEM% or TIME" },
8183
{ .key = " I: ", .info = "invert sort order" },
8284
{ .key = " F6 >: ", .info = "select sort column" },
@@ -268,10 +270,24 @@ static bool setUserOnly(const char* userName, bool* userOnly, uid_t* userId) {
268270
return false;
269271
}
270272

271-
static inline void setSortKey(ProcessList* pl, ProcessField sortKey, Panel* panel, Settings* settings) {
273+
static void setTreeView(ProcessList* pl, FunctionBar* fuBar, bool mode) {
274+
if (mode) {
275+
FunctionBar_setLabel(fuBar, KEY_F(5), "Sorted");
276+
FunctionBar_setLabel(fuBar, KEY_F(6), "Collap");
277+
} else {
278+
FunctionBar_setLabel(fuBar, KEY_F(5), "Tree ");
279+
FunctionBar_setLabel(fuBar, KEY_F(6), "SortBy");
280+
}
281+
if (mode != pl->treeView) {
282+
FunctionBar_draw(fuBar, NULL);
283+
}
284+
pl->treeView = mode;
285+
}
286+
287+
static inline void setSortKey(ProcessList* pl, FunctionBar* fuBar, ProcessField sortKey, Panel* panel, Settings* settings) {
272288
pl->sortKey = sortKey;
273289
pl->direction = 1;
274-
pl->treeView = false;
290+
setTreeView(pl, fuBar, false);
275291
settings->changed = true;
276292
ProcessList_printHeader(pl, Panel_getHeader(panel));
277293
}
@@ -294,6 +310,35 @@ static void tagAllChildren(Panel* panel, Process* parent) {
294310
}
295311
}
296312

313+
static bool expandCollapse(Panel* panel) {
314+
Process* p = (Process*) Panel_getSelected(panel);
315+
if (!p) return false;
316+
p->showChildren = !p->showChildren;
317+
return true;
318+
}
319+
320+
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header) {
321+
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
322+
Panel_setHeader(sortPanel, "Sort by");
323+
const char* fuFunctions[] = {"Sort ", "Cancel ", NULL};
324+
ProcessField* fields = pl->fields;
325+
for (int i = 0; fields[i]; i++) {
326+
char* name = String_trim(Process_fieldNames[fields[i]]);
327+
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
328+
if (fields[i] == pl->sortKey)
329+
Panel_setSelected(sortPanel, i);
330+
free(name);
331+
}
332+
ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
333+
if (field) {
334+
settings->changed = true;
335+
setSortKey(pl, defaultBar, field->key, panel, settings);
336+
} else {
337+
ProcessList_printHeader(pl, Panel_getHeader(panel));
338+
}
339+
Object_delete(sortPanel);
340+
}
341+
297342
int main(int argc, char** argv) {
298343

299344
int delay = -1;
@@ -437,16 +482,15 @@ int main(int argc, char** argv) {
437482
Panel* panel = Panel_new(0, headerHeight, COLS, LINES - headerHeight - 2, false, &Process_class);
438483
ProcessList_setPanel(pl, panel);
439484

485+
FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
486+
setTreeView(pl, defaultBar, pl->treeView);
487+
440488
if (sortKey > 0) {
441489
pl->sortKey = sortKey;
442-
pl->treeView = false;
490+
setTreeView(pl, defaultBar, false);
443491
pl->direction = 1;
444492
}
445493
ProcessList_printHeader(pl, Panel_getHeader(panel));
446-
447-
const char* defaultFunctions[] = {"Help ", "Setup ", "Search", "Filter", "Tree ",
448-
"SortBy", "Nice -", "Nice +", "Kill ", "Quit ", NULL};
449-
FunctionBar* defaultBar = FunctionBar_new(defaultFunctions, NULL, NULL);
450494

451495
IncSet* inc = IncSet_new(defaultBar);
452496

@@ -468,6 +512,8 @@ int main(int argc, char** argv) {
468512

469513
bool idle = false;
470514

515+
bool collapsed = false;
516+
471517
while (!quit) {
472518
gettimeofday(&tv, NULL);
473519
newTime = ((double)tv.tv_sec * 10) + ((double)tv.tv_usec / 100000);
@@ -490,9 +536,24 @@ int main(int argc, char** argv) {
490536
idle = false;
491537
}
492538
doRefresh = true;
539+
540+
if (pl->treeView) {
541+
Process* p = (Process*) Panel_getSelected(panel);
542+
if (p) {
543+
if (!p->showChildren && !collapsed) {
544+
FunctionBar_setLabel(defaultBar, KEY_F(6), "Expand");
545+
FunctionBar_draw(defaultBar, NULL);
546+
} else if (p->showChildren && collapsed) {
547+
FunctionBar_setLabel(defaultBar, KEY_F(6), "Collap");
548+
FunctionBar_draw(defaultBar, NULL);
549+
}
550+
collapsed = !p->showChildren;
551+
}
552+
}
493553

494-
if (!idle)
554+
if (!idle) {
495555
Panel_draw(panel, true);
556+
}
496557

497558
int prev = ch;
498559
if (inc->active)
@@ -524,9 +585,9 @@ int main(int argc, char** argv) {
524585
ProcessField field = ProcessList_keyAt(pl, x);
525586
if (field == pl->sortKey) {
526587
ProcessList_invertSortOrder(pl);
527-
pl->treeView = false;
588+
setTreeView(pl, defaultBar, false);
528589
} else {
529-
setSortKey(pl, field, panel, settings);
590+
setSortKey(pl, defaultBar, field, panel, settings);
530591
}
531592
refreshTimeout = 0;
532593
continue;
@@ -580,13 +641,13 @@ int main(int argc, char** argv) {
580641
case 'M':
581642
{
582643
refreshTimeout = 0;
583-
setSortKey(pl, PERCENT_MEM, panel, settings);
644+
setSortKey(pl, defaultBar, PERCENT_MEM, panel, settings);
584645
break;
585646
}
586647
case 'T':
587648
{
588649
refreshTimeout = 0;
589-
setSortKey(pl, TIME, panel, settings);
650+
setSortKey(pl, defaultBar, TIME, panel, settings);
590651
break;
591652
}
592653
case 'c':
@@ -608,7 +669,7 @@ int main(int argc, char** argv) {
608669
case 'P':
609670
{
610671
refreshTimeout = 0;
611-
setSortKey(pl, PERCENT_CPU, panel, settings);
672+
setSortKey(pl, defaultBar, PERCENT_CPU, panel, settings);
612673
break;
613674
}
614675
case KEY_F(1):
@@ -704,11 +765,10 @@ int main(int argc, char** argv) {
704765
case '=':
705766
case '-':
706767
{
707-
Process* p = (Process*) Panel_getSelected(panel);
708-
if (!p) break;
709-
p->showChildren = !p->showChildren;
710-
refreshTimeout = 0;
711-
doRecalculate = true;
768+
if (expandCollapse(panel)) {
769+
doRecalculate = true;
770+
refreshTimeout = 0;
771+
}
712772
break;
713773
}
714774
case KEY_F(9):
@@ -764,31 +824,25 @@ int main(int argc, char** argv) {
764824
break;
765825
case '<':
766826
case ',':
767-
case KEY_F(18):
768827
case '>':
769828
case '.':
829+
{
830+
sortBy(panel, pl, settings, headerHeight, defaultBar, header);
831+
refreshTimeout = 0;
832+
break;
833+
}
834+
case KEY_F(18):
770835
case KEY_F(6):
771836
{
772-
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem));
773-
Panel_setHeader(sortPanel, "Sort by");
774-
const char* fuFunctions[] = {"Sort ", "Cancel ", NULL};
775-
ProcessField* fields = pl->fields;
776-
for (int i = 0; fields[i]; i++) {
777-
char* name = String_trim(Process_fieldNames[fields[i]]);
778-
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
779-
if (fields[i] == pl->sortKey)
780-
Panel_setSelected(sortPanel, i);
781-
free(name);
782-
}
783-
ListItem* field = (ListItem*) pickFromVector(panel, sortPanel, 15, headerHeight, fuFunctions, defaultBar, header);
784-
if (field) {
785-
settings->changed = true;
786-
setSortKey(pl, field->key, panel, settings);
837+
if (pl->treeView) {
838+
if (expandCollapse(panel)) {
839+
doRecalculate = true;
840+
refreshTimeout = 0;
841+
}
787842
} else {
788-
ProcessList_printHeader(pl, Panel_getHeader(panel));
843+
sortBy(panel, pl, settings, headerHeight, defaultBar, header);
844+
refreshTimeout = 0;
789845
}
790-
Object_delete(sortPanel);
791-
refreshTimeout = 0;
792846
break;
793847
}
794848
case 'i':
@@ -842,7 +896,8 @@ int main(int argc, char** argv) {
842896
case 't':
843897
case KEY_F(5):
844898
refreshTimeout = 0;
845-
pl->treeView = !pl->treeView;
899+
collapsed = false;
900+
setTreeView(pl, defaultBar, !pl->treeView);
846901
if (pl->treeView) pl->direction = 1;
847902
ProcessList_printHeader(pl, Panel_getHeader(panel));
848903
ProcessList_expandTree(pl);

htop.h

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ in the source distribution for its full text.
1515

1616
typedef bool(*ForeachProcessFn)(Process*, size_t);
1717

18+
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header);
19+
1820
int main(int argc, char** argv);
1921

2022
#endif

0 commit comments

Comments
 (0)