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 javafx.css.PseudoClass; 029import javafx.beans.property.ObjectProperty; 030import javafx.beans.property.ObjectPropertyBase; 031import javafx.collections.FXCollections; 032import javafx.collections.ObservableList; 033import javafx.event.ActionEvent; 034import javafx.geometry.Side; 035import javafx.scene.Node; 036import com.sun.javafx.scene.control.skin.MenuButtonSkin; 037import javafx.beans.property.ReadOnlyBooleanProperty; 038import javafx.beans.property.ReadOnlyBooleanWrapper; 039 040/** 041 * MenuButton is a button which, when clicked or pressed, will show a 042 * {@link ContextMenu}. A MenuButton shares a very similar API to the {@link Menu} 043 * control, insofar that you set the items that should be shown in the 044 * {@link #items} ObservableList, and there is a {@link #text} property to specify the 045 * label shown within the MenuButton. 046 * <p> 047 * As mentioned, like the Menu API itself, you'll find an {@link #items} ObservableList 048 * within which you can provide any type of Node, although it is recommended to 049 * only insert controls that extend from {@link MenuItem}. There are several useful subclasses 050 * of {@link MenuItem} including {@link RadioMenuItem}, {@link CheckMenuItem}, 051 * {@link Menu}, {@link Separator} and {@link CustomMenuItem}. 052 * <p> 053 * A MenuButton can be set to show its menu on any side of the button. This is 054 * specified using the {@link #popupSideProperty() popupSide} property. By default 055 * the menu appears below the button. However, regardless of the popupSide specified, 056 * if there is not enough room, the {@link ContextMenu} will be 057 * smartly repositioned, most probably to be on the opposite side of the 058 * MenuButton. 059 * 060 * <p>Example:</p> 061 * <pre> 062 * MenuButton m = new MenuButton("Eats"); 063 * m.getItems().addAll(new MenuItem("Burger"), new MenuItem("Hot Dog")); 064 * </pre> 065 * 066 * <p> 067 * MnemonicParsing is enabled by default for MenuButton. 068 * </p> 069 * 070 * @see MenuItem 071 * @see Menu 072 * @see SplitMenuButton 073 */ 074public class MenuButton extends ButtonBase { 075 076 /*************************************************************************** 077 * * 078 * Constructors * 079 * * 080 **************************************************************************/ 081 082 /** 083 * Creates a new empty menu button. Use {@link #setText(String)}, 084 * {@link #setGraphic(Node)} and {@link #getItems()} to set the content. 085 */ 086 public MenuButton() { 087 this(null, null); 088 } 089 090 /** 091 * Creates a new empty menu button with the given text to display on the 092 * menu. Use {@link #setGraphic(Node)} and {@link #getItems()} to set the 093 * content. 094 * 095 * @param text the text to display on the menu button 096 */ 097 public MenuButton(String text) { 098 this(text, null); 099 } 100 101 /** 102 * Creates a new empty menu button with the given text and graphic to 103 * display on the menu. Use {@link #getItems()} to set the content. 104 * 105 * @param text the text to display on the menu button 106 * @param graphic the graphic to display on the menu button 107 */ 108 public MenuButton(String text, Node graphic) { 109 if (text != null) { 110 setText(text); 111 } 112 if (graphic != null) { 113 setGraphic(graphic); 114 } 115 getStyleClass().setAll(DEFAULT_STYLE_CLASS); 116 setMnemonicParsing(true); // enable mnemonic auto-parsing by default 117 // the default value for popupSide = Side.BOTTOM therefor 118 // PSEUDO_CLASS_OPENVERTICALLY should be set from the start. 119 pseudoClassStateChanged(PSEUDO_CLASS_OPENVERTICALLY, true); 120 } 121 122 /*************************************************************************** 123 * * 124 * Properties * 125 * * 126 **************************************************************************/ 127 private final ObservableList<MenuItem> items = FXCollections.<MenuItem>observableArrayList(); 128 129 /** 130 * The items to show within this buttons menu. If this ObservableList is modified 131 * at runtime, the Menu will update as expected. 132 * <p> 133 * Commonly used controls include including {@code MenuItem}, 134 * {@code CheckMenuItem}, {@code RadioMenuItem}, 135 * and of course {@code Menu}, which if added to a menu, will become a sub 136 * menu. {@link SeparatorMenuItem} is another commonly used Node in the Menu's items 137 * ObservableList. 138 */ 139 public final ObservableList<MenuItem> getItems() { 140 return items; 141 } 142 143 // --- Showing 144 private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper(this, "showing", false) { 145 @Override protected void invalidated() { 146 pseudoClassStateChanged(PSEUDO_CLASS_SHOWING, get()); 147 super.invalidated(); 148 } 149 }; 150 private void setShowing(boolean value) { showing.set(value); } 151 /** 152 * Indicates whether the {@link ContextMenu} is currently visible. 153 */ 154 public final boolean isShowing() { return showing.get(); } 155 public final ReadOnlyBooleanProperty showingProperty() { return showing.getReadOnlyProperty(); } 156 157 158 159 /** 160 * Indicates on which side the {@link ContextMenu} should open in 161 * relation to the MenuButton. Menu items are generally laid 162 * out vertically in either case. 163 * For example, if the menu button were in a vertical toolbar on the left 164 * edge of the application, you might change {@link #popupSide} to {@code Side.RIGHT} so that 165 * the popup will appear to the right of the MenuButton. 166 * 167 * @defaultValue {@code Side.BOTTOM} 168 */ 169 // TODO expose via CSS 170 private ObjectProperty<Side> popupSide; 171 172 public final void setPopupSide(Side value) { 173 popupSideProperty().set(value); 174 } 175 176 public final Side getPopupSide() { 177 return popupSide == null ? Side.BOTTOM : popupSide.get(); 178 } 179 180 public final ObjectProperty<Side> popupSideProperty() { 181 if (popupSide == null) { 182 popupSide = new ObjectPropertyBase<Side>(Side.BOTTOM) { 183 @Override protected void invalidated() { 184 final Side side = get(); 185 final boolean active = (side == Side.TOP) || (side == Side.BOTTOM); 186 pseudoClassStateChanged(PSEUDO_CLASS_OPENVERTICALLY, active); 187 } 188 189 @Override 190 public Object getBean() { 191 return MenuButton.this; 192 } 193 194 @Override 195 public String getName() { 196 return "popupSide"; 197 } 198 }; 199 } 200 return popupSide; 201 } 202 203 /*************************************************************************** 204 * * 205 * Control methods * 206 * * 207 **************************************************************************/ 208 209 /** 210 * Shows the {@link ContextMenu}, assuming this MenuButton is not disabled. 211 * 212 * @see #isDisabled() 213 * @see #isShowing() 214 */ 215 public void show() { 216 // TODO: isBound check is probably unnecessary here 217 if (!isDisabled() && !showing.isBound()) { 218 setShowing(true); 219 } 220 } 221 222 /** 223 * Hides the {@link ContextMenu}. 224 * 225 * @see #isShowing() 226 */ 227 public void hide() { 228 // TODO: isBound check is probably unnecessary here 229 if (!showing.isBound()) { 230 setShowing(false); 231 } 232 } 233 234 /** 235 * This has no impact. 236 */ 237 @Override 238 public void fire() { 239 fireEvent(new ActionEvent()); 240 } 241 242 /** {@inheritDoc} */ 243 @Override protected Skin<?> createDefaultSkin() { 244 return new MenuButtonSkin(this); 245 } 246 247 /*************************************************************************** 248 * * 249 * Stylesheet Handling * 250 * * 251 **************************************************************************/ 252 253 private static final String DEFAULT_STYLE_CLASS = "menu-button"; 254 private static final PseudoClass PSEUDO_CLASS_OPENVERTICALLY = 255 PseudoClass.getPseudoClass("openvertically"); 256 private static final PseudoClass PSEUDO_CLASS_SHOWING = 257 PseudoClass.getPseudoClass("showing"); 258 259}