• 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
129         @Override
paint(IGraphics gc, INode node, DropFeedback feedback)130         public void paint(IGraphics gc, INode node, DropFeedback feedback) {
131             Rect b = node.getBounds();
132             if (!b.isValid()) {
133                 return;
134             }
135 
136             // Highlight the receiver
137             gc.useStyle(DrawingStyle.DROP_RECIPIENT);
138             gc.drawRect(b);
139             GridDropHandler data = (GridDropHandler) feedback.userData;
140 
141             if (!GridLayoutRule.sGridMode) {
142                 paintFreeFormDropFeedback(gc, node, feedback, b, data);
143             } else {
144                 paintGridModeDropFeedback(gc, b, data);
145             }
146         }
147 
148         /**
149          * Paints the drag feedback for a free-form mode drag
150          */
paintFreeFormDropFeedback(IGraphics gc, INode node, DropFeedback feedback, Rect b, GridDropHandler data)151         private void paintFreeFormDropFeedback(IGraphics gc, INode node, DropFeedback feedback,
152                 Rect b, GridDropHandler data) {
153             GridModel grid = data.getGrid();
154             if (GridLayoutRule.sSnapToGrid) {
155                 GridLayoutPainter.paintGrid(node, gc);
156             }
157             GridLayoutPainter.paintStructure(DrawingStyle.GRID, node, gc, grid);
158 
159             GridMatch rowMatch = data.getRowMatch();
160             GridMatch columnMatch = data.getColumnMatch();
161 
162             if (rowMatch == null || columnMatch == null) {
163                 return;
164             }
165 
166             IDragElement first = mElements[0];
167             Rect dragBounds = first.getBounds();
168             int offsetX = 0;
169             int offsetY = 0;
170             if (rowMatch.type == SegmentType.BOTTOM) {
171                 offsetY -= dragBounds.h;
172             } else if (rowMatch.type == SegmentType.BASELINE) {
173                 offsetY -= feedback.dragBaseline;
174             }
175             if (columnMatch.type == SegmentType.RIGHT) {
176                 offsetX -= dragBounds.w;
177             } else if (columnMatch.type == SegmentType.CENTER_HORIZONTAL) {
178                 offsetX -= dragBounds.w / 2;
179             }
180 
181             // Draw guidelines for matches
182             int y = rowMatch.matchedLine;
183             int x = columnMatch.matchedLine;
184             Rect bounds = first.getBounds();
185 
186             // Draw margin
187             if (rowMatch.margin != UNDEFINED && rowMatch.margin > 0) {
188                 gc.useStyle(DrawingStyle.DISTANCE);
189                 int centerX = bounds.w / 2 + offsetX + x;
190                 int y1;
191                 int y2;
192                 if (rowMatch.type == SegmentType.TOP) {
193                     y1 = offsetY + y - 1;
194                     y2 = rowMatch.matchedLine - rowMatch.margin;
195                 } else {
196                     assert rowMatch.type == SegmentType.BOTTOM;
197                     y1 = bounds.h + offsetY + y - 1;
198                     y2 = rowMatch.matchedLine + rowMatch.margin;
199                 }
200                 gc.drawLine(b.x, y1, b.x2(), y1);
201                 gc.drawLine(b.x, y2, b.x2(), y2);
202                 gc.drawString(Integer.toString(rowMatch.margin),
203                         centerX - 3, y1 + (y2 - y1 - 16) / 2);
204             } else {
205                 gc.useStyle(rowMatch.margin == 0 ? DrawingStyle.DISTANCE
206                         : rowMatch.createCell ? DrawingStyle.GUIDELINE_DASHED
207                                 : DrawingStyle.GUIDELINE);
208                 gc.drawLine(b.x, y, b.x2(), y );
209             }
210 
211             if (columnMatch.margin != UNDEFINED && columnMatch.margin > 0) {
212                 gc.useStyle(DrawingStyle.DISTANCE);
213                 int centerY = bounds.h / 2 + offsetY + y;
214                 int x1;
215                 int x2;
216                 if (columnMatch.type == SegmentType.LEFT) {
217                     x1 = offsetX + x - 1;
218                     x2 = columnMatch.matchedLine - columnMatch.margin;
219                 } else {
220                     assert columnMatch.type == SegmentType.RIGHT;
221                     x1 = bounds.w + offsetX + x - 1;
222                     x2 = columnMatch.matchedLine + columnMatch.margin;
223                 }
224                 gc.drawLine(x1, b.y, x1, b.y2());
225                 gc.drawLine(x2, b.y, x2, b.y2());
226                 gc.drawString(Integer.toString(columnMatch.margin),
227                         x1 + (x2 - x1 - 16) / 2, centerY - 3);
228             } else {
229                 gc.useStyle(columnMatch.margin == 0 ? DrawingStyle.DISTANCE
230                         : columnMatch.createCell ? DrawingStyle.GUIDELINE_DASHED
231                                 : DrawingStyle.GUIDELINE);
232                 gc.drawLine(x, b.y, x, b.y2());
233             }
234 
235             // Draw preview rectangle of the first dragged element
236             gc.useStyle(DrawingStyle.DROP_PREVIEW);
237             mRule.drawElement(gc, first, x + offsetX - bounds.x, y + offsetY - bounds.y);
238 
239             // Preview baseline as well
240             if (feedback.dragBaseline != -1) {
241                 int x1 = dragBounds.x + x + offsetX - bounds.x;
242                 int y1 = dragBounds.y + y + offsetY - bounds.y + feedback.dragBaseline;
243                 gc.drawLine(x1, y1, x1 + dragBounds.w, y1);
244             }
245         }
246 
247         /**
248          * Paints the drag feedback for a grid-mode drag
249          */
paintGridModeDropFeedback(IGraphics gc, Rect b, GridDropHandler data)250         private void paintGridModeDropFeedback(IGraphics gc, Rect b, GridDropHandler data) {
251             int radius = mRule.getNewCellSize();
252             GridModel grid = data.getGrid();
253 
254             gc.useStyle(DrawingStyle.GUIDELINE);
255             // Paint grid
256             for (int row = 1; row < grid.actualRowCount; row++) {
257                 int y = grid.getRowY(row);
258                 gc.drawLine(b.x, y - radius, b.x2(), y - radius);
259                 gc.drawLine(b.x, y + radius, b.x2(), y + radius);
260 
261             }
262             for (int column = 1; column < grid.actualColumnCount; column++) {
263                 int x = grid.getColumnX(column);
264                 gc.drawLine(x - radius, b.y, x - radius, b.y2());
265                 gc.drawLine(x + radius, b.y, x + radius, b.y2());
266             }
267             gc.drawRect(b.x, b.y, b.x2(), b.y2());
268             gc.drawRect(b.x + 2 * radius, b.y + 2 * radius,
269                     b.x2() - 2 * radius, b.y2() - 2 * radius);
270 
271             int column = data.getColumnMatch().cellIndex;
272             int row = data.getRowMatch().cellIndex;
273             boolean createColumn = data.getColumnMatch().createCell;
274             boolean createRow = data.getRowMatch().createCell;
275 
276             Rect cellBounds = grid.getCellBounds(row, column, 1, 1);
277 
278             IDragElement first = mElements[0];
279             Rect dragBounds = first.getBounds();
280             int offsetX = cellBounds.x - dragBounds.x;
281             int offsetY = cellBounds.y - dragBounds.y;
282 
283             gc.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
284             if (createColumn) {
285                 gc.fillRect(new Rect(cellBounds.x - radius,
286                         cellBounds.y + (createRow ? -radius : radius),
287                         2 * radius + 1, cellBounds.h - (createRow ? 0 : 2 * radius)));
288                 offsetX -= radius + dragBounds.w / 2;
289             }
290             if (createRow) {
291                 gc.fillRect(new Rect(cellBounds.x + radius, cellBounds.y - radius,
292                         cellBounds.w - 2 * radius, 2 * radius + 1));
293                 offsetY -= radius + dragBounds.h / 2;
294             } else if (!createColumn) {
295                 // Choose this cell
296                 gc.fillRect(new Rect(cellBounds.x + radius, cellBounds.y + radius,
297                         cellBounds.w - 2 * radius, cellBounds.h - 2 * radius));
298             }
299 
300             gc.useStyle(DrawingStyle.DROP_PREVIEW);
301             mRule.drawElement(gc, first, offsetX, offsetY);
302         }
303     }
304 
305     /**
306      * Paints the structure (the row and column boundaries) of the given
307      * GridLayout
308      *
309      * @param view the instance of the GridLayout whose structure should be
310      *            painted
311      * @param style the drawing style to use for the cell boundaries
312      * @param layout the layout element
313      * @param gc the graphics context
314      * @return true if the structure was successfully inferred from the view and
315      *         painted
316      */
paintStructure(Object view, DrawingStyle style, INode layout, IGraphics gc)317     public static boolean paintStructure(Object view, DrawingStyle style, INode layout,
318             IGraphics gc) {
319         Pair<int[],int[]> cellBounds = GridModel.getAxisBounds(view);
320         if (cellBounds != null) {
321             int[] xs = cellBounds.getFirst();
322             int[] ys = cellBounds.getSecond();
323             Rect b = layout.getBounds();
324             gc.useStyle(style);
325             for (int row = 0; row < ys.length; row++) {
326                 int y = ys[row] + b.y;
327                 gc.drawLine(b.x, y, b.x2(), y);
328             }
329             for (int column = 0; column < xs.length; column++) {
330                 int x = xs[column] + b.x;
331                 gc.drawLine(x, b.y, x, b.y2());
332             }
333 
334             return true;
335         } else {
336             return false;
337         }
338     }
339 }
340