wiki:TEXT_MODEL_REDESIGN
Last modified 15 years ago Last modified on 03/31/10 10:55:45

Error: Macro BackLinksMenu(None) failed
compressed data is corrupt

Error: Macro TicketQuery(summary=TEXT_MODEL_REDESIGN, format=table, col=summary|owner|status|type|component|priority|effort|importance, rows=description|analysis_owners|analysis_reviewers|analysis_score|design_owners|design_reviewers|design_score|implementation_owners|implementation_reviewers|implementation_score|test_owners|test_reviewers|test_score|) failed
current transaction is aborted, commands ignored until end of transaction block

Analysis

Overview

This task is result from the merge of #2370 (HOT_TEXT_INTERNAL) and #2344 (TEXT_LAYOUT_COMMONS). This is needed because the tasks are too connected to each other.

  • The existing stable poses in the sophie text are not easy to work with and require working with text history which is not cheap and fast enough. This task should remove the stable poses from the text.
  • The current Hot Layout has several important issues:
    • It is not known how will hyphenation be integrated with it;
    • Spell check underlining is not currently possible;
    • Text links cannot have foreground color;
    • Pressing "Tab" inserts 4 spaces, instead of a real tab;
    • Performance issues - Segment layouts could not be cached, which means that their redrawing is very slow.

Task requirements

  • Remove the HotPos class
  • Remove most of the ImmHotText existing code. Keep the recent text functionality and improve the code quality.
  • Modify the layout basics, so that
    • Tab will be functional;
    • Future features (hyphenation, spell check, links foreground) will be achievable;
    • Performance will not degrade. If possible, it should be improved.
  • In the design section, describe:
    • What changes will be made to the layout mechanism;
    • What causes theses changes, what will the effect be;
  • In the implementation, describe:
    • Implementation idea for hyphenation;
    • Implementation idea for spell check and foreground highlighting.

Task result

The result should be code.

Implementation idea

  • Remove HotPos class(the stable poses) and replace them with unstable text indexes.
  • Keep the text styles in every text unit.
  • Drop TextLayout, LineBreakMeasurer and AttributedCharacterIterator - use Font instead. This will allow deleting of most of the HotSegmentLayout contents. Several utility methods will also be deleted. Hooray :)

(Add links to related tasks that could be useful or helpful.)

How to demo

  • Run all the text related tests in sophie.
  • Play around with a text frame - show affine transforms, wrapping modes, chaining, navigation, etc.

Design

TEXT MODEL PART

