Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
[DOC] Rich Text support
This is our plan for supporting rich text features in Red/View.
What we call "rich text" here is one or several paragraphs of text, where different graphic styles can be applied to segments of the text. The proposed API has three different levels, from simplest to most optimized:
- Using the RTD dialect (from VID, or when manually constructing the face).
- A low-level rich styling dialect for one text string (for fast performance).
- Multiple rich text paragraphs in a single face (for complex layouts).
High-level Rich-Text Dialect
This Rich-Text Dialect (RTD) provides a high-level interface for specifying styled text. Styles application range can be specified using starting/ending delimiters or using blocks.
any-single is a pseudo-keyword meaning: any combination of single occurence of following alternatives.
color-name: word! nested: [ahead block! into rtd] color: [tuple! | issue! | color-name] f-args: [ ahead block! into [integer! string! | string! integer!] | integer! | string! ] style: [ ['b | 'bold | <b>] [nested | rtd [/b | /bold | </b>]] | ['i | 'italic | <i>] [nested | rtd [/i | /italic | </i>]] | ['u | 'underline | <u>] [nested | rtd [/u | /underline | </u>]] | ['s | 'strike | <s>] [nested | rtd [/s | /strike | </s>]] | ['f | 'font | <font>] f-args [nested | rtd [/f | /font | </font>]] | ['bg | <bg>] color [nested | rtd [/bg | </bg>]] | tuple! opt nested ;-- color as R.G.B tuple | issue! opt nested ;-- color as #rgb or #rrggbb hex value | color-name opt nested | ahead path! into [any-single ['b | 'i | 'u | 's | color-name]] nested ] rtd: [some [style | string!]]
- The path syntax allows to combine several styles together to be applied on the block following the path.
- Mixed usage of delimiters and blocks for different styles is allowed.
RTD input is processed by a specific
rtd-layout function that will return a single-box rich-text face, where the RTD code will be compiled to a single text string (stored in
/text facet) and a low-level styling description (stored in
/data facet). The full specification of the function is:
rtd-layout: func [rtd [block!] /with target [face!] return: [face!]]
rtd-layout [<i> <b> "Hello" </b> <font> 24 red " Red " </font> blue "World!" </i>] rtd-layout [i b "Hello" /b font 24 red " Red " /font blue "World!" /i] rtd-layout [i [b ["Hello"] red font 24 [" Red "] blue "World!"]] rtd-layout [i/b/u/red ["Hello" font 32 " Red " /font blue "World!"]]
Low-level Styling Dialect
This dialect describes a list of styles to be applied on the string referred by
/text facet in a rich-text face. The purpose of this dialect is to provide a solution for dynamic changes and info querying that performs as fast as possible. This also maps well with the underlying hardware-accelerated APIs (it relies on Direct2D on Windows).
The dialect grammar is a simple list of text segments (defined using a starting position and a length combined in a
pair! value) followed by a list of styles. So, the typical structure is:
[ <range1> style1 style2 ... ;-- range1: start1 x length1 <range2> style1 style2 ... ;-- range2: start2 x length2 ... ]
Styles can overlap, and later styles have higher priority (cascading styles).
The following styles are supported:
[ tuple! ;-- text color | backdrop tuple! ;-- background color | bold ;-- bold font | italic | underline tuple! (color) lit-word! ('dash, 'double, 'triple) ;@@ color and type are not supported yet | strike tuple! (color) lit-word! ('wave, 'double) ;@@ color and type are not supported yet | border tuple! (color) lit-word! ('dash, 'wave) ;@@ not implemented | integer! ;-- new font size | string! ;-- new font name ]
Rich text face type
A new native
rich-text face type supports rich text features with underlying hardware-acceleration. The face has two modes for displaying rich text:
The whole face area is used for displaying the rich text, starting at upper left corner, using the following specific facets:
/data(block!): a block of low-level styling dialect instructions to be applied on
/text(string!): a text string to be displayed using the
/datafacet styles description.
Draw facet can still be used and it will be rendered on top of the rich text display.
In this mode, an arbitrary number of rich text areas can be displayed inside the same rich-text face. In order to achieve that, each rich text area is specified using the
text keyword in Draw dialect.
/draw(block!): a block of
textinstructions, eventually mixed with regular Draw instructions.
/text(none!): this facet must be set to
nonein order to enable this mode.
text <pos> <text> <pos> : a pair! value indicating the upper left corner of the text-box. <text> : a string, or a rich-text face object with a rich-text description in single-box mode.
This mode is optimized for lowest system resources usage when a high number of rich text areas need to be rendered.
Info querying functions
The following functions are provided to query information about a rich-text face content. Those functions can be used to easily implement:
- cursor navigation
- hit testing
From global context:
caret-to-offset: function [ "Given a text position, returns the corresponding coordinate relative to the top-left of the layout box" face [object!] pos [integer!] return: [pair!] ] offset-to-caret: function [ "Given a coordinate, returns the corresponding text position" face [object!] pt [pair!] return: [integer!] ] size-text: function [ "Returns the area size of the text in a face" face [object!] /with ;-- unused for rich-text text [string!] return: [pair! none!] ]
line-height?: function [ "Given a text position, returns the corresponding line's height" face [object!] pos [integer!] return: [integer!] ] line-count?: function [ "number of lines (> 1 if line wrapped)" face [object!] return: [integer!] ]