![]() |
Nilorea Library
C utilities for networking, threading, graphics
|
Markdown layout and drawing. More...
#include "nilorea/n_gui_md.h"#include "nilorea/n_wrap.h"#include <allegro5/allegro_primitives.h>#include <stdio.h>#include <stdlib.h>#include <string.h>Go to the source code of this file.
Data Structures | |
| struct | md_atom |
| One layout atom: a glyph (nbytes > 0) or a hard break. More... | |
| struct | md_hit |
| A hit-test threaded through the layout: when a link atom's cell contains the point, its url is copied out while the document is still live. More... | |
| struct | md_line_ctx |
| Everything a line needs, carried through n_wrap to the emit callback, which is handed only an item range, a line number and a width. More... | |
Macros | |
| #define | MD_COLS_MAX 32 |
| Widest table this lays out. | |
Enumerations | |
| enum | { MD_AT_SPACE = 1 , MD_AT_NEWLINE = 2 } |
| Atom flags. More... | |
| enum | { MD_MEASURE = 0 , MD_DRAW , MD_HIT } |
| What one pass is doing. More... | |
| enum | { MD_COL_TEXT = 0 , MD_COL_LINK , MD_COL_CODE , MD_COL_HEADING , MD_COL_QUOTE } |
| Which colour of the style an atom takes. More... | |
Functions | |
| 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. | |
| static ALLEGRO_COLOR | md_col (const N_GUI_MD_STYLE *st, unsigned char colid) |
| 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 void | md_emit_line (size_t first, size_t last, size_t line, double width, void *user) |
| static ALLEGRO_COLOR | md_fade (ALLEGRO_COLOR c, float a) |
| A colour at a fraction of its opacity, premultiplied. | |
| 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_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, summed without allocating. | |
| 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 al_draw_text. | |
| 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 its 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 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 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 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 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. | |
| 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. | |
| 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 | |
| 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 | |
| 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 | |
| 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 | |
Markdown layout and drawing.
See n_gui_md.h.
The text is parsed once per call, then each block is laid out as a flow of "atoms", one per codepoint, each carrying its own font and colour. A flow is word-wrapped by n_wrap, which never sees a glyph or a font: it is handed a width and a pair of flags per atom and hands back line ranges.
Measure, draw and hit-test are one function with a mode, so a height cannot disagree with what is painted. That is the whole reason for the shape of this file, and the reason the measure pass allocates and walks exactly as the draw does rather than taking a cheaper route to a number.
Definition in file n_gui_md.c.
| struct md_atom |
One layout atom: a glyph (nbytes > 0) or a hard break.
url is non-NULL for an atom belonging to a link, and points into the parsed document, which outlives every atom made from it.
Definition at line 59 of file n_gui_md.c.
| Data Fields | ||
|---|---|---|
| char | bytes[4] | |
| unsigned char | colid | |
| unsigned char | flags | |
| ALLEGRO_FONT * | font | |
| unsigned char | nbytes | |
| const char * | url | |
| float | w | |
| struct md_hit |
A hit-test threaded through the layout: when a link atom's cell contains the point, its url is copied out while the document is still live.
Definition at line 78 of file n_gui_md.c.
| Data Fields | ||
|---|---|---|
| size_t | cap | |
| int | found | |
| char * | out | |
| float | px | |
| float | py | |
| struct md_line_ctx |
Everything a line needs, carried through n_wrap to the emit callback, which is handed only an item range, a line number and a width.
Definition at line 234 of file n_gui_md.c.
| Data Fields | ||
|---|---|---|
| const md_atom * | a | |
| unsigned char | align | |
| float | avail | |
| md_hit * | hit | |
| float | lh | |
| int | mode | |
| const N_GUI_MD_STYLE * | st | |
| float | x | |
| float | y | |
| #define MD_COLS_MAX 32 |
Widest table this lays out.
Past it the remaining columns are dropped, which is also n_markdown's own cap, so it cannot be reached from a parsed document; it bounds the stack arrays below.
Definition at line 50 of file n_gui_md.c.
Referenced by md_table(), and md_table_cols().
| anonymous enum |
| anonymous enum |
What one pass is doing.
A measure is the draw walk with the per-line callback left off, which is what keeps the two in step.
| Enumerator | |
|---|---|
| MD_MEASURE | |
| MD_DRAW | |
| MD_HIT | |
Definition at line 87 of file n_gui_md.c.
| anonymous enum |
Which colour of the style an atom takes.
| Enumerator | |
|---|---|
| MD_COL_TEXT | |
| MD_COL_LINK | |
| MD_COL_CODE | |
| MD_COL_HEADING | |
| MD_COL_QUOTE | |
Definition at line 70 of file n_gui_md.c.
|
static |
Append atoms for one styled segment.
hard_nl decides what a newline in the text becomes: a line break, or a space for the wrap to break at if it needs to. Returns the new atom count.
Definition at line 181 of file n_gui_md.c.
References md_atom::bytes, md_atom::colid, md_atom::flags, font, md_atom::font, MD_AT_NEWLINE, MD_AT_SPACE, md_cp_len(), md_wrap_of(), md_atom::nbytes, md_atom::url, and md_atom::w.
Referenced by md_flow().
|
static |
Definition at line 132 of file n_gui_md.c.
References N_GUI_MD_STYLE::code, N_GUI_MD_STYLE::heading, N_GUI_MD_STYLE::link, MD_COL_CODE, MD_COL_HEADING, MD_COL_LINK, MD_COL_QUOTE, N_GUI_MD_STYLE::quote, and N_GUI_MD_STYLE::text.
Referenced by md_flush_line().
|
static |
Bytes in the UTF-8 codepoint at s[i], clamped to what is left.
Definition at line 94 of file n_gui_md.c.
Referenced by md_build_seg(), and md_flow_width().
|
static |
Definition at line 302 of file n_gui_md.c.
References last, md_line_ctx::lh, line(), md_flush_line(), and md_line_ctx::y.
Referenced by md_flow().
|
static |
A colour at a fraction of its opacity, premultiplied.
Allegro's default blender adds the source and keeps (1 - alpha) of the destination, so the rgb has to come down with the alpha or the colour draws at very nearly full strength over a hole it has punched in what was there. Setting .a alone, which is the obvious thing to write, gives a rule that looks solid and a panel that looks white.
Definition at line 155 of file n_gui_md.c.
Referenced by n_gui_md_style_default().
|
static |
One inline flow: an optional prefix, then the spans.
base_font, when given, is the face every atom takes whatever its span's style says, bar a code span which keeps the mono face: that is how a heading is drawn in the heading face rather than in a bold body face, and there is no bold-heading face to switch to inside one. NULL picks the face from each span's own style, which is what body text wants.
style_or is ORed into every span's style, which is how a table's header row is bold without the parser having to say so. colid is the colour every non-link, non-code atom takes. Returns the height.
Definition at line 318 of file n_gui_md.c.
References md_line_ctx::a, md_line_ctx::align, md_line_ctx::avail, font, md_line_ctx::hit, md_line_ctx::lh, md_build_seg(), MD_COL_CODE, MD_COL_LINK, md_emit_line(), md_font_for(), MD_MEASURE, md_line_ctx::mode, mode, N_GUI_MD_FONTS::mono, N_MARKDOWN_CODE, N_MARKDOWN_LINK, n_wrap_layout(), N_GUI_MD_FONTS::regular, md_line_ctx::st, n_markdown_span::style, md_line_ctx::x, and md_line_ctx::y.
Referenced by md_run(), and md_table().
|
static |
The width one flow would take on a single line: the same per-codepoint widths the atoms carry, summed without allocating.
Used to size table columns, where a whole-string measure would disagree with the wrap.
Definition at line 383 of file n_gui_md.c.
References font, md_cp_len(), md_font_for(), and n_markdown_span::text.
Referenced by md_table_cols().
|
static |
Draw atoms [from, to) as one line: consecutive atoms sharing a font and a colour are batched into one al_draw_text.
On a hit-test pass nothing is drawn and a link atom under the point records its url instead.
Definition at line 246 of file n_gui_md.c.
References md_line_ctx::a, md_line_ctx::align, md_line_ctx::avail, md_atom::bytes, md_hit::cap, md_atom::colid, md_atom::font, md_hit::found, md_line_ctx::hit, md_line_ctx::lh, md_col(), MD_HIT, md_line_ctx::mode, N_MARKDOWN_ALIGN_CENTER, N_MARKDOWN_ALIGN_RIGHT, md_atom::nbytes, md_hit::out, md_hit::px, md_hit::py, md_line_ctx::st, md_atom::url, md_atom::w, and md_line_ctx::x.
Referenced by md_emit_line().
|
static |
The face for an inline style, falling back towards regular so that a caller with one font still gets its text.
Definition at line 105 of file n_gui_md.c.
References N_GUI_MD_FONTS::bold, N_GUI_MD_FONTS::bolditalic, N_GUI_MD_FONTS::italic, N_GUI_MD_FONTS::mono, N_MARKDOWN_BOLD, N_MARKDOWN_CODE, N_MARKDOWN_ITALIC, and N_GUI_MD_FONTS::regular.
Referenced by md_flow(), and md_flow_width().
|
static |
The face for a heading of this level, falling back to bold then regular.
Definition at line 119 of file n_gui_md.c.
References N_GUI_MD_FONTS::bold, N_GUI_MD_FONTS::h1, N_GUI_MD_FONTS::h2, N_GUI_MD_FONTS::h3, and N_GUI_MD_FONTS::regular.
Referenced by md_run().
|
static |
A font's line height, or the regular face's when it is NULL.
Definition at line 164 of file n_gui_md.c.
References font, and N_GUI_MD_FONTS::regular.
Referenced by md_run(), and md_table().
|
static |
Parse and lay out the whole text.
The one walk behind all three entry points: mode says whether it draws, and used accumulates from zero so the height it returns is independent of y.
Definition at line 521 of file n_gui_md.c.
References N_GUI_MD_STYLE::block_gap, n_markdown_doc::blocks, N_GUI_MD_STYLE::cell_pad, n_markdown_block::code, N_GUI_MD_STYLE::code_bg, N_GUI_MD_STYLE::hard_breaks, N_GUI_MD_STYLE::indent, n_markdown_block::inl, n_markdown_block::items, n_markdown_block::kind, n_markdown_block::level, MD_COL_CODE, MD_COL_HEADING, MD_COL_QUOTE, MD_COL_TEXT, MD_DRAW, md_flow(), md_font_heading(), md_lh(), MD_MEASURE, md_table(), mode, N_GUI_MD_FONTS::mono, n_markdown_doc::n_blocks, n_markdown_block::n_items, N_MARKDOWN_ALIGN_LEFT, N_MARKDOWN_CODE, N_MARKDOWN_CODE_BLOCK, n_markdown_free(), N_MARKDOWN_HEADING, N_MARKDOWN_OL, N_MARKDOWN_PARA, n_markdown_parse_ex(), N_MARKDOWN_QUOTE, N_MARKDOWN_TABLE, N_MARKDOWN_UL, n_markdown_inline::n_spans, N_GUI_MD_STYLE::parse_flags, N_GUI_MD_STYLE::quote, N_GUI_MD_FONTS::regular, N_GUI_MD_STYLE::rule, n_markdown_inline::spans, n_markdown_span::style, n_markdown_span::text, and n_markdown_span::url.
Referenced by n_gui_md_draw(), n_gui_md_height(), and n_gui_md_link_at().
|
static |
One table.
Cells wrap inside their column, a row is as tall as its tallest cell, and a rule goes under the header row. Returns the height.
Definition at line 460 of file n_gui_md.c.
References n_markdown_block::align, N_GUI_MD_STYLE::cell_pad, n_markdown_block::items, MD_COL_TEXT, MD_COLS_MAX, MD_DRAW, md_flow(), md_lh(), MD_MEASURE, md_table_cols(), mode, n_markdown_block::n_cols, n_markdown_block::n_items, N_MARKDOWN_ALIGN_LEFT, N_MARKDOWN_BOLD, n_markdown_inline::n_spans, N_GUI_MD_FONTS::regular, N_GUI_MD_STYLE::rule, and n_markdown_inline::spans.
Referenced by md_run().
|
static |
Column widths for a table, including the cell padding.
Every column asks for its widest cell on one line, and if the table then fits, that is what it gets: a two-column table of short keys does not stretch across the pane. When the asks add up to more than there is, they are scaled by the same factor with a floor of style->col_min, so a long neighbour cannot squeeze a column to nothing – and if even the floors do not fit, the width is shared equally, because at that point nothing will look right and equal is at least predictable.
Cells wrap to the width they are given; the alternative, panning the table sideways, hides the right-hand column of exactly the tables people read.
Definition at line 420 of file n_gui_md.c.
References N_GUI_MD_STYLE::cell_pad, N_GUI_MD_STYLE::col_min, n_markdown_block::items, MD_COLS_MAX, md_flow_width(), n_markdown_block::n_cols, n_markdown_block::n_items, N_MARKDOWN_BOLD, n_markdown_inline::n_spans, and n_markdown_inline::spans.
Referenced by md_table().
|
static |
Project an atom onto the generic wrap's view of it: a width, and whether it is a break point.
n_wrap never sees a font, a glyph or a colour.
Definition at line 172 of file n_gui_md.c.
References md_atom::flags, n_wrap_item::flags, MD_AT_NEWLINE, MD_AT_SPACE, N_WRAP_NEWLINE, N_WRAP_SPACE, md_atom::w, and n_wrap_item::width.
Referenced by md_build_seg().
| 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
Draws into the current Allegro target with the current transform and clipping rectangle; a caller wanting the overflow clipped sets one.
| fonts | the faces. Must not be NULL, nor fonts->regular. |
| style | colours and metrics. Must not be NULL. |
| x | left edge |
| y | top edge |
| width | the width to wrap to, in pixels |
| text | the markdown source. NULL is treated as empty. |
Definition at line 657 of file n_gui_md.c.
References MD_DRAW, md_run(), and N_GUI_MD_FONTS::regular.
Referenced by _draw_markdownview(), and main().
| 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
The same walk n_gui_md_draw() makes, with nothing drawn, so the two agree to the pixel. Independent of any y: measuring at the origin gives the height a draw anywhere will use.
| fonts | the faces. Must not be NULL, nor fonts->regular. |
| style | colours and metrics. Must not be NULL. |
| width | the width to wrap to, in pixels |
| text | the markdown source. NULL is treated as empty. |
Definition at line 651 of file n_gui_md.c.
References MD_MEASURE, md_run(), and N_GUI_MD_FONTS::regular.
Referenced by _markdownview_measure(), and main().
| 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
Lays the text out exactly as n_gui_md_draw() would at the same position and width, and reports the link whose glyph cell the point falls in.
| fonts | the faces. Must not be NULL, nor fonts->regular. |
| style | colours and metrics. Must not be NULL. |
| x | left edge the text would be drawn at |
| y | top edge |
| width | the width it would wrap to |
| text | the markdown source. NULL is treated as empty. |
| px | the point to test |
| py | the point to test |
| url | receives the url, NUL-terminated and bounded by url_cap |
| url_cap | size of url |
url was written, else 0 Definition at line 663 of file n_gui_md.c.
References md_hit::cap, md_hit::found, MD_HIT, md_run(), md_hit::out, md_hit::px, md_hit::py, and N_GUI_MD_FONTS::regular.
Referenced by main(), and n_gui_process_event().
| 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
The derived colours are the obvious ones: a heading and body text share a colour, a link is blue, code sits on a panel lifted or dropped from the background depending on which is darker. The metrics are fractions of line_height, so they follow the font.
| out | the style to fill. Must not be NULL. |
| text | body text colour |
| background | what the text is drawn on, used to derive the code panel |
| line_height | the regular face's line height, in pixels |
Definition at line 622 of file n_gui_md.c.
References background(), N_GUI_MD_STYLE::block_gap, N_GUI_MD_STYLE::cell_pad, N_GUI_MD_STYLE::code, N_GUI_MD_STYLE::code_bg, N_GUI_MD_STYLE::col_min, N_GUI_MD_STYLE::hard_breaks, N_GUI_MD_STYLE::heading, N_GUI_MD_STYLE::indent, N_GUI_MD_STYLE::link, md_fade(), N_MARKDOWN_ALL, N_GUI_MD_STYLE::parse_flags, N_GUI_MD_STYLE::quote, N_GUI_MD_STYLE::rule, and N_GUI_MD_STYLE::text.
Referenced by _markdownview_follow_theme(), and main().