< prev index next >

modules/graphics/src/main/java/javafx/scene/text/TextFlow.java

Print this page


   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() {


< prev index next >