commit 749e270c9e436c9e475d2ef00a30660ad7f1b12e from: Onana Onana Xavier Manuel date: Wed Sep 17 13:46:32 2025 UTC Label changes and some minor changes - Implemented `MUI_TEXT_MULTILINE` and `MUI_TEXT_WRAPPED` to label for additionnal styling. - Fix/changed some example containing the label drawable to match the function commit - 94783f656eaf549437a61d52e2df97f28791e880 commit + 749e270c9e436c9e475d2ef00a30660ad7f1b12e blob - aec9a0c7220e03ce9f502e4ec0c4777ea53660cc blob + 7e285dc9cc7d37558104a0476578f5ec3960492c --- TODO +++ TODO @@ -11,10 +11,21 @@ - Implement groups styles (ex. VStack groups, HStack groups, Grid groups, etc.) with a set of flags (ex. Scrollable and clipping?) -- Add font options (ex. color, size, etc) with flags (ex. multiline, text - wrapping) +- Add a minimum size option for layouts so drawables are still visible + even if scaled down to the maximum. -- Add a minimum size option for layouts +- Get rid of xcb-render-util dependency. This is to reduce dependancies + mostly for FreeBSD as OpenBSD and NetBSD already come with X11R6 and + X11R7. -- Fix issues happening in text rendering (noises that adds odd colors to - text and sometimes text from a dead program invading the new one) +- Fix font line spacing in `mui_label`. When turning on setting the color + of the xcb_render picture in `context.c` there is a noticable gap at + the end of the texture. This is due to the layout height being multiplied + by 1.5 as a temporary hack. + +- Makefile for OpenBSD ports. The make command currently only create a + limbui.so file in the library folder. Change the makefile to install the + library on an OpenBSD system. + +- Add label partial font styling to have different styles inside the same + label. More customization to label in other world. blob - 98401c97a40dd5fb6e56f4dbb3e0840c44a1750d blob + 1a3f4838d6f15495ff8fcdc0c8b1becb22a91327 --- context.c +++ context.c @@ -188,7 +188,6 @@ ctx_load_font() font_size = 16; font->gs = xcb_generate_id(xcb.conn); xcb_render_create_glyph_set(xcb.conn, font->gs, xcb.fmt_alpha8); - printf("%s\n", fpath); if (FT_New_Face(xcb.ft_library, fpath, 0, &face)) { exit(1); @@ -370,14 +369,13 @@ struct gheader { xcb_render_picture_t ctx_create_label(struct ctx_node *node) { - uint32_t mask; - uint32_t value; uint16_t width; uint16_t height; + xcb_rectangle_t rect; xcb_pixmap_t xcb_pixmap; - xcb_render_color_t color; - xcb_rectangle_t rectangle; xcb_render_picture_t xcb_picture; + xcb_render_color_t xcb_color; + struct mui_label *label; struct mui_layout *layout; @@ -387,50 +385,59 @@ ctx_create_label(struct ctx_node *node) width = mui_get_layout_width(layout); height = mui_get_layout_height(layout) * 1.5; - rectangle.x = 0; - rectangle.y = 0; - rectangle.width = width; - rectangle.height = height; + xcb_color.red = 0; + xcb_color.green = 0; + xcb_color.blue = 0; + xcb_color.alpha = 0; - color.red = 0; - color.green = 0; - color.blue = 0; - color.alpha = 0; + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; xcb_pixmap = xcb_generate_id(xcb.conn); xcb_picture = xcb_generate_id(xcb.conn); xcb_create_pixmap(xcb.conn, 32, xcb_pixmap, xcb.screen->root, width, height); xcb_render_create_picture(xcb.conn, xcb_picture, xcb_pixmap, xcb.fmt_argb32, 0, NULL); - xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, xcb_picture, color, 1, &rectangle); + xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_SRC, xcb_picture, xcb_color, 1, &rect); /* * Actual drawing the text */ + uint32_t mask; + uint32_t value; xcb_pixmap_t gpixmap; xcb_render_picture_t gpicture; struct gheader header; uint32_t *data; char *text; + struct mui_color m_color; + int tindex; /* Text index */ + int last_width; + struct mui_gindice *ptr; + struct mui_gindice *last_indice; + m_color = mui_get_label_color(label); + header.count = mui_get_label_len(label); header.pad[0] = 0; header.pad[1] = 0; header.pad[2] = 0; header.dx = 0; - header.dy = xcb.fonts[0]->height; + header.dy = 0; width = 1; height = 1; - rectangle.x = 0; - rectangle.y = 0; - rectangle.width = width; - rectangle.height = height; + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; - color.red = 0; - color.green = 0; - color.blue = 0; - color.alpha = 0xffff; + xcb_color.red = m_color.r * 0xffff; + xcb_color.green = m_color.g * 0xffff; + xcb_color.blue = m_color.b * 0xffff; + xcb_color.alpha = m_color.a * 0xffff; mask = XCB_RENDER_CP_REPEAT; value = XCB_RENDER_REPEAT_NORMAL; @@ -438,29 +445,52 @@ ctx_create_label(struct ctx_node *node) gpixmap = xcb_generate_id(xcb.conn); gpicture = xcb_generate_id(xcb.conn); - xcb_create_pixmap(xcb.conn, 32, gpixmap, xcb.screen->root, width, height); + xcb_create_pixmap(xcb.conn, 32, gpixmap, xcb.screen->root, 1, 1); xcb_render_create_picture(xcb.conn, gpicture, gpixmap, xcb.fmt_argb32, mask, &value); - xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, gpicture, color, 1, &rectangle); + xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, gpicture, xcb_color, 1, &rect); + ptr = NULL; + tindex = 0; + last_width = 0; + last_indice = NULL; + while ((ptr = mui_get_label_next_indice(label, ptr))) { + header.dy += xcb.fonts[ptr->gs]->height; - void *line; - line = NULL; + /* + * This is in a case the current pointer has the same line number + * as the last one. + * + * This line will offset properly the xcb_picture + */ + if (last_indice) { + if (last_indice->lnum == ptr->lnum) { + header.dx = last_width; + header.dy -= xcb.fonts[ptr->gs]->height; + } else { + last_width = 0; + header.dx = 0; + } + } - header.count = mui_get_label_len(label); + header.count = ptr->len; + text = mui_get_label_text(label); - text = mui_get_label_text(label); - data = malloc(sizeof(uint32_t) * header.count + sizeof(header)); + data = malloc(sizeof(uint32_t) * header.count + sizeof(header)); - for (int i = 0; i < header.count; i++) - data[i + sizeof(header) / sizeof(uint32_t)] = text[i]; + for (int i = 0; i < header.count; i++) { + data[i + sizeof(header) / sizeof(uint32_t)] = text[tindex + i]; + last_width += xcb.fonts[ptr->gs]->widths[text[tindex + i]]; + } - memcpy(data, &header, sizeof(header)); + memcpy(data, &header, sizeof(header)); + tindex += ptr->len; - xcb_render_composite_glyphs_32(xcb.conn, XCB_RENDER_PICT_OP_OVER, gpicture, xcb_picture, 0, - xcb.fonts[0]->gs, 0, 0, sizeof(header) + header.count * sizeof(uint32_t), (uint8_t *)data); + xcb_render_composite_glyphs_32(xcb.conn, XCB_RENDER_PICT_OP_OVER, gpicture, xcb_picture, 0, + xcb.fonts[0]->gs, 0, 0, sizeof(header) + header.count * sizeof(uint32_t), (uint8_t *)data); + free(data); + last_indice = ptr; + } - free(data); - xcb_render_free_picture(xcb.conn, gpicture); xcb_free_pixmap(xcb.conn, gpixmap); xcb_free_pixmap(xcb.conn, xcb_pixmap); blob - 9f1c515482dc202e4a3c6a404e7d50de332466f0 blob + 1cbab7f2dd6cff54abf9d07ee00db76a9458e912 --- demo/image/main.c +++ demo/image/main.c @@ -43,7 +43,7 @@ main(void) data = load_image_data("../common/fish.raw"); image = mui_create_image(data, 200, 200); - label = mui_create_label("The Fish", 8); + label = mui_create_label("The Fish"); layout = mui_get_image_layout(image); layint = mui_get_label_layout(label); blob - 54dd7ff5de77607479a727ae6c1491c06daab714 blob + 1c9fac3bbc67c9dd9f7c24349f0b5e23c2a18b85 --- demo/text/main.c +++ demo/text/main.c @@ -10,27 +10,25 @@ static struct { } window; -char notice[] = "OpenBSD - The secure OS"; +char notice[] = +"OpenBSD - Free, Functional and secure\n\n" +"NetBSD- Free, Fast, Secure and highly portable Unix-like Open Source operating system\n\n" +"FreeBSD - The Power to Serve\n\n" +"DragonflyBSD"; int main(void) { - int count; struct mui_event event; struct mui_label *label; - struct mui_layout *layout; - window.mw = mui_create_window("Text", 320, 240); + window.mw = mui_create_window("text.ex", 320, 240); window.group = mui_get_window_group(window.mw); window.layout = mui_get_group_layout(window.group); window.handler = mui_get_window_event_handler(window.mw); - label = mui_create_label(notice, strlen(notice)); - layout = mui_get_label_layout(label); - - mui_set_layout_flags(layout, MUI_ALIGN_CENTER_X | MUI_ALIGN_CENTER_Y | - MUI_ANCHOR_CENTER_X | MUI_ANCHOR_CENTER_Y | MUI_KEEP_RATIO); - + label = mui_create_label(notice); + mui_set_label_flags(label, MUI_TEXT_WRAPPED | MUI_TEXT_MULTILINE); mui_group_add(window.group, MUI_LABEL, label); while ((event = mui_pop_event(window.handler)).type != MUI_EVENT_QUIT) { blob - 90108fc2a236a4cf13c5c1d62990b01aae977805 blob + 31d3d95df9965d8d0bdb58d7ab0a3ae82384dff4 --- group.c +++ group.c @@ -9,9 +9,12 @@ struct member { }; struct mui_group { + uint16_t type; struct mui_ctx *ctx; struct mui_layout *layout; + struct { uint16_t cols, rows; } grid; + TAILQ_HEAD(mem_head, member) head; }; @@ -23,6 +26,7 @@ mui_create_group() group = malloc(sizeof(*group)); group->layout = mui_create_layout(MUI_GROUP, group); + group->type = MUI_UNDEF; group->ctx = NULL; TAILQ_INIT(&group->head); @@ -30,6 +34,19 @@ mui_create_group() return group; } +struct mui_group* +mui_create_grid(uint16_t cols, uint16_t rows) +{ + struct mui_group *group; + + group = mui_create_group(); + group->type = MUI_GRID; + group->grid.cols = cols; + group->grid.rows = rows; + + return group; +} + void mui_delete_group(struct mui_group *group) { blob - 3170b3ca351bb8f97c1fd0b9d812ca430198e11b blob + 27be9d05366f4aba761b51139b2701a9ca1877e5 --- label.c +++ label.c @@ -2,28 +2,35 @@ struct mui_label { int len; + int vlen; char *text; int flags; int font_id; + struct mui_color color; struct mui_group *group; struct mui_layout *layout; + + TAILQ_HEAD(mui_gindice_h, mui_gindice) head; }; struct mui_label* -mui_create_label(char *text, int len) +mui_create_label(char *text) { struct mui_label *label; label = calloc(1, sizeof(*label)); - label->text = malloc(len + 1); - memcpy(label->text, text, len); - label->text[len] = '\0'; - label->len = len; + label->len = strlen(text); + label->text = malloc(label->len + 1); + memcpy(label->text, text, label->len); + label->text[label->len] = '\0'; label->font_id = 0; label->group = NULL; + label->color = (struct mui_color) { 0, 0, 0, 1.0 }; label->layout = mui_create_layout(MUI_LABEL, label); + TAILQ_INIT(&label->head); + return label; } @@ -68,53 +75,182 @@ mui_get_label_text(struct mui_label *label) return label->text; } +struct mui_color +mui_get_label_color(struct mui_label *label) +{ + return label->color; +} + void -mui_get_label_lines(struct mui_label *label, int *line_count, int *rwidth, int *rheight) +mui_set_label_color(struct mui_label *label, float r, float g, float b, float a) { - char **lines; - uint16_t width; - uint16_t lwidth; - int lindex; - uint16_t swidth; - int sindex; - char *line; - int len; - struct mui_layout *layout; + label->color = (struct mui_color) { r, g, b, a }; + if (label->group) + mui_update_group_member_node(label->group, mui_in_group(label->group, MUI_LABEL, label)); +} - *line_count = 1; - layout = mui_get_label_layout(label); - width = mui_get_layout_width(layout); +struct mui_gindice* +mui_get_label_next_indice(struct mui_label *label, struct mui_gindice *ptr) +{ + if (ptr == NULL) + return TAILQ_FIRST(&label->head); - *rwidth = *rheight = 0; + struct mui_gindice *curr; + struct mui_gindice *next; - for (int i = 0; i < label->len; i++) { + curr = (struct mui_gindice *)ptr; + next = TAILQ_NEXT(curr, entries); - lindex = i; - lwidth = 0; + return (curr == next) ? NULL : next; +} - for (; lwidth < width && i < label->len && label->text[i] != '\n'; i++) { - if (label->text[i] == ' ') { - swidth = lwidth; - sindex = i; +/* + * Text is stored in an easy to read format to help render properly + * on the context. This is done using the `mui_gindice` struct which + * has the line number, the glyphset and the number of characters. + * + * You can vien it in this way : + * + * { {0, 1, 50} , {1, 0, 25} } + * + * In this example there are two lines where the first line has 50 + * character and uses the font id 1 and the second has 25 lines + * + * This function also allows implementation of flags such as + * `MUI_MULTILINE` and `MUI_TEXT_WRAP` + */ + +void +mui_format_label(struct mui_label *label) +{ + uint8_t len; + int last_space; + uint16_t width; + uint16_t height; + uint16_t twidth; + uint16_t lwidth; + uint32_t gs, lnum; + struct mui_gindice *np; + struct mui_layout *layout; + + /* Cleanup */ + while ((np = TAILQ_FIRST(&label->head))) { + TAILQ_REMOVE(&label->head, np, entries); + free(np); + } + + layout = mui_get_group_layout(label->group); + lwidth = mui_get_layout_width(layout); + + gs = len = lnum = 0; + width = height = 0; + last_space = 0; + twidth = 0; + + /* Appending */ + for (int i = 0; i < label->len + 1; i++) { + len++; + + twidth += mui_ctx_get_glyph_width(label->text[i], label->font_id); + + if (label->text[i] == ' ') + last_space = i; + + if (twidth > lwidth && last_space > 0 && label->flags & MUI_TEXT_WRAPPED) { + /* Checking in current indice for last line */ + if (last_space > i - len) { + np = malloc(sizeof(*np)); + np->len = (last_space - (i - len)); + np->gs = 0; + np->lnum = lnum++; + width = (width < lwidth) ? lwidth : width; + height += mui_ctx_get_font_height(label->font_id); + + + TAILQ_INSERT_TAIL(&label->head, np, entries); + i = last_space; + last_space = 0; + twidth = 0; + len = 0; + continue; + + /* Checking for previous indices for and cleaning after */ + } else { + int cindex; + struct mui_gindice *curr; + struct mui_gindice *next; + + cindex = i - len; + curr = TAILQ_LAST(&label->head, mui_gindice_h); + + while (cindex - curr->len > last_space) { + cindex -= curr->len; + curr = TAILQ_PREV(curr, mui_gindice_h, entries); + } + + while ((next = TAILQ_NEXT(curr, entries))) { + TAILQ_REMOVE(&label->head, next, entries); + free(next); + } + + i = cindex - curr->len; + curr->len = last_space - i; + width = (width < lwidth) ? lwidth : width; + + i = last_space; + last_space = 0; + twidth = 0; + lnum++; + len = 0; + + continue; } } - if (lwidth > width) { - lwidth = swidth; - i = sindex; + if (i == label->len) { + np = malloc(sizeof(*np)); + np->len = len; + np->gs = 0; + np->lnum = lnum; + width = (width < twidth) ? twidth : width; + height += mui_ctx_get_font_height(label->font_id); + + TAILQ_INSERT_TAIL(&label->head, np, entries); + break; } - len = i - lindex; - line = malloc(len + 1); - memcpy(line, label->text + lindex, len); - line[len] = '\0'; + /* + * XCB only accept up to 254 glyphs being composited at once. Hence + * why a `mui_gindice` is limited to this amount + */ + if (len == 254) { + np = malloc(sizeof(*np)); + np->lnum = lnum; + np->len = len; + np->gs = 0; - *rwidth = (*rwidth > lwidth) ? *rwidth : lwidth; - *rheight += mui_ctx_get_font_height(label->font_id); + len = 0; + TAILQ_INSERT_TAIL(&label->head, np, entries); + printf("{%d, %d, %d}, ", np->lnum, np->gs, np->len); + } - free(line); + if (label->text[i] == '\n' && label->flags & MUI_TEXT_MULTILINE) { + np = malloc(sizeof(*np)); + np->len = len; + np->gs = 0; + np->lnum = lnum++; + width = (width < twidth) ? twidth : width; + height += mui_ctx_get_font_height(label->font_id); + twidth = 0; + + TAILQ_INSERT_TAIL(&label->head, np, entries); + + len = 0; + twidth = 0; + } } + mui_resize_layout(label->layout, width, height); } void @@ -135,13 +271,15 @@ mui_set_label_group(struct mui_label *label, struct mu if (group) { layout = mui_get_group_layout(group); - /* Initial dimensions when group changes */ - for (int i = 0; i < label->len; i++) - width += mui_ctx_get_glyph_width(label->text[i], label->font_id); - height = mui_ctx_get_font_height(label->font_id); - mui_resize_layout(label->layout, width, height); + mui_format_label(label); mui_group_add(group, MUI_LABEL, label); mui_resize_layout(layout, mui_get_layout_width(layout), mui_get_layout_height(layout)); } } + +void +mui_set_label_flags(struct mui_label *label, int flags) +{ + label->flags = flags; +} blob - ce6f83d61a7141b265272ee276b83e3d79851a72 blob + daa50e1a8b330d153dcdaa012f2f6343b9e805b1 --- mui.h +++ mui.h @@ -19,6 +19,14 @@ /* BASIC TYPES */ +struct mui_gindice { + uint8_t len; + uint32_t gs; + uint32_t lnum; + + TAILQ_ENTRY(mui_gindice) entries; +}; + struct mui_label; struct mui_shape; @@ -45,8 +53,8 @@ enum mui_layout_flags { MUI_ALIGN_BOTTOM = 1024, MUI_ALIGN_CENTER_X = 2048, MUI_ALIGN_CENTER_Y = 4096, - MUI_EXPAND_X = 8192, - MUI_EXPAND_Y = 16384 + MUI_FILL_X = 8192, + MUI_FILL_Y = 16384 }; struct mui_layout; @@ -131,7 +139,7 @@ void mui_set_shape_colo /* LABEL FUNCTIONS */ -struct mui_label *mui_create_label(char *text, int len); +struct mui_label *mui_create_label(char *text); void mui_delete_label(struct mui_label *label); @@ -145,12 +153,12 @@ char *mui_get_label_text int mui_get_label_len(struct mui_label *label); -/* - * Returns the line count, width and height of a label - * based on its parent. - */ -void mui_get_label_lines(struct mui_label *label, int *line_count, int *rwidth, int *rheight); +struct mui_color mui_get_label_color(struct mui_label *label); +struct mui_gindice *mui_get_label_next_indice(struct mui_label *label, struct mui_gindice *ptr); + +void mui_set_label_color(struct mui_label *label, float r, float g, float b, float a); + void mui_set_label_flags(struct mui_label *label, int flags); void mui_set_label_group(struct mui_label *label, struct mui_group *group); @@ -228,6 +236,8 @@ void mui_print_layout_m struct mui_group *mui_create_group(void); +struct mui_group *mui_create_grid(uint16_t cols, uint16_t rows); + void mui_draw_group_nodes(struct mui_group *group); void mui_set_group_ctx(struct mui_group *group, struct mui_ctx *ctx); blob - ae3e6a466b9b02b4d8ac140933a1af8bb7b8f830 blob + 05ae5884ee29a157f5cd46af0859bc04b7f9075a --- window.c +++ window.c @@ -19,7 +19,7 @@ mui_create_window(const char *name, uint16_t width, ui mw->group = mui_create_group(); layout = mui_get_group_layout(mw->group); mui_resize_layout(layout, width, height); - mui_set_layout_flags(layout, MUI_EXPAND_X | MUI_EXPAND_Y); + mui_set_layout_flags(layout, MUI_SCALE_X | MUI_SCALE_Y); mw->handler = mui_create_event_handler(mw); mw->name = malloc(strlen(name)+1);