mirror of
https://github.com/Cloudef/bemenu
synced 2024-11-23 01:12:01 +01:00
Add basic vim bindings
This commit is contained in:
parent
e8e9957719
commit
bcf53bcb25
@ -64,7 +64,7 @@ util.a: private override LDFLAGS += -fPIC
|
||||
util.a: lib/util.c lib/internal.h
|
||||
|
||||
libbemenu.so: private override LDLIBS += -ldl
|
||||
libbemenu.so: lib/bemenu.h lib/internal.h lib/filter.c lib/item.c lib/library.c lib/list.c lib/menu.c util.a cdl.a
|
||||
libbemenu.so: lib/bemenu.h lib/internal.h lib/filter.c lib/item.c lib/library.c lib/list.c lib/menu.c lib/vim.c util.a cdl.a
|
||||
|
||||
bemenu-renderer-curses.so: private override LDLIBS += $(shell pkg-config --libs ncursesw) -lm
|
||||
bemenu-renderer-curses.so: private override CPPFLAGS += $(shell pkg-config --cflags-only-I ncursesw)
|
||||
|
@ -181,6 +181,7 @@ usage(FILE *out, const char *name)
|
||||
" -C, --no-cursor ignore cursor events.\n"
|
||||
" -T, --no-touch ignore touch events.\n"
|
||||
" -K, --no-keyboard ignore keyboard events.\n"
|
||||
" --vim use vim bindings.\n"
|
||||
" --scrollbar display scrollbar. (none (default), always, autohide)\n"
|
||||
" --accept-single immediately return if there is only one item.\n"
|
||||
" --ifne only display menu if there are items.\n"
|
||||
@ -308,6 +309,7 @@ do_getopt(struct client *client, int *argc, char **argv[])
|
||||
{ "scb", required_argument, 0, 0x114 },
|
||||
{ "scf", required_argument, 0, 0x115 },
|
||||
{ "bdr", required_argument, 0, 0x121 },
|
||||
{ "vim", no_argument, 0, 0x128 },
|
||||
|
||||
{ "disco", no_argument, 0, 0x116 },
|
||||
{ 0, 0, 0, 0 }
|
||||
@ -479,6 +481,9 @@ do_getopt(struct client *client, int *argc, char **argv[])
|
||||
case 0x121:
|
||||
client->colors[BM_COLOR_BORDER] = optarg;
|
||||
break;
|
||||
case 0x128:
|
||||
client->use_vim_bindings = true;
|
||||
break;
|
||||
|
||||
case 0x116:
|
||||
disco();
|
||||
@ -534,6 +539,7 @@ menu_with_options(struct client *client)
|
||||
bm_menu_set_password(menu, client->password);
|
||||
bm_menu_set_width(menu, client->hmargin_size, client->width_factor);
|
||||
bm_menu_set_border_size(menu, client->border_size);
|
||||
bm_menu_set_use_vim_bindings(menu, client->use_vim_bindings);
|
||||
|
||||
if (client->center) {
|
||||
bm_menu_set_align(menu, BM_ALIGN_CENTER);
|
||||
|
@ -36,6 +36,7 @@ struct client {
|
||||
bool force_fork, fork;
|
||||
bool no_exec;
|
||||
bool password;
|
||||
bool use_vim_bindings;
|
||||
char *monitor_name;
|
||||
};
|
||||
|
||||
|
@ -788,6 +788,13 @@ BM_PUBLIC void bm_menu_set_spacing(struct bm_menu *menu, bool spacing);
|
||||
BM_PUBLIC bool bm_menu_get_password(struct bm_menu *menu);
|
||||
|
||||
|
||||
/**
|
||||
* Should the menu use vim bindings?
|
||||
* @param menu bm_menu instance to enable/disable vim bindings
|
||||
*/
|
||||
BM_PUBLIC void bm_menu_set_use_vim_bindings(struct bm_menu *menu, bool use_vim_bindings);
|
||||
|
||||
|
||||
/** @} Properties */
|
||||
|
||||
/**
|
||||
|
@ -411,6 +411,10 @@ struct bm_menu {
|
||||
* Is the menu needing a redraw ?
|
||||
*/
|
||||
bool dirty;
|
||||
|
||||
bool use_vim_bindings;
|
||||
char vim_mode;
|
||||
uint32_t vim_last_key;
|
||||
};
|
||||
|
||||
/* library.c */
|
||||
|
25
lib/menu.c
25
lib/menu.c
@ -6,6 +6,8 @@
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "vim.h"
|
||||
|
||||
/**
|
||||
* Default font.
|
||||
*/
|
||||
@ -53,6 +55,10 @@ bm_menu_new(const char *renderer)
|
||||
|
||||
menu->dirty = true;
|
||||
|
||||
menu->use_vim_bindings = false;
|
||||
menu->vim_mode = 'i';
|
||||
menu->vim_last_key = 0;
|
||||
|
||||
uint32_t count;
|
||||
const struct bm_renderer **renderers = bm_get_renderers(&count);
|
||||
|
||||
@ -683,6 +689,11 @@ bm_menu_set_selected_items(struct bm_menu *menu, struct bm_item **items, uint32_
|
||||
return list_set_items_no_copy(&menu->selection, new_items, nmemb);
|
||||
}
|
||||
|
||||
void
|
||||
bm_menu_set_use_vim_bindings(struct bm_menu *menu, bool use_vim_bindings){
|
||||
menu->use_vim_bindings = use_vim_bindings;
|
||||
}
|
||||
|
||||
struct bm_item**
|
||||
bm_menu_get_selected_items(const struct bm_menu *menu, uint32_t *out_nmemb)
|
||||
{
|
||||
@ -939,6 +950,20 @@ bm_menu_run_with_key(struct bm_menu *menu, enum bm_key key, uint32_t unicode)
|
||||
if (key != BM_KEY_NONE)
|
||||
menu->dirty = true;
|
||||
|
||||
if(menu->use_vim_bindings){
|
||||
enum bm_vim_code code = bm_vim_key_press(menu, key, unicode, count, displayed);
|
||||
|
||||
switch(code){
|
||||
case BM_VIM_CONSUME:
|
||||
return BM_RUN_RESULT_RUNNING;
|
||||
case BM_VIM_EXIT:
|
||||
list_free_list(&menu->selection);
|
||||
return BM_RUN_RESULT_CANCEL;
|
||||
case BM_VIM_IGNORE:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case BM_KEY_LEFT:
|
||||
if (menu->filter) {
|
||||
|
335
lib/vim.c
Normal file
335
lib/vim.c
Normal file
@ -0,0 +1,335 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "vim.h"
|
||||
|
||||
|
||||
#define LOG(x)
|
||||
//#define LOG(x) printf(x)
|
||||
|
||||
static enum bm_vim_code vim_on_first_key(struct bm_menu *menu, uint32_t unicode, uint32_t item_count, uint32_t items_displayed);
|
||||
static enum bm_vim_code vim_on_second_key(struct bm_menu *menu, uint32_t unicode, uint32_t item_count, uint32_t items_displayed);
|
||||
|
||||
|
||||
static void move_left(struct bm_menu *menu){
|
||||
if(menu->filter && menu->cursor > 0){
|
||||
uint32_t old_cursor = menu->cursor;
|
||||
menu->cursor -= bm_utf8_rune_prev(menu->filter, menu->cursor);
|
||||
menu->curses_cursor -= bm_utf8_rune_width(menu->filter + menu->cursor, old_cursor - menu->cursor);
|
||||
}
|
||||
}
|
||||
|
||||
static void move_right(struct bm_menu *menu, size_t filter_length){
|
||||
if(menu->cursor < filter_length){
|
||||
uint32_t old_cursor = menu->cursor;
|
||||
menu->cursor += bm_utf8_rune_next(menu->filter, menu->cursor);
|
||||
menu->curses_cursor += bm_utf8_rune_width(menu->filter + old_cursor, menu->cursor - old_cursor);
|
||||
}
|
||||
}
|
||||
|
||||
static void move_line_start(struct bm_menu *menu){
|
||||
menu->cursor = 0;
|
||||
menu->curses_cursor = 0;
|
||||
}
|
||||
|
||||
static void move_line_end(struct bm_menu *menu){
|
||||
menu->cursor = (menu->filter ? strlen(menu->filter) : 0);
|
||||
menu->curses_cursor = (menu->filter ? bm_utf8_string_screen_width(menu->filter) : 0);
|
||||
}
|
||||
|
||||
static void menu_next(struct bm_menu *menu, uint32_t count, bool wrap){
|
||||
if (menu->index < count - 1) {
|
||||
bm_menu_set_highlighted_index(menu, menu->index + 1);
|
||||
} else if (wrap) {
|
||||
bm_menu_set_highlighted_index(menu, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_prev(struct bm_menu *menu, uint32_t count, bool wrap){
|
||||
if (menu->index > 0) {
|
||||
bm_menu_set_highlighted_index(menu, menu->index - 1);
|
||||
} else if (wrap) {
|
||||
bm_menu_set_highlighted_index(menu, count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_first(struct bm_menu *menu, uint32_t count){
|
||||
if(count > 0){
|
||||
bm_menu_set_highlighted_index(menu, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_last(struct bm_menu *menu, uint32_t count){
|
||||
if(count > 0){
|
||||
bm_menu_set_highlighted_index(menu, count - 1);
|
||||
}
|
||||
}
|
||||
|
||||
static void menu_page_down(struct bm_menu *menu, uint32_t count, uint32_t displayed){
|
||||
bm_menu_set_highlighted_index(menu, (menu->index + displayed >= count ? count - 1 : menu->index + (displayed - 1)));
|
||||
}
|
||||
|
||||
static void menu_page_up(struct bm_menu *menu, uint32_t displayed){
|
||||
bm_menu_set_highlighted_index(menu, (menu->index < displayed ? 0 : menu->index - (displayed - 1)));
|
||||
}
|
||||
|
||||
static void move_word(struct bm_menu *menu){
|
||||
if(!menu->filter) return;
|
||||
|
||||
size_t filter_length = strlen(menu->filter);
|
||||
while (menu->cursor < filter_length && !isspace(menu->filter[menu->cursor])) move_right(menu, filter_length);
|
||||
while (menu->cursor < filter_length && isspace(menu->filter[menu->cursor])) move_right(menu, filter_length);
|
||||
}
|
||||
|
||||
static void move_word_back(struct bm_menu *menu){
|
||||
while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])) move_left(menu);
|
||||
while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])) move_left(menu);
|
||||
}
|
||||
|
||||
static void move_word_end(struct bm_menu *menu){
|
||||
size_t filter_length = strlen(menu->filter);
|
||||
size_t old_cursor = menu->cursor;
|
||||
size_t old_curses_cursor = menu->curses_cursor;
|
||||
|
||||
while (menu->cursor < filter_length && isspace(menu->filter[menu->cursor + 1])) move_right(menu, filter_length);
|
||||
while (menu->cursor < filter_length && !isspace(menu->filter[menu->cursor + 1])) move_right(menu, filter_length);
|
||||
|
||||
if(menu->cursor == filter_length){
|
||||
menu->cursor = old_cursor;
|
||||
menu->curses_cursor = old_curses_cursor;
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_char(struct bm_menu *menu){
|
||||
if(menu->filter){
|
||||
if(menu->cursor < strlen(menu->filter)){
|
||||
size_t width;
|
||||
bm_utf8_rune_remove(menu->filter, menu->cursor + 1, &width);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_word(struct bm_menu *menu){
|
||||
if(!menu->filter) return;
|
||||
|
||||
size_t filter_length = strlen(menu->filter);
|
||||
while (menu->cursor < filter_length && !isspace(menu->filter[menu->cursor])) delete_char(menu);
|
||||
while (menu->cursor < filter_length && isspace(menu->filter[menu->cursor])) delete_char(menu);
|
||||
}
|
||||
|
||||
static void delete_word_back(struct bm_menu *menu){
|
||||
while (menu->cursor > 0 && isspace(menu->filter[menu->cursor - 1])){
|
||||
move_left(menu);
|
||||
delete_char(menu);
|
||||
}
|
||||
|
||||
while (menu->cursor > 0 && !isspace(menu->filter[menu->cursor - 1])){
|
||||
move_left(menu);
|
||||
delete_char(menu);
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_line(struct bm_menu *menu){
|
||||
if(menu->filter){
|
||||
menu->filter[0] = 0;
|
||||
menu->cursor = 0;
|
||||
menu->curses_cursor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_to_line_end(struct bm_menu *menu){
|
||||
if(menu->filter){
|
||||
menu->filter[menu->cursor] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_to_line_start(struct bm_menu *menu){
|
||||
if(menu->filter){
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wrestrict"
|
||||
strcpy(menu->filter, menu->filter + menu->cursor);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
menu->cursor = 0;
|
||||
menu->curses_cursor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum bm_vim_code bm_vim_key_press(struct bm_menu *menu, enum bm_key key, uint32_t unicode, uint32_t item_count, uint32_t items_displayed){
|
||||
if(key == BM_KEY_ESCAPE && unicode == 99) return BM_VIM_EXIT;
|
||||
|
||||
if(menu->vim_mode == 'n'){
|
||||
if(unicode == 0 || unicode > 128) return BM_VIM_IGNORE;
|
||||
|
||||
if(menu->vim_last_key == 0) return vim_on_first_key(menu, unicode, item_count, items_displayed);
|
||||
else return vim_on_second_key(menu, unicode, item_count, items_displayed);
|
||||
} else if(menu->vim_mode == 'i'){
|
||||
if(key == BM_KEY_ESCAPE && unicode == 0){
|
||||
menu->vim_mode = 'n';
|
||||
return BM_VIM_CONSUME;
|
||||
}
|
||||
}
|
||||
|
||||
return BM_VIM_IGNORE;
|
||||
}
|
||||
|
||||
static enum bm_vim_code vim_on_first_key(struct bm_menu *menu, uint32_t unicode, uint32_t item_count, uint32_t items_displayed){
|
||||
size_t filter_length;
|
||||
|
||||
switch(unicode){
|
||||
case 'q':
|
||||
LOG("quit\n");
|
||||
return BM_VIM_EXIT;
|
||||
case 'i':
|
||||
LOG("insert mode\n");
|
||||
goto insert_action_executed;
|
||||
case 'I':
|
||||
LOG("insert line beginning\n");
|
||||
move_line_start(menu);
|
||||
goto insert_action_executed;
|
||||
case 'a':
|
||||
LOG("append\n");
|
||||
filter_length = strlen(menu->filter);
|
||||
move_right(menu, filter_length);
|
||||
goto insert_action_executed;
|
||||
case 'A':
|
||||
LOG("append line end\n");
|
||||
move_line_end(menu);
|
||||
goto insert_action_executed;
|
||||
case 'h':
|
||||
LOG("left\n");
|
||||
move_left(menu);
|
||||
goto action_executed;
|
||||
case 'n':
|
||||
case 'j':
|
||||
LOG("down\n");
|
||||
menu_next(menu, item_count, menu->wrap);
|
||||
goto action_executed;
|
||||
case 'p':
|
||||
case 'k':
|
||||
LOG("up\n");
|
||||
menu_prev(menu, item_count, menu->wrap);
|
||||
goto action_executed;
|
||||
case 'l':
|
||||
LOG("right\n");
|
||||
filter_length = strlen(menu->filter);
|
||||
move_right(menu, filter_length);
|
||||
goto action_executed;
|
||||
case 'w':
|
||||
LOG("move word\n");
|
||||
move_word(menu);
|
||||
goto action_executed;
|
||||
case 'b':
|
||||
LOG("move word back\n");
|
||||
move_word_back(menu);
|
||||
goto action_executed;
|
||||
case 'e':
|
||||
LOG("move word end\n");
|
||||
move_word_end(menu);
|
||||
goto action_executed;
|
||||
case 'x':
|
||||
LOG("delete char\n");
|
||||
delete_char(menu);
|
||||
goto action_executed;
|
||||
case '0':
|
||||
LOG("move line start\n");
|
||||
move_line_start(menu);
|
||||
goto action_executed;
|
||||
case '$':
|
||||
LOG("move line end\n");
|
||||
move_line_end(menu);
|
||||
goto action_executed;
|
||||
case 'g':
|
||||
LOG("move line end\n");
|
||||
menu_first(menu, item_count);
|
||||
goto action_executed;
|
||||
case 'G':
|
||||
LOG("move line end\n");
|
||||
menu_last(menu, item_count);
|
||||
goto action_executed;
|
||||
case 'F':
|
||||
LOG("menu page down\n");
|
||||
menu_page_down(menu, item_count, items_displayed);
|
||||
goto action_executed;
|
||||
case 'B':
|
||||
LOG("menu page up\n");
|
||||
menu_page_up(menu, items_displayed);
|
||||
goto action_executed;
|
||||
case 'c':
|
||||
case 'd':
|
||||
menu->vim_last_key = unicode;
|
||||
return BM_VIM_CONSUME;
|
||||
default:
|
||||
menu->vim_last_key = 0;
|
||||
return BM_VIM_CONSUME;
|
||||
}
|
||||
|
||||
insert_action_executed:
|
||||
menu->vim_mode = 'i';
|
||||
action_executed:
|
||||
menu->vim_last_key = 0;
|
||||
return BM_VIM_CONSUME;
|
||||
}
|
||||
|
||||
static enum bm_vim_code vim_on_second_key(struct bm_menu *menu, uint32_t unicode, uint32_t item_count, uint32_t items_displayed){
|
||||
if(menu->vim_last_key == 'd'){
|
||||
switch(unicode){
|
||||
case 'd':
|
||||
LOG("Delete line\n");
|
||||
delete_line(menu);
|
||||
goto action_executed;
|
||||
case 'w':
|
||||
LOG("Delete word\n");
|
||||
delete_word(menu);
|
||||
goto action_executed;
|
||||
case 'b':
|
||||
LOG("Delete word back\n");
|
||||
delete_word_back(menu);
|
||||
goto action_executed;
|
||||
case '$':
|
||||
LOG("Delete to line end\n");
|
||||
delete_to_line_end(menu);
|
||||
goto action_executed;
|
||||
case '0':
|
||||
LOG("Delete to line start\n");
|
||||
delete_to_line_start(menu);
|
||||
goto action_executed;
|
||||
}
|
||||
} else if(menu->vim_last_key == 'c'){
|
||||
switch(unicode){
|
||||
case 'c':
|
||||
LOG("Change line\n");
|
||||
delete_line(menu);
|
||||
goto insert_action_executed;
|
||||
case 'w':
|
||||
LOG("Change word\n");
|
||||
delete_word(menu);
|
||||
goto insert_action_executed;
|
||||
case 'b':
|
||||
LOG("Change word back\n");
|
||||
delete_word_back(menu);
|
||||
goto insert_action_executed;
|
||||
case '$':
|
||||
LOG("Change to line end\n");
|
||||
delete_to_line_end(menu);
|
||||
goto insert_action_executed;
|
||||
case '0':
|
||||
LOG("Change to line start\n");
|
||||
delete_to_line_start(menu);
|
||||
goto insert_action_executed;
|
||||
}
|
||||
}
|
||||
|
||||
return vim_on_first_key(menu, unicode, item_count, items_displayed);
|
||||
|
||||
insert_action_executed:
|
||||
menu->vim_mode = 'i';
|
||||
action_executed:
|
||||
menu->vim_last_key = 0;
|
||||
return BM_VIM_CONSUME;
|
||||
}
|
14
lib/vim.h
Normal file
14
lib/vim.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef _VIM_H_
|
||||
#define _VIM_H_
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
enum bm_vim_code {
|
||||
BM_VIM_CONSUME,
|
||||
BM_VIM_IGNORE,
|
||||
BM_VIM_EXIT
|
||||
};
|
||||
|
||||
enum bm_vim_code bm_vim_key_press(struct bm_menu *menu, enum bm_key key, uint32_t unicode, uint32_t item_count, uint32_t items_displayed);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user