41#include <allegro5/allegro_primitives.h>
94static int md_cp_len(
const char* s,
int i,
int len) {
98 while (i + n < len && ((
unsigned char)s[i + n] & 0xC0) == 0x80)
120 ALLEGRO_FONT* want = NULL;
155static ALLEGRO_COLOR
md_fade(ALLEGRO_COLOR c,
float a) {
183 while (i < len && na < cap) {
191 if (text[i] ==
'\n' && !hard_nl) {
198 a->
w = (float)al_get_text_width(
font,
" ");
204 if (text[i] ==
'\n') {
217 memcpy(g, text + i, (
size_t)bl);
219 a->
w = (float)al_get_text_width(
font, g);
220 a->
nbytes = (
unsigned char)bl;
221 memcpy(a->
bytes, g, (
size_t)bl);
222 if (text[i] ==
' ' || text[i] ==
'\t')
250 ALLEGRO_FONT* bfont = NULL;
251 unsigned char bcol = 0;
252 float x0 = c->
x, rx, bx;
257 x0 += (c->
avail - line_w) * 0.5f;
259 x0 += c->
avail - line_w;
266 for (k = from; k < to; k++) {
278 for (k = from; k < to; k++) {
282 if (blen && (at->
font != bfont || at->
colid != bcol ||
283 blen + at->
nbytes >= (
int)
sizeof(buf))) {
284 al_draw_text(bfont,
md_col(c->
st, bcol), bx, y, 0, buf);
298 al_draw_text(bfont,
md_col(c->
st, bcol), bx, y, 0, buf);
318static float md_flow(
const N_GUI_MD_FONTS* f,
const N_GUI_MD_STYLE* st,
const char* prefix,
const n_markdown_span* spans,
size_t nspans,
float x,
float y,
float w,
float lh, ALLEGRO_FONT* base_font,
unsigned style_or,
unsigned char colid,
unsigned char align,
int hard_nl,
int mode,
md_hit* hit) {
319 size_t total = prefix ? strlen(prefix) : 0, i;
326 for (i = 0; i < nspans; i++)
327 total += spans[i].text ? strlen(spans[i].text) : 0;
334 atoms = malloc(total * (
sizeof(*atoms) +
sizeof(*wrap)));
339 na =
md_build_seg(atoms, wrap, (
int)total, na, prefix, (
int)strlen(prefix),
340 base_font ? base_font : f->
regular, colid, NULL, hard_nl);
341 for (i = 0; i < nspans; i++) {
342 unsigned sty = spans[i].
style | style_or;
345 unsigned char cid = colid;
357 na =
md_build_seg(atoms, wrap, (
int)total, na, spans[i].text,
358 (
int)strlen(spans[i].text),
font, cid,
359 is_link ? spans[i].url : NULL,
377 return (
float)lines * lh;
386 for (i = 0; i < nspans; i++) {
388 const char* t = spans[i].
text;
389 int len = t ? (int)strlen(t) : 0;
396 memcpy(g, t + j, (
size_t)bl);
398 sum += (float)al_get_text_width(
font, g);
423 float pad2 = st->
cell_pad * 2.0f, sum = 0.0f;
427 for (c = 0; c < cols; c++) {
429 for (r = 0; r < rows; r++) {
436 out[c] = want + pad2;
439 if (sum <= avail || sum <= 0.0f)
443 float k = avail / sum;
444 for (c = 0; c < cols; c++) {
450 if (scaled > avail) {
451 float each = avail / (float)cols;
452 for (c = 0; c < cols; c++)
473 for (r = 0; r < rows; r++) {
474 float cx = x, row_h = 0.0f;
480 for (c = 0; c < cols; c++) {
483 widths[c] - st->
cell_pad * 2.0f, lh, NULL, style_or,
489 for (c = 0; c < cols; c++) {
496 widths[c] - st->
cell_pad * 2.0f, lh, NULL, style_or,
506 for (c = 0; c < cols; c++)
509 al_draw_line(x, y + used + 1.0f, x + total, y + used + 1.0f, st->
rule, 1.0f);
531 for (bi = 0; bi < doc.
n_blocks; bi++) {
538 float hlh =
md_lh(f, hf);
546 if (blk->
level <= 2) {
548 al_draw_line(x, by + h + 1.0f, x + w, by + h + 1.0f, st->
rule, 1.0f);
562 al_draw_filled_rectangle(x, by, x + 2.0f, by + h, st->
quote);
582 al_draw_filled_rectangle(x, by - st->
cell_pad, x + w,
593 for (ii = 0; ii < blk->
n_items; ii++) {
596 (void)snprintf(bullet,
sizeof(bullet),
"- ");
598 (
void)snprintf(bullet,
sizeof(bullet),
"%u. ", (
unsigned)(ii + 1));
626 if (line_height < 1.0f)
630 out->
link = al_map_rgb(120, 170, 245);
639 out->
code_bg = level > 0.5f ? al_premul_rgba_f(0.0f, 0.0f, 0.0f, 0.06f)
640 : al_premul_rgba_f(1.0f, 1.0f, 1.0f, 0.10f);
644 out->
indent = line_height * 0.8f;
645 out->
cell_pad = line_height * 0.35f;
646 out->
col_min = line_height * 2.5f;
652 if (!fonts || !fonts->
regular || !style)
658 if (!fonts || !fonts->
regular || !style)
660 return md_run(fonts, style, x, y, width, text,
MD_DRAW, NULL);
665 if (!fonts || !fonts->
regular || !style)
674 (void)
md_run(fonts, style, x, y, width, text,
MD_HIT, &hit);
static void background(float x, float y, float w, float h, void *user_data)
a background
static N_GUI_MOUSE_EVENT last
the last event the widget was told of, and how many
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a line
FONT * font
the default 8x8 font, valid after allegro_init()
static float md_flow(const N_GUI_MD_FONTS *f, const N_GUI_MD_STYLE *st, const char *prefix, const n_markdown_span *spans, size_t nspans, float x, float y, float w, float lh, ALLEGRO_FONT *base_font, unsigned style_or, unsigned char colid, unsigned char align, int hard_nl, int mode, md_hit *hit)
One inline flow: an optional prefix, then the spans.
static float md_table(const N_GUI_MD_FONTS *f, const N_GUI_MD_STYLE *st, const n_markdown_block *b, float x, float y, float w, int mode, md_hit *hit)
One table.
static float md_lh(const N_GUI_MD_FONTS *f, ALLEGRO_FONT *font)
A font's line height, or the regular face's when it is NULL.
static int md_cp_len(const char *s, int i, int len)
Bytes in the UTF-8 codepoint at s[i], clamped to what is left.
static float md_run(const N_GUI_MD_FONTS *f, const N_GUI_MD_STYLE *st, float x, float y, float w, const char *text, int mode, md_hit *hit)
Parse and lay out the whole text.
static ALLEGRO_FONT * md_font_heading(const N_GUI_MD_FONTS *f, int level)
The face for a heading of this level, falling back to bold then regular.
static void md_wrap_of(const md_atom *a, n_wrap_item *w)
Project an atom onto the generic wrap's view of it: a width, and whether it is a break point.
static ALLEGRO_COLOR md_fade(ALLEGRO_COLOR c, float a)
A colour at a fraction of its opacity, premultiplied.
static void md_emit_line(size_t first, size_t last, size_t line, double width, void *user)
static ALLEGRO_FONT * md_font_for(const N_GUI_MD_FONTS *f, unsigned style)
The face for an inline style, falling back towards regular so that a caller with one font still gets ...
static void md_table_cols(const N_GUI_MD_FONTS *f, const N_GUI_MD_STYLE *st, const n_markdown_block *b, float avail, float *out)
Column widths for a table, including the cell padding.
int n_gui_md_link_at(const N_GUI_MD_FONTS *fonts, const N_GUI_MD_STYLE *style, float x, float y, float width, const char *text, float px, float py, char *url, size_t url_cap)
the url of the link under (px, py), if there is one
static void md_flush_line(const md_line_ctx *c, size_t from, size_t to, float y, float line_w)
Draw atoms [from, to) as one line: consecutive atoms sharing a font and a colour are batched into one...
static float md_flow_width(const N_GUI_MD_FONTS *f, const n_markdown_span *spans, size_t nspans, unsigned style_or)
The width one flow would take on a single line: the same per-codepoint widths the atoms carry,...
float n_gui_md_draw(const N_GUI_MD_FONTS *fonts, const N_GUI_MD_STYLE *style, float x, float y, float width, const char *text)
draw text as markdown at (x, y), wrapped to width
static ALLEGRO_COLOR md_col(const N_GUI_MD_STYLE *st, unsigned char colid)
const N_GUI_MD_STYLE * st
#define MD_COLS_MAX
Widest table this lays out.
static int md_build_seg(md_atom *out, n_wrap_item *wrap, int cap, int na, const char *text, int len, ALLEGRO_FONT *font, unsigned char colid, const char *url, int hard_nl)
Append atoms for one styled segment.
void n_gui_md_style_default(N_GUI_MD_STYLE *out, ALLEGRO_COLOR text, ALLEGRO_COLOR background, float line_height)
fill a style from two colours and a line height
float n_gui_md_height(const N_GUI_MD_FONTS *fonts, const N_GUI_MD_STYLE *style, float width, const char *text)
height of text laid out as markdown at width, in pixels
One layout atom: a glyph (nbytes > 0) or a hard break.
A hit-test threaded through the layout: when a link atom's cell contains the point,...
Everything a line needs, carried through n_wrap to the emit callback, which is handed only an item ra...
Lay out and draw n_markdown's block model with Allegro fonts.
ALLEGRO_COLOR heading
heading text
int hard_breaks
Non-zero to break a paragraph where its source breaks, which is what a chat message wants: somebody p...
float col_min
narrowest a table column may be squeezed to, in pixels
float cell_pad
padding inside a table cell, left and right
ALLEGRO_COLOR text
body text
ALLEGRO_COLOR quote
a blockquote's text, and its bar
ALLEGRO_COLOR link
link text
ALLEGRO_FONT * regular
body text, and the fallback for every other face.
ALLEGRO_COLOR code_bg
the panel behind a fenced code block, and behind inline code
ALLEGRO_FONT * h1
"# " headings, ideally the bold face at a larger size
ALLEGRO_COLOR rule
the rule under a heading and under a table's header row
ALLEGRO_FONT * bolditalic
both at once
ALLEGRO_FONT * h3
"### " and deeper
unsigned parse_flags
n_markdown parse flags.
ALLEGRO_FONT * mono
code and fenced code blocks
ALLEGRO_FONT * h2
"## " headings
ALLEGRO_FONT * italic
italic
ALLEGRO_COLOR code
code and code block text
float indent
how far a quote, a list item's text and a code block are indented
float block_gap
vertical space between two blocks, in pixels
void n_markdown_free(n_markdown_doc *doc)
free everything a n_markdown_doc owns and zero it
void n_markdown_parse_ex(const char *text, size_t len, unsigned flags, n_markdown_doc *out)
parse text into out, choosing which optional block kinds to recognise
#define N_MARKDOWN_CODE
inline code style flag (carries no emphasis)
#define N_MARKDOWN_ALIGN_CENTER
what "|:---:|" asks for.
#define N_MARKDOWN_ALIGN_RIGHT
what "|---:|" asks for.
#define N_MARKDOWN_ITALIC
italic inline style flag
#define N_MARKDOWN_ALL
every block kind this parser knows.
#define N_MARKDOWN_LINK
[text](http(s)://url) style flag; url is set only when this is present
#define N_MARKDOWN_BOLD
bold inline style flag
n_markdown_inline * items
@ N_MARKDOWN_HEADING
"## " heading (inl, level)
@ N_MARKDOWN_OL
ordered list (items)
@ N_MARKDOWN_TABLE
pipe table (items, n_cols)
@ N_MARKDOWN_UL
unordered list (items)
@ N_MARKDOWN_CODE_BLOCK
``` fenced code (raw code)
@ N_MARKDOWN_PARA
paragraph
int kind
enum n_markdown_block_kind
unsigned char * align
TABLE: N_MARKDOWN_ALIGN_* per column, n_cols entries, owned.
#define N_MARKDOWN_ALIGN_LEFT
table column alignment: the default, and what "|:---|" asks for.
n_markdown_block * blocks
size_t n_cols
TABLE: cells per row, always at least 1.
int level
HEADING: 1 to 6.
A parsed document: an owned array of blocks.
One flow of inline runs (a paragraph, a quote, or one list item).
One inline run: visible text, a style bitmask, and (for a link) an already-http(s)-validated url.
size_t n_wrap_layout(const n_wrap_item *items, size_t count, double max_width, n_wrap_emit emit, void *user)
Break items into lines no wider than max_width.
Greedy word-wrap over pre-measured, styled items.
#define N_WRAP_SPACE
the item is a legal break point (typically a space); it is DROPPED when a line breaks there,...
#define N_WRAP_NEWLINE
the item is a hard line break; it ends the line and occupies no cell
double width
advance width in the caller's units (pixels, columns, ...)
unsigned int flags
N_WRAP_SPACE / N_WRAP_NEWLINE, or 0
One pre-measured layout item: a glyph, an emoji cell, an inline image, or a whole word,...