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:
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, <k_draw_text_edit, <k_destroy_text_edit, 1);
+	te->widget.mouse_press = <k_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, <k_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;