Ilya Zakharevich > etext > textblocks

Download:
modules/etext/etext.1.6.3.zip

Annotate this POD

View/Report Bugs
Source  

NAME ^

textblocks - additional text widget subcommand for Tk distribution.

SYNOPSIS ^

  .widgetName block subcommand ?opt opt ...?
  .widgetName configure -getFlags integer

What else is in the widget?

The other difference with the stock Text widget is the object oriented dump. It dumps a list of objects which represent the contents of the widget in the given range. The temporary position for this command is

   .widget block list from to

An object is a real object in Perl, and a list with a first element being the class id in TCL (the rest being the contests, in what follows the first element of the rest is referenced as the first element of the object).

The particular elements of the list represent:

String

An object with contents being the string. Object class is Tk::Text::String, class id is S.

Marks

The contents is the name of the mark, the classes are Tk::Text::MarkLeft with id L, and Tk::Text::MarkRight with id R.

Tags

The contents is the name of the tag, the classes are Tk::Text::TagOn with id T, and Tk::Text::TagOff with id t.

Window

The contents is a list of the name of the window and the script to create the window, the class is Tk::Text::EmbWin with id W.

block

A block is dumped as a list of

type

The first element is a list of block name, depth and (optionally) instance data.

contents

The rest of the list represents the contents of the block in the same form as the top-level dump, with exception for inter-leaf separators.

The inter-leaf separators are dumped as on object of class Tk::Text::BlockSeparator, with id |, the contents being the "depth" of the separator. Note that the minimal possible depth is 1 for separators between leaves of the same parent.

The object class in Perl is Tk::Text::Block, the class id is B.

Empty

An empty block is dumped in the same way with the object class being Tk::Text::Empty, the class id being E. The contenst is the list of the block name, and (optionally) instance data.

What is a block?

A Block is an additional editable unit of information for inclusion into widget. Block consists of block type, block contents, and, possibly, block instance data. Block contents is a tree whose leaves can contain anything that can be in a row in a text widget (including other blocks). Block instance data can be arbitrary data as in a language variable.

Current implementation allows only trees with the distance from root to leaves being the same for all the leaves.

Examples of blocks

Here we provide several examples on the internal structure of blocks that can implement the following objects. To see how the internal structure (does not) correlate with the visual appearance, see "Display of blocks".

Button
    ___ Text to show when released
   /
   \___ Text to show when pressed
Multilevel sub-super-script
     __ First superscript
    /
   /\__ Second superscript
  /
  \    __ First subscript
   \  /
    \/___ Second subscript
     \
      \__ Third subscript
Radical
  ___ Text to show inside
Tabulation
  (Empty)

Indices in blocks

A block occupies the following place in the index space: 1 index unit for the start of the block, then all the leaves separated by 1 index separators, and 1 index for the end of the block. Thus a block with one empty leaf occupies 2 index units.

An empty block (without leaves) occupies 1 unit of the index space.

Note that a block (however complicated) always fits in one line of the index space. In particular, the usual bindings for up and down will move you out of a block. (You should expect it, since leaves of a block may be placed one under another, but may be not.)

String representation of blocks

The commands that convert block contents to string (.t get, selection and search) see the contents of a block as a string of the same size as the size of block in the index space. The start of the block looks like {, end like }, interleaf separators like |.

An empty block looks like ..

Note that a string representation of a block (however complicated) always fits in one line.

Insertion in blocks

Insertion changes contents of the block if the insertion point is inside the block, i.e., after starting index of the block and before the ending position (and not inside some other block contained in the given one). Note that it is impossible to be inside an empty block.

If inserted text contains no newlines, it is added to the leaf of tree the insertion point is in (i.e., at or after the start of, and before the end of). If it contains newlines, then the resulting leaf is split into several at the inserted newlines. All the new leaves have the same parent node as the initial leaf. Registered callback will be called on insertion of newlines (not implemented).

Destruction of blocks

When the block is destructed, it contents is merged into the surrounding environment. Block's head and tail disappear, interleaf separators behave like newlines. I.e., if block was contained in another block, interleaf separators of deceased block became interleaf separators of the parent block, otherwise they became line separators in the text widget.

Deletion of text in blocks

Tags and blocks

Characters inside leaves can be tagged in the usual way. Interleaf separators and tail of the block behave like end-of-lines when tagged. The behaviour of tags on the start of block is undefined.

Bounding boxes

Bounding box for anything invisible is of size 0 and at the upper-left corner of the block.

Display of blocks ^

