1 /*
2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.scene.text;
27
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import javafx.beans.property.DoubleProperty;
32 import javafx.beans.property.ObjectProperty;
33 import javafx.geometry.HPos;
34 import javafx.geometry.Insets;
35 import javafx.geometry.NodeOrientation;
36 import javafx.geometry.Orientation;
37 import javafx.geometry.VPos;
38 import javafx.scene.AccessibleAttribute;
39 import javafx.scene.AccessibleRole;
40 import javafx.scene.Node;
41 import javafx.scene.layout.Pane;
42 import javafx.css.StyleableDoubleProperty;
43 import javafx.css.StyleableObjectProperty;
44 import javafx.css.CssMetaData;
45 import javafx.css.converter.EnumConverter;
46 import javafx.css.converter.SizeConverter;
47 import com.sun.javafx.geom.BaseBounds;
48 import com.sun.javafx.geom.Point2D;
49 import com.sun.javafx.geom.RectBounds;
50 import com.sun.javafx.scene.text.GlyphList;
51 import com.sun.javafx.scene.text.TextLayout;
52 import com.sun.javafx.scene.text.TextLayoutFactory;
53 import com.sun.javafx.scene.text.TextSpan;
54 import com.sun.javafx.tk.Toolkit;
55 import javafx.css.Styleable;
56 import javafx.css.StyleableProperty;
57
58 /**
59 * TextFlow is special layout designed to lay out rich text.
60 * It can be used to layout several {@link Text} nodes in a single text flow.
61 * The TextFlow uses the text and the font of each {@link Text} node inside of it
133 * TextFlow provides properties for setting the size range directly. These
134 * properties default to the sentinel value Region.USE_COMPUTED_SIZE, however the
135 * application may set them to other values as needed:
136 * <pre><code>
137 * <b>textflow.setMaxWidth(500);</b>
138 * </code></pre>
139 * Applications may restore the computed values by setting these properties back
140 * to Region.USE_COMPUTED_SIZE.
141 * <p>
142 * TextFlow does not clip its content by default, so it is possible that childrens'
143 * bounds may extend outside its own bounds if a child's pref size is larger than
144 * the space textflow has to allocate for it.</p>
145 *
146 * @since JavaFX 8.0
147 */
148 public class TextFlow extends Pane {
149
150 private TextLayout layout;
151 private boolean needsContent;
152 private boolean inLayout;
153
154 /**
155 * Creates an empty TextFlow layout.
156 */
157 public TextFlow() {
158 super();
159 effectiveNodeOrientationProperty().addListener(observable -> checkOrientation());
160 setAccessibleRole(AccessibleRole.TEXT);
161 }
162
163 /**
164 * Creates a TextFlow layout with the given children.
165 *
166 * @param children children.
167 */
168 public TextFlow(Node... children) {
169 this();
170 getChildren().addAll(children);
171 }
172
173 private void checkOrientation() {
174 NodeOrientation orientation = getEffectiveNodeOrientation();
175 boolean rtl = orientation == NodeOrientation.RIGHT_TO_LEFT;
176 int dir = rtl ? TextLayout.DIRECTION_RTL : TextLayout.DIRECTION_LTR;
177 TextLayout layout = getTextLayout();
178 if (layout.setDirection(dir)) {
179 requestLayout();
180 }
181 }
182
183 @Override
184 public boolean usesMirroring() {
185 return false;
186 }
187
188 @Override protected void setWidth(double value) {
189 if (value != getWidth()) {
190 TextLayout layout = getTextLayout();
191 Insets insets = getInsets();
192 double left = snapSpace(insets.getLeft());
193 double right = snapSpace(insets.getRight());
194 double width = Math.max(1, value - left - right);
195 layout.setWrapWidth((float)width);
196 super.setWidth(value);
197 }
198 }
199
200 @Override protected double computePrefWidth(double height) {
201 TextLayout layout = getTextLayout();
202 layout.setWrapWidth(0);
281 double baselineOffset = -run.getLineBounds().getMinY();
282
283 layoutInArea(child, left + location.x, top + location.y,
284 run.getWidth(), run.getHeight(),
285 baselineOffset, null, true, true,
286 HPos.CENTER, VPos.BASELINE);
287 }
288 }
289
290 List<Node> managed = getManagedChildren();
291 for (Node node: managed) {
292 if (node instanceof Text) {
293 Text text = (Text)node;
294 text.layoutSpan(runs);
295 BaseBounds spanBounds = text.getSpanBounds();
296 text.relocate(left + spanBounds.getMinX(),
297 top + spanBounds.getMinY());
298 }
299 }
300 inLayout = false;
301 }
302
303 private static class EmbeddedSpan implements TextSpan {
304 RectBounds bounds;
305 Node node;
306 public EmbeddedSpan(Node node, double baseline, double width, double height) {
307 this.node = node;
308 bounds = new RectBounds(0, (float)-baseline,
309 (float)width, (float)(height - baseline));
310 }
311
312 @Override public String getText() {
313 return "\uFFFC";
314 }
315
316 @Override public Object getFont() {
317 return null;
318 }
319
320 @Override public RectBounds getBounds() {
|
1 /*
2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package javafx.scene.text;
27
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.List;
31 import javafx.beans.property.DoubleProperty;
32 import javafx.beans.property.ObjectProperty;
33 import javafx.geometry.HPos;
34 import javafx.geometry.Insets;
35 import javafx.geometry.NodeOrientation;
36 import javafx.geometry.Orientation;
37 import javafx.geometry.VPos;
38 import javafx.scene.AccessibleAttribute;
39 import javafx.scene.AccessibleRole;
40 import javafx.scene.Node;
41 import javafx.scene.layout.Pane;
42 import javafx.scene.shape.PathElement;
43 import javafx.css.StyleableDoubleProperty;
44 import javafx.css.StyleableObjectProperty;
45 import javafx.css.CssMetaData;
46 import javafx.css.converter.EnumConverter;
47 import javafx.css.converter.SizeConverter;
48 import com.sun.javafx.geom.BaseBounds;
49 import com.sun.javafx.geom.Point2D;
50 import com.sun.javafx.geom.RectBounds;
51 import com.sun.javafx.scene.text.GlyphList;
52 import com.sun.javafx.scene.text.TextLayout;
53 import com.sun.javafx.scene.text.TextLayoutFactory;
54 import com.sun.javafx.scene.text.TextSpan;
55 import com.sun.javafx.tk.Toolkit;
56 import javafx.css.Styleable;
57 import javafx.css.StyleableProperty;
58
59 /**
60 * TextFlow is special layout designed to lay out rich text.
61 * It can be used to layout several {@link Text} nodes in a single text flow.
62 * The TextFlow uses the text and the font of each {@link Text} node inside of it
134 * TextFlow provides properties for setting the size range directly. These
135 * properties default to the sentinel value Region.USE_COMPUTED_SIZE, however the
136 * application may set them to other values as needed:
137 * <pre><code>
138 * <b>textflow.setMaxWidth(500);</b>
139 * </code></pre>
140 * Applications may restore the computed values by setting these properties back
141 * to Region.USE_COMPUTED_SIZE.
142 * <p>
143 * TextFlow does not clip its content by default, so it is possible that childrens'
144 * bounds may extend outside its own bounds if a child's pref size is larger than
145 * the space textflow has to allocate for it.</p>
146 *
147 * @since JavaFX 8.0
148 */
149 public class TextFlow extends Pane {
150
151 private TextLayout layout;
152 private boolean needsContent;
153 private boolean inLayout;
154 // private static final PathElement[] EMPTY_PATH_ELEMENT_ARRAY = new PathElement[0];
155
156 /**
157 * Creates an empty TextFlow layout.
158 */
159 public TextFlow() {
160 super();
161 effectiveNodeOrientationProperty().addListener(observable -> checkOrientation());
162 setAccessibleRole(AccessibleRole.TEXT);
163 }
164
165 /**
166 * Creates a TextFlow layout with the given children.
167 *
168 * @param children children.
169 */
170 public TextFlow(Node... children) {
171 this();
172 getChildren().addAll(children);
173 }
174
175 private void checkOrientation() {
176 NodeOrientation orientation = getEffectiveNodeOrientation();
177 boolean rtl = orientation == NodeOrientation.RIGHT_TO_LEFT;
178 int dir = rtl ? TextLayout.DIRECTION_RTL : TextLayout.DIRECTION_LTR;
179 TextLayout layout = getTextLayout();
180 if (layout.setDirection(dir)) {
181 requestLayout();
182 }
183 }
184
185 /**
186 * Maps local point to index in the content.
187 *
188 * @since 9
189 */
190 public final HitInfo hitTest(javafx.geometry.Point2D point) {
191 if (point != null) {
192 TextLayout layout = getTextLayout();
193 double x = point.getX()/* - getX()*/;
194 double y = point.getY()/* - getY()/* + getYRendering()*/;
195 TextLayout.Hit layoutHit = layout.getHitInfo((float)x, (float)y);
196 return new HitInfo(layoutHit.getCharIndex(), layoutHit.getInsertionIndex(),
197 layoutHit.isLeading(), null/*getText()*/);
198 } else {
199 return null;
200 }
201 }
202
203 /**
204 * Returns shape of caret in local coordinates.
205 *
206 * @since 9
207 */
208 public PathElement[] caretShape(int charIndex, boolean leading) {
209 return getTextLayout().getCaretShape(charIndex, leading, 0, 0);
210 }
211
212 /**
213 * Returns shape for the range of the text in local coordinates.
214 *
215 * @since 9
216 */
217 public final PathElement[] rangeShape(int start, int end) {
218 return getRange(start, end, TextLayout.TYPE_TEXT);
219 }
220
221 @Override
222 public boolean usesMirroring() {
223 return false;
224 }
225
226 @Override protected void setWidth(double value) {
227 if (value != getWidth()) {
228 TextLayout layout = getTextLayout();
229 Insets insets = getInsets();
230 double left = snapSpace(insets.getLeft());
231 double right = snapSpace(insets.getRight());
232 double width = Math.max(1, value - left - right);
233 layout.setWrapWidth((float)width);
234 super.setWidth(value);
235 }
236 }
237
238 @Override protected double computePrefWidth(double height) {
239 TextLayout layout = getTextLayout();
240 layout.setWrapWidth(0);
319 double baselineOffset = -run.getLineBounds().getMinY();
320
321 layoutInArea(child, left + location.x, top + location.y,
322 run.getWidth(), run.getHeight(),
323 baselineOffset, null, true, true,
324 HPos.CENTER, VPos.BASELINE);
325 }
326 }
327
328 List<Node> managed = getManagedChildren();
329 for (Node node: managed) {
330 if (node instanceof Text) {
331 Text text = (Text)node;
332 text.layoutSpan(runs);
333 BaseBounds spanBounds = text.getSpanBounds();
334 text.relocate(left + spanBounds.getMinX(),
335 top + spanBounds.getMinY());
336 }
337 }
338 inLayout = false;
339 }
340
341 private PathElement[] getRange(int start, int end, int type) {
342 TextLayout layout = getTextLayout();
343 // int length = textLayout().getCharCount();
344 // if (0 <= start && start < end && end <= length) {
345 return layout.getRange(start, end, type, 0, 0);
346 // }
347 // return EMPTY_PATH_ELEMENT_ARRAY;
348 }
349
350 private static class EmbeddedSpan implements TextSpan {
351 RectBounds bounds;
352 Node node;
353 public EmbeddedSpan(Node node, double baseline, double width, double height) {
354 this.node = node;
355 bounds = new RectBounds(0, (float)-baseline,
356 (float)width, (float)(height - baseline));
357 }
358
359 @Override public String getText() {
360 return "\uFFFC";
361 }
362
363 @Override public Object getFont() {
364 return null;
365 }
366
367 @Override public RectBounds getBounds() {
|