improve rich text

This commit is contained in:
xianjimli 2018-07-29 08:46:01 +08:00
parent 62ffe87669
commit 2b1f40fc41
10 changed files with 114 additions and 45 deletions

View File

@ -1,4 +1,4 @@
<window anim_hint="htranslate" >
<rich_text x="0" y="0" w="100%" h="60" text="<font color=&quota;red&quota; size=&quota;24&quota;>hello</font><font color=&quota;green&quota; size=&quota;18&quota;>&amp;nbsp;world</font><image name=&quota;earth&quota; />" />
<rich_text x="0" y="0" w="100%" h="60" text="<font color=&quota;red&quota; size=&quota;24&quota;>hello</font><font color=&quota;green&quota; size=&quota;20&quota;>&amp;nbsp;world</font><image name=&quota;bricks&quota; />" />
<button name="close" text="Close" x="c" y="bottom:10" w="25%" h="40" />
</window>

View File

@ -63,9 +63,11 @@ static ret_t rich_text_on_paint_text(widget_t* widget, canvas_t* c) {
case RICH_TEXT_IMAGE: {
bitmap_t bitmap;
const char* name = iter->node->u.image.name;
image_draw_type_t draw_type = iter->node->u.image.draw_type;
if (image_manager_load(image_manager(), name, &bitmap) == RET_OK) {
canvas_draw_icon_in_rect(c, &bitmap, r);
canvas_draw_image_ex(c, &bitmap, draw_type, r);
}
break;
}

View File

@ -15,7 +15,7 @@
/**
* History:
* ================================================================
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> adapt from uclib
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> created
*
*/
@ -36,6 +36,7 @@ rich_text_node_t* rich_text_text_create_with_len(rich_text_font_t* font, const c
node->next = NULL;
node->type = RICH_TEXT_TEXT;
node->u.text.font = *font;
if (font->name != NULL) {
node->u.text.font.name = tk_strdup(font->name);
goto_error_if_fail(node->u.text.font.name != NULL);
@ -60,7 +61,8 @@ rich_text_node_t* rich_text_text_create(rich_text_font_t* font, const char* text
return rich_text_text_create_with_len(font, text, strlen(text));
}
rich_text_node_t* rich_text_image_create(const char* name, uint32_t w, uint32_t h) {
rich_text_node_t* rich_text_image_create(const char* name, uint32_t w, uint32_t h,
uint32_t draw_type) {
rich_text_node_t* node = NULL;
return_value_if_fail(name != NULL, NULL);
@ -69,11 +71,16 @@ rich_text_node_t* rich_text_image_create(const char* name, uint32_t w, uint32_t
node->next = NULL;
node->type = RICH_TEXT_IMAGE;
node->u.image.name = tk_strdup(name);
node->u.image.w = w;
node->u.image.h = h;
node->u.image.draw_type = draw_type;
node->u.image.name = tk_strdup(name);
goto_error_if_fail(node->u.image.name != NULL);
return node;
error:
TKMEM_FREE(node);
return NULL;
}
ret_t rich_text_text_destroy(rich_text_node_t* node) {

View File

@ -15,7 +15,7 @@
/**
* History:
* ================================================================
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> adapt from uclib
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> created
*
*/
@ -27,8 +27,8 @@
BEGIN_C_DECLS
typedef struct _rich_text_font_t {
color_t color;
char* name;
color_t color;
uint16_t size;
uint16_t bold : 1;
uint16_t italic : 1;
@ -39,11 +39,12 @@ typedef struct _rich_text_image_t {
char* name;
uint32_t w;
uint32_t h;
uint32_t draw_type;
} rich_text_image_t;
typedef struct _rich_text_text_t {
rich_text_font_t font;
wchar_t* text;
rich_text_font_t font;
} rich_text_text_t;
typedef enum _rich_text_node_type_t {
@ -63,7 +64,8 @@ typedef struct _rich_text_node_t {
rich_text_node_t* rich_text_text_create(rich_text_font_t* font, const char* text);
rich_text_node_t* rich_text_text_create_with_len(rich_text_font_t* font, const char* text,
uint32_t len);
rich_text_node_t* rich_text_image_create(const char* name, uint32_t w, uint32_t h);
rich_text_node_t* rich_text_image_create(const char* name, uint32_t w, uint32_t h,
uint32_t draw_type);
rich_text_node_t* rich_text_node_append(rich_text_node_t* node, rich_text_node_t* next);
int32_t rich_text_node_count(rich_text_node_t* node);

View File

@ -15,13 +15,15 @@
/**
* History:
* ================================================================
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> adapt from uclib
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include "base/mem.h"
#include "base/str.h"
#include "base/utils.h"
#include "base/enums.h"
#include "base/bitmap.h"
#include "xml/xml_parser.h"
#include "base/color_parser.h"
#include "rich_text/rich_text_node.h"
@ -92,6 +94,7 @@ static void xml_rich_text_on_start(XmlBuilder* thiz, const char* tag, const char
} else if (tk_str_eq(tag, "image")) {
int32_t w = 0;
int32_t h = 0;
int32_t draw_type = IMAGE_DRAW_CENTER;
const char* image_name = NULL;
while (attrs[i]) {
@ -103,12 +106,17 @@ static void xml_rich_text_on_start(XmlBuilder* thiz, const char* tag, const char
w = tk_atoi(value);
} else if (tk_str_eq(key, "h")) {
h = tk_atoi(value);
} else if (tk_str_eq(key, "draw_type")) {
const key_type_value_t* kv = image_draw_type_find(value);
if (kv != NULL) {
draw_type = kv->value;
}
}
i += 2;
}
if (image_name != NULL) {
b->node = rich_text_node_append(b->node, rich_text_image_create(image_name, w, h));
b->node = rich_text_node_append(b->node, rich_text_image_create(image_name, w, h, draw_type));
}
}

View File

@ -15,7 +15,7 @@
/**
* History:
* ================================================================
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> adapt from uclib
* 2018-07-27 Li XianJing <xianjimli@hotmail.com> created
*
*/

View File

@ -15,7 +15,7 @@
/**
* History:
* ================================================================
* 2018-07-28 Li XianJing <xianjimli@hotmail.com> adapt from uclib
* 2018-07-28 Li XianJing <xianjimli@hotmail.com> created
*
*/
@ -28,6 +28,7 @@
rich_text_render_node_t* rich_text_render_node_create(rich_text_node_t* node) {
rich_text_render_node_t* render_node = NULL;
return_value_if_fail(node != NULL, NULL);
render_node = TKMEM_ZALLOC(rich_text_render_node_t);
return_value_if_fail(render_node != NULL, NULL);
@ -36,6 +37,37 @@ rich_text_render_node_t* rich_text_render_node_create(rich_text_node_t* node) {
return render_node;
}
#define MOVE_TO_NEXT_ROW() \
x = margin; \
y += row_h; \
if (row_first_node != NULL) { \
rich_text_render_node_tune_row(row_first_node, row_h); \
row_first_node = NULL; \
}
rich_text_render_node_t* rich_text_render_node_tune_row(rich_text_render_node_t* row_first_node,
int32_t row_h) {
rich_text_render_node_t* iter = row_first_node;
while (iter != NULL) {
iter->rect.h = row_h;
switch (iter->node->type) {
case RICH_TEXT_TEXT: {
break;
}
case RICH_TEXT_IMAGE: {
break;
}
default:
break;
}
iter = iter->next;
}
return NULL;
}
rich_text_render_node_t* rich_text_render_node_layout(rich_text_node_t* node, canvas_t* c,
int32_t w, int32_t h, int32_t margin) {
int32_t row_h = 0;
@ -49,48 +81,48 @@ rich_text_render_node_t* rich_text_render_node_layout(rich_text_node_t* node, ca
rich_text_node_t* iter = node;
rich_text_render_node_t* new_node = NULL;
rich_text_render_node_t* render_node = NULL;
rich_text_render_node_t* row_first_node = NULL;
return_value_if_fail(node != NULL && c != NULL && client_w > 0 && client_h > 0, NULL);
while (iter != NULL) {
switch (iter->type) {
case RICH_TEXT_IMAGE: {
bitmap_t bitmap;
const char* name = iter->u.image.name;
rich_text_image_t* image = &(iter->u.image);
const char* name = image->name;
new_node = rich_text_render_node_create(iter);
return_value_if_fail(new_node != NULL, render_node);
if (image_manager_load(image_manager(), name, &bitmap) == RET_OK) {
if (iter->u.image.w == 0) {
iter->u.image.w = bitmap.w;
if (image->w == 0) {
image->w = bitmap.w;
}
if (iter->u.image.h == 0) {
iter->u.image.h = bitmap.h;
if (image->h == 0) {
image->h = bitmap.h;
}
}
if (iter->u.image.w > client_w) {
iter->u.image.w = client_h;
}
if ((y + iter->u.image.h) > bottom) {
return render_node;
}
if (iter->u.image.w > ICON_SIZE || (x + iter->u.image.w) > right) {
x = margin;
y += row_h;
}
if (row_h < iter->u.image.h) {
row_h = iter->u.image.h;
if (image->w > ICON_SIZE || (x + image->w) > right) {
MOVE_TO_NEXT_ROW();
}
new_node->rect.x = x;
new_node->rect.y = y;
new_node->rect.w = iter->u.image.w;
new_node->rect.h = iter->u.image.h;
new_node->rect.w = image->w;
new_node->rect.h = image->h;
if (image->w > ICON_SIZE) {
new_node->rect.w = client_w;
}
if (row_h < image->h) {
row_h = image->h;
}
render_node = rich_text_render_node_append(render_node, new_node);
if (row_first_node == NULL) {
row_first_node = new_node;
}
break;
}
case RICH_TEXT_TEXT: {
@ -110,18 +142,20 @@ rich_text_render_node_t* rich_text_render_node_layout(rich_text_node_t* node, ca
for (i = 0; str[i]; i++) {
cw = canvas_measure_text(c, str + i, 1);
if ((x + tw + cw) > right) {
x = margin;
y += row_h;
MOVE_TO_NEXT_ROW();
new_node = rich_text_render_node_create(iter);
return_value_if_fail(new_node != NULL, render_node);
new_node->text = str + start;
new_node->size = i - start + 1;
new_node->rect = rect_init(x, y + row_h - font_size, tw, font_size);
x += tw;
new_node->rect = rect_init(x, y, tw, font_size);
x += tw + 1;
render_node = rich_text_render_node_append(render_node, new_node);
if (row_first_node == NULL) {
row_first_node = new_node;
}
start = i + 1;
} else {
@ -135,10 +169,13 @@ rich_text_render_node_t* rich_text_render_node_layout(rich_text_node_t* node, ca
new_node->text = str + start;
new_node->size = i - start;
new_node->rect = rect_init(x, y + row_h - font_size, tw, font_size);
x += tw;
new_node->rect = rect_init(x, y, tw, font_size);
x += tw + 1;
render_node = rich_text_render_node_append(render_node, new_node);
if (row_first_node == NULL) {
row_first_node = new_node;
}
}
break;
@ -149,6 +186,7 @@ rich_text_render_node_t* rich_text_render_node_layout(rich_text_node_t* node, ca
iter = iter->next;
}
MOVE_TO_NEXT_ROW();
return render_node;
}

View File

@ -15,7 +15,7 @@
/**
* History:
* ================================================================
* 2018-07-28 Li XianJing <xianjimli@hotmail.com> adapt from uclib
* 2018-07-28 Li XianJing <xianjimli@hotmail.com> created
*
*/
@ -30,9 +30,9 @@ BEGIN_C_DECLS
typedef struct _rich_text_render_node_t {
rich_text_node_t* node;
rect_t rect;
wchar_t* text;
uint32_t size;
rect_t rect;
struct _rich_text_render_node_t* next;
} rich_text_render_node_t;

View File

@ -34,6 +34,10 @@ typedef struct _font_stb_t {
font_t base;
stbtt_fontinfo stb_font;
glyph_cache_t cache;
int ascent;
int descent;
int lineGap;
} font_stb_t;
static bool_t font_stb_match(font_t* f, const char* name, uint16_t font_size) {
@ -46,20 +50,27 @@ static ret_t font_stb_find_glyph(font_t* f, wchar_t c, glyph_t* g, uint16_t font
int y = 0;
int w = 0;
int h = 0;
int x1 = 0;
int y1 = 0;
int x2 = 0;
int y2 = 0;
int s = font_size;
font_stb_t* font = (font_stb_t*)f;
stbtt_fontinfo* sf = &(font->stb_font);
float scale = stbtt_ScaleForPixelHeight(sf, s);
if (glyph_cache_lookup(&(font->cache), c, font_size, g) == RET_OK) {
return RET_OK;
}
g->data = stbtt_GetCodepointBitmap(sf, 0, stbtt_ScaleForPixelHeight(sf, s), c, &w, &h, &x, &y);
g->data = stbtt_GetCodepointBitmap(sf, 0, scale, c, &w, &h, &x, &y);
g->x = x;
g->y = y;
g->w = w;
g->h = h;
glyph_cache_add(&(font->cache), c, font_size, g);
stbtt_GetGlyphBitmapBox(sf, c, 0, scale, &x1, &y1, &x2, &y2);
return g->data != NULL ? RET_OK : RET_NOT_FOUND;
}
@ -96,6 +107,7 @@ font_t* font_stb_create(const char* name, const uint8_t* buff, uint32_t buff_siz
glyph_cache_init(&(f->cache), 256, destroy_glyph);
stbtt_InitFont(&(f->stb_font), buff, stbtt_GetFontOffsetForIndex(buff, 0));
stbtt_GetFontVMetrics(&(f->stb_font), &(f->ascent), &(f->descent), &(f->lineGap));
return &(f->base);
}

View File

@ -20,7 +20,7 @@ TEST(RichTextNode, text) {
}
TEST(RichTextNode, image) {
rich_text_node_t* image = rich_text_image_create("earth", 12, 24);
rich_text_node_t* image = rich_text_image_create("earth", 12, 24, 0);
ASSERT_EQ(image->type, RICH_TEXT_IMAGE);
ASSERT_EQ(string(image->u.image.name), string("earth"));
@ -39,7 +39,7 @@ TEST(RichTextNode, basic) {
ASSERT_EQ(rich_text_node_count(node), 1);
node = rich_text_node_append(node, rich_text_text_create(&font, "good"));
ASSERT_EQ(rich_text_node_count(node), 2);
node = rich_text_node_append(node, rich_text_image_create("earth", 12, 24));
node = rich_text_node_append(node, rich_text_image_create("earth", 12, 24, 0));
ASSERT_EQ(rich_text_node_count(node), 3);
rich_text_node_destroy(node);