REBOL
Docs Blog Get-it

REBOL/View Graphics - Face Contents

About the text, images, and edges that go inside a face.

Face Text

The face text field allows users to add text to a face, from just a few characters to entire paragraphs and pages.

The face object includes a few fields that are related to how text is handled:

textthe field that holds the text to be displayed in the face.
fontthe font attributes for rendering the text.
parathe paragraph spacing and alignment attributes.
line-lista special cache of line positions used to speed up text operations. This field should not be used by applications, except to delete the cache when text changes are made.

Text Field

This field specifies the text to be displayed in the face.

The field accepts any printable REBOL value. The value will be converted to a string (formed) dynamically by the system. If you provide a string, no conversion is needed. This is important if the face must be shown (refreshed) at a high frequency (such as in the case of cells within large scrolling tables). In such cases, strings provide greater performance.

Note that if the text is greater than 200 characters in length and you make changes to the text, you must also delete the line-length cache to avoid possible garbage characters from being displayed in the face. See more below.

Font Facet Object

The font facet is an object that describes the attributes of the text to be used within a face. The font is specified as an sub-object within the face object.

The font object contains these fields:

Field Type Description
name string! The name of the font to use for the text. There are three predefined variables for machine independent fonts that have a similar appearance: font-serif (times-like), font-sans-serif (helvetica-like), and font-fixed (courier fixed width). To create machine independent programs, avoid specifying custom fonts. The default is font-sans-serif.
size integer! The point size of the font. The default size is 12.
style word! block! The style of the text. Choices are: bold, italic, and underline. When set to none, no styles are used (default).
color tuple! The color of the text. The default color is black (0.0.0).
align word! The alignment of the text within the face. Choices are: left, right, and center.
valign word! The vertical alignment of the text within the face. Choices are: top, bottom, and middle.
offset pair! The offset of the text from the upper left corner of the face. The para facet object also has an effect on this offset. Default is 2x2.
space pair! The spacing between characters and between lines. The x value affects the spacing between characters. The y value changes the spacing between lines. Positive values expand the text, negative values condense it. The default is 0x0.
shadow pair! The direction and offset of the drop shadow to use for the text. Positive values project a shadow toward the lower right corner. Negative values project toward the upper left. The default is none.

Here is an example that sets most of the above fields for the font object.

view make face [
    offset: 100x100
    size: 300x100
    edge: none
    pane: reduce [
        make face [
            offset: 0x0
            size: 300x100
            color: water
            edge: none
            text: "Modified Font Object Settings"
            font: make font [
                name: "times"
                size: 20
                style: 'bold
                color: white
                align: 'center
                valign: 'middle
                space: 4x4
                shadow: 2x2
            ]
        ]
    ]
]

Para Facet Object

The para facet is an object that controls the formatting of text paragraphs within the face. A para is specified as a sub-object within a face object.

The para object contains these fields:

Field Type Description
origin pair! The offset of the text from the upper left corner of a face. The default is 2x2.
margin pair! The right-most and bottom limits of text display within the face. The position is relative to the bottom right corner of the face. The default is 2x2.
indent pair! The offset of a the first line of a paragraph. The X value specifies the indentation used for the first line of the paragraph. Positive and negative values may be used. The Y value specifies the spacing between the end of the previous paragraph and the first line of the next paragraph. The Y value has no effect on the first paragraph. The default is 0x0.
scroll pair! Used for horizontal and vertical scrolling of text within a face. The scroll amount that modifies the offset of the text relative to the face. The origin and margin values are not affected. The default is 0x0.
tabs integer! block! An integer! or block of integers that provide the tab spacing used within a paragraph. An integer! value indicates a fixed tab size spaced at regular intervals across the text. A block of integers provides the precise horizontal offset positions of each tab in order. The default is 40.
wrap? logic! Indicates that automatic line wrapping should occur. When set to true, text that exceeds the margin will be automatically wrapped to the origin. When set to false, text will not be wrapped.

Here is an example that shows how text is formatted when you change a few of the para fields:

; First, remove extra lines from text for proper wrapping:
para-text: copy system/license
replace/all para-text "^/^/" "!!"
trim/lines para-text
replace/all para-text "!!" newline

view make face [
    offset: 100x100
    size: 420x320
    edge: none
    color: white
    pane: reduce [
        make face [
            offset: 10x10
            size: 400x300
            color: white
            text: para-text
            font: make font [align: 'left]
            para: make para [
                origin: 30x30
                margin: 30x30
                indent: 30x10
                wrap?: on
            ]
        ]
    ]
]

One of the more interesting fields here is indent. It sets the indentation of the first line of each paragraph and the distance between paragraphs. If you provide a negative x value, it will also do "outdenting" (as sometimes used for bullet sections, etc.)

Focus and Keyboard Events

Keyboard events are not sent to all faces. Keyboard events are only sent to a single face, called the focal-face. This face provides an event target that is the implied "focus" of user input actions.

The focal-face is set in the system/view object. This face can be set directly, or you can use the focus function to set it and handle a few other details related to text input.

The focal-face must be set to a valid face object (one that is part of a pane in the face hierarchy) and the system/view/caret (explained in next section) must also be set in order to:

  1. Receive keyboard events
  2. Receive mouse scroll-wheel events (scroll-line and scroll-page)
  3. Handle text editing caret and text selection highlighting

If the system/view/focal-face has not been set (is set to none), the above events will be ignored by the system, even by the top-level detect function.

When the focus is no longer needed, you can set the focal-face field to none, or call the unfocus function.

The VID system provides useful functions for handling keyboard input into text fields. It is suggested that you use them if possible. However, here is a example that shows how text input can be handled for a very simple case:

aface: make face [
    offset: 100x100
    pane: reduce [
        tface: make face [
            text: "Type on keyboard."
            font: make font [align: 'left]
            color: snow
            edge: none
            feel: make feel [
                engage: func [face action event] [
                    if action = 'key [
                        switch/default event/key reduce [
                            bs [remove back tail face/text]
                            cr [unview]
                            escape [unview]
                        ][
                            append face/text event/key
                        ]
                        system/view/caret: tail face/text
                        show face
                    ]
                ]
            ]
        ]
    ]
]
system/view/focal-face: tface
system/view/caret: tail tface/text
view aface

This is a low level example, and only handles the simplest of keyboard input actions. Keyboard text input processing is a somewhat complicated matter, and it is recommended to use the functions provided by VID.

Text Caret Position

When entering text into fields and areas, it is customary to mark the edit point with a vertical bar, called the caret. This is where new text will be inserted or existing text is deleted.

The system/view/caret field determines the current text edit point within the text. You set this caret field to a position within your face/text string. For example, if your face is named tface, then:

To set the caret edit point to the start of the text:

system/view/caret: head tface/text

To set the caret edit point to the end of the text:

system/view/caret: tail tface/text

To set the caret edit point to the 10th character:

system/view/caret: at tface/text 10

Keep in mind that the caret field actually points into your string series, so in fact you can directly use it with insert and remove functions to change the text string.

system/view/caret: insert system/view/caret "test"

The above inserts the string "test" at the current edit point and updates the edit point (to just after the insert position). See the REBOL/Core Users Guide for more about Series functions.

Text Position Mapping

In graphical user interfaces, the mouse pointer is often used to set the caret to a location within the text string. To do so, a graphical xy position needs to be mapped to a series position within the string.

Sometimes the opposite is also required. You know a position within the string series and need to map it to an xy position within the face.

If you are using VID, these operations are performed for you automatically when fields, areas, or other selectable text is used.

In the lower level View system, REBOL provides two functions to perform these graphics-to-string mappings:

offset-to-caretMaps from a graphical position to a string position. Returns the string series position within the face/text field that corresponds with the given XY graphical location (a pair!).
caret-to-offsetMaps from a string position to a graphical position. Returns XY graphical location (relative to the face) for a given string series position.

The functions both take a face and an offset as arguments:

offset-to-caret face pair-offset

caret-to-offset face string-offset

Here is an example that shows how offset-to-caret works:

aface: make face [
    offset: 100x100
    pane: reduce [
        tface: make face [
            text: "Click the mouse somewhere within this text string."
            font: make font [align: 'left]
            color: snow
            edge: none
            feel: make feel [
                engage: func [face action event] [
                    if action = 'down [
                        print offset-to-caret face event/offset
                    ]
                ]
            ]
        ]
    ]
]
view aface

Text Highlighting

In addition to selecting the edit position (the caret), most text editing also allows the selection of a sequence of text characters. This is often called highlighting or selection of a string and is often used with text cut, copy, and paste operations.

REBOL highlights text by using inverse video for the selected text area. The range of the selection is controlled by two fields in the system/view object:

highlight-startThe string position to begin highlighting text.
highlight-endThe string position to stop highlighting text.

When using VID, these fields are set to their correct values automatically when using the mouse to select text.

However, you can set these values directly, as shown below. Both of these fields must refer to the same string series and the focal-face must refer to the face that contains the text.

aface: make face [
    offset: 100x100
    pane: reduce [
        tface: make face [
            text: "Example of text highlight selection."
            font: make font [align: 'left]
            color: snow
            edge: none
        ]
    ]
]
system/view/focal-face: tface
system/view/highlight-start: find tface/text "text"
system/view/highlight-end: find/tail tface/text "light"
view aface

There is currently no way to modify the highlight color. (Although we plan to include that as an enhancement in future versions of REBOL.)

Text Scrolling

Text can be scrolled within a face by setting the scroll field of the face's para object. The scroll field allows both horizontal and vertical scrolling depending on the pair! value you provide. Positive scroll values move text down (or to the right) and negative values move text up (or to the left). That is, usual text scrolling (where text goes up or to the left) is done by using negative numbers for x and y in the scroll field.

Sharing the Para Object - If you are going to change values in the para object, you should make a copy of it first. That allows you to make changes without affecting other faces that share the same para object. See the example below.

Here is a simple example. Each time you click the text, it will scroll up by 10 pixels. If you right-click (alt-click) the text it will scroll down by 10 pixels.

the-text: read http://data.rebol.com/speed.r

view make face [
    offset: 100x100
    size: 640x480
    pane: reduce [
        tface: make face [
            text: the-text
            size: 640x480
            font: make font [align: 'left]
            color: snow
            para: make para [scroll: 0x0 wrap?: true]
            feel: make feel [
                engage: func [face action event] [
                    if find [down alt-down] action [
                        face/para/scroll: face/para/scroll +
                            pick [0x-10 0x10] action = 'down
                        show face
                    ]
                ]
            ]
        ]
    ]
]

Text Sizing

At times you will need to know a little more about the rendered size of the text a face is creating. For instance, if you want to write functions to align or center text, you will need to know its size in pixels.

Size-Text

The size-text function returns the number of pixels in width and height required to render text for a face. For a single line of text, this function is mainly used to determine the width of the text. For multiple lines, it is normally used to compute the height of the text.

If the face wrap? field is true, the text will be wrapped at the width, and the height will be determined by the number of lines, including any wrapped text.

In addition to the width of the face, this function will also take into account the horizontal component (x) of the para/origin, para/margin, para/indent, and edge/size. The height of the face, para/offset, and para/scroll do not affect the result.

Note that the face does not need to be displayed for this function to work correctly.

Here is an example:

aface: make face [text: "This is some text"]
print size-text aface

==74x30

Here is an example that uses a lot more text:

page: read http://data.rebol.com
print length? page

==9902

aface: make face [text: page size: 600x500]
print size-text aface

==593x3885

Note in the example above, the height (size) of the face did not matter in order to calculate the height of the text within it.

See the important notes below.

TextInfo

A lower-level text mapping function is textinfo. This is more of an internal function that was created for text editing purposes, but can be quite useful at times. (And notice that it is not named according to standard REBOL function naming conventions - making it more difficult to remember.)

The textinfo function provides text line information for the text that is rendered in a face. The form of the function is:

result: textinfo face line-info line

The result is either none if the text cannot be rendered, or it is the line-info object (when successful).

The function arguments are:

faceThe face that specifies the text and its facets.
line-infoA special text line object that is passed to the function and gets modified to hold the result. See details below.
lineThe string index position or line number (zero based) to get information about.

The line-info object is defined by system/view/line-info. It is set by calling the textinfo function. It includes these fields:

startOffset of start of line in the string (string!)
num-charsNumber of chars in the line (integer!)
offsetXY position of the line (pair!)
sizeWidth and height of the line (pair!)

Here is an example that fetches information about the third line of text in a face:

aface: make face [
    text: trim/lines {This is a longer line of text that is
        used to illustrate the example.}
]

line-list: make system/view/line-info []
if textinfo aface line-list 2 [
    probe line-list 
]

==make object! [
    start: "used to illustrate the example."
    num-chars: 19
    offset: 2x32
    size: 93x15
]
Important Notes:
  1. If you change the text for a face, be sure to set the face's line-list field to none to force the font system to re-render all the text.
  2. You must use a copy of the system/view/line-info object with the textinfo function. Doing otherwise could cause a fatal error in current versions of REBOL.
  3. The line number is zero based, not one-based (unlike other index positions in REBOL).

Text Clipboard

For text cut and paste operations between other applications, REBOL provides the clipboard port. This port allows you to insert text and copy text as you would with other REBOL ports. The text will be stored in the OS clipboard and can be pasted into other applications. If other applications store 8-bit text, they can be copied into REBOL.

The standard read/write port operations are:

readRead a text clip from the clipboard.
writeWrite a text clip to the clipboard.

For example, to write text to the clipboard, you simply write to the clipboard using its URL scheme:

write clipboard:// "This is text"

To read from the clipboard:

print read clipboard://

Other port functions are also allowed:

openOpen the clipboard for text read and write.
closeClose the previously opened clipboard.
insertInsert text into the clipboard.
copyCopy text from the clipboard and return it as a string.

Here is an interesting example that shows how to use the clipboard. It opens a window and displays the current contents of the clipboard. Go to another application, such as your web browser, then select and copy some text. You will see the clip text appear in the REBOL window. (It updates four times second.)

clipboard: open clipboard://

view make face [
    offset: 100x100
    size: 640x480
    pane: reduce [
        tface: make face [
            text: "Shows clipboard"
            size: 640x480
            font: make font [align: 'left]
            color: snow
            rate: 4
            feel: make feel [
                engage: func [face action event] [
                    if action = 'time [
                        face/text: copy clipboard
                        show face
                    ]
                ]
            ]
        ]
    ]
]

close clipboard

Note that REBOL does not currently support images or other datatypes in the clipboard. (However, we plan to improve the clipboard capabilities in the future.)

Face Images

Any face can include an image. When an image is part of a face, it will appear behind the edge image, if an edge is specified.

The image is not scaled by default, but if it is too large, it will be clipped and if it is too small, the remaining area will be filled with the color specified in the face color field. To make the image scale to the size of the face, use the fit effect. To keep the image in proper aspect (the same ratio as its source, not to the ratio of the face), use the aspect effect.

Note that it is a common mistake for beginners to specify a file name for this field, rather than an image. You must write:

image: load %photo.jpg

or:

image: load-image %photo.jpg  ; caches image in memory

not:

image: %photo.jpg

A variety of special effects can be applied to the image. See the effects section below.

Images can also be used for the edge of a face. See the edge section below.

Face Edges

The edge field of a face can optionally contain an object that describes a rectangular frame that borders a face. It is used for creating image frames, button edges, table cell dividers, and other border effects.

Definition

An edge object contains these fields:

Field Datatype Description
color tuple! The color of the edge. For effects like BEVEL, the final color will be based on this original color plus or minus a small amount to provide proper shading.
size pair! The thickness of the edge. The x value refers to the thickness of the vertical edges on the left and right, and the Y value refers to the horizontal edges at the top and bottom.
effect word! block! A word! or block! that describes the effect to use for the edge. In addition to the normal face effects described below, edges also include bevel, ibevel, bezel,w:ibezel, and nubs effects. These are useful for quickly creating button and box looks.
image image! Use an image for the edge area. The image can be processed using the above effect if necessary (e.g. colorize, etc.)
About | Contact | PrivacyREBOL Technologies 2024