Prima::TextView(3)

πŸ“¦
version 1.74000 Β· 5 versions
, in opensuse-leap 16.0

πŸ’– Prima::TextView(3) β€” rich text browser widget, but make it fashion πŸ’…

Ok so bestie, this is the Prima::TextView page from the Perl manual (perl v5.42.0, hot off the press 2024-08-20, we'll get to that at the end πŸŽ€). Buckle up, because this widget is lowkey a whole personality: it's a rich text browser and it's about to eat. πŸ”₯


✨ NAME ✨

Prima::TextView β€” a rich text browser widget. That's it, that's the vibe. πŸ‘‘


🌸 SYNOPSIS 🌸

Here's the whole thing in action, no cap, copy it exactly:

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];
$t->recalc_ymap;

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

run Prima;

Like, look at her go. She's beautiful. πŸ˜­πŸ’–


πŸ¦‹ DESCRIPTION πŸ¦‹

So Prima::TextView takes blocks of formatted text and gives you the basics for text scrolling and user selection. The text strings all live together as one big text chunk, and you can get at it with the ::text and ::textRef properties. 🫢

A block of formatted text is an array, and it's got a fixed-length header up front followed by commands. Every command is an opcode plus a fixed number of arguments. The block header holds the text offset, and text commands implicitly add that offset when they're addressing text strings via the offsets in their own arguments. Sneaky but iconic. ✨

The package tb:: is your bestie here β€” it hands you the block constants plus simple functions for creating and accessing blocks, opcodes, and commands. πŸŽ€

🌈 Capabilities

Prima::TextView is mostly about text block functions and helpers: wrapping text blocks, calculating block dimensions, drawing, and converting coordinates from (X,Y) into a block position. The class is focused on text functionality, and even though you can embed any custom graphic of arbitrary complexity into 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. πŸ’«

This functionality does not imply any particular text layout β€” that's up to the class descendants, and they have to bring 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. Text gaps, though? Totally allowed, live your truth. πŸ™ˆ

The basic text block drawing function handles the commands for changing color, backColor, and font, plus painting text strings. Any other kind of graphic? You supply custom code, bestie. 🎨

  • block_draw CANVAS, BLOCK, X, Y β€” draws BLOCK on the CANVAS in screen coordinates (X,Y). Ngl this one's flexible: it's not only usable inside begin_paint/end_paint brackets, because CANVAS can be any arbitrary Prima::Drawable descendant. Slay. πŸ’…
  • block_walk BLOCK, %OPTIONS β€” cycles through the block opcodes and calls the relevant callbacks on each one. You drop those callbacks in %OPTIONS. Simple as. πŸ“

πŸ’œ Coordinate system methods

Prima::TextView is running two coordinate systems, because one is never enough, main character energy: document-based (X,Y) and block-based (TEXT_OFFSET,BLOCK). Each block's text offset is also called the big text offset, versus the small text offset used by individual commands β€” the small text offset always gets added to the block's big text offset to address the string in the widget's text scalar. iykyk. 🧚

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

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. 🌸

Here come the converters, and yes, we're listing every single one because I would never do you like that: πŸ’–

  • screen2point, point2screen X, Y β€” screen2point takes (X,Y) in screen coordinates (where O is the lower left widget corner) and returns (X,Y) in document coordinates (where O is the upper left corner of the document). point2screen does the reverse transformation. πŸ”
  • xy2info X, Y β€” takes (X,Y) in document coordinates and 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. πŸ—ΊοΈ
  • info2xy TEXT_OFFSET, BLOCK β€” takes (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. πŸ“
  • info2text_offset β€” takes (TEXT_OFFSET,BLOCK) coordinates and returns the text offset from the beginning of the whole text. 🌟
  • text_offset2info TEXT_OFFSET β€” takes a big text offset and returns (TEXT_OFFSET,BLOCK) coordinates. πŸ’«
  • text_offset2block TEXT_OFFSET β€” takes a big text offset and returns the BLOCK coordinate. 🎯

πŸŽ€ Text selection

Selection happens automatically when the user selects a text region with the mouse. It's stored as a (TEXT_OFFSET,BLOCK) coordinate pair and you can reach it through the ::selection property. If you assign it (-1,-1,-1,-1), that means there's no selection β€” for convenience there's also the has_selection method. πŸ–±οΈ

Also: get_selected_text returns the text inside the selection (or undef if there's no selection), and copy automatically copies the selected text into the clipboard. That last one is bound to the Ctrl+Insert key combo. Obsessed. πŸ’…

Heads up bestie: a block with TEXT_OFFSET set to -1 is treated as not containing any text, so it straight up will not be selectable. Not me warning you in advance. 😭

πŸ¦„ Event rectangles

Partly as an option for future development, partly as a hack, the concept of event rectangles exists. Currently the {contents} private variable points to an array of objects equipped with on_mousedown, on_mousemove, and on_mouseup methods. The widget's mouse events call these, so overloaded classes can define interactive content without overloading the actual mouse events (which is easy, sure, but it depends on the implementation of Prima::TextView's mouse handlers). 🀯

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

Right now, the Prima::TextView::EventRectangles class is provided for this usage. Its ::rectangles property holds an array of rectangles, and the contains method returns an integer telling you whether the coordinates you passed are inside one of its rectangles or not β€” and if they are, that integer is the rectangle index. ✨


πŸ‘‘ AUTHOR πŸ‘‘

Dmitry Karasik, <dmitry@karasik.eu.org>. Legend behavior, fr fr. 🫢


🌟 SEE ALSO 🌟

Go check out Prima::Drawable::TextBlock, Prima::PodView, and examples/mouse_tale.pl. Do it, iykyk. πŸ’–


And that's the whole page, bestie! This little beauty came from perl v5.42.0, dated 2024-08-20 β€” so go forth, render some text blocks, and remember: strictly increasing BLK_TEXT_OFFSET, no overlapping chunks, and absolutely no selection for your -1 blocks. Periodt. πŸ’…βœ¨πŸŽ€

πŸ“¦ where this page comes from