commit f1e4126fb66f7b15d1a0ed50a4b5da72e8224fb4
parent 657c2bb9354fc1f5d1e224c5db412ce0c4da447f
Author: lumidify <nobody@lumidify.org>
Date:   Mon,  1 Mar 2021 20:07:48 +0100
Add basic line breaking to stb text renderer
This is not very good line breaking.
Diffstat:
4 files changed, 83 insertions(+), 18 deletions(-)
diff --git a/src/button.h b/src/button.h
@@ -22,7 +22,6 @@
 typedef struct {
 	ltk_widget widget;
 	ltk_text_line *tl;
-	Pixmap pixmap;
 } ltk_button;
 
 void ltk_button_setup_theme_defaults(ltk_window *window);
diff --git a/src/label.c b/src/label.c
@@ -113,11 +113,11 @@ ltk_label_create(ltk_window *window, const char *id, const char *text) {
 	uint16_t font_size = window->theme.font_size;
 	text_copy = ltk_strdup(text);
 	label->tl = ltk_text_line_create(window->xwindow, font_size, text_copy, -1);
+	ltk_text_line_render(label->tl, &window->theme.bg, &theme.text_color);
 	int text_w, text_h;
 	ltk_text_line_get_size(label->tl, &text_w, &text_h);
 	label->widget.ideal_w = text_w + theme.pad * 2;
 	label->widget.ideal_h = text_h + theme.pad * 2;
-	ltk_text_line_render(label->tl, &window->theme.bg, &theme.text_color);
 	ltk_fill_widget_defaults(&label->widget, id, window, &vtable, label->widget.ideal_w, label->widget.ideal_h);
 
 	return label;
diff --git a/src/label.h b/src/label.h
@@ -22,7 +22,6 @@
 typedef struct {
 	ltk_widget widget;
 	ltk_text_line *tl;
-	Pixmap text_pixmap;
 } ltk_label;
 
 void ltk_label_setup_theme_defaults(ltk_window *window);
diff --git a/src/text_stb.c b/src/text_stb.c
@@ -36,6 +36,8 @@
 #include "util.h"
 #include "text.h"
 
+/* FIXME: Actually implement reference counting for the glyphs! */
+
 typedef struct {
 	stbtt_fontinfo info;
 	char *path;
@@ -46,7 +48,7 @@ typedef struct {
 
 /* Contains general info on glyphs that doesn't change regardless of the context */
 typedef struct {
-	int id;
+	int id; /* FIXME: shouldn't this be uint32_t or larger? */
 	unsigned char *alphamap;
 	int w;
 	int h;
@@ -60,21 +62,30 @@ typedef struct {
 /* Contains glyph info specific to one run of text */
 typedef struct {
 	ltk_glyph_info *info;
+	uint32_t codepoint;
 	int x;
 	int y;
 } ltk_glyph;
 
 struct ltk_text_line {
-	Window window;
 	XImage *img;
 	char *text;
 	ltk_glyph *glyphs;
 	size_t glyph_len;
-	uint16_t font_size;
+
+	int *line_indeces;
+	int lines;
+	int lines_alloc;
+
+	Window window;
+
+	int w_max;
 	int w;
 	int h;
+	int line_h;
 	int x_min;
 	int y_min;
+	uint16_t font_size;
 };
 
 /* Hash definitions */
@@ -115,7 +126,7 @@ static ltk_font *ltk_get_font(char *path, int index);
 static void ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text,
     uint16_t font_size, int *x_min, int *y_min, int *x_max, int *y_max);
 static void ltk_text_line_create_glyphs(ltk_text_line *tl);
-static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff,
+static void ltk_text_line_draw_glyph(ltk_glyph *glyph, int x, int y,
     XImage *img, XColor fg);
 static XImage *ltk_create_ximage(int w, int h, int depth, XColor bg);
 
@@ -422,6 +433,7 @@ ltk_text_to_glyphs(ltk_glyph *glyphs, int num_glyphs, char *text, uint16_t font_
 
 		glyphs[i].x = x1_abs;
 		glyphs[i].y = y;
+		glyphs[i].codepoint = c1;
 
                 stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0);
                 x += (int) (ax * scale);
@@ -484,9 +496,7 @@ ltk_create_ximage(int w, int h, int depth, XColor bg) {
 
 /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */
 static void
-ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, XImage *img, XColor fg) {
-	int x = glyph->x + xoff;
-	int y = glyph->y + yoff;
+ltk_text_line_draw_glyph(ltk_glyph *glyph, int x, int y, XImage *img, XColor fg) {
 	double a;
 	int b;
 	for (int i = 0; i < glyph->info->h; i++) {
@@ -503,6 +513,47 @@ ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, XImage *img, XCol
 	}
 }
 
+static void
+ltk_text_line_break_lines(ltk_text_line *tl) {
+	tl->lines = 1;
+	if (tl->w_max == -1)
+		return;
+
+	if (!tl->line_indeces) {
+		tl->line_indeces = ltk_malloc(sizeof(int));
+		tl->lines_alloc = 1;
+	}
+	tl->w = tl->w_max;
+
+	/* FIXME: make this actually work properly */
+	size_t last_space = 0;
+	size_t last_break = 0;
+	int last_break_pos = tl->x_min;
+	size_t i = 0;
+	while (i < tl->glyph_len) {
+		/* 0x20 == space character */
+		if (tl->glyphs[i].codepoint == 0x20)
+			last_space = i + 1; /* actually break after the space */
+		if (tl->glyphs[i].x + tl->glyphs[i].info->w - last_break_pos > tl->w) {
+			/* FIXME: safeguard for widths smaller than a single char */
+			if (last_space > last_break)
+				last_break = last_space;
+			else
+				last_break = i;
+			i = last_break - 1;
+			last_break_pos = tl->glyphs[last_break].x;
+			if (tl->lines >= tl->lines_alloc) {
+				tl->lines_alloc = tl->lines_alloc ? tl->lines_alloc * 2 : 2;
+				tl->line_indeces = ltk_realloc(tl->line_indeces, tl->lines_alloc * sizeof(int));
+			}
+			tl->line_indeces[tl->lines - 1] = last_break;
+			tl->lines++;
+		}
+		i++;
+	}
+	tl->h = tl->line_h * tl->lines;
+}
+
 void
 ltk_text_line_render(
 	ltk_text_line *tl,
@@ -512,12 +563,24 @@ ltk_text_line_render(
 	XWindowAttributes attrs;
 	XGetWindowAttributes(tm.dpy, tl->window, &attrs);
 	int depth = attrs.depth;
-	/* FIXME: pass old image; if it has same dimensions, just clear it */
+	/* FIXME: if old image has same or smaller dimensions, just clear it */
 	if (tl->img)
 		XDestroyImage(tl->img);
 	tl->img = ltk_create_ximage(tl->w, tl->h, depth, bg->xcolor);
-	for (size_t i = 0; i < tl->glyph_len; i++) {
-		ltk_text_line_draw_glyph(&tl->glyphs[i], -tl->x_min, -tl->y_min, tl->img, fg->xcolor);
+
+	int last_break = 0;
+	for (int i = 0; i < tl->lines; i++) {
+		int next_break;
+		if (i <= tl->lines - 2)
+			next_break = tl->line_indeces[i];
+		else
+			next_break = tl->glyph_len;
+		for (int j = last_break; j < next_break; j++) {
+			int x = tl->glyphs[j].x - tl->glyphs[last_break].x;
+			int y = tl->glyphs[j].y - tl->y_min + tl->line_h * i;
+			ltk_text_line_draw_glyph(&tl->glyphs[j], x, y, tl->img, fg->xcolor);
+		}
+		last_break = next_break;
 	}
 }
 
@@ -539,9 +602,10 @@ ltk_text_line_draw(ltk_text_line *tl, Drawable d, GC gc, int x, int y, ltk_rect 
 
 void
 ltk_text_line_set_width(ltk_text_line *tl, int width) {
-	(void)tl;
-	(void)width;
-	/* FIXME: implement */
+	/* FIXME: clarify what the difference between w_max and w is */
+	tl->w_max = width;
+	tl->w = width;
+	ltk_text_line_break_lines(tl);
 }
 
 void
@@ -559,12 +623,11 @@ ltk_text_line_create_glyphs(ltk_text_line *tl) {
 	tl->x_min = x_min;
 	tl->y_min = y_min;
 	tl->w = x_max - x_min;
-	tl->h = y_max - y_min;
+	tl->h = tl->line_h = y_max - y_min;
 }
 
 ltk_text_line *
 ltk_text_line_create(Window window, uint16_t font_size, char *text, int width) {
-	(void)width;
 	ltk_text_line *line = ltk_malloc(sizeof(ltk_text_line));
 	line->window = window;
 	line->img = NULL;
@@ -572,7 +635,11 @@ ltk_text_line_create(Window window, uint16_t font_size, char *text, int width) {
 	line->glyph_len = u8_strlen(text);
 	line->glyphs = ltk_malloc(line->glyph_len * sizeof(ltk_glyph));
 	line->font_size = font_size;
+	line->w_max = width;
 	ltk_text_line_create_glyphs(line);
+	line->lines_alloc = line->lines = 0;
+	line->line_indeces = NULL;
+	ltk_text_line_break_lines(line);
 	return line;
 }