All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javafx.scene.control.ContextMenu Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2010, 2021, 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 com.sun.javafx.beans.IDProperty;
import com.sun.javafx.collections.TrackableObservableList;
import com.sun.javafx.util.Utils;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Point2D;
import javafx.geometry.Side;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.skin.ContextMenuSkin;
import javafx.stage.Window;

/**
 * 

* A popup control containing an ObservableList of menu items. The {@link #getItems() items} * ObservableList allows for any {@link MenuItem} type to be inserted, * including its subclasses {@link Menu}, {@link MenuItem}, {@link RadioMenuItem}, {@link CheckMenuItem} and * {@link CustomMenuItem}. If an arbitrary Node needs to be * inserted into a menu, a CustomMenuItem can be used. One exception to this general rule is that * {@link SeparatorMenuItem} could be used for inserting a separator. *

* A common use case for this class is creating and showing context menus to * users. To create a context menu using ContextMenu you can do the * following: *

final ContextMenu contextMenu = new ContextMenu();
contextMenu.setOnShowing(new EventHandler<WindowEvent>() {
    public void handle(WindowEvent e) {
        System.out.println("showing");
    }
});
contextMenu.setOnShown(new EventHandler<WindowEvent>() {
    public void handle(WindowEvent e) {
        System.out.println("shown");
    }
});

MenuItem item1 = new MenuItem("About");
item1.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent e) {
        System.out.println("About");
    }
});
MenuItem item2 = new MenuItem("Preferences");
item2.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent e) {
        System.out.println("Preferences");
    }
});
contextMenu.getItems().addAll(item1, item2);

final TextField textField = new TextField("Type Something");
textField.setContextMenu(contextMenu);
* * Image of the ContextMenu control * *

{@link Control#setContextMenu(javafx.scene.control.ContextMenu) } convenience * method can be used to set a context menu on on any control. The example above results in the * context menu being displayed on the right {@link javafx.geometry.Side Side} * of the TextField. Alternatively, an event handler can also be set on the control * to invoke the context menu as shown below. *

textField.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent e) {
        contextMenu.show(textField, Side.BOTTOM, 0, 0);
    }
});

Group root = (Group) scene.getRoot();
root.getChildren().add(textField);
* *

In this example, the context menu is shown when the user clicks on the * {@link javafx.scene.control.Button Button} (of course, you should use the * {@link MenuButton} control to do this rather than doing the above).

* *

Note that the show function used in the code sample * above will result in the ContextMenu appearing directly beneath the * TextField. You can vary the {@link javafx.geometry.Side Side} to get the results you expect.

* * @see MenuItem * @see Menu * @since JavaFX 2.0 */ @IDProperty("id") public class ContextMenu extends PopupControl { /* ************************************************************************* * * * Fields * * * **************************************************************************/ private boolean showRelativeToWindow = false; /* ************************************************************************* * * * Constructors * * * **************************************************************************/ /** * Create a new ContextMenu */ public ContextMenu() { getStyleClass().setAll(DEFAULT_STYLE_CLASS); setAutoHide(true); setConsumeAutoHidingEvents(false); } /** * Create a new ContextMenu initialized with the given items * @param items the list of menu items */ public ContextMenu(MenuItem... items) { this(); this.items.addAll(items); } /* ************************************************************************* * * * Properties * * * **************************************************************************/ /** * Callback function to be informed when an item contained within this * {@code ContextMenu} has been activated. The current implementation informs * all parent menus as well, so that it is not necessary to listen to all * sub menus for events. */ private ObjectProperty> onAction = new ObjectPropertyBase>() { @Override protected void invalidated() { setEventHandler(ActionEvent.ACTION, get()); } @Override public Object getBean() { return ContextMenu.this; } @Override public String getName() { return "onAction"; } }; public final void setOnAction(EventHandler value) { onActionProperty().set(value); } public final EventHandler getOnAction() { return onActionProperty().get(); } public final ObjectProperty> onActionProperty() { return onAction; } private final ObservableList items = new TrackableObservableList() { @Override protected void onChanged(Change c) { while (c.next()) { for (MenuItem item : c.getRemoved()) { item.setParentPopup(null); } for (MenuItem item : c.getAddedSubList()) { if (item.getParentPopup() != null) { // we need to remove this item from its current parentPopup // as a MenuItem should not exist in multiple parentPopup // instances item.getParentPopup().getItems().remove(item); } item.setParentPopup(ContextMenu.this); } } } }; /* ************************************************************************* * * * Public API * * * **************************************************************************/ /** * The menu items on the context menu. If this ObservableList is modified at * runtime, the ContextMenu will update as expected. * @return the menu items on this context menu * @see MenuItem */ public final ObservableList getItems() { return items; } /** * Shows the {@code ContextMenu} relative to the given anchor node, on the side * specified by the {@code side} parameter, and offset * by the given {@code dx} and {@code dy} values for the x-axis and y-axis, respectively. * If there is not enough room, the menu is moved to the opposite side and * the offset is not applied. *

* To clarify the purpose of the {@code side} parameter, * consider that it is relative to the anchor node. * As such, a {@code side} of {@code TOP} would mean that the ContextMenu's bottom left corner * is set to the top left corner of the anchor. *

* This function is useful for finely tuning the position of a menu, * relative to the parent node to ensure close alignment. * @param anchor the anchor node * @param side the side * @param dx the dx value for the x-axis * @param dy the dy value for the y-axis */ public void show(Node anchor, Side side, double dx, double dy) { if (anchor == null) return; if (getItems().size() == 0) return; getScene().setNodeOrientation(anchor.getEffectiveNodeOrientation()); if (getScene().getStylesheets().isEmpty()) { getScene().getStylesheets().setAll(anchor.getScene().getStylesheets()); } HPos hpos = side == Side.LEFT ? HPos.LEFT : side == Side.RIGHT ? HPos.RIGHT : HPos.CENTER; VPos vpos = side == Side.TOP ? VPos.TOP : side == Side.BOTTOM ? VPos.BOTTOM : VPos.CENTER; // translate from anchor/hpos/vpos/dx/dy into screenX/screenY Point2D point = Utils.pointRelativeTo(anchor, prefWidth(-1), prefHeight(-1), hpos, vpos, dx, dy, true); doShow(anchor, point.getX(), point.getY()); } /** * Shows the {@code ContextMenu} at the specified screen coordinates. If there * is not enough room at the specified location to show the {@code ContextMenu} * given its size requirements, the necessary adjustments are made to bring * the {@code ContextMenu} back on screen. This also means that the * {@code ContextMenu} will not span multiple monitors. * @param anchor the anchor node * @param screenX the x position of the anchor in screen coordinates * @param screenY the y position of the anchor in screen coordinates */ @Override public void show(Node anchor, double screenX, double screenY) { if (anchor == null) return; if (getItems().size() == 0) return; getScene().setNodeOrientation(anchor.getEffectiveNodeOrientation()); doShow(anchor, screenX, screenY); } /** * Hides this {@code ContextMenu} and any visible submenus, assuming that when this function * is called that the {@code ContextMenu} was showing. *

* If this {@code ContextMenu} is not showing, then nothing happens. */ @Override public void hide() { if (!isShowing()) return; Event.fireEvent(this, new Event(Menu.ON_HIDING)); super.hide(); Event.fireEvent(this, new Event(Menu.ON_HIDDEN)); } /** {@inheritDoc} */ @Override protected Skin createDefaultSkin() { return new ContextMenuSkin(this); } /* ************************************************************************* * * * Private Implementation * * * **************************************************************************/ final boolean isShowRelativeToWindow() { return showRelativeToWindow; } final void setShowRelativeToWindow(boolean value) { showRelativeToWindow = value; } private void doShow(Node anchor, double screenX, double screenY) { Event.fireEvent(this, new Event(Menu.ON_SHOWING)); if(isShowRelativeToWindow()) { final Scene scene = (anchor == null) ? null : anchor.getScene(); final Window win = (scene == null) ? null : scene.getWindow(); if (win == null) return; super.show(win, screenX, screenY); } else { super.show(anchor, screenX, screenY); } Event.fireEvent(this, new Event(Menu.ON_SHOWN)); } /* ************************************************************************* * * * Stylesheet Handling * * * ***************************************************************************/ private static final String DEFAULT_STYLE_CLASS = "context-menu"; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy