diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java index 54c8d09ec0..39626f1f21 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListPopupMenu.java @@ -19,11 +19,15 @@ import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXPopup; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.animation.KeyValue; import javafx.beans.binding.Bindings; import javafx.beans.binding.BooleanBinding; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.ObservableList; +import javafx.geometry.Bounds; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; @@ -33,11 +37,14 @@ import javafx.scene.layout.BorderPane; import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; +import javafx.scene.transform.Scale; import javafx.stage.WindowEvent; +import javafx.util.Duration; import org.jackhuang.hmcl.game.GameInstanceID; import org.jackhuang.hmcl.game.GameInstanceManifest; import org.jackhuang.hmcl.game.HMCLGameRepository; import org.jackhuang.hmcl.ui.FXUtils; +import org.jackhuang.hmcl.ui.animation.AnimationUtils; import org.jackhuang.hmcl.ui.construct.ImageContainer; import org.jackhuang.hmcl.ui.construct.RipplerContainer; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; @@ -46,20 +53,69 @@ import java.util.List; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; - +import static org.jackhuang.hmcl.ui.FXUtils.SINE; /// @author Glavo public final class GameListPopupMenu extends StackPane { private static final String KEY = GameListPopupMenu.class.getName() + ".popup"; + private static final String HIDING_KEY = GameListPopupMenu.class.getName() + ".hiding"; - public static boolean hideShowing(Node owner) { - JFXPopup popup = (JFXPopup) owner.getProperties().get(KEY); - if (popup != null && popup.isShowing()) { + public static void hideAnimated(JFXPopup popup) { + if (popup == null || !popup.isShowing()) { + return; + } + + if (!AnimationUtils.isAnimationEnabled()) { popup.hide(); - return true; - } else { + return; + } + + Node content = popup.getPopupContent(); + if (content == null) { + popup.hide(); + return; + } + + Node container = content.getParent() != null ? content.getParent() : content; + Bounds bounds = container.getLayoutBounds(); + + Scale scaleTransform = new Scale(1.0, 1.0, bounds.getWidth(), bounds.getHeight()); + container.getTransforms().setAll(scaleTransform); + + Timeline closeAnimation = new Timeline( + new KeyFrame(Duration.ZERO, + new KeyValue(container.opacityProperty(), 1.0, SINE), + new KeyValue(scaleTransform.xProperty(), 1.0, SINE), + new KeyValue(scaleTransform.yProperty(), 1.0, SINE) + ), + new KeyFrame(Duration.millis(160), + new KeyValue(container.opacityProperty(), 0.0, SINE), + new KeyValue(scaleTransform.xProperty(), 0.0, SINE), + new KeyValue(scaleTransform.yProperty(), 0.0, SINE) + ) + ); + + closeAnimation.setOnFinished(event -> { + popup.hide(); + container.getTransforms().clear(); + container.setOpacity(1.0); + }); + + FXUtils.playAnimation(container, "popup-close", closeAnimation); + } + + public static boolean hideShowing(Node owner) { + if (!(owner.getProperties().get(KEY) instanceof JFXPopup popup && popup.isShowing())) { return false; } + + if (Boolean.TRUE.equals(popup.getProperties().get(HIDING_KEY))) { + return true; + } + + popup.getProperties().put(HIDING_KEY, true); + hideAnimated(popup); + return true; } /// Shows an instance selection popup relative to its owner.