The display of blocks is completely customizable. It is based on the current configuration of the type of displayed block.

Layout callback ^

Layout callback is the main component of the block.

Details are subject to change.

A layout procedure takes a variable number of input arguments. The first element is a list containing the block name and widget name, (possibly with an addition, say, if instance data is present in the block, it will be the next element). The second element gives the horizontal offset of the start of the block inside the ambient line (it can be different from the absolute horizontal position if the block is inside some other block). The remaining elements specify the tree structure of the block and the layout details of the leaves. Any child of the root node of the tree provides one argument of the function. All the arguments are lists, and

Summary:

Input arguments:
  {bname wname ...} ho {rc yo w tw h b hs vs bs} ...
Abbreviations:

BlockNAME, WidgetNAME, Horizontal Offset, Repeat Count, Y Offset, Width, Total Width, Baseline. hs, vs, bs are currently not used.

The return value of the callback must be a list. The length of the list (counting multiplicities, see below) must be at least the number of leaves plus one. The first element of the list specifies the layout information on the block as a whole, the others specify layout information for the leaves and (possibly) additional elements to show.

There are three possible formats of the elements in this list. All are lists of numbers (and should be of the form {dd ... dd}) of lengths 1, 7, or 13. The first number is either repeat count, or id of additional line to show (i.e., the same data as returned by textWidget block addline index command). Lists of different length have the following meaning:

1:

the child is not shown at all. This format is prohibited for the layout information of the block as a whole and for additional lines. The only element of the list is the repeat count.

7:

If the block as a whole is not laid out yet, this is the information about it, and all the elements but what describe width, height and baseline are ignored. If not all the leaves are laid out yet, the first element is the repeat count, what means the number of leaves that are described by this list (it can be 0).

The remaining elements of the list provide x and y offsets of the upper-left corner inside the ambient block, width, total width, height, and baseline of the block as a whole, or of a group of leaves, or a leaf. If it describes a group of leaves, the leaves are laid out as by default layout procedure. Width, height, and baseline information is used for displaying background of the line.

If all the leaves are laid out, then this list describes an additional element to draw if the first element is greater than 0, i.e. it is considered a Block ID of an additional drawing element, and the rest specifies the layout of this element. If it is 0, then this element of the list is ignored exactly as in other cases.

13:

(not implemented, but accepted by the widget): the first 7 elements have the same meaning as above, the last 6 provide stretchability x and y position of upper-left corner, width, total width, height, and baseline.

The total width above is the width of the leaf including the space reserved for showing the background of the leaf terminator.

Summary:

Return list elements:
  {Block Layout} {Leaf0 Layout} ...
        {Leafn Layout} {Additional Element0 Layout} ...
        {Additional Elementn Layout}
Layout Styles
  • Block Layout - {ig ig ig w ig h b}
  • Show one or more leaves - {rc xo yo w tw h b}
  • Hide one or more leaves - {rc}
  • Show Additional Elements - {bi xo yo w tw h b}

    (w is not very important), put after leaves.

Abbreviations:

IGnored, Width, Total Width, Height, Baseline, XOffset, YOffset, Repeat Count, Block Id

None of the returned date is used for clipping. However, it is used in directing the mouse events and reporting bounding boxes.

Lying to layout callback

It is possible to configure a block to lie about its structure to the layout callback. This can greatly simplify writing the callback in interpreted languages, if the default layout procedure is satisfactory for parts of a block.

Setting the -layoutdepth and/or -layoutwidths options for a block forces the layout procedure to be called with agruments corresponding to modified tree structure.

If -layoutdepth is set, the depth of the tree is modified to be the given number. If the true depth of the tree is too small, tree is extended from the root side, if it too deep, the subtrees on the maximal allowed depth level are mocked as lines with repeat count.

If -layoutwidths is set, this can restrict the number of branches going out of any node of the modified tree. The value of this option should be a list, elements of this list are either numbers, or lists of the form {min max}. (A single number is equivalent to a pair {number number}). N-th element of the list controls the number of branches going out of nodes on the distance N from the root. If there are too few branches, it adds branches that contain simulated groups of leaves with repeat count 0. If there are too many branches, it groups several last brunches into one with appropriate repeat count. (Current implementation is buggy in interaction of -layoutwidths and -layoutdepth if the depth of the node is less than -layoutdepth.)

Block subcommands ^

configure blockName ?option value ... ?

Standard procedure for creating and configuring block type. Currently supported configuration options are

-layoutcmd

Layout callback.

-layoutdepth

