ltkx

GUI toolkit for X11 (WIP)
git clone git://lumidify.org/ltkx.git
Log | Files | Refs | README | LICENSE

commit aed6b7ca52af0f276ca91bcf4d156469baf999d7
parent 4898ef0c1cc92eee1a6c61535a33e26e94b143a7
Author: lumidify <nobody@lumidify.org>
Date:   Mon, 18 May 2020 20:56:54 +0200

Start adding basic position to index mapping

Diffstat:
Mtext_buffer.c | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mtext_buffer.h | 3+++
Mtext_edit.c | 18++++++++++++++++++
Mtext_edit.h | 1+
4 files changed, 86 insertions(+), 13 deletions(-)

diff --git a/text_buffer.c b/text_buffer.c @@ -48,10 +48,17 @@ LTK_ARRAY_INIT_IMPL(line, struct ltk_soft_line *) void ltk_soft_line_destroy(struct ltk_soft_line *sl) { + /* FIXME: destroy arrays */ if (sl->img) XDestroyImage(sl->img); free(sl); } +/* FIXME: redo this to store a list of all runs and indeces to be drawn in + each soft line -> that would take a bit more space but simplify later + logic and (most importantly) allow ltk_soft_lien_get_index_from_pos to + loop over the actual runs and see what direction each one is (that's + necessary for determining on which side of the glyph the new text has + to be inserted) */ struct ltk_array_line * ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) { struct ltk_array_line *soft_lines = ltk_array_create_line(1); @@ -199,23 +206,26 @@ ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) { /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */ void -ltk_draw_glyph(LtkGlyph *glyph, XImage *img, int x, int y, XColor fg) { - printf("%d,%d\n", x, y); +ltk_soft_line_draw_glyph(LtkGlyph *glyph, struct ltk_soft_line *sl, int x, int y, XColor fg) { + ltk_array_append_int(sl->glyph_pos, x); + ltk_array_append_int(sl->glyph_clusters, glyph->cluster); double a; int b; for (int i = 0; i < glyph->info->h; i++) { for (int j = 0; j < glyph->info->w; j++) { - if (y + i >= img->height || x + j >= img->width) + if (y + i >= sl->img->height || x + j >= sl->img->width) continue; - b = (y + i) * img->bytes_per_line + (x + j) * 4; + b = (y + i) * sl->img->bytes_per_line + (x + j) * 4; a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0; - img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257; - img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257; - img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)img->data[b + 2] * 257) / 257; + sl->img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)sl->img->data[b] * 257) / 257; + sl->img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)sl->img->data[b + 1] * 257) / 257; + sl->img->data[b + 2] = (fg.red * a + (1 - a) * (uint16_t)sl->img->data[b + 2] * 257) / 257; } } } +/* FIXME: pass old soft lines and check if anything changed - only rerender then; + also reuse the old struct ltk_soft_line's to avoid reallocating */ /* FIXME: rename this once everything is cleaned up (currently conflicts with the old render function */ struct ltk_array_line * @@ -239,6 +249,9 @@ ltk_render_text_line_new( for (int i = 0; i < soft_lines->len; i++) { struct ltk_soft_line *sl = soft_lines->buf[i]; + /* FIXME: allow to disable this if selection isn't needed */ + sl->glyph_pos = ltk_array_create_int(tl->len); + sl->glyph_clusters = ltk_array_create_int(tl->len); sl->img = ltk_create_ximage(dpy, sl->w, tl->h, depth, bg); struct ltk_text_run *cur = sl->run; size_t cur_len = 0; @@ -257,14 +270,14 @@ ltk_render_text_line_new( for (int i = start_index; i >= 0 && cur_len < sl->len; i--) { cur_len++; int x = cur_border - (local_border - cur->glyphs[i].x_abs); - ltk_draw_glyph(&cur->glyphs[i], sl->img, x, cur->glyphs[i].y_abs, fg); + ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg); } cur_border -= local_border - cur->glyphs[end_index].x_abs; } else { for (int i = end_index; i <= start_index && cur_len < sl->len; i++) { cur_len++; int x = cur_border + (cur->glyphs[i].x_abs - cur->glyphs[end_index].x_abs); - ltk_draw_glyph(&cur->glyphs[i], sl->img, x, cur->glyphs[i].y_abs, fg); + ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg); } cur_border += local_border - cur->glyphs[end_index].x_abs; } @@ -280,14 +293,14 @@ ltk_render_text_line_new( for (int i = end_index; i >= start_index && cur_len < sl->len; i--) { cur_len++; int x = cur_border - (cur->glyphs[end_index].x_abs + cur->glyphs[end_index].x_advance - cur->glyphs[i].x_abs); - ltk_draw_glyph(&cur->glyphs[i], sl->img, x, cur->glyphs[i].y_abs, fg); + ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg); } cur_border -= cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance - local_border; } else { for (int i = start_index; i < cur->num_glyphs && cur_len < sl->len; i++) { cur_len++; int x = cur_border + (cur->glyphs[i].x_abs - local_border); - ltk_draw_glyph(&cur->glyphs[i], sl->img, x, cur->glyphs[i].y_abs, fg); + ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg); } cur_border += cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance - local_border; } @@ -299,8 +312,46 @@ ltk_render_text_line_new( return soft_lines; } -size_t -ltk_soft_line_get_index_from_pos(int x, struct ltk_soft_line *sl) { +uint32_t +ltk_soft_line_get_index_from_pos(int x, struct ltk_soft_line *sl, hb_direction_t dir, int *found_pos) { + /* FIXME: need par dir! for better guess! */ + /* also need it to determine if insert should be before or after char */ + /* FIXME: should I be messing around with casting between uint32_t and size_t? */ + /* FIXME: handle negative x */ + uint32_t guess = (int)(((double)x / sl->w) * (sl->len - 1)); + guess = guess >= sl->len ? sl->len - 1 : guess; + int delta = 0; + int i = 0; + int last_dist; + /* FIXME: add more safety - sl->len and sl->glyph_pos->len *should* be the + same, but who knows? */ + if (sl->glyph_pos->len - 1 > guess && + abs(sl->glyph_pos->buf[guess + 1] - x) < abs(sl->glyph_pos->buf[guess] - x)) { + delta = 1; + i = guess + 1; + last_dist = abs(sl->glyph_pos->buf[guess + 1] - x); + } else if (guess > 0 && + abs(sl->glyph_pos->buf[guess - 1] - x) < abs(sl->glyph_pos->buf[guess] - x)) { + delta = -1; + i = guess - 1; + last_dist = abs(sl->glyph_pos->buf[guess - 1] - x); + } + if (delta == 0) { + if (found_pos) + found_pos = sl->glyph_pos->buf[guess]; + return guess; + } + int new_dist; + for (; i >= 0 && i < sl->len; i += delta) { + new_dist = abs(sl->glyph_pos->buf[i] - x); + if (last_dist < new_dist) + break; + last_dist = new_dist; + } + i -= delta; + if (found_pos) + *found_pos = sl->glyph_pos->buf[i]; + return sl->glyph_clusters->buf[i]; } /* Begin stuff stolen from raqm */ diff --git a/text_buffer.h b/text_buffer.h @@ -61,6 +61,8 @@ struct ltk_soft_line { int w; struct ltk_text_run *run; XImage *img; + struct ltk_array_int *glyph_pos; + struct ltk_array_int *glyph_clusters; }; LTK_ARRAY_INIT_DECL(line, struct ltk_soft_line *) @@ -98,6 +100,7 @@ struct ltk_text_buffer { void ltk_soft_line_destroy(struct ltk_soft_line *sl); struct ltk_array_line *ltk_render_text_line_new(struct ltk_text_line *tl, int max_width, Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg); +uint32_t ltk_soft_line_get_index_from_pos(int x, struct ltk_soft_line *sl, hb_direction_t dir, int *found_pos); void ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len); struct ltk_text_line *ltk_text_line_create(void); void ltk_text_line_destroy(struct ltk_text_line *tl); diff --git a/text_edit.c b/text_edit.c @@ -22,6 +22,7 @@ */ #include <stdint.h> +#include <math.h> #include <X11/Xlib.h> #include <X11/Xutil.h> #include "khash.h" @@ -57,20 +58,37 @@ ltk_draw_text_edit(LtkTextEdit *te) { } } +void +ltk_text_edit_tmp(LtkTextEdit *te, XEvent event) { + /* this should never be negative, but just to be sure... */ + int local_y = abs(event.xbutton.y - te->widget.rect.y); + int i = ((double)local_y / te->tl->h) * te->soft_lines->len; + i = i >= te->soft_lines->len ? te->soft_lines->len - 1 : i; + int x = event.xbutton.x - te->widget.rect.x; + if (te->tl->dir == HB_DIRECTION_RTL) + x -= (te->widget.rect.w - te->soft_lines->buf[i]->img->width); + int found_pos = 0; + te->cursor = ltk_soft_line_get_index_from_pos( + x, te->soft_lines->buf[i], te->tl->dir, &found_pos); +} + LtkTextEdit * ltk_create_text_edit(LtkWindow *window, const char *text) { LtkTextEdit *te = malloc(sizeof(LtkTextEdit)); if (!te) ltk_fatal("ERROR: Unable to allocate memory for LtkTextEdit.\n"); ltk_fill_widget_defaults(&te->widget, window, &ltk_draw_text_edit, &ltk_destroy_text_edit, 1); + te->widget.mouse_press = &ltk_text_edit_tmp; te->tl = ltk_text_line_create(); ltk_text_line_insert_utf8(te->tl, text); te->soft_lines = NULL; + te->cursor = 0; return te; } void ltk_text_edit_insert_text(LtkTextEdit *te, const char *text) { + te->tl->cursor_pos = te->cursor; ltk_text_line_insert_utf8(te->tl, text); if (te->soft_lines) ltk_array_destroy_deep_line(te->soft_lines, &ltk_soft_line_destroy); diff --git a/text_edit.h b/text_edit.h @@ -26,6 +26,7 @@ typedef struct { LtkWidget widget; + uint32_t cursor; struct ltk_text_line *tl; struct ltk_array_line *soft_lines; } LtkTextEdit;