Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.shape; 027 028import java.util.List; 029 030import javafx.beans.property.ObjectProperty; 031import javafx.beans.property.ObjectPropertyBase; 032import javafx.collections.ListChangeListener.Change; 033import javafx.collections.ObservableList; 034import javafx.geometry.BoundingBox; 035import javafx.geometry.Bounds; 036import javafx.scene.paint.Color; 037 038import com.sun.javafx.collections.TrackableObservableList; 039import javafx.css.CssMetaData; 040import com.sun.javafx.geom.Path2D; 041import com.sun.javafx.scene.DirtyBits; 042import com.sun.javafx.scene.shape.PathUtils; 043import com.sun.javafx.sg.PGNode; 044import com.sun.javafx.sg.PGPath; 045import com.sun.javafx.tk.Toolkit; 046import java.util.Collection; 047import javafx.css.StyleableProperty; 048import javafx.scene.paint.Paint; 049 050/** 051 * The {@code Path} class represents a simple shape 052 * and provides facilities required for basic construction 053 * and management of a geometric path. Example: 054 * 055<PRE> 056import javafx.scene.shape.*; 057 058Path path = new Path(); 059 060MoveTo moveTo = new MoveTo(); 061moveTo.setX(0.0f); 062moveTo.setY(0.0f); 063 064HLineTo hLineTo = new HLineTo(); 065hLineTo.setX(70.0f); 066 067QuadCurveTo quadCurveTo = new QuadCurveTo(); 068quadCurveTo.setX(120.0f); 069quadCurveTo.setY(60.0f); 070quadCurveTo.setControlX(100.0f); 071quadCurveTo.setControlY(0.0f); 072 073LineTo lineTo = new LineTo(); 074lineTo.setX(175.0f); 075lineTo.setY(55.0f); 076 077ArcTo arcTo = new ArcTo(); 078arcTo.setX(50.0f); 079arcTo.setY(50.0f); 080arcTo.setRadiusX(50.0f); 081arcTo.setRadiusY(50.0f); 082 083path.getElements().add(moveTo); 084path.getElements().add(hLineTo); 085path.getElements().add(quadCurveTo); 086path.getElements().add(lineTo); 087path.getElements().add(arcTo); 088 089</PRE> 090 */ 091public class Path extends Shape { 092 093 private Path2D path2d = null; 094 095 /** 096 * Creates an empty instance of Path. 097 */ 098 public Path() { 099 } 100 101 /** 102 * Creates a new instance of Path 103 * @param elements Elements of the Path 104 */ 105 public Path(PathElement... elements) { 106 if (elements != null) { 107 this.elements.addAll(elements); 108 } 109 } 110 111 /** 112 * Creates new instance of Path 113 * @param elements The collection of the elements of the Path 114 * @since 2.2 115 */ 116 public Path(Collection<? extends PathElement> elements) { 117 if (elements != null) { 118 this.elements.addAll(elements); 119 } 120 } 121 122 static com.sun.javafx.sg.PGPath.FillRule toPGFillRule(FillRule rule) { 123 if (rule == FillRule.NON_ZERO) { 124 return PGPath.FillRule.NON_ZERO; 125 } else { 126 return PGPath.FillRule.EVEN_ODD; 127 } 128 } 129 130 { 131 // overriding default values for fill and stroke 132 // Set through CSS property so that it appears to be a UA style rather 133 // that a USER style so that fill and stroke can still be set from CSS. 134 ((StyleableProperty)fillProperty()).applyStyle(null, null); 135 ((StyleableProperty)strokeProperty()).applyStyle(null, Color.BLACK); 136 } 137 138 void markPathDirty() { 139 path2d = null; 140 impl_markDirty(DirtyBits.NODE_CONTENTS); 141 impl_geomChanged(); 142 } 143 144 /** 145 * Defines the filling rule constant for determining the interior of the path. 146 * The value must be one of the following constants: 147 * {@code FillRile.EVEN_ODD} or {@code FillRule.NON_ZERO}. 148 * The default value is {@code FillRule.NON_ZERO}. 149 * 150 * @defaultValue FillRule.NON_ZERO 151 */ 152 private ObjectProperty<FillRule> fillRule; 153 154 public final void setFillRule(FillRule value) { 155 if (fillRule != null || value != FillRule.NON_ZERO) { 156 fillRuleProperty().set(value); 157 } 158 } 159 160 public final FillRule getFillRule() { 161 return fillRule == null ? FillRule.NON_ZERO : fillRule.get(); 162 } 163 164 public final ObjectProperty<FillRule> fillRuleProperty() { 165 if (fillRule == null) { 166 fillRule = new ObjectPropertyBase<FillRule>(FillRule.NON_ZERO) { 167 168 @Override 169 public void invalidated() { 170 impl_markDirty(DirtyBits.NODE_CONTENTS); 171 impl_geomChanged(); 172 } 173 174 @Override 175 public Object getBean() { 176 return Path.this; 177 } 178 179 @Override 180 public String getName() { 181 return "fillRule"; 182 } 183 }; 184 } 185 return fillRule; 186 } 187 188 private boolean isPathValid; 189 /** 190 * Defines the array of path elements of this path. 191 * 192 * @defaultValue empty 193 */ 194 private final ObservableList<PathElement> elements = new TrackableObservableList<PathElement>() { 195 @Override 196 protected void onChanged(Change<PathElement> c) { 197 List<PathElement> list = c.getList(); 198 boolean firstElementChanged = false; 199 while (c.next()) { 200 List<PathElement> removed = c.getRemoved(); 201 for (int i = 0; i < c.getRemovedSize(); ++i) { 202 removed.get(i).removeNode(Path.this); 203 } 204 for (int i = c.getFrom(); i < c.getTo(); ++i) { 205 list.get(i).addNode(Path.this); 206 } 207 firstElementChanged |= c.getFrom() == 0; 208 } 209 210 //Note: as ArcTo may create a various number of PathElements, 211 // we cannot count the number of PathElements removed (fast enough). 212 // Thus we can optimize only if some elements were added to the end 213 if (path2d != null) { 214 c.reset(); 215 c.next(); 216 // we just have to check the first change, as more changes cannot come after such change 217 if (c.getFrom() == c.getList().size() && !c.wasRemoved() && c.wasAdded()) { 218 // some elements added 219 for (int i = c.getFrom(); i < c.getTo(); ++i) { 220 list.get(i).impl_addTo(path2d); 221 } 222 } else { 223 path2d = null; 224 } 225 } 226 if (firstElementChanged) { 227 isPathValid = impl_isFirstPathElementValid(); 228 } 229 230 impl_markDirty(DirtyBits.NODE_CONTENTS); 231 impl_geomChanged(); 232 } 233 }; 234 235 /** 236 * Gets observable list of path elements of this path. 237 * @return Elements of this path 238 */ 239 public final ObservableList<PathElement> getElements() { return elements; } 240 241 /** 242 * @treatAsPrivate implementation detail 243 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 244 */ 245 @Deprecated 246 @Override 247 protected PGNode impl_createPGNode() { 248 return Toolkit.getToolkit().createPGPath(); 249 } 250 251 /** 252 * @treatAsPrivate implementation detail 253 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 254 */ 255 @Deprecated 256 public PGPath impl_getPGPath() { 257 return (PGPath)impl_getPGNode(); 258 } 259 260 /** 261 * @treatAsPrivate implementation detail 262 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 263 */ 264 @Deprecated 265 @Override 266 public Path2D impl_configShape() { 267 if (isPathValid) { 268 if (path2d == null) { 269 path2d = PathUtils.configShape(getElements(), getFillRule() == FillRule.EVEN_ODD); 270 } else { 271 path2d.setWindingRule(getFillRule() == FillRule.NON_ZERO ? 272 Path2D.WIND_NON_ZERO : Path2D.WIND_EVEN_ODD); 273 } 274 return path2d; 275 } else { 276 return new Path2D(); 277 } 278 } 279 280 /** 281 * @treatAsPrivate implementation detail 282 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 283 */ 284 @Deprecated 285 @Override 286 protected Bounds impl_computeLayoutBounds() { 287 if (isPathValid) { 288 return super.impl_computeLayoutBounds(); 289 } 290 return new BoundingBox(0, 0, -1, -1); //create empty bounds 291 } 292 293 private boolean impl_isFirstPathElementValid() { 294 ObservableList<PathElement> _elements = getElements(); 295 if (_elements != null && _elements.size() > 0) { 296 PathElement firstElement = _elements.get(0); 297 if (!firstElement.isAbsolute()) { 298 System.err.printf("First element of the path can not be relative. Path: %s\n", this); 299 return false; 300 } else if (firstElement instanceof MoveTo) { 301 return true; 302 } else { 303 System.err.printf("Missing initial moveto in path definition. Path: %s\n", this); 304 return false; 305 } 306 } 307 return true; 308 } 309 310 /** 311 * @treatAsPrivate implementation detail 312 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 313 */ 314 @Deprecated 315 @Override 316 public void impl_updatePG() { 317 super.impl_updatePG(); 318 319 if (impl_isDirty(DirtyBits.NODE_CONTENTS)) { 320 PGPath peer = impl_getPGPath(); 321 if (peer.acceptsPath2dOnUpdate()) { 322 peer.updateWithPath2d(impl_configShape()); 323 } else { 324 peer.reset(); 325 if (isPathValid) { 326 peer.setFillRule(toPGFillRule(getFillRule())); 327 for (final PathElement elt : getElements()) { 328 elt.addTo(peer); 329 } 330 peer.update(); 331 } 332 } 333 } 334 } 335 336 /*************************************************************************** 337 * * 338 * Stylesheet Handling * 339 * * 340 **************************************************************************/ 341 342 /** 343 * Some sub-class of Shape, such as {@link Line}, override the 344 * default value for the {@link Shape#fill} property. This allows 345 * CSS to get the correct initial value. 346 * @treatAsPrivate Implementation detail 347 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 348 */ 349 @Deprecated 350 protected Paint impl_cssGetFillInitialValue() { 351 return null; 352 } 353 354 /** 355 * Some sub-class of Shape, such as {@link Line}, override the 356 * default value for the {@link Shape#stroke} property. This allows 357 * CSS to get the correct initial value. 358 * @treatAsPrivate Implementation detail 359 * @deprecated This is an internal API that is not intended for use and will be removed in the next version 360 */ 361 @Deprecated 362 protected Paint impl_cssGetStrokeInitialValue() { 363 return Color.BLACK; 364 } 365 366 /** 367 * Returns a string representation of this {@code Path} object. 368 * @return a string representation of this {@code Path} object. 369 */ 370 @Override 371 public String toString() { 372 final StringBuilder sb = new StringBuilder("Path["); 373 374 String id = getId(); 375 if (id != null) { 376 sb.append("id=").append(id).append(", "); 377 } 378 379 sb.append("elements=").append(getElements()); 380 381 sb.append(", fill=").append(getFill()); 382 sb.append(", fillRule=").append(getFillRule()); 383 384 Paint stroke = getStroke(); 385 if (stroke != null) { 386 sb.append(", stroke=").append(stroke); 387 sb.append(", strokeWidth=").append(getStrokeWidth()); 388 } 389 390 return sb.append("]").toString(); 391 } 392}