source: trunk/src/org/expeditee/gio/swing/SwingTextLayoutManager.java@ 1184

Last change on this file since 1184 was 1184, checked in by bln4, 6 years ago

org.expeditee.gio.swing.SwingTextLayoutManager ->

Fixed a bug that was causing Frame titles on newly generated profiles (and other frames?) to have a their characters written one per line. The following is an explanation of the problem and solution that was implemented.

Due to the size of the font, combined with the length Frame title names tend to be, combined with the fact that they are only a single word, the SwingTextLayoutManager, which appears to contain code altered from before JFX snapshot, decides that these items are multiple lines long. This artificially inflates the size of the bounding box (needing an answer to determine it)!
The code as it was written in SwingTextLayoutManager.layoutString would first try laying out lines respecting the 'dontBreakWords' variable. When it cannot do this it chooses to ignore this variable and have a final attempt.
This final attempt however still respects the 'widthLimit' variable. The 'widthLimit' variable is a result of a (eventual) call to updateBounds(). This is therefore a chicken and egg situation.


My solution therefore is to: when giving up on attempting to respect the 'dontBreakWords' variable, also give up on the 'widthLimit' variable. This seems logical as, to break, it would require a scenario where we have a multi-word text item wherein the first word is too long to fit within width.
Further investigation is required to solve why the Y position of FrameTitle items are being positioned low.

