#include <stdint.h>
#include <string.h>
#include <limits.h>
+#include <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Face face;
char *text;
+ ply_rich_text_t *rich_text;
+ ply_rich_text_span_t span;
float red;
float green;
float blue;
}
static FT_Int
-width_of_line (ply_label_plugin_control_t *label,
- const char *text)
+width_of_string (ply_label_plugin_control_t *label,
+ const char *text)
{
FT_Int width = 0;
FT_Int left_bearing = 0;
static void
size_control (ply_label_plugin_control_t *label)
{
- FT_Int width;
- const char *text = label->text;
+ FT_Int character_width, line_width;
+ ply_rich_text_iterator_t rich_text_iterator;
+ ply_utf8_string_iterator_t utf8_string_iterator;
+ bool should_stop = false;
+
+ if (label->rich_text == NULL && label->text == NULL) {
+ label->area.width = 0;
+ label->area.height = 0;
+ return;
+ }
+ if (label->rich_text != NULL) {
+ ply_rich_text_iterator_init (&rich_text_iterator,
+ label->rich_text,
+ &label->span);
+ } else {
+ ply_utf8_string_iterator_init (&utf8_string_iterator,
+ label->text,
+ 0,
+ ply_utf8_string_get_length (label->text, strlen (label->text)));
+ }
label->area.width = 0;
label->area.height = 0;
+ line_width = 0;
+
/* Go through each line */
- while (text && *text) {
- width = width_of_line (label, text);
- if ((uint32_t) width > label->area.width)
- label->area.width = width;
-
- label->area.height += (label->face->size->metrics.ascender
- - label->face->size->metrics.descender) >> 6;
-
- text = strchr (text, '\n');
- /* skip newline character */
- if (text)
- ++text;
- }
+ do {
+ const char *current_character;
+
+ if (label->rich_text != NULL) {
+ ply_rich_text_character_t *rich_text_character;
+ should_stop = !ply_rich_text_iterator_next (&rich_text_iterator,
+ &rich_text_character);
+ if (!should_stop)
+ current_character = rich_text_character->bytes;
+ } else {
+ size_t character_size;
+
+ should_stop = !ply_utf8_string_iterator_next (&utf8_string_iterator,
+ ¤t_character,
+ &character_size);
+ }
+
+ if (!should_stop) {
+ character_width = width_of_string (label, current_character);
+ line_width += character_width;
+ }
+
+ if (should_stop || character_width == 0) {
+ if ((uint32_t) line_width > label->area.width)
+ label->area.width = line_width;
+
+ line_width = 0;
+
+ label->area.height += (label->face->size->metrics.ascender
+ - label->face->size->metrics.descender) >> 6;
+ }
+ } while (!should_stop);
/* If centered, area.x is not the origin anymore */
if ((long) label->area.width < label->width)
ply_rectangle_t target_size,
FT_Bitmap *source,
FT_Int x_start,
- FT_Int y_start)
+ FT_Int y_start,
+ uint8_t rs,
+ uint8_t gs,
+ uint8_t bs)
{
FT_Int x, y, xs, ys;
FT_Int x_end = MIN (x_start + source->width, target_size.width);
(uint32_t) y_start >= target_size.height)
return;
- uint8_t rs, gs, bs, rd, gd, bd, ad;
- rs = 255 * label->red;
- gs = 255 * label->green;
- bs = 255 * label->blue;
+ uint8_t rd, gd, bd, ad;
for (y = y_start, ys = 0; y < y_end; ++y, ++ys) {
for (x = x_start, xs = 0; x < x_end; ++x, ++xs) {
}
}
+static void
+look_up_rgb_color_from_terminal_color (ply_label_plugin_control_t *label,
+ ply_terminal_color_t color,
+ uint8_t *red,
+ uint8_t *green,
+ uint8_t *blue)
+{
+ switch (color) {
+ case PLY_TERMINAL_COLOR_BLACK:
+ *red = 0x00;
+ *green = 0x00;
+ *blue = 0x00;
+ break;
+ /* Linux VT Color: 0xaa0000 */
+ case PLY_TERMINAL_COLOR_RED:
+ *red = 0xaa;
+ *green = 0x00;
+ *blue = 0x00;
+ break;
+ /* Linux VT Color: 0x00aa00 */
+ case PLY_TERMINAL_COLOR_GREEN:
+ *red = 0x00;
+ *green = 0xaa;
+ *blue = 0x00;
+ break;
+ /* Linux VT Color: 0xaa5500 */
+ case PLY_TERMINAL_COLOR_BROWN:
+ *red = 0xaa;
+ *green = 0x55;
+ *blue = 0x00;
+ break;
+ /* Linux VT Color: 0x0000aa */
+ case PLY_TERMINAL_COLOR_BLUE:
+ *red = 0x00;
+ *green = 0x00;
+ *blue = 0xaa;
+ break;
+ /* Linux VT Color: 0xaa00aa */
+ case PLY_TERMINAL_COLOR_MAGENTA:
+ *red = 0xaa;
+ *green = 0x00;
+ *blue = 0xaa;
+ break;
+ /* Linux VT Color: 0x00aaaa */
+ case PLY_TERMINAL_COLOR_CYAN:
+ *red = 0x00;
+ *green = 0xaa;
+ *blue = 0xaa;
+ break;
+ /* Linux VT Color: 0xaaaaaa */
+ case PLY_TERMINAL_COLOR_WHITE:
+ break;
+
+ default:
+ *red = 255 * label->red;
+ *green = 255 * label->green;
+ *blue = 255 * label->blue;
+ break;
+ }
+}
+
static void
draw_control (ply_label_plugin_control_t *label,
ply_pixel_buffer_t *pixel_buffer,
unsigned long width,
unsigned long height)
{
- FT_Error error;
FT_Vector pen;
FT_GlyphSlot slot;
- const char *cur_c;
+ ply_rich_text_iterator_t rich_text_iterator;
+ ply_utf8_string_iterator_t utf8_string_iterator;
uint32_t *target;
ply_rectangle_t target_size;
-
if (label->is_hidden)
return;
+ if (label->rich_text == NULL &&
+ label->text == NULL)
+ return;
+
/* Check for overlap.
* TODO: Don't redraw everything if only a part should be drawn! */
if (label->area.x > x + (long) width || label->area.y > y + (long) height
slot = label->face->glyph;
- cur_c = label->text;
+ if (label->rich_text != NULL) {
+ ply_rich_text_iterator_init (&rich_text_iterator,
+ label->rich_text,
+ &label->span);
+ } else {
+ ply_utf8_string_iterator_init (&utf8_string_iterator,
+ label->text,
+ 0,
+ ply_utf8_string_get_length (label->text, strlen (label->text)));
+ }
target = ply_pixel_buffer_get_argb32_data (pixel_buffer);
ply_pixel_buffer_get_size (pixel_buffer, &target_size);
pen.y += label->face->size->metrics.ascender;
/* Go through each line */
- while (*cur_c) {
+ do {
+ bool should_stop;
+ const char *current_character;
+ uint8_t red, green, blue;
+
+ FT_Int extraAdvance = 0, positiveBearingX = 0;
+ ply_rich_text_character_t *rich_text_character;
+
+ red = 255 * label->red;
+ green = 255 * label->green;
+ blue = 255 * label->blue;
+
+ if (label->rich_text != NULL) {
+ should_stop = !ply_rich_text_iterator_next (&rich_text_iterator,
+ &rich_text_character);
+ if (should_stop)
+ break;
+
+ current_character = rich_text_character->bytes;
+
+ look_up_rgb_color_from_terminal_color (label,
+ rich_text_character->style.foreground_color,
+ &red,
+ &green,
+ &blue);
+ } else {
+ size_t character_size;
+
+ should_stop = !ply_utf8_string_iterator_next (&utf8_string_iterator,
+ ¤t_character,
+ &character_size);
+ }
+
+ if (*current_character == '\n')
+ continue;
+
pen.x = label->area.x << 6;
/* Start at start position (alignment) */
if (label->alignment == PLY_LABEL_ALIGN_CENTER)
- pen.x += (label->area.width - width_of_line (label, cur_c)) << 5;
+ pen.x += (label->area.width - width_of_string (label, current_character)) << 5;
else if (label->alignment == PLY_LABEL_ALIGN_RIGHT)
- pen.x += (label->area.width - width_of_line (label, cur_c)) << 6;
-
- while (*cur_c && *cur_c != '\n') {
- FT_Int extraAdvance = 0, positiveBearingX = 0;
-
- if (!load_character (label, &cur_c))
- continue;
-
- /* We consider negative left bearing an increment in size,
- * as we draw full character boxes and don't "go back" in
- * this plugin. Positive left bearing is treated as usual.
- * For definitions see
- * https://freetype.org/freetype2/docs/glyphs/glyphs-3.html
- */
- if (slot->bitmap_left < 0) {
- extraAdvance = -slot->bitmap_left;
- } else {
- positiveBearingX = slot->bitmap_left;
- }
- draw_bitmap (label, target, target_size, &slot->bitmap,
- (pen.x >> 6) + positiveBearingX,
- (pen.y >> 6) - slot->bitmap_top);
-
- pen.x += slot->advance.x + extraAdvance;
- pen.y += slot->advance.y;
- }
- /* skip newline character */
- if (*cur_c)
- ++cur_c;
+ pen.x += (label->area.width - width_of_string (label, current_character)) << 6;
+
+ if (!load_character (label, ¤t_character))
+ continue;
+
+ /* We consider negative left bearing an increment in size,
+ * as we draw full character boxes and don't "go back" in
+ * this plugin. Positive left bearing is treated as usual.
+ * For definitions see
+ * https://freetype.org/freetype2/docs/glyphs/glyphs-3.html
+ */
+ if (slot->bitmap_left < 0)
+ extraAdvance = -slot->bitmap_left;
+ else
+ positiveBearingX = slot->bitmap_left;
+
+ draw_bitmap (label, target, target_size, &slot->bitmap,
+ (pen.x >> 6) + positiveBearingX,
+ (pen.y >> 6) - slot->bitmap_top,
+ red,
+ green,
+ blue);
+
+ pen.x += slot->advance.x + extraAdvance;
+ pen.y += slot->advance.y;
/* Next line */
pen.y += label->face->size->metrics.height;
- }
+ } while (true);
}
static void
}
}
+static void
+clear_text (ply_label_plugin_control_t *label)
+{
+ free (label->text);
+ label->text = NULL;
+
+ if (label->rich_text != NULL) {
+ ply_rich_text_drop_reference (label->rich_text);
+ label->rich_text = NULL;
+ label->span.offset = 0;
+ label->span.range = 0;
+ }
+}
+
static void
set_text_for_control (ply_label_plugin_control_t *label,
const char *text)
{
if (label->text != text) {
- free (label->text);
+ clear_text (label);
label->text = strdup (text);
trigger_redraw (label, true);
}
}
+static void
+set_rich_text_for_control (ply_label_plugin_control_t *label,
+ ply_rich_text_t *rich_text,
+ ply_rich_text_span_t *span)
+{
+ clear_text (label);
+
+ label->rich_text = rich_text;
+ ply_rich_text_take_reference (rich_text);
+ label->span = *span;
+
+ trigger_redraw (label, true);
+}
+
static void
set_font_for_control (ply_label_plugin_control_t *label,
const char *fontdesc)
.draw_control = draw_control,
.is_control_hidden = is_control_hidden,
.set_text_for_control = set_text_for_control,
+ .set_rich_text_for_control = set_rich_text_for_control,
.set_alignment_for_control = set_alignment_for_control,
.set_width_for_control = set_width_for_control,
.set_font_for_control = set_font_for_control,