javafx webview / webengine上的自定义上下文菜单

时间:2014-11-20 19:01:32

标签: java javafx

如何在WebEngine javafx中为整个文档输入设置自定义上下文菜单?
像这样的东西

+------------+
|Reload      |
|Save page   |
|Hide Images |
+------------+

我喜欢调用并显示整个文档条目的上下文弹出窗口(每个节点都相同)。感谢。

2 个答案:

答案 0 :(得分:12)

我没有看到与默认上下文菜单进行交互的方法。但是,禁用它并实现自己的并不困难。

使用

禁用默认上下文菜单
webView.setContextMenuEnabled();

然后创建自己的上下文菜单,并在Web视图中注册一个鼠标监听器,以便在右键单击时显示:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import javafx.stage.Stage;


public class WebViewContextMenuTest extends Application {

    private final String START_URL = 
            "http://stackoverflow.com/questions/27047447/customized-context-menu-on-javafx-webview-webengine/27047830#27047830";

    @Override
    public void start(Stage primaryStage) {
        TextField locationField = new TextField(START_URL);
        WebView webView = new WebView();
        webView.getEngine().load(START_URL);

        webView.setContextMenuEnabled(false);
        createContextMenu(webView);

        locationField.setOnAction(e -> {
            webView.getEngine().load(getUrl(locationField.getText()));
        });
        BorderPane root = new BorderPane(webView, locationField, null, null, null);
        primaryStage.setScene(new Scene(root, 800, 600));
        primaryStage.show();

    }

    private void createContextMenu(WebView webView) {
        ContextMenu contextMenu = new ContextMenu();
        MenuItem reload = new MenuItem("Reload");
        reload.setOnAction(e -> webView.getEngine().reload());
        MenuItem savePage = new MenuItem("Save Page");
        savePage.setOnAction(e -> System.out.println("Save page..."));
        MenuItem hideImages = new MenuItem("Hide Images");
        hideImages.setOnAction(e -> System.out.println("Hide Images..."));
        contextMenu.getItems().addAll(reload, savePage, hideImages);

        webView.setOnMousePressed(e -> {
            if (e.getButton() == MouseButton.SECONDARY) {
                contextMenu.show(webView, e.getScreenX(), e.getScreenY());
            } else {
                contextMenu.hide();
            }
        });
    }

    private String getUrl(String text) {
        if (text.indexOf("://")==-1) {
            return "http://" + text ;
        } else {
            return text ;
        }
    }

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

答案 1 :(得分:9)

对此没有简单的解决方案,因为没有公共API,request仍未解决。

hacky解决方案使用了一些私有API,所以它不太合适,因为它可能会在没有通知的情况下发生变化。

当用户右键单击网页时显示的ContextMenu位于另一个窗口中,因此使用某些查找我们会尝试找到它,然后访问其内容然后修改现有内容或添加更多内容MenuItem秒。

这些是所需的私人课程:

import com.sun.javafx.scene.control.skin.ContextMenuContent;
import com.sun.javafx.scene.control.skin.ContextMenuContent.MenuItemContainer;

在我们的应用程序中,我们监听上下文菜单请求:

@Override
public void start(Stage primaryStage) {
    WebView webView = new WebView();
    WebEngine webEngine = webView.getEngine();
    Scene scene = new Scene(webView);

    primaryStage.setScene(scene);
    primaryStage.show();

    webView.setOnContextMenuRequested(new EventHandler<ContextMenuEvent>() {

        @Override
        public void handle(ContextMenuEvent e) {
            getPopupWindow();
        }
    });

}

getPopupWindow()将:

  • 寻找新窗口为ContextMenu
  • 的实例
  • 使用lookup查找CSS选择器context-menu。这是一个节点,其唯一的子节点为ContextMenuContent实例。
  • 此对象的VBox作为所有项目的容器,MenuItem位于特殊容器中MenuItemContainer
  • 我们可以访问任何现有项目,例如重新加载页面,返回,...以及自定义项目,修改文本或添加图形。
  • 我们可以将自定义项添加到此框中,提供我们自己的操作。

根据需要自定义项目:

private PopupWindow getPopupWindow() {
    @SuppressWarnings("deprecation") 
    final Iterator<Window> windows = Window.impl_getWindows();

    while (windows.hasNext()) {
        final Window window = windows.next();

        if (window instanceof ContextMenu) {
            if(window.getScene()!=null && window.getScene().getRoot()!=null){ 
                Parent root = window.getScene().getRoot();

                // access to context menu content
                if(root.getChildrenUnmodifiable().size()>0){
                    Node popup = root.getChildrenUnmodifiable().get(0);
                    if(popup.lookup(".context-menu")!=null){
                        Node bridge = popup.lookup(".context-menu");
                        ContextMenuContent cmc= (ContextMenuContent)((Parent)bridge).getChildrenUnmodifiable().get(0);

                        VBox itemsContainer = cmc.getItemsContainer();
                        for(Node n: itemsContainer.getChildren()){
                            MenuItemContainer item=(MenuItemContainer)n;
                            // customize text:
                            item.getItem().setText("My Custom: "+item.getItem().getText());
                            // customize graphic:
                            item.getItem().setGraphic(new ImageView(new Image(getClass().getResource("unlock24.png").toExternalForm())));
                        }
                        // remove some item:
                        // itemsContainer.getChildren().remove(0);

                        // adding new item:
                        MenuItem menuItem = new MenuItem("Save page");
                        menuItem.setOnAction(new EventHandler<ActionEvent>() {

                            @Override
                            public void handle(ActionEvent e) {
                                System.out.println("Save Page");
                            }
                        });
                        // add new item:
                        cmc.getItemsContainer().getChildren().add(cmc.new MenuItemContainer(menuItem));

                        return (PopupWindow)window;
                    }
                }
            }
            return null;
        }
    }
    return null;
}

这就是它的样子:

Custom ContextMenu