Itai
Itai

Reputation: 6921

JavaFX: TabPane context menu, depending on selected tab

I want to have a TabPane with multiple tabs, with a context menu different for every tab.

Setting the ContextMenu on the TabPane obviously results in the same ContextMenu used regardless of selected tab. I tried setting different ContextMenus on different Tabs, but this has two unwanted effects:

  1. The context menu is only opened when right-clicking on the tab header (whereas setting it on the TabPane allows right-clicking anywhere inside the tab). I want the user to be able to access the Context Menu from anywhere inside the tab, not just the header.

  2. Right-clicking on one tab header while another is selected still opens the context menu of the tab clicked on - I want the context menu to depend on the tab currently selected.

The only way I could think of how to do it is to catch onContextMenuRequested, see which tab is currently selected, and set various MenuItems visible/invisible, but that seems pretty silly. Is there a better way of doing this?

Edit: Calrification - the tab contents are Panes (VBox, GridPane, etc.) so setting the ContextMenu directly on the content is sadly impossible.

Upvotes: 0

Views: 3298

Answers (1)

James_D
James_D

Reputation: 209694

I can see two solutions to this. One is to set a single context menu on the tab pane. Register a listener with the selected tab, and repopulate the context menu when the selection changes.

The other solution is just to set the context menu on the content of the tabs. Note that you can set a context menu on any node, by registering a handler for the contextMenuRequested event, and show the context menu. You can set the same context menu on the tab.

This example demonstrates both techniques:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public class TabPanesWithContextMenu extends Application {

    @Override
    public void start(Stage primaryStage) {
        TabPane tabPane1 = new TabPane();
        ContextMenu contextMenu = new ContextMenu();
        tabPane1.setContextMenu(contextMenu);

        List<MenuItem> tab1Items = new ArrayList<>();
        tab1Items.add(new MenuItem("Choice 1"));
        tab1Items.add(new MenuItem("Choice 2"));

        List<MenuItem> tab2Items = new ArrayList<>();
        tab2Items.add(new MenuItem("Choice 3"));
        tab2Items.add(new MenuItem("Choice 4"));

        Tab tab1 = new Tab("Tab 1");
        tab1.setContent(new Pane());
        Tab tab2 = new Tab("Tab 2");
        tab2.setContent(new Pane());
        tabPane1.getTabs().addAll(tab1, tab2);

        Map<Tab, List<MenuItem>> itemsByTab = new HashMap<>();
        itemsByTab.put(tab1, tab1Items);
        itemsByTab.put(tab2, tab2Items);

        tabPane1.getSelectionModel().selectedItemProperty().addListener((obs, oldTab, newTab) -> 
           contextMenu.getItems().setAll(itemsByTab.get(newTab)) );

        contextMenu.getItems().addAll(tab1Items);

        TabPane tabPane2 = new TabPane();

        Tab tab3 =  createTabWithContextMenu("Tab 3", new MenuItem("Choice 5"), new MenuItem("Choice 6"));
        Tab tab4 =  createTabWithContextMenu("Tab 4", new MenuItem("Choice 7"), new MenuItem("Choice 8"));

        tabPane2.getTabs().addAll(tab3, tab4);

        HBox root = new HBox(10, tabPane1, tabPane2);

        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

    private Tab createTabWithContextMenu(String title, MenuItem... items) {
        Tab tab = new Tab(title);
        ContextMenu contextMenu = new ContextMenu(items);
        tab.setContextMenu(contextMenu);

        Pane content = new Pane();
        content.setOnContextMenuRequested(e -> 
            contextMenu.show(content, e.getScreenX(), e.getScreenY()));
        tab.setContent(content);

        return tab ;
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Upvotes: 2

Related Questions