I have a piece of code in JavaFX which creates a small web browser. The web page is shown in a web view. I have managed to get custom context menu popped up by right clicking on the links on the webpage. The context menu has a menu item labeled "open in new tab". However, I have a strange problem. If I click on the "open in new tab", sometimes related webpage opens in new tab, but other times it fails to do so. Please note that if I implement the functionality without any context menu and using right click alone directly on the link, the link opens every time in new tab. How do we describe the situation? Is it a threading issue? I have tested the code on Windows 7 64-bit and Windows 8.1 64-bit. I am using BlueJ on Java 17 and JavaFX 20.
Here is what I have tried.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.scene.web.WebEvent;
import javafx.stage.Stage;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
public class QuickTab extends Application
{
TabPane tpane;
TextField urlField;
WebView webView;
String url;
public void start(Stage stage)
{
tpane = new TabPane();
Tab urlTab = new Tab();
HBox box = new HBox();
urlField = new TextField();
urlField.setPromptText("Enter URL");
Button goBtn = new Button("Go");
goBtn.setOnAction(e -> load_page());
box.getChildren().addAll(urlField, goBtn);
urlTab.setGraphic(box);
tpane.getTabs().add(urlTab);
stage.setScene(new Scene(tpane, 400, 300));
stage.show();
}
private void load_page()
{
webView = new WebView();
WebEngine engine = webView.getEngine();
webView.setContextMenuEnabled(false);//***
Tab tab = new Tab("home tab");
tab.setContent(webView);
tpane.getTabs().add(tab);
engine.setOnStatusChanged((WebEvent<String> event) -> {
url = event.getData();
});
engine.load(urlField.getText());
webView.setOnMouseClicked(new TabOpener());
}
private class TabOpener implements EventHandler<MouseEvent>
{
public void handle(MouseEvent e)
{
if(e.getButton() == MouseButton.SECONDARY)
{
ContextMenu cmenu = new ContextMenu();
MenuItem item = new MenuItem("open in new tab");
cmenu.getItems().addAll(item);
cmenu.show(webView, e.getScreenX(), e.getScreenY());
item.setOnAction(event -> {
Platform.runLater(() -> load_newPage());
});
}
e.consume();
}
private void load_newPage()
{
WebView newView = new WebView();
WebEngine newEngine = newView.getEngine();
Tab newTab = new Tab(url);
tpane.getTabs().add(newTab);
newTab.setContent(newView);
newEngine.load(url);
}
}
}
It's not clear how your program is going awry, but I see distinct code paths to add a new
Tab
: one gets a context listener, one doesn't.One approach is to minimize unrelated class level attributes, while aggregating relevant fields with the corresponding
Tab
. In the variation below,All
Tab
instances have access to the containingTabPane
and a sharedContextMenu
; the remaining attributes are local to eachWebTab
.The initial
Tab
opens on theHOME
page.Each
Tab
gets an editable URL; edit and press Enter to load the page; press Tab to change tabs.The context menu simply opens the current page in a new
Tab
, allowing continued browsing in the currentTab
.Also consider isolating individual links, illustrated here, or parsing the document, shown here.