File size: 8.3 KB
Line 
1package org.expeditee.gio.swing;
2
3import java.awt.Rectangle;
4import java.awt.font.FontRenderContext;
5import java.awt.font.LineBreakMeasurer;
6import java.awt.font.TextAttribute;
7import java.text.AttributedString;
8import java.util.HashMap;
9import java.util.LinkedList;
10import java.util.List;
11
12import org.expeditee.core.Font;
13import org.expeditee.core.Line;
14import org.expeditee.core.Point;
15import org.expeditee.core.TextHitInfo;
16import org.expeditee.core.TextLayout;
17import org.expeditee.core.bounds.AxisAlignedBoxBounds;
18import org.expeditee.gio.TextLayoutManager;
19
20/**
21 * TODO: Comment. cts16
22 *
23 * @author cts16
24 */
25public class SwingTextLayoutManager extends TextLayoutManager
26{
27 /** Singleton instance. */
28 private static SwingTextLayoutManager _instance = null;
29
30 /** Singleton instantiator. */
31 public static SwingTextLayoutManager getInstance()
32 {
33 if (_instance == null) {
34 _instance = new SwingTextLayoutManager();
35 }
36
37 return _instance;
38 }
39
40 /** Mapping from Expeditee text-layout objects to swing equivalents. */
41 private HashMap<Long, java.awt.font.TextLayout> _layoutMap;
42
43 private SwingTextLayoutManager()
44 {
45 _layoutMap = new HashMap<Long, java.awt.font.TextLayout>();
46 }
47
48 private synchronized void register(TextLayout layout, java.awt.font.TextLayout swingLayout)
49 {
50 if (layout == null || swingLayout == null) {
51 return;
52 }
53
54 _layoutMap.put(layout.getHandle(), swingLayout);
55 }
56
57 public synchronized java.awt.font.TextLayout getInternalLayout(TextLayout layout)
58 {
59 if (!isTextLayoutValid(layout)) {
60 return null;
61 }
62
63 return _layoutMap.get(layout.getHandle());
64 }
65
66 public synchronized boolean isTextLayoutValid(TextLayout layout)
67 {
68 if (layout == null) {
69 return false;
70 }
71
72 return _layoutMap.containsKey(layout.getHandle());
73 }
74
75 @Override
76 public float getAdvance(TextLayout layout)
77 {
78 if (!isTextLayoutValid(layout)) {
79 return Float.NaN;
80 }
81
82 return getInternalLayout(layout).getAdvance();
83 }
84
85 @Override
86 public int getCharacterCount(TextLayout layout)
87 {
88 if (!isTextLayoutValid(layout)) {
89 return 0;
90 }
91
92 return getInternalLayout(layout).getCharacterCount();
93 }
94
95 @Override
96 public TextHitInfo getNextLeftHit(TextLayout layout, int offset)
97 {
98 if (!isTextLayoutValid(layout)) {
99 return null;
100 }
101
102 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextLeftHit(offset));
103 }
104
105 @Override
106 public TextHitInfo getNextLeftHit(TextLayout layout, TextHitInfo hit)
107 {
108 if (!isTextLayoutValid(layout)) {
109 return null;
110 }
111
112 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextLeftHit(SwingConversions.toSwingTextHitInfo(hit)));
113 }
114
115 @Override
116 public TextHitInfo getNextRightHit(TextLayout layout, int offset)
117 {
118 if (!isTextLayoutValid(layout)) {
119 return null;
120 }
121
122 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextRightHit(offset));
123 }
124
125 @Override
126 public TextHitInfo getNextRightHit(TextLayout layout, TextHitInfo hit)
127 {
128 if (!isTextLayoutValid(layout)) {
129 return null;
130 }
131
132 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).getNextRightHit(SwingConversions.toSwingTextHitInfo(hit)));
133 }
134
135 @Override
136 public float[] getCaretInfo(TextLayout layout, TextHitInfo hit)
137 {
138 if (!isTextLayoutValid(layout)) {
139 return null;
140 }
141
142 return getInternalLayout(layout).getCaretInfo(SwingConversions.toSwingTextHitInfo(hit));
143 }
144
145 @Override
146 public TextHitInfo hitTestChar(TextLayout layout, float x, float y)
147 {
148 if (!isTextLayoutValid(layout)) {
149 return null;
150 }
151
152 return SwingConversions.fromSwingTextHitInfo(getInternalLayout(layout).hitTestChar(x, y));
153 }
154
155 @Override
156 public AxisAlignedBoxBounds getPixelBounds(TextLayout layout, float x, float y)
157 {
158 if (!isTextLayoutValid(layout)) {
159 return null;
160 }
161
162 return SwingConversions.fromSwingRectangle(getInternalLayout(layout).getPixelBounds(null, x, y));
163 }
164
165 @Override
166 public AxisAlignedBoxBounds getLogicalHighlightShape(TextLayout layout, int firstEndpoint, int secondEndpoint)
167 {
168 if (!isTextLayoutValid(layout)) {
169 return null;
170 }
171
172 Rectangle rect = getInternalLayout(layout).getLogicalHighlightShape(firstEndpoint, secondEndpoint).getBounds();
173
174 return SwingConversions.fromSwingRectangle(rect);
175 }
176
177 @Override
178 public float getAscent(TextLayout layout)
179 {
180 if (!isTextLayoutValid(layout)) {
181 return Float.NaN;
182 }
183
184 return getInternalLayout(layout).getAscent();
185 }
186
187 @Override
188 public float getDescent(TextLayout layout)
189 {
190 if (!isTextLayoutValid(layout)) {
191 return Float.NaN;
192 }
193
194 return getInternalLayout(layout).getDescent();
195 }
196
197 @Override
198 public float getLeading(TextLayout layout)
199 {
200 if (!isTextLayoutValid(layout)) {
201 return Float.NaN;
202 }
203
204 return getInternalLayout(layout).getLeading();
205 }
206
207 @Override
208 public synchronized void releaseLayout(TextLayout layout)
209 {
210 if (!isTextLayoutValid(layout)) {
211 return;
212 }
213
214 _layoutMap.remove(layout.getHandle());
215 }
216
217 @Override
218 public List<TextLayout> layoutString(String string, Font font, Point start, Line[] lineBreakers, int widthLimit, int lineSpacing, boolean dontBreakWords, boolean fullJustify)
219 {
220 if (string == null || font == null || start == null) {
221 return null;
222 }
223
224 // Make sure we have a swing font to use
225 java.awt.Font swingFont;
226 SwingFontManager fontManager = SwingMiscManager.getIfUsingSwingFontManager();
227 if (fontManager == null) {
228 return null;
229 }
230 swingFont = fontManager.getInternalFont(font);
231 if (swingFont == null) {
232 return null;
233 }
234
235 // Temporary list to accumulate the TextLayouts in to
236 List<java.awt.font.TextLayout> layouts = new LinkedList<java.awt.font.TextLayout>();
237 List<Integer> offsets = new LinkedList<Integer>();
238 offsets.add(0);
239 List<Point> positions = new LinkedList<Point>();
240 positions.add(new Point(start));
241
242 // Set up a line-break measurer to layout the text
243 AttributedString paragraphText = new AttributedString(string);
244 paragraphText.addAttribute(TextAttribute.FONT, swingFont);
245 FontRenderContext frc = new FontRenderContext(null, true, true);
246 LineBreakMeasurer lineBreaker = new LineBreakMeasurer(paragraphText.getIterator(), frc);
247 lineBreaker.setPosition(0);
248
249 // Keep laying out text until the input string is completely laid out
250 while (lineBreaker.getPosition() < string.length()) {
251
252 int width = widthLimit;
253 if (lineBreakers != null) {
254 width = Math.min(width, (int) getLineWidth(start, lineBreakers));
255 }
256
257 int end = string.length();
258 for (int i = lineBreaker.getPosition() + 1; i < string.length(); i++) {
259 if (string.charAt(i) == '\n') {
260 end = i;
261 break;
262 }
263 }
264
265 java.awt.font.TextLayout layout = lineBreaker.nextLayout(width, end, dontBreakWords);
266
267 // If it's impossible to layout any more text without breaking a word, just do it
268 if (layout == null && width == widthLimit) {
269 layout = lineBreaker.nextLayout(Integer.MAX_VALUE, end, false);
270 // If still impossible, give up
271
272 if (layout == null) {
273 break;
274 }
275 }
276
277 if (fullJustify && lineBreaker.getPosition() < string.length()) {
278 layout = layout.getJustifiedLayout(width);
279 }
280
281 layouts.add(layout);
282 offsets.add(lineBreaker.getPosition());
283 int lineDrop = (int) (layout.getAscent() + layout.getDescent());
284 if (lineSpacing >= 0) {
285 lineDrop += lineSpacing;
286 } else {
287 lineDrop += layout.getLeading();
288 }
289 start.setY(start.getY() + lineDrop);
290 positions.add(new Point(start));
291 }
292
293 // Convert the accumulated list into an internal-type array
294 List<TextLayout> ret = new LinkedList<TextLayout>();
295 for (int i = 0; i < layouts.size(); i++) {
296 java.awt.font.TextLayout swingLayout = layouts.get(i);
297 int startCharIndex = offsets.get(i);
298 int endCharIndex = offsets.get(i + 1) - 1;
299 String line = string.substring(startCharIndex, endCharIndex + 1);
300 TextLayout layout = TextLayout.get(line, font, startCharIndex, endCharIndex);
301 register(layout, swingLayout);
302 ret.add(layout);
303 }
304
305 return ret;
306
307 }
308
309 @Override
310 public int getStringWidth(Font font, String string)
311 {
312 SwingGraphicsManager g = SwingMiscManager.getIfUsingSwingGraphicsManager();
313 SwingFontManager f = SwingMiscManager.getIfUsingSwingFontManager();
314
315 if (g == null || f == null) {
316 return -1;
317 }
318
319 if (font != null) {
320 return g.getFontMetrics(f.getInternalFont(font)).stringWidth(string);
321 } else {
322 return g.getFontMetrics(f.getInternalFont(f.getDefaultFont())).stringWidth(string);
323 }
324
325 }
326
327}
Note: See TracBrowser for help on using the repository browser.