URI: 
       tStart adding basic position to index mapping - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit aed6b7ca52af0f276ca91bcf4d156469baf999d7
   DIR parent 4898ef0c1cc92eee1a6c61535a33e26e94b143a7
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Mon, 18 May 2020 20:56:54 +0200
       
       Start adding basic position to index mapping
       
       Diffstat:
         M text_buffer.c                       |      77 +++++++++++++++++++++++++------
         M text_buffer.h                       |       3 +++
         M text_edit.c                         |      18 ++++++++++++++++++
         M text_edit.h                         |       1 +
       
       4 files changed, 86 insertions(+), 13 deletions(-)
       ---
   DIR diff --git a/text_buffer.c b/text_buffer.c
       t@@ -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);
       t@@ -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 *
       t@@ -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;
       t@@ -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;
                                        }
       t@@ -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;
                                        }
       t@@ -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 */
   DIR diff --git a/text_buffer.h b/text_buffer.h
       t@@ -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 *)
       t@@ -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);
   DIR diff --git a/text_edit.c b/text_edit.c
       t@@ -22,6 +22,7 @@
         */
        
        #include <stdint.h>
       +#include <math.h>
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
        #include "khash.h"
       t@@ -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);
   DIR diff --git a/text_edit.h b/text_edit.h
       t@@ -26,6 +26,7 @@
        
        typedef struct {
                LtkWidget widget;
       +        uint32_t cursor;
                struct ltk_text_line *tl;
                struct ltk_array_line *soft_lines;
        } LtkTextEdit;