155
HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java
Normal file
155
HMCL/src/main/java/org/jackhuang/hmcl/ui/DialogUtils.java
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Hello Minecraft! Launcher
|
||||||
|
* Copyright (C) 2025 huangyuhui <huanghongxun2008@126.com> and contributors
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.jackhuang.hmcl.ui;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXDialog;
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.event.EventHandler;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.DialogAware;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.JFXDialogPane;
|
||||||
|
import org.jackhuang.hmcl.ui.decorator.Decorator;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public final class DialogUtils {
|
||||||
|
private DialogUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String PROPERTY_DIALOG_INSTANCE = DialogUtils.class.getName() + ".dialog.instance";
|
||||||
|
public static final String PROPERTY_DIALOG_PANE_INSTANCE = DialogUtils.class.getName() + ".dialog.pane.instance";
|
||||||
|
public static final String PROPERTY_DIALOG_CLOSE_HANDLER = DialogUtils.class.getName() + ".dialog.closeListener";
|
||||||
|
|
||||||
|
public static final String PROPERTY_PARENT_PANE_REF = DialogUtils.class.getName() + ".dialog.parentPaneRef";
|
||||||
|
public static final String PROPERTY_PARENT_DIALOG_REF = DialogUtils.class.getName() + ".dialog.parentDialogRef";
|
||||||
|
|
||||||
|
public static void show(Decorator decorator, Node content) {
|
||||||
|
if (decorator.getDrawerWrapper() == null) {
|
||||||
|
Platform.runLater(() -> show(decorator, content));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
show(decorator.getDrawerWrapper(), content, (dialog) -> {
|
||||||
|
JFXDialogPane pane = (JFXDialogPane) dialog.getContent();
|
||||||
|
decorator.capableDraggingWindow(dialog);
|
||||||
|
decorator.forbidDraggingWindow(pane);
|
||||||
|
dialog.setDialogContainer(decorator.getDrawerWrapper());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(StackPane container, Node content) {
|
||||||
|
show(container, content, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(StackPane container, Node content, @Nullable Consumer<JFXDialog> onDialogCreated) {
|
||||||
|
FXUtils.checkFxUserThread();
|
||||||
|
|
||||||
|
JFXDialog dialog = (JFXDialog) container.getProperties().get(PROPERTY_DIALOG_INSTANCE);
|
||||||
|
JFXDialogPane dialogPane = (JFXDialogPane) container.getProperties().get(PROPERTY_DIALOG_PANE_INSTANCE);
|
||||||
|
|
||||||
|
if (dialog == null) {
|
||||||
|
dialog = new JFXDialog(AnimationUtils.isAnimationEnabled()
|
||||||
|
? JFXDialog.DialogTransition.CENTER
|
||||||
|
: JFXDialog.DialogTransition.NONE);
|
||||||
|
dialogPane = new JFXDialogPane();
|
||||||
|
|
||||||
|
dialog.setContent(dialogPane);
|
||||||
|
dialog.setDialogContainer(container);
|
||||||
|
dialog.setOverlayClose(false);
|
||||||
|
|
||||||
|
container.getProperties().put(PROPERTY_DIALOG_INSTANCE, dialog);
|
||||||
|
container.getProperties().put(PROPERTY_DIALOG_PANE_INSTANCE, dialogPane);
|
||||||
|
|
||||||
|
if (onDialogCreated != null) {
|
||||||
|
onDialogCreated.accept(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
content.getProperties().put(PROPERTY_PARENT_PANE_REF, dialogPane);
|
||||||
|
content.getProperties().put(PROPERTY_PARENT_DIALOG_REF, dialog);
|
||||||
|
|
||||||
|
dialogPane.push(content);
|
||||||
|
|
||||||
|
EventHandler<DialogCloseEvent> handler = event -> close(content);
|
||||||
|
content.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler);
|
||||||
|
content.addEventHandler(DialogCloseEvent.CLOSE, handler);
|
||||||
|
|
||||||
|
handleDialogShown(dialog, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleDialogShown(JFXDialog dialog, Node node) {
|
||||||
|
if (dialog.isVisible()) {
|
||||||
|
dialog.requestFocus();
|
||||||
|
if (node instanceof DialogAware dialogAware)
|
||||||
|
dialogAware.onDialogShown();
|
||||||
|
} else {
|
||||||
|
dialog.visibleProperty().addListener(new ChangeListener<>() {
|
||||||
|
@Override
|
||||||
|
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
||||||
|
if (newValue) {
|
||||||
|
dialog.requestFocus();
|
||||||
|
if (node instanceof DialogAware dialogAware)
|
||||||
|
dialogAware.onDialogShown();
|
||||||
|
observable.removeListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static void close(Node content) {
|
||||||
|
FXUtils.checkFxUserThread();
|
||||||
|
|
||||||
|
Optional.ofNullable(content.getProperties().get(PROPERTY_DIALOG_CLOSE_HANDLER))
|
||||||
|
.ifPresent(handler -> content.removeEventHandler(DialogCloseEvent.CLOSE, (EventHandler<DialogCloseEvent>) handler));
|
||||||
|
|
||||||
|
JFXDialogPane pane = (JFXDialogPane) content.getProperties().get(PROPERTY_PARENT_PANE_REF);
|
||||||
|
JFXDialog dialog = (JFXDialog) content.getProperties().get(PROPERTY_PARENT_DIALOG_REF);
|
||||||
|
|
||||||
|
if (dialog != null && pane != null) {
|
||||||
|
if (pane.size() == 1 && pane.peek().orElse(null) == content) {
|
||||||
|
dialog.setOnDialogClosed(e -> pane.pop(content));
|
||||||
|
dialog.close();
|
||||||
|
|
||||||
|
StackPane container = dialog.getDialogContainer();
|
||||||
|
if (container != null) {
|
||||||
|
container.getProperties().remove(PROPERTY_DIALOG_INSTANCE);
|
||||||
|
container.getProperties().remove(PROPERTY_DIALOG_PANE_INSTANCE);
|
||||||
|
container.getProperties().remove(PROPERTY_PARENT_DIALOG_REF);
|
||||||
|
container.getProperties().remove(PROPERTY_PARENT_PANE_REF);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pane.pop(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (content instanceof DialogAware dialogAware) {
|
||||||
|
dialogAware.onDialogClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,11 +24,11 @@ import javafx.geometry.Insets;
|
|||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Alert;
|
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Priority;
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.layout.StackPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import javafx.scene.text.Text;
|
import javafx.scene.text.Text;
|
||||||
import javafx.scene.text.TextFlow;
|
import javafx.scene.text.TextFlow;
|
||||||
@@ -40,6 +40,7 @@ import org.jackhuang.hmcl.launch.ProcessListener;
|
|||||||
import org.jackhuang.hmcl.setting.StyleSheets;
|
import org.jackhuang.hmcl.setting.StyleSheets;
|
||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
import org.jackhuang.hmcl.ui.construct.TwoLineListItem;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
import org.jackhuang.hmcl.util.Log4jLevel;
|
||||||
@@ -84,6 +85,7 @@ public class GameCrashWindow extends Stage {
|
|||||||
private final ProcessListener.ExitType exitType;
|
private final ProcessListener.ExitType exitType;
|
||||||
private final LaunchOptions launchOptions;
|
private final LaunchOptions launchOptions;
|
||||||
private final View view;
|
private final View view;
|
||||||
|
private final StackPane stackPane;
|
||||||
|
|
||||||
private final List<Log> logs;
|
private final List<Log> logs;
|
||||||
|
|
||||||
@@ -106,9 +108,10 @@ public class GameCrashWindow extends Stage {
|
|||||||
|
|
||||||
this.view = new View();
|
this.view = new View();
|
||||||
|
|
||||||
|
this.stackPane = new StackPane(view);
|
||||||
this.feedbackTextFlow.getChildren().addAll(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction));
|
this.feedbackTextFlow.getChildren().addAll(FXUtils.parseSegment(i18n("game.crash.feedback"), Controllers::onHyperlinkAction));
|
||||||
|
|
||||||
setScene(new Scene(view, 800, 480));
|
setScene(new Scene(stackPane, 800, 480));
|
||||||
StyleSheets.init(getScene());
|
StyleSheets.init(getScene());
|
||||||
setTitle(i18n("game.crash.title"));
|
setTitle(i18n("game.crash.title"));
|
||||||
FXUtils.setIcon(this);
|
FXUtils.setIcon(this);
|
||||||
@@ -297,19 +300,16 @@ public class GameCrashWindow extends Stage {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
.handleAsync((result, exception) -> {
|
.handleAsync((result, exception) -> {
|
||||||
Alert alert;
|
|
||||||
|
|
||||||
if (exception == null) {
|
if (exception == null) {
|
||||||
FXUtils.showFileInExplorer(logFile);
|
FXUtils.showFileInExplorer(logFile);
|
||||||
alert = new Alert(Alert.AlertType.INFORMATION, i18n("settings.launcher.launcher_log.export.success", logFile));
|
var dialog = new MessageDialogPane.Builder(i18n("settings.launcher.launcher_log.export.success", logFile), i18n("message.success"), MessageDialogPane.MessageType.SUCCESS).ok(null).build();
|
||||||
|
DialogUtils.show(stackPane, dialog);
|
||||||
} else {
|
} else {
|
||||||
LOG.warning("Failed to export game crash info", exception);
|
LOG.warning("Failed to export game crash info", exception);
|
||||||
alert = new Alert(Alert.AlertType.WARNING, i18n("settings.launcher.launcher_log.export.failed") + "\n" + StringUtils.getStackTrace(exception));
|
var dialog = new MessageDialogPane.Builder(i18n("settings.launcher.launcher_log.export.failed") + "\n" + StringUtils.getStackTrace(exception), i18n("message.error"), MessageDialogPane.MessageType.ERROR).ok(null).build();
|
||||||
|
DialogUtils.show(stackPane, dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
alert.setTitle(i18n("settings.launcher.launcher_log.export"));
|
|
||||||
alert.showAndWait();
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}, Schedulers.javafx());
|
}, Schedulers.javafx());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import javafx.event.EventHandler;
|
|||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.input.KeyCode;
|
import javafx.scene.input.KeyCode;
|
||||||
import javafx.scene.input.MouseButton;
|
import javafx.scene.input.MouseButton;
|
||||||
@@ -44,11 +43,10 @@ import org.jackhuang.hmcl.game.GameDumpGenerator;
|
|||||||
import org.jackhuang.hmcl.game.Log;
|
import org.jackhuang.hmcl.game.Log;
|
||||||
import org.jackhuang.hmcl.setting.StyleSheets;
|
import org.jackhuang.hmcl.setting.StyleSheets;
|
||||||
import org.jackhuang.hmcl.theme.Themes;
|
import org.jackhuang.hmcl.theme.Themes;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.MessageDialogPane;
|
||||||
import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel;
|
import org.jackhuang.hmcl.ui.construct.NoneMultipleSelectionModel;
|
||||||
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||||
import org.jackhuang.hmcl.util.CircularArrayList;
|
import org.jackhuang.hmcl.util.*;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
|
||||||
import org.jackhuang.hmcl.util.Log4jLevel;
|
|
||||||
import org.jackhuang.hmcl.util.platform.*;
|
import org.jackhuang.hmcl.util.platform.*;
|
||||||
import org.jackhuang.hmcl.util.platform.windows.Dwmapi;
|
import org.jackhuang.hmcl.util.platform.windows.Dwmapi;
|
||||||
import org.jackhuang.hmcl.util.platform.windows.WinConstants;
|
import org.jackhuang.hmcl.util.platform.windows.WinConstants;
|
||||||
@@ -65,8 +63,8 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||||
import static org.jackhuang.hmcl.util.Lang.thread;
|
import static org.jackhuang.hmcl.util.Lang.thread;
|
||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
|
||||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author huangyuhui
|
* @author huangyuhui
|
||||||
@@ -199,6 +197,7 @@ public final class LogWindow extends Stage {
|
|||||||
private final StringProperty[] buttonText = new StringProperty[LEVELS.length];
|
private final StringProperty[] buttonText = new StringProperty[LEVELS.length];
|
||||||
private final BooleanProperty[] showLevel = new BooleanProperty[LEVELS.length];
|
private final BooleanProperty[] showLevel = new BooleanProperty[LEVELS.length];
|
||||||
private final JFXComboBox<Integer> cboLines = new JFXComboBox<>();
|
private final JFXComboBox<Integer> cboLines = new JFXComboBox<>();
|
||||||
|
private final StackPane stackPane = new StackPane();
|
||||||
|
|
||||||
LogWindowImpl() {
|
LogWindowImpl() {
|
||||||
getStyleClass().add("log-window");
|
getStyleClass().add("log-window");
|
||||||
@@ -241,9 +240,8 @@ public final class LogWindow extends Stage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Alert alert = new Alert(Alert.AlertType.INFORMATION, i18n("settings.launcher.launcher_log.export.success", logFile));
|
var dialog = new MessageDialogPane.Builder(i18n("settings.launcher.launcher_log.export.success", logFile), i18n("message.success"), MessageDialogPane.MessageType.SUCCESS).ok(null).build();
|
||||||
alert.setTitle(i18n("settings.launcher.launcher_log.export"));
|
DialogUtils.show(stackPane, dialog);
|
||||||
alert.showAndWait();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
FXUtils.showFileInExplorer(logFile);
|
FXUtils.showFileInExplorer(logFile);
|
||||||
@@ -267,9 +265,8 @@ public final class LogWindow extends Stage {
|
|||||||
LOG.warning("Failed to create minecraft jstack dump", e);
|
LOG.warning("Failed to create minecraft jstack dump", e);
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
Platform.runLater(() -> {
|
||||||
Alert alert = new Alert(Alert.AlertType.ERROR, i18n("logwindow.export_dump"));
|
var dialog = new MessageDialogPane.Builder(i18n("logwindow.export_dump") + "\n" + StringUtils.getStackTrace(e), i18n("message.error"), MessageDialogPane.MessageType.ERROR).ok(null).build();
|
||||||
alert.setTitle(i18n("message.error"));
|
DialogUtils.show(stackPane, dialog);
|
||||||
alert.showAndWait();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +297,9 @@ public final class LogWindow extends Stage {
|
|||||||
|
|
||||||
VBox vbox = new VBox(3);
|
VBox vbox = new VBox(3);
|
||||||
vbox.setPadding(new Insets(3, 0, 3, 0));
|
vbox.setPadding(new Insets(3, 0, 3, 0));
|
||||||
getChildren().setAll(vbox);
|
getSkinnable().stackPane.getChildren().setAll(vbox);
|
||||||
|
getChildren().setAll(getSkinnable().stackPane);
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
BorderPane borderPane = new BorderPane();
|
BorderPane borderPane = new BorderPane();
|
||||||
|
|||||||
@@ -17,18 +17,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.jackhuang.hmcl.ui.decorator;
|
package org.jackhuang.hmcl.ui.decorator;
|
||||||
|
|
||||||
import com.jfoenix.controls.JFXDialog;
|
|
||||||
import com.jfoenix.controls.JFXSnackbar;
|
import com.jfoenix.controls.JFXSnackbar;
|
||||||
import javafx.animation.Interpolator;
|
import javafx.animation.Interpolator;
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.animation.KeyFrame;
|
||||||
import javafx.animation.KeyValue;
|
import javafx.animation.KeyValue;
|
||||||
import javafx.animation.Timeline;
|
import javafx.animation.Timeline;
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.InvalidationListener;
|
import javafx.beans.InvalidationListener;
|
||||||
import javafx.beans.WeakInvalidationListener;
|
import javafx.beans.WeakInvalidationListener;
|
||||||
import javafx.beans.value.ChangeListener;
|
|
||||||
import javafx.beans.value.ObservableValue;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
@@ -48,14 +43,15 @@ import org.jackhuang.hmcl.setting.EnumBackgroundImage;
|
|||||||
import org.jackhuang.hmcl.task.Schedulers;
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
import org.jackhuang.hmcl.task.Task;
|
import org.jackhuang.hmcl.task.Task;
|
||||||
import org.jackhuang.hmcl.ui.Controllers;
|
import org.jackhuang.hmcl.ui.Controllers;
|
||||||
|
import org.jackhuang.hmcl.ui.DialogUtils;
|
||||||
import org.jackhuang.hmcl.ui.FXUtils;
|
import org.jackhuang.hmcl.ui.FXUtils;
|
||||||
import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane;
|
import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane;
|
||||||
import org.jackhuang.hmcl.ui.animation.*;
|
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.Motion;
|
||||||
import org.jackhuang.hmcl.ui.animation.TransitionPane.AnimationProducer;
|
import org.jackhuang.hmcl.ui.animation.TransitionPane.AnimationProducer;
|
||||||
import org.jackhuang.hmcl.ui.construct.DialogAware;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.Navigator;
|
|
||||||
import org.jackhuang.hmcl.ui.construct.JFXDialogPane;
|
import org.jackhuang.hmcl.ui.construct.JFXDialogPane;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.Navigator;
|
||||||
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
import org.jackhuang.hmcl.ui.wizard.Refreshable;
|
||||||
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
import org.jackhuang.hmcl.ui.wizard.WizardProvider;
|
||||||
import org.jackhuang.hmcl.util.Lang;
|
import org.jackhuang.hmcl.util.Lang;
|
||||||
@@ -66,7 +62,9 @@ import java.io.IOException;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.util.stream.Collectors.toList;
|
import static java.util.stream.Collectors.toList;
|
||||||
@@ -77,14 +75,9 @@ import static org.jackhuang.hmcl.util.io.FileUtils.getExtension;
|
|||||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||||
|
|
||||||
public class DecoratorController {
|
public class DecoratorController {
|
||||||
private static final String PROPERTY_DIALOG_CLOSE_HANDLER = DecoratorController.class.getName() + ".dialog.closeListener";
|
|
||||||
|
|
||||||
private final Decorator decorator;
|
private final Decorator decorator;
|
||||||
private final Navigator navigator;
|
private final Navigator navigator;
|
||||||
|
|
||||||
private JFXDialog dialog;
|
|
||||||
private JFXDialogPane dialogPane;
|
|
||||||
|
|
||||||
public DecoratorController(Stage stage, Node mainPage) {
|
public DecoratorController(Stage stage, Node mainPage) {
|
||||||
decorator = new Decorator(stage);
|
decorator = new Decorator(stage);
|
||||||
decorator.setOnCloseButtonAction(() -> {
|
decorator.setOnCloseButtonAction(() -> {
|
||||||
@@ -134,19 +127,24 @@ public class DecoratorController {
|
|||||||
|
|
||||||
// pass key events to current dialog / current page
|
// pass key events to current dialog / current page
|
||||||
decorator.addEventFilter(KeyEvent.ANY, e -> {
|
decorator.addEventFilter(KeyEvent.ANY, e -> {
|
||||||
if (!(e.getTarget() instanceof Node)) {
|
if (!(e.getTarget() instanceof Node t)) {
|
||||||
return; // event source can't be determined
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node newTarget;
|
Node newTarget;
|
||||||
if (dialogPane != null && dialogPane.peek().isPresent()) {
|
|
||||||
newTarget = dialogPane.peek().get(); // current dialog
|
JFXDialogPane currentDialogPane = null;
|
||||||
} else {
|
if (decorator.getDrawerWrapper() != null) {
|
||||||
newTarget = navigator.getCurrentPage(); // current page
|
currentDialogPane = (JFXDialogPane) decorator.getDrawerWrapper().getProperties().get(DialogUtils.PROPERTY_DIALOG_PANE_INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (currentDialogPane != null && currentDialogPane.peek().isPresent()) {
|
||||||
|
newTarget = currentDialogPane.peek().get();
|
||||||
|
} else {
|
||||||
|
newTarget = navigator.getCurrentPage();
|
||||||
|
}
|
||||||
boolean needsRedirect = true;
|
boolean needsRedirect = true;
|
||||||
Node t = (Node) e.getTarget();
|
|
||||||
while (t != null) {
|
while (t != null) {
|
||||||
if (t == newTarget) {
|
if (t == newTarget) {
|
||||||
// current event target is in newTarget
|
// current event target is in newTarget
|
||||||
@@ -445,81 +443,12 @@ public class DecoratorController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==== Dialog ====
|
// ==== Dialog ====
|
||||||
|
|
||||||
public void showDialog(Node node) {
|
public void showDialog(Node node) {
|
||||||
FXUtils.checkFxUserThread();
|
DialogUtils.show(decorator, node);
|
||||||
|
|
||||||
if (dialog == null) {
|
|
||||||
if (decorator.getDrawerWrapper() == null) {
|
|
||||||
// Sometimes showDialog will be invoked before decorator was initialized.
|
|
||||||
// Keep trying again.
|
|
||||||
Platform.runLater(() -> showDialog(node));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dialog = new JFXDialog(AnimationUtils.isAnimationEnabled()
|
|
||||||
? JFXDialog.DialogTransition.CENTER
|
|
||||||
: JFXDialog.DialogTransition.NONE);
|
|
||||||
dialogPane = new JFXDialogPane();
|
|
||||||
|
|
||||||
dialog.setContent(dialogPane);
|
|
||||||
decorator.capableDraggingWindow(dialog);
|
|
||||||
decorator.forbidDraggingWindow(dialogPane);
|
|
||||||
dialog.setDialogContainer(decorator.getDrawerWrapper());
|
|
||||||
dialog.setOverlayClose(false);
|
|
||||||
dialog.show();
|
|
||||||
|
|
||||||
navigator.setDisable(true);
|
|
||||||
}
|
|
||||||
dialogPane.push(node);
|
|
||||||
|
|
||||||
EventHandler<DialogCloseEvent> handler = event -> closeDialog(node);
|
|
||||||
node.getProperties().put(PROPERTY_DIALOG_CLOSE_HANDLER, handler);
|
|
||||||
node.addEventHandler(DialogCloseEvent.CLOSE, handler);
|
|
||||||
|
|
||||||
if (dialog.isVisible()) {
|
|
||||||
dialog.requestFocus();
|
|
||||||
if (node instanceof DialogAware)
|
|
||||||
((DialogAware) node).onDialogShown();
|
|
||||||
} else {
|
|
||||||
dialog.visibleProperty().addListener(new ChangeListener<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
|
|
||||||
if (newValue) {
|
|
||||||
dialog.requestFocus();
|
|
||||||
if (node instanceof DialogAware)
|
|
||||||
((DialogAware) node).onDialogShown();
|
|
||||||
observable.removeListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private void closeDialog(Node node) {
|
private void closeDialog(Node node) {
|
||||||
FXUtils.checkFxUserThread();
|
DialogUtils.close(node);
|
||||||
|
|
||||||
Optional.ofNullable(node.getProperties().get(PROPERTY_DIALOG_CLOSE_HANDLER))
|
|
||||||
.ifPresent(handler -> node.removeEventHandler(DialogCloseEvent.CLOSE, (EventHandler<DialogCloseEvent>) handler));
|
|
||||||
|
|
||||||
if (dialog != null) {
|
|
||||||
JFXDialogPane pane = dialogPane;
|
|
||||||
|
|
||||||
if (pane.size() == 1 && pane.peek().orElse(null) == node) {
|
|
||||||
dialog.setOnDialogClosed(e -> pane.pop(node));
|
|
||||||
dialog.close();
|
|
||||||
dialog = null;
|
|
||||||
dialogPane = null;
|
|
||||||
|
|
||||||
navigator.setDisable(false);
|
|
||||||
} else {
|
|
||||||
pane.pop(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node instanceof DialogAware) {
|
|
||||||
((DialogAware) node).onDialogClosed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==== Toast ====
|
// ==== Toast ====
|
||||||
|
|||||||
Reference in New Issue
Block a user