< prev index next >

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

Print this page

 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
 63  * plus its own width and text alignment to determine the location for each child.
 64  * A single {@link Text} node can span over several lines due to wrapping, and
 65  * the visual location of {@link Text} node can differ from the logical location
 66  * due to bidi reordering.
 67  *
 68  * <p>
 69  * Any Node, other than Text, will be treated as an embedded object in the
 70  * text layout. It will be inserted in the content using its preferred width,
 71  * height, and baseline offset.
 72  *
 73  * <p>
 74  * When a {@link Text} node is inside of a TextFlow, some of its properties are ignored.
 75  * For example, the x and y properties of the {@link Text} node are ignored since
 76  * the location of the node is determined by the parent. Likewise, the wrapping

366             return "\uFFFC";
367         }
368 
369         @Override public Object getFont() {
370             return null;
371         }
372 
373         @Override public RectBounds getBounds() {
374             return bounds;
375         }
376 
377         public Node getNode() {
378             return node;
379         }
380     }
381 
382     TextLayout getTextLayout() {
383         if (layout == null) {
384             TextLayoutFactory factory = Toolkit.getToolkit().getTextLayoutFactory();
385             layout = factory.createLayout();

386             needsContent = true;
387         }
388         if (needsContent) {
389             List<Node> children = getManagedChildren();
390             TextSpan[] spans = new TextSpan[children.size()];
391             for (int i = 0; i < spans.length; i++) {
392                 Node node = children.get(i);
393                 if (node instanceof Text) {
394                     spans[i] = ((Text)node).getTextSpan();
395                 } else {
396                     /* Creating a text span every time forces text layout
397                      * to run a full text analysis in the new content.
398                      */
399                     double baseline = node.getBaselineOffset();
400                     if (baseline == BASELINE_OFFSET_SAME_AS_HEIGHT) {
401                         baseline = node.getLayoutBounds().getHeight();
402                     }
403                     double width = computeChildPrefAreaWidth(node, null);
404                     double height = computeChildPrefAreaHeight(node, null);
405                     spans[i] = new EmbeddedSpan(node, baseline, width, height);

466 
467     public final DoubleProperty lineSpacingProperty() {
468         if (lineSpacing == null) {
469             lineSpacing =
470                 new StyleableDoubleProperty(0) {
471                 @Override public Object getBean() { return TextFlow.this; }
472                 @Override public String getName() { return "lineSpacing"; }
473                 @Override public CssMetaData<TextFlow, Number> getCssMetaData() {
474                     return StyleableProperties.LINE_SPACING;
475                 }
476                 @Override public void invalidated() {
477                     TextLayout layout = getTextLayout();
478                     if (layout.setLineSpacing((float)get())) {
479                         requestLayout();
480                     }
481                 }
482             };
483         }
484         return lineSpacing;
485     }

















































486 
487     @Override public final double getBaselineOffset() {
488         Insets insets = getInsets();
489         double top = snapSpaceY(insets.getTop());
490         return top - getTextLayout().getBounds().getMinY();
491     }
492 
493    /***************************************************************************
494     *                                                                         *
495     *                            Stylesheet Handling                          *
496     *                                                                         *
497     **************************************************************************/
498 
499     /*
500      * Super-lazy instantiation pattern from Bill Pugh.
501      */
502      private static class StyleableProperties {
503 
504          private static final
505              CssMetaData<TextFlow, TextAlignment> TEXT_ALIGNMENT =
506                  new CssMetaData<TextFlow,TextAlignment>("-fx-text-alignment",
507                  new EnumConverter<TextAlignment>(TextAlignment.class),
508                  TextAlignment.LEFT) {
509 
510             @Override public boolean isSettable(TextFlow node) {
511                 return node.textAlignment == null || !node.textAlignment.isBound();
512             }
513 
514             @Override public StyleableProperty<TextAlignment> getStyleableProperty(TextFlow node) {
515                 return (StyleableProperty<TextAlignment>)node.textAlignmentProperty();
516             }
517          };
518 
519          private static final
520              CssMetaData<TextFlow,Number> LINE_SPACING =
521                  new CssMetaData<TextFlow,Number>("-fx-line-spacing",
522                  SizeConverter.getInstance(), 0) {
523 
524             @Override public boolean isSettable(TextFlow node) {
525                 return node.lineSpacing == null || !node.lineSpacing.isBound();
526             }
527 
528             @Override public StyleableProperty<Number> getStyleableProperty(TextFlow node) {
529                 return (StyleableProperty<Number>)node.lineSpacingProperty();
530             }
531          };









532 
533      private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
534          static {






535             final List<CssMetaData<? extends Styleable, ?>> styleables =
536                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Pane.getClassCssMetaData());
537             styleables.add(TEXT_ALIGNMENT);
538             styleables.add(LINE_SPACING);

539             STYLEABLES = Collections.unmodifiableList(styleables);
540          }
541     }
542 
543     /**
544      * @return The CssMetaData associated with this class, which may include the
545      * CssMetaData of its superclasses.
546      */
547     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
548         return StyleableProperties.STYLEABLES;
549     }
550 
551     @Override
552     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
553         return getClassCssMetaData();
554     }
555 
556     /* The methods in this section are copied from Region due to package visibility restriction */
557     private static double snapSpace(double value, boolean snapToPixel) {
558         return snapToPixel ? Math.round(value) : value;
559     }
560 

 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.beans.property.IntegerProperty;
 57 import javafx.beans.property.IntegerPropertyBase;
 58 import javafx.css.Styleable;
 59 import javafx.css.StyleableIntegerProperty;
 60 import javafx.css.StyleableProperty;
 61 
 62 /**
 63  * TextFlow is special layout designed to lay out rich text.
 64  * It can be used to layout several {@link Text} nodes in a single text flow.
 65  * The TextFlow uses the text and the font of each {@link Text} node inside of it
 66  * plus its own width and text alignment to determine the location for each child.
 67  * A single {@link Text} node can span over several lines due to wrapping, and
 68  * the visual location of {@link Text} node can differ from the logical location
 69  * due to bidi reordering.
 70  *
 71  * <p>
 72  * Any Node, other than Text, will be treated as an embedded object in the
 73  * text layout. It will be inserted in the content using its preferred width,
 74  * height, and baseline offset.
 75  *
 76  * <p>
 77  * When a {@link Text} node is inside of a TextFlow, some of its properties are ignored.
 78  * For example, the x and y properties of the {@link Text} node are ignored since
 79  * the location of the node is determined by the parent. Likewise, the wrapping

369             return "\uFFFC";
370         }
371 
372         @Override public Object getFont() {
373             return null;
374         }
375 
376         @Override public RectBounds getBounds() {
377             return bounds;
378         }
379 
380         public Node getNode() {
381             return node;
382         }
383     }
384 
385     TextLayout getTextLayout() {
386         if (layout == null) {
387             TextLayoutFactory factory = Toolkit.getToolkit().getTextLayoutFactory();
388             layout = factory.createLayout();
389             layout.setTabSize(getTabSize());
390             needsContent = true;
391         }
392         if (needsContent) {
393             List<Node> children = getManagedChildren();
394             TextSpan[] spans = new TextSpan[children.size()];
395             for (int i = 0; i < spans.length; i++) {
396                 Node node = children.get(i);
397                 if (node instanceof Text) {
398                     spans[i] = ((Text)node).getTextSpan();
399                 } else {
400                     /* Creating a text span every time forces text layout
401                      * to run a full text analysis in the new content.
402                      */
403                     double baseline = node.getBaselineOffset();
404                     if (baseline == BASELINE_OFFSET_SAME_AS_HEIGHT) {
405                         baseline = node.getLayoutBounds().getHeight();
406                     }
407                     double width = computeChildPrefAreaWidth(node, null);
408                     double height = computeChildPrefAreaHeight(node, null);
409                     spans[i] = new EmbeddedSpan(node, baseline, width, height);

470 
471     public final DoubleProperty lineSpacingProperty() {
472         if (lineSpacing == null) {
473             lineSpacing =
474                 new StyleableDoubleProperty(0) {
475                 @Override public Object getBean() { return TextFlow.this; }
476                 @Override public String getName() { return "lineSpacing"; }
477                 @Override public CssMetaData<TextFlow, Number> getCssMetaData() {
478                     return StyleableProperties.LINE_SPACING;
479                 }
480                 @Override public void invalidated() {
481                     TextLayout layout = getTextLayout();
482                     if (layout.setLineSpacing((float)get())) {
483                         requestLayout();
484                     }
485                 }
486             };
487         }
488         return lineSpacing;
489     }
490     /**
491      * The size of a tab stop in spaces.
492      *
493      * @return the {@code tabSize} property
494      *
495      * @defaultValue {@code 8}
496      *
497      * @since 14
498      */
499     private IntegerProperty tabSize;
500 
501     /**
502      * Gets the size of a tab stop in spaces.
503      * @return the size of a tab in spaces
504      * @since 14
505      */
506     public final int getTabSize() {
507         return tabSize == null ? TextLayout.DEFAULT_TAB_SIZE : tabSize.get();
508     }
509 
510     /**
511      * Sets the size of a tab stop.
512      * @param spaces the size of a tab in spaces. Defaults to 8.
513      * Minimum is 1, lower values will be clamped to 1.
514      * @since 14
515     */
516     public final void setTabSize(int spaces) {
517         tabSizeProperty().set(spaces);
518     }
519 
520     final IntegerProperty tabSizeProperty() {
521         if (tabSize == null) {
522             tabSize = new StyleableIntegerProperty(TextLayout.DEFAULT_TAB_SIZE) {
523                 @Override public Object getBean() { return TextFlow.this; }
524                 @Override public String getName() { return "tabSize"; }
525                 @Override public CssMetaData getCssMetaData() {
526                     return StyleableProperties.TAB_SIZE;
527                 }
528                 @Override public void set(int v) { super.set((v < 1) ? 1 : v); }
529                 @Override protected void invalidated() {
530                     TextLayout layout = getTextLayout();
531                     if (layout.setTabSize(get())) {
532                         requestLayout();
533                     }
534                 }
535             };
536         }
537         return tabSize;
538     }
539 
540     @Override public final double getBaselineOffset() {
541         Insets insets = getInsets();
542         double top = snapSpaceY(insets.getTop());
543         return top - getTextLayout().getBounds().getMinY();
544     }
545 
546    /***************************************************************************
547     *                                                                         *
548     *                            Stylesheet Handling                          *
549     *                                                                         *
550     **************************************************************************/
551 
552     /*
553      * Super-lazy instantiation pattern from Bill Pugh.
554      */
555     private static class StyleableProperties {
556 
557         private static final
558             CssMetaData<TextFlow, TextAlignment> TEXT_ALIGNMENT =
559                 new CssMetaData<TextFlow,TextAlignment>("-fx-text-alignment",
560                 new EnumConverter<TextAlignment>(TextAlignment.class),
561                 TextAlignment.LEFT) {
562 
563             @Override public boolean isSettable(TextFlow node) {
564                 return node.textAlignment == null || !node.textAlignment.isBound();
565             }
566 
567             @Override public StyleableProperty<TextAlignment> getStyleableProperty(TextFlow node) {
568                 return (StyleableProperty<TextAlignment>)node.textAlignmentProperty();
569             }
570         };
571 
572         private static final
573             CssMetaData<TextFlow,Number> LINE_SPACING =
574                 new CssMetaData<TextFlow,Number>("-fx-line-spacing",
575                 SizeConverter.getInstance(), 0) {
576 
577             @Override public boolean isSettable(TextFlow node) {
578                 return node.lineSpacing == null || !node.lineSpacing.isBound();
579             }
580 
581             @Override public StyleableProperty<Number> getStyleableProperty(TextFlow node) {
582                 return (StyleableProperty<Number>)node.lineSpacingProperty();
583             }
584         };
585 
586         private static final CssMetaData<TextFlow,Number> TAB_SIZE =
587                 new CssMetaData<TextFlow,Number>("-fx-tab-size",
588                 SizeConverter.getInstance(), TextLayout.DEFAULT_TAB_SIZE) {
589 
590             @Override
591             public boolean isSettable(TextFlow node) {
592                 return node.tabSize == null || !node.tabSize.isBound();
593             }
594 
595             @Override
596             public StyleableProperty<Number> getStyleableProperty(TextFlow node) {
597                 return (StyleableProperty<Number>)node.tabSizeProperty();
598             }
599         };
600 
601         private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
602         static {
603             final List<CssMetaData<? extends Styleable, ?>> styleables =
604                 new ArrayList<CssMetaData<? extends Styleable, ?>>(Pane.getClassCssMetaData());
605             styleables.add(TEXT_ALIGNMENT);
606             styleables.add(LINE_SPACING);
607             styleables.add(TAB_SIZE);
608             STYLEABLES = Collections.unmodifiableList(styleables);
609         }
610     }
611 
612     /**
613      * @return The CssMetaData associated with this class, which may include the
614      * CssMetaData of its superclasses.
615      */
616     public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
617         return StyleableProperties.STYLEABLES;
618     }
619 
620     @Override
621     public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() {
622         return getClassCssMetaData();
623     }
624 
625     /* The methods in this section are copied from Region due to package visibility restriction */
626     private static double snapSpace(double value, boolean snapToPixel) {
627         return snapToPixel ? Math.round(value) : value;
628     }
629 
< prev index next >