diff --git a/src/main/java/com/faforever/client/chat/AbstractChatTabController.java b/src/main/java/com/faforever/client/chat/AbstractChatTabController.java index 0978dbb748..ea6ebb692e 100644 --- a/src/main/java/com/faforever/client/chat/AbstractChatTabController.java +++ b/src/main/java/com/faforever/client/chat/AbstractChatTabController.java @@ -64,6 +64,7 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.function.Consumer; import java.util.regex.Matcher; @@ -163,8 +164,6 @@ protected void onInitialize() { mentionPattern = Pattern.compile( "(^|[^A-Za-z0-9-])" + Pattern.quote(loginService.getUsername()) + "([^A-Za-z0-9-]|$)", CASE_INSENSITIVE); - initChatView(); - chatChannel.when(attached).subscribe(((oldValue, newValue) -> { if (oldValue != null) { oldValue.openProperty().unbind(); @@ -294,13 +293,12 @@ protected void onClosed(Event event) { protected abstract TextInputControl messageTextField(); - private void initChatView() { - WebView messagesWebView = getMessagesWebView(); - webViewConfigurer.configureWebView(messagesWebView); + protected void configureWebView(WebView webView) { + webViewConfigurer.configureWebView(webView); - messagesWebView.zoomProperty().bindBidirectional(chatPrefs.zoomProperty()); + webView.zoomProperty().bindBidirectional(chatPrefs.zoomProperty()); - configureBrowser(messagesWebView); + configureBrowser(webView); loadChatContainer(); } @@ -323,10 +321,6 @@ private void loadChatContainer() { private void configureBrowser(WebView messagesWebView) { engine = messagesWebView.getEngine(); - configureLoadListener(); - } - - private void configureLoadListener() { engine.getLoadWorker() .stateProperty() .addListener((observable, oldValue, newValue) -> sendWaitingMessagesIfLoaded(newValue)); @@ -345,13 +339,17 @@ private void sendWaitingMessagesIfLoaded(State newValue) { } } - protected abstract WebView getMessagesWebView(); + protected abstract CompletableFuture getMessagesWebView(); protected JSObject getJsObject() { return (JSObject) engine.executeScript("window"); } protected void callJsMethod(String methodName, Object... args) { + if (engine == null) { + return; + } + try { getJsObject().call(methodName, args); } catch (Exception e) { @@ -628,7 +626,7 @@ protected String convertUrlsToHyperlinks(String text) { private void insertIntoContainer(String html, String containerId) { ((JSObject) engine.executeScript("document.getElementById('" + containerId + "')")).call("insertAdjacentHTML", "beforeend", html); - getMessagesWebView().requestLayout(); + getMessagesWebView().thenAcceptAsync(WebView::requestLayout, fxApplicationThreadExecutor); } /** diff --git a/src/main/java/com/faforever/client/chat/ChannelTabController.java b/src/main/java/com/faforever/client/chat/ChannelTabController.java index 4b3531a77f..21517aede1 100644 --- a/src/main/java/com/faforever/client/chat/ChannelTabController.java +++ b/src/main/java/com/faforever/client/chat/ChannelTabController.java @@ -36,6 +36,7 @@ import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.HBox; +import javafx.scene.layout.StackPane; import javafx.scene.text.TextFlow; import javafx.scene.web.WebView; import lombok.extern.slf4j.Slf4j; @@ -47,6 +48,7 @@ import java.time.Instant; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import static com.faforever.client.fx.PlatformService.URL_REGEX_PATTERN; @@ -70,7 +72,7 @@ public class ChannelTabController extends AbstractChatTabController { public HBox chatMessageSearchContainer; public Button closeChatMessageSearchButton; public TextField chatMessageSearchTextField; - public WebView messagesWebView; + public StackPane webViewContainer; public TextField messageTextField; public HBox topicPane; public Label topicCharactersLimitLabel; @@ -88,6 +90,8 @@ public class ChannelTabController extends AbstractChatTabController { FXCollections.emptyObservableList()); private final ListChangeListener channelUserListChangeListener = this::updateChangedUsersStyles; + private CompletableFuture webViewInitializationFuture; + public ChannelTabController(WebViewConfigurer webViewConfigurer, LoginService loginService, ChatService chatService, PlayerService playerService, @@ -167,6 +171,13 @@ protected void onInitialize() { AutoCompletionHelper autoCompletionHelper = getAutoCompletionHelper(); autoCompletionHelper.bindTo(messageTextField()); + + webViewInitializationFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + webViewContainer.getChildren().add(webView); + configureWebView(webView); + return webView; + }, fxApplicationThreadExecutor); } @Override @@ -397,7 +408,7 @@ protected TextInputControl messageTextField() { } @Override - protected WebView getMessagesWebView() { - return messagesWebView; + protected CompletableFuture getMessagesWebView() { + return webViewInitializationFuture; } } diff --git a/src/main/java/com/faforever/client/chat/MatchmakingChatController.java b/src/main/java/com/faforever/client/chat/MatchmakingChatController.java index bdcf4020dc..2ffecde78a 100644 --- a/src/main/java/com/faforever/client/chat/MatchmakingChatController.java +++ b/src/main/java/com/faforever/client/chat/MatchmakingChatController.java @@ -22,6 +22,7 @@ import javafx.scene.control.Tab; import javafx.scene.control.TextField; import javafx.scene.control.TextInputControl; +import javafx.scene.layout.StackPane; import javafx.scene.text.TextFlow; import javafx.scene.web.WebView; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -31,6 +32,7 @@ import java.time.Instant; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CompletableFuture; @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @@ -38,8 +40,8 @@ public class MatchmakingChatController extends AbstractChatTabController { private final JoinDiscordEventHandler joinDiscordEventHandler; + public StackPane webViewContainer; public Tab matchmakingChatTabRoot; - public WebView messagesWebView; public TextField messageTextField; public TextFlow topicText; public Hyperlink discordLink; @@ -54,6 +56,8 @@ public class MatchmakingChatController extends AbstractChatTabController { } }; + private CompletableFuture webViewInitializationFuture; + // TODO cut dependencies public MatchmakingChatController(LoginService loginService, PlayerService playerService, TimeService timeService, I18n i18n, @@ -96,6 +100,13 @@ protected void onInitialize() { }).toList(); topicText.getChildren().setAll(labels); topicText.getChildren().add(discordLink); + + webViewInitializationFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + webViewContainer.getChildren().add(webView); + configureWebView(webView); + return webView; + }, fxApplicationThreadExecutor); } @Override @@ -123,8 +134,8 @@ protected TextInputControl messageTextField() { } @Override - protected WebView getMessagesWebView() { - return messagesWebView; + protected CompletableFuture getMessagesWebView() { + return webViewInitializationFuture; } public void onDiscordButtonClicked() { diff --git a/src/main/java/com/faforever/client/chat/PrivateChatTabController.java b/src/main/java/com/faforever/client/chat/PrivateChatTabController.java index af77524a46..4edc487ade 100644 --- a/src/main/java/com/faforever/client/chat/PrivateChatTabController.java +++ b/src/main/java/com/faforever/client/chat/PrivateChatTabController.java @@ -28,6 +28,7 @@ import javafx.scene.control.TextInputControl; import javafx.scene.image.ImageView; import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; import javafx.scene.web.WebView; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -36,6 +37,7 @@ import java.time.Instant; import java.util.List; +import java.util.concurrent.CompletableFuture; import static com.faforever.client.player.SocialStatus.FOE; @@ -48,14 +50,15 @@ public class PrivateChatTabController extends AbstractChatTabController { public Tab privateChatTabRoot; public ImageView avatarImageView; public Region defaultIconImageView; - public WebView messagesWebView; public TextField messageTextField; public PrivatePlayerInfoController privatePlayerInfoController; public ScrollPane gameDetailScrollPane; + public StackPane webViewContainer; private final ListChangeListener usersChangeListener = this::handlerPlayerChange; private boolean userOffline; + private CompletableFuture webViewInitializationFuture; @Autowired // TODO cut dependencies @@ -104,6 +107,13 @@ protected void onInitialize() { newValue.addUsersListeners(usersChangeListener); } })); + + webViewInitializationFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + webViewContainer.getChildren().add(webView); + configureWebView(webView); + return webView; + }, fxApplicationThreadExecutor); } @Override @@ -136,8 +146,8 @@ protected TextInputControl messageTextField() { } @Override - protected WebView getMessagesWebView() { - return messagesWebView; + protected CompletableFuture getMessagesWebView() { + return webViewInitializationFuture; } @Override diff --git a/src/main/java/com/faforever/client/coop/CoopController.java b/src/main/java/com/faforever/client/coop/CoopController.java index 3dace01d1d..0ae5588441 100644 --- a/src/main/java/com/faforever/client/coop/CoopController.java +++ b/src/main/java/com/faforever/client/coop/CoopController.java @@ -28,7 +28,6 @@ import com.faforever.client.util.TimeService; import com.faforever.commons.lobby.GameStatus; import com.faforever.commons.lobby.GameType; -import com.github.benmanes.caffeine.cache.CacheLoader; import com.google.common.base.Strings; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -49,6 +48,7 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; import javafx.scene.web.WebView; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -63,6 +63,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.function.Predicate; @@ -97,7 +98,7 @@ public class CoopController extends NodeController { public GridPane coopRoot; public ComboBox missionComboBox; - public WebView descriptionWebView; + public StackPane webViewContainer; public Pane gameViewContainer; public TextField titleTextField; public Button playButton; @@ -116,6 +117,8 @@ public class CoopController extends NodeController { public TableColumn replayColumn; public GamesTableController gamesTableController; + private CompletableFuture webViewInitializationFuture; + @Override protected void onInitialize() { missionComboBox.setCellFactory(param -> missionListCell()); @@ -190,9 +193,14 @@ protected void onInitialize() { tooltip.setShowDuration(javafx.util.Duration.seconds(30)); Tooltip.install(leaderboardInfoIcon, tooltip); - // Without this and no coop missions, the WebView is empty and the transparent background can't be applied to - descriptionWebView.getEngine().loadContent(""); - webViewConfigurer.configureWebView(descriptionWebView); + webViewInitializationFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + + // Without this and no coop missions, the WebView is empty and the transparent background can't be applied to + webView.getEngine().loadContent(""); + webViewConfigurer.configureWebView(webView); + return webView; + }, fxApplicationThreadExecutor); FilteredList filteredItems = new FilteredList<>(gameService.getGames()); filteredItems.setPredicate(OPEN_COOP_GAMES_PREDICATE); @@ -310,7 +318,8 @@ private void setSelectedMission(CoopMissionBean mission) { fxApplicationThreadExecutor.execute(() -> { String description = mission.getDescription(); if (description != null) { - descriptionWebView.getEngine().loadContent(description); + webViewInitializationFuture.thenAcceptAsync(webView -> webView.getEngine().loadContent(description), + fxApplicationThreadExecutor); } }); loadLeaderboard(); diff --git a/src/main/java/com/faforever/client/fx/DualStringListCellController.java b/src/main/java/com/faforever/client/fx/DualStringListCellController.java index 4c50692b29..1749fecf96 100644 --- a/src/main/java/com/faforever/client/fx/DualStringListCellController.java +++ b/src/main/java/com/faforever/client/fx/DualStringListCellController.java @@ -4,6 +4,7 @@ import com.google.common.base.Strings; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; import javafx.scene.layout.HBox; import javafx.scene.text.Font; import javafx.scene.web.WebView; @@ -12,16 +13,31 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import java.util.concurrent.CompletableFuture; + @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @RequiredArgsConstructor public class DualStringListCellController extends NodeController { private final ThemeService themeService; + private final FxApplicationThreadExecutor fxApplicationThreadExecutor; public HBox root; public Label left; public Label right; - public WebView webViewToolTip; + public Tooltip tooltip; + + private CompletableFuture webViewInitializeFuture; + + @Override + protected void onInitialize() { + webViewInitializeFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + themeService.registerWebView(webView); + tooltip.setGraphic(webView); + return webView; + }, fxApplicationThreadExecutor); + } public void setLeftText(String apply) { left.setText(apply); @@ -29,10 +45,10 @@ public void setLeftText(String apply) { public void setRightText(String apply) { right.setText(apply); } - public void setWebViewToolTip(String apply) { - if (!Strings.isNullOrEmpty(apply)) { - themeService.registerWebView(webViewToolTip); - webViewToolTip.getEngine().loadContent(apply); + public void setWebViewToolTip(String content) { + if (!Strings.isNullOrEmpty(content)) { + webViewInitializeFuture.thenAcceptAsync(webView -> webView.getEngine().loadContent(content), + fxApplicationThreadExecutor); } } diff --git a/src/main/java/com/faforever/client/news/NewsController.java b/src/main/java/com/faforever/client/news/NewsController.java index a58f8299db..968285fffc 100644 --- a/src/main/java/com/faforever/client/news/NewsController.java +++ b/src/main/java/com/faforever/client/news/NewsController.java @@ -4,10 +4,7 @@ import com.faforever.client.config.ClientProperties.Website; import com.faforever.client.fx.FxApplicationThreadExecutor; import com.faforever.client.fx.NodeController; -import com.faforever.client.fx.SimpleChangeListener; import com.faforever.client.fx.WebViewConfigurer; -import com.faforever.client.main.event.NavigateEvent; -import com.faforever.client.theme.UiService; import javafx.concurrent.Worker; import javafx.scene.Node; import javafx.scene.control.Control; @@ -18,6 +15,8 @@ import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import java.util.concurrent.CompletableFuture; + @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @RequiredArgsConstructor @@ -25,32 +24,42 @@ public class NewsController extends NodeController { private final WebViewConfigurer webViewConfigurer; private final ClientProperties clientProperties; private final FxApplicationThreadExecutor fxApplicationThreadExecutor; - private final UiService uiService; public Pane newsRoot; - public WebView newsWebView; public Control loadingIndicator; - private final SimpleChangeListener loadingIndicatorListener = newValue - -> loadingIndicator.getParent().getChildrenUnmodifiable().stream() - .filter(node -> node != loadingIndicator) - .forEach(node -> node.setVisible(!newValue)); + + private WebView newsWebView; @Override protected void onInitialize() { - newsWebView.setContextMenuEnabled(false); - webViewConfigurer.configureWebView(newsWebView); loadingIndicator.managedProperty().bind(loadingIndicator.visibleProperty()); - loadingIndicator.visibleProperty().addListener(loadingIndicatorListener); - loadingIndicatorListener.changed(loadingIndicator.visibleProperty(), null, true); + loadingIndicator.visibleProperty() + .subscribe(newValue -> loadingIndicator.getParent() + .getChildrenUnmodifiable() + .stream() + .filter(node1 -> node1 != loadingIndicator) + .forEach(node1 -> node1.setVisible(!newValue))); loadingIndicator.getParent().getChildrenUnmodifiable() .forEach(node -> node.managedProperty().bind(node.visibleProperty())); - newsWebView.getEngine().getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> { - if (newState == Worker.State.SUCCEEDED) { - onLoadingStop(); - } - }); + + onLoadingStart(); + + CompletableFuture.runAsync(() -> { + newsWebView = new WebView(); + newsWebView.setVisible(false); + newsRoot.getChildren().add(newsWebView); + newsWebView.setContextMenuEnabled(false); + webViewConfigurer.configureWebView(newsWebView); + newsWebView.getEngine().getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> { + if (newState == Worker.State.SUCCEEDED) { + onLoadingStop(); + } + }); + Website website = clientProperties.getWebsite(); + newsWebView.getEngine().load(website.getNewsHubUrl()); + }, fxApplicationThreadExecutor); } private void onLoadingStart() { @@ -61,19 +70,6 @@ private void onLoadingStop() { fxApplicationThreadExecutor.execute(() -> loadingIndicator.setVisible(false)); } - @Override - protected void onNavigate(NavigateEvent navigateEvent) { - onLoadingStart(); - loadNews(); - } - - private void loadNews() { - fxApplicationThreadExecutor.execute(() -> { - Website website = clientProperties.getWebsite(); - newsWebView.getEngine().load(website.getNewsHubUrl()); - }); - } - @Override public Node getRoot() { return newsRoot; diff --git a/src/main/java/com/faforever/client/notification/ServerNotificationController.java b/src/main/java/com/faforever/client/notification/ServerNotificationController.java index 063ce8a08e..fc50b8fee6 100644 --- a/src/main/java/com/faforever/client/notification/ServerNotificationController.java +++ b/src/main/java/com/faforever/client/notification/ServerNotificationController.java @@ -3,13 +3,16 @@ import com.faforever.client.fx.FxApplicationThreadExecutor; import com.faforever.client.fx.NodeController; import com.faforever.client.fx.WebViewConfigurer; +import com.faforever.client.notification.Action.Type; import com.faforever.client.ui.dialog.DialogLayout; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonBar.ButtonData; import javafx.scene.control.Label; import javafx.scene.control.TextArea; import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.web.WebView; import lombok.RequiredArgsConstructor; @@ -20,6 +23,7 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.util.Optional; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @Component @@ -31,21 +35,28 @@ public class ServerNotificationController extends NodeController { private final FxApplicationThreadExecutor fxApplicationThreadExecutor; private final DialogLayout dialogLayout = new DialogLayout(); - public WebView errorMessageView; + public StackPane webViewContainer; public Label exceptionAreaTitleLabel; public TextArea exceptionTextArea; public VBox serverNotificationRoot; private Runnable closeListener; + private CompletableFuture webViewInitializeFuture; + @Override protected void onInitialize() { exceptionAreaTitleLabel.managedProperty().bind(exceptionAreaTitleLabel.visibleProperty()); exceptionAreaTitleLabel.visibleProperty().bind(exceptionTextArea.visibleProperty()); exceptionTextArea.managedProperty().bind(exceptionTextArea.visibleProperty()); - webViewConfigurer.configureWebView(errorMessageView); - errorMessageView.managedProperty().bind(errorMessageView.visibleProperty()); - dialogLayout.setBody(serverNotificationRoot); + + webViewInitializeFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + webViewConfigurer.configureWebView(webView); + webView.managedProperty().bind(webView.visibleProperty()); + return webView; + }, fxApplicationThreadExecutor); + } public ServerNotificationController setNotification(ImmediateNotification notification) { @@ -60,7 +71,8 @@ public ServerNotificationController setNotification(ImmediateNotification notifi } dialogLayout.setHeading(new Label(notification.getTitle())); - fxApplicationThreadExecutor.execute(() -> errorMessageView.getEngine().loadContent(notification.getText())); + webViewInitializeFuture.thenAcceptAsync(webView -> webView.getEngine().loadContent(notification.getText()), + fxApplicationThreadExecutor); Optional.ofNullable(notification.getActions()) .map(actions -> actions.stream().map(this::createButton).collect(Collectors.toList())) @@ -80,11 +92,9 @@ private Button createButton(Action action) { } }); - switch (action.getType()) { - case OK_DONE: - button.getStyleClass().add("dialog-accept"); - ButtonBar.setButtonData(button, ButtonBar.ButtonData.OK_DONE); - break; + if (action.getType() == Type.OK_DONE) { + button.getStyleClass().add("dialog-accept"); + ButtonBar.setButtonData(button, ButtonData.OK_DONE); } return button; diff --git a/src/main/java/com/faforever/client/theme/ThemeService.java b/src/main/java/com/faforever/client/theme/ThemeService.java index 6e5989ecba..b245576f27 100644 --- a/src/main/java/com/faforever/client/theme/ThemeService.java +++ b/src/main/java/com/faforever/client/theme/ThemeService.java @@ -31,7 +31,6 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Lazy; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Service; @@ -112,7 +111,6 @@ public class ThemeService implements InitializingBean, DisposableBean { private static final String METADATA_FILE_NAME = "theme.properties"; private final ExecutorService executorService; - private final ApplicationContext applicationContext; private final DataPrefs dataPrefs; private final Preferences preferences; private final FxApplicationThreadExecutor fxApplicationThreadExecutor; diff --git a/src/main/java/com/faforever/client/tournament/TournamentsController.java b/src/main/java/com/faforever/client/tournament/TournamentsController.java index 495b7e71fd..f06269d18a 100644 --- a/src/main/java/com/faforever/client/tournament/TournamentsController.java +++ b/src/main/java/com/faforever/client/tournament/TournamentsController.java @@ -14,6 +14,7 @@ import javafx.scene.Node; import javafx.scene.control.ListView; import javafx.scene.layout.Pane; +import javafx.scene.layout.StackPane; import javafx.scene.web.WebView; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,6 +28,7 @@ import java.io.Reader; import java.text.MessageFormat; import java.util.Comparator; +import java.util.concurrent.CompletableFuture; @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @@ -43,11 +45,13 @@ public class TournamentsController extends NodeController { private final FxApplicationThreadExecutor fxApplicationThreadExecutor; public Pane tournamentRoot; - public WebView tournamentDetailWebView; + public StackPane webViewContainer; public Pane loadingIndicator; public Node contentPane; public ListView tournamentListView; + private CompletableFuture webViewInitializationFuture; + @Override public Node getRoot() { return tournamentRoot; @@ -60,6 +64,14 @@ protected void onInitialize() { tournamentListView.setCellFactory(param -> new TournamentItemListCell(uiService)); tournamentListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> displayTournamentItem(newValue)); + + webViewInitializationFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + webView.setContextMenuEnabled(false); + webViewConfigurer.configureWebView(webView); + webViewContainer.getChildren().add(webView); + return webView; + }, fxApplicationThreadExecutor); } private void onLoadingStart() { @@ -81,9 +93,6 @@ protected void onNavigate(NavigateEvent navigateEvent) { } onLoadingStart(); - tournamentDetailWebView.setContextMenuEnabled(false); - webViewConfigurer.configureWebView(tournamentDetailWebView); - tournamentService.getAllTournaments() .thenAcceptAsync(tournaments -> { tournaments.sort( @@ -125,7 +134,8 @@ private void displayTournamentItem(TournamentBean tournamentBean) { .replace("{completed-at-label}", i18n.get("tournament.completedAt")) .replace("{loading-label}", i18n.get("loading")); - tournamentDetailWebView.getEngine().loadContent(html); + webViewInitializationFuture.thenAcceptAsync(webView -> webView.getEngine().loadContent(html), + fxApplicationThreadExecutor); } catch (IOException e) { throw new AssetLoadException("Tournament view could not be loaded", e, "tournament.viewNotLoaded"); } diff --git a/src/main/java/com/faforever/client/tutorial/TutorialDetailController.java b/src/main/java/com/faforever/client/tutorial/TutorialDetailController.java index 103a27ce10..25c4ed9aec 100644 --- a/src/main/java/com/faforever/client/tutorial/TutorialDetailController.java +++ b/src/main/java/com/faforever/client/tutorial/TutorialDetailController.java @@ -3,6 +3,7 @@ import com.faforever.client.domain.MapBean; import com.faforever.client.domain.MapVersionBean; import com.faforever.client.domain.TutorialBean; +import com.faforever.client.fx.FxApplicationThreadExecutor; import com.faforever.client.fx.NodeController; import com.faforever.client.fx.WebViewConfigurer; import com.faforever.client.i18n.I18n; @@ -13,41 +14,50 @@ import javafx.scene.control.Label; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; +import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.scene.web.WebView; +import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import java.util.concurrent.CompletableFuture; + @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +@RequiredArgsConstructor public class TutorialDetailController extends NodeController { private final I18n i18n; private final MapService mapService; private final WebViewConfigurer webViewConfigurer; - private final TutorialService tutorialServie; + private final TutorialService tutorialService; + private final FxApplicationThreadExecutor fxApplicationThreadExecutor; + public VBox mapContainer; public BorderPane root; public ImageView mapImage; public Label mapNameLabel; - public WebView descriptionWebView; + public VBox webViewContainer; public Label titleLabel; public Button launchButton; private TutorialBean tutorial; - public TutorialDetailController(I18n i18n, MapService mapService, WebViewConfigurer webViewConfigurer, TutorialService tutorialService) { - this.i18n = i18n; - this.mapService = mapService; - this.webViewConfigurer = webViewConfigurer; - this.tutorialServie = tutorialService; - } + private CompletableFuture initializeWebViewFuture; @Override protected void onInitialize() { mapContainer.managedProperty().bind(mapContainer.visibleProperty()); - descriptionWebView.setContextMenuEnabled(false); - webViewConfigurer.configureWebView(descriptionWebView); launchButton.managedProperty().bind(launchButton.visibleProperty()); + + initializeWebViewFuture = CompletableFuture.supplyAsync(() -> { + WebView webView = new WebView(); + webView.setContextMenuEnabled(false); + webViewConfigurer.configureWebView(webView); + webViewContainer.getChildren().add(webView); + VBox.setVgrow(webView, Priority.ALWAYS); + return webView; + }, fxApplicationThreadExecutor); } @Override @@ -57,7 +67,7 @@ public Node getRoot() { public void launchReplay() { if (tutorial != null) { - tutorialServie.launchTutorial(tutorial); + tutorialService.launchTutorial(tutorial); } } @@ -81,7 +91,8 @@ public void setTutorial(TutorialBean tutorial) { mapContainer.setVisible(false); } - descriptionWebView.getEngine().loadContent(tutorial.getDescription()); + initializeWebViewFuture.thenAcceptAsync(webView -> webView.getEngine().loadContent(tutorial.getDescription()), + fxApplicationThreadExecutor); launchButton.visibleProperty().bind(tutorial.launchableProperty()); } } diff --git a/src/main/java/com/faforever/client/units/UnitsController.java b/src/main/java/com/faforever/client/units/UnitsController.java index 9c2e7cb3e6..a93115b8e0 100644 --- a/src/main/java/com/faforever/client/units/UnitsController.java +++ b/src/main/java/com/faforever/client/units/UnitsController.java @@ -2,33 +2,45 @@ import com.faforever.client.config.ClientProperties; import com.faforever.client.config.ClientProperties.UnitDatabase; +import com.faforever.client.fx.FxApplicationThreadExecutor; import com.faforever.client.fx.NodeController; import com.faforever.client.preferences.Preferences; import com.faforever.client.preferences.Preferences.UnitDataBaseType; import javafx.scene.Node; +import javafx.scene.layout.StackPane; import javafx.scene.web.WebView; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; +import java.util.concurrent.CompletableFuture; + @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @RequiredArgsConstructor public class UnitsController extends NodeController { private final ClientProperties clientProperties; private final Preferences preferences; + private final FxApplicationThreadExecutor fxApplicationThreadExecutor; + + public StackPane unitsRoot; - public WebView unitsRoot; + private WebView webView; @Override protected void onInitialize() { - preferences.unitDataBaseTypeProperty().when(showing).subscribe(this::loadUnitDataBase); + CompletableFuture.runAsync(() -> { + webView = new WebView(); + unitsRoot.getChildren().add(webView); + preferences.unitDataBaseTypeProperty().when(showing).subscribe(this::loadUnitDataBase); + }, fxApplicationThreadExecutor); } private void loadUnitDataBase(UnitDataBaseType newValue) { UnitDatabase unitDatabase = clientProperties.getUnitDatabase(); - unitsRoot.getEngine().load(newValue == UnitDataBaseType.SPOOKY ? unitDatabase.getSpookiesUrl() : unitDatabase.getRackOversUrl()); + webView.getEngine() + .load(newValue == UnitDataBaseType.SPOOKY ? unitDatabase.getSpookiesUrl() : unitDatabase.getRackOversUrl()); } @Override diff --git a/src/main/resources/theme/chat/channel_tab.fxml b/src/main/resources/theme/chat/channel_tab.fxml index 2b79765b8c..8fca313261 100644 --- a/src/main/resources/theme/chat/channel_tab.fxml +++ b/src/main/resources/theme/chat/channel_tab.fxml @@ -13,7 +13,6 @@ - @@ -50,9 +49,9 @@ - + diff --git a/src/main/resources/theme/chat/private_chat_tab.fxml b/src/main/resources/theme/chat/private_chat_tab.fxml index 6066f7d0e5..fd11820d36 100644 --- a/src/main/resources/theme/chat/private_chat_tab.fxml +++ b/src/main/resources/theme/chat/private_chat_tab.fxml @@ -6,12 +6,11 @@ - + - @@ -26,11 +25,7 @@ - - - + - - + + - - +