From 8df651c5a8c302ad6d83396bd310804296daaf83 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Mon, 6 Apr 2020 13:21:17 +0800 Subject: [PATCH] add: scroll to game crash report automatically --- .../jackhuang/hmcl/game/LauncherHelper.java | 29 +++++------ .../java/org/jackhuang/hmcl/ui/LogWindow.java | 51 +++++++++++-------- HMCL/src/main/resources/assets/css/root.css | 4 ++ .../org/jackhuang/hmcl/util/StringUtils.java | 18 +++++++ 4 files changed, 67 insertions(+), 35 deletions(-) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 9cd32c1eb..9e1b8080b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -566,28 +566,27 @@ public final class LauncherHelper { if (exitType != ExitType.NORMAL) { repository.markVersionLaunchedAbnormally(version); - } - - if (exitType != ExitType.NORMAL && logWindow == null) Platform.runLater(() -> { - logWindow = new LogWindow(); + if (logWindow == null) { + logWindow = new LogWindow(); - switch (exitType) { - case JVM_ERROR: - logWindow.setTitle(i18n("launch.failed.cannot_create_jvm")); - break; - case APPLICATION_ERROR: - logWindow.setTitle(i18n("launch.failed.exited_abnormally")); - break; - } + switch (exitType) { + case JVM_ERROR: + logWindow.setTitle(i18n("launch.failed.cannot_create_jvm")); + break; + case APPLICATION_ERROR: + logWindow.setTitle(i18n("launch.failed.exited_abnormally")); + break; + } - logWindow.show(); - logWindow.onDone.register(() -> { logWindow.logLine("Command: " + new CommandBuilder().addAll(process.getCommands()).toString(), Log4jLevel.INFO); for (Map.Entry entry : logs) logWindow.logLine(entry.getKey(), entry.getValue()); - }); + } + + logWindow.showGameCrashReport(); }); + } checkExit(); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java index 00b34101b..d01ec2077 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/LogWindow.java @@ -21,6 +21,7 @@ import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXComboBox; import com.jfoenix.controls.JFXListView; +import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.binding.Bindings; import javafx.beans.property.*; @@ -33,8 +34,6 @@ import javafx.scene.control.*; import javafx.scene.control.Label; import javafx.scene.layout.*; import javafx.stage.Stage; -import org.jackhuang.hmcl.event.Event; -import org.jackhuang.hmcl.event.EventManager; import org.jackhuang.hmcl.game.LauncherHelper; import org.jackhuang.hmcl.util.Log4jLevel; @@ -58,6 +57,7 @@ import static org.jackhuang.hmcl.setting.ConfigHolder.config; import static org.jackhuang.hmcl.ui.FXUtils.newImage; import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.Logging.LOG; +import static org.jackhuang.hmcl.util.StringUtils.parseEscapeSequence; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; /** @@ -66,7 +66,6 @@ import static org.jackhuang.hmcl.util.i18n.I18n.i18n; */ public final class LogWindow extends Stage { - public final EventManager onDone = new EventManager<>(); private final ArrayDeque logs = new ArrayDeque<>(); private final Map levelCountMap = new EnumMap(Log4jLevel.class) { { @@ -76,30 +75,46 @@ public final class LogWindow extends Stage { private final Map levelShownMap = new EnumMap(Log4jLevel.class) { { for (Log4jLevel level : Log4jLevel.values()) { - SimpleBooleanProperty property = new SimpleBooleanProperty(); + SimpleBooleanProperty property = new SimpleBooleanProperty(true); put(level, property); - property.addListener((a, b, newValue) -> shakeLogs()); } } }; private final LogWindowImpl impl = new LogWindowImpl(); private final WeakChangeListener logLinesListener = FXUtils.onWeakChange(config().logLinesProperty(), logLines -> checkLogCount()); + private boolean stopCheckLogCount = false; + public LogWindow() { setScene(new Scene(impl, 800, 480)); getScene().getStylesheets().addAll(config().getTheme().getStylesheets()); setTitle(i18n("logwindow.title")); getIcons().add(newImage("/assets/img/icon.png")); + + levelShownMap.values().forEach(property -> property.addListener((a, b, newValue) -> shakeLogs())); } public void logLine(String line, Log4jLevel level) { - Log log = new Log(line, level); + Log log = new Log(parseEscapeSequence(line), level); logs.add(log); if (levelShownMap.get(level).get()) impl.listView.getItems().add(log); levelCountMap.get(level).setValue(levelCountMap.get(level).getValue() + 1); - checkLogCount(); + if (!stopCheckLogCount) checkLogCount(); + } + + public void showGameCrashReport() { + stopCheckLogCount = true; + for (Log log : impl.listView.getItems()) { + if (log.log.contains("Minecraft Crash Report")) { + Platform.runLater(() -> { + impl.listView.scrollTo(log); + }); + break; + } + } + show(); } private void shakeLogs() { @@ -131,7 +146,7 @@ public final class LogWindow extends Stage { private ListView listView = new JFXListView<>(); private BooleanProperty autoScroll = new SimpleBooleanProperty(); private List buttonText = IntStream.range(0, 5).mapToObj(x -> new SimpleStringProperty()).collect(Collectors.toList()); - private List showLevel = IntStream.range(0, 5).mapToObj(x -> new SimpleBooleanProperty()).collect(Collectors.toList()); + private List showLevel = IntStream.range(0, 5).mapToObj(x -> new SimpleBooleanProperty(true)).collect(Collectors.toList()); private JFXComboBox cboLines = new JFXComboBox<>(); LogWindowImpl() { @@ -193,6 +208,7 @@ public final class LogWindow extends Stage { } private static class LogWindowSkin extends SkinBase { + private static PseudoClass EMPTY = PseudoClass.getPseudoClass("empty"); private static PseudoClass FATAL = PseudoClass.getPseudoClass("fatal"); private static PseudoClass ERROR = PseudoClass.getPseudoClass("error"); private static PseudoClass WARN = PseudoClass.getPseudoClass("warn"); @@ -271,22 +287,17 @@ public final class LogWindow extends Stage { @Override protected void updateItem(Log item, boolean empty) { super.updateItem(item, empty); + pseudoClassStateChanged(EMPTY, empty); + pseudoClassStateChanged(FATAL, !empty && item.level == Log4jLevel.FATAL); + pseudoClassStateChanged(ERROR, !empty && item.level == Log4jLevel.ERROR); + pseudoClassStateChanged(WARN, !empty && item.level == Log4jLevel.WARN); + pseudoClassStateChanged(INFO, !empty && item.level == Log4jLevel.INFO); + pseudoClassStateChanged(DEBUG, !empty && item.level == Log4jLevel.DEBUG); + pseudoClassStateChanged(TRACE, !empty && item.level == Log4jLevel.TRACE); if (empty) { setText(null); - pseudoClassStateChanged(FATAL, false); - pseudoClassStateChanged(ERROR, false); - pseudoClassStateChanged(WARN, false); - pseudoClassStateChanged(INFO, false); - pseudoClassStateChanged(DEBUG, false); - pseudoClassStateChanged(TRACE, false); } else { setText(item.log); - pseudoClassStateChanged(FATAL, item.level == Log4jLevel.FATAL); - pseudoClassStateChanged(ERROR, item.level == Log4jLevel.ERROR); - pseudoClassStateChanged(WARN, item.level == Log4jLevel.WARN); - pseudoClassStateChanged(INFO, item.level == Log4jLevel.INFO); - pseudoClassStateChanged(DEBUG, item.level == Log4jLevel.DEBUG); - pseudoClassStateChanged(TRACE, item.level == Log4jLevel.TRACE); } } }); diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index bae15ce08..f2cdfdf7e 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -936,6 +936,10 @@ -fx-border-color: #dddddd; } +.log:empty { + -fx-border-width: 0; +} + .log:fatal { -fx-background-color: #F7A699; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java index e1567b16e..3a71fdeb2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java @@ -217,4 +217,22 @@ public final class StringUtils { public static String parseColorEscapes(String original) { return original.replaceAll("\u00A7\\d", ""); } + + public static String parseEscapeSequence(String str) { + StringBuilder builder = new StringBuilder(); + boolean inEscape = false; + for (int i = 0; i < str.length(); i++) { + char ch = str.charAt(i); + if (ch == '\033') { + inEscape = true; + } + if (!inEscape) { + builder.append(ch); + } + if (inEscape && ch == 'm') { + inEscape = false; + } + } + return builder.toString(); + } }