The idea of the new text design is to remove the existing stable poses with unstable indexes. In order to achieve this functionality the following changes have been made:

  • Rename HotIndexInterval in org.sophie2.base.model.text.smart.position package to HotTextInterval.
  • From org.sophie2.base.model.text.smart.position remove HotInterval and replace its usage with HotTextInterval.
  • Remove the HotPos class.
  • Remove the HotPos persister class.
  • In HotTextInterval add a comparator for intervals.
  • In org.sophie2.base.model.text.smart package add new class named TextUnit - this class represents a single text unit in sophie text. This class is needed mostly to hold the styled value of every single text unit in the whole text.
    • Every text unit holds a char value and HotStyleDef value.
    • Add getUnitStyle function in the class to get the unit style.
    • Add getUnitChar function to get the char.
  • In org.sophie2.base.model.text.smart add ImmText interface - the interface of the new sophie text.
    • Hash getStyledHash() - Returns the styled hash of the text.
    • CharSequence toSequence() - Gets the chars of the text as CharSequence.
    • TextUnit unitAt(int index) - Method for getting a text unit at a specific index.
    • ImmText replace(HotTextInterval interval, ImmText text) - Method for replacing interval of the given text with another text. Replaces form the begin index inclusive to the end index exclusive. Applies the styles from the first replaced text to the replacing text.
    • T getStyleValue(HotAttr<T> attr, HotTextInterval interval) - Gets the style value for a given attribute and given interval. If not all interval has the same value for the given attribute - null is returned.
    • ImmText applyStyle(HotStyleDef style, HotTextInterval interval) - Applies style to the text for a given interval.
    • int getBegin() - Gets the begin of the text.
    • int getEnd() - Gets the end of the text.
    • ImmText subText(HotTextInterval interval) - Creates a new sub text of the given text.
  • In ImmHotText class remove all the existing fields and add ImmList<TextUnit> textUnits.
  • Make the ImmHotText class implement the ImmText interface and implement the functions of the interface.
  • Make HotStyleDef class implement Hashable and intern the HotStyleDef's values(add static field with the used styles till now and when creating a new HotStyleDef object if the map contains the object take the containing object else add the object in the map).
  • In HotStyleDef class add replaceDerive function that concatenates only the non default values from this style and the given one and constructs a new HotStyleDef with this values.
  • In HotStyleDef class add getAllAttr function that returns list of all the CommonAttr's values.
  • In CommonAttr add a new value: HotAttr<String> LINK_ATTRIBUTE - represents the link id's for the text.
    • Two different attributes are needed because two different text processors will handle them(we may need to underline the links and change the

foreground of the text with the attachments and this should be handled differently).

  • The idea of the links and attachments is to handle them as styles (apply styles to the text when added).
  • In LinkAttachment class add String linkId attribute - this is a random final value that is unique for every link in the text and is genetared in the constructor of the class. Add a getter method for the value.
  • In HotTextResourceR4 class add Key<ImmMap<String, LinkAttachment>> KEY_TEXT_LINK_MAP key that holds all the the text link attachments' ids to the link attachment itself(this is used in the ligics for adding and removing text links).
  • The new logic for adding/removing link is as follows: first applying style to the text with the map consisting of LINK_ATTRIBUTE with value equal to the added link id's(the default LINK_ATTRIBUTE's value if removed), second we change the map of the current hottextresource to a new one(with one added or removed value).
  • In TextViewFlow add abstract Prop<ImmMap<HotTextInterval, Attachment>> attachmentMap() - Property, holding map of intervals to link attachments. It holds all the links that are not none-refs(it is called attachmentMap not linkAttachmentMap because it will hold all the link attachments and the other attachments in the future).
  • In org.sophie2.base.model.text.smart add new utility class ImmTextUtils that has:
    • ImmText createEmptyText() - Creates an empty text.
    • int advance(ImmText text, int index, int offset) - navigates in text(if negative offset - navigate backwards else advande forwards).
    • HotTextInterval getIntervalForAttr(ImmText text, HotAttr<T> attribute, int position) - Returns interval from the given position to the last position in the given text before which the value of the given attribute in this position is the same as the value of the attribute in the given position.
    • boolean isIndexInText(int index, ImmText text) - Checks if the given index is in the bounds of the given text.
    • ImmText addLinkAttachment(ImmText text, String linkAttachmentId,HotTextInterval attachmentInterval) - Adds LINK_ATTRIBUTE to the given text with given link id.
    • ImmText removeLinkAttachment(ImmText text,HotTextInterval attachmentInterval)- Removes LINK_ATTRIBUTE from the given text.This method only applies style to the given interval that has link attribute set to it's default value. Does not check if the interval has consistent link attribute.
    • <T> T getStyleValue(ImmText text, int position, HotAttr<T> attribute) - Retrieves a specific text attribute's value at a specific position of the given text.

TEXT LAYOUT PART

Why should TextLayout be removed from our code:

  • We only use the nextLayout() method, get the line metrics and get the text hit info (determine which char hits a concrete Point2D) from it. Drawing with TextLayout is actually problematic, since it causes the baseline zig-zagging problem. Thus we use tons of reflection, which I suppose is not good.
  • TextLayout is constructed by a LineBreakMeasurer, which demands AttributedCharacterIterator to be made out of the HotText. In the current situation, this could cause some performance problems with large texts and many styles. On the other hand, LineBreakMeasurers need to be cached in order to have better performance (so says the JavaDoc), but they are statefull. So, simultaneous usage is quite undetermined (this could happen with the backgound thread which is used by the lazy layout).

