/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Tom Schindl<tom.schindl@bestsolution.at> - ported to JavaFX *******************************************************************************/ package at.bestsolution.efxclipse.runtime.panels; import java.util.ArrayList; import java.util.List; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.value.WritableIntegerValue; import javafx.event.EventHandler; import javafx.geometry.BoundingBox; import javafx.geometry.Bounds; import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; public class SashLayoutPane extends Pane { class SashRect { Bounds rect; MGenericTile container; MUIElement left; MUIElement right; public SashRect(Bounds rect, MGenericTile container, MUIElement left, MUIElement right) { this.container = container; this.rect = rect; this.left = left; this.right = right; } } public abstract static class MUIElement { Double weight; Object widget; boolean visible = true; public boolean isVisible() { return visible; } public void setWeight(Double weight) { this.weight = weight; } } public static class MGenericTile extends MUIElement { boolean horizontal; List<MUIElement> children = new ArrayList<MUIElement>(); public MGenericTile() { } public void setHorizontal(boolean horizontal) { this.horizontal = horizontal; } public void add(MUIElement element) { children.add(element); } } public static class MUIControl extends MUIElement { public MUIControl() { } } private WritableIntegerValue minSashPercent = new SimpleIntegerProperty(this, "minSashPercent", 10); private WritableIntegerValue marginLeft = new SimpleIntegerProperty(this, "marginLeft", 0); private WritableIntegerValue marginRight = new SimpleIntegerProperty(this, "marginRight", 0); private WritableIntegerValue marginTop = new SimpleIntegerProperty(this, "marginTop", 0); private WritableIntegerValue marginBottom = new SimpleIntegerProperty(this, "marginBottom", 0); private WritableIntegerValue sashWidth = new SimpleIntegerProperty(this, "sashWidth", 4); private MGenericTile root; private List<SashRect> sashes = new ArrayList<SashRect>(); private boolean draggingSashes = false; List<SashRect> sashesToDrag; public SashLayoutPane() { this.root = new MGenericTile(); this.root.widget = this; onMouseMovedProperty().set(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (!draggingSashes) { // Set the cursor feedback List<SashRect> sashList = getSashRects(event.getX(), event.getY()); if (sashList.size() == 0) { setCursor(Cursor.DEFAULT); } else if (sashList.size() == 1) { if (sashList.get(0).container.horizontal) { setCursor(Cursor.W_RESIZE); } else { setCursor(Cursor.S_RESIZE); } } else { setCursor(Cursor.CROSSHAIR); } } } }); onMouseDraggedProperty().set(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { if (draggingSashes) { adjustWeights(sashesToDrag, event.getX(), event.getY()); requestLayout(); } } }); onMouseReleasedProperty().set(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { draggingSashes = false; } }); onMousePressedProperty().set(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { sashesToDrag = getSashRects(event.getX(), event.getY()); if (sashesToDrag.size() > 0) { draggingSashes = true; } } }); // onMouseMovedProperty().addListener(new ChangeListener<EventHandler<MouseEvent>>() { // // @Override // public void changed( // ObservableValue<? extends EventHandler<MouseEvent>> observable, // EventHandler<MouseEvent> oldValue, // EventHandler<MouseEvent> newValue) { // newValue. // } // }); // host.addMouseListener(new MouseListener() { // public void mouseUp(MouseEvent e) { // host.setCapture(false); // draggingSashes = false; // } // // public void mouseDown(MouseEvent e) { // if (e.button != 1) // return; // // sashesToDrag = getSashRects(e.x, e.y); // if (sashesToDrag.size() > 0) { // draggingSashes = true; // host.setCapture(true); // } // } // // public void mouseDoubleClick(MouseEvent e) { // } // }); } public void setMarginLeft(int marginLeft) { this.marginLeft.set(marginLeft); } public int getMarginLeft() { return this.marginLeft.get(); } public void setMarginRight(int marginRight) { this.marginRight.set(marginRight); } public int getMarginRight() { return this.marginRight.get(); } public void setMarginTop(int marginTop) { this.marginTop.set(marginTop); } public int getMarginTop() { return this.marginTop.get(); } public void setMarginBottom(int marginBottom) { this.marginBottom.set(marginBottom); } public int getMarginBottom() { return this.marginBottom.get(); } public void setSashWidth(int sashWidth) { this.sashWidth.set(sashWidth); } public int getSashWidth() { return this.sashWidth.get(); } protected List<SashRect> getSashRects(double x, double y) { List<SashRect> srs = new ArrayList<SashRect>(); boolean inSash = false; for (SashRect sr : sashes) { if (sr.rect.contains(x, y)) inSash = true; } if (!inSash) return srs; BoundingBox target = new BoundingBox(x - 5, y - 5, 10, 10); for (SashRect sr : sashes) { if (sr.rect.intersects(target)) srs.add(sr); } return srs; } protected void adjustWeights(List<SashRect> sashes, double curX, double curY) { for (SashRect sr : sashes) { double totalWeight = getWeight(sr.left) + getWeight(sr.right); double minSashValue = (((totalWeight / 100.0) * minSashPercent.get()) + 0.5); Bounds leftRect = getRectangle(sr.left); Bounds rightRect = getRectangle(sr.right); if (leftRect == null || rightRect == null) continue; double leftWeight; double rightWeight; if (sr.container.horizontal) { double left = leftRect.getMinX(); double right = rightRect.getMinX() + rightRect.getWidth(); double pct = (curX - left) / (right - left); leftWeight = ((totalWeight * pct) + 0.5); if (leftWeight < minSashValue) leftWeight = minSashValue; if (leftWeight > (totalWeight - minSashValue)) leftWeight = totalWeight - minSashValue; rightWeight = totalWeight - leftWeight; } else { double top = leftRect.getMinY(); double bottom = rightRect.getMinY() + rightRect.getHeight(); double pct = (curY - top) / (bottom - top); leftWeight = (int)((totalWeight * pct) + 0.5); if (leftWeight < minSashValue) leftWeight = minSashValue; if (leftWeight > (totalWeight - minSashValue)) leftWeight = totalWeight - minSashValue; rightWeight = totalWeight - leftWeight; } setWeight(sr.left, leftWeight); setWeight(sr.right, rightWeight); } } private void setWeight(MUIElement element, double weight) { element.weight = weight; } public void setConstraint(Node node, MUIControl data) { data.widget = node; } public MGenericTile getRoot() { return root; } @Override protected void layoutChildren() { if (root == null) return; Bounds bounds = getLayoutBounds(); bounds = new BoundingBox(bounds.getMinX() + marginLeft.get(), bounds.getMinY() + marginTop.get(), bounds.getWidth() - (marginLeft.get() + marginRight.get()), bounds.getHeight() - (marginTop.get() + marginBottom.get())); sashes.clear(); tileSubNodes(bounds, root); } private Bounds getRectangle(MUIElement element) { if (element instanceof MGenericTile) return (Bounds) element.widget; else if (element.widget instanceof Node) return ((Node) (element.widget)).getBoundsInParent(); return null; } private void setRectangle(MUIElement node, Bounds bounds) { if (node.widget instanceof Node) { Node ctrl = (Node) node.widget; ctrl.resizeRelocate(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight()); } else if (node instanceof MGenericTile) { Bounds newRect = new BoundingBox(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight()); node.widget = newRect; } } private List<MUIElement> getVisibleChildren(MGenericTile sashContainer) { List<MUIElement> visKids = new ArrayList<MUIElement>(); for (MUIElement child : sashContainer.children) { if (child.isVisible()) visKids.add(child); } return visKids; } private double totalWeight(MGenericTile node) { double total = 0; for (MUIElement subNode : node.children) { if (subNode.isVisible()) total += getWeight(subNode); } return total; } private void tileSubNodes(Bounds bounds, MUIElement node) { if (node != root) setRectangle(node, bounds); if (!(node instanceof MGenericTile)) return; MGenericTile sashContainer = (MGenericTile) node; List<MUIElement> visibleChildren = getVisibleChildren(sashContainer); int childCount = visibleChildren.size(); // How many pixels do we have? double availableWidth = sashContainer.horizontal ? bounds.getWidth() : bounds.getHeight(); // Subtract off the room for the sashes availableWidth -= ((childCount - 1) * sashWidth.get()); // Get the total of the weights double totalWeight = totalWeight(sashContainer); double tilePos = sashContainer.horizontal ? bounds.getMinX() : bounds.getMinY(); MUIElement prev = null; for (MUIElement subNode : visibleChildren) { // Add a 'sash' between this node and the 'prev' if (prev != null) { Bounds sashRect = sashContainer.horizontal ? new BoundingBox( tilePos, bounds.getMinY(), sashWidth.get(), bounds.getHeight()) : new BoundingBox(bounds.getMinX(), tilePos, bounds.getWidth(), sashWidth.get()); sashes.add(new SashRect(sashRect, sashContainer, prev, subNode)); tilePos += sashWidth.get(); } // Calc the new size as a %'age of the total double ratio = getWeight(subNode) / totalWeight; double newSize = availableWidth * ratio; Bounds subBounds = sashContainer.horizontal ? new BoundingBox( tilePos, bounds.getMinY(), newSize, bounds.getHeight()) : new BoundingBox( bounds.getMinX(), tilePos, bounds.getWidth(), newSize); tilePos += newSize; tileSubNodes(subBounds, subNode); prev = subNode; } } private static double getWeight(MUIElement element) { Double info = element.weight; if (info == null ) { element.weight = Double.valueOf(100); info = element.weight; } return info; } }