blob: c92736a6d7911e1f05ca8b4310c712e806f3962f [file] [log] [blame]
/*
* Copyright (C) 2015 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.android.tools.idea.uibuilder.handlers.relative;
import com.android.annotations.NonNull;
import com.android.tools.idea.uibuilder.graphics.NlDrawingStyle;
import com.android.tools.idea.uibuilder.graphics.NlGraphics;
import com.android.tools.idea.uibuilder.handlers.relative.DependencyGraph.Constraint;
import com.android.tools.idea.uibuilder.model.Insets;
import com.android.tools.idea.uibuilder.model.NlComponent;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.android.tools.idea.uibuilder.graphics.NlDrawingStyle.*;
/**
* 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 {
private static final AlphaComposite ALPHA_COMPOSITE = AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.5f);
public static void paint(@NonNull NlGraphics g, GuidelineHandler myState) {
g.useStyle(DRAGGED);
for (NlComponent dragged : myState.myDraggedNodes) {
if (dragged.w > 0 && dragged.h > 0) {
g.fillRect(dragged.x, dragged.y, dragged.w, dragged.h);
}
}
Set<NlComponent> horizontalDeps = myState.myHorizontalDeps;
Set<NlComponent> verticalDeps = myState.myVerticalDeps;
Set<NlComponent> deps = new HashSet<NlComponent>(horizontalDeps.size() + verticalDeps.size());
deps.addAll(horizontalDeps);
deps.addAll(verticalDeps);
if (deps.size() > 0) {
g.useStyle(DEPENDENCY);
for (NlComponent n : deps) {
// Don't highlight the selected nodes themselves
if (myState.myDraggedNodes.contains(n)) {
continue;
}
g.fillRect(n.x, n.y, n.w, n.h);
}
}
// 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(NlDrawingStyle.PADDING_BOUNDS);
NlComponent layout = myState.layout;
Insets padding = layout.getPadding();
g.drawRect(layout.x + padding.left,
layout.y + padding.top,
Math.max(0, layout.w - padding.left - padding.right),
Math.max(0, layout.h - padding.top - padding.bottom));
}
if (myState.myBounds != null) {
Rectangle bounds = myState.myBounds;
if (myState instanceof RelativeDragHandler) {
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(myState, g, myState.myHorizontalCycle);
}
if (myState.myVerticalCycle != null) {
paintCycle(myState, g, myState.myVerticalCycle);
}
}
/**
* Paints a particular match constraint
*/
private static void showMatch(NlGraphics 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
*/
private static void paintCycle(GuidelineHandler myState, NlGraphics g, List<Constraint> cycle) {
assert cycle.size() > 0;
NlComponent from = cycle.get(0).from.node;
assert from != null;
Rectangle fromBounds = new Rectangle(from.x, from.y, from.w, from.h);
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;
NlComponent to = constraint.to.node;
assert to != null;
Point toCenter = new Point(to.x + to.w / 2, to.y + to.h / 2);
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);
}
}
}