• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.ide.common.layout.grid;
17 
18 import static com.android.ide.common.layout.GridLayoutRule.GRID_SIZE;
19 import static com.android.ide.common.layout.GridLayoutRule.MARGIN_SIZE;
20 import static com.android.ide.common.layout.grid.GridModel.UNDEFINED;
21 
22 import com.android.ide.common.api.DrawingStyle;
23 import com.android.ide.common.api.DropFeedback;
24 import com.android.ide.common.api.IDragElement;
25 import com.android.ide.common.api.IFeedbackPainter;
26 import com.android.ide.common.api.IGraphics;
27 import com.android.ide.common.api.INode;
28 import com.android.ide.common.api.Rect;
29 import com.android.ide.common.api.SegmentType;
30 import com.android.ide.common.layout.GridLayoutRule;
31 import com.android.util.Pair;
32 
33 /**
34  * Painter which paints feedback during drag, drop and resizing operations, as well as
35  * static selection feedback
36  */
37 public class GridLayoutPainter {
38 
39     /**
40      * Creates a painter for drop feedback
41      *
42      * @param rule the corresponding {@link GridLayoutRule}
43      * @param elements the dragged elements
44      * @return a {@link IFeedbackPainter} which can paint the drop feedback
45      */
createDropFeedbackPainter(GridLayoutRule rule, IDragElement[] elements)46     public static IFeedbackPainter createDropFeedbackPainter(GridLayoutRule rule,
47             IDragElement[] elements) {
48         return new DropFeedbackPainter(rule, elements);
49     }
50 
51     /**
52      * Paints the structure (the grid model) of the given GridLayout.
53      *
54      * @param style the drawing style to use to paint the structure lines
55      * @param layout the grid layout node
56      * @param gc the graphics context to paint into
57      * @param grid the grid model to be visualized
58      */
paintStructure(DrawingStyle style, INode layout, IGraphics gc, GridModel grid)59     public static void paintStructure(DrawingStyle style, INode layout, IGraphics gc,
60             GridModel grid) {
61         Rect b = layout.getBounds();
62 
63         gc.useStyle(style);
64         for (int row = 0; row < grid.actualRowCount; row++) {
65             int y = grid.getRowY(row);
66             gc.drawLine(b.x, y, b.x2(), y);
67         }
68         for (int column = 0; column < grid.actualColumnCount; column++) {
69             int x = grid.getColumnX(column);
70             gc.drawLine(x, b.y, x, b.y2());
71         }
72     }
73 
74     /**
75      * Paints a regular grid according to the {@link GridLayoutRule#GRID_SIZE} and
76      * {@link GridLayoutRule#MARGIN_SIZE} dimensions. These are the same lines that
77      * snap-to-grid will align with.
78      *
79      * @param layout the GridLayout node
80      * @param gc the graphics context to paint the grid into
81      */
paintGrid(INode layout, IGraphics gc)82     public static void paintGrid(INode layout, IGraphics gc) {
83         Rect b = layout.getBounds();
84 
85         int oldAlpha = gc.getAlpha();
86         gc.useStyle(DrawingStyle.GUIDELINE);
87         gc.setAlpha(128);
88 
89         int y1 = b.y + MARGIN_SIZE;
90         int y2 = b.y2() - MARGIN_SIZE;
91         for (int y = y1; y < y2; y += GRID_SIZE) {
92             int x1 = b.x + MARGIN_SIZE;
93             int x2 = b.x2() - MARGIN_SIZE;
94             for (int x = x1; x < x2; x += GRID_SIZE) {
95                 gc.drawPoint(x, y);
96             }
97         }
98         gc.setAlpha(oldAlpha);
99     }
100 
101     /**
102      * Paint resizing feedback (which currently paints the grid model faintly.)
103      *
104      * @param gc the graphics context
105      * @param layout the GridLayout
106      * @param grid the grid model
107      */
paintResizeFeedback(IGraphics gc, INode layout, GridModel grid)108     public static void paintResizeFeedback(IGraphics gc, INode layout, GridModel grid) {
109         paintStructure(DrawingStyle.GRID, layout, gc, grid);
110     }
111 
112     /**
113      * A painter which can paint the drop feedback for elements being dragged into or
114      * within a GridLayout.
115      */
116     private static class DropFeedbackPainter implements IFeedbackPainter {
117         private final GridLayoutRule mRule;
118         private final IDragElement[] mElements;
119 
120         /** Constructs a new {@link GridLayoutPainter} bound to the given {@link GridLayoutRule}
121          * @param rule the corresponding rule
122          * @param elements the elements to draw */
DropFeedbackPainter(GridLayoutRule rule, IDragElement[] elements)123         public DropFeedbackPainter(GridLayoutRule rule, IDragElement[] elements) {
124             mRule = rule;
125             mElements = elements;
126         }
127 
128         // Implements IFeedbackPainter
paint(IGraphics gc, INode node, DropFeedback feedback)129         public void paint(IGraphics gc, INode node, DropFeedback feedback) {
130             Rect b = node.getBounds();
131             if (!b.isValid()) {
132                 return;
133             }
134 
135             // Highlight the receiver
136             gc.useStyle(DrawingStyle.DROP_RECIPIENT);
137             gc.drawRect(b);
138             GridDropHandler data = (GridDropHandler) feedback.userData;
139 
140             if (!GridLayoutRule.sGridMode) {
141                 paintFreeFormDropFeedback(gc, node, feedback, b, data);
142             } else {
143                 paintGridModeDropFeedback(gc, b, data);
144             }
145         }
146 
147         /**
148          * Paints the drag feedback for a free-form mode drag
149          */
paintFreeFormDropFeedback(IGraphics gc, INode node, DropFeedback feedback, Rect b, GridDropHandler data)150         private void paintFreeFormDropFeedback(IGraphics gc, INode node, DropFeedback feedback,
151                 Rect b, GridDropHandler data) {
152             GridModel grid = data.getGrid();
153             if (GridLayoutRule.sSnapToGrid) {
154                 GridLayoutPainter.paintGrid(node, gc);
155             }
156             GridLayoutPainter.paintStructure(DrawingStyle.GRID, node, gc, grid);
157 
158             GridMatch rowMatch = data.getRowMatch();
159             GridMatch columnMatch = data.getColumnMatch();
160 
161             if (rowMatch == null || columnMatch == null) {
162                 return;
163             }
164 
165             IDragElement first = mElements[0];
166             Rect dragBounds = first.getBounds();
167             int offsetX = 0;
168             int offsetY = 0;
169             if (rowMatch.type == SegmentType.BOTTOM) {
170                 offsetY -= dragBounds.h;
171             } else if (rowMatch.type == SegmentType.BASELINE) {
172                 offsetY -= feedback.dragBaseline;
173             }
174             if (columnMatch.type == SegmentType.RIGHT) {
175                 offsetX -= dragBounds.w;
176             } else if (columnMatch.type == SegmentType.CENTER_HORIZONTAL) {
177                 offsetX -= dragBounds.centerX();
178             }
179 
180             // Draw guidelines for matches
181             int y = rowMatch.matchedLine;
182             int x = columnMatch.matchedLine;
183             Rect bounds = first.getBounds();
184 
185             // Draw margin
186             if (rowMatch.margin != UNDEFINED && rowMatch.margin > 0) {
187                 gc.useStyle(DrawingStyle.DISTANCE);
188                 int centerX = bounds.w / 2 + offsetX + x;
189                 int y1;
190                 int y2;
191                 if (rowMatch.type == SegmentType.TOP) {
192                     y1 = offsetY + y - 1;
193                     y2 = rowMatch.matchedLine - rowMatch.margin;
194                 } else {
195                     assert rowMatch.type == SegmentType.BOTTOM;
196                     y1 = bounds.h + offsetY + y - 1;
197                     y2 = rowMatch.matchedLine + rowMatch.margin;
198                 }
199                 gc.drawLine(b.x, y1, b.x2(), y1);
200                 gc.drawLine(b.x, y2, b.x2(), y2);
201                 gc.drawString(Integer.toString(rowMatch.margin),
202                         centerX - 3, y1 + (y2 - y1 - 16) / 2);
203             } else {
204                 gc.useStyle(rowMatch.margin == 0 ? DrawingStyle.DISTANCE
205                         : rowMatch.createCell ? DrawingStyle.GUIDELINE_DASHED
206                                 : DrawingStyle.GUIDELINE);
207                 gc.drawLine(b.x, y, b.x2(), y );
208             }
209 
210             if (columnMatch.margin != UNDEFINED && columnMatch.margin > 0) {
211                 gc.useStyle(DrawingStyle.DISTANCE);
212                 int centerY = bounds.h / 2 + offsetY + y;
213                 int x1;
214                 int x2;
215                 if (columnMatch.type == SegmentType.LEFT) {
216                     x1 = offsetX + x - 1;
217                     x2 = columnMatch.matchedLine - columnMatch.margin;
218                 } else {
219                     assert columnMatch.type == SegmentType.RIGHT;
220                     x1 = bounds.w + offsetX + x - 1;
221                     x2 = columnMatch.matchedLine + columnMatch.margin;
222                 }
223                 gc.drawLine(x1, b.y, x1, b.y2());
224                 gc.drawLine(x2, b.y, x2, b.y2());
225                 gc.drawString(Integer.toString(columnMatch.margin),
226                         x1 + (x2 - x1 - 16) / 2, centerY - 3);
227             } else {
228                 gc.useStyle(columnMatch.margin == 0 ? DrawingStyle.DISTANCE
229                         : columnMatch.createCell ? DrawingStyle.GUIDELINE_DASHED
230                                 : DrawingStyle.GUIDELINE);
231                 gc.drawLine(x, b.y, x, b.y2());
232             }
233 
234             // Draw preview rectangle of the first dragged element
235             gc.useStyle(DrawingStyle.DROP_PREVIEW);
236             mRule.drawElement(gc, first, x + offsetX - bounds.x, y + offsetY - bounds.y);
237 
238             // Preview baseline as well
239             if (feedback.dragBaseline != -1) {
240                 int x1 = dragBounds.x + x + offsetX - bounds.x;
241                 int y1 = dragBounds.y + y + offsetY - bounds.y + feedback.dragBaseline;
242                 gc.drawLine(x1, y1, x1 + dragBounds.w, y1);
243             }
244         }
245 
246         /**
247          * Paints the drag feedback for a grid-mode drag
248          */
paintGridModeDropFeedback(IGraphics gc, Rect b, GridDropHandler data)249         private void paintGridModeDropFeedback(IGraphics gc, Rect b, GridDropHandler data) {
250             int radius = mRule.getNewCellSize();
251             GridModel grid = data.getGrid();
252 
253             gc.useStyle(DrawingStyle.GUIDELINE);
254             for (int row = 1; row < grid.actualRowCount; row++) {
255                 int y = grid.getRowY(row);
256                 gc.drawLine(b.x, y - radius, b.x2(), y - radius);
257                 gc.drawLine(b.x, y + radius, b.x2(), y + radius);
258 
259             }
260             for (int column = 1; column < grid.actualColumnCount; column++) {
261                 int x = grid.getColumnX(column);
262                 gc.drawLine(x - radius, b.y, x - radius, b.y2());
263                 gc.drawLine(x + radius, b.y, x + radius, b.y2());
264             }
265             gc.drawRect(b.x, b.y, b.x2(), b.y2());
266             gc.drawRect(b.x + 2 * radius, b.y + 2 * radius,
267                     b.x2() - 2 * radius, b.y2() - 2 * radius);
268 
269             int column = data.getColumnMatch().cellIndex;
270             int row = data.getRowMatch().cellIndex;
271             boolean createColumn = data.getColumnMatch().createCell;
272             boolean createRow = data.getRowMatch().createCell;
273 
274             Rect cellBounds = grid.getCellBounds(row, column, 1, 1);
275 
276             IDragElement first = mElements[0];
277             Rect dragBounds = first.getBounds();
278             int offsetX = cellBounds.x - dragBounds.x;
279             int offsetY = cellBounds.y - dragBounds.y;
280 
281             gc.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
282             if (createColumn) {
283                 gc.fillRect(new Rect(cellBounds.x - radius,
284                         cellBounds.y + (createRow ? -radius : radius),
285                         2 * radius + 1, cellBounds.h - (createRow ? 0 : 2 * radius)));
286                 offsetX -= radius + dragBounds.w / 2;
287             }
288             if (createRow) {
289                 gc.fillRect(new Rect(cellBounds.x + radius, cellBounds.y - radius,
290                         cellBounds.w - 2 * radius, 2 * radius + 1));
291                 offsetY -= radius + dragBounds.h / 2;
292             } else if (!createColumn) {
293                 // Choose this cell
294                 gc.fillRect(new Rect(cellBounds.x + radius, cellBounds.y + radius,
295                         cellBounds.w - 2 * radius, cellBounds.h - 2 * radius));
296             }
297 
298             gc.useStyle(DrawingStyle.DROP_PREVIEW);
299             mRule.drawElement(gc, first, offsetX, offsetY);
300         }
301     }
302 
303     /**
304      * Paints the structure (the row and column boundaries) of the given GridLayout
305      *
306      * @param view the instance of the GridLayout whose structure should be painted
307      * @param style the drawing style to use for the cell boundaries
308      * @param layout the layout element
309      * @param gc the graphics context
310      */
paintStructure(Object view, DrawingStyle style, INode layout, IGraphics gc)311     public static void paintStructure(Object view, DrawingStyle style, INode layout,
312             IGraphics gc) {
313         Pair<int[],int[]> cellBounds = GridModel.getAxisBounds(view);
314         if (cellBounds != null) {
315             int[] xs = cellBounds.getFirst();
316             int[] ys = cellBounds.getSecond();
317             Rect b = layout.getBounds();
318             gc.useStyle(style);
319             for (int row = 0; row < ys.length; row++) {
320                 int y = ys[row] + b.y;
321                 gc.drawLine(b.x, y, b.x2(), y);
322             }
323             for (int column = 0; column < xs.length; column++) {
324                 int x = xs[column] + b.x;
325                 gc.drawLine(x, b.y, x, b.y2());
326             }
327         }
328     }
329 }
330