Javafx: FXML tag name completion fix - don't suggest type parameter of raw collection. Cache property types when computing completion list. Test added, similar tests simplified. (IDEA-153663)

This commit is contained in:
Pavel Dolgov
2016-03-29 13:42:44 +03:00
parent 678ec7a127
commit 40d8250dbd
5 changed files with 172 additions and 114 deletions
@@ -133,57 +133,48 @@ public class JavaFxCompletionTest extends LightFixtureCompletionTestCase {
}
public void testPrimitiveSubtags() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertDoesntContain(myFixture.getLookupElementStrings(), "geomBoundsInvalid");
}
public void testDefaultPropertyWrappedField() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertContainsElements(myFixture.getLookupElementStrings(), "image", "Image");
}
public void testInfinity() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertContainsElements(myFixture.getLookupElementStrings(), "Infinity", "-Infinity", "NaN", "-NaN");
}
public void testNoInfinity() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertDoesntContain(myFixture.getLookupElementStrings(), "Infinity");
}
public void testBooleanValues() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertContainsElements(myFixture.getLookupElementStrings(), "true", "false");
}
public void testBooleanValuesNonStatic() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertContainsElements(myFixture.getLookupElementStrings(), "true", "false");
}
public void testPropertyNameWithoutField() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertContainsElements(myFixture.getLookupElementStrings(), "disable");
}
public void testPropertyTagSubclass() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertContainsElements(myFixture.getLookupElementStrings(), "Color", "ImagePattern", "LinearGradient", "RadialGradient");
assertDoesntContain(myFixture.getLookupElementStrings(), "Paint");
}
public void testSubclassesAndDefaultProperty() throws Exception {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
final List<String> lookupElementStrings = myFixture.getLookupElementStrings();
assertNotNull(lookupElementStrings);
final String buttonVariant = "Button";
@@ -221,6 +212,11 @@ public class JavaFxCompletionTest extends LightFixtureCompletionTestCase {
doTest("ColumnConstraints");
}
public void testRawCollectionItem() throws Exception {
configureAndComplete();
assertDoesntContain(myFixture.getLookupElementStrings(), "T", "Object", "java.lang.Object");
}
public void testFxIdExactOptionsLabel() throws Exception {
configureAndComplete("FxIdExactOptionsController.java", "FxIdExactOptionsModel.java");
assertSameElements(myFixture.getLookupElementStrings(), "parentPrivateLabel", "parentPublicLabel", "privateLabel", "publicLabel", "parentControl", "control", "grandLabel");
@@ -260,16 +256,21 @@ public class JavaFxCompletionTest extends LightFixtureCompletionTestCase {
}
private void doOrderTest(String... expected) {
myFixture.configureByFiles(getTestName(true) + ".fxml");
complete();
configureAndComplete();
assertOrderedEquals(myFixture.getLookupElementStrings(), expected);
}
private void configureAndComplete(final String... extraFiles) {
final List<String> files = new ArrayList<>();
files.add(getTestName(true) + ".fxml");
Collections.addAll(files, extraFiles);
myFixture.configureByFiles(ArrayUtil.toStringArray(files));
final String fxmlFileName = getTestName(true) + ".fxml";
if (extraFiles.length != 0) {
final List<String> files = new ArrayList<>();
files.add(fxmlFileName);
Collections.addAll(files, extraFiles);
myFixture.configureByFiles(ArrayUtil.toStringArray(files));
}
else {
myFixture.configureByFiles(fxmlFileName);
}
complete();
}
@@ -43,6 +43,7 @@ public class JavaFxCommonNames {
@NonNls public static final String JAVAFX_FXML_BUILDER = "javafx.util.Builder";
@NonNls public static final String JAVAFX_BEANS_OBSERVABLE = "javafx.beans.Observable";
@NonNls public static final String VALUE_OF = "valueOf";
@NonNls public static final String GET_VALUE = "getValue";
@NonNls public static final String VALUE = "value";
@NonNls public static final String JAVAFX_FXML_FXMLLOADER = "javafx.fxml.FXMLLoader";
@NonNls public static final String JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE = "javafx.beans.value.ObservableValue";
@@ -41,7 +41,8 @@ import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.javaFX.fxml.descriptors.*;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxClassTagDescriptorBase;
import org.jetbrains.plugins.javaFX.fxml.descriptors.JavaFxPropertyTagDescriptor;
import java.util.*;
import java.util.function.BiConsumer;
@@ -71,10 +72,10 @@ public class JavaFxPsiUtil {
}
private static List<String> parseInstructions(XmlFile file, String instructionName) {
List<String> definedImports = new ArrayList<String>();
XmlDocument document = file.getDocument();
final List<String> definedImports = new ArrayList<String>();
final XmlDocument document = file.getDocument();
if (document != null) {
XmlProlog prolog = document.getProlog();
final XmlProlog prolog = document.getProlog();
final Collection<XmlProcessingInstruction>
instructions = new ArrayList<XmlProcessingInstruction>(PsiTreeUtil.findChildrenOfType(prolog, XmlProcessingInstruction.class));
@@ -131,7 +132,7 @@ public class JavaFxPsiUtil {
public static void insertImportWhenNeeded(XmlFile xmlFile,
String shortName,
String qualifiedName) {
if (shortName != null && findPsiClass(shortName, xmlFile.getRootTag()) == null) {
if (shortName != null && qualifiedName != null && findPsiClass(shortName, xmlFile.getRootTag()) == null) {
final XmlDocument document = xmlFile.getDocument();
if (document != null) {
final XmlProcessingInstruction processingInstruction = createSingleImportInstruction(qualifiedName, xmlFile.getProject());
@@ -336,21 +337,23 @@ public class JavaFxPsiUtil {
@Nullable
public static PsiMethod findValueOfMethod(@NotNull final PsiClass psiClass) {
final PsiMethod[] methods = psiClass.findMethodsByName(JavaFxCommonNames.VALUE_OF, true);
for (PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC)) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length == 1) {
final PsiType type = parameters[0].getType();
if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING) || type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
if (psiClass.equals(PsiUtil.resolveClassInType(method.getReturnType()))) {
return method;
return CachedValuesManager.getCachedValue(psiClass, () -> {
final PsiMethod[] methods = psiClass.findMethodsByName(JavaFxCommonNames.VALUE_OF, true);
for (PsiMethod method : methods) {
if (method.hasModifierProperty(PsiModifier.STATIC)) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length == 1) {
final PsiType type = parameters[0].getType();
if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING) || type.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) {
if (psiClass.equals(PsiUtil.resolveClassInType(method.getReturnType()))) {
return CachedValueProvider.Result.create(method, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
}
}
}
}
}
return null;
return CachedValueProvider.Result.create(null, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
}
public static boolean isReadOnly(String attributeName, XmlTag tag) {
@@ -387,19 +390,22 @@ public class JavaFxPsiUtil {
@Nullable
private static PsiType getDefaultPropertyExpectedType(@Nullable PsiClass aClass) {
if (aClass == null) return null;
final PsiAnnotation annotation =
AnnotationUtil.findAnnotationInHierarchy(aClass, Collections.singleton(JavaFxCommonNames.JAVAFX_BEANS_DEFAULT_PROPERTY));
if (annotation != null) {
final PsiAnnotationMemberValue memberValue = annotation.findAttributeValue(null);
if (memberValue != null) {
final String propertyName = StringUtil.unquoteString(memberValue.getText());
final PsiMethod getter = findPropertyGetter(propertyName, aClass);
if (getter != null) {
return getter.getReturnType();
return CachedValuesManager.getCachedValue(aClass, () -> {
final PsiAnnotation annotation =
AnnotationUtil.findAnnotationInHierarchy(aClass, Collections.singleton(JavaFxCommonNames.JAVAFX_BEANS_DEFAULT_PROPERTY));
if (annotation != null) {
final PsiAnnotationMemberValue memberValue = annotation.findAttributeValue(null);
if (memberValue != null) {
final String propertyName = StringUtil.unquoteString(memberValue.getText());
final PsiMethod getter = findPropertyGetter(propertyName, aClass);
if (getter != null) {
final PsiType propertyType = eraseFreeTypeParameters(getter.getReturnType());
return CachedValueProvider.Result.create(propertyType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
}
}
}
return null;
return CachedValueProvider.Result.create(null, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
}
public static String getDefaultPropertyName(@Nullable PsiClass aClass) {
@@ -424,10 +430,25 @@ public class JavaFxPsiUtil {
}
public static boolean isAbleToInstantiate(@NotNull PsiClass psiClass, @NotNull Consumer<String> messageConsumer) {
if (psiClass.getConstructors().length > 0) {
if (psiClass.getConstructors().length == 0) return true;
if (hasNamedArgOrNoArgConstructor(psiClass)) return true;
final PsiMethod valueOf = findValueOfMethod(psiClass);
if (valueOf == null) {
if (!hasBuilder(psiClass)) {
messageConsumer.accept("Unable to instantiate");
return false;
}
}
return true;
}
private static boolean hasNamedArgOrNoArgConstructor(@NotNull PsiClass psiClass) {
return CachedValuesManager.getCachedValue(psiClass, () -> {
for (PsiMethod constructor : psiClass.getConstructors()) {
final PsiParameter[] parameters = constructor.getParameterList().getParameters();
if (parameters.length == 0) return true;
if (parameters.length == 0) {
return CachedValueProvider.Result.create(true, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
boolean annotated = true;
for (PsiParameter parameter : parameters) {
if (!AnnotationUtil.isAnnotated(parameter, JavaFxCommonNames.JAVAFX_BEANS_NAMED_ARG, false)) {
@@ -435,25 +456,18 @@ public class JavaFxPsiUtil {
break;
}
}
if (annotated) return true;
if (annotated) return CachedValueProvider.Result.create(true, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
final PsiMethod valueOf = findValueOfMethod(psiClass);
if (valueOf == null) {
if (!hasBuilder(psiClass)) {
messageConsumer.accept("Unable to instantiate");
return false;
}
}
}
return true;
return CachedValueProvider.Result.create(false, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
}
public static boolean hasBuilder(@NotNull final PsiClass psiClass) {
final Project project = psiClass.getProject();
return CachedValuesManager.getCachedValue(psiClass, new CachedValueProvider<Boolean>() {
@Nullable
@Override
public Result<Boolean> compute() {
final Project project = psiClass.getProject();
final PsiClass builderClass = JavaPsiFacade.getInstance(project).findClass(JavaFxCommonNames.JAVAFX_FXML_BUILDER,
GlobalSearchScope.allScope(project));
if (builderClass != null) {
@@ -524,7 +538,6 @@ public class JavaFxPsiUtil {
private static boolean canCoerce(@Nullable PsiType targetType, @NotNull PsiClass fromClass, @NotNull PsiElement context,
@NotNull BiConsumer<? super String, Validator.ValidationHost.ErrorType> messageConsumer) {
if (targetType == null) return true;
targetType = eraseFreeTypeParameters(targetType);
PsiType collectionItemType = JavaGenericsUtil.getCollectionItemType(targetType, fromClass.getResolveScope());
if (collectionItemType == null && InheritanceUtil.isInheritor(targetType, JavaFxCommonNames.JAVAFX_BEANS_PROPERTY)) {
collectionItemType = getWritablePropertyType(targetType, fromClass.getProject());
@@ -538,8 +551,9 @@ public class JavaFxPsiUtil {
/**
* Similar to {@link GenericsUtil#getVariableTypeByExpressionType(PsiType)} and {@link TypeConversionUtil#erasure(PsiType)}
*/
@NotNull
public static PsiType eraseFreeTypeParameters(@NotNull PsiType psiType) {
@Nullable
private static PsiType eraseFreeTypeParameters(@Nullable PsiType psiType) {
if (psiType == null) return null;
return psiType.accept(new PsiTypeVisitor<PsiType>() {
@Nullable
@Override
@@ -666,36 +680,38 @@ public class JavaFxPsiUtil {
}
public static PsiType getWrappedPropertyType(final PsiField field, final Project project, final Map<String, PsiType> typeMap) {
return CachedValuesManager.getCachedValue(field, new CachedValueProvider<PsiType>() {
@Nullable
@Override
public Result<PsiType> compute() {
final PsiType fieldType = field.getType();
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(fieldType);
final PsiClass fieldClass = resolveResult.getElement();
if (fieldClass == null) return Result.create(fieldType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
PsiType substitute = null;
for (String typeName : typeMap.keySet()) {
if (InheritanceUtil.isInheritor(fieldType, typeName)) {
substitute = typeMap.get(typeName);
break;
}
}
if (substitute == null) {
if (!InheritanceUtil.isInheritor(fieldType, JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE)) {
return Result.create(fieldType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
final PsiClass aClass = JavaPsiFacade.getInstance(project)
.findClass(JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE, GlobalSearchScope.allScope(project));
LOG.assertTrue(aClass != null);
final PsiSubstitutor substitutor =
TypeConversionUtil.getSuperClassSubstitutor(aClass, fieldClass, resolveResult.getSubstitutor());
final PsiMethod[] values = aClass.findMethodsByName("getValue", false);
substitute = substitutor.substitute(values[0].getReturnType());
}
return Result.create(substitute, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
return CachedValuesManager.getCachedValue(field, () -> {
final PsiType fieldType = field.getType();
final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(fieldType);
final PsiClass fieldClass = resolveResult.getElement();
if (fieldClass == null) {
final PsiType propertyType = eraseFreeTypeParameters(fieldType);
return CachedValueProvider.Result.create(propertyType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
PsiType substitute = null;
for (String typeName : typeMap.keySet()) {
if (InheritanceUtil.isInheritor(fieldType, typeName)) {
substitute = typeMap.get(typeName);
break;
}
}
if (substitute == null) {
if (!InheritanceUtil.isInheritor(fieldType, JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE)) {
final PsiType propertyType = eraseFreeTypeParameters(fieldType);
return CachedValueProvider.Result.create(propertyType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
final PsiClass aClass = JavaPsiFacade.getInstance(project)
.findClass(JavaFxCommonNames.JAVAFX_BEANS_VALUE_OBSERVABLE_VALUE, GlobalSearchScope.allScope(project));
LOG.assertTrue(aClass != null);
final PsiSubstitutor substitutor =
TypeConversionUtil.getSuperClassSubstitutor(aClass, fieldClass, resolveResult.getSubstitutor());
final PsiMethod[] values = aClass.findMethodsByName(JavaFxCommonNames.GET_VALUE, false);
LOG.assertTrue(values.length == 1);
substitute = substitutor.substitute(values[0].getReturnType());
}
final PsiType propertyType = eraseFreeTypeParameters(substitute);
return CachedValueProvider.Result.create(propertyType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
}
@@ -712,25 +728,35 @@ public class JavaFxPsiUtil {
final String propertyName = PropertyUtil.getPropertyName(method);
final PsiClass psiClass = containingClass != null ? containingClass : method.getContainingClass();
if (propertyName != null && containingClass != null) {
PsiMethod setter = findInstancePropertySetter(psiClass, propertyName);
final PsiMethod setter = findInstancePropertySetter(psiClass, propertyName);
if (setter != null) {
final PsiType setterArgumentType = getSetterArgumentType(setter);
if (setterArgumentType != null) return setterArgumentType;
}
}
return method.getReturnType();
return getGetterReturnType(method);
}
return null;
}
@Nullable
private static PsiType getSetterArgumentType(@NotNull PsiMethod method) {
final PsiParameter[] parameters = method.getParameterList().getParameters();
final boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
if (isStatic && parameters.length == 2 || !isStatic && parameters.length == 1) {
return parameters[parameters.length - 1].getType();
}
return null;
return CachedValuesManager.getCachedValue(method, () -> {
final PsiParameter[] parameters = method.getParameterList().getParameters();
final boolean isStatic = method.hasModifierProperty(PsiModifier.STATIC);
if (isStatic && parameters.length == 2 || !isStatic && parameters.length == 1) {
final PsiType argumentType = eraseFreeTypeParameters(parameters[parameters.length - 1].getType());
return CachedValueProvider.Result.create(argumentType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
}
return CachedValueProvider.Result.create(null, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
}
private static PsiType getGetterReturnType(@NotNull PsiMethod method) {
return CachedValuesManager.getCachedValue(method, () -> {
final PsiType returnType = eraseFreeTypeParameters(method.getReturnType());
return CachedValueProvider.Result.create(returnType, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT);
});
}
@@ -741,10 +767,9 @@ public class JavaFxPsiUtil {
}
if (declaration instanceof PsiMethod) {
PsiMethod psiMethod = (PsiMethod)declaration;
final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
final boolean isStatic = psiMethod.hasModifierProperty(PsiModifier.STATIC);
if (!isStatic && parameters.length == 0) {
return psiMethod.getReturnType();
if (psiMethod.getParameterList().getParametersCount() == 0 &&
!psiMethod.hasModifierProperty(PsiModifier.STATIC)) {
return getGetterReturnType(psiMethod);
}
}
return null;
@@ -1,8 +1,10 @@
package org.jetbrains.plugins.javaFX.fxml.descriptors;
import com.intellij.codeInsight.daemon.impl.analysis.JavaGenericsUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiUtil;
@@ -69,20 +71,26 @@ public class JavaFxPropertyTagDescriptor implements XmlElementDescriptor {
final PsiType collectionItemType = JavaGenericsUtil.getCollectionItemType(propertyType, declaration.getResolveScope());
if (collectionItemType != null) {
collectSubclassesDescriptors(collectionItemType, descriptors);
collectSubclassesDescriptors(collectionItemType, descriptors, context);
}
else if (!JavaFxPsiUtil.isPrimitiveOrBoxed(propertyType)) {
collectSubclassesDescriptors(propertyType, descriptors);
collectSubclassesDescriptors(propertyType, descriptors, context);
}
if (!descriptors.isEmpty()) return descriptors.toArray(new XmlElementDescriptor[descriptors.size()]);
if (!descriptors.isEmpty()) return descriptors.toArray(XmlElementDescriptor.EMPTY_ARRAY);
}
return XmlElementDescriptor.EMPTY_ARRAY;
}
private static void collectSubclassesDescriptors(PsiType psiType, @NotNull final List<XmlElementDescriptor> descriptors) {
private static void collectSubclassesDescriptors(@Nullable PsiType psiType,
@NotNull final List<XmlElementDescriptor> descriptors,
@NotNull PsiElement context) {
final PsiClass aClass = PsiUtil.resolveClassInType(psiType);
if (aClass != null) {
if (CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName())) {
collectRawPropertyDescriptors(descriptors, context);
return;
}
ClassInheritorsSearch.search(aClass, aClass.getUseScope(), true, true, false)
.forEach(psiClass -> {
addElementDescriptor(descriptors, psiClass);
@@ -92,8 +100,24 @@ public class JavaFxPropertyTagDescriptor implements XmlElementDescriptor {
}
}
private static void addElementDescriptor(@NotNull List<XmlElementDescriptor> descriptors, @NotNull PsiClass aClass) {
if (!PsiUtil.isAbstractClass(aClass) && !PsiUtil.isInnerClass(aClass)) {
private static void collectRawPropertyDescriptors(@NotNull List<XmlElementDescriptor> descriptors, @NotNull PsiElement context) {
final Project project = context.getProject();
final JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
// Offer most used simple types. TODO try to guess suitable types from the project sources
addElementDescriptor(descriptors, facade.findClass(CommonClassNames.JAVA_LANG_STRING, scope));
addElementDescriptor(descriptors, facade.findClass(CommonClassNames.JAVA_LANG_DOUBLE, scope));
addElementDescriptor(descriptors, facade.findClass(CommonClassNames.JAVA_LANG_INTEGER, scope));
addElementDescriptor(descriptors, facade.findClass(CommonClassNames.JAVA_LANG_BOOLEAN, scope));
}
private static void addElementDescriptor(@NotNull List<XmlElementDescriptor> descriptors, @Nullable PsiClass aClass) {
if (aClass != null &&
!CommonClassNames.JAVA_LANG_OBJECT.equals(aClass.getQualifiedName()) &&
!aClass.isInterface() &&
!PsiUtil.isAbstractClass(aClass) &&
!PsiUtil.isInnerClass(aClass) &&
JavaFxPsiUtil.isAbleToInstantiate(aClass)) {
descriptors.add(new JavaFxClassTagDescriptor(aClass.getName(), aClass));
}
}
@@ -0,0 +1,7 @@
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.layout.GridPane?>
<GridPane>
<ChoiceBox>
<<caret>
</ChoiceBox>
</GridPane>