The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.


Prima::TextView - rich text browser widget


 use strict;
 use warnings;
 use Prima qw(TextView Application);

 my $w = Prima::MainWindow-> create(
     name => 'TextView example',

 my $t = $w->insert(TextView =>
     text     => 'Hello from TextView!',
     pack     => { expand => 1, fill => 'both' },

 # Create a single block that renders all the text using the default font
 my $tb = tb::block_create();
 my $text_width_px = $t->get_text_width($t->text);
 my $font_height_px = $t->font->height;
 $tb->[tb::BLK_WIDTH]  = $text_width_px;
 $tb->[tb::BLK_HEIGHT] = $font_height_px;
 $tb->[tb::BLK_BACKCOLOR] = cl::Back;
 $tb->[tb::BLK_FONT_SIZE] = int($font_height_px) + tb::F_HEIGHT;
 # Add an operation that draws the text:
 push @$tb, tb::text(0, length($t->text), $text_width_px);

 # Set the markup block(s) and recalculate the ymap
 $t->{blocks} = [$tb];

 # Additional step needed for horizontal scroll as well as per-character
 # selection:
 $t->paneSize($text_width_px, $font_height_px);

 run Prima;


Prima::TextView accepts blocks of formatted text and provides basic functionality for text scrolling and user selection. The text strings are stored as one large text chunk accessible with the ::text and ::textRef properties. A block of formatted text is an array with a fixed-length header and following commands. Each command is formed as an opcode followed by a fixed number of arguments. The block header contains the text offset, which text commands implicitly add to when addressing text strings by the offsets in their arguments.

The package tb:: provides the block constants and simple functions for creating and accessing blocks, opcodes, and commands.


Prima::TextView is mostly about text block functions and helpers. It provides functions for wrapping text blocks, calculating block dimensions, and drawing and converting coordinates from (X,Y) to a block position. The class functionality is focused on the text functionality, and although any custom graphic of arbitrary complexity can be embedded in a text block, the internal coordinate system is ( TEXT_OFFSET, BLOCK ) , where TEXT_OFFSET is the text offset from the beginning of a block and BLOCK is an index of a block.

The functionality does not imply any particular text layout - this is up to the class descendants, they must provide their own layout policy. The only policy Prima::TextView requires is that the 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 handles the commands changing of color, backColor, and font, and the painting of text strings. Other types of graphics can be achieved by supplying custom code.

block_draw CANVAS, BLOCK, X, Y

The block_draw method draws BLOCK on the CANVAS in screen coordinates (X,Y). It may be used not only inside begin_paint/end_paint brackets; CANVAS can be an arbitrary Prima::Drawable descendant.

block_walk BLOCK, %OPTIONS

Cycles through the block opcodes, calls the relevant callbacks on each. The callbacks can be supplied in %OPTIONS.

Coordinate system methods

Prima::TextView employs two own coordinate systems: document-based (X,Y) and block-based (TEXT_OFFSET,BLOCK). Each block's text offset is also referred to as big text offset vs small text offset that is used by individual commands; the small text offset always is added to the block's big text offset to address the string in the widget's text scalar.

The document coordinate system is isometric and measured in pixels. Its origin is located in the imaginary point of the beginning of the document ( not in the first block! ), in the upper-left pixel. X increases to the right, and Y increases down. The block header values BLK_X and BLK_Y use document coordinates, and the widget's pane extents ( regulated the by ::paneSize, ::paneWidth and ::paneHeight properties ) are also in the document coordinates.

The block coordinate system is anisometric - its second axis BLOCK, is an index of a text block in the widget's blocks storage, $self->{blocks}, and its first axis TEXT_OFFSET is a text offset from the beginning of the block.

Below are described different coordinate system converters:

screen2point, point2screen X, Y

screen2point accepts (X,Y) in the screen coordinates ( O is the lower left widget corner ) and returns (X,Y) in document coordinates ( O is the upper left corner of the document ). point2screen does the reverse transformation.

xy2info X, Y

Accepts (X,Y) is document coordinates, returns (TEXT_OFFSET,BLOCK) coordinates where TEXT_OFFSET is the text offset from the beginning of a block ( not of the whole text! ) , and BLOCK is an index of a block.


Accepts (TEXT_OFFSET,BLOCK) coordinates and returns (X,Y) in document coordinates of a block.

text2xoffset TEXT_OFFSET, BLOCK

Returns the X coordinate where TEXT_OFFSET begins in a block. BLOCK is the index of the latter.


Accepts (TEXT_OFFSET,BLOCK) coordinates and returns the text offset from the beginning of the whole text.

text_offset2info TEXT_OFFSET

Accepts big text offset and returns (TEXT_OFFSET,BLOCK) coordinates

text_offset2block TEXT_OFFSET

Accepts big text offset and returns the BLOCK coordinate.

Text selection

The text selection is performed automatically when the user selects a text region with the mouse. The selection is stored in (TEXT_OFFSET,BLOCK) coordinate pair and is accessible via the ::selection property. If its value is assigned to (-1,-1,-1,-1) then this indicates that there is no selection. For convenience, the has_selection method is introduced.

Also, 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 the Ctrl+Insert key combination.

A block with TEXT_OFFSET set to -1 will be treated as not containing any text, and therefore will not be able to get selected.

Event rectangles

Partly as an option for future development, partly as a hack a concept of event rectangles was introduced. Currently, the {contents} private variable points to an array of objects equipped with the on_mousedown, on_mousemove, and on_mouseup methods. These are called by the widget mouse events so that the overloaded classes can define the interactive content without overloading the actual mouse events ( which is although easy but is dependent on the implementation of Prima::TextView's mouse handlers ).

As an example, Prima::PodView uses the event rectangles to catch the mouse events over the document links. Theoretically, every 'content' can be bound with a separate logical layer; the concept was designed with an HTML browser in mind, so such layers can be thought of as links, image maps, layers, external widgets, etc in the HTML world.

Currently, the 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.


Dmitry Karasik, <>.


Prima::Drawable::TextBlock, Prima::PodView, examples/