From 8cbadb61604667b407b53ee1258433994fa4765a Mon Sep 17 00:00:00 2001 From: coasteen Date: Thu, 9 Jul 2026 10:42:28 +0330 Subject: init --- wmenu/render.c | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100755 wmenu/render.c (limited to 'wmenu/render.c') diff --git a/wmenu/render.c b/wmenu/render.c new file mode 100755 index 0000000..e86d93b --- /dev/null +++ b/wmenu/render.c @@ -0,0 +1,212 @@ +#define _POSIX_C_SOURCE 200809L +#include +#include +#include + +#include "render.h" + +#include "menu.h" +#include "pango.h" +#include "pool-buffer.h" +#include "wayland.h" + +// Calculate text widths. +void calc_widths(struct menu *menu) { + struct wl_context *context = menu->context; + int scale = context_get_scale(context); + cairo_surface_set_device_scale(menu->test_surface, scale, scale); + cairo_set_antialias(menu->test_cairo, CAIRO_ANTIALIAS_BEST); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_set_font_options(menu->test_cairo, fo); + cairo_font_options_destroy(fo); + cairo_t *cairo = menu->test_cairo; + + // Calculate prompt width + if (menu->prompt) { + menu->promptw = text_width(cairo, menu->font, menu->prompt) + menu->padding + menu->padding/2; + } else { + menu->promptw = 0; + } + + // Calculate scroll indicator widths + menu->left_arrow = text_width(cairo, menu->font, "<") + 2 * menu->padding; + menu->right_arrow = text_width(cairo, menu->font, ">") + 2 * menu->padding; + + // Calculate item widths and input area width + for (size_t i = 0; i < menu->item_count; i++) { + struct item *item = &menu->items[i]; + item->width = text_width(cairo, menu->font, item->text); + if (item->width > menu->inputw) { + menu->inputw = item->width; + } + } +} + +static void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { + cairo_set_source_rgba(cairo, + (color >> (3*8) & 0xFF) / 255.0, + (color >> (2*8) & 0xFF) / 255.0, + (color >> (1*8) & 0xFF) / 255.0, + (color >> (0*8) & 0xFF) / 255.0); +} + +// Renders text to cairo. +static int render_text(struct menu *menu, cairo_t *cairo, const char *str, + int x, int y, int width, uint32_t bg_color, uint32_t fg_color, + int left_padding, int right_padding) { + + int text_width, text_height; + get_text_size(cairo, menu->font, &text_width, &text_height, NULL, 1, str); + int text_y = (menu->line_height / 2.0) - (text_height / 2.0); + + if (width == 0) { + width = text_width + left_padding + right_padding; + } + if (bg_color) { + cairo_set_source_u32(cairo, bg_color); + cairo_rectangle(cairo, x, y, width, menu->line_height); + cairo_fill(cairo); + } + cairo_move_to(cairo, x + left_padding, y + text_y); + cairo_set_source_u32(cairo, fg_color); + pango_printf(cairo, menu->font, 1, str); + + return width; +} + +// Renders the prompt message. +static void render_prompt(struct menu *menu, cairo_t *cairo) { + if (!menu->prompt) { + return; + } + render_text(menu, cairo, menu->prompt, 0, 0, 0, + menu->promptbg, menu->promptfg, menu->padding, menu->padding/2); +} + +// Renders the input text. +static void render_input(struct menu *menu, cairo_t *cairo) { + char *censort = NULL; + + if (menu->passwd) { + censort = calloc(1, sizeof(menu->input)); + if (!censort) { + return; + } + memset(censort, '*', strlen(menu->input)); + } + + render_text(menu, cairo, menu->passwd ? censort : menu->input, + menu->promptw, 0, 0, 0, menu->normalfg, menu->padding, menu->padding); + + if (censort) { + free(censort); + } +} + +// Renders a cursor for the input field. +static void render_cursor(struct menu *menu, cairo_t *cairo) { + const int cursor_width = 2; + const int cursor_margin = 2; + int cursor_pos = menu->promptw + menu->padding + + text_width(cairo, menu->font, menu->input) + - text_width(cairo, menu->font, &menu->input[menu->cursor]) + - cursor_width / 2; + cairo_rectangle(cairo, cursor_pos, cursor_margin, cursor_width, + menu->line_height - 2 * cursor_margin); + cairo_fill(cairo); +} + +// Renders a single menu item horizontally. +static int render_horizontal_item(struct menu *menu, cairo_t *cairo, struct item *item, int x) { + uint32_t bg_color = menu->sel == item ? menu->selectionbg : menu->normalbg; + uint32_t fg_color = menu->sel == item ? menu->selectionfg : menu->normalfg; + + return render_text(menu, cairo, item->text, x, 0, 0, + bg_color, fg_color, menu->padding, menu->padding); +} + +// Renders a single menu item vertically. +static int render_vertical_item(struct menu *menu, cairo_t *cairo, struct item *item, int x, int y) { + uint32_t bg_color = menu->sel == item ? menu->selectionbg : menu->normalbg; + uint32_t fg_color = menu->sel == item ? menu->selectionfg : menu->normalfg; + + render_text(menu, cairo, item->text, x, y, menu->width - x, + bg_color, fg_color, menu->padding, 0); + return menu->line_height; +} + +// Renders a page of menu items horizontally. +static void render_horizontal_page(struct menu *menu, cairo_t *cairo, struct page *page) { + int x = menu->promptw + menu->inputw + menu->left_arrow; + for (struct item *item = page->first; item != page->last->next_match; item = item->next_match) { + x += render_horizontal_item(menu, cairo, item, x); + } + + // Draw left and right scroll indicators if necessary + if (page->prev) { + cairo_move_to(cairo, menu->promptw + menu->inputw + menu->padding, 0); + pango_printf(cairo, menu->font, 1, "<"); + } + if (page->next) { + cairo_move_to(cairo, menu->width - menu->right_arrow + menu->padding, 0); + pango_printf(cairo, menu->font, 1, ">"); + } +} + +// Renders a page of menu items vertically. +static void render_vertical_page(struct menu *menu, cairo_t *cairo, struct page *page) { + int x = menu->promptw; + int y = menu->line_height; + for (struct item *item = page->first; item != page->last->next_match; item = item->next_match) { + y += render_vertical_item(menu, cairo, item, x, y); + } +} + +// Renders the menu to cairo. +static void render_to_cairo(struct menu *menu, cairo_t *cairo) { + // Render background + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + cairo_set_source_u32(cairo, menu->normalbg); + cairo_paint(cairo); + + // Render prompt and input + render_prompt(menu, cairo); + render_input(menu, cairo); + render_cursor(menu, cairo); + + // Render selected page + if (!menu->sel) { + return; + } + if (menu->lines > 0) { + render_vertical_page(menu, cairo, menu->sel->page); + } else { + render_horizontal_page(menu, cairo, menu->sel->page); + } +} + +// Renders a single frame of the menu. +void render_menu(struct menu *menu) { + struct wl_context *context = menu->context; + + int scale = context_get_scale(context); + struct pool_buffer *buffer = context_get_next_buffer(context, scale); + if (!buffer) { + return; + } + + cairo_t *shm = buffer->cairo; + cairo_set_antialias(shm, CAIRO_ANTIALIAS_BEST); + cairo_font_options_t *fo = cairo_font_options_create(); + cairo_set_font_options(shm, fo); + cairo_font_options_destroy(fo); + render_to_cairo(menu, shm); + + struct wl_surface *surface = context_get_surface(context); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_attach(surface, buffer->buffer, 0, 0); + wl_surface_damage(surface, 0, 0, menu->width, menu->height); + wl_surface_commit(surface); + + menu->rendered = true; +} -- cgit v1.2.3