LinearLayout

This commit is contained in:
Alexander Lobas
2012-04-09 21:21:05 +04:00
parent f814f07fc1
commit a09f87c378
14 changed files with 432 additions and 310 deletions
@@ -75,7 +75,8 @@ public class FlowBaseOperation extends AbstractEditOperation {
if (myHorizontal) {
for (RadComponent child : myContainer.getChildren()) {
Rectangle childBounds = child.getBounds(layer);
if (childBounds.x <= location.x && location.x <= childBounds.getMaxX()) {
Rectangle margins = ((RadViewComponent)child).getMargins();
if (childBounds.x - margins.x <= location.x && location.x <= childBounds.getMaxX() + margins.width) {
myChildTarget = child;
break;
}
@@ -84,7 +85,8 @@ public class FlowBaseOperation extends AbstractEditOperation {
else {
for (RadComponent child : myContainer.getChildren()) {
Rectangle childBounds = child.getBounds(layer);
if (childBounds.y <= location.y && location.y <= childBounds.getMaxY()) {
Rectangle margins = ((RadViewComponent)child).getMargins();
if (childBounds.y - margins.y <= location.y && location.y <= childBounds.getMaxY() + margins.height) {
myChildTarget = child;
break;
}
@@ -95,24 +97,25 @@ public class FlowBaseOperation extends AbstractEditOperation {
}
Rectangle targetBounds = myChildTarget.getBounds(layer);
Rectangle targetMargins = ((RadViewComponent)myChildTarget).getMargins();
if (myHorizontal) {
myInsertBefore = location.x < targetBounds.getCenterX();
if (myInsertBefore) {
myInsertFeedback.setLocation(targetBounds.x, myBounds.y);
myInsertFeedback.setLocation(targetBounds.x - targetMargins.x, myBounds.y);
}
else {
myInsertFeedback.setLocation(targetBounds.x + targetBounds.width, myBounds.y);
myInsertFeedback.setLocation(targetBounds.x + targetBounds.width + targetMargins.width, myBounds.y);
}
}
else {
myInsertBefore = location.y < targetBounds.getCenterY();
if (myInsertBefore) {
myInsertFeedback.setLocation(myBounds.x, targetBounds.y);
myInsertFeedback.setLocation(myBounds.x, targetBounds.y - targetMargins.y);
}
else {
myInsertFeedback.setLocation(myBounds.x, targetBounds.y + targetBounds.height);
myInsertFeedback.setLocation(myBounds.x, targetBounds.y + targetBounds.height + targetMargins.height);
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.android.designer.designSurface.layout;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.designer.designSurface.DecorationLayer;
import com.intellij.designer.designSurface.StaticDecorator;
import com.intellij.designer.model.RadComponent;
@@ -43,13 +44,15 @@ public abstract class FlowStaticDecorator extends StaticDecorator {
if (isHorizontal()) {
for (RadComponent child : component.getChildren()) {
Rectangle childBounds = child.getBounds(layer);
g.drawLine(childBounds.x + childBounds.width, bounds.y, childBounds.x + childBounds.width, bounds.y + bounds.height);
int x = childBounds.x + childBounds.width + ((RadViewComponent)child).getMargins().width;
g.drawLine(x, bounds.y, x, bounds.y + bounds.height);
}
}
else {
for (RadComponent child : component.getChildren()) {
Rectangle childBounds = child.getBounds(layer);
g.drawLine(bounds.x, childBounds.y + childBounds.height, bounds.x + bounds.width, childBounds.y + childBounds.height);
int y = childBounds.y + childBounds.height + ((RadViewComponent)child).getMargins().height;
g.drawLine(bounds.x, y, bounds.x + bounds.width, y);
}
}
}
@@ -18,7 +18,6 @@ package com.intellij.android.designer.designSurface.layout;
import com.intellij.android.designer.designSurface.AbstractEditOperation;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.model.layout.Gravity;
import com.intellij.android.designer.model.layout.RadFrameLayout;
import com.intellij.designer.designSurface.FeedbackLayer;
import com.intellij.designer.designSurface.OperationContext;
import com.intellij.designer.designSurface.feedbacks.AlphaFeedback;
@@ -52,7 +51,7 @@ public class FrameLayoutOperation extends AbstractEditOperation {
myExcludes = new HashSet<Pair<Gravity, Gravity>>();
for (RadComponent component : context.getComponents()) {
myExcludes.add(RadFrameLayout.gravity(component));
myExcludes.add(Gravity.getSides(component));
}
}
}
@@ -15,10 +15,7 @@
*/
package com.intellij.android.designer.designSurface.layout;
import com.android.ide.common.rendering.api.ViewInfo;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.model.layout.Gravity;
import com.intellij.android.designer.model.layout.RadFrameLayout;
import com.intellij.designer.designSurface.DecorationLayer;
import com.intellij.designer.designSurface.EditOperation;
import com.intellij.designer.designSurface.FeedbackLayer;
@@ -33,7 +30,6 @@ import com.intellij.designer.designSurface.tools.InputTool;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.utils.Position;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
@@ -44,8 +40,8 @@ import java.util.List;
/**
* @author Alexander Lobas
*/
public class FrameLayoutMarginOperation implements EditOperation {
public static final String TYPE = "frame_layout_margin";
public class LayoutMarginOperation implements EditOperation {
public static final String TYPE = "layout_margin";
private final OperationContext myContext;
private RadViewComponent myComponent;
@@ -53,14 +49,14 @@ public class FrameLayoutMarginOperation implements EditOperation {
private TextFeedback myTextFeedback;
private Rectangle myMargins;
public FrameLayoutMarginOperation(OperationContext context) {
public LayoutMarginOperation(OperationContext context) {
myContext = context;
}
@Override
public void setComponent(RadComponent component) {
myComponent = (RadViewComponent)component;
myMargins = getMargins(myComponent);
myMargins = myComponent.getMargins();
}
@Override
@@ -185,25 +181,6 @@ public class FrameLayoutMarginOperation implements EditOperation {
}
}
public static boolean visible(RadComponent component, DirectionResizePoint point) {
Pair<Gravity, Gravity> gravity = RadFrameLayout.gravity(component);
int direction = point.getDirection();
if (direction == Position.WEST) { // left
return gravity.first == Gravity.left || gravity.first == Gravity.center;
}
else if (direction == Position.EAST) { // right
return gravity.first == Gravity.right || gravity.first == Gravity.center;
}
else if (direction == Position.NORTH) { // top
return gravity.second == Gravity.top || gravity.second == Gravity.center;
}
else if (direction == Position.SOUTH) { // bottom
return gravity.second == Gravity.bottom || gravity.second == Gravity.center;
}
return true;
}
private static final BasicStroke STROKE = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1, new float[]{1, 2}, 0);
public static void points(ResizeSelectionDecorator decorator) {
@@ -212,7 +189,7 @@ public class FrameLayoutMarginOperation implements EditOperation {
@Override
protected void paint(DecorationLayer layer, Graphics2D g, RadComponent component) {
Rectangle bounds = component.getBounds(layer);
applyMargins(bounds, getMargins(component));
applyMargins(bounds, ((RadViewComponent)component).getMargins());
g.setStroke(STROKE);
g.setColor(Color.red);
@@ -240,40 +217,40 @@ public class FrameLayoutMarginOperation implements EditOperation {
}
});
decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.WEST, FrameLayoutMarginOperation.TYPE) { // left
decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.WEST, LayoutMarginOperation.TYPE) { // left
@Override
protected Point getLocation(DecorationLayer layer, RadComponent component) {
Point location = super.getLocation(layer, component);
location.x -= getMargin(component, "leftMargin");
location.x -= ((RadViewComponent)component).getMargins().x;
return location;
}
});
decorator
.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.EAST, FrameLayoutMarginOperation.TYPE) { // right
.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.EAST, LayoutMarginOperation.TYPE) { // right
@Override
protected Point getLocation(DecorationLayer layer, RadComponent component) {
Point location = super.getLocation(layer, component);
location.x += getMargin(component, "rightMargin");
location.x += ((RadViewComponent)component).getMargins().width;
return location;
}
}.move(1, 0.25));
decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.NORTH, FrameLayoutMarginOperation.TYPE) { // top
decorator.addPoint(new DirectionResizePoint(Color.orange, Color.black, Position.NORTH, LayoutMarginOperation.TYPE) { // top
@Override
protected Point getLocation(DecorationLayer layer, RadComponent component) {
Point location = super.getLocation(layer, component);
location.y -= getMargin(component, "topMargin");
location.y -= ((RadViewComponent)component).getMargins().y;
return location;
}
});
decorator.addPoint(
new DirectionResizePoint(Color.orange, Color.black, Position.SOUTH, FrameLayoutMarginOperation.TYPE) { // bottom
new DirectionResizePoint(Color.orange, Color.black, Position.SOUTH, LayoutMarginOperation.TYPE) { // bottom
@Override
protected Point getLocation(DecorationLayer layer, RadComponent component) {
Point location = super.getLocation(layer, component);
location.y += getMargin(component, "bottomMargin");
location.y += ((RadViewComponent)component).getMargins().height;
return location;
}
}.move(0.25, 1));
@@ -289,35 +266,4 @@ public class FrameLayoutMarginOperation implements EditOperation {
bounds.width += margins.width;
bounds.height += margins.height;
}
private static Rectangle getMargins(RadComponent component) {
Rectangle margins = new Rectangle();
try {
ViewInfo viewInfo = ((RadViewComponent)component).getViewInfo();
Object layoutParams = viewInfo.getLayoutParamsObject();
Class<?> layoutClass = layoutParams.getClass();
margins.x = layoutClass.getField("leftMargin").getInt(layoutParams);
margins.y = layoutClass.getField("topMargin").getInt(layoutParams);
margins.width = layoutClass.getField("rightMargin").getInt(layoutParams);
margins.height = layoutClass.getField("bottomMargin").getInt(layoutParams);
}
catch (Throwable e) {
}
return margins;
}
private static int getMargin(RadComponent component, String marginName) {
try {
ViewInfo viewInfo = ((RadViewComponent)component).getViewInfo();
Object layoutParams = viewInfo.getLayoutParamsObject();
return layoutParams.getClass().getField(marginName).getInt(layoutParams);
}
catch (Throwable e) {
return 0;
}
}
}
@@ -17,10 +17,8 @@ package com.intellij.android.designer.designSurface.layout;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.model.layout.Gravity;
import com.intellij.android.designer.model.layout.RadFrameLayout;
import com.intellij.designer.designSurface.FeedbackLayer;
import com.intellij.designer.designSurface.OperationContext;
import com.intellij.designer.designSurface.feedbacks.AlphaFeedback;
import com.intellij.designer.designSurface.feedbacks.TextFeedback;
import com.intellij.designer.model.RadComponent;
import com.intellij.openapi.application.ApplicationManager;
@@ -28,11 +26,10 @@ import com.intellij.openapi.util.Pair;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlTag;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.LightColors;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
@@ -41,42 +38,27 @@ import java.util.List;
public class LinearLayoutOperation extends FlowBaseOperation {
private GravityFeedback myFeedback;
private TextFeedback myTextFeedback;
private List<Gravity> myExcludes;
private Gravity myExclude;
private Gravity myGravity;
public LinearLayoutOperation(RadViewComponent container, OperationContext context, boolean horizontal) {
super(container, context, horizontal);
if (context.isMove()) {
myExcludes = new ArrayList<Gravity>();
for (RadComponent component : context.getComponents()) {
String fill;
if (myHorizontal) {
fill = ((RadViewComponent)component).getTag().getAttributeValue("android:layout_height");
}
else {
fill = ((RadViewComponent)component).getTag().getAttributeValue("android:layout_width");
}
if ("match_parent".equals(fill) || "fill_parent".equals(fill)) {
myExcludes.add(null);
}
else {
Pair<Gravity, Gravity> gravity = RadFrameLayout.gravity(component);
myExcludes.add(myHorizontal ? gravity.second : gravity.first);
}
}
if (context.isMove() && context.getComponents().size() == 1) {
myExclude = getGravity(myHorizontal, context.getComponents().get(0));
}
}
@Override
protected void createFeedback() {
super.createFeedback();
if (myFeedback == null) {
FeedbackLayer layer = myContext.getArea().getFeedbackLayer();
myFeedback = new GravityFeedback();
layer.add(myFeedback);
myFeedback.setBounds(myBounds);
layer.add(myFeedback, 0);
myTextFeedback = new TextFeedback();
myTextFeedback.setBorder(IdeBorderFactory.createEmptyBorder(0, 3, 2, 0));
@@ -90,18 +72,6 @@ public class LinearLayoutOperation extends FlowBaseOperation {
public void showFeedback() {
super.showFeedback();
if (myChildTarget == null) {
myFeedback.setBounds(myBounds);
}
else if (myHorizontal) {
Rectangle childBounds = myChildTarget.getBounds(myContext.getArea().getFeedbackLayer());
myFeedback.setBounds(childBounds.x, myBounds.y, childBounds.width, myBounds.height);
}
else {
Rectangle childBounds = myChildTarget.getBounds(myContext.getArea().getFeedbackLayer());
myFeedback.setBounds(myBounds.x, childBounds.y, myBounds.width, childBounds.height);
}
Point location = myContext.getLocation();
Gravity gravity = myHorizontal ? calculateVertical(myBounds, location) : calculateHorizontal(myBounds, location);
@@ -129,11 +99,11 @@ public class LinearLayoutOperation extends FlowBaseOperation {
@Nullable
private static Gravity calculateHorizontal(Rectangle bounds, Point location) {
Gravity horizontal = null;
Gravity horizontal = Gravity.right;
double width = bounds.width / 4.0;
double left = bounds.x + width;
double center = bounds.x + 2 * width;
double right = bounds.x + 3 * width;
double fill = bounds.x + 3 * width;
if (location.x < left) {
horizontal = Gravity.left;
@@ -141,8 +111,8 @@ public class LinearLayoutOperation extends FlowBaseOperation {
else if (left < location.x && location.x < center) {
horizontal = Gravity.center;
}
else if (center < location.x && location.x < right) {
horizontal = Gravity.right;
else if (center < location.x && location.x < fill) {
horizontal = null;
}
return horizontal;
@@ -150,11 +120,11 @@ public class LinearLayoutOperation extends FlowBaseOperation {
@Nullable
private static Gravity calculateVertical(Rectangle bounds, Point location) {
Gravity vertical = null;
Gravity vertical = Gravity.bottom;
double height = bounds.height / 4.0;
double top = bounds.y + height;
double center = bounds.y + 2 * height;
double bottom = bounds.y + 3 * height;
double fill = bounds.y + 3 * height;
if (location.y < top) {
vertical = Gravity.top;
@@ -162,8 +132,8 @@ public class LinearLayoutOperation extends FlowBaseOperation {
else if (top < location.y && location.y < center) {
vertical = Gravity.center;
}
else if (center < location.y && location.y < bottom) {
vertical = Gravity.bottom;
else if (center < location.y && location.y < fill) {
vertical = null;
}
return vertical;
@@ -171,197 +141,147 @@ public class LinearLayoutOperation extends FlowBaseOperation {
@Override
public boolean canExecute() {
if (myContext.isMove()) {
return !isExclude(myGravity);
}
return true;
}
private boolean isExclude(Gravity gravity) {
int index = myComponents.indexOf(myChildTarget);
return index != -1 && gravity == myExcludes.get(index);
return super.canExecute() || (myComponents.size() == 1 && myGravity != myExclude);
}
@Override
public void execute() throws Exception {
super.execute();
if (super.canExecute()) {
super.execute();
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
if (myGravity == null) {
for (RadComponent component : myComponents) {
XmlTag tag = ((RadViewComponent)component).getTag();
XmlAttribute attribute = tag.getAttribute("android:layout_gravity");
if (attribute != null) {
attribute.delete();
}
tag.setAttribute(myHorizontal ? "android:layout_height" : "android:layout_width", "fill_parent");
}
}
else {
for (RadComponent component : myComponents) {
XmlTag tag = ((RadViewComponent)component).getTag();
XmlAttribute attribute = tag.getAttribute(myHorizontal ? "android:layout_height" : "android:layout_width");
if (attribute != null && ("match_parent".equals(attribute.getValue()) || "fill_parent".equals(attribute.getValue()))) {
attribute.setValue("wrap_content");
}
tag.setAttribute("android:layout_gravity", myHorizontal ?
Gravity.getValue(null, myGravity) : Gravity.getValue(myGravity, null));
}
}
execute(myHorizontal, myGravity, myComponents);
}
});
}
public static void execute(boolean horizontal, Gravity gravity, List<RadComponent> components) {
if (gravity == null) {
for (RadComponent component : components) {
XmlTag tag = ((RadViewComponent)component).getTag();
XmlAttribute attribute = tag.getAttribute("android:layout_gravity");
if (attribute != null) {
attribute.delete();
}
tag.setAttribute(horizontal ? "android:layout_height" : "android:layout_width", "fill_parent");
}
}
else {
String gravityValue = horizontal ? Gravity.getValue(null, gravity) : Gravity.getValue(gravity, null);
for (RadComponent component : components) {
XmlTag tag = ((RadViewComponent)component).getTag();
XmlAttribute attribute = tag.getAttribute(horizontal ? "android:layout_height" : "android:layout_width");
if (attribute != null && ("match_parent".equals(attribute.getValue()) || "fill_parent".equals(attribute.getValue()))) {
attribute.setValue("wrap_content");
}
tag.setAttribute("android:layout_gravity", gravityValue);
}
}
}
@Nullable
public static Gravity getGravity(boolean horizontal, RadComponent component) {
XmlTag tag = ((RadViewComponent)component).getTag();
String length = tag.getAttributeValue(horizontal ? "android:layout_height" : "android:layout_width");
if (!ResizeOperation.isFill(length)) {
Pair<Gravity, Gravity> gravity = Gravity.getSides(component);
return horizontal ? gravity.second : gravity.first;
}
return null;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Feedback
//
//////////////////////////////////////////////////////////////////////////////////////////
public static final Gravity[] HORIZONTALS = {Gravity.left, Gravity.center, Gravity.right, null};
public static final Gravity[] VERTICALS = {Gravity.top, Gravity.center, Gravity.bottom, null};
private class GravityFeedback extends AlphaFeedback {
private static final int SIZE = 2;
private class GravityFeedback extends JComponent {
private Gravity myGravity;
public GravityFeedback() {
super(BorderStaticDecorator.COLOR);
}
public void setGravity(Gravity gravity) {
myGravity = gravity;
repaint();
}
@Override
protected void paintOther1(Graphics2D g2d) {
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.magenta);
if (myHorizontal) {
for (Gravity gravity : VERTICALS) {
if (!myContext.isMove() || !isExclude(gravity)) {
paintHorizontalCell(g2d, gravity, false);
}
}
paintHorizontalCell(g);
}
else {
for (Gravity gravity : HORIZONTALS) {
if (!myContext.isMove() || !isExclude(gravity)) {
paintVerticalCell(g2d, gravity, false);
}
}
paintVerticalCell(g);
}
}
@Override
protected void paintOther2(Graphics2D g2d) {
if (myHorizontal) {
for (Gravity gravity : VERTICALS) {
if ((!myContext.isMove() || !isExclude(gravity)) && paintHorizontalCell(g2d, gravity, true)) {
break;
}
}
}
else {
for (Gravity gravity : HORIZONTALS) {
if ((!myContext.isMove() || !isExclude(gravity)) && paintVerticalCell(g2d, gravity, true)) {
break;
}
}
}
}
private boolean paintHorizontalCell(Graphics2D g2d, Gravity gravity, boolean selection) {
int x = 0;
int width = getWidth();
private void paintHorizontalCell(Graphics g) {
int y = 0;
int height = (getHeight() - 3) / 4;
if (gravity == Gravity.center) {
if (myGravity == Gravity.center) {
y = height + 1;
}
else if (gravity == Gravity.bottom) {
else if (myGravity == null) {
y = 2 * height + 2;
}
else if (gravity == null) {
else if (myGravity == Gravity.bottom) {
y = getHeight() - height;
}
int hSpace = Math.min(5, Math.max(1, getWidth() / 30));
if (hSpace > 1) {
x += hSpace;
width -= 2 * hSpace;
}
int vSpace = Math.min(5, Math.max(1, getHeight() / 30));
if (vSpace > 1) {
y += vSpace;
height -= 2 * vSpace;
}
if (selection) {
if (myGravity == gravity) {
Color oldColor = g2d.getColor();
g2d.setColor(LightColors.YELLOW);
g2d.fillRect(x, y, width, height);
g2d.setColor(oldColor);
return true;
}
if (myContainer.getChildren().isEmpty()) {
g.fillRect(0, y, 2, height);
g.fillRect(myBounds.width - SIZE, y, SIZE, height);
}
else {
g2d.fillRect(x, y, width, height);
g.fillRect(myInsertFeedback.getX() - myBounds.x + SIZE, y, SIZE, height);
}
return false;
}
private boolean paintVerticalCell(Graphics2D g2d, Gravity gravity, boolean selection) {
private void paintVerticalCell(Graphics g) {
int x = 0;
int width = (getWidth() - 3) / 4;
if (gravity == Gravity.center) {
if (myGravity == Gravity.center) {
x = width + 1;
}
else if (gravity == Gravity.right) {
else if (myGravity == null) {
x = 2 * width + 2;
}
else if (gravity == null) {
else if (myGravity == Gravity.right) {
x = getWidth() - width;
}
int y = 0;
int height = getHeight();
int hSpace = Math.min(5, Math.max(1, getWidth() / 30));
if (hSpace > 1) {
x += hSpace;
width -= 2 * hSpace;
}
int vSpace = Math.min(5, Math.max(1, getHeight() / 30));
if (vSpace > 1) {
y += vSpace;
height -= 2 * vSpace;
}
if (selection) {
if (myGravity == gravity) {
Color oldColor = g2d.getColor();
g2d.setColor(LightColors.YELLOW);
g2d.fillRect(x, y, width, height);
g2d.setColor(oldColor);
return true;
}
if (myContainer.getChildren().isEmpty()) {
g.fillRect(x, 0, width, SIZE);
g.fillRect(x, myBounds.height - SIZE, width, SIZE);
}
else {
g2d.fillRect(x, y, width, height);
g.fillRect(x, myInsertFeedback.getY() - myBounds.y + SIZE, width, SIZE);
}
return false;
}
}
}
@@ -145,13 +145,17 @@ public class ResizeOperation implements EditOperation {
if ("wrap_content".equals(value)) {
wrap = size;
}
else if ("fill_parent".equals(value) || "match_parent".equals(value)) {
else if (isFill(value)) {
fill = size;
}
return new Pair<Integer, Integer>(wrap, fill);
}
public static boolean isFill(String value) {
return "fill_parent".equals(value) || "match_parent".equals(value);
}
private void createStaticFeedback(Rectangle bounds, String width, String height) {
int direction = myContext.getResizeDirection();
Binary file not shown.

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

@@ -38,6 +38,7 @@ public class RadViewComponent extends RadComponent {
private ViewInfo myViewInfo;
private Component myNativeComponent;
private final Rectangle myBounds = new Rectangle();
private Rectangle myMargins;
private XmlTag myTag;
private List<Property> myProperties;
@@ -60,6 +61,26 @@ public class RadViewComponent extends RadComponent {
public void setViewInfo(ViewInfo viewInfo) {
myViewInfo = viewInfo;
myMargins = null;
}
public Rectangle getMargins() {
if (myMargins == null) {
myMargins = new Rectangle();
try {
Object layoutParams = myViewInfo.getLayoutParamsObject();
Class<?> layoutClass = layoutParams.getClass();
myMargins.x = layoutClass.getField("leftMargin").getInt(layoutParams);
myMargins.y = layoutClass.getField("topMargin").getInt(layoutParams);
myMargins.width = layoutClass.getField("rightMargin").getInt(layoutParams);
myMargins.height = layoutClass.getField("bottomMargin").getInt(layoutParams);
}
catch (Throwable e) {
}
}
return myMargins;
}
@Override
@@ -15,15 +15,23 @@
*/
package com.intellij.android.designer.model.layout;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.designer.model.RadComponent;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Alexander Lobas
*/
public enum Gravity {
left, right, center, top, bottom;
public static Icon ICON = IconLoader.getIcon("/com/intellij/android/designer/icons/gravity.png");
public static final int NONE = 0;
public static final int TOP = 1 << 0;
public static final int BOTTOM = 1 << 1;
@@ -134,4 +142,33 @@ public enum Gravity {
}
return flags;
}
public static Pair<Gravity, Gravity> getSides(RadComponent component) {
String value = ((RadViewComponent)component).getTag().getAttributeValue("android:layout_gravity");
int flags = getFlags(value);
Gravity horizontal = left;
if ((flags & LEFT) != 0) {
horizontal = left;
}
else if ((flags & CENTER_HORIZONTAL) != 0) {
horizontal = center;
}
else if ((flags & RIGHT) != 0) {
horizontal = right;
}
Gravity vertical = top;
if ((flags & TOP) != 0) {
vertical = top;
}
else if ((flags & CENTER_VERTICAL) != 0) {
vertical = center;
}
else if ((flags & BOTTOM) != 0) {
vertical = bottom;
}
return Pair.create(horizontal, vertical);
}
}
@@ -16,21 +16,24 @@
package com.intellij.android.designer.model.layout;
import com.intellij.android.designer.designSurface.TreeDropToOperation;
import com.intellij.android.designer.designSurface.layout.FrameLayoutMarginOperation;
import com.intellij.android.designer.designSurface.layout.FrameLayoutOperation;
import com.intellij.android.designer.designSurface.layout.LayoutMarginOperation;
import com.intellij.android.designer.designSurface.layout.ResizeOperation;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.model.RadViewLayoutWithData;
import com.intellij.designer.actions.AbstractComboBoxAction;
import com.intellij.designer.designSurface.*;
import com.intellij.designer.designSurface.ComponentDecorator;
import com.intellij.designer.designSurface.DesignerEditorPanel;
import com.intellij.designer.designSurface.EditOperation;
import com.intellij.designer.designSurface.OperationContext;
import com.intellij.designer.designSurface.selection.DirectionResizePoint;
import com.intellij.designer.designSurface.selection.ResizePoint;
import com.intellij.designer.designSurface.selection.ResizeSelectionDecorator;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.utils.Position;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Pair;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.annotations.NotNull;
@@ -46,15 +49,7 @@ import java.util.List;
public class RadFrameLayout extends RadViewLayoutWithData {
private static final String[] LAYOUT_PARAMS = {"FrameLayout_Layout", "ViewGroup_MarginLayout"};
private final ResizeSelectionDecorator mySelectionDecorator = new ResizeSelectionDecorator(Color.red, 1) {
@Override
protected boolean visible(RadComponent component, ResizePoint point) {
if (point.getType() == FrameLayoutMarginOperation.TYPE) {
return FrameLayoutMarginOperation.visible(component, (DirectionResizePoint)point);
}
return true;
}
};
private ResizeSelectionDecorator mySelectionDecorator;
@Override
@NotNull
@@ -73,19 +68,46 @@ public class RadFrameLayout extends RadViewLayoutWithData {
else if (context.is(ResizeOperation.TYPE)) {
return new ResizeOperation(context);
}
else if (context.is(FrameLayoutMarginOperation.TYPE)) {
return new FrameLayoutMarginOperation(context);
else if (context.is(LayoutMarginOperation.TYPE)) {
return new LayoutMarginOperation(context);
}
return null;
}
@Override
public ComponentDecorator getChildSelectionDecorator(RadComponent component, List<RadComponent> selection) {
if (mySelectionDecorator == null) {
mySelectionDecorator = new ResizeSelectionDecorator(Color.red, 1) {
@Override
protected boolean visible(RadComponent component, ResizePoint point) {
if (point.getType() == LayoutMarginOperation.TYPE) {
Pair<Gravity, Gravity> gravity = Gravity.getSides(component);
int direction = ((DirectionResizePoint)point).getDirection();
if (direction == Position.WEST) { // left
return gravity.first == Gravity.left || gravity.first == Gravity.center;
}
if (direction == Position.EAST) { // right
return gravity.first == Gravity.right || gravity.first == Gravity.center;
}
if (direction == Position.NORTH) { // top
return gravity.second == Gravity.top || gravity.second == Gravity.center;
}
if (direction == Position.SOUTH) { // bottom
return gravity.second == Gravity.bottom || gravity.second == Gravity.center;
}
}
return true;
}
};
}
mySelectionDecorator.clear();
if (selection.size() == 1) {
FrameLayoutMarginOperation.points(mySelectionDecorator);
LayoutMarginOperation.points(mySelectionDecorator);
}
ResizeOperation.points(mySelectionDecorator);
return mySelectionDecorator;
}
@@ -118,7 +140,7 @@ public class RadFrameLayout extends RadViewLayoutWithData {
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
myGravity = gravity(selection.get(0));
myGravity = Gravity.getSides(selection.get(0));
return super.createPopupActionGroup(button);
}
@@ -159,38 +181,9 @@ public class RadFrameLayout extends RadViewLayoutWithData {
};
Presentation presentation = action.getTemplatePresentation();
presentation.setDescription("Gravity");
presentation.setIcon(IconLoader.getIcon("/com/intellij/android/designer/icons/gravity.png"));
presentation.setIcon(Gravity.ICON);
action.setItems(ITEMS, null);
actionGroup.add(action);
}
public static Pair<Gravity, Gravity> gravity(RadComponent component) {
String value = ((RadViewComponent)component).getTag().getAttributeValue("android:layout_gravity");
int flags = Gravity.getFlags(value);
Gravity horizontal = Gravity.left;
if ((flags & Gravity.LEFT) != 0) {
horizontal = Gravity.left;
}
else if ((flags & Gravity.CENTER_HORIZONTAL) != 0) {
horizontal = Gravity.center;
}
else if ((flags & Gravity.RIGHT) != 0) {
horizontal = Gravity.right;
}
Gravity vertical = Gravity.top;
if ((flags & Gravity.TOP) != 0) {
vertical = Gravity.top;
}
else if ((flags & Gravity.CENTER_VERTICAL) != 0) {
vertical = Gravity.center;
}
else if ((flags & Gravity.BOTTOM) != 0) {
vertical = Gravity.bottom;
}
return Pair.create(horizontal, vertical);
}
}
@@ -17,19 +17,32 @@ package com.intellij.android.designer.model.layout;
import com.intellij.android.designer.designSurface.TreeDropToOperation;
import com.intellij.android.designer.designSurface.layout.FlowStaticDecorator;
import com.intellij.android.designer.designSurface.layout.LayoutMarginOperation;
import com.intellij.android.designer.designSurface.layout.LinearLayoutOperation;
import com.intellij.android.designer.designSurface.layout.ResizeOperation;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.android.designer.model.RadViewLayoutWithData;
import com.intellij.designer.actions.AbstractComboBoxAction;
import com.intellij.designer.designSurface.*;
import com.intellij.designer.designSurface.selection.DirectionResizePoint;
import com.intellij.designer.designSurface.selection.ResizePoint;
import com.intellij.designer.designSurface.selection.ResizeSelectionDecorator;
import com.intellij.designer.model.RadComponent;
import com.intellij.designer.utils.Position;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.util.Pair;
import com.intellij.util.ThrowableRunnable;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
@@ -38,13 +51,11 @@ import java.util.List;
public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDecorator {
private static final String[] LAYOUT_PARAMS = {"LinearLayout_Layout", "ViewGroup_MarginLayout"};
private final ResizeSelectionDecorator mySelectionDecorator = new ResizeSelectionDecorator(Color.red, 1) {
@Override
protected boolean visible(RadComponent component, ResizePoint point) {
// XXX
return true;
}
};
private static final Icon myHorizontalIcon = IconLoader.getIcon("/com/intellij/android/designer/icons/LinearLayout.png");
private static final Icon myVerticalIcon = IconLoader.getIcon("/com/intellij/android/designer/icons/LinearLayout2.png");
private static final Icon myHorizontalOverrideIcon = IconLoader.getIcon("/com/intellij/android/designer/icons/LinearLayout3.png");
private ResizeSelectionDecorator mySelectionDecorator;
private FlowStaticDecorator myLineDecorator;
@Override
@@ -53,6 +64,10 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec
return LAYOUT_PARAMS;
}
private boolean isHorizontal() {
return !"vertical".equals(((RadViewComponent)myContainer).getTag().getAttributeValue("android:orientation"));
}
@Override
public EditOperation processChildOperation(OperationContext context) {
if (context.isCreate() || context.isPaste() || context.isAdd() || context.isMove()) {
@@ -64,6 +79,9 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec
else if (context.is(ResizeOperation.TYPE)) {
return new ResizeOperation(context);
}
else if (context.is(LayoutMarginOperation.TYPE)) {
return new LayoutMarginOperation(context);
}
return null;
}
@@ -79,10 +97,6 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec
return myLineDecorator;
}
private boolean isHorizontal() {
return !"vertical".equals(((RadViewComponent)myContainer).getTag().getAttributeValue("android:orientation"));
}
@Override
public void addStaticDecorators(List<StaticDecorator> decorators, List<RadComponent> selection) {
if (selection.contains(myContainer)) {
@@ -103,27 +117,207 @@ public class RadLinearLayout extends RadViewLayoutWithData implements ILayoutDec
@Override
public ComponentDecorator getChildSelectionDecorator(RadComponent component, List<RadComponent> selection) {
if (mySelectionDecorator == null) {
mySelectionDecorator = new ResizeSelectionDecorator(Color.red, 1) {
@Override
protected boolean visible(RadComponent component, ResizePoint point) {
if (point.getType() == LayoutMarginOperation.TYPE) {
boolean horizontal = isHorizontal();
Pair<Gravity, Gravity> gravity = Gravity.getSides(component);
int direction = ((DirectionResizePoint)point).getDirection();
if (direction == Position.WEST) { // left
return horizontal || gravity.first != Gravity.right;
}
if (direction == Position.EAST) { // right
return horizontal || gravity.first != Gravity.left;
}
if (direction == Position.NORTH) { // top
return !horizontal || gravity.second != Gravity.bottom;
}
if (direction == Position.SOUTH) { // bottom
return !horizontal || gravity.second != Gravity.top;
}
}
return true;
}
};
}
mySelectionDecorator.clear();
if (selection.size() == 1) {
// XXX
LayoutMarginOperation.points(mySelectionDecorator);
}
ResizeOperation.points(mySelectionDecorator);
return mySelectionDecorator;
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Actions
//
//////////////////////////////////////////////////////////////////////////////////////////
@Override
public void addContainerSelectionActions(DesignerEditorPanel designer,
DefaultActionGroup actionGroup,
JComponent shortcuts,
List<RadComponent> selection) {
super.addContainerSelectionActions(designer, actionGroup, shortcuts, selection); // TODO: Auto-generated method stub
if (selection.get(selection.size() - 1) != myContainer) {
return;
}
for (RadComponent component : selection) {
if (!(component.getLayout() instanceof RadLinearLayout)) {
return;
}
}
createOrientationAction(designer, actionGroup, shortcuts, selection);
}
private static final List<Gravity> HORIZONTALS = Arrays.asList(Gravity.left, Gravity.center, Gravity.right, null);
private static final List<Gravity> VERTICALS = Arrays.asList(Gravity.top, Gravity.center, Gravity.bottom, null);
@Override
public void addSelectionActions(DesignerEditorPanel designer,
public void addSelectionActions(final DesignerEditorPanel designer,
DefaultActionGroup actionGroup,
JComponent shortcuts,
List<RadComponent> selection) {
super.addSelectionActions(designer, actionGroup, shortcuts, selection); // TODO: Auto-generated method stub
final List<RadComponent> selection) {
for (RadComponent component : selection) {
if (component.getParent() != myContainer) {
return;
}
}
createOrientationAction(designer, actionGroup, shortcuts, Arrays.asList(myContainer));
AbstractComboBoxAction<Gravity> action = new AbstractComboBoxAction<Gravity>() {
private Gravity mySelection;
@NotNull
@Override
protected DefaultActionGroup createPopupActionGroup(JComponent button) {
boolean horizontal = isHorizontal();
Gravity unknown = horizontal ? Gravity.left : Gravity.top;
setItems(horizontal ? VERTICALS : HORIZONTALS, unknown);
Iterator<RadComponent> I = selection.iterator();
mySelection = LinearLayoutOperation.getGravity(horizontal, I.next());
while (I.hasNext()) {
if (mySelection != LinearLayoutOperation.getGravity(horizontal, I.next())) {
mySelection = unknown;
break;
}
}
return super.createPopupActionGroup(button);
}
@Override
protected void update(Gravity item, Presentation presentation, boolean popup) {
if (popup) {
presentation.setIcon(mySelection == item ? CHECKED : null);
presentation.setText(item == null ? "fill" : item.name());
}
}
@Override
protected boolean selectionChanged(final Gravity item) {
designer.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
LinearLayoutOperation.execute(isHorizontal(), item, selection);
}
});
}
}, "Change attribute 'gravity'", true);
return false;
}
@Override
public void update() {
}
};
Presentation presentation = action.getTemplatePresentation();
presentation.setDescription("Gravity");
presentation.setIcon(Gravity.ICON);
actionGroup.add(action);
}
private static void createOrientationAction(DesignerEditorPanel designer,
DefaultActionGroup actionGroup,
JComponent shortcuts,
List<RadComponent> components) {
boolean override = false;
Iterator<RadComponent> I = components.iterator();
boolean horizontal = ((RadLinearLayout)I.next().getLayout()).isHorizontal();
while (I.hasNext()) {
boolean next = ((RadLinearLayout)I.next().getLayout()).isHorizontal();
if (horizontal != next) {
override = true;
break;
}
}
actionGroup.add(new OrientationAction(designer, components, horizontal, override));
}
private static class OrientationAction extends AnAction {
private final DesignerEditorPanel myDesigner;
private final List<RadComponent> myComponents;
private boolean mySelection;
public OrientationAction(DesignerEditorPanel designer, List<RadComponent> components, boolean horizontal, boolean override) {
myDesigner = designer;
myComponents = components;
mySelection = horizontal;
update(getTemplatePresentation(), override);
}
@Override
public void actionPerformed(AnActionEvent e) {
mySelection = !mySelection;
update(e.getPresentation(), false);
myDesigner.getToolProvider().execute(new ThrowableRunnable<Exception>() {
@Override
public void run() throws Exception {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
String value = mySelection ? "horizontal" : "vertical";
for (RadComponent component : myComponents) {
((RadViewComponent)component).getTag().setAttribute("android:orientation", value);
}
}
});
}
}, "Change attribute 'orientation'", true);
}
public void update(Presentation presentation, boolean override) {
String text;
Icon icon;
if (override) {
text = "Override orientation to horizontal";
icon = myHorizontalOverrideIcon;
}
else {
text = "Convert orientation to " + (mySelection ? "vertical" : "horizontal");
icon = mySelection ? myHorizontalIcon : myVerticalIcon;
}
presentation.setText(text);
presentation.setDescription(text);
presentation.setIcon(icon);
}
}
}
@@ -43,6 +43,7 @@
<meta model="com.intellij.android.designer.model.RadViewComponent"
class="android.widget.TextView"
tag="TextView">
<presentation title=" - &quot;%text%&quot;"/>
<palette title="TextView" icon="/com/intellij/android/designer/icons/TextView.png"
@@ -67,6 +68,7 @@
<meta model="com.intellij.android.designer.model.RadViewComponent"
class="android.widget.Button"
tag="Button">
<presentation title=" - &quot;%text%&quot;"/>
<palette title="Button" icon="/com/intellij/android/designer/icons/Button.png"
@@ -988,6 +990,8 @@
class="android.widget.LinearLayout"
tag="LinearLayout">
<presentation title=" (%orientation%)"/>
<palette title="LinearLayout" icon="/com/intellij/android/designer/icons/LinearLayout.png"
tooltip="A Layout that arranges its children in a single column or a single row."/>
@@ -26,7 +26,6 @@ import org.jetbrains.annotations.NonNls;
import javax.swing.*;
import java.util.List;
import java.util.Set;
/**
* @author Alexander Lobas
@@ -120,8 +119,7 @@ public class DesignerActionPanel implements DataProvider {
}
myDynamicGroup.removeAll();
Set<RadComponent> parents = RadComponent.getParents(selection);
for (RadComponent parent : parents) {
for (RadComponent parent : RadComponent.getParents(selection)) {
parent.getLayout().addSelectionActions(myDesigner, myDynamicGroup, myShortcuts, selection);
}
for (RadComponent component : selection) {