Prima::TextView - rich text browser widget
Prima::TextView - rich text browser widget
Prima::TextView accepts blocks of formatted text, and provides
basic functionality - scrolling and user selection. The text strings
are stored as one large text chunk, available by the
A block of a formatted text is an array with fixed-length header and
the following instructions.
A special package
tb:: provides the block constants and simple functions
for text block access.
Prima::TextView is mainly the text block functions and helpers. It provides function for wrapping text block, calculating block dimensions, drawing and converting coordinates from (X,Y) to a block position. Prima::TextView is centered around the text functionality, and although any custom graphic of arbitrary complexity can be embedded in a text block, the internal coordinate system is used ( TEXT_OFFSET, BLOCK ), where TEXT_OFFSET is a text offset from the beginning of a block and BLOCK is an index of a block.
The functionality does not imply any text layout - this is up to the class descendants, they must provide they own layout policy. The only policy Prima::TextView requires is that blocks' BLK_TEXT_OFFSET field must be strictly increasing, and the block text chunks must not overlap. The text gaps are allowed though.
A text block basic drawing function includes change of color, backColor and font, and the painting of text strings. Other types of graphics can be achieved by supplying custom code.
A block's fixed header consists of
tb::BLK_START - 1 integer scalars,
each of those is accessible via the corresponding
The constants are separated into two logical groups:
BLK_FLAGS BLK_WIDTH BLK_HEIGHT BLK_X BLK_Y BLK_APERTURE_X BLK_APERTURE_Y BLK_TEXT_OFFSET
BLK_FONT_ID BLK_FONT_SIZE BLK_FONT_STYLE BLK_COLOR BLK_BACKCOLOR
The second group is enclosed in
range, like the whole header is contained in 0 -
tb::BLK_START - 1 range.
This is done for the backward compatibility, if the future development changes
the length of the header.
The first group fields define the text block dimension, aperture position and text offset ( remember, the text is stored as one big chunk ). The second defines the initial color and font settings. Prima::TextView needs all fields of every block to be initialized before displaying. block_wrap method can be used for automated assigning of these fields.
The scalars, beginning from
tb::BLK_START, represent the commands to the renderer.
These commands have their own parameters, that follow the command. The length of
a command is located in
@oplen array, and must not be changed. The basic command
The additional codes are
OP_MARK, not used in drawing but are
special commands to block_wrap.
OP_TEXTcommands to draw a string, from offset
tb::BLK_TEXT_OFFSET + TEXT_OFFSET, with a length TEXT_LENGTH. The third parameter TEXT_WIDTH contains the width of the text in pixels. Such the two-part offset scheme is made for simplification or an imaginary code, that would alter ( insert to, or delete part of ) the big text chunk; the updating procedure would not need to traverse all commands, but just the block headers.
OP_COLORsets foreground or background color. To set the background, COLOR must be or-ed with
tb::BACKCOLOR_FLAGvalue. In addition to the two toolkit supported color values ( RRGGBB and system color index ), COLOR can also be or-ed with
tb::COLOR_INDEXflags, in such case it is an index in
OP_FONTKEY represents one of the three parameters -
tb::F_STYLE. All three have different VALUE meaning.
fs::XXXconstants, such as
Default value: 0
F_SIZEvalues larger than the default font size, one must be vary when relying on the combined font size value .
F_SIZE value is added to a
F_HEIGHT constant, then it is treated as a font height
in pixels rather than font size in points. The macros for these opcodes are named respectively
tb::fontHeight, while the opcode is the same.
::fontPaletteproperty array, which contains font hashes with the other font keys initialized - name, encoding, and pitch. These three are minimal required set, and the other font keys can be also selected.
tb::X_EXTEND, then in addition to the block expansion, current coordinate is also moved to (X,Y). In this regard,
(OP_TRANSPOSE,0,0,X_EXTEND)are identical and are empty operators. A special flag
X_DIMENSION_FONT_HEIGHTis only in effect with block_wrap function. It indicates that (X,Y) values must be multiplied to the current font height.
OP_TRANSPOSE can be used for customized graphics, in conjunction with
to assign a space, so the rendering
algorithms do not need to be re-written every time the new graphic is invented. As
an example, see how the Prima::PodView manpage deals with the images.
( $widget, $canvas, $text_block, $font_and_color_state, $x, $y, $parameter);
$font_and_color_state ( or $state, through the code ) contains the state of font and color commands in effect, and is changed as the rendering algorithm advances through a block. The format of the state is the same as of text block, so one may notice that for readability F_ID, F_SIZE, F_STYLE constants are paired to BLK_FONT_ID, BLK_FONT_SIZE and BLK_FONT_STYLE.
The SUB code is executed only when the block is about to draw.
OP_WRAPis only in effect in block_wrap method. ON_OFF is a boolean flag, selecting if the wrapping is turned on or off. block_wrap does not support stacking for the wrap commands, so the
(OP_WRAP,1,OP_WRAP,1,OP_WRAP,0)has same effect as
(OP_WRAP,0). If ON_OFF is 1, wrapping is disabled - all following commands treated an non-wrapable until
OP_MARKis only in effect in block_wrap method and is a user command. block_wrap only sets (!) X and Y to the current coordinates when the command is met. Thus,
OP_MARKcan be used for arbitrary reasons, easy marking the geometrical positions that undergo the block wrapping.
As can be noticed, these opcodes are far not enough for the full-weight rich text
viewer. However, the new opcodes can be created using
tb::opcode, that accepts
the opcode length and returns the new opcode value.
block_wrapis the function, that is used to wrap a block into a given width. It returns one or more text blocks with fully assigned headers. The returned blocks are located one below another, providing an illusion that the text itself is wrapped. It does not only traverses the opcodes and sees if the command fit or not in the given width; it also splits the text strings if these do not fit.
By default the wrapping can occur either on a command boundary or by the spaces or tab characters
in the text strings. The unsolicited wrapping can be prevented by using
command brackets. The commands inside these brackets are not wrapped;
are removed from the output blocks.
block_wrap copies all commands and their parameters as is, ( as it is supposed
to do ), but some commands are treated especially:
OP_TEXT's third parameter,
TEXT_WIDTH, is disregarded, and is recalculated for every
OP_TRANSPOSE's third parameter,
the command coordinates X and Y are multiplied to the current font height and the flag is
cleared in the output block.
OP_MARK's second and third parameters assigned to the current (X,Y) coordinates.
OP_WRAP removed from the output.
block_drawdraws BLOCK onto CANVAS in screen coordinates (X,Y). It can not only be used for drawing inside begin_paint/end_paint brackets; CANVAS can be an arbitrary
Prima::TextView employs two its own coordinate systems: (X,Y)-document and (TEXT_OFFSET,BLOCK)-block.
The document coordinate system is isometric and measured in pixels. Its origin is located
into the imaginary point of the beginning of the document ( not of the first block! ),
in the upper-left point. X increases to the right, Y increases downwards.
The block header values BLK_X and BLK_Y are in document coordinates, and
the widget's pane extents ( regulated by
::paneHeight properties ) are also in document coordinates.
The block coordinate system in an-isometric - its second axis, BLOCK, is an index
of a text block in the widget's blocks storage,
its first axis, TEXT_OFFSET is a text offset from the beginning of the block.
Below described different coordinate system converters
The text selection is performed automatically when the user selects the
region with a mouse. The selection is stored in (TEXT_OFFSET,BLOCK)
coordinate pair, and is accessible via the
If its value is assigned to (-1,-1,-1,-1) this indicates that there is
no selection. For convenience the
has_selection method is introduced.
get_selected_text returns the text within the selection
(or undef with no selection ), and
copy copies automatically
the selected text into the clipboard. The latter action is bound to
Ctrl+Insert key combination.
Partly as an option for future development, partly as a hack a
concept of 'event rectangles' was introduced. Currently,
private variable points to an array of objects, equipped with
on_mouseup methods. These
are called within the widget's mouse events, so the overloaded classes
can define the interactive content without overloading the actual
mouse events ( which is although easy but is dependent on Prima::TextView
own mouse reactions ).
As an example the Prima::PodView manpage uses the event rectangles to catch the mouse events over the document links. Theoretically, every 'content' is to be bound with a separate logical layer; when the concept was designed, a html-browser was in mind, so such layers can be thought as ( in the html world ) links, image maps, layers, external widgets.
Prima::TextView::EventRectangles class is provided
for such usage. Its property
::rectangles contains an array of
rectangles, and the
contains method returns an integer value, whether
the passed coordinates are inside one of its rectangles or not; in the first
case it is the rectangle index.
Prima::TextView - rich text browser widget