diff options
author | adnano <me@adnano.co> | 2022-04-17 13:15:07 -0400 |
---|---|---|
committer | adnano <me@adnano.co> | 2022-04-17 13:15:09 -0400 |
commit | decd2452deddb9c8a4bdcb1a0184cd056b01a5a0 (patch) | |
tree | 1e9d1d7a20fb115c5ee30a29b225075a8ceacd4e | |
parent | 2f1c189d535c2d8dce74ec44c670305f00e4a30c (diff) | |
download | wmenu-decd2452deddb9c8a4bdcb1a0184cd056b01a5a0.tar.gz |
Implement vertical list support
Implements: https://todo.sr.ht/~adnano/wmenu/1
-rw-r--r-- | main.c | 291 |
1 files changed, 218 insertions, 73 deletions
@@ -61,6 +61,7 @@ struct menu_state { int width; int height; + int line_height; int padding; int inputw; int promptw; @@ -69,6 +70,7 @@ struct menu_state { bool bottom; int (*fstrncmp)(const char *, const char *, size_t); char *font; + bool vertical; int lines; char *prompt; uint32_t background, foreground; @@ -112,7 +114,30 @@ int render_text(struct menu_state *state, cairo_t *cairo, const char *str, int text_width, text_height; get_text_size(cairo, state->font, &text_width, &text_height, NULL, 1, str); - int text_y = (state->height / 2.0) - (text_height / 2.0); + int text_y = (state->line_height / 2.0) - (text_height / 2.0); + + if (background) { + int bg_width = text_width + left_padding + right_padding; + cairo_set_source_u32(cairo, background); + cairo_rectangle(cairo, x, y, bg_width, height); + cairo_fill(cairo); + } + + cairo_move_to(cairo, x + left_padding, y + text_y); + cairo_set_source_u32(cairo, foreground); + pango_printf(cairo, state->font, 1, str); + + return x + text_width + left_padding + right_padding; +} + +int render_horizontal_item(struct menu_state *state, cairo_t *cairo, const char *str, + int x, int y, int width, int height, + uint32_t foreground, uint32_t background, + int left_padding, int right_padding) { + + int text_width, text_height; + get_text_size(cairo, state->font, &text_width, &text_height, NULL, 1, str); + int text_y = (state->line_height / 2.0) - (text_height / 2.0); if (x + left_padding + text_width > width) { return -1; @@ -120,11 +145,11 @@ int render_text(struct menu_state *state, cairo_t *cairo, const char *str, if (background) { int bg_width = text_width + left_padding + right_padding; cairo_set_source_u32(cairo, background); - cairo_rectangle(cairo, x, 0, bg_width, height); + cairo_rectangle(cairo, x, y, bg_width, height); cairo_fill(cairo); } - cairo_move_to(cairo, x + left_padding, text_y); + cairo_move_to(cairo, x + left_padding, y + text_y); cairo_set_source_u32(cairo, foreground); pango_printf(cairo, state->font, 1, str); } @@ -132,61 +157,117 @@ int render_text(struct menu_state *state, cairo_t *cairo, const char *str, return x + text_width + left_padding + right_padding; } +void render_vertical_item(struct menu_state *state, cairo_t *cairo, const char *str, + int x, int y, int width, int height, + uint32_t foreground, uint32_t background, + int left_padding) { + + int text_height; + get_text_size(cairo, state->font, NULL, &text_height, NULL, 1, str); + int text_y = (state->line_height / 2.0) - (text_height / 2.0); + + if (background) { + int bg_width = state->width - x; + cairo_set_source_u32(cairo, background); + cairo_rectangle(cairo, x, y, bg_width, height); + cairo_fill(cairo); + } + + cairo_move_to(cairo, x + left_padding, y + text_y); + cairo_set_source_u32(cairo, foreground); + pango_printf(cairo, state->font, 1, str); +} + void scroll_matches(struct menu_state *state) { if (!state->matches) { return; } - // Calculate available space - int padding = state->padding; - int width = state->width - state->inputw - state->promptw - - state->left_arrow - state->right_arrow; - if (state->leftmost == NULL) { - state->leftmost = state->matches; + if (state->vertical) { + if (state->leftmost == NULL) { + state->leftmost = state->matches; + if (state->rightmost == NULL) { + int offs = 0; + struct menu_item *item; + for (item = state->matches; item->left != state->selection; item = item->right) { + offs += state->line_height; + if (offs >= state->height) { + state->leftmost = item->left; + offs = state->height - offs; + } + } + } else { + int offs = 0; + struct menu_item *item; + for (item = state->rightmost; item; item = item->left) { + offs += state->line_height; + if (offs >= state->height) { + state->leftmost = item->right; + break; + } + } + } + } if (state->rightmost == NULL) { + state->rightmost = state->matches; int offs = 0; struct menu_item *item; - for (item = state->matches; item->left != state->selection; item = item->right) { - offs += item->width + 2 * padding; - if (offs >= width) { - state->leftmost = item->left; - offs = width - offs; + for (item = state->leftmost; item; item = item->right) { + offs += state->line_height; + if (offs >= state->height) { + break; } + state->rightmost = item; } - } else { + } + } else { + // Calculate available space + int padding = state->padding; + int width = state->width - state->inputw - state->promptw + - state->left_arrow - state->right_arrow; + if (state->leftmost == NULL) { + state->leftmost = state->matches; + if (state->rightmost == NULL) { + int offs = 0; + struct menu_item *item; + for (item = state->matches; item->left != state->selection; item = item->right) { + offs += item->width + 2 * padding; + if (offs >= width) { + state->leftmost = item->left; + offs = width - offs; + } + } + } else { + int offs = 0; + struct menu_item *item; + for (item = state->rightmost; item; item = item->left) { + offs += item->width + 2 * padding; + if (offs >= width) { + state->leftmost = item->right; + break; + } + } + } + } + if (state->rightmost == NULL) { + state->rightmost = state->matches; int offs = 0; struct menu_item *item; - for (item = state->rightmost; item; item = item->left) { + for (item = state->leftmost; item; item = item->right) { offs += item->width + 2 * padding; if (offs >= width) { - state->leftmost = item->right; break; } + state->rightmost = item; } } } - if (state->rightmost == NULL) { - state->rightmost = state->matches; - int offs = 0; - struct menu_item *item; - for (item = state->leftmost; item; item = item->right) { - offs += item->width + 2 * padding; - if (offs >= width) { - break; - } - state->rightmost = item; - } - } } void render_to_cairo(struct menu_state *state, cairo_t *cairo) { int width = state->width; - int height = state->height; int padding = state->padding; - int text_height; - get_text_size(cairo, state->font, NULL, &text_height, NULL, 1, ""); - int y = (height / 2.0) - (text_height / 2.0); cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); cairo_set_source_u32(cairo, state->background); @@ -197,7 +278,7 @@ void render_to_cairo(struct menu_state *state, cairo_t *cairo) { // Draw prompt if (state->prompt) { state->promptw = render_text(state, cairo, state->prompt, - 0, y, state->width, state->height, + 0, 0, state->width, state->line_height, state->promptfg, state->promptbg, padding, padding/2); x += state->promptw; @@ -205,12 +286,12 @@ void render_to_cairo(struct menu_state *state, cairo_t *cairo) { // Draw background cairo_set_source_u32(cairo, state->background); - cairo_rectangle(cairo, x, 0, 300, height); + cairo_rectangle(cairo, x, 0, 300, state->height); cairo_fill(cairo); // Draw input render_text(state, cairo, state->text, - x, y, state->width, state->height, + x, 0, state->width, state->line_height, state->foreground, 0, padding, padding); // Draw cursor @@ -222,7 +303,7 @@ void render_to_cairo(struct menu_state *state, cairo_t *cairo) { - text_width(cairo, state->font, &state->text[state->cursor]) - cursor_width / 2; cairo_rectangle(cairo, cursor_pos, cursor_margin, cursor_width, - state->height - 2 * cursor_margin); + state->line_height - 2 * cursor_margin); cairo_fill(cairo); } @@ -230,42 +311,59 @@ void render_to_cairo(struct menu_state *state, cairo_t *cairo) { return; } - // Leave room for input - x += state->inputw; - - // Calculate scroll indicator widths - state->left_arrow = text_width(cairo, state->font, "<") + 2 * padding; - state->right_arrow = text_width(cairo, state->font, ">") + 2 * padding; - - // Remember scroll indicator position - int left_arrow_pos = x + padding; - x += state->left_arrow; - - // Draw matches - bool scroll_right = false; - struct menu_item *item; - for (item = state->leftmost; item; item = item->right) { - uint32_t bg_color = state->selection == item ? state->selectionbg : state->background; - uint32_t fg_color = state->selection == item ? state->selectionfg : state->foreground; - x = render_text(state, cairo, item->text, - x, y, width - state->right_arrow, height, - fg_color, bg_color, padding, padding); - if (x == -1) { - scroll_right = true; - break; + if (state->vertical) { + // Draw matches vertically + int y = state->line_height; + struct menu_item *item; + for (item = state->leftmost; item; item = item->right) { + uint32_t bg_color = state->selection == item ? state->selectionbg : state->background; + uint32_t fg_color = state->selection == item ? state->selectionfg : state->foreground; + render_vertical_item(state, cairo, item->text, + x, y, width, state->line_height, + fg_color, bg_color, padding); + y += state->line_height; + if (y >= state->height) { + break; + } } - } + } else { + // Leave room for input + x += state->inputw; - // Draw left scroll indicator if necessary - if (state->leftmost != state->matches) { - cairo_move_to(cairo, left_arrow_pos, y); - pango_printf(cairo, state->font, 1, "<"); - } + // Calculate scroll indicator widths + state->left_arrow = text_width(cairo, state->font, "<") + 2 * padding; + state->right_arrow = text_width(cairo, state->font, ">") + 2 * padding; + + // Remember scroll indicator position + int left_arrow_pos = x + padding; + x += state->left_arrow; + + // Draw matches horizontally + bool scroll_right = false; + struct menu_item *item; + for (item = state->leftmost; item; item = item->right) { + uint32_t bg_color = state->selection == item ? state->selectionbg : state->background; + uint32_t fg_color = state->selection == item ? state->selectionfg : state->foreground; + x = render_horizontal_item(state, cairo, item->text, + x, 0, width - state->right_arrow, state->line_height, + fg_color, bg_color, padding, padding); + if (x == -1) { + scroll_right = true; + break; + } + } + + // Draw left scroll indicator if necessary + if (state->leftmost != state->matches) { + cairo_move_to(cairo, left_arrow_pos, 0); + pango_printf(cairo, state->font, 1, "<"); + } - // Draw right scroll indicator if necessary - if (scroll_right) { - cairo_move_to(cairo, width - state->right_arrow + padding, y); - pango_printf(cairo, state->font, 1, ">"); + // Draw right scroll indicator if necessary + if (scroll_right) { + cairo_move_to(cairo, width - state->right_arrow + padding, 0); + pango_printf(cairo, state->font, 1, ">"); + } } } @@ -423,6 +521,9 @@ void keypress(struct menu_state *state, enum wl_keyboard_key_state key_state, } break; case XKB_KEY_Left: + if (state->vertical) { + break; + } if (state->cursor && (!state->selection || !state->selection->left)) { state->cursor = nextrune(state, -1); render_frame(state); @@ -438,6 +539,46 @@ void keypress(struct menu_state *state, enum wl_keyboard_key_state key_state, } break; case XKB_KEY_Right: + if (state->vertical) { + break; + } + if (state->cursor < len) { + state->cursor = nextrune(state, +1); + render_frame(state); + } else if (state->cursor == len) { + if (state->selection && state->selection->right) { + if (state->selection == state->rightmost) { + state->leftmost = state->selection->right; + state->rightmost = NULL; + } + state->selection = state->selection->right; + scroll_matches(state); + render_frame(state); + } + } + break; + case XKB_KEY_Up: + if (!state->vertical) { + break; + } + if (state->cursor && (!state->selection || !state->selection->left)) { + state->cursor = nextrune(state, -1); + render_frame(state); + } + if (state->selection && state->selection->left) { + if (state->selection == state->leftmost) { + state->rightmost = state->selection->left; + state->leftmost = NULL; + } + state->selection = state->selection->left; + scroll_matches(state); + render_frame(state); + } + break; + case XKB_KEY_Down: + if (!state->vertical) { + break; + } if (state->cursor < len) { state->cursor = nextrune(state, +1); render_frame(state); @@ -769,7 +910,11 @@ void read_stdin(struct menu_state *state) { static void menu_init(struct menu_state *state) { int height = get_font_height(state->font); - state->height = height + 2; + state->line_height = height + 2; + state->height = state->line_height; + if (state->vertical) { + state->height += state->height * state->lines; + } state->padding = height / 2; state->display = wl_display_connect(NULL); @@ -856,6 +1001,7 @@ int main(int argc, char **argv) { struct menu_state state = { .fstrncmp = strncmp, .font = "monospace 10", + .vertical = false, .background = 0x222222ff, .foreground = 0xbbbbbbff, .promptbg = 0x005577ff, @@ -885,8 +1031,7 @@ int main(int argc, char **argv) { state.font = optarg; break; case 'l': - // TODO - fputs("warning: -l unimplemented\n", stderr); + state.vertical = true; state.lines = atoi(optarg); break; case 'o': |