1 /*
2 * Copyright (c) 2010, 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 javafx.css.converter.BooleanConverter;
29 import javafx.css.converter.EnumConverter;
30 import javafx.css.converter.SizeConverter;
31 import com.sun.javafx.geom.BaseBounds;
32 import com.sun.javafx.geom.Path2D;
33 import com.sun.javafx.geom.RectBounds;
34 import com.sun.javafx.geom.TransformedShape;
35 import com.sun.javafx.geom.transform.BaseTransform;
36 import com.sun.javafx.scene.DirtyBits;
37 import com.sun.javafx.scene.text.*;
38 import com.sun.javafx.sg.prism.NGNode;
39 import com.sun.javafx.sg.prism.NGShape;
40 import com.sun.javafx.sg.prism.NGText;
41 import com.sun.javafx.tk.Toolkit;
42 import javafx.beans.DefaultProperty;
43 import javafx.beans.InvalidationListener;
44 import javafx.beans.binding.DoubleBinding;
45 import javafx.beans.binding.ObjectBinding;
46 import javafx.beans.property.*;
47 import javafx.css.*;
48 import javafx.geometry.*;
49 import javafx.scene.AccessibleAttribute;
50 import javafx.scene.AccessibleRole;
51 import javafx.scene.paint.Color;
52 import javafx.scene.paint.Paint;
53 import javafx.scene.shape.LineTo;
54 import javafx.scene.shape.MoveTo;
55 import javafx.scene.shape.PathElement;
56 import javafx.scene.shape.Shape;
57 import javafx.scene.shape.StrokeType;
370 textProperty().set(value);
371 }
372
373 public final String getText() {
374 return text == null ? "" : text.get();
375 }
376
377 private String getTextInternal() {
378 // this might return null in case of bound property
379 String localText = getText();
380 return localText == null ? "" : localText;
381 }
382
383 public final StringProperty textProperty() {
384 if (text == null) {
385 text = new StringPropertyBase("") {
386 @Override public Object getBean() { return Text.this; }
387 @Override public String getName() { return "text"; }
388 @Override public void invalidated() {
389 needsFullTextLayout();
390 setImpl_selectionStart(-1);
391 setImpl_selectionEnd(-1);
392 setImpl_caretPosition(-1);
393 setImpl_caretBias(true);
394
395 // MH: Functionality copied from store() method,
396 // which was removed.
397 // Wonder what should happen if text is bound
398 // and becomes null?
399 final String value = get();
400 if ((value == null) && !isBound()) {
401 set("");
402 }
403 notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
404 }
405 };
406 }
407 return text;
408 }
409
410 /**
411 * Defines the X coordinate of text origin.
412 *
413 * @defaultValue 0
759 * @treatAsPrivate implementation detail
760 * @deprecated This is an internal API that is not intended
761 * for use and will be removed in the next version
762 */
763 @Deprecated
764 @Override
765 protected final void impl_geomChanged() {
766 super.impl_geomChanged();
767 if (attributes != null) {
768 if (attributes.impl_caretBinding != null) {
769 attributes.impl_caretBinding.invalidate();
770 }
771 if (attributes.impl_selectionBinding != null) {
772 attributes.impl_selectionBinding.invalidate();
773 }
774 }
775 impl_markDirty(DirtyBits.NODE_GEOMETRY);
776 }
777
778 /**
779 * @treatAsPrivate implementation detail
780 * @deprecated This is an internal API that is not intended
781 * for use and will be removed in the next version
782 */
783 @Deprecated
784 public final PathElement[] getImpl_selectionShape() {
785 return impl_selectionShapeProperty().get();
786 }
787
788 /**
789 * Shape of selection in local coordinates.
790 *
791 * @treatAsPrivate implementation detail
792 * @deprecated This is an internal API that is not intended
793 * for use and will be removed in the next version
794 */
795 @Deprecated
796 public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() {
797 return getTextAttribute().impl_selectionShapeProperty();
798 }
799
800 /**
801 * @treatAsPrivate implementation detail
802 * @deprecated This is an internal API that is not intended
803 * for use and will be removed in the next version
804 */
805 @Deprecated
806 public final void setImpl_selectionStart(int value) {
807 if (value == -1 &&
808 (attributes == null || attributes.impl_selectionStart == null)) {
809 return;
810 }
811 impl_selectionStartProperty().set(value);
812 }
813
814 /**
815 * @treatAsPrivate implementation detail
816 * @deprecated This is an internal API that is not intended
817 * for use and will be removed in the next version
818 */
819 @Deprecated
820 public final int getImpl_selectionStart() {
821 if (attributes == null || attributes.impl_selectionStart == null) {
822 return DEFAULT_SELECTION_START;
823 }
824 return attributes.getImpl_selectionStart();
825 }
826
827 /**
828 * Selection start index in the content.
829 * set to {@code -1} to unset selection.
830 *
831 * @treatAsPrivate implementation detail
832 * @deprecated This is an internal API that is not intended
833 * for use and will be removed in the next version
834 */
835 @Deprecated
836 public final IntegerProperty impl_selectionStartProperty() {
837 return getTextAttribute().impl_selectionStartProperty();
838 }
839
840 /**
841 * @treatAsPrivate implementation detail
842 * @deprecated This is an internal API that is not intended
843 * for use and will be removed in the next version
844 */
845 @Deprecated
846 public final void setImpl_selectionEnd(int value) {
847 if (value == -1 &&
848 (attributes == null || attributes.impl_selectionEnd == null)) {
849 return;
850 }
851 impl_selectionEndProperty().set(value);
852 }
853
854 /**
855 * @treatAsPrivate implementation detail
856 * @deprecated This is an internal API that is not intended
857 * for use and will be removed in the next version
858 */
859 @Deprecated
860 public final int getImpl_selectionEnd() {
861 if (attributes == null || attributes.impl_selectionEnd == null) {
862 return DEFAULT_SELECTION_END;
863 }
864 return attributes.getImpl_selectionEnd();
865 }
866
867 /**
868 * Selection end index in the content.
869 * set to {@code -1} to unset selection.
870 *
871 * @treatAsPrivate implementation detail
872 * @deprecated This is an internal API that is not intended
873 * for use and will be removed in the next version
874 */
875 @Deprecated
876 public final IntegerProperty impl_selectionEndProperty() {
877 return getTextAttribute().impl_selectionEndProperty();
878 }
879
880 /**
881 * @treatAsPrivate implementation detail
882 * @deprecated This is an internal API that is not intended
883 * for use and will be removed in the next version
884 */
885 @Deprecated
886 public final ObjectProperty<Paint> impl_selectionFillProperty() {
887 return getTextAttribute().impl_selectionFillProperty();
888 }
889
890 /**
891 * @treatAsPrivate implementation detail
892 * @deprecated This is an internal API that is not intended
893 * for use and will be removed in the next version
894 */
895 @Deprecated
896 public final PathElement[] getImpl_caretShape() {
897 return impl_caretShapeProperty().get();
898 }
899
900 /**
901 * Shape of caret in local coordinates.
902 *
903 * @treatAsPrivate implementation detail
904 * @deprecated This is an internal API that is not intended
905 * for use and will be removed in the next version
906 */
907 @Deprecated
908 public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() {
909 return getTextAttribute().impl_caretShapeProperty();
910 }
911
912 /**
913 * @treatAsPrivate implementation detail
914 * @deprecated This is an internal API that is not intended
915 * for use and will be removed in the next version
916 */
917 @Deprecated
918 public final void setImpl_caretPosition(int value) {
919 if (value == -1 &&
920 (attributes == null || attributes.impl_caretPosition == null)) {
921 return;
922 }
923 impl_caretPositionProperty().set(value);
924 }
925
926 /**
927 * @treatAsPrivate implementation detail
928 * @deprecated This is an internal API that is not intended
929 * for use and will be removed in the next version
930 */
931 @Deprecated
932 public final int getImpl_caretPosition() {
933 if (attributes == null || attributes.impl_caretPosition == null) {
934 return DEFAULT_CARET_POSITION;
935 }
936 return attributes.getImpl_caretPosition();
937 }
938
939 /**
940 * caret index in the content.
941 * set to {@code -1} to unset caret.
942 *
943 * @treatAsPrivate implementation detail
944 * @deprecated This is an internal API that is not intended
945 * for use and will be removed in the next version
946 */
947 @Deprecated
948 public final IntegerProperty impl_caretPositionProperty() {
949 return getTextAttribute().impl_caretPositionProperty();
950 }
951
952 /**
953 * @treatAsPrivate implementation detail
954 * @deprecated This is an internal API that is not intended
955 * for use and will be removed in the next version
956 */
957 @Deprecated
958 public final void setImpl_caretBias(boolean value) {
959 if (value && (attributes == null || attributes.impl_caretBias == null)) {
960 return;
961 }
962 impl_caretBiasProperty().set(value);
963 }
964
965 /**
966 * @treatAsPrivate implementation detail
967 * @deprecated This is an internal API that is not intended
968 * for use and will be removed in the next version
969 */
970 @Deprecated
971 public final boolean isImpl_caretBias() {
972 if (attributes == null || attributes.impl_caretBias == null) {
973 return DEFAULT_CARET_BIAS;
974 }
975 return getTextAttribute().isImpl_caretBias();
976 }
977
978 /**
979 * caret bias in the content. true means a bias towards forward character
980 * (true=leading/false=trailing)
981 *
982 * @treatAsPrivate implementation detail
983 * @deprecated This is an internal API that is not intended
984 * for use and will be removed in the next version
985 */
986 @Deprecated
987 public final BooleanProperty impl_caretBiasProperty() {
988 return getTextAttribute().impl_caretBiasProperty();
989 }
990
991 /**
992 * Maps local point to index in the content.
993 *
994 * @treatAsPrivate implementation detail
995 * @deprecated This is an internal API that is not intended
996 * for use and will be removed in the next version
997 */
998 @Deprecated
999 public final HitInfo impl_hitTestChar(Point2D point) {
1000 if (point == null) return null;
1001 TextLayout layout = getTextLayout();
1002 double x = point.getX() - getX();
1003 double y = point.getY() - getY() + getYRendering();
1004 return layout.getHitInfo((float)x, (float)y);
1005 }
1006
1007 private PathElement[] getRange(int start, int end, int type) {
1008 int length = getTextInternal().length();
1009 if (0 <= start && start < end && end <= length) {
1010 TextLayout layout = getTextLayout();
1011 float x = (float)getX();
1012 float y = (float)getY() - getYRendering();
1013 return layout.getRange(start, end, type, x, y);
1014 }
1015 return EMPTY_PATH_ELEMENT_ARRAY;
1016 }
1017
1018 /**
1019 * Returns shape for the range of the text in local coordinates.
1020 *
1021 * @treatAsPrivate implementation detail
1022 * @deprecated This is an internal API that is not intended
1023 * for use and will be removed in the next version
1024 */
1025 @Deprecated
1026 public final PathElement[] impl_getRangeShape(int start, int end) {
1027 return getRange(start, end, TextLayout.TYPE_TEXT);
1028 }
1029
1030 /**
1031 * Returns shape for the underline in local coordinates.
1032 *
1033 * @treatAsPrivate implementation detail
1034 * @deprecated This is an internal API that is not intended
1035 * for use and will be removed in the next version
1036 */
1037 @Deprecated
1038 public final PathElement[] impl_getUnderlineShape(int start, int end) {
1039 return getRange(start, end, TextLayout.TYPE_UNDERLINE);
1040 }
1041
1042 /**
1043 * Shows/Hides on-screen keyboard if available (mobile platform)
1044 *
1045 * @treatAsPrivate implementation detail
1046 * @deprecated This is an internal API that is not intended
1047 * for use and will be removed in the next version
1048 */
1049 @Deprecated
1050 public final void impl_displaySoftwareKeyboard(boolean display) {
1051 }
1052
1053 private float getYAdjustment(BaseBounds bounds) {
1054 VPos origin = getTextOrigin();
1055 if (origin == null) origin = DEFAULT_TEXT_ORIGIN;
1056 switch (origin) {
1057 case TOP: return -bounds.getMinY();
1058 case BASELINE: return 0;
1460 }
1461 if (impl_isDirty(DirtyBits.TEXT_FONT)) {
1462 peer.setFont(getFontInternal());
1463 }
1464 if (impl_isDirty(DirtyBits.NODE_CONTENTS)) {
1465 peer.setGlyphs(getRuns());
1466 }
1467 if (impl_isDirty(DirtyBits.NODE_GEOMETRY)) {
1468 if (isSpan()) {
1469 BaseBounds spanBounds = getSpanBounds();
1470 peer.setLayoutLocation(spanBounds.getMinX(), spanBounds.getMinY());
1471 } else {
1472 float x = (float)getX();
1473 float y = (float)getY();
1474 float yadj = getYRendering();
1475 peer.setLayoutLocation(-x, yadj - y);
1476 }
1477 }
1478 if (impl_isDirty(DirtyBits.TEXT_SELECTION)) {
1479 Object fillObj = null;
1480 int start = getImpl_selectionStart();
1481 int end = getImpl_selectionEnd();
1482 int length = getTextInternal().length();
1483 if (0 <= start && start < end && end <= length) {
1484 Paint fill = impl_selectionFillProperty().get();
1485 fillObj = fill != null ? Toolkit.getPaintAccessor().getPlatformPaint(fill) : null;
1486 }
1487 peer.setSelection(start, end, fillObj);
1488 }
1489 }
1490
1491 /**
1492 * @treatAsPrivate implementation detail
1493 * @deprecated This is an internal API that is not intended
1494 * for use and will be removed in the next version
1495 */
1496 @Deprecated
1497 @Override
1498 public final void impl_updatePeer() {
1499 super.impl_updatePeer();
1500 updatePGText();
1501 }
1502
1503 /***************************************************************************
1504 * *
1674 * for the first line in the layout */
1675 BaseBounds bounds = getLogicalBounds();
1676 return -bounds.getMinY();
1677 }
1678 });}
1679 };
1680 }
1681 return baselineOffset.getReadOnlyProperty();
1682 }
1683
1684 @Deprecated
1685 private ObjectProperty<PathElement[]> impl_selectionShape;
1686 private ObjectBinding<PathElement[]> impl_selectionBinding;
1687
1688 @Deprecated
1689 public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() {
1690 if (impl_selectionShape == null) {
1691 impl_selectionBinding = new ObjectBinding<PathElement[]>() {
1692 {bind(impl_selectionStartProperty(), impl_selectionEndProperty());}
1693 @Override protected PathElement[] computeValue() {
1694 int start = getImpl_selectionStart();
1695 int end = getImpl_selectionEnd();
1696 return getRange(start, end, TextLayout.TYPE_TEXT);
1697 }
1698 };
1699 impl_selectionShape = new SimpleObjectProperty<PathElement[]>(Text.this, "impl_selectionShape");
1700 impl_selectionShape.bind(impl_selectionBinding);
1701 }
1702 return impl_selectionShape;
1703 }
1704
1705 private ObjectProperty<Paint> selectionFill;
1706
1707 @Deprecated
1708 public final ObjectProperty<Paint> impl_selectionFillProperty() {
1709 if (selectionFill == null) {
1710 selectionFill =
1711 new ObjectPropertyBase<Paint>(DEFAULT_SELECTION_FILL) {
1712 @Override public Object getBean() { return Text.this; }
1713 @Override public String getName() { return "impl_selectionFill"; }
1714 @Override protected void invalidated() {
1715 impl_markDirty(DirtyBits.TEXT_SELECTION);
1750 public final int getImpl_selectionEnd() {
1751 return impl_selectionEnd == null ? DEFAULT_SELECTION_END : impl_selectionEnd.get();
1752 }
1753
1754 @Deprecated
1755 public final IntegerProperty impl_selectionEndProperty() {
1756 if (impl_selectionEnd == null) {
1757 impl_selectionEnd =
1758 new IntegerPropertyBase(DEFAULT_SELECTION_END) {
1759 @Override public Object getBean() { return Text.this; }
1760 @Override public String getName() { return "impl_selectionEnd"; }
1761 @Override protected void invalidated() {
1762 impl_markDirty(DirtyBits.TEXT_SELECTION);
1763 notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTION_END);
1764 }
1765 };
1766 }
1767 return impl_selectionEnd;
1768 }
1769
1770 @Deprecated
1771 private ObjectProperty<PathElement[]> impl_caretShape;
1772 private ObjectBinding<PathElement[]> impl_caretBinding;
1773
1774 @Deprecated
1775 public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() {
1776 if (impl_caretShape == null) {
1777 impl_caretBinding = new ObjectBinding<PathElement[]>() {
1778 {bind(impl_caretPositionProperty(), impl_caretBiasProperty());}
1779 @Override protected PathElement[] computeValue() {
1780 int pos = getImpl_caretPosition();
1781 int length = getTextInternal().length();
1782 if (0 <= pos && pos <= length) {
1783 boolean bias = isImpl_caretBias();
1784 float x = (float)getX();
1785 float y = (float)getY() - getYRendering();
1786 TextLayout layout = getTextLayout();
1787 return layout.getCaretShape(pos, bias, x, y);
1788 }
1789 return EMPTY_PATH_ELEMENT_ARRAY;
1790 }
1880
1881 Paint stroke = getStroke();
1882 if (stroke != null) {
1883 sb.append(", stroke=").append(stroke);
1884 sb.append(", strokeWidth=").append(getStrokeWidth());
1885 }
1886
1887 return sb.append("]").toString();
1888 }
1889
1890 @Override
1891 public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
1892 switch (attribute) {
1893 case TEXT: {
1894 String accText = getAccessibleText();
1895 if (accText != null && !accText.isEmpty()) return accText;
1896 return getText();
1897 }
1898 case FONT: return getFont();
1899 case CARET_OFFSET: {
1900 int sel = getImpl_caretPosition();
1901 if (sel >= 0) return sel;
1902 return getText().length();
1903 }
1904 case SELECTION_START: {
1905 int sel = getImpl_selectionStart();
1906 if (sel >= 0) return sel;
1907 sel = getImpl_caretPosition();
1908 if (sel >= 0) return sel;
1909 return getText().length();
1910 }
1911 case SELECTION_END: {
1912 int sel = getImpl_selectionEnd();
1913 if (sel >= 0) return sel;
1914 sel = getImpl_caretPosition();
1915 if (sel >= 0) return sel;
1916 return getText().length();
1917 }
1918 case LINE_FOR_OFFSET: {
1919 int offset = (Integer)parameters[0];
1920 if (offset > getTextInternal().length()) return null;
1921 TextLine[] lines = getTextLayout().getLines();
1922 int lineIndex = 0;
1923 for (int i = 1; i < lines.length; i++) {
1924 TextLine line = lines[i];
1925 if (line.getStart() > offset) break;
1926 lineIndex++;
1927 }
1928 return lineIndex;
1929 }
1930 case LINE_START: {
1931 int lineIndex = (Integer)parameters[0];
1932 TextLine[] lines = getTextLayout().getLines();
1933 if (0 <= lineIndex && lineIndex < lines.length) {
1934 TextLine line = lines[lineIndex];
1935 return line.getStart();
1936 }
1937 return null;
1938 }
1939 case LINE_END: {
1940 int lineIndex = (Integer)parameters[0];
1941 TextLine[] lines = getTextLayout().getLines();
1942 if (0 <= lineIndex && lineIndex < lines.length) {
1943 TextLine line = lines[lineIndex];
1944 return line.getStart() + line.getLength();
1945 }
1946 return null;
1947 }
1948 case OFFSET_AT_POINT: {
1949 Point2D point = (Point2D)parameters[0];
1950 point = screenToLocal(point);
1951 return impl_hitTestChar(point).getCharIndex();
1952 }
1953 case BOUNDS_FOR_RANGE: {
1954 int start = (Integer)parameters[0];
1955 int end = (Integer)parameters[1];
1956 PathElement[] elements = impl_getRangeShape(start, end + 1);
1957 /* Each bounds is defined by a MoveTo (top-left) followed by
1958 * 4 LineTo (to top-right, bottom-right, bottom-left, back to top-left).
1959 */
1960 Bounds[] bounds = new Bounds[elements.length / 5];
1961 int index = 0;
1962 for (int i = 0; i < bounds.length; i++) {
1963 MoveTo topLeft = (MoveTo)elements[index];
1964 LineTo topRight = (LineTo)elements[index+1];
1965 LineTo bottomRight = (LineTo)elements[index+2];
1966 BoundingBox b = new BoundingBox(topLeft.getX(), topLeft.getY(),
1967 topRight.getX() - topLeft.getX(),
1968 bottomRight.getY() - topRight.getY());
1969 bounds[i] = localToScreen(b);
1970 index += 5;
1971 }
1972 return bounds;
1973 }
1974 default: return super.queryAccessibleAttribute(attribute, parameters);
1975 }
1976 }
|
1 /*
2 * Copyright (c) 2010, 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 javafx.css.converter.BooleanConverter;
29 import javafx.css.converter.EnumConverter;
30 import javafx.css.converter.SizeConverter;
31 import com.sun.javafx.geom.BaseBounds;
32 import com.sun.javafx.geom.Path2D;
33 import com.sun.javafx.geom.RectBounds;
34 import com.sun.javafx.geom.TransformedShape;
35 import com.sun.javafx.geom.transform.BaseTransform;
36 import com.sun.javafx.scene.DirtyBits;
37 import com.sun.javafx.scene.text.GlyphList;
38 import com.sun.javafx.scene.text.TextLayout;
39 import com.sun.javafx.scene.text.TextLayoutFactory;
40 import com.sun.javafx.scene.text.TextLine;
41 import com.sun.javafx.scene.text.TextSpan;
42 import com.sun.javafx.sg.prism.NGNode;
43 import com.sun.javafx.sg.prism.NGShape;
44 import com.sun.javafx.sg.prism.NGText;
45 import com.sun.javafx.tk.Toolkit;
46 import javafx.beans.DefaultProperty;
47 import javafx.beans.InvalidationListener;
48 import javafx.beans.binding.DoubleBinding;
49 import javafx.beans.binding.ObjectBinding;
50 import javafx.beans.property.*;
51 import javafx.css.*;
52 import javafx.geometry.*;
53 import javafx.scene.AccessibleAttribute;
54 import javafx.scene.AccessibleRole;
55 import javafx.scene.paint.Color;
56 import javafx.scene.paint.Paint;
57 import javafx.scene.shape.LineTo;
58 import javafx.scene.shape.MoveTo;
59 import javafx.scene.shape.PathElement;
60 import javafx.scene.shape.Shape;
61 import javafx.scene.shape.StrokeType;
374 textProperty().set(value);
375 }
376
377 public final String getText() {
378 return text == null ? "" : text.get();
379 }
380
381 private String getTextInternal() {
382 // this might return null in case of bound property
383 String localText = getText();
384 return localText == null ? "" : localText;
385 }
386
387 public final StringProperty textProperty() {
388 if (text == null) {
389 text = new StringPropertyBase("") {
390 @Override public Object getBean() { return Text.this; }
391 @Override public String getName() { return "text"; }
392 @Override public void invalidated() {
393 needsFullTextLayout();
394 setSelectionStart(-1);
395 setSelectionEnd(-1);
396 setCaretPosition(-1);
397 setCaretBias(true);
398
399 // MH: Functionality copied from store() method,
400 // which was removed.
401 // Wonder what should happen if text is bound
402 // and becomes null?
403 final String value = get();
404 if ((value == null) && !isBound()) {
405 set("");
406 }
407 notifyAccessibleAttributeChanged(AccessibleAttribute.TEXT);
408 }
409 };
410 }
411 return text;
412 }
413
414 /**
415 * Defines the X coordinate of text origin.
416 *
417 * @defaultValue 0
763 * @treatAsPrivate implementation detail
764 * @deprecated This is an internal API that is not intended
765 * for use and will be removed in the next version
766 */
767 @Deprecated
768 @Override
769 protected final void impl_geomChanged() {
770 super.impl_geomChanged();
771 if (attributes != null) {
772 if (attributes.impl_caretBinding != null) {
773 attributes.impl_caretBinding.invalidate();
774 }
775 if (attributes.impl_selectionBinding != null) {
776 attributes.impl_selectionBinding.invalidate();
777 }
778 }
779 impl_markDirty(DirtyBits.NODE_GEOMETRY);
780 }
781
782 /**
783 * Shape of selection in local coordinates.
784 *
785 * @since 9
786 */
787 public final PathElement[] getSelectionShape() {
788 return selectionShapeProperty().get();
789 }
790
791 public final ReadOnlyObjectProperty<PathElement[]> selectionShapeProperty() {
792 return getTextAttribute().impl_selectionShapeProperty();
793 }
794
795 /**
796 * Selection start index in the content.
797 * set to {@code -1} to unset selection.
798 *
799 * @since 9
800 */
801 public final void setSelectionStart(int value) {
802 if (value == -1 &&
803 (attributes == null || attributes.impl_selectionStart == null)) {
804 return;
805 }
806 selectionStartProperty().set(value);
807 }
808
809 public final int getSelectionStart() {
810 if (attributes == null || attributes.impl_selectionStart == null) {
811 return DEFAULT_SELECTION_START;
812 }
813 return attributes.getImpl_selectionStart();
814 }
815
816 public final IntegerProperty selectionStartProperty() {
817 return getTextAttribute().impl_selectionStartProperty();
818 }
819
820 /**
821 * Selection end index in the content.
822 * set to {@code -1} to unset selection.
823 *
824 * @since 9
825 */
826 public final void setSelectionEnd(int value) {
827 if (value == -1 &&
828 (attributes == null || attributes.impl_selectionEnd == null)) {
829 return;
830 }
831 selectionEndProperty().set(value);
832 }
833
834 public final int getSelectionEnd() {
835 if (attributes == null || attributes.impl_selectionEnd == null) {
836 return DEFAULT_SELECTION_END;
837 }
838 return attributes.getImpl_selectionEnd();
839 }
840
841 public final IntegerProperty selectionEndProperty() {
842 return getTextAttribute().impl_selectionEndProperty();
843 }
844
845 /**
846 * The fill color of selected text.
847 *
848 * @since 9
849 */
850 public final ObjectProperty<Paint> selectionFillProperty() {
851 return getTextAttribute().impl_selectionFillProperty();
852 }
853
854 public final void setSelectionFill(Paint paint) {
855 selectionFillProperty().set(paint);
856 }
857 public final Paint getSelectionFill() {
858 return selectionFillProperty().get();
859 }
860
861 /**
862 * Shape of caret in local coordinates.
863 *
864 * @since 9
865 */
866 public final PathElement[] getCaretShape() {
867 return caretShapeProperty().get();
868 }
869
870 public final ReadOnlyObjectProperty<PathElement[]> caretShapeProperty() {
871 return getTextAttribute().impl_caretShapeProperty();
872 }
873
874 /**
875 * caret index in the content.
876 * set to {@code -1} to unset caret.
877 *
878 * @since 9
879 */
880 public final void setCaretPosition(int value) {
881 if (value == -1 &&
882 (attributes == null || attributes.impl_caretPosition == null)) {
883 return;
884 }
885 caretPositionProperty().set(value);
886 }
887
888 public final int getCaretPosition() {
889 if (attributes == null || attributes.impl_caretPosition == null) {
890 return DEFAULT_CARET_POSITION;
891 }
892 return attributes.getImpl_caretPosition();
893 }
894
895 public final IntegerProperty caretPositionProperty() {
896 return getTextAttribute().impl_caretPositionProperty();
897 }
898
899 /**
900 * caret bias in the content. {@code true} means a bias towards the leading character edge.
901 * (true=leading/false=trailing)
902 *
903 * @since 9
904 */
905 public final void setCaretBias(boolean value) {
906 if (value && (attributes == null || attributes.impl_caretBias == null)) {
907 return;
908 }
909 caretBiasProperty().set(value);
910 }
911
912 public final boolean isCaretBias() {
913 if (attributes == null || attributes.impl_caretBias == null) {
914 return DEFAULT_CARET_BIAS;
915 }
916 return getTextAttribute().isImpl_caretBias();
917 }
918
919 public final BooleanProperty caretBiasProperty() {
920 return getTextAttribute().impl_caretBiasProperty();
921 }
922
923 /**
924 * Maps local point to index in the content.
925 *
926 * @since 9
927 */
928 public final HitInfo hitTest(Point2D point) {
929 if (point == null) return null;
930 TextLayout layout = getTextLayout();
931 double x = point.getX() - getX();
932 double y = point.getY() - getY() + getYRendering();
933 TextLayout.Hit layoutHit = layout.getHitInfo((float)x, (float)y);
934 return new HitInfo(layoutHit.getCharIndex(), layoutHit.getInsertionIndex(),
935 layoutHit.isLeading(), getText());
936 }
937
938 private PathElement[] getRange(int start, int end, int type) {
939 int length = getTextInternal().length();
940 if (0 <= start && start < end && end <= length) {
941 TextLayout layout = getTextLayout();
942 float x = (float)getX();
943 float y = (float)getY() - getYRendering();
944 return layout.getRange(start, end, type, x, y);
945 }
946 return EMPTY_PATH_ELEMENT_ARRAY;
947 }
948
949 /**
950 * Returns shape for the caret at given index and bias.
951 *
952 * @since 9
953 */
954 public final PathElement[] caretShape(int charIndex, boolean caretBias) {
955 if (0 <= charIndex && charIndex <= getTextInternal().length()) {
956 float x = (float)getX();
957 float y = (float)getY() - getYRendering();
958 return getTextLayout().getCaretShape(charIndex, caretBias, x, y);
959 } else {
960 return null;
961 }
962 }
963
964 /**
965 * Returns shape for the range of the text in local coordinates.
966 *
967 * @since 9
968 */
969 public final PathElement[] rangeShape(int start, int end) {
970 return getRange(start, end, TextLayout.TYPE_TEXT);
971 }
972
973 /**
974 * Returns shape for the underline in local coordinates.
975 *
976 * @since 9
977 */
978 public final PathElement[] underlineShape(int start, int end) {
979 return getRange(start, end, TextLayout.TYPE_UNDERLINE);
980 }
981
982 /**
983 * Shows/Hides on-screen keyboard if available (mobile platform)
984 *
985 * @treatAsPrivate implementation detail
986 * @deprecated This is an internal API that is not intended
987 * for use and will be removed in the next version
988 */
989 @Deprecated
990 public final void impl_displaySoftwareKeyboard(boolean display) {
991 }
992
993 private float getYAdjustment(BaseBounds bounds) {
994 VPos origin = getTextOrigin();
995 if (origin == null) origin = DEFAULT_TEXT_ORIGIN;
996 switch (origin) {
997 case TOP: return -bounds.getMinY();
998 case BASELINE: return 0;
1400 }
1401 if (impl_isDirty(DirtyBits.TEXT_FONT)) {
1402 peer.setFont(getFontInternal());
1403 }
1404 if (impl_isDirty(DirtyBits.NODE_CONTENTS)) {
1405 peer.setGlyphs(getRuns());
1406 }
1407 if (impl_isDirty(DirtyBits.NODE_GEOMETRY)) {
1408 if (isSpan()) {
1409 BaseBounds spanBounds = getSpanBounds();
1410 peer.setLayoutLocation(spanBounds.getMinX(), spanBounds.getMinY());
1411 } else {
1412 float x = (float)getX();
1413 float y = (float)getY();
1414 float yadj = getYRendering();
1415 peer.setLayoutLocation(-x, yadj - y);
1416 }
1417 }
1418 if (impl_isDirty(DirtyBits.TEXT_SELECTION)) {
1419 Object fillObj = null;
1420 int start = getSelectionStart();
1421 int end = getSelectionEnd();
1422 int length = getTextInternal().length();
1423 if (0 <= start && start < end && end <= length) {
1424 Paint fill = selectionFillProperty().get();
1425 fillObj = fill != null ? Toolkit.getPaintAccessor().getPlatformPaint(fill) : null;
1426 }
1427 peer.setSelection(start, end, fillObj);
1428 }
1429 }
1430
1431 /**
1432 * @treatAsPrivate implementation detail
1433 * @deprecated This is an internal API that is not intended
1434 * for use and will be removed in the next version
1435 */
1436 @Deprecated
1437 @Override
1438 public final void impl_updatePeer() {
1439 super.impl_updatePeer();
1440 updatePGText();
1441 }
1442
1443 /***************************************************************************
1444 * *
1614 * for the first line in the layout */
1615 BaseBounds bounds = getLogicalBounds();
1616 return -bounds.getMinY();
1617 }
1618 });}
1619 };
1620 }
1621 return baselineOffset.getReadOnlyProperty();
1622 }
1623
1624 @Deprecated
1625 private ObjectProperty<PathElement[]> impl_selectionShape;
1626 private ObjectBinding<PathElement[]> impl_selectionBinding;
1627
1628 @Deprecated
1629 public final ReadOnlyObjectProperty<PathElement[]> impl_selectionShapeProperty() {
1630 if (impl_selectionShape == null) {
1631 impl_selectionBinding = new ObjectBinding<PathElement[]>() {
1632 {bind(impl_selectionStartProperty(), impl_selectionEndProperty());}
1633 @Override protected PathElement[] computeValue() {
1634 int start = getSelectionStart();
1635 int end = getSelectionEnd();
1636 return getRange(start, end, TextLayout.TYPE_TEXT);
1637 }
1638 };
1639 impl_selectionShape = new SimpleObjectProperty<PathElement[]>(Text.this, "impl_selectionShape");
1640 impl_selectionShape.bind(impl_selectionBinding);
1641 }
1642 return impl_selectionShape;
1643 }
1644
1645 private ObjectProperty<Paint> selectionFill;
1646
1647 @Deprecated
1648 public final ObjectProperty<Paint> impl_selectionFillProperty() {
1649 if (selectionFill == null) {
1650 selectionFill =
1651 new ObjectPropertyBase<Paint>(DEFAULT_SELECTION_FILL) {
1652 @Override public Object getBean() { return Text.this; }
1653 @Override public String getName() { return "impl_selectionFill"; }
1654 @Override protected void invalidated() {
1655 impl_markDirty(DirtyBits.TEXT_SELECTION);
1690 public final int getImpl_selectionEnd() {
1691 return impl_selectionEnd == null ? DEFAULT_SELECTION_END : impl_selectionEnd.get();
1692 }
1693
1694 @Deprecated
1695 public final IntegerProperty impl_selectionEndProperty() {
1696 if (impl_selectionEnd == null) {
1697 impl_selectionEnd =
1698 new IntegerPropertyBase(DEFAULT_SELECTION_END) {
1699 @Override public Object getBean() { return Text.this; }
1700 @Override public String getName() { return "impl_selectionEnd"; }
1701 @Override protected void invalidated() {
1702 impl_markDirty(DirtyBits.TEXT_SELECTION);
1703 notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTION_END);
1704 }
1705 };
1706 }
1707 return impl_selectionEnd;
1708 }
1709
1710 private ObjectProperty<PathElement[]> impl_caretShape;
1711 private ObjectBinding<PathElement[]> impl_caretBinding;
1712
1713 @Deprecated
1714 public final ReadOnlyObjectProperty<PathElement[]> impl_caretShapeProperty() {
1715 if (impl_caretShape == null) {
1716 impl_caretBinding = new ObjectBinding<PathElement[]>() {
1717 {bind(impl_caretPositionProperty(), impl_caretBiasProperty());}
1718 @Override protected PathElement[] computeValue() {
1719 int pos = getImpl_caretPosition();
1720 int length = getTextInternal().length();
1721 if (0 <= pos && pos <= length) {
1722 boolean bias = isImpl_caretBias();
1723 float x = (float)getX();
1724 float y = (float)getY() - getYRendering();
1725 TextLayout layout = getTextLayout();
1726 return layout.getCaretShape(pos, bias, x, y);
1727 }
1728 return EMPTY_PATH_ELEMENT_ARRAY;
1729 }
1819
1820 Paint stroke = getStroke();
1821 if (stroke != null) {
1822 sb.append(", stroke=").append(stroke);
1823 sb.append(", strokeWidth=").append(getStrokeWidth());
1824 }
1825
1826 return sb.append("]").toString();
1827 }
1828
1829 @Override
1830 public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) {
1831 switch (attribute) {
1832 case TEXT: {
1833 String accText = getAccessibleText();
1834 if (accText != null && !accText.isEmpty()) return accText;
1835 return getText();
1836 }
1837 case FONT: return getFont();
1838 case CARET_OFFSET: {
1839 int sel = getCaretPosition();
1840 if (sel >= 0) return sel;
1841 return getText().length();
1842 }
1843 case SELECTION_START: {
1844 int sel = getSelectionStart();
1845 if (sel >= 0) return sel;
1846 sel = getCaretPosition();
1847 if (sel >= 0) return sel;
1848 return getText().length();
1849 }
1850 case SELECTION_END: {
1851 int sel = getSelectionEnd();
1852 if (sel >= 0) return sel;
1853 sel = getCaretPosition();
1854 if (sel >= 0) return sel;
1855 return getText().length();
1856 }
1857 case LINE_FOR_OFFSET: {
1858 int offset = (Integer)parameters[0];
1859 if (offset > getTextInternal().length()) return null;
1860 TextLine[] lines = getTextLayout().getLines();
1861 int lineIndex = 0;
1862 for (int i = 1; i < lines.length; i++) {
1863 TextLine line = lines[i];
1864 if (line.getStart() > offset) break;
1865 lineIndex++;
1866 }
1867 return lineIndex;
1868 }
1869 case LINE_START: {
1870 int lineIndex = (Integer)parameters[0];
1871 TextLine[] lines = getTextLayout().getLines();
1872 if (0 <= lineIndex && lineIndex < lines.length) {
1873 TextLine line = lines[lineIndex];
1874 return line.getStart();
1875 }
1876 return null;
1877 }
1878 case LINE_END: {
1879 int lineIndex = (Integer)parameters[0];
1880 TextLine[] lines = getTextLayout().getLines();
1881 if (0 <= lineIndex && lineIndex < lines.length) {
1882 TextLine line = lines[lineIndex];
1883 return line.getStart() + line.getLength();
1884 }
1885 return null;
1886 }
1887 case OFFSET_AT_POINT: {
1888 Point2D point = (Point2D)parameters[0];
1889 point = screenToLocal(point);
1890 return hitTest(point).getCharIndex();
1891 }
1892 case BOUNDS_FOR_RANGE: {
1893 int start = (Integer)parameters[0];
1894 int end = (Integer)parameters[1];
1895 PathElement[] elements = rangeShape(start, end + 1);
1896 /* Each bounds is defined by a MoveTo (top-left) followed by
1897 * 4 LineTo (to top-right, bottom-right, bottom-left, back to top-left).
1898 */
1899 Bounds[] bounds = new Bounds[elements.length / 5];
1900 int index = 0;
1901 for (int i = 0; i < bounds.length; i++) {
1902 MoveTo topLeft = (MoveTo)elements[index];
1903 LineTo topRight = (LineTo)elements[index+1];
1904 LineTo bottomRight = (LineTo)elements[index+2];
1905 BoundingBox b = new BoundingBox(topLeft.getX(), topLeft.getY(),
1906 topRight.getX() - topLeft.getX(),
1907 bottomRight.getY() - topRight.getY());
1908 bounds[i] = localToScreen(b);
1909 index += 5;
1910 }
1911 return bounds;
1912 }
1913 default: return super.queryAccessibleAttribute(attribute, parameters);
1914 }
1915 }
|