@Override protected void layoutChildren() { super.layoutChildren(); if (_nodeByPosition.isEmpty()) { adjustLineCount(0); setPrefWidth(0); setPrefHeight(0); return; } // Calculate width per position based on layout bounds Map<NodePosition, Double> widthByPosition = new HashMap<>(); Map<Integer, Double> levelHeight = new HashMap<>(); Map<Integer, Set<NodePosition>> positionsByLevel = new HashMap<>(); Map<NodePosition, Set<NodePosition>> positionsByParentPosition = new HashMap<>(); int maxLevel = Collections.max(_nodeByLevel.keySet()); for (int curLevel = maxLevel; curLevel >= 0; --curLevel) { levelHeight.put(curLevel, 0.0); positionsByLevel.put(curLevel, new HashSet<NodePosition>()); } for (int curLevel = maxLevel; curLevel >= 0; --curLevel) { // Get bounds of nodes on current level Set<Node> curLevelNodes = _nodeByLevel.get(curLevel); if (curLevelNodes != null) { // Get node bounds for (Node node : curLevelNodes) { // Node data NodePosition nodePosition = _positionByNode.get(node); Bounds nodeBounds = node.getLayoutBounds(); // Get bounds widthByPosition.put(nodePosition, nodeBounds.getWidth() + this.getXAxisSpacing()); levelHeight.put( curLevel, Math.max(levelHeight.get(curLevel), nodeBounds.getHeight() + this.getYAxisSpacing())); // Register positions positionsByLevel.get(curLevel).add(nodePosition); if (curLevel > 0) { positionsByLevel.get(curLevel - 1).add(nodePosition.getParent()); } } } // Calculate position widths of current level for (NodePosition position : positionsByLevel.get(curLevel)) { // Register positions if (position.getLevel() > 0) { NodePosition parentPosition = position.getParent(); positionsByLevel.get(position.getLevel() - 1).add(parentPosition); if (positionsByParentPosition.containsKey(parentPosition) == false) { positionsByParentPosition.put(parentPosition, new HashSet<NodePosition>()); } positionsByParentPosition.get(parentPosition).add(position); } // Get width of children double widthOfChildren = 0; Set<NodePosition> parentPositions = positionsByParentPosition.get(position); if (parentPositions != null) { for (NodePosition childPosition : parentPositions) { if (widthByPosition.containsKey(childPosition) == true) { widthOfChildren += widthByPosition.get(childPosition); } } } // Get maximum of node bound and sum of child node bounds if (widthByPosition.containsKey(position) == false) { widthByPosition.put(position, widthOfChildren); } else { widthByPosition.put(position, Math.max(widthByPosition.get(position), widthOfChildren)); } } } // Calculate position boxes Map<NodePosition, Rectangle2D> boxesByPosition = new HashMap<>(); if (positionsByLevel.containsKey(0) == false || positionsByLevel.get(0).size() != 1) { throw new IllegalStateException(); } boxesByPosition.put( NodePosition.ROOT, new Rectangle2D(0, 0, widthByPosition.get(NodePosition.ROOT), levelHeight.get(0))); for (int curLevel = 0; curLevel <= maxLevel; ++curLevel) { for (NodePosition position : positionsByLevel.get(curLevel)) { Rectangle2D positionBox = boxesByPosition.get(position); List<NodePosition> childPositions = new ArrayList<>(); if (positionsByParentPosition.containsKey(position)) { childPositions.addAll(positionsByParentPosition.get(position)); } Collections.sort(childPositions); double childX = positionBox.getMinX(); for (NodePosition childPosition : childPositions) { double childWidth = widthByPosition.get(childPosition); boxesByPosition.put( childPosition, new Rectangle2D( childX, positionBox.getMaxY(), childWidth, levelHeight.get(childPosition.getLevel()))); childX += childWidth; } } } // Position nodes Map<NodePosition, Double> xCenterHintByPosition = new HashMap<>(); Map<NodePosition, Double> yCenterHintByPosition = new HashMap<>(); for (int curLevel = maxLevel; curLevel >= 0; --curLevel) { for (NodePosition position : positionsByLevel.get(curLevel)) { // Calculate center hints Rectangle2D positionBox = boxesByPosition.get(position); double xCenterHint = (positionBox.getMinX() + positionBox.getMaxX()) / 2; if (xCenterHintByPosition.containsKey(position) == true) { xCenterHint = xCenterHintByPosition.get(position); } double yCenterHint = (positionBox.getMinY() + positionBox.getMaxY()) / 2; xCenterHintByPosition.put(position, xCenterHint); yCenterHintByPosition.put(position, yCenterHint); // Position node if (_nodeByPosition.containsKey(position)) { Node node = _nodeByPosition.get(position); Bounds nodeBounds = node.getLayoutBounds(); node.relocate( xCenterHint - nodeBounds.getWidth() / 2, yCenterHint - nodeBounds.getHeight() / 2); } // Update parent node position hint NodePosition parentPosition = position.getParent(); if (xCenterHintByPosition.containsKey(parentPosition)) { xCenterHintByPosition.put( parentPosition, (xCenterHintByPosition.get(parentPosition) + xCenterHint) / 2); } else { xCenterHintByPosition.put(parentPosition, xCenterHint); } } } // Update lines if (this.getShowLines() == true) { adjustLineCount(boxesByPosition.size() - 1); int currentLine = 0; for (NodePosition position : boxesByPosition.keySet()) { if (positionsByParentPosition.containsKey(position) == false) { continue; } for (NodePosition childPosition : positionsByParentPosition.get(position)) { Bounds fromBounds = _nodeByPosition.containsKey(position) ? _nodeByPosition.get(position).getLayoutBounds() : null; Bounds toBounds = _nodeByPosition.containsKey(childPosition) ? _nodeByPosition.get(childPosition).getLayoutBounds() : null; Point2D lineFrom = new Point2D( xCenterHintByPosition.get(position), yCenterHintByPosition.get(position) + (fromBounds != null ? (fromBounds.getHeight() / 2) : 0) + this.getLineSpacing()); Point2D lineTo = new Point2D( xCenterHintByPosition.get(childPosition), yCenterHintByPosition.get(childPosition) - (toBounds != null ? (toBounds.getHeight() / 2) : 0) - this.getLineSpacing()); Line l = _lines.get(currentLine); l.setStartX(lineFrom.getX()); l.setStartY(lineFrom.getY()); l.setEndX(lineTo.getX()); l.setEndY(lineTo.getY()); ++currentLine; } } } else { adjustLineCount(0); } // Update preferred size double totalHeight = 0; for (Double h : levelHeight.values()) { totalHeight += h; } setPrefWidth(widthByPosition.get(NodePosition.ROOT)); setPrefHeight(totalHeight); }
private void shiftDock() { long now = System.currentTimeMillis(); Rectangle2D cfgBounds = Client.getConfiguredBounds(); // The bounds to work in int boundsSize = cfg.isVertical() ? (int) cfgBounds.getHeight() : (int) cfgBounds.getWidth(); // Total amount to slide int value = cfg.sizeProperty().get() - AUTOHIDE_TAB_OPPOSITE_SIZE; // How far along the timeline? float fac = Math.min(1f, 1f - ((float) (yEnd - now) / (float) AUTOHIDE_DURATION)); // The amount of movement so far float amt = fac * (float) value; // The amount to shrink the width (or height when vertical) of the // visible 'bar' float barSize = (float) boundsSize * fac; // If showing, reverse final boolean fhidden = hidden; if (!hidden) { amt = value - amt; barSize = (float) boundsSize - barSize; if (!pull.isVisible()) pull.setVisible(true); } // Reveal or hide the pull tab dockContent.setOpacity(hidden ? 1f - fac : fac); pull.setOpacity((hidden ? fac : 1f - fac) * 0.5f); Stage stage = getStage(); if (stage != null) { if (cfg.topProperty().get()) { getScene().getRoot().translateYProperty().set(-amt); stage.setHeight(cfg.sizeProperty().get() - amt + Client.DROP_SHADOW_SIZE); stage.setWidth(Math.max(AUTOHIDE_TAB_SIZE, cfgBounds.getWidth() - barSize)); stage.setX(cfgBounds.getMinX() + ((cfgBounds.getWidth() - stage.getWidth()) / 2f)); } else if (cfg.bottomProperty().get()) { stage.setY(cfgBounds.getMaxY() + amt); stage.setHeight(cfg.sizeProperty().get() - amt + Client.DROP_SHADOW_SIZE); stage.setWidth(Math.max(AUTOHIDE_TAB_SIZE, cfgBounds.getWidth() - barSize)); stage.setX(cfgBounds.getMinX() + ((cfgBounds.getWidth() - stage.getWidth()) / 2f)); } else if (cfg.leftProperty().get()) { getScene().getRoot().translateXProperty().set(-amt); stage.setWidth(cfg.sizeProperty().get() - amt); stage.setHeight(Math.max(AUTOHIDE_TAB_SIZE, cfgBounds.getHeight() - barSize)); stage.setY(cfgBounds.getMinY() + ((cfgBounds.getHeight() - stage.getHeight()) / 2f)); } else if (cfg.rightProperty().get()) { stage.setX(cfgBounds.getMaxX() + amt - cfg.sizeProperty().get()); stage.setWidth(cfg.sizeProperty().get() - amt); stage.setHeight(Math.max(AUTOHIDE_TAB_SIZE, cfgBounds.getHeight() - barSize)); stage.setY(cfgBounds.getMinY() + ((cfgBounds.getHeight() - stage.getHeight()) / 2f)); } else { throw new UnsupportedOperationException(); } } // The update or the sign in dialog may have been popped, so make sure // it is position correctly if (signInPopup != null && signInPopup.isShowing()) { signInPopup.sizeToScene(); } // If not fully hidden / revealed, play again if (now < yEnd) { dockHider.playFromStart(); } else { // Defer this as events may still be coming in Platform.runLater( new Runnable() { @Override public void run() { if (!fhidden && stage != null) { stage.requestFocus(); pull.setVisible(false); } hiding = false; } }); } }