forked from RepoMirrors/bemenu
Make it possible filter manually, and optimized filtering.
This commit is contained in:
parent
45e2117159
commit
71beb7583f
11
lib/bemenu.h
11
lib/bemenu.h
@ -318,6 +318,17 @@ bmItem** bmMenuGetFilteredItems(const bmMenu *menu, unsigned int *outNmemb);
|
||||
*/
|
||||
void bmMenuRender(const bmMenu *menu);
|
||||
|
||||
/**
|
||||
* Trigger filtering of menu manually.
|
||||
* This is useful when adding new items and want to dynamically see them filtered.
|
||||
*
|
||||
* Do note that filtering might be heavy, so you should only call it after batch manipulation of items.
|
||||
* Not after manipulation of each single item.
|
||||
*
|
||||
* @param menu bmMenu instance which to filter.
|
||||
*/
|
||||
void bmMenuFilter(bmMenu *menu);
|
||||
|
||||
/**
|
||||
* Poll key and unicode from underlying UI toolkit.
|
||||
*
|
||||
|
27
lib/filter.c
27
lib/filter.c
@ -76,12 +76,13 @@ fail:
|
||||
* Dmenu filterer that accepts substring function.
|
||||
*
|
||||
* @param menu bmMenu instance to filter.
|
||||
* @param addition This will be 1, if filter is same as previous filter with something appended.
|
||||
* @param fstrstr Substring function used to match items.
|
||||
* @param outNmemb unsigned int reference to filtered items outNmemb.
|
||||
* @param outHighlighted unsigned int reference to new outHighlighted item index.
|
||||
* @return Pointer to array of bmItem pointers.
|
||||
*/
|
||||
bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
bmItem** _bmFilterDmenuFun(bmMenu *menu, char addition, char* (*fstrstr)(const char *a, const char *b), unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
{
|
||||
assert(menu);
|
||||
assert(outNmemb);
|
||||
@ -89,7 +90,13 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c
|
||||
*outNmemb = *outHighlighted = 0;
|
||||
|
||||
unsigned int itemsCount;
|
||||
bmItem **items = bmMenuGetItems(menu, &itemsCount);
|
||||
bmItem **items;
|
||||
|
||||
if (addition) {
|
||||
items = bmMenuGetFilteredItems(menu, &itemsCount);
|
||||
} else {
|
||||
items = bmMenuGetItems(menu, &itemsCount);
|
||||
}
|
||||
|
||||
bmItem **filtered = calloc(itemsCount, sizeof(bmItem*));
|
||||
if (!filtered)
|
||||
@ -99,6 +106,7 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c
|
||||
unsigned int tokc;
|
||||
char *buffer = _bmFilterTokenize(menu, &tokv, &tokc);
|
||||
|
||||
char found = 0;
|
||||
unsigned int i, f;
|
||||
for (f = i = 0; i < itemsCount; ++i) {
|
||||
bmItem *item = items[i];
|
||||
@ -112,8 +120,11 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c
|
||||
continue;
|
||||
}
|
||||
|
||||
if (f == 0 || item == bmMenuGetHighlightedItem(menu))
|
||||
if (!found && item == bmMenuGetHighlightedItem(menu)) {
|
||||
*outHighlighted = f;
|
||||
found = 1;
|
||||
}
|
||||
|
||||
filtered[f++] = item;
|
||||
}
|
||||
|
||||
@ -130,26 +141,28 @@ bmItem** _bmFilterDmenuFun(bmMenu *menu, char* (*fstrstr)(const char *a, const c
|
||||
* Filter that mimics the vanilla dmenu filtering.
|
||||
*
|
||||
* @param menu bmMenu instance to filter.
|
||||
* @param addition This will be 1, if filter is same as previous filter with something appended.
|
||||
* @param outNmemb unsigned int reference to filtered items outNmemb.
|
||||
* @param outHighlighted unsigned int reference to new outHighlighted item index.
|
||||
* @return Pointer to array of bmItem pointers.
|
||||
*/
|
||||
bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
{
|
||||
return _bmFilterDmenuFun(menu, strstr, outNmemb, outHighlighted);
|
||||
return _bmFilterDmenuFun(menu, addition, strstr, outNmemb, outHighlighted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter that mimics the vanilla case-insensitive dmenu filtering.
|
||||
*
|
||||
* @param menu bmMenu instance to filter.
|
||||
* @param addition This will be 1, if filter is same as previous filter with something appended.
|
||||
* @param outNmemb unsigned int reference to filtered items outNmemb.
|
||||
* @param outHighlighted unsigned int reference to new outHighlighted item index.
|
||||
* @return Pointer to array of bmItem pointers.
|
||||
*/
|
||||
bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted)
|
||||
{
|
||||
return _bmFilterDmenuFun(menu, _bmStrupstr, outNmemb, outHighlighted);
|
||||
return _bmFilterDmenuFun(menu, addition, _bmStrupstr, outNmemb, outHighlighted);
|
||||
}
|
||||
|
||||
/* vim: set ts=8 sw=4 tw=0 :*/
|
||||
|
@ -102,6 +102,11 @@ struct _bmMenu {
|
||||
*/
|
||||
char filter[1024];
|
||||
|
||||
/**
|
||||
* Used as optimization.
|
||||
*/
|
||||
char *oldFilter;
|
||||
|
||||
/**
|
||||
* Current byte offset on filter text.
|
||||
*/
|
||||
@ -136,8 +141,8 @@ int _bmDrawCursesInit(struct _bmRenderApi *api);
|
||||
int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item);
|
||||
|
||||
/* filter.c */
|
||||
bmItem** _bmFilterDmenu(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted);
|
||||
bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted);
|
||||
bmItem** _bmFilterDmenu(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted);
|
||||
bmItem** _bmFilterDmenuCaseInsensitive(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted);
|
||||
|
||||
/* list.c */
|
||||
void _bmItemListFreeList(struct _bmItemList *list);
|
||||
|
103
lib/menu.c
103
lib/menu.c
@ -7,27 +7,11 @@
|
||||
/**
|
||||
* Filter function map.
|
||||
*/
|
||||
static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, unsigned int *outNmemb, unsigned int *outHighlighted) = {
|
||||
static bmItem** (*filterFunc[BM_FILTER_MODE_LAST])(bmMenu *menu, char addition, unsigned int *outNmemb, unsigned int *outHighlighted) = {
|
||||
_bmFilterDmenu, /* BM_FILTER_DMENU */
|
||||
_bmFilterDmenuCaseInsensitive /* BM_FILTER_DMENU_CASE_INSENSITIVE */
|
||||
};
|
||||
|
||||
static void _bmMenuFilter(bmMenu *menu)
|
||||
{
|
||||
assert(menu);
|
||||
|
||||
if (!strlen(menu->filter) || !menu->items.list || menu->items.count <= 0) {
|
||||
_bmItemListFreeList(&menu->filtered);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int count, selected;
|
||||
bmItem **filtered = filterFunc[menu->filterMode](menu, &count, &selected);
|
||||
|
||||
_bmItemListSetItemsNoCopy(&menu->filtered, filtered, count);
|
||||
menu->index = selected;
|
||||
}
|
||||
|
||||
int _bmMenuItemIsSelected(const bmMenu *menu, const bmItem *item)
|
||||
{
|
||||
assert(menu);
|
||||
@ -87,6 +71,9 @@ void bmMenuFree(bmMenu *menu)
|
||||
if (menu->title)
|
||||
free(menu->title);
|
||||
|
||||
if (menu->oldFilter)
|
||||
free(menu->oldFilter);
|
||||
|
||||
bmMenuFreeItems(menu);
|
||||
free(menu);
|
||||
}
|
||||
@ -138,12 +125,7 @@ void* bmMenuGetUserdata(bmMenu *menu)
|
||||
void bmMenuSetFilterMode(bmMenu *menu, bmFilterMode mode)
|
||||
{
|
||||
assert(menu);
|
||||
|
||||
bmFilterMode oldMode = mode;
|
||||
menu->filterMode = (mode >= BM_FILTER_MODE_LAST ? BM_FILTER_MODE_DMENU : mode);
|
||||
|
||||
if (oldMode != mode)
|
||||
_bmMenuFilter(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,13 +184,7 @@ const char* bmMenuGetTitle(const bmMenu *menu)
|
||||
int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index)
|
||||
{
|
||||
assert(menu);
|
||||
|
||||
int ret = _bmItemListAddItemAt(&menu->items, item, index);
|
||||
|
||||
if (ret)
|
||||
_bmMenuFilter(menu);
|
||||
|
||||
return ret;
|
||||
return _bmItemListAddItemAt(&menu->items, item, index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -220,12 +196,7 @@ int bmMenuAddItemAt(bmMenu *menu, bmItem *item, unsigned int index)
|
||||
*/
|
||||
int bmMenuAddItem(bmMenu *menu, bmItem *item)
|
||||
{
|
||||
int ret = _bmItemListAddItem(&menu->items, item);
|
||||
|
||||
if (ret)
|
||||
_bmMenuFilter(menu);
|
||||
|
||||
return ret;
|
||||
return _bmItemListAddItem(&menu->items, item);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,7 +221,6 @@ int bmMenuRemoveItemAt(bmMenu *menu, unsigned int index)
|
||||
if (ret) {
|
||||
_bmItemListRemoveItem(&menu->selection, item);
|
||||
_bmItemListRemoveItem(&menu->filtered, item);
|
||||
_bmMenuFilter(menu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -274,7 +244,6 @@ int bmMenuRemoveItem(bmMenu *menu, bmItem *item)
|
||||
if (ret) {
|
||||
_bmItemListRemoveItem(&menu->selection, item);
|
||||
_bmItemListRemoveItem(&menu->filtered, item);
|
||||
_bmMenuFilter(menu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -393,7 +362,6 @@ int bmMenuSetItems(bmMenu *menu, const bmItem **items, unsigned int nmemb)
|
||||
if (ret) {
|
||||
_bmItemListFreeList(&menu->selection);
|
||||
_bmItemListFreeList(&menu->filtered);
|
||||
_bmMenuFilter(menu);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -447,6 +415,57 @@ void bmMenuRender(const bmMenu *menu)
|
||||
menu->renderApi.render(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger filtering of menu manually.
|
||||
* This is useful when adding new items and want to dynamically see them filtered.
|
||||
*
|
||||
* Do note that filtering might be heavy, so you should only call it after batch manipulation of items.
|
||||
* Not after manipulation of each single item.
|
||||
*
|
||||
* @param menu bmMenu instance which to filter.
|
||||
*/
|
||||
void bmMenuFilter(bmMenu *menu)
|
||||
{
|
||||
assert(menu);
|
||||
|
||||
char addition = 0;
|
||||
size_t len = strlen(menu->filter);
|
||||
|
||||
if (!len || !menu->items.list || menu->items.count <= 0) {
|
||||
_bmItemListFreeList(&menu->filtered);
|
||||
|
||||
if (menu->oldFilter)
|
||||
free(menu->oldFilter);
|
||||
|
||||
menu->oldFilter = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu->oldFilter) {
|
||||
size_t oldLen = strlen(menu->oldFilter);
|
||||
addition = (oldLen < len && !memcmp(menu->oldFilter, menu->filter, oldLen));
|
||||
}
|
||||
|
||||
if (menu->oldFilter && addition && menu->filtered.count <= 0)
|
||||
return;
|
||||
|
||||
if (menu->oldFilter && !strcmp(menu->filter, menu->oldFilter))
|
||||
return;
|
||||
|
||||
unsigned int count, selected;
|
||||
bmItem **filtered = filterFunc[menu->filterMode](menu, addition, &count, &selected);
|
||||
|
||||
_bmItemListSetItemsNoCopy(&menu->filtered, filtered, count);
|
||||
menu->index = selected;
|
||||
|
||||
if (count) {
|
||||
if (menu->oldFilter)
|
||||
free(menu->oldFilter);
|
||||
|
||||
menu->oldFilter = _bmStrdup(menu->filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Poll key and unicode from underlying UI toolkit.
|
||||
*
|
||||
@ -485,8 +504,6 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
|
||||
unsigned int itemsCount;
|
||||
bmMenuGetFilteredItems(menu, &itemsCount);
|
||||
|
||||
char *oldFilter = _bmStrdup(menu->filter);
|
||||
|
||||
switch (key) {
|
||||
case BM_KEY_LEFT:
|
||||
{
|
||||
@ -619,11 +636,7 @@ bmRunResult bmMenuRunWithKey(bmMenu *menu, bmKey key, unsigned int unicode)
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (oldFilter && strcmp(oldFilter, menu->filter))
|
||||
_bmMenuFilter(menu);
|
||||
|
||||
if (oldFilter)
|
||||
free(oldFilter);
|
||||
bmMenuFilter(menu);
|
||||
|
||||
switch (key) {
|
||||
case BM_KEY_RETURN: return BM_RUN_RESULT_SELECTED;
|
||||
|
Loading…
Reference in New Issue
Block a user