/** Determine if not can grow due to localization or not. */ public boolean fixedWidth() { String width = mNode.getAttributeNS(ANDROID_URI, ATTR_LAYOUT_WIDTH); if (width.equals(VALUE_WRAP_CONTENT)) { // First check child nodes. If at least one of them is not // fixed-width, // treat whole layout as non-fixed-width NodeList childNodes = mNode.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node child = childNodes.item(i); if (child.getNodeType() == Node.ELEMENT_NODE) { LayoutNode childLayout = new LayoutNode((Element) child, i); if (!childLayout.fixedWidth()) { return false; } } } // If node contains text attribute, consider it fixed-width if // text is hard-coded, otherwise it is not fixed-width. String text = mNode.getAttributeNS(ANDROID_URI, ATTR_TEXT); if (!text.isEmpty()) { return !text.startsWith(PREFIX_RESOURCE_REF) && !text.startsWith(PREFIX_THEME_REF); } String nodeName = mNode.getTagName(); if (nodeName.contains("Image") || nodeName.contains("Progress") || nodeName.contains("Radio")) { return true; } else if (nodeName.contains("Button") || nodeName.contains("Text")) { return false; } } return true; }
/** Draws the panel. */ public void paintComponent(Graphics g) { super.paintComponent(g); Log.setTrace(true); // Log.trace(this, getBounds().toString()); // Log.trace(this, getInsets().toString()); // respect borders Insets insets = getInsets(); Rectangle r = getBounds(); r.x += insets.left; r.y += insets.top; r.width -= insets.left + insets.right; r.height -= insets.top + insets.bottom; // System.out.println("paintComponent" + count++); g.setColor(Color.black); DirectedGraph<LayoutNode, DirectedEdge<LayoutNode>> graph = fLayout.graph(); // draw edges Iterator<DirectedEdge<LayoutNode>> edgeIter = graph.edgeIterator(); while (edgeIter.hasNext()) { DirectedEdge<LayoutNode> edge = edgeIter.next(); // Log.trace(this, edge.toString()); LayoutNode source = (LayoutNode) edge.source(); LayoutNode target = (LayoutNode) edge.target(); int x1 = source.getX() * 80 + 30; int y1 = 50 + source.fLayer * 50; // if (source.isDummy() ) // x1 += 5; int x2 = target.getX() * 80 + 30; int y2 = 50 + target.fLayer * 50; // if (target.isDummy() ) // x2 += 5; g.drawLine(x1, y1, x2, y2); } // draw nodes Iterator<LayoutNode> nodeIter = graph.iterator(); while (nodeIter.hasNext()) { LayoutNode node = nodeIter.next(); if (node.isDummy()) continue; int x = node.getX() * 80 + 30; int y = 50 + node.fLayer * 50; g.setColor(Color.white); g.fillRect(x - 10, y - 10, 20, 20); g.setColor(Color.black); g.drawRect(x - 10, y - 10, 20, 20); g.drawString(node.toString(), x - 7, y + 8); } }
@NonNull public Set<LayoutNode> canGrowRight() { Set<LayoutNode> nodes; if (mToLeft != null) { nodes = mToLeft.canGrowRight(); } else { nodes = new LinkedHashSet<LayoutNode>(); } if (!fixedWidth()) { nodes.add(this); } return nodes; }
@Override public void containerLayoutChanged(PropertyChangeEvent ev) throws PropertyVetoException { if (ev != null && ev.getPropertyName() != null) { layoutDelegate.acceptContainerLayoutChange(ev); FormModel formModel = radContainer.getFormModel(); formModel.fireContainerLayoutChanged( radContainer, ev.getPropertyName(), ev.getOldValue(), ev.getNewValue()); } else { propertySets = null; } LayoutNode node = radContainer.getLayoutNodeReference(); if (node != null) { // propagate the change to node if (ev != null && ev.getPropertyName() != null) { node.fireLayoutPropertiesChange(); } else { node.fireLayoutPropertySetsChange(); } } }
@Override public void visitElement(@NonNull XmlContext context, @NonNull Element element) { // Traverse all child elements NodeList childNodes = element.getChildNodes(); int count = childNodes.getLength(); Map<String, LayoutNode> nodes = Maps.newHashMap(); for (int i = 0; i < count; i++) { Node node = childNodes.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { LayoutNode ln = new LayoutNode((Element) node, i); nodes.put(ln.getNodeId(), ln); } } // Node map is populated, recalculate nodes sizes for (LayoutNode ln : nodes.values()) { ln.processNode(nodes); } for (LayoutNode right : nodes.values()) { if (!right.mLastLeft || right.skip()) { continue; } Set<LayoutNode> canGrowLeft = right.canGrowLeft(); for (LayoutNode left : nodes.values()) { if (left == right || !left.mLastRight || left.skip() || !left.sameBucket(right)) { continue; } Set<LayoutNode> canGrowRight = left.canGrowRight(); if (canGrowLeft.size() > 0 || canGrowRight.size() > 0) { canGrowRight.addAll(canGrowLeft); LayoutNode nodeToBlame = right; LayoutNode otherNode = left; if (!canGrowRight.contains(right) && canGrowRight.contains(left)) { nodeToBlame = left; otherNode = right; } context.report( ISSUE, nodeToBlame.getNode(), context.getLocation(nodeToBlame.getNode()), String.format( "`%1$s` can overlap `%2$s` if %3$s %4$s due to localized text expansion", nodeToBlame.getNodeId(), otherNode.getNodeId(), Joiner.on(", ").join(canGrowRight), canGrowRight.size() > 1 ? "grow" : "grows")); } } } }
/** * Process a node of a layout. Put it into one of three processing units and determine its right * and left neighbours. */ public void processNode(@NonNull Map<String, LayoutNode> nodes) { if (mProcessed) { return; } mProcessed = true; if (isInvisible() || hasAttr(ATTR_LAYOUT_ALIGN_RIGHT) || hasAttr(ATTR_LAYOUT_ALIGN_END) || hasAttr(ATTR_LAYOUT_ALIGN_LEFT) || hasAttr(ATTR_LAYOUT_ALIGN_START)) { mBucket = Bucket.SKIP; } else if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_TOP)) { mBucket = Bucket.TOP; } else if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM)) { mBucket = Bucket.BOTTOM; } else { if (hasAttr(ATTR_LAYOUT_ABOVE) || hasAttr(ATTR_LAYOUT_BELOW)) { mBucket = Bucket.SKIP; } else { String[] checkAlignment = { ATTR_LAYOUT_ALIGN_TOP, ATTR_LAYOUT_ALIGN_BOTTOM, ATTR_LAYOUT_ALIGN_BASELINE }; for (String alignment : checkAlignment) { String value = mNode.getAttributeNS(ANDROID_URI, alignment); if (!value.isEmpty()) { LayoutNode otherNode = nodes.get(uniformId(value)); if (otherNode != null) { otherNode.processNode(nodes); mBucket = otherNode.mBucket; } } } } } if (mBucket == null) { mBucket = Bucket.TOP; } // Check relative placement mToLeft = findNodeByAttr(nodes, ATTR_LAYOUT_TO_START_OF); if (mToLeft == null) { mToLeft = findNodeByAttr(nodes, ATTR_LAYOUT_TO_LEFT_OF); } if (mToLeft != null) { mToLeft.mLastLeft = false; mLastRight = false; } mToRight = findNodeByAttr(nodes, ATTR_LAYOUT_TO_END_OF); if (mToRight == null) { mToRight = findNodeByAttr(nodes, ATTR_LAYOUT_TO_RIGHT_OF); } if (mToRight != null) { mToRight.mLastLeft = false; mLastRight = false; } if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_END) || hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_RIGHT)) { mLastRight = false; } if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_START) || hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_LEFT)) { mLastLeft = false; } if (mToLeft == null && mToRight == null && mLastRight && mLastLeft) { mLastLeft = false; } }