Depth of simulated tree for layout callback. Default -1.

-layoutwidths

List of widths of levels of simulated tree for layout callback. Default {}.

-empty

Boolean value that specifies that the blocks of this type contain no leaves (empty blocks).

cget

Standard configuration get procedure.

at index

Returns information in the block that starts at the given index or "nothing" if there is no such block. Returned information is a list consisting of block start index, block length in index space, block name, block instance data, structure of the block. The latter is a list (possibly of lists) with leaves being lengths of block leaves.

of index

Returns information in the innermost block that contains the given index or empty list if there is no such block. See at subcommand for description of return value.

delete blockName ?blockName ...?

Deletes the information on block types. Error is returned if the are blocks of the given types in the widget.

insert blockName index1 ?index2?

Inserts block of the given type in the widget. The block contains one node, and all the leaves are immediate children of this node. If index2 is not specified, creates a block with one empty leaf, otherwise moves the contents of the widget between index1 and index2 into the tree.

In the latter case index1 and index2 should be outside of any block, or properly inside the same block. The newlines (or, correspondingly, interleaf separators of enclosing block) are converted into interleaf separators of newly created block. This is prohibited for empty blocks.

names

Returns list of names of currently defined block types.

rename index name

Change the name of the block containing position index to be name.

split index ?level?
  • If index is outside of blocks, inserts newline.
  • Otherwise, if level is not specified, or is 0, is equivalent to insertion of newline, i.e., splits the leaf into two with the same parent.
  • If level is positive, splits the leaf in two and changes the structure of the tree.
    • If level is less than the distance from the leaf to the root, splits the levelth parent (and all the parents of less order) in two. The beginning of the split leaf goes to one node, the rest (together with the leafs in the same node after the splitted one) goes to another. Example of split 1 follows (the leaf marked X is split into two marked x):
            ___                 ___
           / __                /  _
          / /                 /  /
       .-------X           .-------x
        \   \__             \ \____x
         \_____              \   \_
                              \____
    • If level is too big to proceed with the above procedure, the tree is extended to the left first. The added nodes have one child each.
trim index

If the block at index has only one edge coming from the root, and this edge does not end in a leaf, deletes the root. The resulting tree will be 1 level more shallow. Returns error if out of block or the above conditions are not met.

data index ?data?

Returns or sets instance data information for the block at index.

addline index

"Steals" the displayed structure of the line starting at index. Returns the id of the resulting element. This id can be used in the return value of the layout callback to specify additional elements used in drawing of the block. Beware that the behaviour is undefined if the line contains something more complicated than tagged characters.

deletelines

Deletes all the "stolen" lines.

list from to

See "What else is in the widget?".

Additional text widget options ^

-getFlags

The value is an integer, binary bits of this integer denote current text representation options. If no bits are set, the operations that consider text contents as a string behave like the standard Tk text widget.

If bit 1 is set, the string representation of a text range always gives a string of the same length as the length of the interval in the index space. If some text annotation results in an empty string in the standard representation, it is represented as an appropriate number of *s if this bit is set. Currently this concerns embedded windows only, which are represented as *.

All undocumented bits are reserved and should be 0.

Minitutorial ^

Example of a simple "pedestal" block

Suppose you are satisfied with the standard layout procedure, but want to extend the resulting group of lines 10 points to the right (so when part of block is selected, selection extends 10 points to the right of the rightmost element in leaves), and want to add a blue 3D background of width 5 with "height" 2 around the resulting guy, and draw the block on gray 3D background of width 2.

Note that it is not enough to use a tag even for gray background, since the border of the tag is inside the rectangle that contains the letter, and to get a correct pedestal look we want the border to surround the letter, not to be drawn outside of them.

In what follows we use TCL as the language for callback. Of course, the resulting code will be much simpler in some other languages.

First, we create the 2 new additional elements to display:

  .t tag configure backgr1 -background blue -border 2 -relief raised
  .t tag configure backgr2 -background gray90 -border 2 -relief raised
  .t insert 1.0 \n backgr1
  set backgrId1 [.t block addline 1.0]
  .t delete 1.0 1.0+1c
  .t insert 1.0 \n backgr2
  set backgrId2 [.t block addline 1.0]
  .t delete 1.0 1.0+1c

(or we could use existing ones, if possible). The third row creates a row in the text widget that is empty, and has tag "backgr1". This row will show the background only, and we will use it to show the raised background behind the block. The fourth row memorizes the id of this additional line, and the fifth one returns the text in the widget to its initial state. The line is not shown anymore, but the way to show it is preserved in a safe place. The second element is created in the same way.

