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:
Hisham Muhammad 2014-04-09 18:02:50 -03:00
parent af4c412ebf
commit 19b438de10
4 changed files with 112 additions and 49 deletions

View File

@ -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]);

View File

@ -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
View File

@ -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,17 +482,16 @@ 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);
ProcessList_scan(pl);
@ -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);
@ -491,8 +537,23 @@ int main(int argc, char** argv) {
}
doRefresh = true;
if (!idle)
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) {
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);

2
htop.h
View File

@ -15,6 +15,8 @@ in the source distribution for its full text.
typedef bool(*ForeachProcessFn)(Process*, size_t);
void sortBy(Panel* panel, ProcessList* pl, Settings* settings, int headerHeight, FunctionBar* defaultBar, Header* header);
int main(int argc, char** argv);
#endif