when we right click for context menu, the first option in the list is being highlighted without hovering the mouse. This happens only for the first time right click after the application is opened. This behavior is observed from javafx-9. Till javafx-8 its working fine.
Tried with the sample code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
public class SampleContextMenu extends Application {
// labels
Label l;
public static void main(String args[]) {
// launch the application
launch(args);
}
// launch the application
public void start(Stage stage) {
// set title for the stage
stage.setTitle("creating contextMenu ");
// create a label
Label label1 = new Label("This is a ContextMenu example ");
// create a menu
ContextMenu contextMenu = new ContextMenu();
// create menuitems
MenuItem menuItem1 = new MenuItem("menu item 1");
MenuItem menuItem2 = new MenuItem("menu item 2");
MenuItem menuItem3 = new MenuItem("menu item 3");
// add menu items to menu
contextMenu.getItems().add(menuItem1);
contextMenu.getItems().add(menuItem2);
contextMenu.getItems().add(menuItem3);
// create a tilepane
TilePane tilePane = new TilePane(label1);
// setContextMenu to label
label1.setContextMenu(contextMenu);
// create a scene
Scene sc = new Scene(tilePane, 200, 200);
// set the scene
stage.setScene(sc);
stage.show();
}
}
After a bit of digging, turns out that the culprit (so to say) is the default focus traversal on initially showing of a scene - which is to focus the first focusable node, which in the case of a contextMenu is the first item.
First try of a hack-around: request the focus back onto the scene's root when the item is focused. The steps:
Beware: this is not good enough, turned out to be a cosmetic hack only, there are several glitches as noted in the comments
Next try (now going really dirty, requiring access to hidden implementation details of non-public classes!): replace the last step of the first try by
In code:
contextMenu.setOnShown(e -> {
Scene scene = contextMenu.getScene();
scene.focusOwnerProperty().addListener((src, ov, nv) -> {
// focusOwner set after first showing
if (ov == null) {
// transfer focus to root
// old hack (see the beware section) on why it doesn't work
// scene.getRoot().requestFocus();
// next try:
// grab the containing ContextMenuContainer and force the internal
// book-keeping into no-item-focused state
Parent parent = nv.getParent().getParent();
parent.requestFocus();
// reflective setting of private field, this is my utility method, use your own ;)
invokeSetFieldValue(ContextMenuContent.class, parent, "currentFocusedIndex", -1);
// cleanup
contextMenu.setOnShown(null);
}
});
});
For convenience, here's the utility method for reflective access of internal fields (no rocket sciene, just plain java ;)
public static void invokeSetFieldValue(Class<?> declaringClass, Object target, String name, Object value) {
try {
Field field = declaringClass.getDeclaredField(name);
field.setAccessible(true);
field.set(target, value);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With