Search code examples
javajavafxjavafx-webengine

JavaFX disable highlight and copy mode in WebEngine


I'm switching from JEditorPane to WebEngine(JavaFX).
I used to lock the text highlighting(selecting) in JEditorPane as following.

my_editor.setEditable(false);
my_editor.getInputMap().put(KeyStroke.getKeyStroke("control C"), "none");

Now I like to do the same with WebEngine, how may I do this? disabling copy, highlighting and editing mode. Thanks.


Solution

  • If you want to disable copy, highlighting and editing from JavaFX, without the use of Javascript, one way to do it is by trapping the events and deal accordingly with them, leaving the rest of the options intact.

    Let's use an event dispatcher to filter a few events:

    For key events:

    • Avoid copy with Ctrl+C or Ctrl+Insert
    • Avoid selection with shift+Arrow

    For Mouse events:

    • Avoid selection of word, line, paragraph with mouse click
    • Avoid selection with mouse dragging, but allowing dragging the scrollbars

    (Others could be added if you need to)

    public class WebEventDispatcher implements EventDispatcher {
    
        private final EventDispatcher oldDispatcher;
        private Point2D limit;
    
        public WebEventDispatcher(EventDispatcher oldDispatcher) {
            this.oldDispatcher = oldDispatcher;
        }
    
        public void setLimit(Point2D limit){
            this.limit = limit;
        }
    
        private boolean allowDrag=false;
    
        @Override
        public Event dispatchEvent(Event event, EventDispatchChain tail) {
            if (event instanceof MouseEvent){
                MouseEvent m = (MouseEvent)event;
                if (event.getEventType().equals(MouseEvent.MOUSE_CLICKED) || 
                    event.getEventType().equals(MouseEvent.MOUSE_PRESSED)) {
                    Point2D origin=new Point2D(m.getX(),m.getY());
                    allowDrag=!(origin.getX()<limit.getX() && origin.getY()<limit.getY());
                }
                // avoid selection with mouse dragging, allowing dragging the scrollbars
                if (event.getEventType().equals(MouseEvent.MOUSE_DRAGGED)) {
                    if(!allowDrag){
                        event.consume();
                    }
                }
                // Avoid selection of word, line, paragraph with mouse click
                if(m.getClickCount()>1){
                    event.consume();
                }
            }
            if (event instanceof KeyEvent && event.getEventType().equals(KeyEvent.KEY_PRESSED)){
                KeyEvent k= (KeyEvent)event;
                // Avoid copy with Ctrl+C or Ctrl+Insert
                if((k.getCode().equals(KeyCode.C) || k.getCode().equals(KeyCode.INSERT)) && k.isControlDown()){
                    event.consume();
                }
                // Avoid selection with shift+Arrow
                if(k.isShiftDown() && (k.getCode().equals(KeyCode.RIGHT) || k.getCode().equals(KeyCode.LEFT) ||
                    k.getCode().equals(KeyCode.UP) || k.getCode().equals(KeyCode.DOWN))){
                    event.consume();
                }
            }
            return oldDispatcher.dispatchEvent(event, tail);
        }
    }
    

    Now on your scene, disable context menu to avoid copy/paste options, find the content area of the webview without the scrollbars, if any, and set the custom event dispatcher.

    private Point2D pLimit;
    private double width, height;
    
    @Override
    public void start(Stage primaryStage) {
        WebView webView = new WebView();
        WebEngine webEngine = webView.getEngine();
    
        // disable context menu (copy option)
        webView.setContextMenuEnabled(false);
    
        WebEventDispatcher webEventDispatcher = new WebEventDispatcher(webView.getEventDispatcher());
        webEngine.getLoadWorker().stateProperty().addListener(new ChangeListener<State>() {
    
            @Override
            public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
                if(newValue.equals(State.SUCCEEDED)){
                    // dispatch all events
                    webView.setEventDispatcher(webEventDispatcher);
                }
            }
    
        });
        webEngine.load("<URL>");
    
        Scene scene = new Scene(webView);
    
        primaryStage.setTitle("WebView scrollbar test");
        primaryStage.setScene(scene);
        primaryStage.show();
    
        webView.getChildrenUnmodifiable().addListener(new ListChangeListener<Node>() {
    
            @Override
            public void onChanged(Change<? extends Node> c) {
                pLimit=webView.localToScene(webView.getWidth(),webView.getHeight());
                webView.lookupAll(".scroll-bar").stream()
                        .map(s->(ScrollBar)s).forEach(s->{
                            if(s.getOrientation().equals(VERTICAL)){
                                width=s.getBoundsInLocal().getWidth();
                            }
                            if(s.getOrientation().equals(HORIZONTAL)){
                                height=s.getBoundsInLocal().getHeight();
                            }
                        });
                // dispatch all events
                webEventDispatcher.setLimit(pLimit.subtract(width, height));
            }
        });
    
    }