/* * 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); } } }