Next, since we are satisfied with the way the leaves of the tree are places on the screen (i.e., one under another), we do not want to be concerned with internal structure of the block tree, so we ask the widget to lie about internal structure of the tree:

  .t block config myBlock -layoutdepth 1 \
        -layoutwidths 1 -layoutcmd myLayoutCmd

Now the data given to myLayoutCmd will be always the same, as if the block consisted of one leaf only that is the direct child of the root. So the arguments the myLayoutCmd receives are the following ones:

  proc myLayoutCmd {block x row} {
    global backgrId1 backgrId2
    set c [lindex $row 0]
    set w [lindex $row 2]
    set tw [lindex $row 3]
    set h [lindex $row 4]
    set b [lindex $row 5]

Now $c, $w, $tw, $h, $b contain the count, width, total width, height and baseline of the contents of the block. Next we extend the total width of the row.

    set tw [expr $tw+10]
    set tw2 [expr $tw+4]
    set tw1 [expr $tw+10]

We will not replace the width of the row, so mouse events could be directed to outside of the block if clicked in the extended area. (In fact they won't, since we will extend the size of the ambient block.) Next we calculated widths of additonal rows,

    set h2 [expr $h+4]
    set h1 [expr $h+10]
    set b2 [expr $b+2]
    set b1 [expr $b+5]

and their height and baselines. All is prepared now to layout the rows:

    set addrow1 [list $backgrId1 0 0 $tw1 $tw1 $h1 $b1]
    set addrow2 [list $backgrId2 3 3 $tw2 $tw2 $h2 $b2]

Note that the same list $addrow1 is good for specifying the size of the total block, since the first element of the list (that is $backgrId) is ignored in the information for the whole block. Now we need to move the real contents of the block 5 points to the right and down with respect to the rectangle occupied by the block, and return the calculated information:

    set row [list $c 5 5 $w $tw $h $b]
    return [list $addrow1 $row $addrow1 $addrow2]
  }

Note that it is vital to put addrow2 after addrow1, since it should be drawn after the addrow1 for it to appear to be on top of addrow1.

That's all! Now you can create a binding for insertion of empty block into the widget (this binding supposes that bindtags are reverted):

  bind .text <Control-Meta-m> {
    %W block insert myBlock insert
    %W mark set index index-1c
    break
  }

This binding moves the insertion point inside the block, so you are ready to fill block with whatever information you need.

Sample code in the distribution of extended text widget provides other examples of blocks: superSub, Fraction, and so on.

Example of a simple "tabulation" block

Here we describe how to code blocks that contain no editable information, but provide geometry management. Consider an example of tabulation: if we implement tabulation as a block, the size of this block should change depending on the position of the start, but the only editing operation should be the deletion of the block as a whole.

While it is possible to implement this kind of block using the same types of blocks as in the previous section, we will need a lot of code change in the bindings if we use this approach: a usual block takes at least 2 positions in the index space, so it is possible to make an insertion inside this block. Thus we either need correction of keybindings that move the insertion point, or should correct the the insertion code to check whether the insertion happens inside a block of this type. If we do not do this, we need additional decision how to handle blocks of this type that have not-empty contents.

The solution is to use "empty" blocks that take only 1 position in the index space, so it is impossible to insert text inside them. We make block type empty by using configuration option -empty:

  .t block configure Tab -empty on -layoutcmd {layoutTab 5 35}

Note the spaces in the name of layoutcmd: No quoting is performed during the call, so 5 and 35 become first two arguments of layoutTab procedure. This procedure can be as simple as follows:

  proc layoutTab {min mult block x} {
    global backgrId2
    set w [expr $min + $mult - ($x + $min - 1) % $mult - 1]
    set totblock [list $backgrId2 0 0 $w $w 5 3]
    return [list $totblock $totblock]
  }

The only "working" row is the set w one. It calculates the width of the block using the following rule:

The last row returns a list of length 2. The first element is the description of the block as a whole, the next one is necesssarily the description of an additional element (since there are no "regular" leaves in the tree). This additional element is the same size as the block itself, and is used only to provide some visual feedback. We use id $backgrId2 created elsewhere (say, by the code in the previous example), and ask for the rectangle to be 3 points above the baseline, and 2 points below.

AUTHOR ^

Ilya Zakharevich <ilya@math.ohio-state.edu>

AVAILABILITY ^

ftp://ftp.math.ohio-state.edu/pub/users/ilya.

syntax highlighting: