blob: 9c4c934c0589d00a059d44a84c169bbd9b297f51 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Eclipse Public License, Version 1.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.eclipse.org/org/documents/epl-v10.php
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ide.common.layout;
import static com.android.SdkConstants.ANDROID_URI;
import static com.android.SdkConstants.ATTR_ID;
import static com.android.SdkConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.SdkConstants.ATTR_LAYOUT_WIDTH;
import static com.android.SdkConstants.ATTR_ORIENTATION;
import static com.android.SdkConstants.VALUE_HORIZONTAL;
import static com.android.SdkConstants.VALUE_VERTICAL;
import com.android.ide.common.api.DropFeedback;
import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.common.api.IDragElement;
import com.android.ide.common.api.IMenuCallback;
import com.android.ide.common.api.INode;
import com.android.ide.common.api.IViewRule;
import com.android.ide.common.api.Point;
import com.android.ide.common.api.Rect;
import com.android.ide.common.api.RuleAction;
import com.android.ide.common.api.RuleAction.NestedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/** Test the {@link LinearLayoutRule} */
public class LinearLayoutRuleTest extends LayoutTestBase {
// Utility for other tests
protected void dragIntoEmpty(Rect dragBounds) {
boolean haveBounds = dragBounds.isValid();
IViewRule rule = new LinearLayoutRule();
INode targetNode = TestNode.create("android.widget.LinearLayout").id(
"@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480));
Point dropPoint = new Point(10, 5);
IDragElement[] elements = TestDragElement.create(TestDragElement.create(
"android.widget.Button", dragBounds).id("@+id/Button01"));
// Enter target
DropFeedback feedback = rule.onDropEnter(targetNode, null/*targetView*/, elements);
assertNotNull(feedback);
assertFalse(feedback.invalidTarget);
assertNotNull(feedback.painter);
feedback = rule.onDropMove(targetNode, elements, feedback, dropPoint);
assertNotNull(feedback);
assertFalse(feedback.invalidTarget);
// Paint feedback and make sure it's what we expect
TestGraphics graphics = new TestGraphics();
assertNotNull(feedback.painter);
feedback.painter.paint(graphics, targetNode, feedback);
assertEquals(
// Expect to see a recipient rectangle around the bounds of the
// LinearLayout,
// as well as a single vertical line as a drop preview located
// along the left
// edge (for this horizontal linear layout) showing insert
// position at index 0,
// and finally a rectangle for the bounds of the inserted button
// centered over
// the middle
"[useStyle(DROP_RECIPIENT), "
+
// Bounds rectangle
"drawRect(Rect[0,0,240,480]), "
+ "useStyle(DROP_ZONE), drawLine(1,0,1,480), "
+ "useStyle(DROP_ZONE_ACTIVE), " + "useStyle(DROP_PREVIEW), " +
// Insert position line
"drawLine(1,0,1,480)" + (haveBounds ?
// Outline of dragged node centered over position line
", useStyle(DROP_PREVIEW), " + "drawRect(1,0,101,80)"
// Nothing when we don't have bounds
: "") + "]", graphics.getDrawn().toString());
// Attempt a drop
assertEquals(0, targetNode.getChildren().length);
rule.onDropped(targetNode, elements, feedback, dropPoint);
assertEquals(1, targetNode.getChildren().length);
assertEquals("@+id/Button01", targetNode.getChildren()[0].getStringAttr(
ANDROID_URI, ATTR_ID));
}
// Utility for other tests
protected INode dragInto(boolean vertical, Rect dragBounds, Point dragPoint,
int insertIndex, int currentIndex,
String... graphicsFragments) {
INode linearLayout = TestNode.create("android.widget.LinearLayout").id(
"@+id/LinearLayout01").bounds(new Rect(0, 0, 240, 480)).set(ANDROID_URI,
ATTR_ORIENTATION,
vertical ? VALUE_VERTICAL : VALUE_HORIZONTAL)
.add(
TestNode.create("android.widget.Button").id("@+id/Button01").bounds(
new Rect(0, 0, 100, 80)),
TestNode.create("android.widget.Button").id("@+id/Button02").bounds(
new Rect(0, 100, 100, 80)),
TestNode.create("android.widget.Button").id("@+id/Button03").bounds(
new Rect(0, 200, 100, 80)),
TestNode.create("android.widget.Button").id("@+id/Button04").bounds(
new Rect(0, 300, 100, 80)));
return super.dragInto(new LinearLayoutRule(), linearLayout, dragBounds, dragPoint, null,
insertIndex, currentIndex, graphicsFragments);
}
// Check that the context menu registers the expected menu items
public void testContextMenu() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
INode node = TestNode.create("android.widget.Button").id("@+id/Button012");
List<RuleAction> contextMenu = new ArrayList<RuleAction>();
rule.addContextMenuActions(contextMenu, node);
assertEquals(6, contextMenu.size());
assertEquals("Edit ID...", contextMenu.get(0).getTitle());
assertTrue(contextMenu.get(1) instanceof RuleAction.Separator);
assertEquals("Layout Width", contextMenu.get(2).getTitle());
assertEquals("Layout Height", contextMenu.get(3).getTitle());
assertTrue(contextMenu.get(4) instanceof RuleAction.Separator);
assertEquals("Other Properties", contextMenu.get(5).getTitle());
RuleAction propertiesMenu = contextMenu.get(5);
assertTrue(propertiesMenu.getClass().getName(),
propertiesMenu instanceof NestedAction);
}
public void testContextMenuCustom() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
INode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout")
.set(ANDROID_URI, ATTR_LAYOUT_WIDTH, "42dip")
.set(ANDROID_URI, ATTR_LAYOUT_HEIGHT, "50sp");
List<RuleAction> contextMenu = new ArrayList<RuleAction>();
rule.addContextMenuActions(contextMenu, node);
assertEquals(6, contextMenu.size());
assertEquals("Layout Width", contextMenu.get(2).getTitle());
RuleAction menuAction = contextMenu.get(2);
assertTrue(menuAction instanceof RuleAction.Choices);
RuleAction.Choices choices = (RuleAction.Choices) menuAction;
List<String> titles = choices.getTitles();
List<String> ids = choices.getIds();
assertEquals("Wrap Content", titles.get(0));
assertEquals("wrap_content", ids.get(0));
assertEquals("Match Parent", titles.get(1));
assertEquals("match_parent", ids.get(1));
assertEquals("42dip", titles.get(2));
assertEquals("42dip", ids.get(2));
assertEquals("42dip", choices.getCurrent());
}
// Check that the context menu manipulates the orientation attribute
public void testOrientation() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
TestNode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout012");
node.putAttributeInfo(ANDROID_URI, "orientation",
new TestAttributeInfo(ATTR_ORIENTATION, Format.ENUM_SET,
"android.widget.LinearLayout",
new String[] {"horizontal", "vertical"}, null, null));
assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION));
List<RuleAction> contextMenu = new ArrayList<RuleAction>();
rule.addContextMenuActions(contextMenu, node);
assertEquals(7, contextMenu.size());
RuleAction orientationAction = contextMenu.get(1);
assertEquals("Orientation", orientationAction.getTitle());
assertTrue(orientationAction.getClass().getName(),
orientationAction instanceof RuleAction.Choices);
RuleAction.Choices choices = (RuleAction.Choices) orientationAction;
IMenuCallback callback = choices.getCallback();
callback.action(orientationAction, Collections.singletonList(node), VALUE_VERTICAL, true);
String orientation = node.getStringAttr(ANDROID_URI,
ATTR_ORIENTATION);
assertEquals(VALUE_VERTICAL, orientation);
callback.action(orientationAction, Collections.singletonList(node), VALUE_HORIZONTAL,
true);
orientation = node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION);
assertEquals(VALUE_HORIZONTAL, orientation);
}
// Check that the context menu manipulates the orientation attribute
public void testProperties() {
LinearLayoutRule rule = new LinearLayoutRule();
initialize(rule, "android.widget.LinearLayout");
TestNode node = TestNode.create("android.widget.LinearLayout").id("@+id/LinearLayout012");
node.putAttributeInfo(ANDROID_URI, "orientation",
new TestAttributeInfo(ATTR_ORIENTATION, Format.ENUM_SET,
"android.widget.LinearLayout",
new String[] {"horizontal", "vertical"}, null, null));
node.setAttributeSources(Arrays.asList("android.widget.LinearLayout",
"android.view.ViewGroup", "android.view.View"));
node.putAttributeInfo(ANDROID_URI, "gravity",
new TestAttributeInfo("gravity", Format.INTEGER_SET,
"android.widget.LinearLayout", null, null, null));
assertNull(node.getStringAttr(ANDROID_URI, ATTR_ORIENTATION));
List<RuleAction> contextMenu = new ArrayList<RuleAction>();
rule.addContextMenuActions(contextMenu, node);
assertEquals(8, contextMenu.size());
assertEquals("Orientation", contextMenu.get(1).getTitle());
assertEquals("Edit Gravity...", contextMenu.get(2).getTitle());
assertEquals("Other Properties", contextMenu.get(7).getTitle());
RuleAction propertiesMenu = contextMenu.get(7);
assertTrue(propertiesMenu.getClass().getName(),
propertiesMenu instanceof NestedAction);
NestedAction nested = (NestedAction) propertiesMenu;
List<RuleAction> nestedActions = nested.getNestedActions(node);
assertEquals(9, nestedActions.size());
assertEquals("Recent", nestedActions.get(0).getTitle());
assertTrue(nestedActions.get(1) instanceof RuleAction.Separator);
assertEquals("Defined by LinearLayout", nestedActions.get(2).getTitle());
assertEquals("Inherited from ViewGroup", nestedActions.get(3).getTitle());
assertEquals("Inherited from View", nestedActions.get(4).getTitle());
assertTrue(nestedActions.get(5) instanceof RuleAction.Separator);
assertEquals("Layout Parameters", nestedActions.get(6).getTitle());
assertTrue(nestedActions.get(7) instanceof RuleAction.Separator);
assertEquals("All By Name", nestedActions.get(8).getTitle());
BaseViewRule.editedProperty(ATTR_ORIENTATION);
RuleAction recentAction = nestedActions.get(0);
assertTrue(recentAction instanceof NestedAction);
NestedAction recentChoices = (NestedAction) recentAction;
List<RuleAction> recentItems = recentChoices.getNestedActions(node);
assertEquals(1, recentItems.size());
assertEquals("Orientation", recentItems.get(0).getTitle());
BaseViewRule.editedProperty("gravity");
recentItems = recentChoices.getNestedActions(node);
assertEquals(2, recentItems.size());
assertEquals("Gravity...", recentItems.get(0).getTitle());
assertEquals("Orientation", recentItems.get(1).getTitle());
BaseViewRule.editedProperty(ATTR_ORIENTATION);
recentItems = recentChoices.getNestedActions(node);
assertEquals(2, recentItems.size());
assertEquals("Orientation", recentItems.get(0).getTitle());
assertEquals("Gravity...", recentItems.get(1).getTitle());
// Lots of other properties -- flushes out properties that apply to this view
for (int i = 0; i < 30; i++) {
BaseViewRule.editedProperty("dummy_" + i);
}
recentItems = recentChoices.getNestedActions(node);
assertEquals(0, recentItems.size());
BaseViewRule.editedProperty("gravity");
recentItems = recentChoices.getNestedActions(node);
assertEquals(1, recentItems.size());
assertEquals("Gravity...", recentItems.get(0).getTitle());
}
public void testDragInEmptyWithBounds() {
dragIntoEmpty(new Rect(0, 0, 100, 80));
}
public void testDragInEmptyWithoutBounds() {
dragIntoEmpty(new Rect(0, 0, 0, 0));
}
public void testDragInVerticalTop() {
dragInto(true,
// Bounds of the dragged item
new Rect(0, 0, 105, 80),
// Drag point
new Point(30, -10),
// Expected insert location
0,
// Not dragging one of the existing children
-1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
+ "drawLine(0,190,240,190), drawLine(0,290,240,290), "
+ "drawLine(0,381,240,381)",
// Active nearest line
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)",
// Preview of the dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(0,-40,105,40)");
// Without drag bounds it should be identical except no preview
// rectangle
dragInto(true,
new Rect(0, 0, 0, 0), // Invalid
new Point(30, -10), 0, -1,
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,0,240,0)");
}
public void testDragInVerticalBottom() {
dragInto(true,
// Bounds of the dragged item
new Rect(0, 0, 105, 80),
// Drag point
new Point(30, 500),
// Expected insert location
4,
// Not dragging one of the existing children
-1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
+ "drawLine(0,190,240,190), drawLine(0,290,240,290), drawLine(0,381,240,381), ",
// Active nearest line
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)",
// Preview of the dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(0,381,105,461)");
// Check without bounds too
dragInto(true, new Rect(0, 0, 105, 80), new Point(30, 500), 4, -1,
"useStyle(DROP_PREVIEW), drawRect(0,381,105,461)");
}
public void testDragInVerticalMiddle() {
dragInto(true,
// Bounds of the dragged item
new Rect(0, 0, 105, 80),
// Drag point
new Point(0, 170),
// Expected insert location
2,
// Not dragging one of the existing children
-1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,90,240,90), "
+ "drawLine(0,190,240,190), drawLine(0,290,240,290)",
// Active nearest line
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,190,240,190)",
// Preview of the dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(0,150,105,230)");
// Check without bounds too
dragInto(true, new Rect(0, 0, 105, 80), new Point(0, 170), 2, -1,
"useStyle(DROP_PREVIEW), drawRect(0,150,105,230)");
}
public void testDragInVerticalMiddleSelfPos() {
// Drag the 2nd button, down to the position between 3rd and 4th
dragInto(true,
// Bounds of the dragged item
new Rect(0, 100, 100, 80),
// Drag point
new Point(0, 250),
// Expected insert location
2,
// Dragging 1st item
1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones - these are different because we exclude drop
// zones around the
// dragged item itself (it doesn't make sense to insert directly
// before or after
// myself
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), "
+ "drawLine(0,381,240,381)",
// Preview line along insert axis
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,290,240,290)",
// Preview of dropped rectangle
"useStyle(DROP_PREVIEW), drawRect(0,250,100,330)");
// Test dropping on self (no position change):
dragInto(true,
// Bounds of the dragged item
new Rect(0, 100, 100, 80),
// Drag point
new Point(0, 210),
// Expected insert location
1,
// Dragging from same pos
1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop zones - these are different because we exclude drop
// zones around the
// dragged item itself (it doesn't make sense to insert directly
// before or after
// myself
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), "
+ "drawLine(0,381,240,381)",
// No active nearest line when you're over the self pos!
// Preview of the dropped rectangle
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawRect(0,100,100,180)");
}
public void testDragToLastPosition() {
// Drag a button to the last position -- and confirm that the preview rectangle
// is now shown midway between the second to last and last positions, but fully
// below the drop zone line:
dragInto(true,
// Bounds of the dragged item
new Rect(0, 100, 100, 80),
// Drag point
new Point(0, 400),
// Expected insert location
3,
// Dragging 1st item
1,
// Bounds rectangle
"useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])",
// Drop Zones
"useStyle(DROP_ZONE), drawLine(0,0,240,0), drawLine(0,290,240,290), " +
"drawLine(0,381,240,381), ",
// Active Drop Zone
"useStyle(DROP_ZONE_ACTIVE), useStyle(DROP_PREVIEW), drawLine(0,381,240,381)",
// Drop Preview
"useStyle(DROP_PREVIEW), drawRect(0,381,100,461)");
}
// Left to test:
// Check inserting at last pos with multiple children
// Check inserting with no bounds rectangle for dragged element
}