aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java676
1 files changed, 0 insertions, 676 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java
deleted file mode 100644
index 80a23c6db..000000000
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/GridLayoutRule.java
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
- *
- * 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.ide.common.layout;
-
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_LAYOUT_COLUMN;
-import static com.android.SdkConstants.ATTR_LAYOUT_GRAVITY;
-import static com.android.SdkConstants.ATTR_LAYOUT_ROW;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.FQCN_GRID_LAYOUT;
-import static com.android.SdkConstants.FQCN_SPACE;
-import static com.android.SdkConstants.FQCN_SPACE_V7;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL_HORIZONTAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_FILL_VERTICAL;
-import static com.android.SdkConstants.GRAVITY_VALUE_LEFT;
-import static com.android.SdkConstants.GRID_LAYOUT;
-import static com.android.SdkConstants.VALUE_HORIZONTAL;
-import static com.android.SdkConstants.VALUE_TRUE;
-
-import com.android.annotations.NonNull;
-import com.android.annotations.Nullable;
-import com.android.ide.common.api.DrawingStyle;
-import com.android.ide.common.api.DropFeedback;
-import com.android.ide.common.api.IDragElement;
-import com.android.ide.common.api.IFeedbackPainter;
-import com.android.ide.common.api.IGraphics;
-import com.android.ide.common.api.IMenuCallback;
-import com.android.ide.common.api.INode;
-import com.android.ide.common.api.INodeHandler;
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.common.api.IViewMetadata.FillPreference;
-import com.android.ide.common.api.IViewRule;
-import com.android.ide.common.api.InsertType;
-import com.android.ide.common.api.Point;
-import com.android.ide.common.api.Rect;
-import com.android.ide.common.api.RuleAction;
-import com.android.ide.common.api.RuleAction.Choices;
-import com.android.ide.common.api.SegmentType;
-import com.android.ide.common.layout.grid.GridDropHandler;
-import com.android.ide.common.layout.grid.GridLayoutPainter;
-import com.android.ide.common.layout.grid.GridModel;
-import com.android.ide.common.layout.grid.GridModel.ViewData;
-import com.android.utils.Pair;
-
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * An {@link IViewRule} for android.widget.GridLayout which provides designtime
- * interaction with GridLayouts.
- * <p>
- * TODO:
- * <ul>
- * <li>Handle multi-drag: preserving relative positions and alignments among dragged
- * views.
- * <li>Handle GridLayouts that have been configured in a vertical orientation.
- * <li>Handle free-form editing GridLayouts that have been manually edited rather than
- * built up using free-form editing (e.g. they might not follow the same spacing
- * convention, might use weights etc)
- * <li>Avoid setting row and column numbers on the actual elements if they can be skipped
- * to make the XML leaner.
- * </ul>
- */
-public class GridLayoutRule extends BaseLayoutRule {
- /**
- * The size of the visual regular grid that we snap to (if {@link #sSnapToGrid} is set
- */
- public static final int GRID_SIZE = 16;
-
- /** Standard gap between views */
- public static final int SHORT_GAP_DP = 16;
-
- /**
- * The preferred margin size, in pixels
- */
- public static final int MARGIN_SIZE = 32;
-
- /**
- * Size in screen pixels in the IDE of the gutter shown for new rows and columns (in
- * grid mode)
- */
- private static final int NEW_CELL_WIDTH = 10;
-
- /**
- * Maximum size of a widget relative to a cell which is allowed to fit into a cell
- * (and thereby enlarge it) before it is spread with row or column spans.
- */
- public static final double MAX_CELL_DIFFERENCE = 1.2;
-
- /** Whether debugging diagnostics is available in the toolbar */
- private static final boolean CAN_DEBUG =
- VALUE_TRUE.equals(System.getenv("ADT_DEBUG_GRIDLAYOUT")); //$NON-NLS-1$
-
- private static final String ACTION_ADD_ROW = "_addrow"; //$NON-NLS-1$
- private static final String ACTION_REMOVE_ROW = "_removerow"; //$NON-NLS-1$
- private static final String ACTION_ADD_COL = "_addcol"; //$NON-NLS-1$
- private static final String ACTION_REMOVE_COL = "_removecol"; //$NON-NLS-1$
- private static final String ACTION_ORIENTATION = "_orientation"; //$NON-NLS-1$
- private static final String ACTION_SHOW_STRUCTURE = "_structure"; //$NON-NLS-1$
- private static final String ACTION_GRID_MODE = "_gridmode"; //$NON-NLS-1$
- private static final String ACTION_SNAP = "_snap"; //$NON-NLS-1$
- private static final String ACTION_DEBUG = "_debug"; //$NON-NLS-1$
-
- private static final URL ICON_HORIZONTAL = GridLayoutRule.class.getResource("hlinear.png"); //$NON-NLS-1$
- private static final URL ICON_VERTICAL = GridLayoutRule.class.getResource("vlinear.png"); //$NON-NLS-1$
- private static final URL ICON_ADD_ROW = GridLayoutRule.class.getResource("addrow.png"); //$NON-NLS-1$
- private static final URL ICON_REMOVE_ROW = GridLayoutRule.class.getResource("removerow.png"); //$NON-NLS-1$
- private static final URL ICON_ADD_COL = GridLayoutRule.class.getResource("addcol.png"); //$NON-NLS-1$
- private static final URL ICON_REMOVE_COL = GridLayoutRule.class.getResource("removecol.png"); //$NON-NLS-1$
- private static final URL ICON_SHOW_STRUCT = GridLayoutRule.class.getResource("showgrid.png"); //$NON-NLS-1$
- private static final URL ICON_GRID_MODE = GridLayoutRule.class.getResource("gridmode.png"); //$NON-NLS-1$
- private static final URL ICON_SNAP = GridLayoutRule.class.getResource("snap.png"); //$NON-NLS-1$
-
- /**
- * Whether the IDE should show diagnostics for debugging the grid layout - including
- * spacers visibly in the outline, showing row and column numbers, and so on
- */
- public static boolean sDebugGridLayout = CAN_DEBUG;
-
- /** Whether the structure (grid model) should be displayed persistently to the user */
- public static boolean sShowStructure = false;
-
- /** Whether the drop positions should snap to a regular grid */
- public static boolean sSnapToGrid = false;
-
- /**
- * Whether the grid is edited in "grid mode" where the operations are row/column based
- * rather than free-form
- */
- public static boolean sGridMode = true;
-
- /** Constructs a new {@link GridLayoutRule} */
- public GridLayoutRule() {
- }
-
- @Override
- public void addLayoutActions(
- @NonNull List<RuleAction> actions,
- final @NonNull INode parentNode,
- final @NonNull List<? extends INode> children) {
- super.addLayoutActions(actions, parentNode, children);
-
- String namespace = getNamespace(parentNode);
- Choices orientationAction = RuleAction.createChoices(
- ACTION_ORIENTATION,
- "Orientation", //$NON-NLS-1$
- new PropertyCallback(Collections.singletonList(parentNode),
- "Change LinearLayout Orientation", namespace, ATTR_ORIENTATION), Arrays
- .<String> asList("Set Horizontal Orientation", "Set Vertical Orientation"),
- Arrays.<URL> asList(ICON_HORIZONTAL, ICON_VERTICAL), Arrays.<String> asList(
- "horizontal", "vertical"), getCurrentOrientation(parentNode),
- null /* icon */, -10, false);
- orientationAction.setRadio(true);
- actions.add(orientationAction);
-
- // Gravity and margins
- if (children != null && children.size() > 0) {
- actions.add(RuleAction.createSeparator(35));
- actions.add(createMarginAction(parentNode, children));
- actions.add(createGravityAction(children, ATTR_LAYOUT_GRAVITY));
- }
-
- IMenuCallback actionCallback = new IMenuCallback() {
- @Override
- public void action(
- final @NonNull RuleAction action,
- @NonNull List<? extends INode> selectedNodes,
- final @Nullable String valueId,
- final @Nullable Boolean newValue) {
- parentNode.editXml("Add/Remove Row/Column", new INodeHandler() {
- @Override
- public void handle(@NonNull INode n) {
- String id = action.getId();
- if (id.equals(ACTION_SHOW_STRUCTURE)) {
- sShowStructure = !sShowStructure;
- mRulesEngine.redraw();
- return;
- } else if (id.equals(ACTION_GRID_MODE)) {
- sGridMode = !sGridMode;
- mRulesEngine.redraw();
- return;
- } else if (id.equals(ACTION_SNAP)) {
- sSnapToGrid = !sSnapToGrid;
- mRulesEngine.redraw();
- return;
- } else if (id.equals(ACTION_DEBUG)) {
- sDebugGridLayout = !sDebugGridLayout;
- mRulesEngine.layout();
- return;
- }
-
- GridModel grid = GridModel.get(mRulesEngine, parentNode, null);
- if (id.equals(ACTION_ADD_ROW)) {
- grid.addRow(children);
- } else if (id.equals(ACTION_REMOVE_ROW)) {
- grid.removeRows(children);
- } else if (id.equals(ACTION_ADD_COL)) {
- grid.addColumn(children);
- } else if (id.equals(ACTION_REMOVE_COL)) {
- grid.removeColumns(children);
- }
- }
-
- });
- }
- };
-
- actions.add(RuleAction.createSeparator(142));
-
- actions.add(RuleAction.createToggle(ACTION_GRID_MODE, "Grid Model Mode",
- sGridMode, actionCallback, ICON_GRID_MODE, 145, false));
-
- // Add and Remove Column actions only apply in Grid Mode
- if (sGridMode) {
- actions.add(RuleAction.createToggle(ACTION_SHOW_STRUCTURE, "Show Structure",
- sShowStructure, actionCallback, ICON_SHOW_STRUCT, 147, false));
-
- // Add Row and Add Column
- actions.add(RuleAction.createSeparator(150));
- actions.add(RuleAction.createAction(ACTION_ADD_COL, "Add Column", actionCallback,
- ICON_ADD_COL, 160, false /* supportsMultipleNodes */));
- actions.add(RuleAction.createAction(ACTION_ADD_ROW, "Add Row", actionCallback,
- ICON_ADD_ROW, 165, false));
-
- // Remove Row and Remove Column (if something is selected)
- if (children != null && children.size() > 0) {
- // TODO: Add "Merge Columns" and "Merge Rows" ?
-
- actions.add(RuleAction.createAction(ACTION_REMOVE_COL, "Remove Column",
- actionCallback, ICON_REMOVE_COL, 170, false));
- actions.add(RuleAction.createAction(ACTION_REMOVE_ROW, "Remove Row",
- actionCallback, ICON_REMOVE_ROW, 175, false));
- }
-
- actions.add(RuleAction.createSeparator(185));
- } else {
- actions.add(RuleAction.createToggle(ACTION_SHOW_STRUCTURE, "Show Structure",
- sShowStructure, actionCallback, ICON_SHOW_STRUCT, 190, false));
-
- // Snap to Grid and Show Structure are only relevant in free form mode
- actions.add(RuleAction.createToggle(ACTION_SNAP, "Snap to Grid",
- sSnapToGrid, actionCallback, ICON_SNAP, 200, false));
- }
-
- // Temporary: Diagnostics for GridLayout
- if (CAN_DEBUG) {
- actions.add(RuleAction.createToggle(ACTION_DEBUG, "Debug",
- sDebugGridLayout, actionCallback, null, 210, false));
- }
- }
-
- /**
- * Returns the orientation attribute value currently used by the node (even if not
- * defined, in which case the default horizontal value is returned)
- */
- private String getCurrentOrientation(final INode node) {
- String orientation = node.getStringAttr(getNamespace(node), ATTR_ORIENTATION);
- if (orientation == null || orientation.length() == 0) {
- orientation = VALUE_HORIZONTAL;
- }
- return orientation;
- }
-
- @Override
- public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView,
- @Nullable IDragElement[] elements) {
- GridDropHandler userData = new GridDropHandler(this, targetNode, targetView);
- IFeedbackPainter painter = GridLayoutPainter.createDropFeedbackPainter(this, elements);
- return new DropFeedback(userData, painter);
- }
-
- @Override
- public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- if (feedback == null) {
- return null;
- }
- feedback.requestPaint = true;
-
- GridDropHandler handler = (GridDropHandler) feedback.userData;
- handler.computeMatches(feedback, p);
-
- return feedback;
- }
-
- @Override
- public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements,
- @Nullable DropFeedback feedback, @NonNull Point p) {
- if (feedback == null) {
- return;
- }
-
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- GridDropHandler dropHandler = (GridDropHandler) feedback.userData;
- if (dropHandler.getRowMatch() == null || dropHandler.getColumnMatch() == null) {
- return;
- }
-
- // Collect IDs from dropped elements and remap them to new IDs
- // if this is a copy or from a different canvas.
- Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- feedback.isCopy || !feedback.sameCanvas);
-
- for (IDragElement element : elements) {
- INode newChild;
- if (!sGridMode) {
- newChild = dropHandler.handleFreeFormDrop(targetNode, element);
- } else {
- newChild = dropHandler.handleGridModeDrop(targetNode, element);
- }
-
- // Copy all the attributes, modifying them as needed.
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
-
- addInnerElements(newChild, element, idMap);
- }
- }
-
- @Override
- public void onChildInserted(@NonNull INode node, @NonNull INode parent,
- @NonNull InsertType insertType) {
- if (insertType == InsertType.MOVE_WITHIN) {
- // Don't adjust widths/heights/weights when just moving within a single layout
- return;
- }
-
- if (GridModel.isSpace(node.getFqcn())) {
- return;
- }
-
- // Attempt to set "fill" properties on newly added views such that for example
- // a text field will stretch horizontally.
- String fqcn = node.getFqcn();
- IViewMetadata metadata = mRulesEngine.getMetadata(fqcn);
- FillPreference fill = metadata.getFillPreference();
- String gravity = computeDefaultGravity(fill);
- if (gravity != null) {
- node.setAttribute(getNamespace(parent), ATTR_LAYOUT_GRAVITY, gravity);
- }
- }
-
- /**
- * Returns the namespace URI to use for GridLayout-specific attributes, such
- * as columnCount, layout_column, layout_column_span, layout_gravity etc.
- *
- * @param layout the GridLayout instance to look up the namespace for
- * @return the namespace, never null
- */
- public String getNamespace(INode layout) {
- String namespace = ANDROID_URI;
-
- String fqcn = layout.getFqcn();
- if (!fqcn.equals(GRID_LAYOUT) && !fqcn.equals(FQCN_GRID_LAYOUT)) {
- namespace = mRulesEngine.getAppNameSpace();
- }
-
- return namespace;
- }
-
- /**
- * Computes the default gravity to be used for a widget of the given fill
- * preference when added to a grid layout
- *
- * @param fill the fill preference for the widget
- * @return the gravity value, or null, to be set on the widget
- */
- public static String computeDefaultGravity(FillPreference fill) {
- String horizontal = GRAVITY_VALUE_LEFT;
- String vertical = null;
- if (fill.fillHorizontally(true /*verticalContext*/)) {
- horizontal = GRAVITY_VALUE_FILL_HORIZONTAL;
- }
- if (fill.fillVertically(true /*verticalContext*/)) {
- vertical = GRAVITY_VALUE_FILL_VERTICAL;
- }
- String gravity;
- if (horizontal == GRAVITY_VALUE_FILL_HORIZONTAL
- && vertical == GRAVITY_VALUE_FILL_VERTICAL) {
- gravity = GRAVITY_VALUE_FILL;
- } else if (vertical != null) {
- gravity = horizontal + '|' + vertical;
- } else {
- gravity = horizontal;
- }
-
- return gravity;
- }
-
- @Override
- public void onRemovingChildren(@NonNull List<INode> deleted, @NonNull INode parent,
- boolean moved) {
- super.onRemovingChildren(deleted, parent, moved);
-
- if (!sGridMode) {
- // Attempt to clean up spacer objects for any newly-empty rows or columns
- // as the result of this deletion
- GridModel grid = GridModel.get(mRulesEngine, parent, null);
- grid.onDeleted(deleted);
- }
- }
-
- @Override
- protected void paintResizeFeedback(IGraphics gc, INode node, ResizeState state) {
- if (!sGridMode) {
- GridModel grid = getGrid(state);
- GridLayoutPainter.paintResizeFeedback(gc, state.layout, grid);
- }
-
- if (resizingWidget(state)) {
- super.paintResizeFeedback(gc, node, state);
- } else {
- GridModel grid = getGrid(state);
- int startColumn = grid.getColumn(state.bounds.x);
- int endColumn = grid.getColumn(state.bounds.x2());
- int columnSpan = endColumn - startColumn + 1;
-
- int startRow = grid.getRow(state.bounds.y);
- int endRow = grid.getRow(state.bounds.y2());
- int rowSpan = endRow - startRow + 1;
-
- Rect cellBounds = grid.getCellBounds(startRow, startColumn, rowSpan, columnSpan);
- gc.useStyle(DrawingStyle.RESIZE_PREVIEW);
- gc.drawRect(cellBounds);
- }
- }
-
- /** Returns the grid size cached on the given {@link ResizeState} object */
- private GridModel getGrid(ResizeState resizeState) {
- GridModel grid = (GridModel) resizeState.clientData;
- if (grid == null) {
- grid = GridModel.get(mRulesEngine, resizeState.layout, resizeState.layoutView);
- resizeState.clientData = grid;
- }
-
- return grid;
- }
-
- @Override
- protected void setNewSizeBounds(ResizeState state, INode node, INode layout,
- Rect oldBounds, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
-
- if (resizingWidget(state)) {
- if (state.fillWidth || state.fillHeight || state.wrapWidth || state.wrapHeight) {
- GridModel grid = getGrid(state);
- ViewData view = grid.getView(node);
- if (view != null) {
- String gravityString = grid.getGridAttribute(view.node, ATTR_LAYOUT_GRAVITY);
- int gravity = GravityHelper.getGravity(gravityString, 0);
- if (view.column > 0 && verticalEdge != null && state.fillWidth) {
- state.fillWidth = false;
- state.wrapWidth = true;
- gravity &= ~GravityHelper.GRAVITY_HORIZ_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_HORIZ;
- } else if (verticalEdge != null && state.wrapWidth) {
- gravity &= ~GravityHelper.GRAVITY_HORIZ_MASK;
- gravity |= GravityHelper.GRAVITY_LEFT;
- }
- if (view.row > 0 && horizontalEdge != null && state.fillHeight) {
- state.fillHeight = false;
- state.wrapHeight = true;
- gravity &= ~GravityHelper.GRAVITY_VERT_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_VERT;
- } else if (horizontalEdge != null && state.wrapHeight) {
- gravity &= ~GravityHelper.GRAVITY_VERT_MASK;
- gravity |= GravityHelper.GRAVITY_TOP;
- }
- gravityString = GravityHelper.getGravity(gravity);
- grid.setGridAttribute(view.node, ATTR_LAYOUT_GRAVITY, gravityString);
- // Fall through and set layout_width and/or layout_height to wrap_content
- }
- }
- super.setNewSizeBounds(state, node, layout, oldBounds, newBounds, horizontalEdge,
- verticalEdge);
- } else {
- Pair<Integer, Integer> spans = computeResizeSpans(state);
- int rowSpan = spans.getFirst();
- int columnSpan = spans.getSecond();
- GridModel grid = getGrid(state);
- grid.setColumnSpanAttribute(node, columnSpan);
- grid.setRowSpanAttribute(node, rowSpan);
-
- ViewData view = grid.getView(node);
- if (view != null) {
- String gravityString = grid.getGridAttribute(view.node, ATTR_LAYOUT_GRAVITY);
- int gravity = GravityHelper.getGravity(gravityString, 0);
- if (verticalEdge != null && columnSpan > 1) {
- gravity &= ~GravityHelper.GRAVITY_HORIZ_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_HORIZ;
- }
- if (horizontalEdge != null && rowSpan > 1) {
- gravity &= ~GravityHelper.GRAVITY_VERT_MASK;
- gravity |= GravityHelper.GRAVITY_FILL_VERT;
- }
- gravityString = GravityHelper.getGravity(gravity);
- grid.setGridAttribute(view.node, ATTR_LAYOUT_GRAVITY, gravityString);
- }
- }
- }
-
- @Override
- protected String getResizeUpdateMessage(ResizeState state, INode child, INode parent,
- Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
- Pair<Integer, Integer> spans = computeResizeSpans(state);
- if (resizingWidget(state)) {
- String width = state.getWidthAttribute();
- String height = state.getHeightAttribute();
-
- String message;
- if (horizontalEdge == null) {
- message = width;
- } else if (verticalEdge == null) {
- message = height;
- } else {
- // U+00D7: Unicode for multiplication sign
- message = String.format("%s \u00D7 %s", width, height);
- }
-
- // Tack on a tip about using the Shift modifier key
- return String.format("%s\n(Press Shift to resize row/column spans)", message);
- } else {
- int rowSpan = spans.getFirst();
- int columnSpan = spans.getSecond();
- return String.format("ColumnSpan=%d, RowSpan=%d\n(Release Shift to resize widget itself)",
- columnSpan, rowSpan);
- }
- }
-
- /**
- * Returns true if we're resizing the widget, and false if we're resizing the cell
- * spans
- */
- private static boolean resizingWidget(ResizeState state) {
- return (state.modifierMask & DropFeedback.MODIFIER2) == 0;
- }
-
- /**
- * Computes the new column and row spans as the result of the current resizing
- * operation
- */
- private Pair<Integer, Integer> computeResizeSpans(ResizeState state) {
- GridModel grid = getGrid(state);
-
- int startColumn = grid.getColumn(state.bounds.x);
- int endColumn = grid.getColumn(state.bounds.x2());
- int columnSpan = endColumn - startColumn + 1;
-
- int startRow = grid.getRow(state.bounds.y);
- int endRow = grid.getRow(state.bounds.y2());
- int rowSpan = endRow - startRow + 1;
-
- return Pair.of(rowSpan, columnSpan);
- }
-
- /**
- * Returns the size of the new cell gutter in layout coordinates
- *
- * @return the size of the new cell gutter in layout coordinates
- */
- public int getNewCellSize() {
- return mRulesEngine.screenToLayout(NEW_CELL_WIDTH / 2);
- }
-
- @Override
- public void paintSelectionFeedback(@NonNull IGraphics graphics, @NonNull INode parentNode,
- @NonNull List<? extends INode> childNodes, @Nullable Object view) {
- super.paintSelectionFeedback(graphics, parentNode, childNodes, view);
-
- if (sShowStructure) {
- // TODO: Cache the grid
- if (view != null) {
- if (GridLayoutPainter.paintStructure(view, DrawingStyle.GUIDELINE_DASHED,
- parentNode, graphics)) {
- return;
- }
- }
- GridLayoutPainter.paintStructure(DrawingStyle.GUIDELINE_DASHED,
- parentNode, graphics, GridModel.get(mRulesEngine, parentNode, view));
- } else if (sDebugGridLayout) {
- GridLayoutPainter.paintStructure(DrawingStyle.GRID,
- parentNode, graphics, GridModel.get(mRulesEngine, parentNode, view));
- }
-
- // TBD: Highlight the cells around the selection, and display easy controls
- // for for example tweaking the rowspan/colspan of a cell? (but only in grid mode)
- }
-
- /**
- * Paste into a GridLayout. We have several possible behaviors (and many
- * more than are listed here):
- * <ol>
- * <li> Preserve the current positions of the elements (if pasted from another
- * canvas, not just XML markup copied from say a web site) and apply those
- * into the current grid. This might mean "overwriting" (sitting on top of)
- * existing elements.
- * <li> Fill available "holes" in the grid.
- * <li> Lay them out consecutively, row by row, like text.
- * <li> Some hybrid approach, where I attempt to preserve the <b>relative</b>
- * relationships (columns/wrapping, spacing between the pasted views etc)
- * but I append them to the bottom of the layout on one or more new rows.
- * <li> Try to paste at the current mouse position, if known, preserving the
- * relative distances between the existing elements there.
- * </ol>
- * Attempting to preserve the current position isn't possible right now,
- * because the clipboard data contains only the textual representation of
- * the markup. (We'd need to stash position information from a previous
- * layout render along with the clipboard data).
- * <p>
- * Currently, this implementation simply lays out the elements row by row,
- * approach #3 above.
- */
- @Override
- public void onPaste(
- @NonNull INode targetNode,
- @Nullable Object targetView,
- @NonNull IDragElement[] elements) {
- DropFeedback feedback = onDropEnter(targetNode, targetView, elements);
- if (feedback != null) {
- Rect b = targetNode.getBounds();
- if (!b.isValid()) {
- return;
- }
-
- Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
- true /* remap id's */);
-
- for (IDragElement element : elements) {
- // Skip <Space> elements and only insert the real elements being
- // copied
- if (elements.length > 1 && (FQCN_SPACE.equals(element.getFqcn())
- || FQCN_SPACE_V7.equals(element.getFqcn()))) {
- continue;
- }
-
- String fqcn = element.getFqcn();
- INode newChild = targetNode.appendChild(fqcn);
- addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
-
- // Ensure that we reset any potential row/column attributes from a different
- // grid layout being copied from
- GridDropHandler handler = (GridDropHandler) feedback.userData;
- GridModel grid = handler.getGrid();
- grid.setGridAttribute(newChild, ATTR_LAYOUT_COLUMN, null);
- grid.setGridAttribute(newChild, ATTR_LAYOUT_ROW, null);
-
- // TODO: Set columnSpans to avoid making these widgets completely
- // break the layout
- // Alternatively, I could just lay them all out on subsequent lines
- // with a column span of columnSpan5
-
- addInnerElements(newChild, element, idMap);
- }
- }
- }
-}