53 | | * Rename HotIndexInterval in org.sophie2.base.model.text.smart.position package with HotTextInterval. |
54 | | * From org.sophie2.base.model.text.smart.position remove HotInterval and replace all of its usage |
55 | | in Sophie with HotTextInterval. |
56 | | * Remove HotPos class. |
57 | | * Remove HotPos persister class. |
| 55 | |
| 56 | * Rename HotIndexInterval in org.sophie2.base.model.text.smart.position package to HotTextInterval. |
| 57 | |
| 58 | * From org.sophie2.base.model.text.smart.position remove HotInterval and replace its usage with HotTextInterval. |
| 59 | |
| 60 | * Remove the HotPos class. |
| 61 | |
| 62 | * Remove the HotPos persister class. |
| 63 | |
| 134 | === Text Layout === |
| 135 | |
| 136 | Why should TextLayout be removed from our code: |
| 137 | |
| 138 | * 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. |
| 139 | |
| 140 | * 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). |
| 141 | |
| 142 | What to use instead: |
| 143 | |
| 144 | * '''Definition''': a TextRun is a sequence of characters with equal styles. |
| 145 | |
| 146 | * :) |
| 147 | |
| 148 | * 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. |
| 149 | |
| 150 | * 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. |
| 151 | |
| 152 | * 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. |
| 153 | |
| 154 | * 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 line-breaking policy in LineBreakMeasurer, which we currently use. |
| 155 | |
| 156 | * 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. |
| 157 | |
| 158 | * 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. |
| 159 | |
| 160 | * 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. |
| 161 | |
| 162 | === Positioning === |
| 163 | |
| 164 | * Since the HotPos will be removed, HotLayout should not use it. So simply replace all the HotPos-related methods with int-related ones. |
| 165 | |
| 166 | |
| 167 | |
| 168 | == Tests == |
| 169 | |
| 170 | * We should make all the current text and layout-related tests to work again. |
| 171 | * New tests: [branches/private/kyli/2344/modules/org.sophie2.base.model.text/src/test/java/org/sophie2/base/model/text/smart/TextPerformanceTest.java Performance test], [branches/private/kyli/2344/modules/org.sophie2.base.model.text/src/test/java/org/sophie2/base/model/text/smart/ImmTextTest.java] |