changeset 6856:73368266ee9f

RT-36636: [Accessibility] Add hit test and bounds support (common code + win), Add text attributes suppotr (win), fix some text navigation support but it still needs more work.
author Felipe Heidrich <felipe.heidrich@oracle.com>
date Tue, 22 Apr 2014 16:16:05 -0700
parents 03bdbccae5d8
children 54b08edc5557
files modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextAreaSkin.java modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextFieldSkin.java modules/controls/src/main/java/javafx/scene/control/TextArea.java modules/controls/src/main/java/javafx/scene/control/TextField.java modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java modules/graphics/src/main/java/com/sun/glass/ui/win/WinTextRangeProvider.java modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java modules/graphics/src/main/java/javafx/scene/text/Text.java modules/graphics/src/main/native-glass/win/GlassAccessible.cpp modules/graphics/src/main/native-glass/win/GlassTextRangeProvider.cpp
diffstat 10 files changed, 213 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextAreaSkin.java	Tue Apr 22 16:16:05 2014 -0700
@@ -1382,6 +1382,8 @@
             case LINE_FOR_OFFSET:
             case LINE_START:
             case LINE_END:
+            case BOUNDS_FOR_RANGE:
+            case OFFSET_AT_POINT:
                 Text text = getTextNode();
                 return text.accGetAttribute(attribute, parameters);
             default: return super.accGetAttribute(attribute, parameters);
--- a/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/controls/src/main/java/com/sun/javafx/scene/control/skin/TextFieldSkin.java	Tue Apr 22 16:16:05 2014 -0700
@@ -44,6 +44,7 @@
 import javafx.geometry.Rectangle2D;
 import javafx.scene.Group;
 import javafx.scene.Node;
+import javafx.scene.accessibility.Attribute;
 import javafx.scene.control.IndexRange;
 import javafx.scene.control.PasswordField;
 import javafx.scene.control.TextField;
@@ -797,4 +798,14 @@
             return txt;
         }
     }
+
+    @Override
+    protected Object accGetAttribute(Attribute attribute, Object... parameters) {
+        switch (attribute) {
+            case BOUNDS_FOR_RANGE:
+            case OFFSET_AT_POINT:
+                return textNode.accGetAttribute(attribute, parameters);
+            default: return super.accGetAttribute(attribute, parameters);
+        }
+    }
 }
--- a/modules/controls/src/main/java/javafx/scene/control/TextArea.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/controls/src/main/java/javafx/scene/control/TextArea.java	Tue Apr 22 16:16:05 2014 -0700
@@ -707,6 +707,8 @@
             case LINE_FOR_OFFSET: //Skin
             case LINE_START: //Skin
             case LINE_END:  //Skin
+            case BOUNDS_FOR_RANGE: //Skin
+            case OFFSET_AT_POINT: //Skin
             default: return super.accGetAttribute(attribute, parameters);
         }
     }
--- a/modules/controls/src/main/java/javafx/scene/control/TextField.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/controls/src/main/java/javafx/scene/control/TextField.java	Tue Apr 22 16:16:05 2014 -0700
@@ -344,6 +344,8 @@
     @Override public Object accGetAttribute(Attribute attribute, Object... parameters) {
         switch (attribute) {
             case ROLE: return Role.TEXT_FIELD;
+            case BOUNDS_FOR_RANGE: //Skin
+            case OFFSET_AT_POINT: //Skin
             default: return super.accGetAttribute(attribute, parameters);
         }
     }
--- a/modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/win/WinAccessible.java	Tue Apr 22 16:16:05 2014 -0700
@@ -1221,7 +1221,13 @@
 
     long RangeFromPoint(double x, double y) {
         if (isDisposed()) return 0;
-        return get_DocumentRange();
+        Integer offset = (Integer)getAttribute(OFFSET_AT_POINT, new Point2D(x, y));
+        if (offset != null) {
+            WinTextRangeProvider range = new WinTextRangeProvider(this);
+            range.setRange(offset, offset);
+            return range.getNativeProvider();
+        }
+        return 0;
     }
 
     long get_DocumentRange() {
--- a/modules/graphics/src/main/java/com/sun/glass/ui/win/WinTextRangeProvider.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/graphics/src/main/java/com/sun/glass/ui/win/WinTextRangeProvider.java	Tue Apr 22 16:16:05 2014 -0700
@@ -26,7 +26,10 @@
 package com.sun.glass.ui.win;
 
 import static javafx.scene.accessibility.Attribute.*;
+import javafx.geometry.Bounds;
 import javafx.scene.accessibility.Attribute;
+import javafx.scene.text.Font;
+import javafx.scene.text.FontWeight;
 
 /*
  * This class is the Java peer for GlassTextRangeProvider.
@@ -52,6 +55,14 @@
     private static final int TextUnit_Page = 5;
     private static final int TextUnit_Document = 6;
 
+    /* Text Attribute Identifiers */
+    private static final int UIA_FontNameAttributeId = 40005;
+    private static final int UIA_FontSizeAttributeId = 40006;
+    private static final int UIA_FontWeightAttributeId = 40007;
+    private static final int UIA_IsHiddenAttributeId = 40013;
+    private static final int UIA_IsItalicAttributeId = 40014;
+    private static final int UIA_IsReadOnlyAttributeId = 40015;
+
     private static int idCount = 1;
     private int id;
     private int start, end;
@@ -127,6 +138,7 @@
                 start = end = caretOffset;
                 break;
             }
+            case TextUnit_Format:
             case TextUnit_Word:
                 //TODO
                 break;
@@ -145,8 +157,7 @@
                 break;
             }
             case TextUnit_Document:
-            case TextUnit_Page:
-            case TextUnit_Format: {
+            case TextUnit_Page: {
                 String text = (String)getAttribute(TITLE);
                 if (text == null) return;
                 start = 0;
@@ -168,17 +179,77 @@
     }
 
     WinVariant GetAttributeValue(int attributeId) {
-        System.out.println("GetAttributeValue");
-        return null;
+//        System.out.println("+GetAttributeValue " + attributeId);
+        WinVariant variant = null;
+        switch (attributeId) {
+            case UIA_FontNameAttributeId: {
+                Font font = (Font)getAttribute(FONT);
+                if (font != null) {
+                    variant = new WinVariant();
+                    variant.vt = WinVariant.VT_BSTR;
+                    variant.bstrVal = font.getName();
+                }
+                break;
+            }
+            case UIA_FontSizeAttributeId: {
+                Font font = (Font)getAttribute(FONT);
+                if (font != null) {
+                    variant = new WinVariant();
+                    variant.vt = WinVariant.VT_R8;
+                    variant.dblVal = font.getSize();
+                }
+                break;
+            }
+            case UIA_FontWeightAttributeId: {
+                Font font = (Font)getAttribute(FONT);
+                if (font != null) {
+                    boolean bold = font.getStyle().toLowerCase().contains("bold");
+                    variant = new WinVariant();
+                    variant.vt = WinVariant.VT_I4;
+                    variant.lVal = bold ? FontWeight.BOLD.getWeight() : FontWeight.NORMAL.getWeight();
+                }
+                break;
+            }
+            case UIA_IsHiddenAttributeId:
+            case UIA_IsReadOnlyAttributeId:
+                variant = new WinVariant();
+                variant.vt = WinVariant.VT_BOOL;
+                variant.boolVal = false;
+                break;
+            case UIA_IsItalicAttributeId: {
+                Font font = (Font)getAttribute(FONT);
+                if (font != null) {
+                    boolean italic = font.getStyle().toLowerCase().contains("italic");
+                    variant = new WinVariant();
+                    variant.vt = WinVariant.VT_BOOL;
+                    variant.boolVal = italic;   
+                }
+                break;
+            }
+            default:
+//                System.out.println("GetAttributeValue " + attributeId + " Not implemented");
+        }
+        return variant;
     }
 
     double[] GetBoundingRectangles() {
-        System.out.println("GetBoundingRectangles");
+        Bounds[] bounds = (Bounds[])getAttribute(BOUNDS_FOR_RANGE, start, end);
+        if (bounds != null) {
+            double[] result = new double[bounds.length * 4];
+            int index = 0;
+            for (int i = 0; i < bounds.length; i++) {
+                Bounds b = bounds[i];
+                result[index++] = b.getMinX();
+                result[index++] = b.getMinY();
+                result[index++] = b.getWidth();
+                result[index++] = b.getHeight();
+            }
+            return result;
+        }
         return null;
     }
 
     long GetEnclosingElement() {
-        System.out.println("+GetEnclosingElement");
         return accessible.getNativeAccessible();
     }
 
@@ -186,13 +257,42 @@
         String text = (String)getAttribute(TITLE);
         if (text == null) return null;
         int endOffset = maxLength != -1 ? Math.min(end, start + maxLength) : end;
-        System.out.println("+GetText " + text.substring(start, endOffset));
+        System.out.println("+GetText [" + text.substring(start, endOffset)+"]");
         return text.substring(start, endOffset);
     }
 
     int Move(int unit, int count) {
-        System.out.println("Move");
-        return 0;
+        System.out.println("+Move " + unit + " " + count + " " + this);
+
+        int offset = start;
+        switch (unit) {
+            case TextUnit_Character: {
+                start = offset + count; //check range
+                end = start + 1;
+                break;
+            }
+            case TextUnit_Format:
+            case TextUnit_Word:
+                //TODO
+                break;
+            case TextUnit_Line:
+            case TextUnit_Paragraph: {
+                Integer lineIndex = (Integer)getAttribute(LINE_FOR_OFFSET, offset);
+                lineIndex += count;//check range;
+                Integer lineStart = (Integer)getAttribute(LINE_START, lineIndex);
+                if (lineStart == null) return 0;
+                Integer lineEnd = (Integer)getAttribute(LINE_START, lineIndex);
+                if (lineEnd == null) return 0;
+                start = lineStart;
+                end = lineEnd;
+                break;
+            }
+            case TextUnit_Document:
+            case TextUnit_Page: {
+               return 0;
+            }
+        }
+        return count;
     }
 
     int MoveEndpointByUnit(int endpoint, int unit, int count) {
@@ -201,7 +301,16 @@
     }
 
     void MoveEndpointByRange(int endpoint, WinTextRangeProvider targetRange, int targetEndpoint) {
-        System.out.println("MoveEndpointByRange");
+        int offset = targetEndpoint == TextPatternRangeEndpoint_Start ? targetRange.start : targetRange.end;
+        if (endpoint == TextPatternRangeEndpoint_Start) {
+            start = offset;
+        } else {
+            end = offset;
+        }
+        if (start > end) {
+            start = end = offset;
+        }
+        System.out.println("+MoveEndpointByRange");
     }
 
     void Select() {
@@ -221,8 +330,7 @@
     }
 
     long[] GetChildren() {
-        System.out.println("GetChildren");
-        return null;
+        return new long[0];
     }
 
 }
