From 5c6180af773f147f214c6a86d5eaca0cc0b4daf2 Mon Sep 17 00:00:00 2001 From: asvitkine Date: Wed, 20 Sep 2023 18:19:04 -0400 Subject: [PATCH 1/3] Fix NPE with UnitHelp. --- .../ui/background/BackgroundTaskRunner.java | 30 ++++++++++++--- .../triplea/ui/menubar/help/UnitHelpMenu.java | 37 +++++++++++-------- .../engine/lobby/client/login/LobbyLogin.java | 1 + 3 files changed, 47 insertions(+), 21 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java index 5a36d024d94..b25b8facdd9 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java @@ -4,13 +4,16 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.Throwables; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; import java.util.function.Supplier; import javax.annotation.Nullable; import javax.swing.JFrame; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; +import lombok.Getter; import lombok.experimental.UtilityClass; import org.triplea.java.Interruptibles; import org.triplea.java.function.ThrowingSupplier; @@ -19,7 +22,7 @@ /** Provides methods for running tasks in the background to avoid blocking the UI. */ @UtilityClass public final class BackgroundTaskRunner { - private static JFrame mainFrame; + @Getter private static JFrame mainFrame; public static void setMainFrame(final JFrame mainFrame) { checkState(BackgroundTaskRunner.mainFrame == null); @@ -72,7 +75,16 @@ public static void runInBackground(final String message, final Runnable backgrou */ public static T runInBackgroundAndReturn( final String message, final Supplier backgroundAction) throws InterruptedException { - return runInBackgroundAndReturn(message, backgroundAction::get, RuntimeException.class); + return runInBackgroundAndReturn(message, backgroundAction::get, null, RuntimeException.class); + } + + public static T runInBackgroundAndReturn( + Consumer runOnEdtBeforeDialogClose, + final String message, + final Supplier backgroundAction) + throws InterruptedException { + return runInBackgroundAndReturn( + message, backgroundAction::get, runOnEdtBeforeDialogClose, RuntimeException.class); } /** @@ -88,6 +100,9 @@ public static T runInBackgroundAndReturn( * @param The type of exception thrown by the background action. * @param message The message displayed to the user while the background action is running. * @param backgroundAction The action to run in the background. + * @param runOnEdtBeforeDialogClose Work to be done on EDT with the result, before closing the + * dialog. Some Swing operations can be slow (e.g. layout of lots of HTML like unit help), so + * this can be used to ensure we only close the progress dialog once this work has been done. * @param exceptionType The type of exception thrown by the background action. * @return The value returned by {@code backgroundAction}. * @throws IllegalStateException If this method is not called from the EDT. @@ -98,6 +113,7 @@ public static T runInBackgroundAndReturn( public static T runInBackgroundAndReturn( final String message, final ThrowingSupplier backgroundAction, + final Consumer runOnEdtBeforeDialogClose, final Class exceptionType) throws E, InterruptedException { checkState(SwingUtilities.isEventDispatchThread()); @@ -117,16 +133,18 @@ protected T doInBackground() throws Exception { @Override protected void done() { - waitDialog.setVisible(false); - waitDialog.dispose(); - try { - resultRef.set(get()); + T t = get(); + resultRef.set(t); + Optional.ofNullable(runOnEdtBeforeDialogClose).ifPresent(c -> c.accept(t)); } catch (final ExecutionException e) { exceptionRef.set(e.getCause()); } catch (final InterruptedException e) { Thread.currentThread().interrupt(); exceptionRef.set(e); + } finally { + waitDialog.setVisible(false); + waitDialog.dispose(); } } }; diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java index e12a3eec102..4a6cc244fde 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java @@ -2,7 +2,9 @@ import games.strategy.engine.data.GameData; import games.strategy.engine.framework.ui.background.BackgroundTaskRunner; +import games.strategy.engine.framework.ui.background.WaitDialog; import games.strategy.triplea.ui.UiContext; +import java.awt.Dimension; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JDialog; @@ -20,21 +22,26 @@ class UnitHelpMenu { Action buildMenu(final GameData gameData, final UiContext uiContext) { return SwingAction.of( unitHelpTitle, - e -> { - final Result result = - Interruptibles.awaitResult( - () -> - BackgroundTaskRunner.runInBackgroundAndReturn( - "Calculating Data", - () -> { - String text = UnitStatsTable.getUnitStatsTable(gameData, uiContext); - JEditorPane editorPane = new JEditorPane("text/html", text); - editorPane.setEditable(false); - JScrollPane scroll = new JScrollPane(editorPane); - scroll.setBorder(BorderFactory.createEmptyBorder()); - return InformationDialog.createDialog(scroll, unitHelpTitle); - })); - result.result.orElseThrow().setVisible(true); + event -> { + try { + BackgroundTaskRunner.runInBackgroundAndReturn( + UnitHelpMenu::showDialog, + "Calculating Data", + () -> UnitStatsTable.getUnitStatsTable(gameData, uiContext)); + } catch (InterruptedException e) { + // Nothing to do. + } }); } + + private static void showDialog(String text) { + final JEditorPane editorPane = new JEditorPane(); + editorPane.setContentType("text/html"); + editorPane.setEditable(false); + final JScrollPane scroll = new JScrollPane(editorPane); + scroll.setBorder(BorderFactory.createEmptyBorder()); + editorPane.setText(text); + editorPane.setCaretPosition(0); + InformationDialog.createDialog(scroll, unitHelpTitle).setVisible(true); + } } diff --git a/game-app/game-headed/src/main/java/games/strategy/engine/lobby/client/login/LobbyLogin.java b/game-app/game-headed/src/main/java/games/strategy/engine/lobby/client/login/LobbyLogin.java index 96b71077a70..45feec8c395 100644 --- a/game-app/game-headed/src/main/java/games/strategy/engine/lobby/client/login/LobbyLogin.java +++ b/game-app/game-headed/src/main/java/games/strategy/engine/lobby/client/login/LobbyLogin.java @@ -235,6 +235,7 @@ private void forgotPassword(final ForgotPasswordPanel panel) { .username(panel.getUserName()) .email(panel.getEmail()) .build()), + null, IOException.class) .getResponseMessage(); DialogBuilder.builder() From 42652953b5273929da4c828fc16d9b764da4d255 Mon Sep 17 00:00:00 2001 From: asvitkine Date: Wed, 20 Sep 2023 18:22:31 -0400 Subject: [PATCH 2/3] Make args more concise. --- .../engine/framework/ui/background/BackgroundTaskRunner.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java b/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java index b25b8facdd9..7165f6d13b5 100644 --- a/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java +++ b/game-app/game-core/src/main/java/games/strategy/engine/framework/ui/background/BackgroundTaskRunner.java @@ -79,9 +79,7 @@ public static T runInBackgroundAndReturn( } public static T runInBackgroundAndReturn( - Consumer runOnEdtBeforeDialogClose, - final String message, - final Supplier backgroundAction) + Consumer runOnEdtBeforeDialogClose, String message, Supplier backgroundAction) throws InterruptedException { return runInBackgroundAndReturn( message, backgroundAction::get, runOnEdtBeforeDialogClose, RuntimeException.class); From 88cd0f37725aed567c056f44f0e8026201f0b61d Mon Sep 17 00:00:00 2001 From: asvitkine Date: Wed, 20 Sep 2023 19:27:35 -0400 Subject: [PATCH 3/3] Remove unused imports. --- .../games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java b/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java index 4a6cc244fde..5b5e6c2fcd4 100644 --- a/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java +++ b/game-app/game-core/src/main/java/games/strategy/triplea/ui/menubar/help/UnitHelpMenu.java @@ -2,17 +2,12 @@ import games.strategy.engine.data.GameData; import games.strategy.engine.framework.ui.background.BackgroundTaskRunner; -import games.strategy.engine.framework.ui.background.WaitDialog; import games.strategy.triplea.ui.UiContext; -import java.awt.Dimension; import javax.swing.Action; import javax.swing.BorderFactory; -import javax.swing.JDialog; import javax.swing.JEditorPane; import javax.swing.JScrollPane; import lombok.experimental.UtilityClass; -import org.triplea.java.Interruptibles; -import org.triplea.java.Interruptibles.Result; import org.triplea.swing.SwingAction; @UtilityClass