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.control; 027 028import com.sun.javafx.beans.IDProperty; 029import javafx.collections.ObservableSet; 030import javafx.css.PseudoClass; 031import javafx.css.Styleable; 032import javafx.css.CssMetaData; 033import javafx.beans.property.BooleanProperty; 034import javafx.beans.property.ObjectProperty; 035import javafx.beans.property.ObjectPropertyBase; 036import javafx.beans.property.SimpleBooleanProperty; 037import javafx.beans.property.SimpleObjectProperty; 038import javafx.beans.property.SimpleStringProperty; 039import javafx.beans.property.StringProperty; 040import javafx.collections.FXCollections; 041import javafx.collections.ObservableList; 042import javafx.event.ActionEvent; 043import javafx.event.Event; 044import javafx.event.EventDispatchChain; 045import javafx.event.EventHandler; 046import javafx.event.EventTarget; 047import javafx.event.EventType; 048import javafx.scene.Node; 049import javafx.scene.input.KeyCombination; 050 051import com.sun.javafx.event.EventHandlerManager; 052import com.sun.javafx.scene.control.skin.ContextMenuContent; 053import com.sun.javafx.scene.control.skin.ContextMenuSkin; 054import java.util.Collections; 055import java.util.HashMap; 056import java.util.List; 057 058import javafx.beans.property.ReadOnlyObjectProperty; 059import javafx.beans.property.ReadOnlyObjectWrapper; 060import javafx.collections.ObservableMap; 061import javafx.scene.Parent; 062 063/** 064 * <p> 065 * MenuItem is intended to be used in conjunction with {@link Menu} to provide 066 * options to users. MenuItem serves as the base class for the bulk of JavaFX menus 067 * API. 068 * It has a display {@link #getText() text} property, as well as an optional {@link #getGraphic() graphic} node 069 * that can be set on it. 070 * The {@link #getAccelerator() accelerator} property enables accessing the 071 * associated action in one keystroke. Also, as with the {@link Button} control, 072 * by using the {@link #setOnAction} method, you can have an instance of MenuItem 073 * perform any action you wish. 074 * <p> 075 * <b>Note:</b> Whilst any size of graphic can be inserted into a MenuItem, the most 076 * commonly used size in most applications is 16x16 pixels. This is 077 * the recommended graphic dimension to use if you're using the default style provided by 078 * JavaFX. 079 * <p> 080 * To create a MenuItem is simple: 081<pre><code> 082MenuItem menuItem = new MenuItem("Open"); 083menuItem.setOnAction(new EventHandler<ActionEvent>() { 084 @Override public void handle(ActionEvent e) { 085 System.out.println("Opening Database Connection..."); 086 } 087}); 088menuItem.setGraphic(new ImageView(new Image("flower.png"))); 089</code></pre> 090 * <p> 091 * Refer to the {@link Menu} page to learn how to insert MenuItem into a menu 092 * instance. Briefly however, you can insert the MenuItem from the previous 093 * example into a Menu as such: 094<pre><code> 095final Menu menu = new Menu("File"); 096menu.getItems().add(menuItem); 097</code></pre> 098 * 099 * @see Menu 100 */ 101@IDProperty("id") 102public class MenuItem implements EventTarget, Styleable { 103 104 /*************************************************************************** 105 * * 106 * Constructors * 107 * * 108 **************************************************************************/ 109 110 /** 111 * Constructs a MenuItem with no display text. 112 */ 113 public MenuItem() { 114 this(null,null); 115 } 116 117 /** 118 * Constructs a MenuItem and sets the display text with the specified text 119 * @see #setText 120 */ 121 public MenuItem(String text) { 122 this(text,null); 123 } 124 125 /** 126 * Constructor s MenuItem and sets the display text with the specified text 127 * and sets the graphic {@link Node} to the given node. 128 * @see #setText 129 * @see #setGraphic 130 */ 131 public MenuItem(String text, Node graphic) { 132 setText(text); 133 setGraphic(graphic); 134 styleClass.add(DEFAULT_STYLE_CLASS); 135 } 136 137 138 139 /*************************************************************************** 140 * * 141 * Instance Variables * 142 * * 143 **************************************************************************/ 144 145 private final ObservableList<String> styleClass = FXCollections.observableArrayList(); 146 147 final EventHandlerManager eventHandlerManager = 148 new EventHandlerManager(this); 149 150 private Object userData; 151 private ObservableMap<Object, Object> properties; 152 153 /*************************************************************************** 154 * * 155 * Properties * 156 * * 157 **************************************************************************/ 158 159 /** 160 * The id of this MenuItem. This simple string identifier is useful for finding 161 * a specific MenuItem within the scene graph. 162 */ 163 private StringProperty id; 164 public final void setId(String value) { idProperty().set(value); } 165 @Override public final String getId() { return id == null ? null : id.get(); } 166 public final StringProperty idProperty() { 167 if (id == null) { 168 id = new SimpleStringProperty(this, "id"); 169 } 170 return id; 171 } 172 173 /** 174 * A string representation of the CSS style associated with this specific MenuItem. 175 * This is analogous to the "style" attribute of an HTML element. Note that, 176 * like the HTML style attribute, this variable contains style properties and 177 * values and not the selector portion of a style rule. 178 */ 179 private StringProperty style; 180 public final void setStyle(String value) { styleProperty().set(value); } 181 @Override public final String getStyle() { return style == null ? null : style.get(); } 182 public final StringProperty styleProperty() { 183 if (style == null) { 184 style = new SimpleStringProperty(this, "style"); 185 } 186 return style; 187 } 188 189 // --- Parent Menu (useful for submenus) 190 /** 191 * This is the {@link Menu} in which this {@code MenuItem} exists. It is 192 * possible for an instance of this class to not have a {@code parentMenu} - 193 * this means that this instance is either: 194 * <ul> 195 * <li>Not yet associated with its {@code parentMenu}. 196 * <li>A 'root' {@link Menu} (i.e. it is a context menu, attached directly to a 197 * {@link MenuBar}, {@link MenuButton}, or any of the other controls that use 198 * {@link Menu} internally. 199 * </ul> 200 */ 201 private ReadOnlyObjectWrapper<Menu> parentMenu; 202 203 protected final void setParentMenu(Menu value) { 204 parentMenuPropertyImpl().set(value); 205 } 206 207 public final Menu getParentMenu() { 208 return parentMenu == null ? null : parentMenu.get(); 209 } 210 211 public final ReadOnlyObjectProperty<Menu> parentMenuProperty() { 212 return parentMenuPropertyImpl().getReadOnlyProperty(); 213 } 214 215 private ReadOnlyObjectWrapper<Menu> parentMenuPropertyImpl() { 216 if (parentMenu == null) { 217 parentMenu = new ReadOnlyObjectWrapper<Menu>(this, "parentMenu"); 218 } 219 return parentMenu; 220 } 221 222 223 // --- Parent Popup 224 /** 225 * This is the {@link ContextMenu} in which this {@code MenuItem} exists. 226 */ 227 private ReadOnlyObjectWrapper<ContextMenu> parentPopup; 228 229 protected final void setParentPopup(ContextMenu value) { 230 parentPopupPropertyImpl().set(value); 231 } 232 233 public final ContextMenu getParentPopup() { 234 return parentPopup == null ? null : parentPopup.get(); 235 } 236 237 public final ReadOnlyObjectProperty<ContextMenu> parentPopupProperty() { 238 return parentPopupPropertyImpl().getReadOnlyProperty(); 239 } 240 241 private ReadOnlyObjectWrapper<ContextMenu> parentPopupPropertyImpl() { 242 if (parentPopup == null) { 243 parentPopup = new ReadOnlyObjectWrapper<ContextMenu>(this, "parentPopup"); 244 } 245 return parentPopup; 246 } 247 248 249 // --- Text 250 /** 251 * The text to display in the {@code MenuItem}. 252 */ 253 private StringProperty text; 254 255 public final void setText(String value) { 256 textProperty().set(value); 257 } 258 259 public final String getText() { 260 return text == null ? null : text.get(); 261 } 262 263 public final StringProperty textProperty() { 264 if (text == null) { 265 text = new SimpleStringProperty(this, "text"); 266 } 267 return text; 268 } 269 270 271 // --- Graphic 272 /** 273 * An optional graphic for the {@code MenuItem}. This will normally be 274 * an {@link javafx.scene.image.ImageView} node, but there is no requirement for this to be 275 * the case. 276 */ 277 private ObjectProperty<Node> graphic; 278 279 public final void setGraphic(Node value) { 280 graphicProperty().set(value); 281 } 282 283 public final Node getGraphic() { 284 return graphic == null ? null : graphic.get(); 285 } 286 287 public final ObjectProperty<Node> graphicProperty() { 288 if (graphic == null) { 289 graphic = new SimpleObjectProperty<Node>(this, "graphic"); 290 } 291 return graphic; 292 } 293 294 295 // --- OnAction 296 /** 297 * The action, which is invoked whenever the MenuItem is fired. This 298 * may be due to the user clicking on the button with the mouse, or by 299 * a touch event, or by a key press, or if the developer programatically 300 * invokes the {@link #fire()} method. 301 */ 302 private ObjectProperty<EventHandler<ActionEvent>> onAction; 303 304 public final void setOnAction(EventHandler<ActionEvent> value) { 305 onActionProperty().set( value); 306 } 307 308 public final EventHandler<ActionEvent> getOnAction() { 309 return onAction == null ? null : onAction.get(); 310 } 311 312 public final ObjectProperty<EventHandler<ActionEvent>> onActionProperty() { 313 if (onAction == null) { 314 onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() { 315 @Override protected void invalidated() { 316 eventHandlerManager.setEventHandler(ActionEvent.ACTION, get()); 317 } 318 319 @Override 320 public Object getBean() { 321 return MenuItem.this; 322 } 323 324 @Override 325 public String getName() { 326 return "onAction"; 327 } 328 }; 329 } 330 return onAction; 331 } 332 333 /** 334 * <p>Called when a accelerator for the Menuitem is invoked</p> 335 */ 336 public static final EventType<Event> MENU_VALIDATION_EVENT = new EventType<Event> 337 (Event.ANY, "MENU_VALIDATION_EVENT"); 338 339 /** 340 * The event handler that is associated with invocation of an accelerator for a MenuItem. This 341 * can happen when a key sequence for an accelerator is pressed. The event handler is also 342 * invoked when onShowing event handler is called. 343 * @since 2.2 344 */ 345 private ObjectProperty<EventHandler<Event>> onMenuValidation; 346 347 public final void setOnMenuValidation(EventHandler<Event> value) { 348 onMenuValidationProperty().set( value); 349 } 350 351 public final EventHandler<Event> getOnMenuValidation() { 352 return onMenuValidation == null ? null : onMenuValidation.get(); 353 } 354 355 public final ObjectProperty<EventHandler<Event>> onMenuValidationProperty() { 356 if (onMenuValidation == null) { 357 onMenuValidation = new ObjectPropertyBase<EventHandler<Event>>() { 358 @Override protected void invalidated() { 359 eventHandlerManager.setEventHandler(MENU_VALIDATION_EVENT, get()); 360 } 361 @Override public Object getBean() { 362 return MenuItem.this; 363 } 364 @Override public String getName() { 365 return "onMenuValidation"; 366 } 367 }; 368 } 369 return onMenuValidation; 370 } 371 372 // --- Disable 373 /** 374 * Sets the individual disabled state of this MenuItem. 375 * Setting disable to true will cause this MenuItem to become disabled. 376 */ 377 private BooleanProperty disable; 378 public final void setDisable(boolean value) { disableProperty().set(value); } 379 public final boolean isDisable() { return disable == null ? false : disable.get(); } 380 public final BooleanProperty disableProperty() { 381 if (disable == null) { 382 disable = new SimpleBooleanProperty(this, "disable"); 383 } 384 return disable; 385 } 386 387 388 // --- Visible 389 /** 390 * Specifies whether this MenuItem should be rendered as part of the scene graph. 391 */ 392 private BooleanProperty visible; 393 public final void setVisible(boolean value) { visibleProperty().set(value); } 394 public final boolean isVisible() { return visible == null ? true : visible.get(); } 395 public final BooleanProperty visibleProperty() { 396 if (visible == null) { 397 visible = new SimpleBooleanProperty(this, "visible", true); 398 } 399 return visible; 400 } 401 402 /** 403 * The accelerator property enables accessing the associated action in one keystroke. 404 * It is a convenience offered to perform quickly a given action. 405 */ 406 private ObjectProperty<KeyCombination> accelerator; 407 public final void setAccelerator(KeyCombination value) { 408 acceleratorProperty().set(value); 409 } 410 public final KeyCombination getAccelerator() { 411 return accelerator == null ? null : accelerator.get(); 412 } 413 public final ObjectProperty<KeyCombination> acceleratorProperty() { 414 if (accelerator == null) { 415 accelerator = new SimpleObjectProperty<KeyCombination>(this, "accelerator"); 416 } 417 return accelerator; 418 } 419 420 /** 421 * MnemonicParsing property to enable/disable text parsing. 422 * If this is set to true, then the MenuItem text will be 423 * parsed to see if it contains the mnemonic parsing character '_'. 424 * When a mnemonic is detected the key combination will 425 * be determined based on the succeeding character, and the mnemonic 426 * added. 427 * 428 * <p> 429 * The default value for MenuItem is true. 430 * </p> 431 */ 432 private BooleanProperty mnemonicParsing; 433 public final void setMnemonicParsing(boolean value) { 434 mnemonicParsingProperty().set(value); 435 } 436 public final boolean isMnemonicParsing() { 437 return mnemonicParsing == null ? true : mnemonicParsing.get(); 438 } 439 public final BooleanProperty mnemonicParsingProperty() { 440 if (mnemonicParsing == null) { 441 mnemonicParsing = new SimpleBooleanProperty(this, "mnemonicParsing", true); 442 } 443 return mnemonicParsing; 444 } 445 446 /*************************************************************************** 447 * * 448 * Public API * 449 * * 450 **************************************************************************/ 451 452 @Override public ObservableList<String> getStyleClass() { 453 return styleClass; 454 } 455 456 /** 457 * Fires a new ActionEvent. 458 */ 459 public void fire() { 460 Event.fireEvent(this, new ActionEvent(this, this)); 461 } 462 463 /** 464 * Registers an event handler to this MenuItem. The handler is called when the 465 * menu item receives an {@code Event} of the specified type during the bubbling 466 * phase of event delivery. 467 * 468 * @param <E> the specific event class of the handler 469 * @param eventType the type of the events to receive by the handler 470 * @param eventHandler the handler to register 471 * @throws NullPointerException if the event type or handler is null 472 */ 473 public <E extends Event> void addEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) { 474 eventHandlerManager.addEventHandler(eventType, eventHandler); 475 } 476 477 /** 478 * Unregisters a previously registered event handler from this MenuItem. One 479 * handler might have been registered for different event types, so the 480 * caller needs to specify the particular event type from which to 481 * unregister the handler. 482 * 483 * @param <E> the specific event class of the handler 484 * @param eventType the event type from which to unregister 485 * @param eventHandler the handler to unregister 486 * @throws NullPointerException if the event type or handler is null 487 */ 488 public <E extends Event> void removeEventHandler(EventType<E> eventType, EventHandler<E> eventHandler) { 489 eventHandlerManager.removeEventHandler(eventType, eventHandler); 490 } 491 492 /** {@inheritDoc} */ 493 @Override public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) { 494 // FIXME review that these are configure properly 495 if (getParentPopup() != null) { 496 getParentPopup().buildEventDispatchChain(tail); 497 } 498 499 if (getParentMenu() != null) { 500 getParentMenu().buildEventDispatchChain(tail); 501 } 502 503 return tail.prepend(eventHandlerManager); 504 } 505 506 /** 507 * Returns a previously set Object property, or null if no such property 508 * has been set using the {@link MenuItem#setUserData(java.lang.Object)} method. 509 * 510 * @return The Object that was previously set, or null if no property 511 * has been set or if null was set. 512 */ 513 public Object getUserData() { 514 return userData; 515 } 516 517 /** 518 * Convenience method for setting a single Object property that can be 519 * retrieved at a later date. This is functionally equivalent to calling 520 * the getProperties().put(Object key, Object value) method. This can later 521 * be retrieved by calling {@link Node#getUserData()}. 522 * 523 * @param value The value to be stored - this can later be retrieved by calling 524 * {@link Node#getUserData()}. 525 */ 526 public void setUserData(Object value) { 527 this.userData = value; 528 } 529 530 /** 531 * Returns an observable map of properties on this menu item for use primarily 532 * by application developers. 533 * 534 * @return an observable map of properties on this menu item for use primarily 535 * by application developers 536 */ 537 public ObservableMap<Object, Object> getProperties() { 538 if (properties == null) { 539 properties = FXCollections.observableMap(new HashMap<Object, Object>()); 540 } 541 return properties; 542 } 543 544 /*************************************************************************** 545 * * 546 * Stylesheet Handling * 547 * * 548 **************************************************************************/ 549 550 private static final String DEFAULT_STYLE_CLASS = "menu-item"; 551 552 /** 553 * {@inheritDoc} 554 * @return "MenuItem" 555 */ 556 @Override 557 public String getTypeSelector() { 558 return "MenuItem"; 559 } 560 561 /** 562 * {@inheritDoc} 563 * @return {@code getParentMenu()}, or {@code getParentPopup()} 564 * if {@code parentMenu} is null 565 */ 566 @Override 567 public Styleable getStyleableParent() { 568 569 if(getParentMenu() == null) { 570 return getParentPopup(); 571 } else { 572 return getParentMenu(); 573 } 574 } 575 576 /** 577 * {@inheritDoc} 578 */ 579 public final ObservableSet<PseudoClass> getPseudoClassStates() { 580 return FXCollections.emptyObservableSet(); 581 } 582 583 @Override 584 public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { 585 return Collections.emptyList(); 586 } 587 588 public Node impl_styleableGetNode() { 589 // Fix for RT-20582. We dive into the visual representation 590 // of this MenuItem so that we may return it to the caller. 591 ContextMenu parentPopup = MenuItem.this.getParentPopup(); 592 if (parentPopup == null || ! (parentPopup.getSkin() instanceof ContextMenuSkin)) return null; 593 594 ContextMenuSkin skin = (ContextMenuSkin) parentPopup.getSkin(); 595 if (! (skin.getNode() instanceof ContextMenuContent)) return null; 596 597 ContextMenuContent content = (ContextMenuContent) skin.getNode(); 598 Parent nodes = content.getItemsContainer(); 599 600 MenuItem desiredMenuItem = MenuItem.this; 601 List<Node> childrenNodes = nodes.getChildrenUnmodifiable(); 602 for (int i = 0; i < childrenNodes.size(); i++) { 603 if (! (childrenNodes.get(i) instanceof ContextMenuContent.MenuItemContainer)) continue; 604 605 ContextMenuContent.MenuItemContainer MenuRow = 606 (ContextMenuContent.MenuItemContainer) childrenNodes.get(i); 607 608 if (desiredMenuItem.equals(MenuRow.getItem())) { 609 return MenuRow; 610 } 611 } 612 613 return null; 614 } 615}