--- a/modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/graphics/src/main/java/javafx/scene/accessibility/Attribute.java	Tue Apr 22 16:16:05 2014 -0700
@@ -274,14 +274,14 @@
     /**
      * Returns the Node at the given point location.
      * Type: Node
-     * Parameters: Point
+     * Parameters: Point2D
      */
     NODE_AT_POINT("NodeAtPoint", Node.class),
 
     /**
      * Returns the char offset at the given point location.
      * Type: Integer
-     * Parameters: Point
+     * Parameters: Point2D
      */
     OFFSET_AT_POINT("OffsetAtPoint", Integer.class),
 
--- a/modules/graphics/src/main/java/javafx/scene/text/Text.java	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/graphics/src/main/java/javafx/scene/text/Text.java	Tue Apr 22 16:16:05 2014 -0700
@@ -50,6 +50,8 @@
 import javafx.scene.accessibility.Role;
 import javafx.scene.paint.Color;
 import javafx.scene.paint.Paint;
+import javafx.scene.shape.LineTo;
+import javafx.scene.shape.MoveTo;
 import javafx.scene.shape.PathElement;
 import javafx.scene.shape.Shape;
 import javafx.scene.shape.StrokeType;
@@ -1939,6 +1941,32 @@
                 }
                 return 0;
             }
+            case OFFSET_AT_POINT: {
+                Point2D point = (Point2D)parameters[0];
+                point = screenToLocal(point);
+                return impl_hitTestChar(point).getCharIndex();
+            }
+            case BOUNDS_FOR_RANGE: {
+                int start = (Integer)parameters[0];
+                int end = (Integer)parameters[1];
+                PathElement[] elements = impl_getRangeShape(start, end + 1);
+                /* Each bounds is defined by a MoveTo (top-left) followed by 
+                 * 4 LineTo (to top-right, bottom-right, bottom-left, back to top-left).
+                 */
+                Bounds[] bounds = new Bounds[elements.length / 5];
+                int index = 0;
+                for (int i = 0; i < bounds.length; i++) {
+                    MoveTo topLeft = (MoveTo)elements[index];
+                    LineTo topRight = (LineTo)elements[index+1];
+                    LineTo bottomRight = (LineTo)elements[index+2];
+                    BoundingBox b = new BoundingBox(topLeft.getX(), topLeft.getY(), 
+                                                    topRight.getX() - topLeft.getX(),
+                                                    bottomRight.getY() - topRight.getY());
+                    bounds[i] = localToScreen(b);
+                    index += 5;
+                }
+                return bounds;
+            }
             default: return super.accGetAttribute(attribute, parameters);
         }
     }
--- a/modules/graphics/src/main/native-glass/win/GlassAccessible.cpp	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/graphics/src/main/native-glass/win/GlassAccessible.cpp	Tue Apr 22 16:16:05 2014 -0700
@@ -623,19 +623,37 @@
 IFACEMETHODIMP GlassAccessible::RangeFromChild(IRawElementProviderSimple *childElement,  ITextRangeProvider **pRetVal)
 {
     if (pRetVal == NULL) return E_INVALIDARG;
-    IUnknown* ptr = NULL;
-    HRESULT hr = callLongMethod(mid_RangeFromChild, &ptr, (jlong)childElement);
+    JNIEnv* env = GetEnv();
+    jlong ptr = env->CallLongMethod(m_jAccessible, mid_RangeFromChild, (jlong)childElement);
+    if (CheckAndClearException(env)) return E_FAIL;
+
+    /* This code is intentionally commented.
+     * JavaFX returns a new ITextRangeProvider instance each time.
+     * The caller holds the only reference to this object.
+     */
+//    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
+//    if (iUnknown) iUnknown->AddRef();
+
     *pRetVal = reinterpret_cast<ITextRangeProvider*>(ptr);
-    return hr;
+    return S_OK;
 }
 
 IFACEMETHODIMP GlassAccessible::RangeFromPoint(UiaPoint point, ITextRangeProvider **pRetVal)
 {
     if (pRetVal == NULL) return E_INVALIDARG;
-    IUnknown* ptr = NULL;
-    HRESULT hr = callLongMethod(mid_RangeFromPoint, &ptr, point.x, point.y);
+    JNIEnv* env = GetEnv();
+    jlong ptr = env->CallLongMethod(m_jAccessible, mid_RangeFromPoint, point.x, point.y);
+    if (CheckAndClearException(env)) return E_FAIL;
+
+    /* This code is intentionally commented.
+     * JavaFX returns a new ITextRangeProvider instance each time.
+     * The caller holds the only reference to this object.
+     */
+//    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
+//    if (iUnknown) iUnknown->AddRef();
+
     *pRetVal = reinterpret_cast<ITextRangeProvider*>(ptr);
-    return hr;
+    return S_OK;
 }
 
 IFACEMETHODIMP GlassAccessible::get_DocumentRange(ITextRangeProvider **pRetVal)
--- a/modules/graphics/src/main/native-glass/win/GlassTextRangeProvider.cpp	Tue Apr 22 12:36:46 2014 -0700
+++ b/modules/graphics/src/main/native-glass/win/GlassTextRangeProvider.cpp	Tue Apr 22 16:16:05 2014 -0700
@@ -106,7 +106,8 @@
     if (CheckAndClearException(env)) return E_FAIL;
 
     /* This code is intentionally commented.
-     * The refcount of the a new object is one. Keep it one means JavaFX holds no reference to it.
+     * JavaFX returns a new ITextRangeProvider instance each time.
+     * The caller holds the only reference to this object.
      */
 //    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
 //    if (iUnknown) iUnknown->AddRef();
@@ -158,9 +159,12 @@
     jlong ptr = env->CallLongMethod(m_jTextRangeProvider, mid_FindAttribute, attributeId, jVal, backward);
     if (CheckAndClearException(env)) return E_FAIL;
 
-    /* AddRef the result */
-    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
-    if (iUnknown) iUnknown->AddRef();
+    /* This code is intentionally commented.
+     * JavaFX returns a new ITextRangeProvider instance each time.
+     * The caller holds the only reference to this object.
+     */
+//    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
+//    if (iUnknown) iUnknown->AddRef();
 
     *pRetVal = reinterpret_cast<ITextRangeProvider*>(ptr);
     return S_OK;
@@ -174,9 +178,12 @@
     jlong ptr = env->CallLongMethod(m_jTextRangeProvider, mid_FindText, jText, backward, ignoreCase);
     if (CheckAndClearException(env)) return E_FAIL;
 
-    /* AddRef the result */
-    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
-    if (iUnknown) iUnknown->AddRef();
+    /* This code is intentionally commented.
+     * JavaFX returns a new ITextRangeProvider instance each time.
+     * The caller holds the only reference to this object.
+     */
+//    ITextRangeProvider* iUnknown = reinterpret_cast<ITextRangeProvider*>(ptr);
+//    if (iUnknown) iUnknown->AddRef();
 
     *pRetVal = reinterpret_cast<ITextRangeProvider*>(ptr);
     return S_OK;
@@ -209,7 +216,7 @@
     IUnknown* iUnknown = reinterpret_cast<IUnknown*>(ptr);
     if (iUnknown) iUnknown->AddRef();
 
-    *pRetVal = reinterpret_cast<IRawElementProviderSimple*>(iUnknown);
+    *pRetVal = reinterpret_cast<IRawElementProviderSimple*>(ptr);
     return S_OK;
 }