commit 91ef917f5e9467ba6f4c187b70ed98517010489e
parent 77153a68ac84bb58a6b1ce8328e279453c70eebe
Author: lumidify <nobody@lumidify.org>
Date: Wed, 20 May 2020 12:28:02 +0200
Store soft lines in ltk_text_line instead of ltk_text_edit
Diffstat:
4 files changed, 115 insertions(+), 82 deletions(-)
diff --git a/text_buffer.c b/text_buffer.c
@@ -48,31 +48,56 @@ 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);
+ if (sl->glyph_pos)
+ ltk_array_destroy_int(sl->glyph_pos);
+ if (sl->glyph_clusters)
+ ltk_array_destroy_uint32(sl->glyph_clusters);
free(sl);
}
+struct ltk_soft_line *
+ltk_soft_line_create(void) {
+ struct ltk_soft_line *sl = malloc(sizeof(struct ltk_soft_line));
+ if (!sl) {
+ (void)fprintf(stderr, "Error creating soft line\n");
+ exit(1);
+ }
+ sl->glyph_pos = NULL;
+ sl->glyph_clusters = NULL;
+ return sl;
+}
+
+static void
+ltk_text_line_cleanup_soft_lines(struct ltk_array_line *soft_lines, int old_len) {
+ /* remove old soft lines that aren't needed anymore */
+ for (int i = soft_lines->len; i < old_len; i++) {
+ ltk_soft_line_destroy(soft_lines->buf[i]);
+ }
+ ltk_array_resize_line(soft_lines, soft_lines->len);
+}
+
/* 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
+ logic and (most importantly) allow ltk_soft_line_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 *
+void
ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) {
- struct ltk_array_line *soft_lines = ltk_array_create_line(1);
+ tl->w_wrapped = max_width;
+ int old_len = tl->soft_lines->len;
+ tl->soft_lines->len = 0;
int par_is_rtl = tl->dir == HB_DIRECTION_RTL;
struct ltk_text_run *cur = par_is_rtl ? tl->last_run : tl->first_run;
LtkGlyph *glyph;
- struct ltk_soft_line *sl = malloc(sizeof(struct ltk_soft_line));
- if (!sl) goto error;
+ int cur_index = 0;
+ struct ltk_soft_line *sl = old_len > cur_index ? tl->soft_lines->buf[cur_index] : ltk_soft_line_create();
sl->glyph_index = cur->dir == HB_DIRECTION_RTL ? cur->num_glyphs - 1 : 0;
sl->run = cur;
sl->len = 0;
sl->w = 0;
- ltk_array_append_line(soft_lines, sl);
+ ltk_array_append_line(tl->soft_lines, sl);
if (max_width == -1) {
cur = tl->first_run;
while (cur) {
@@ -80,7 +105,10 @@ ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) {
sl->w += cur->w;
cur = cur->next;
}
- return soft_lines;
+ ltk_text_line_cleanup_soft_lines(tl->soft_lines, old_len);
+ tl->w_wrapped = tl->w;
+ tl->h_wrapped = tl->h;
+ return;
}
int last_linebreak = par_is_rtl ? tl->w : 0;
int cur_start = 0;
@@ -113,13 +141,15 @@ ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) {
}
sl->w += cur_start - last_linebreak;
cur_start = last_linebreak;
- sl = malloc(sizeof(struct ltk_soft_line));
- if (!sl) goto error;
+ cur_index++;
+ sl = old_len > cur_index ?
+ tl->soft_lines->buf[cur_index] :
+ ltk_soft_line_create();
sl->glyph_index = i;
sl->run = cur;
sl->len = 0;
sl->w = 0;
- ltk_array_append_line(soft_lines, sl);
+ ltk_array_append_line(tl->soft_lines, sl);
break;
} else {
sl->len--;
@@ -153,13 +183,15 @@ ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) {
}
sl->w += last_linebreak - cur_start;
cur_start = last_linebreak;
- sl = malloc(sizeof(struct ltk_soft_line));
- if (!sl) goto error;
+ cur_index++;
+ sl = old_len > cur_index ?
+ tl->soft_lines->buf[cur_index] :
+ ltk_soft_line_create();
sl->glyph_index = i;
sl->run = cur;
sl->len = 0;
sl->w = 0;
- ltk_array_append_line(soft_lines, sl);
+ ltk_array_append_line(tl->soft_lines, sl);
break;
} else {
sl->len--;
@@ -178,10 +210,9 @@ ltk_text_line_wrap(struct ltk_text_line *tl, int max_width) {
}
cur = par_is_rtl ? cur->last : cur->next;
}
- return soft_lines;
-error:
- (void)fprintf(stderr, "Error allocating memory while wrapping text line.\n");
- exit(1);
+ ltk_text_line_cleanup_soft_lines(tl->soft_lines, old_len);
+ tl->w_wrapped = max_width;
+ tl->h_wrapped = tl->soft_lines->len * tl->h;
}
XImage *
@@ -206,32 +237,28 @@ 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_soft_line_draw_glyph(LtkGlyph *glyph, struct ltk_soft_line *sl, int x, int y, XColor fg) {
+ltk_soft_line_draw_glyph(LtkGlyph *glyph, XImage *img, 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);
+ ltk_array_append_uint32(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 >= sl->img->height || x + j >= sl->img->width)
+ if (y + i >= img->height || x + j >= img->width)
continue;
- b = (y + i) * sl->img->bytes_per_line + (x + j) * 4;
+ b = (y + i) * img->bytes_per_line + (x + j) * 4;
a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0;
- 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;
+ 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;
}
}
}
-/* 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 *
-ltk_render_text_line_new(
+/* FIXME: Fix memory leaks... */
+void
+ltk_text_line_render(
struct ltk_text_line *tl,
- int max_width,
Display *dpy,
Window window,
GC gc,
@@ -241,18 +268,25 @@ ltk_render_text_line_new(
{
LtkGlyph *glyph;
int par_is_rtl = tl->dir == HB_DIRECTION_RTL;
- struct ltk_array_line *soft_lines = ltk_text_line_wrap(tl, max_width);
XWindowAttributes attrs;
XGetWindowAttributes(dpy, window, &attrs);
int depth = attrs.depth;
+ /* FIXME: if tl->img has same dimensions, just clear it */
+ if (tl->img) XDestroyImage(tl->img);
+ tl->img = ltk_create_ximage(dpy, tl->w_wrapped, tl->h_wrapped, depth, bg);
- for (int i = 0; i < soft_lines->len; i++) {
- struct ltk_soft_line *sl = soft_lines->buf[i];
+ for (int i = 0; i < tl->soft_lines->len; i++) {
+ struct ltk_soft_line *sl = tl->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);
+ if (!sl->glyph_pos)
+ sl->glyph_pos = ltk_array_create_int(tl->len);
+ else
+ sl->glyph_pos->len = 0;
+ if (!sl->glyph_clusters)
+ sl->glyph_clusters = ltk_array_create_uint32(tl->len);
+ else
+ sl->glyph_clusters->len = 0;
struct ltk_text_run *cur = sl->run;
size_t cur_len = 0;
int cur_border = par_is_rtl ? sl->w : 0;
@@ -267,17 +301,17 @@ ltk_render_text_line_new(
else
end_index = start_index - (sl->len - cur_len) + 1;
if (par_is_rtl) {
- for (int i = start_index; i >= 0 && cur_len < sl->len; i--) {
+ for (int j = start_index; j >= 0 && cur_len < sl->len; j--) {
cur_len++;
- int x = cur_border - (local_border - cur->glyphs[i].x_abs);
- ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg);
+ int x = cur_border - (local_border - cur->glyphs[j].x_abs) + tl->img->width - sl->w;
+ ltk_soft_line_draw_glyph(&cur->glyphs[j], tl->img, sl, x, cur->glyphs[j].y_abs + tl->h * i, 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++) {
+ for (int j = end_index; j <= start_index && cur_len < sl->len; j++) {
cur_len++;
- int x = cur_border + (cur->glyphs[i].x_abs - cur->glyphs[end_index].x_abs);
- ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg);
+ int x = cur_border + (cur->glyphs[j].x_abs - cur->glyphs[end_index].x_abs);
+ ltk_soft_line_draw_glyph(&cur->glyphs[j], tl->img, sl, x, cur->glyphs[j].y_abs + tl->h * i, fg);
}
cur_border += local_border - cur->glyphs[end_index].x_abs;
}
@@ -290,17 +324,17 @@ ltk_render_text_line_new(
else
end_index = start_index + sl->len - cur_len - 1;
if (par_is_rtl) {
- for (int i = end_index; i >= start_index && cur_len < sl->len; i--) {
+ for (int j = end_index; j >= start_index && cur_len < sl->len; j--) {
cur_len++;
- int x = cur_border - (cur->glyphs[end_index].x_abs + cur->glyphs[end_index].x_advance - cur->glyphs[i].x_abs);
- ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg);
+ int x = cur_border - (cur->glyphs[end_index].x_abs + cur->glyphs[end_index].x_advance - cur->glyphs[j].x_abs) + tl->img->width - sl->w;
+ ltk_soft_line_draw_glyph(&cur->glyphs[j], tl->img, sl, x, cur->glyphs[j].y_abs + tl->h * i, 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++) {
+ for (int j = start_index; j < cur->num_glyphs && cur_len < sl->len; j++) {
cur_len++;
- int x = cur_border + (cur->glyphs[i].x_abs - local_border);
- ltk_soft_line_draw_glyph(&cur->glyphs[i], sl, x, cur->glyphs[i].y_abs, fg);
+ int x = cur_border + (cur->glyphs[j].x_abs - local_border);
+ ltk_soft_line_draw_glyph(&cur->glyphs[j], tl->img, sl, x, cur->glyphs[j].y_abs + tl->h * i, fg);
}
cur_border += cur->glyphs[cur->num_glyphs - 1].x_abs + cur->glyphs[cur->num_glyphs - 1].x_advance - local_border;
}
@@ -308,8 +342,6 @@ ltk_render_text_line_new(
cur = par_is_rtl ? cur->last : cur->next;
}
}
-
- return soft_lines;
}
uint32_t
@@ -649,10 +681,10 @@ ltk_text_run_shape(LtkTextManager *tm, struct ltk_text_run *tr,
static void
ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
struct ltk_text_run *run = tl->first_run;
- tl->y_max = INT_MIN;
- tl->y_min = INT_MAX;
tl->w = tl->h = 0;
- int x_max, y_max;
+ int y_max;
+ int y_max_overall = INT_MIN;
+ int y_min_overall = INT_MAX;
while (run) {
/* Question: Why does this not work with Duplicate? */
FcPattern *pat = FcPatternCreate();//FcPatternDuplicate(tm->fcpattern);
@@ -675,15 +707,15 @@ ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
FcPatternDestroy(match);
FcPatternDestroy(pat);
ltk_text_run_shape(tm, run, tl, tl->font_size, font_id, &y_max);
- if (tl->y_max < y_max)
- tl->y_max = y_max;
+ if (y_max_overall < y_max)
+ y_max_overall = y_max;
/* tr->start_y is -y_min */
- if (tl->y_min > -run->start_y)
- tl->y_min = -run->start_y;
+ if (y_min_overall > -run->start_y)
+ y_min_overall = -run->start_y;
tl->w += run->w;
run = run->next;
};
- tl->h = tl->y_max - tl->y_min;
+ tl->h = y_max_overall - y_min_overall;
/* calculate the actual position of the characters */
run = tl->first_run;
@@ -691,7 +723,7 @@ ltk_text_line_shape(LtkTextManager *tm, struct ltk_text_line *tl) {
LtkGlyph *glyph;
while (run) {
int cur_x = x + run->start_x;
- int cur_y = tl->h - tl->y_max; /* baseline (I think?) */
+ int cur_y = tl->h - y_max_overall; /* baseline (I think?) */
for (int i = 0; i < run->len; i++) {
glyph = &run->glyphs[i];
glyph->x_abs = cur_x + glyph->info->xoff + glyph->x_offset;
@@ -807,13 +839,13 @@ ltk_text_line_create(void) {
line->vis2log = ltk_array_create_int(4);
line->scripts = ltk_array_create_script(4);
line->bidi_levels = ltk_array_create_level(4);
- line->wrap_indeces = ltk_array_create_uint32(1);
+ line->soft_lines = ltk_array_create_line(1);
line->first_run = NULL;
- line->cur_run = NULL;
line->next = NULL;
- line->height = 0;
line->len = 0;
line->cursor_pos = 0;
+ line->w_wrapped = line->h_wrapped = 0;
+ line->img = NULL;
/* FIXME */
line->font_size = 20;
line->line_gap = 5;
@@ -843,7 +875,7 @@ ltk_text_line_destroy(struct ltk_text_line *tl) {
ltk_array_destroy_int(tl->log2vis);
ltk_array_destroy_int(tl->vis2log);
ltk_array_destroy_level(tl->bidi_levels);
- ltk_array_destroy_uint32(tl->wrap_indeces);
+ ltk_array_destroy_deep_line(tl->soft_lines, <k_soft_line_destroy);
ltk_text_line_destroy_runs(tl->first_run);
free(tl);
}
diff --git a/text_buffer.h b/text_buffer.h
@@ -60,9 +60,8 @@ struct ltk_soft_line {
size_t len;
int w;
struct ltk_text_run *run;
- XImage *img;
struct ltk_array_int *glyph_pos;
- struct ltk_array_int *glyph_clusters;
+ struct ltk_array_uint32 *glyph_clusters;
};
LTK_ARRAY_INIT_DECL(line, struct ltk_soft_line *)
@@ -74,31 +73,31 @@ struct ltk_text_line {
struct ltk_array_int *log2vis;
struct ltk_array_int *vis2log;
struct ltk_array_level *bidi_levels;
- struct ltk_array_int *wrap_indeces;
+ struct ltk_array_line *soft_lines;
struct ltk_text_run *first_run; /* first node in the linked list of runs */
struct ltk_text_run *last_run; /* last node in the linked list of runs */
- struct ltk_text_run *cur_run; /* current node in the linked list of runs */
struct ltk_text_line *next; /* next text line in the buffer */
- unsigned int height; /* height of the line (including wrapping) */
hb_direction_t dir; /* overall paragraph direction */
size_t len;
uint16_t font_size;
- int y_max;
- int y_min;
int w;
int h;
+ int w_wrapped;
+ int h_wrapped;
size_t cursor_pos;
unsigned int line_gap;
+ XImage *img;
};
struct ltk_text_buffer {
struct ltk_text_line *head;
struct ltk_text_line *cur_line;
+ size_t cursor_pos;
unsigned int line_gap;
};
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,
+void ltk_text_line_render(struct ltk_text_line *tl,
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_text_line *tl, struct ltk_soft_line *sl, int *found_pos);
void ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len);
diff --git a/text_edit.c b/text_edit.c
@@ -45,6 +45,12 @@ ltk_draw_text_edit(LtkTextEdit *te) {
XColor bg = ltk_global->theme->window->bg;
LtkRect rect = te->widget.rect;
LtkWindow *window = te->widget.window;
+ /* FIXME: need special "resize" function called by grid, etc.
+ to avoid recalculating everything when it really only has to drawn */
+ ltk_text_line_wrap(te->tl, rect.w);
+ ltk_text_line_render(te->tl, ltk_global->display, window->xwindow, window->gc, ltk_global->colormap, fg, bg);
+ XPutImage(ltk_global->display, window->xwindow, window->gc, te->tl->img, 0, 0, rect.x, rect.y, te->tl->img->width, te->tl->img->height);
+ /*
if (!te->soft_lines)
te->soft_lines = ltk_render_text_line_new(te->tl, rect.w, ltk_global->display, window->xwindow, window->gc, ltk_global->colormap, fg, bg);
XSetForeground(ltk_global->display, window->gc, bg.pixel);
@@ -56,8 +62,10 @@ ltk_draw_text_edit(LtkTextEdit *te) {
x += rect.w - img->width;
XPutImage(ltk_global->display, window->xwindow, window->gc, img, 0, 0, x, rect.y + te->tl->h * i, img->width, img->height);
}
+ */
}
+#if 0
void
ltk_text_edit_tmp(LtkTextEdit *te, XEvent event) {
/* this should never be negative, but just to be sure... */
@@ -71,6 +79,7 @@ ltk_text_edit_tmp(LtkTextEdit *te, XEvent event) {
te->cursor = ltk_soft_line_get_index_from_pos(
x, te->tl, te->soft_lines->buf[i], &found_pos);
}
+#endif
LtkTextEdit *
ltk_create_text_edit(LtkWindow *window, const char *text) {
@@ -78,10 +87,9 @@ ltk_create_text_edit(LtkWindow *window, const char *text) {
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->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;
}
@@ -90,16 +98,11 @@ 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);
- te->soft_lines = NULL;
/* FIXME: Need to "queue redraw" for whole window */
ltk_draw_text_edit(te);
}
void ltk_destroy_text_edit(LtkTextEdit *te) {
ltk_text_line_destroy(te->tl);
- if (te->soft_lines)
- ltk_array_destroy_deep_line(te->soft_lines, <k_soft_line_destroy);
free(te);
}
diff --git a/text_edit.h b/text_edit.h
@@ -28,7 +28,6 @@ typedef struct {
LtkWidget widget;
uint32_t cursor;
struct ltk_text_line *tl;
- struct ltk_array_line *soft_lines;
} LtkTextEdit;
/* FIXME: standardize ltk_<widget>_destroy, etc. instead of ltk_destroy_<widget> */