blob: f459bfb1385104779603afed13cfbe79258a9fe3 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.intellij.android.designer.model.layout.relative;
import com.intellij.android.designer.designSurface.RootView;
import com.intellij.android.designer.designSurface.graphics.DesignerGraphics;
import com.intellij.android.designer.designSurface.graphics.DrawingStyle;
import com.intellij.android.designer.model.RadViewComponent;
import com.intellij.designer.model.RadComponent;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.intellij.android.designer.designSurface.graphics.DrawingStyle.*;
import static com.intellij.android.designer.model.layout.relative.DependencyGraph.Constraint;
/**
* The {@link GuidelinePainter} is responsible for painting guidelines during an operation
* which uses a {@link GuidelineHandler} such as a resize operation.
*/
public final class GuidelinePainter extends JComponent {
private static final AlphaComposite ALPHA_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f);
@NotNull private final GuidelineHandler myState;
public GuidelinePainter(@NotNull GuidelineHandler state) {
myState = state;
}
@Override
protected void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
DesignerGraphics g = new DesignerGraphics(graphics, this);
paint(g);
}
private void paint(@NotNull DesignerGraphics g) {
g.useStyle(DRAGGED);
for (RadViewComponent dragged : myState.myDraggedNodes) {
Rectangle bounds = dragged.getBounds(g.getTarget());
if (!bounds.isEmpty()) {
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
Set<RadViewComponent> horizontalDeps = myState.myHorizontalDeps;
Set<RadViewComponent> verticalDeps = myState.myVerticalDeps;
Set<RadViewComponent> deps = new HashSet<RadViewComponent>(horizontalDeps.size() + verticalDeps.size());
deps.addAll(horizontalDeps);
deps.addAll(verticalDeps);
if (deps.size() > 0) {
g.useStyle(DEPENDENCY);
for (RadViewComponent n : deps) {
// Don't highlight the selected nodes themselves
if (myState.myDraggedNodes.contains(n)) {
continue;
}
Rectangle bounds = n.getBounds(g.getTarget());
g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
}
// If the layout has padding applied, draw the padding bounds to make it obvious what the boundaries are
if (!myState.layout.getPadding().isEmpty() || !myState.layout.getMargins().isEmpty()) {
g.useStyle(DrawingStyle.PADDING_BOUNDS);
JComponent target = myState.myContext.getArea().getFeedbackLayer();
Rectangle bounds = myState.layout.getPaddedBounds(target);
g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
}
if (myState.myBounds != null) {
Rectangle bounds = myState.myBounds;
if (myState instanceof MoveHandler) {
List<RadComponent> myComponents = myState.getContext().getComponents();
if (myComponents.size() == 1) {
RadComponent component = myComponents.get(0);
final Rectangle targetBounds = component.getBounds(g.getTarget());
RootView nativeComponent = (RootView)((RadViewComponent)component.getRoot()).getNativeComponent();
if (nativeComponent != null) {
final BufferedImage image = nativeComponent.getImage();
final Rectangle sourceBounds = component.getBounds();
Graphics2D g2d = (Graphics2D)g.getGraphics();
Composite prevComposite = g2d.getComposite();
try {
if (image != null) {
g2d.setComposite(ALPHA_COMPOSITE);
g2d.drawImage(image, bounds.x, bounds.y, bounds.x + targetBounds.width, bounds.y + targetBounds.height, sourceBounds.x,
sourceBounds.y, sourceBounds.x + sourceBounds.width, sourceBounds.y + sourceBounds.height, null);
}
} finally {
g2d.setComposite(prevComposite);
}
}
}
g.useStyle(DROP_PREVIEW);
}
else {
// Resizing
if (myState.haveSuggestions()) {
g.useStyle(RESIZE_PREVIEW);
}
else {
g.useStyle(RESIZE_FAIL);
}
}
g.drawRect(bounds.x, bounds.y, bounds.width, bounds.height);
// Draw baseline preview too
// TODO: Implement when we have drag information from palette drag previews
//if (myFeedback.dragBaseline != -1) {
// int y = myState.myBounds.y + myFeedback.dragBaseline;
// g.drawLine(myState.myBounds.x, y, myState.myBounds.x + myState.myBounds.width, y);
//}
}
showMatch(g, myState.getCurrentLeftMatch(), myState);
showMatch(g, myState.getCurrentRightMatch(), myState);
showMatch(g, myState.getCurrentTopMatch(), myState);
showMatch(g, myState.getCurrentBottomMatch(), myState);
if (myState.myHorizontalCycle != null) {
paintCycle(g, myState.myHorizontalCycle);
}
if (myState.myVerticalCycle != null) {
paintCycle(g, myState.myVerticalCycle);
}
}
/**
* Paints a particular match constraint
*/
private static void showMatch(DesignerGraphics g, Match m, GuidelineHandler state) {
if (m == null) {
return;
}
ConstraintPainter.paintConstraint(g, state, m);
}
private static Point center(Rectangle rectangle) {
return new Point(rectangle.x + rectangle.width / 2, rectangle.y + rectangle.height / 2);
}
/**
* Paints a constraint cycle
*/
void paintCycle(DesignerGraphics g, List<Constraint> cycle) {
assert cycle.size() > 0;
RadViewComponent from = cycle.get(0).from.node;
assert from != null;
Rectangle fromBounds = from.getBounds(g.getTarget());
if (myState.myDraggedNodes.contains(from)) {
fromBounds = myState.myBounds;
}
Point fromCenter = center(fromBounds);
List<Point> points = new ArrayList<Point>();
points.add(fromCenter);
for (Constraint constraint : cycle) {
assert constraint.from.node == from;
RadViewComponent to = constraint.to.node;
assert to != null;
Point toCenter = center(to.getBounds(g.getTarget()));
points.add(toCenter);
// Also go through the dragged node bounds
boolean isDragged = myState.myDraggedNodes.contains(to);
if (isDragged) {
toCenter = center(myState.myBounds);
points.add(toCenter);
}
from = to;
fromCenter = toCenter;
}
points.add(fromCenter);
points.add(points.get(0));
g.useStyle(CYCLE);
for (int i = 1, n = points.size(); i < n; i++) {
Point a = points.get(i - 1);
Point b = points.get(i);
g.drawLine(a.x, a.y, b.x, b.y);
}
}
}