1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.oracle.javafx.scenebuilder.kit.editor.panel.content.driver.resizer; 34 35 import com.oracle.javafx.scenebuilder.kit.metadata.util.PropertyName; 36 import com.oracle.javafx.scenebuilder.kit.util.Deprecation; 37 import com.oracle.javafx.scenebuilder.kit.util.MathUtils; 38 import java.util.HashMap; 39 import java.util.Map; 40 import javafx.geometry.Bounds; 41 import javafx.scene.layout.ColumnConstraints; 42 import javafx.scene.layout.GridPane; 43 import javafx.scene.layout.Region; 44 45 /** 46 * 47 */ 48 public class GridPaneColumnResizer { 49 50 private static final PropertyName minWidthName = new PropertyName("minWidth"); //NOI18N 51 private static final PropertyName prefWidthName = new PropertyName("prefWidth"); //NOI18N 52 private static final PropertyName maxWidthName = new PropertyName("maxWidth"); //NOI18N 53 private static final PropertyName percentWidthName = new PropertyName("percentWidth"); //NOI18N 54 55 private final GridPane gridPane; 56 private final int columnIndex; 57 private final ColumnSizing originalSizing; // Column at columnIndex 58 private final ColumnSizing originalSizingNext; // Column at columnIndex+1 59 private final boolean usePercentSizing; 60 private final double x1, x2, x3, x4, xm; 61 62 public GridPaneColumnResizer(GridPane gridPane, int columnIndex) { 63 assert gridPane != null; 64 assert columnIndex >= 0; 65 assert columnIndex+1 < gridPane.getColumnConstraints().size(); 66 67 this.gridPane = gridPane; 68 this.columnIndex = columnIndex; 69 this.originalSizing 70 = new ColumnSizing(gridPane.getColumnConstraints().get(columnIndex)); 71 this.originalSizingNext 72 = new ColumnSizing(gridPane.getColumnConstraints().get(columnIndex+1)); 73 this.usePercentSizing 74 = countPercentWidths() == Deprecation.getGridPaneColumnCount(gridPane); 75 76 // 77 // x1 x2 x3 xm x4 78 // --+-----------------+----------+--------------+---------+-- 79 // | col n | hgap n | col n+1 | 80 // | | | | 81 // 82 // Range for moving x2 is [x1, xm] 83 // With (x4 - xm) == (x3 - x2) 84 // 85 86 final Bounds cellBounds 87 = Deprecation.getGridPaneCellBounds(gridPane, columnIndex, 0); 88 final Bounds nextBounds 89 = Deprecation.getGridPaneCellBounds(gridPane, columnIndex+1, 0); 90 x1 = cellBounds.getMinX(); 91 x2 = cellBounds.getMaxX(); 92 x3 = nextBounds.getMinX(); 93 x4 = nextBounds.getMaxX(); 94 xm = x4 - (x3 - x2); 95 assert x1 <= xm; 96 } 97 98 public GridPane getSceneGraphObject() { 99 return gridPane; 100 } 101 102 public int getColumnIndex() { 103 return columnIndex; 104 } 105 106 public void updateWidth(double dx) { 107 108 // Clamp x2 + dx in [x1, xm] 109 final double newX2 = Math.max(x1, Math.min(xm, x2 + dx)); 110 final double newX3 = newX2 + (x3 - x2); 111 final double newCellWidth = newX2 - x1; 112 final double newNextWidth = x4 - newX3; 113 114 // assert (newCellWidth+newNextWidth) == (downColWidths[colIndex]+downColWidths[colIndex+1]) : 115 // "newCellWidth+newNextWidth=" + (newCellWidth+newNextWidth) + ", " + 116 // "downColWidths[colIndex]+downColWidths[colIndex+1]=" + 117 // (downColWidths[colIndex]+downColWidths[colIndex+1]); 118 119 // Updates width of columns at columnIndex and columnIndex+1 120 final ColumnConstraints cc = gridPane.getColumnConstraints().get(columnIndex); 121 final ColumnConstraints ccNext = gridPane.getColumnConstraints().get(columnIndex+1); 122 123 if (usePercentSizing) { 124 final double ratio = newCellWidth / (xm - x1); 125 126 final double base 127 = originalSizing.getPercentWidth() 128 + originalSizingNext.getPercentWidth(); 129 130 final double newPercentWidth = Math.floor(ratio * base); 131 final double newPercentWidthNext = base - newPercentWidth; 132 133 cc.setPercentWidth(newPercentWidth); 134 ccNext.setPercentWidth(newPercentWidthNext); 135 136 } else { 137 138 // Column at columnIndex 139 cc.setPrefWidth(newCellWidth); 140 if (cc.getMinWidth() == Region.USE_COMPUTED_SIZE) { 141 cc.setMinWidth(newCellWidth); 142 } else { 143 cc.setMinWidth(Math.min(newCellWidth, cc.getMinWidth())); 144 } 145 if (cc.getMaxWidth() == Region.USE_COMPUTED_SIZE) { 146 cc.setMaxWidth(newCellWidth); 147 } else { 148 cc.setMaxWidth(Math.max(newCellWidth, cc.getMaxWidth())); 149 } 150 151 // Column at columnIndex+1 152 ccNext.setPrefWidth(newNextWidth); 153 if (ccNext.getMinWidth() == Region.USE_COMPUTED_SIZE) { 154 ccNext.setMinWidth(newNextWidth); 155 } else { 156 ccNext.setMinWidth(Math.min(newNextWidth, ccNext.getMinWidth())); 157 } 158 if (ccNext.getMaxWidth() == Region.USE_COMPUTED_SIZE) { 159 ccNext.setMaxWidth(newNextWidth); 160 } else { 161 ccNext.setMaxWidth(Math.max(newNextWidth, ccNext.getMaxWidth())); 162 } 163 } 164 165 // Adjusts min 166 } 167 168 public void revertToOriginalSize() { 169 // Restore sizing of columns at columnIndex and columnIndex+1 170 final ColumnConstraints cc = gridPane.getColumnConstraints().get(columnIndex); 171 final ColumnConstraints ccNext = gridPane.getColumnConstraints().get(columnIndex+1); 172 173 originalSizing.applyTo(cc); 174 originalSizingNext.applyTo(ccNext); 175 } 176 177 178 public Map<PropertyName, Object> getChangeMap() { 179 final Map<PropertyName, Object> result = new HashMap<>(); 180 181 final ColumnConstraints cc = gridPane.getColumnConstraints().get(columnIndex); 182 if (MathUtils.equals(cc.getMinWidth(), originalSizing.getMinWidth()) == false) { 183 result.put(minWidthName, cc.getMinWidth()); 184 } 185 if (MathUtils.equals(cc.getPrefWidth(), originalSizing.getPrefWidth()) == false) { 186 result.put(prefWidthName, cc.getPrefWidth()); 187 } 188 if (MathUtils.equals(cc.getMaxWidth(), originalSizing.getMaxWidth()) == false) { 189 result.put(maxWidthName, cc.getMaxWidth()); 190 } 191 if (MathUtils.equals(cc.getPercentWidth(), originalSizing.getPercentWidth()) == false) { 192 result.put(percentWidthName, cc.getPercentWidth()); 193 } 194 return result; 195 } 196 197 198 public Map<PropertyName, Object> getChangeMapNext() { 199 final Map<PropertyName, Object> result = new HashMap<>(); 200 201 final ColumnConstraints ccNext = gridPane.getColumnConstraints().get(columnIndex+1); 202 if (MathUtils.equals(ccNext.getMinWidth(), originalSizingNext.getMinWidth()) == false) { 203 result.put(minWidthName, ccNext.getMinWidth()); 204 } 205 if (MathUtils.equals(ccNext.getPrefWidth(), originalSizingNext.getPrefWidth()) == false) { 206 result.put(prefWidthName, ccNext.getPrefWidth()); 207 } 208 if (MathUtils.equals(ccNext.getMaxWidth(), originalSizingNext.getMaxWidth()) == false) { 209 result.put(maxWidthName, ccNext.getMaxWidth()); 210 } 211 if (MathUtils.equals(ccNext.getPercentWidth(), originalSizingNext.getPercentWidth()) == false) { 212 result.put(percentWidthName, ccNext.getPercentWidth()); 213 } 214 return result; 215 } 216 217 218 /* 219 * Private 220 */ 221 222 private int countPercentWidths() { 223 int result = 0; 224 for (ColumnConstraints cc : gridPane.getColumnConstraints()) { 225 if (cc.getPercentWidth() != -1) { 226 result++; 227 } 228 } 229 return result; 230 } 231 232 233 private static class ColumnSizing { 234 private final double minWidth; 235 private final double maxWidth; 236 private final double prefWidth; 237 private final double percentWidth; 238 239 public ColumnSizing(ColumnConstraints cc) { 240 this.minWidth = cc.getMinWidth(); 241 this.maxWidth = cc.getMaxWidth(); 242 this.prefWidth = cc.getPrefWidth(); 243 this.percentWidth = cc.getPercentWidth(); 244 } 245 246 public double getMinWidth() { 247 return minWidth; 248 } 249 250 public double getMaxWidth() { 251 return maxWidth; 252 } 253 254 public double getPrefWidth() { 255 return prefWidth; 256 } 257 258 public double getPercentWidth() { 259 return percentWidth; 260 } 261 262 public void applyTo(ColumnConstraints cc) { 263 cc.setMinWidth(minWidth); 264 cc.setMaxWidth(maxWidth); 265 cc.setPrefWidth(prefWidth); 266 cc.setPercentWidth(percentWidth); 267 } 268 } 269 }