What to use instead:

  • Definition: a TextRun is a sequence of characters with equal styles.
  • :)

  • The HotText will provide us the longest possible TextRun, starting from a concrete position. The method would look like this: TextRun getRun(int charIndex); This method will be fast enough with the new HotText design.
  • TextRuns can be cached, because they are immutable. They hold enough information for creating a Font object (it needs CharSequence and AttributeMap), which can create a GlyphVector. The font can also create FontMetrics, which gives additional information.
  • For calculating the line height of a paragraph, we get all the text runs from it, retrieve their font metrics and sum the ascent and descent values. Then we return the largest value from these.
  • For laying out a paragraph text, we get the words one by one and create glyph vectors subsequently, then using their getLogicalBounds methods, we determine the sum of the widths of the vectors. If a vector does not fit in the space left, the whole word will be removed from the segment. If no words are laid out in the current segment, we try to lay out as many text runs as possible. If no runs can be laid out, we split the current TextRun and try again. This logic implements the common word-breaking policy in LineBreakMeasurer, which we currently use.
  • Drawing a TextRun: the vector generated from the run has getOutline() method, which returns a shape. The run attributes hold the foreground and backgound colors, so we fill the shape with the first and the rest with the second. Note: all the glyph vectors in a paragraph must be drawn on the same baseline offset. The baseline offset for a paragraph can be calculated together with the line height.
  • Decoration: the Font class can work with Font Family, Font Size, Bold and Italics attributes. Underline and Strikethrough will be implemented by drawing lines respectively on the underline and strikethrough offsets (retrieved from the font metrics). Other decorations (highlights, caret, spellcheck undrelining and others) will be implemented in another task.
  • Tabbed text: tabs can be calculated when creating the segment layouts. First of all, we must ensure that if the HotText breaks the text runs if a tab is met (e.g. a tab can only be in the end of a run). Next, put a constant in SegmentLayout which defines the max tab width (the logical width). Then we determine the actual tab width with the formula tabSize = MAX_TAB_WIDTH - tabXPos % MAX_TAB_WIDTH. We add the calculated value as an offset between the current glyph vector and the next one. This way, in a rectangular area, the tabbed text will be aligned and will look cool.
  • Since the HotPos will be removed, HotLayout should not use it. So simply replace all the HotPos-related methods with int-related ones.

Tests

Implementation

  • Some other tests were added: LayoutPerformanceTest and TextPerformanceTest.
  • Currently broken features:
    • Text persistence, import/export, copy/paste.
    • Server Collaboration.
    • Highlight/caret/link drawing.
    • Caret updating (when you type text the caret does not move, when you press "Select All" and delete the text, assertionError occurs).
    • Text justification.
    • Search in chained frames.
    • The text font halo menu is somehow not positioned properly in chained frames.
  • The text model seems to work quite faster, related to the trunk one.
  • The layout is also much faster, but we expect to be slowed down quite a lot during the next task (the post-processor will need time to process texts).
  • Implmentation ideas:
    • The hyphenation should be easily implemented in future, since we currently break words on our own in HotSegmentLayout. The difference will be that we will break the text into hyphens instead of words. So, the word break policy will seem like that: Try to layout a word, if not possible, try to layout as many hyphens as possible. If no words are laid out, try to layout as many characters as possible. Some hyphenation library should provide hyphen-breaking of a word. That should be all.
    • Spellchecking capability seems to be implemented in our next task. We will create a text post-processor, which can handle a ImmText so that it will contain certain attributes about some decoration. A spellcheck processor will need to be created - it will receive raw text and return processed one (with curly underlines maybe).

Edit:

  • Text right-to-left support is also not working. Generally, every glyph-processing language would not work with this implementation of TextRun.layoutGlyphVector(). These languages include Arabic, Hebrew, Hindi and maybe some others.

Testing

(Place the testing results here.)

Comments

(Write comments for this or later revisions here.)