REBOL/View Graphics - Face Contents
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:
text | the field that holds the text to be displayed in the face. |
font | the font attributes for rendering the text. |
para | the paragraph spacing and alignment attributes. |
line-list | a 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:
- Receive keyboard events
- Receive mouse scroll-wheel events (scroll-line and scroll-page)
- 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-caret | Maps 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-offset | Maps 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-start | The string position to begin highlighting text. |
highlight-end | The 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:
face | The face that specifies the text and its facets. |
line-info | A special text line object that is passed to the function and gets modified to hold the result. See details below. |
line | The 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:
start | Offset of start of line in the string (string!) |
num-chars | Number of chars in the line (integer!) |
offset | XY position of the line (pair!) |
size | Width 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 ]
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:
read | Read a text clip from the clipboard. |
write | Write 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:
open | Open the clipboard for text read and write. |
close | Close the previously opened clipboard. |
insert | Insert text into the clipboard. |
copy | Copy 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.) |