commit b05e1135200febcf01061df3e109943b6d828b8f
parent f344c3f28da7995f19ec0549d90f275d3fd1715a
Author: lumidify <nobody@lumidify.org>
Date: Fri, 8 May 2020 21:13:42 +0200
Fix gap buffer and clean up textedit
Diffstat:
M | gap_buffer.h | | | 64 | +++++++++++++++++++++++++++++++++------------------------------- |
M | textedit_wip.c | | | 619 | +++++++++++++++---------------------------------------------------------------- |
M | textedit_wip.h | | | 139 | +++++++------------------------------------------------------------------------ |
3 files changed, 162 insertions(+), 660 deletions(-)
diff --git a/gap_buffer.h b/gap_buffer.h
@@ -27,31 +27,32 @@
#include <stdio.h>
#include <stdlib.h>
-#define LTK_GAP_BUFFER_INIT_DECL(type) \
-struct ltk_gap_buffer_##type## { \
+#define LTK_GAP_BUFFER_INIT_DECL(name, type) \
+struct ltk_gap_buffer_##name## { \
type *buf; \
size_t buf_size; \
size_t gap_left; \
size_t gap_size; \
}; \
\
-struct ltk_gap_buffer_##type## * ltk_gap_buffer_create_##type##(void); \
-struct ltk_gap_buffer_##type## * \
-ltk_gap_buffer_create_from_data_##type##(type *data, size_t len); \
-void ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len); \
-void ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb, \
+struct ltk_gap_buffer_##name## * ltk_gap_buffer_create_##name##(void); \
+struct ltk_gap_buffer_##name## * \
+ltk_gap_buffer_create_from_data_##name##(type *data, size_t len); \
+void ltk_gap_buffer_resize_gap_##name##( \
+ struct ltk_gap_buffer_##name## *gb, int len); \
+void ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##type## *gb, \
type *new, size_t start, size_t len); \
-void ltk_gap_buffer_insert_single_##type##( \
- struct ltk_gap_buffer_##type## *gb, type new); \
-void ltk_gap_buffer_move_gap_##type##( \
- struct ltk_gap_buffer_##type## *gb, size_t pos); \
-void ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb);
+void ltk_gap_buffer_insert_single_##name##( \
+ struct ltk_gap_buffer_##name## *gb, type new); \
+void ltk_gap_buffer_move_gap_##name##( \
+ struct ltk_gap_buffer_##name## *gb, size_t pos); \
+void ltk_gap_buffer_destroy_##name##(struct ltk_gap_buffer_##name## *gb);
-#define LTK_GAP_BUFFER_INIT_IMPL(type) \
-struct ltk_gap_buffer_##type## * \
-ltk_gap_buffer_create_##type##(void) { \
- struct ltk_gap_buffer_##type## *gb = \
- malloc(sizeof(struct ltk_gap_buffer)); \
+#define LTK_GAP_BUFFER_INIT_IMPL(name, type) \
+struct ltk_gap_buffer_##name## * \
+ltk_gap_buffer_create_##name##(void) { \
+ struct ltk_gap_buffer_##name## *gb = \
+ malloc(sizeof(struct ltk_gap_buffer_##name##)); \
if (!gb) \
goto error; \
gb->buf = malloc(8 * sizeof(type)); \
@@ -67,10 +68,10 @@ error: \
exit(1); \
} \
\
-struct ltk_gap_buffer_##type## * \
-ltk_gap_buffer_create_from_data_##type##(type *data, size_t len) { \
- struct ltk_gap_buffer_##type## *gb = \
- malloc(sizeof(struct ltk_gap_buffer)); \
+struct ltk_gap_buffer_##name## * \
+ltk_gap_buffer_create_from_data_##name##(type *data, size_t len) { \
+ struct ltk_gap_buffer_##name## *gb = \
+ malloc(sizeof(struct ltk_gap_buffer_##name##)); \
if (!gb) { \
(void)fprintf(stderr, "Out of memory while trying to" \
"allocate gap buffer\n"); \
@@ -84,14 +85,15 @@ ltk_gap_buffer_create_from_data_##type##(type *data, size_t len) { \
} \
\
void \
-ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len) { \
+ltk_gap_buffer_resize_gap_##name##( \
+ struct ltk_gap_buffer_##name## *gb, int len) { \
/* FIXME: Should this use realloc? It's usually more efficient, but \
in this case, I would still need to copy the part after the gap \
manually, so it could potentially be copied twice, which really \
wouldn't be good. Maybe use realloc if only a small part is after \
the gap and just regular malloc otherwise? */ \
int new_size = gb->buf_size - gb->gap-size + len; \
- struct ltk_gap_buffer_##type## *new = malloc(new_size * sizeof(type)); \
+ struct ltk_gap_buffer_##name## *new = malloc(new_size * sizeof(type)); \
if (!new) { \
(void)fprintf(stderr, "Out of memory while trying to" \
"resize gap buffer\n"); \
@@ -108,10 +110,10 @@ ltk_gap_buffer_resize_gap_##type##(struct ltk_gap_buffer *gb, int len) { \
} \
\
void \
-ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb, \
+ltk_gap_buffer_insert_##name##(struct ltk_gap_buffer_##name## *gb, \
type *new, size_t start, size_t len) { \
if (gb->gap_size < len) \
- ltk_gap_buffer_resize_gap_##type##(gb, len + 8); \
+ ltk_gap_buffer_resize_gap_##name##(gb, len + 8); \
for (int i = 0; i < len; i++) { \
gb->buf[gb->gap_left + i] = new[start + i]; \
} \
@@ -120,14 +122,14 @@ ltk_gap_buffer_insert_##type##(struct ltk_gap_buffer_##type## *gb, \
} \
\
void \
-ltk_gap_buffer_insert_single_##type##( \
- struct ltk_gap_buffer_##type## *gb, type new) { \
- ltk_gap_buffer_insert_##type##(gb, &new, 0, 1); \
+ltk_gap_buffer_insert_single_##name##( \
+ struct ltk_gap_buffer_##name## *gb, type new) { \
+ ltk_gap_buffer_insert_##name##(gb, &new, 0, 1); \
} \
\
void \
-ltk_gap_buffer_move_gap_##type##( \
- struct ltk_gap_buffer_##type## *gb, size_t pos) { \
+ltk_gap_buffer_move_gap_##name##( \
+ struct ltk_gap_buffer_##name## *gb, size_t pos) { \
if (pos == gb->gap_left) \
return; \
if (pos < 0 || pos > gb->buf_size - gb->gap_size) { \
@@ -148,7 +150,7 @@ ltk_gap_buffer_move_gap_##type##( \
} \
\
void \
-ltk_gap_buffer_destroy_##type##(struct ltk_gap_buffer_##type## *gb) { \
+ltk_gap_buffer_destroy_##name##(struct ltk_gap_buffer_##name## *gb) { \
free(gb->buf); \
free(gb); \
}
diff --git a/textedit_wip.c b/textedit_wip.c
@@ -1,6 +1,6 @@
/*
* This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2020 lumidify <nobody@lumidify.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -33,290 +33,145 @@
#include <fribidi.h>
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ot.h>
-#include "text-hb.h"
+#include "textedit_wip.h"
+#include "text-common.h"
#include "ltk.h"
extern Ltk *ltk_global;
-/* These unicode routines are taken from
- * https://github.com/JeffBezanson/cutef8 */
-
-/* is c the start of a utf8 sequence? */
-#define isutf(c) (((c)&0xC0)!=0x80)
-
-static const uint32_t offsetsFromUTF8[6] = {
- 0x00000000UL, 0x00003080UL, 0x000E2080UL,
- 0x03C82080UL, 0xFA082080UL, 0x82082080UL
-};
-
-/* next character without NUL character terminator */
-uint32_t u8_nextmemchar(const char *s, size_t *i)
-{
- uint32_t ch = 0;
- size_t sz = 0;
- do {
- ch <<= 6;
- ch += (unsigned char)s[(*i)++];
- sz++;
- } while (!isutf(s[*i]));
- ch -= offsetsFromUTF8[sz-1];
-
- return ch;
-}
-
-/* number of characters in NUL-terminated string */
-size_t u8_strlen(const char *s)
-{
- size_t count = 0;
- size_t i = 0, lasti;
-
- while (1) {
- lasti = i;
- while (s[i] > 0)
- i++;
- count += (i-lasti);
- if (s[i++]==0) break;
- (void)(isutf(s[++i]) || isutf(s[++i]) || ++i);
- count++;
- }
- return count;
-}
-
-size_t u8_wc_toutf8(char *dest, uint32_t ch)
-{
- if (ch < 0x80) {
- dest[0] = (char)ch;
- return 1;
- }
- if (ch < 0x800) {
- dest[0] = (ch>>6) | 0xC0;
- dest[1] = (ch & 0x3F) | 0x80;
- return 2;
- }
- if (ch < 0x10000) {
- dest[0] = (ch>>12) | 0xE0;
- dest[1] = ((ch>>6) & 0x3F) | 0x80;
- dest[2] = (ch & 0x3F) | 0x80;
- return 3;
- }
- if (ch < 0x110000) {
- dest[0] = (ch>>18) | 0xF0;
- dest[1] = ((ch>>12) & 0x3F) | 0x80;
- dest[2] = ((ch>>6) & 0x3F) | 0x80;
- dest[3] = (ch & 0x3F) | 0x80;
- return 4;
- }
- return 0;
-}
-
-LtkTextManager *
-ltk_init_text(char *font_name)
-{
- LtkTextManager *tm = malloc(sizeof(LtkTextManager));
- if (!tm) {
- (void)printf("Memory exhausted when trying to create text manager.");
- exit(1);
- }
- tm->font_paths = kh_init(fontid);
- tm->font_cache = kh_init(fontstruct);
- tm->glyph_cache = kh_init(glyphcache);
- tm->font_id_cur = 0;
- ltk_load_default_font(tm, font_name);
-
- return tm;
-}
-
-void
-ltk_destroy_text_manager(LtkTextManager *tm)
+/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then,
+ or just use harfbuzz (then fribidi doesn't need to do any shaping) */
+LtkTextLine *
+ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
{
+ /* NOTE: This doesn't actually take fontid into account right now - should it? */
+ LtkTextLine *tl = malloc(sizeof(LtkTextLine));
+ tl->start_segment = NULL;
+ LtkTextSegment *cur_ts = NULL;
+ LtkTextSegment *new_ts = NULL;
+ uint16_t cur_font_id = fontid;
int k;
+ LtkFont *font;
- kh_destroy(fontid, tm->font_paths);
-
- for (k = kh_begin(tm->font_cache); k != kh_end(tm->font_cache); k++) {
- if (kh_exist(tm->font_cache, k)) {
- ltk_destroy_font(kh_value(tm->font_cache, k));
- }
- }
- kh_destroy(fontstruct, tm->font_cache);
-
- for (k = kh_begin(tm->glyph_cache); k != kh_end(tm->glyph_cache); k++) {
- if (kh_exist(tm->glyph_cache, k)) {
- ltk_destroy_glyph_cache(kh_value(tm->glyph_cache, k));
- }
- }
- kh_destroy(glyphcache, tm->glyph_cache);
-
- free(tm);
-}
-
-LtkGlyphInfo *
-ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale)
-{
- LtkGlyphInfo *glyph = malloc(sizeof(LtkGlyphInfo));
- if (!glyph) {
- (void)printf("Out of memory!\n");
- exit(1);
- }
-
- glyph->id = id;
- glyph->refs = 0;
- glyph->alphamap = stbtt_GetGlyphBitmap(
- &font->info, scale, scale, id, &glyph->w,
- &glyph->h, &glyph->xoff, &glyph->yoff
- );
-
- return glyph;
-}
-
-void
-ltk_destroy_glyph_info(LtkGlyphInfo *gi)
-{
- free(gi->alphamap);
- free(gi);
-}
-
-LtkGlyphInfo *
-ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache)
-{
- int ret;
- khint_t k;
- LtkGlyphInfo *glyph;
- k = kh_get(glyphinfo, cache, id);
- if (k == kh_end(cache)) {
- glyph = ltk_create_glyph_info(font, id, scale);
- /* FIXME: error checking with ret */
- k = kh_put(glyphinfo, cache, id, &ret);
- kh_value(cache, k) = glyph;
- } else {
- glyph = kh_value(cache, k);
+ unsigned int ulen = u8_strlen(text);
+ uint32_t *log_str = malloc(sizeof(uint32_t) * ulen);
+ size_t inc = 0;
+ for (int i = 0; i < ulen; i++) {
+ log_str[i] = u8_nextmemchar(text, &inc);
}
+ FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen);
+ ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str);
+ fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL);
- return glyph;
-}
-
-khint_t
-ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size)
-{
- khash_t(glyphinfo) *cache = kh_init(glyphinfo);
- int ret;
- khint_t k;
- /* I guess I can just ignore ret for now */
- k = kh_put(glyphcache, tm->glyph_cache, font_id << 16 + font_size, &ret);
- kh_value(tm->glyph_cache, k) = cache;
+ hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
+ hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]);
+ hb_script_t last_script = cur_script;
+ size_t pos = 0;
+ size_t last_pos = 0;
+ size_t start_pos = 0;
+ uint32_t ch;
- return k;
-}
+ for (int p = 0; p <= ulen; p++) {
+ cur_script = hb_unicode_script(ufuncs, vis_str[p]);
+ if (p == ulen ||
+ (last_script != cur_script &&
+ cur_script != HB_SCRIPT_INHERITED &&
+ cur_script != HB_SCRIPT_COMMON)) {
+ FcPattern *pat = FcPatternDuplicate(tm->fcpattern);
+ FcPattern *match;
+ FcResult result;
+ FcPatternAddBool(pat, FC_SCALABLE, 1);
+ FcConfigSubstitute(NULL, pat, FcMatchPattern);
+ FcDefaultSubstitute(pat);
+ FcCharSet *cs = FcCharSetCreate();
+ for (int i = start_pos; i < p; i++) {
+ FcCharSetAddChar(cs, vis_str[i]);
+ }
+ FcPatternAddCharSet(pat, FC_CHARSET, cs);
+ match = FcFontMatch(NULL, pat, &result);
+ char *file;
+ FcPatternGetString(match, FC_FILE, 0, &file);
+ cur_font_id = ltk_get_font(tm, file);
+ k = kh_get(fontstruct, tm->font_cache, cur_font_id);
+ font = kh_value(tm->font_cache, k);
+ FcPatternDestroy(match);
+ FcPatternDestroy(pat);
+ // handle case that this is the last character
+ if (p == ulen) {
+ last_script = cur_script;
+ }
+ /* FIXME: There should be better handling for cases
+ where an error occurs while creating the segment */
+ new_ts = ltk_create_text_segment(
+ tm, vis_str + start_pos,
+ p - start_pos, cur_font_id,
+ size, last_script
+ );
+ if (!new_ts) continue;
+ new_ts->next = NULL;
+ if (!tl->start_segment) tl->start_segment = new_ts;
+ if (cur_ts) cur_ts->next = new_ts;
+ cur_ts = new_ts;
-void
-ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache)
-{
- int k;
- for (k = kh_begin(cache); k != kh_end(cache); k++) {
- if (kh_exist(cache, k)) {
- ltk_destroy_glyph_info(kh_value(cache, k));
+ start_pos = p;
+ last_script = cur_script;
}
}
- kh_destroy(glyphinfo, cache);
-}
-
-void
-ltk_load_default_font(LtkTextManager *tm, char *name)
-{
- FcPattern *match;
- FcResult result;
- char *file;
- int index;
- uint16_t font;
- tm->fcpattern = FcNameParse(name);
- FcPatternAddString(tm->fcpattern, FC_FONTFORMAT, "truetype");
- FcConfigSubstitute(NULL, tm->fcpattern, FcMatchPattern);
- FcDefaultSubstitute(tm->fcpattern);
- match = FcFontMatch(NULL, tm->fcpattern, &result);
-
- FcPatternGetString (match, FC_FILE, 0, (FcChar8 **) &file);
- /* FIXME: Why is index never used? This is the index within the font file,
- so it might be important, although I'm not sure if stb_truetype even
- supports it */
- FcPatternGetInteger (match, FC_INDEX, 0, &index);
-
- tm->default_font = ltk_get_font(tm, file);
-
- FcPatternDestroy (match);
-}
+ free(vis_str);
+ free(log_str);
-LtkFont *
-ltk_create_font(char *path, uint16_t id)
-{
- long len;
- LtkFont *font = malloc(sizeof(LtkFont));
- if (!font) {
- (void)fprintf(stderr, "Out of memory!\n");
- exit(1);
+ /* calculate width of text line
+ NOTE: doesn't work with mixed horizontal and vertical text */
+ LtkTextSegment *ts = tl->start_segment;
+ int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
+ tl->y_max = tl->x_max = INT_MIN;
+ tl->y_min = tl->x_min = INT_MAX;
+ tl->w = tl->h = 0;
+ while (ts) {
+ if (HB_DIRECTION_IS_HORIZONTAL(ts->dir) != is_hor) {
+ (void)fprintf(stderr, "WARNING: mixed horizontal/vertical text is not supported; ignoring\n");
+ continue;
+ }
+ if (is_hor) {
+ if (tl->y_max < ts->y_max) {
+ tl->y_max = ts->y_max;
+ }
+ if (tl->y_min > ts->y_min) {
+ tl->y_min = ts->y_min;
+ }
+ tl->w += ts->w;
+ } else {
+ if (tl->x_max < ts->x_max) {
+ tl->x_max = ts->x_max;
+ }
+ if (tl->x_min > ts->x_min) {
+ tl->x_min = ts->x_min;
+ }
+ tl->h += ts->h;
+ }
+ ts = ts->next;
}
- char *contents = ltk_read_file(path, &len);
- if (!stbtt_InitFont(&font->info, contents, 0))
- {
- (void)fprintf(stderr, "Failed to load font %s\n", path);
- exit(1);
+ if (is_hor) {
+ tl->h = tl->y_max - tl->y_min;
+ } else {
+ tl->w = tl->x_max - tl->x_min;
}
- /* FIXME: make use of the destroy function (last argument to hb_blob_create - see hb-blob.cc in harfbuzz source) */
- hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
- hb_face_t *face = hb_face_create(blob, 0);
- /* FIXME: need to use destroy function in order for the original file data to be freed? */
- hb_blob_destroy(blob);
- font->hb = hb_font_create(face);
- hb_face_destroy(face);
- hb_ot_font_set_funcs(font->hb);
- font->id = id;
- font->refs = 0;
- return font;
+ return tl;
}
void
-ltk_destroy_font(LtkFont *font)
-{
- free(font->info.data);
- hb_font_destroy(font->hb);
- free(font);
-}
-
-uint16_t
-ltk_load_font(LtkTextManager *tm, char *path)
-{
- LtkFont *font = ltk_create_font(path, tm->font_id_cur++);
- int ret;
- khint_t k;
- /* FIXME: does kh_destroy also free these copied strings properly? */
- char *key = strdup(path);
- k = kh_put(fontid, tm->font_paths, key, &ret);
- kh_value(tm->font_paths, k) = font->id;
- k = kh_put(fontstruct, tm->font_cache, (khint_t) font->id, &ret);
- kh_value(tm->font_cache, k) = font;
- k = kh_get(fontid, tm->font_paths, path);
-
- return font->id;
-}
-
-uint16_t
-ltk_get_font(LtkTextManager *tm, char *path)
-{
- int ret;
- khint_t k;
- uint16_t id;
- k = kh_get(fontid, tm->font_paths, path);
- if (k == kh_end(tm->font_paths)) {
- id = ltk_load_font(tm, path);
- } else {
- id = kh_value(tm->font_paths, k);
+ltk_destroy_text_line(LtkTextLine *tl) {
+ LtkTextSegment *last_ts;
+ LtkTextSegment *cur_ts = tl->start_segment;
+ while (cur_ts) {
+ last_ts = cur_ts;
+ cur_ts = cur_ts->next;
+ ltk_destroy_text_segment(last_ts);
}
-
- return id;
}
-
/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
-> in case I want to get rid of uint_16_t, etc. */
LtkTextSegment *
@@ -440,18 +295,6 @@ ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int len, ui
}
void
-ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache)
-{
- int k;
- if (--glyph->info->refs < 1) {
- k = kh_get(glyphinfo, cache, glyph->info->id);
- kh_del(glyphinfo, cache, k);
- ltk_destroy_glyph_info(glyph->info);
- }
- free(glyph);
-}
-
-void
ltk_destroy_text_segment(LtkTextSegment *ts)
{
LtkGlyph *glyph, *next_glyph;
@@ -554,6 +397,7 @@ ltk_render_text_segment(
}
/*
+NOTE: The following notes are outdated.
Notes: When inserting a character, check what the direction of the surrounding
script is - if it is the same (or a weak direction), then just insert the char
without re-doing the bidi algorithm. Only redo the whole line/paragraph is the
@@ -577,102 +421,6 @@ soon as a different script is inserted, everything has to be redone anyways. Als
when reshaping with context, only the text in the current run has to be passed at all.
*/
-struct ltk_gap_buffer *
-ltk_gap_buffer_create(void) {
- struct ltk_gap_buffer *gb = malloc(sizeof(struct ltk_gap_buffer));
- if (!gb)
- goto error;
- gb->buf = malloc(8 * sizeof(uint32_t));
- if (!gb->buf)
- goto error;
- gb->buf_size = 8;
- gb->gap_left = 0;
- gb->gap_size = 8;
- return gb;
-error:
- (void)fprintf(stderr, "Out of memory while trying to allocate gap buffer\n");
- exit(1);
-}
-
-struct ltk_gap_buffer *
-ltk_gap_buffer_create_from_data(uint32_t *data, size_t len) {
- struct ltk_gap_buffer *gb = malloc(sizeof(struct ltk_gap_buffer));
- if (!gb) {
- (void)fprintf(stderr, "Out of memory while trying to allocate gap buffer\n");
- exit(1);
- }
- gb->buf = data;
- gb->buf_size = len;
- gb->gap_left = 0;
- gb->gap_size = 0;
- return gb;
-}
-
-void
-ltk_gap_buffer_resize_gap(struct ltk_gap_buffer *gb, int len) {
- /* FIXME: Should this use realloc? It's usually more efficient, but
- in this case, I would still need to copy the part after the gap
- manually, so it could potentially be copied twice, which really
- wouldn't be good. Maybe use realloc if only a small part is after
- the gap and just regular malloc otherwise? */
- int new_size = gb->buf_size - gb->gap-size + len;
- struct ltk_gap_buffer *new = malloc(new_size * sizeof(uint32_t));
- if (!new) {
- (void)fprintf(stderr, "Out of memory while trying to resize gap buffer\n");
- exit(1);
- }
- for (int i = 0; i < gb->gap_left; i++) {
- new[i] = gb->buf[i];
- }
- for (int i = gb->gap_left + gb->gap_size; i < gb->buf_size) {
- new[i - gb->gap_size + len] = gb->buf[i];
- }
- free(gb->buf);
- gb->buf = new;
-}
-
-void
-ltk_gap_buffer_insert(struct ltk_gap_buffer *gb, uint32_t *new, size_t start, size_t len) {
- if (gb->gap_size < len)
- ltk_gap_buffer_resize_gap(gb, len + 8);
- for (int i = 0; i < len; i++) {
- gb->buf[gb->gap_left + i] = new[start + i];
- }
- gb->gap_left = gb->gap_left + len;
- gb->gap_size -= len;
-}
-
-void
-ltk_gap_buffer_insert_single(struct ltk_gap_buffer *gb, uint32_t new) {
- ltk_gap_buffer_insert(gb, &new, 0, 1);
-}
-
-void
-ltk_gap_buffer_move_gap(struct ltk_gap_buffer *gb, size_t pos) {
- if (pos == gb->gap_left)
- return;
- if (pos < 0 || pos > gb->buf_size - gb->gap_size) {
- (void)fprintf(stderr, "Index out of range while moving gap buffer gap\n");
- return;
- }
- if (pos >= gb->gap_left) {
- for (int i = gb->gap_left; i < pos) {
- gb->buf[i] = gb->buf[i + gb->gap_size];
- }
- } else {
- for (int i = gb->gap_left - 1; i >= pos; i--) {
- gb->buf[i + gb->gap_size] = gb->buf[i];
- }
- }
- gb->gap_left = pos;
-}
-
-void
-ltk_gap_buffer_destroy(struct ltk_gap_buffer *gb) {
- free(gb->buf);
- free(gb);
-}
-
void
ltk_text_line_insert_text(struct ltk_text_line *tl, uint32_t *text, size_t len) {
/* check if any characters have a different script, only recalc then */
@@ -685,136 +433,3 @@ ltk_text_line_delete_text(struct ltk_text_line *tl, size_t len) {
void
ltk_text_line_delete_cur_cluster(struct ltk_text_line *tl) {
}
-
-/* FIXME: allow to either use fribidi for basic shaping and don't use harfbuzz then,
- or just use harfbuzz (then fribidi doesn't need to do any shaping) */
-LtkTextLine *
-ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size)
-{
- /* NOTE: This doesn't actually take fontid into account right now - should it? */
- LtkTextLine *tl = malloc(sizeof(LtkTextLine));
- tl->start_segment = NULL;
- LtkTextSegment *cur_ts = NULL;
- LtkTextSegment *new_ts = NULL;
- uint16_t cur_font_id = fontid;
- int k;
- LtkFont *font;
-
- unsigned int ulen = u8_strlen(text);
- uint32_t *log_str = malloc(sizeof(uint32_t) * ulen);
- size_t inc = 0;
- for (int i = 0; i < ulen; i++) {
- log_str[i] = u8_nextmemchar(text, &inc);
- }
- FriBidiChar *vis_str = malloc(sizeof(FriBidiChar) * ulen);
- ulen = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, text, strlen(text), log_str);
- fribidi_log2vis(log_str, ulen, pbase_dir, vis_str, NULL, NULL, NULL);
-
- hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default();
- hb_script_t cur_script = hb_unicode_script(ufuncs, vis_str[0]);
- hb_script_t last_script = cur_script;
- size_t pos = 0;
- size_t last_pos = 0;
- size_t start_pos = 0;
- uint32_t ch;
-
- for (int p = 0; p <= ulen; p++) {
- cur_script = hb_unicode_script(ufuncs, vis_str[p]);
- if (p == ulen ||
- (last_script != cur_script &&
- cur_script != HB_SCRIPT_INHERITED &&
- cur_script != HB_SCRIPT_COMMON)) {
- FcPattern *pat = FcPatternDuplicate(tm->fcpattern);
- FcPattern *match;
- FcResult result;
- FcPatternAddBool(pat, FC_SCALABLE, 1);
- FcConfigSubstitute(NULL, pat, FcMatchPattern);
- FcDefaultSubstitute(pat);
- FcCharSet *cs = FcCharSetCreate();
- for (int i = start_pos; i < p; i++) {
- FcCharSetAddChar(cs, vis_str[i]);
- }
- FcPatternAddCharSet(pat, FC_CHARSET, cs);
- match = FcFontMatch(NULL, pat, &result);
- char *file;
- FcPatternGetString(match, FC_FILE, 0, &file);
- cur_font_id = ltk_get_font(tm, file);
- k = kh_get(fontstruct, tm->font_cache, cur_font_id);
- font = kh_value(tm->font_cache, k);
- FcPatternDestroy(match);
- FcPatternDestroy(pat);
- // handle case that this is the last character
- if (p == ulen) {
- last_script = cur_script;
- }
- /* FIXME: There should be better handling for cases
- where an error occurs while creating the segment */
- new_ts = ltk_create_text_segment(
- tm, vis_str + start_pos,
- p - start_pos, cur_font_id,
- size, last_script
- );
- if (!new_ts) continue;
- new_ts->next = NULL;
- if (!tl->start_segment) tl->start_segment = new_ts;
- if (cur_ts) cur_ts->next = new_ts;
- cur_ts = new_ts;
-
- start_pos = p;
- last_script = cur_script;
- }
- }
-
- free(vis_str);
- free(log_str);
-
- /* calculate width of text line
- NOTE: doesn't work with mixed horizontal and vertical text */
- LtkTextSegment *ts = tl->start_segment;
- int is_hor = HB_DIRECTION_IS_HORIZONTAL(ts->dir);
- tl->y_max = tl->x_max = INT_MIN;
- tl->y_min = tl->x_min = INT_MAX;
- tl->w = tl->h = 0;
- while (ts) {
- if (HB_DIRECTION_IS_HORIZONTAL(ts->dir) != is_hor) {
- (void)fprintf(stderr, "WARNING: mixed horizontal/vertical text is not supported; ignoring\n");
- continue;
- }
- if (is_hor) {
- if (tl->y_max < ts->y_max) {
- tl->y_max = ts->y_max;
- }
- if (tl->y_min > ts->y_min) {
- tl->y_min = ts->y_min;
- }
- tl->w += ts->w;
- } else {
- if (tl->x_max < ts->x_max) {
- tl->x_max = ts->x_max;
- }
- if (tl->x_min > ts->x_min) {
- tl->x_min = ts->x_min;
- }
- tl->h += ts->h;
- }
- ts = ts->next;
- }
- if (is_hor) {
- tl->h = tl->y_max - tl->y_min;
- } else {
- tl->w = tl->x_max - tl->x_min;
- }
-
- return tl;
-}
-
-void
-ltk_destroy_text_line(LtkTextLine *tl) {
- LtkTextSegment *last_ts;
- LtkTextSegment *cur_ts = tl->start_segment;
- while (cur_ts) {
- last_ts = cur_ts;
- cur_ts = cur_ts->next;
- ltk_destroy_text_segment(last_ts);
- }
-}
diff --git a/textedit_wip.h b/textedit_wip.h
@@ -1,6 +1,6 @@
/*
* This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2017, 2018, 2020 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2020 lumidify <nobody@lumidify.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -31,8 +31,10 @@ Requires the following includes:
<fontconfig/fontconfig.h>
*/
+#include "gap_buffer.h"
+
/* Contains glyph info specific to one run of text */
-typedef struct ltk_glyph {
+struct ltk_glyph {
LtkGlyphInfo *info;
int x_offset; /* additional x offset given by harfbuzz */
int y_offset; /* additional y offset given by harfbuzz */
@@ -43,18 +45,12 @@ typedef struct ltk_glyph {
uint32_t cluster; /* index of char in original text - from harfbuzz */
};
-struct ltk_gap_buffer {
- uint32_t *buf;
- size_t buf_size;
- size_t gap_left;
- size_t gap_size;
-};
+LTK_GAP_BUFFER_INIT_DECL(uint32, uint32_t)
+LTK_GAP_BUFFER_INIT_DECL(int, int)
+LTK_GAP_BUFFER_INIT_DECL(glyph, struct ltk_glyph)
-/* FIXME: macro version of gap buffer */
struct ltk_text_run {
- /* maybe make gap buffer of glyphs? */
- struct ltk_glyph *head_glyph;
- struct ltk_glyph *cur_glyph;
+ struct ltk_gap_buffer_glyph *glyphs;
struct ltk_text_run *next;
LtkFont *font;
unsigned int w;
@@ -69,9 +65,10 @@ struct ltk_text_run {
}
struct ltk_text_line {
- struct ltk_gap_buffer *text_buf; /* buffer of the logical text */
- struct ltk_gap_buffer *visual_text; /* buffer of visual text */
- /* still need log2vis and vis2log */
+ struct ltk_gap_buffer_uint32 *log_buf; /* buffer of the logical text */
+ struct ltk_gap_buffer_uint32 *vis_buf; /* buffer of visual text */
+ struct ltk_gap_buffer_int *log2vis;
+ struct ltk_gap_buffer_int *vis2log;
struct ltk_text_run *runs; /* first 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 */
@@ -84,115 +81,3 @@ struct ltk_text_buffer {
struct ltk_text_line *cur_line;
unsigned int line_gap;
};
-
-typedef struct {
- stbtt_fontinfo info;
- hb_font_t *hb;
- uint16_t id;
- unsigned int refs;
-} LtkFont;
-
-/* Contains general info on glyphs that doesn't change regardless of the context */
-typedef struct _LtkGlyphInfo {
- unsigned int id;
- unsigned char *alphamap;
- unsigned int w;
- unsigned int h;
- unsigned int xoff; /* x offset from origin to top left corner of glyph */
- unsigned int yoff; /* y offset from origin to top left corner of glyph */
- unsigned int refs;
- /* FIXME: does refs need to be long? It could cause problems if a
- program tries to cache/"keep alive" a lot of pages of text. */
-} LtkGlyphInfo;
-
-/* Single segment of text with same font */
-typedef struct LtkTextSegment {
- uint16_t font_id;
- uint16_t font_size;
- unsigned int w;
- unsigned int h;
- int start_x;
- int start_y;
- int x_min;
- int y_min;
- int x_max;
- int y_max;
- hb_direction_t dir;
- uint32_t *str;
- LtkGlyph *start_glyph;
- struct LtkTextSegment *next;
-} LtkTextSegment;
-
-/* Single line of text */
-typedef struct {
- unsigned int w;
- unsigned int h;
- int x_max;
- int x_min;
- int y_max;
- int y_min;
- FriBidiParType dir;
- LtkTextSegment *start_segment;
-} LtkTextLine;
-
-/* Hash definitions */
-/* glyph id -> glyph info struct */
-KHASH_MAP_INIT_INT(glyphinfo, LtkGlyphInfo*)
-/* font path, size -> glyph cache hash */
-KHASH_MAP_INIT_INT(glyphcache, khash_t(glyphinfo)*)
-/* font path -> font id */
-KHASH_MAP_INIT_STR(fontid, uint16_t)
-/* font id -> font struct */
-KHASH_MAP_INIT_INT(fontstruct, LtkFont*)
-
-typedef struct LtkTextManager {
- khash_t(fontid) *font_paths;
- khash_t(fontstruct) *font_cache;
- khash_t(glyphcache) *glyph_cache;
- FcPattern *fcpattern;
- uint16_t default_font;
- uint16_t font_id_cur;
-} LtkTextManager;
-
-LtkTextManager *ltk_init_text(char *font_name);
-
-void ltk_destroy_text_manager(LtkTextManager *tm);
-
-LtkGlyphInfo *ltk_create_glyph_info(LtkFont *font, unsigned int id, float scale);
-
-void ltk_destroy_glyph_info(LtkGlyphInfo *gi);
-
-LtkGlyphInfo *ltk_get_glyph_info(LtkFont *font, unsigned int id, float scale, khash_t(glyphinfo) *cache);
-
-khint_t ltk_create_glyph_cache(LtkTextManager *tm, uint16_t font_id, uint16_t font_size);
-
-void ltk_destroy_glyph_cache(khash_t(glyphinfo) *cache);
-
-void ltk_load_default_font(LtkTextManager *tm, char *name);
-
-LtkFont *ltk_create_font(char *path, uint16_t id);
-
-void ltk_destroy_font(LtkFont *font);
-
-/* FIXME: need to figure out how exactly the whole font system is going to work, especially with default fonts, etc. */
-uint16_t ltk_load_font(LtkTextManager *tm, char *path);
-
-uint16_t ltk_get_font(LtkTextManager *tm, char *path);
-
-/* TODO: different sizes, colors, styles, etc. */
-LtkTextLine *ltk_create_text_line(LtkTextManager *tm, char *text, uint16_t fontid, uint16_t size);
-
-/* FIXME: could use unsigned int for fontid and size as long as there is code to check neither of them become too large
- -> in case I want to get rid of uint_16_t, etc. */
-LtkTextSegment *ltk_create_text_segment(LtkTextManager *tm, uint32_t *text, unsigned int text_len, uint16_t fontid, uint16_t size, hb_script_t script);
-
-void ltk_destroy_glyph(LtkGlyph *glyph, khash_t(glyphinfo) *cache);
-
-void ltk_destroy_text_segment(LtkTextSegment *ts);
-
-XImage *ltk_render_text_line(LtkTextLine *tl, Display *dpy, Window window, GC gc, Colormap colormap, XColor fg, XColor bg);
-
-void ltk_render_text_segment(LtkTextSegment *ts, unsigned int start_x, unsigned int start_y, XImage *img, XColor fg);
-
-
-#endif