/*
* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.control;
import javafx.css.PseudoClass;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Side;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleRole;
import javafx.scene.Node;
import javafx.scene.control.skin.MenuButtonSkin;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
MenuButton is a button which, when clicked or pressed, will show a ContextMenu
. A MenuButton shares a very similar API to the Menu
control, insofar that you set the items that should be shown in the items
ObservableList, and there is a text
property to specify the label shown within the MenuButton. As mentioned, like the Menu API itself, you'll find an items
ObservableList within which you can provide anything that extends from MenuItem
. There are several useful subclasses of MenuItem
including RadioMenuItem
, CheckMenuItem
, Menu
, SeparatorMenuItem
and CustomMenuItem
.
A MenuButton can be set to show its menu on any side of the button. This is specified using the popupSide
property. By default the menu appears below the button. However, regardless of the popupSide specified, if there is not enough room, the ContextMenu
will be smartly repositioned, most probably to be on the opposite side of the MenuButton.
Example:
MenuButton m = new MenuButton("Eats");
m.getItems().addAll(new MenuItem("Burger"), new MenuItem("Hot Dog"));
MnemonicParsing is enabled by default for MenuButton.
See Also: Since: JavaFX 2.0
/**
* MenuButton is a button which, when clicked or pressed, will show a
* {@link ContextMenu}. A MenuButton shares a very similar API to the {@link Menu}
* control, insofar that you set the items that should be shown in the
* {@link #getItems() items} ObservableList, and there is a {@link #textProperty() text} property to specify the
* label shown within the MenuButton.
* <p>
* As mentioned, like the Menu API itself, you'll find an {@link #getItems() items} ObservableList
* within which you can provide anything that extends from {@link MenuItem}.
* There are several useful subclasses of {@link MenuItem} including
* {@link RadioMenuItem}, {@link CheckMenuItem}, {@link Menu},
* {@link SeparatorMenuItem} and {@link CustomMenuItem}.
* <p>
* A MenuButton can be set to show its menu on any side of the button. This is
* specified using the {@link #popupSideProperty() popupSide} property. By default
* the menu appears below the button. However, regardless of the popupSide specified,
* if there is not enough room, the {@link ContextMenu} will be
* smartly repositioned, most probably to be on the opposite side of the
* MenuButton.
*
* <p>Example:</p>
* <pre>
* MenuButton m = new MenuButton("Eats");
* m.getItems().addAll(new MenuItem("Burger"), new MenuItem("Hot Dog"));
* </pre>
*
* <p>
* MnemonicParsing is enabled by default for MenuButton.
* </p>
*
* @see MenuItem
* @see Menu
* @see SplitMenuButton
* @since JavaFX 2.0
*/
public class MenuButton extends ButtonBase {
/***************************************************************************
* *
* Static properties and methods *
* *
**************************************************************************/
Called prior to the MenuButton showing its popup after the user
has clicked or otherwise interacted with the MenuButton.
Since: JavaFX 8u60
/**
* Called prior to the MenuButton showing its popup after the user
* has clicked or otherwise interacted with the MenuButton.
* @since JavaFX 8u60
*/
public static final EventType<Event> ON_SHOWING =
new EventType<Event>(Event.ANY, "MENU_BUTTON_ON_SHOWING");
Called after the MenuButton has shown its popup.
Since: JavaFX 8u60
/**
* Called after the MenuButton has shown its popup.
* @since JavaFX 8u60
*/
public static final EventType<Event> ON_SHOWN =
new EventType<Event>(Event.ANY, "MENU_BUTTON_ON_SHOWN");
Called when the MenuButton popup will be hidden.
Since: JavaFX 8u60
/**
* Called when the MenuButton popup <b>will</b> be hidden.
* @since JavaFX 8u60
*/
public static final EventType<Event> ON_HIDING =
new EventType<Event>(Event.ANY, "MENU_BUTTON_ON_HIDING");
Called when the MenuButton popup has been hidden.
Since: JavaFX 8u60
/**
* Called when the MenuButton popup has been hidden.
* @since JavaFX 8u60
*/
public static final EventType<Event> ON_HIDDEN =
new EventType<Event>(Event.ANY, "MENU_BUTTON_ON_HIDDEN");
/***************************************************************************
* *
* Constructors *
* *
**************************************************************************/
Creates a new empty menu button. Use Labeled.setText(String)
, Labeled.setGraphic(Node)
and getItems()
to set the content. /**
* Creates a new empty menu button. Use {@link #setText(String)},
* {@link #setGraphic(Node)} and {@link #getItems()} to set the content.
*/
public MenuButton() {
this(null, null);
}
Creates a new empty menu button with the given text to display on the button. Use Labeled.setGraphic(Node)
and getItems()
to set the content. Params: - text – the text to display on the menu button
/**
* Creates a new empty menu button with the given text to display on the
* button. Use {@link #setGraphic(Node)} and {@link #getItems()} to set the
* content.
*
* @param text the text to display on the menu button
*/
public MenuButton(String text) {
this(text, null);
}
Creates a new empty menu button with the given text and graphic to display on the button. Use getItems()
to set the content. Params: - text – the text to display on the menu button
- graphic – the graphic to display on the menu button
/**
* Creates a new empty menu button with the given text and graphic to
* display on the button. Use {@link #getItems()} to set the content.
*
* @param text the text to display on the menu button
* @param graphic the graphic to display on the menu button
*/
public MenuButton(String text, Node graphic) {
this(text, graphic, (MenuItem[])null);
}
Creates a new menu button with the given text and graphic to display on the button, and inserts the given items into the items
list. Params: - text – the text to display on the menu button
- graphic – the graphic to display on the menu button
- items – The items to display in the popup menu.
Since: JavaFX 8u40
/**
* Creates a new menu button with the given text and graphic to
* display on the button, and inserts the given items
* into the {@link #getItems() items} list.
*
* @param text the text to display on the menu button
* @param graphic the graphic to display on the menu button
* @param items The items to display in the popup menu.
* @since JavaFX 8u40
*/
public MenuButton(String text, Node graphic, MenuItem... items) {
if (text != null) {
setText(text);
}
if (graphic != null) {
setGraphic(graphic);
}
if (items != null) {
getItems().addAll(items);
}
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
setAccessibleRole(AccessibleRole.MENU_BUTTON);
setMnemonicParsing(true); // enable mnemonic auto-parsing by default
// the default value for popupSide = Side.BOTTOM therefor
// PSEUDO_CLASS_OPENVERTICALLY should be set from the start.
pseudoClassStateChanged(PSEUDO_CLASS_OPENVERTICALLY, true);
}
*
Properties *
*
/***************************************************************************
* *
* Properties *
* *
**************************************************************************/
private final ObservableList<MenuItem> items = FXCollections.<MenuItem>observableArrayList();
The items to show within this buttons menu. If this ObservableList is modified
at runtime, the Menu will update as expected.
Commonly used controls include including MenuItem
, CheckMenuItem
, RadioMenuItem
, and of course Menu
, which if added to a menu, will become a sub menu. SeparatorMenuItem
is another commonly used Node in the Menu's items ObservableList.
Returns: the list of menu items within this buttons menu
/**
* The items to show within this buttons menu. If this ObservableList is modified
* at runtime, the Menu will update as expected.
* <p>
* Commonly used controls include including {@code MenuItem},
* {@code CheckMenuItem}, {@code RadioMenuItem},
* and of course {@code Menu}, which if added to a menu, will become a sub
* menu. {@link SeparatorMenuItem} is another commonly used Node in the Menu's items
* ObservableList.
* @return the list of menu items within this buttons menu
*/
public final ObservableList<MenuItem> getItems() {
return items;
}
// --- Showing
Indicates whether the ContextMenu
is currently visible. /**
* Indicates whether the {@link ContextMenu} is currently visible.
*/
private ReadOnlyBooleanWrapper showing = new ReadOnlyBooleanWrapper(this, "showing", false) {
@Override protected void invalidated() {
pseudoClassStateChanged(PSEUDO_CLASS_SHOWING, get());
super.invalidated();
}
};
private void setShowing(boolean value) {
// these events will not fire if the showing property is bound
Event.fireEvent(this, value ? new Event(ON_SHOWING) :
new Event(ON_HIDING));
showing.set(value);
Event.fireEvent(this, value ? new Event(ON_SHOWN) :
new Event(ON_HIDDEN));
}
public final boolean isShowing() { return showing.get(); }
public final ReadOnlyBooleanProperty showingProperty() { return showing.getReadOnlyProperty(); }
Indicates on which side the ContextMenu
should open in relation to the MenuButton. Menu items are generally laid out vertically in either case. For example, if the menu button were in a vertical toolbar on the left edge of the application, you might change popupSide
to Side.RIGHT
so that the popup will appear to the right of the MenuButton. @defaultValue Side.BOTTOM
/**
* Indicates on which side the {@link ContextMenu} should open in
* relation to the MenuButton. Menu items are generally laid
* out vertically in either case.
* For example, if the menu button were in a vertical toolbar on the left
* edge of the application, you might change {@link #popupSideProperty() popupSide}
* to {@code Side.RIGHT} so that the popup will appear to the right of the MenuButton.
*
* @defaultValue {@code Side.BOTTOM}
*/
// TODO expose via CSS
private ObjectProperty<Side> popupSide;
public final void setPopupSide(Side value) {
popupSideProperty().set(value);
}
public final Side getPopupSide() {
return popupSide == null ? Side.BOTTOM : popupSide.get();
}
public final ObjectProperty<Side> popupSideProperty() {
if (popupSide == null) {
popupSide = new ObjectPropertyBase<Side>(Side.BOTTOM) {
@Override protected void invalidated() {
final Side side = get();
final boolean active = (side == Side.TOP) || (side == Side.BOTTOM);
pseudoClassStateChanged(PSEUDO_CLASS_OPENVERTICALLY, active);
}
@Override
public Object getBean() {
return MenuButton.this;
}
@Override
public String getName() {
return "popupSide";
}
};
}
return popupSide;
}
Called just prior to the ContextMenu
being shown. Returns: the on showing property Since: 10
/**
* Called just prior to the {@code ContextMenu} being shown.
* @return the on showing property
* @since 10
*/
public final ObjectProperty<EventHandler<Event>> onShowingProperty() { return onShowing; }
public final void setOnShowing(EventHandler<Event> value) { onShowingProperty().set(value); }
public final EventHandler<Event> getOnShowing() { return onShowingProperty().get(); }
private ObjectProperty<EventHandler<Event>> onShowing = new ObjectPropertyBase<EventHandler<Event>>() {
@Override protected void invalidated() {
setEventHandler(ON_SHOWING, get());
}
@Override public Object getBean() {
return MenuButton.this;
}
@Override public String getName() {
return "onShowing";
}
};
Called just after the ContextMenu
is shown. Returns: the on shown property Since: 10
/**
* Called just after the {@code ContextMenu} is shown.
* @return the on shown property
* @since 10
*/
public final ObjectProperty<EventHandler<Event>> onShownProperty() { return onShown; }
public final void setOnShown(EventHandler<Event> value) { onShownProperty().set(value); }
public final EventHandler<Event> getOnShown() { return onShownProperty().get(); }
private ObjectProperty<EventHandler<Event>> onShown = new ObjectPropertyBase<EventHandler<Event>>() {
@Override protected void invalidated() {
setEventHandler(ON_SHOWN, get());
}
@Override public Object getBean() {
return MenuButton.this;
}
@Override public String getName() {
return "onShown";
}
};
Called just prior to the ContextMenu
being hidden. Returns: the on hiding property Since: 10
/**
* Called just prior to the {@code ContextMenu} being hidden.
* @return the on hiding property
* @since 10
*/
public final ObjectProperty<EventHandler<Event>> onHidingProperty() { return onHiding; }
public final void setOnHiding(EventHandler<Event> value) { onHidingProperty().set(value); }
public final EventHandler<Event> getOnHiding() { return onHidingProperty().get(); }
private ObjectProperty<EventHandler<Event>> onHiding = new ObjectPropertyBase<EventHandler<Event>>() {
@Override protected void invalidated() {
setEventHandler(ON_HIDING, get());
}
@Override public Object getBean() {
return MenuButton.this;
}
@Override public String getName() {
return "onHiding";
}
};
Called just after the ContextMenu
has been hidden. Returns: the on hidden property Since: 10
/**
* Called just after the {@code ContextMenu} has been hidden.
* @return the on hidden property
* @since 10
*/
public final ObjectProperty<EventHandler<Event>> onHiddenProperty() { return onHidden; }
public final void setOnHidden(EventHandler<Event> value) { onHiddenProperty().set(value); }
public final EventHandler<Event> getOnHidden() { return onHiddenProperty().get(); }
private ObjectProperty<EventHandler<Event>> onHidden = new ObjectPropertyBase<EventHandler<Event>>() {
@Override protected void invalidated() {
setEventHandler(ON_HIDDEN, get());
}
@Override public Object getBean() {
return MenuButton.this;
}
@Override public String getName() {
return "onHidden";
}
};
/***************************************************************************
* *
* Control methods *
* *
**************************************************************************/
Shows the ContextMenu
, assuming this MenuButton is not disabled. See Also:
/**
* Shows the {@link ContextMenu}, assuming this MenuButton is not disabled.
*
* @see #isDisabled()
* @see #isShowing()
*/
public void show() {
// TODO: isBound check is probably unnecessary here
if (!isDisabled() && !showing.isBound()) {
setShowing(true);
}
}
Hides the ContextMenu
. See Also:
/**
* Hides the {@link ContextMenu}.
*
* @see #isShowing()
*/
public void hide() {
// TODO: isBound check is probably unnecessary here
if (!showing.isBound()) {
setShowing(false);
}
}
This has no impact.
/**
* This has no impact.
*/
@Override
public void fire() {
if (!isDisabled()) {
fireEvent(new ActionEvent());
}
}
{@inheritDoc} /** {@inheritDoc} */
@Override protected Skin<?> createDefaultSkin() {
return new MenuButtonSkin(this);
}
*
Stylesheet Handling *
*
/***************************************************************************
* *
* Stylesheet Handling *
* *
**************************************************************************/
private static final String DEFAULT_STYLE_CLASS = "menu-button";
private static final PseudoClass PSEUDO_CLASS_OPENVERTICALLY =
PseudoClass.getPseudoClass("openvertically");
private static final PseudoClass PSEUDO_CLASS_SHOWING =
PseudoClass.getPseudoClass("showing");
/***************************************************************************
* *
* Accessibility handling *
* *
**************************************************************************/
{@inheritDoc} /** {@inheritDoc} */
@Override
public void executeAccessibleAction(AccessibleAction action, Object... parameters) {
switch (action) {
case FIRE:
if (isShowing()) {
hide();
} else {
show();
}
break;
default: super.executeAccessibleAction(action);
}
}
}