diff --git a/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java b/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java index 659c252ff9..bf405a236a 100644 --- a/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java +++ b/HMCL/src/main/java/com/jfoenix/controls/JFXPopup.java @@ -170,8 +170,20 @@ public void show(Window window, double x, double y, PopupVPosition vAlign, Popup @Override public void hide() { + if (!(getSkin() instanceof JFXPopupSkin skin)) { + super.hide(); + return; + } + + if (isShowing()) { + skin.animateClose(this::hideImmediately); + } else { + skin.init(); + } + } + + private void hideImmediately() { super.hide(); - ((JFXPopupSkin) getSkin()).init(); } /*************************************************************************** diff --git a/HMCL/src/main/java/com/jfoenix/skins/JFXPopupSkin.java b/HMCL/src/main/java/com/jfoenix/skins/JFXPopupSkin.java index c1daa775ac..a8ee9767cf 100644 --- a/HMCL/src/main/java/com/jfoenix/skins/JFXPopupSkin.java +++ b/HMCL/src/main/java/com/jfoenix/skins/JFXPopupSkin.java @@ -49,6 +49,9 @@ public class JFXPopupSkin implements Skin { protected Node root; private Animation animation; + private Animation closeAnimation; + private boolean closing; + protected Scale scale; public JFXPopupSkin(JFXPopup control) { @@ -74,6 +77,8 @@ public void reset(PopupVPosition vAlign, PopupHPosition hAlign, double offsetX, } public final void animate() { + closing = false; + container.setMouseTransparent(false); if (animation != null) { if (animation.getStatus() == Status.STOPPED) { container.setOpacity(1); @@ -87,6 +92,41 @@ public final void animate() { } } + public final void animateClose(Runnable onFinished) { + if (closing) { + return; + } + + closing = true; + container.setMouseTransparent(true); + if (animation != null) { + animation.stop(); + } + + if (!AnimationUtils.isAnimationEnabled()) { + onFinished.run(); + init(); + return; + } + + Interpolator interpolator = Motion.EASE; + closeAnimation = new Timeline( + new KeyFrame(Duration.ZERO, + new KeyValue(popupContent.opacityProperty(), popupContent.getOpacity(), interpolator), + new KeyValue(scale.xProperty(), scale.getX(), interpolator), + new KeyValue(scale.yProperty(), scale.getY(), interpolator)), + new KeyFrame(Motion.SHORT4, + new KeyValue(popupContent.opacityProperty(), 0, interpolator), + new KeyValue(scale.xProperty(), 0, interpolator), + new KeyValue(scale.yProperty(), 0.01, interpolator))); + closeAnimation.setOnFinished(event -> { + closeAnimation = null; + onFinished.run(); + init(); + }); + closeAnimation.play(); + } + @Override public JFXPopup getSkinnable() { return control; @@ -103,6 +143,10 @@ public void dispose() { animation.stop(); animation = null; } + if (closeAnimation != null) { + closeAnimation.stop(); + closeAnimation = null; + } container = null; control = null; popupContent = null; @@ -132,6 +176,12 @@ protected Animation getAnimation() { public void init() { if (animation != null) animation.stop(); + if (closeAnimation != null) { + closeAnimation.stop(); + closeAnimation = null; + } + closing = false; + container.setMouseTransparent(false); container.setOpacity(0); scale.setX(1.0); scale.setY(0.01); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java index e3121aa88c..26e245f5c4 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/GameListCell.java @@ -53,6 +53,10 @@ public final class GameListCell extends ListCell { private final StringProperty tag = new SimpleStringProperty(); + private JFXPopup managementPopup; + + private GameListItem managementPopupItem; + public GameListCell() { BorderPane root = new BorderPane(); root.getStyleClass().add("md-list-cell"); @@ -132,7 +136,12 @@ public void fire() { if (item == null) return; - JFXPopup popup = getPopup(item); + JFXPopup popup = getManagementPopup(item); + if (popup.isShowing()) { + popup.hide(); + return; + } + JFXPopup.PopupVPosition vPosition = determineOptimalPopupPosition(root, popup); popup.show(root, vPosition, JFXPopup.PopupHPosition.RIGHT, 0, vPosition == JFXPopup.PopupVPosition.TOP ? root.getHeight() : -root.getHeight()); }); @@ -152,7 +161,12 @@ public void fire() { item.modifyGameSettings(); } } else if (e.getButton() == MouseButton.SECONDARY) { - JFXPopup popup = getPopup(item); + JFXPopup popup = getManagementPopup(item); + if (popup.isShowing()) { + popup.hide(); + return; + } + JFXPopup.PopupVPosition vPosition = determineOptimalPopupPosition(root, popup); popup.show(root, vPosition, JFXPopup.PopupHPosition.LEFT, e.getX(), vPosition == JFXPopup.PopupVPosition.TOP ? e.getY() : e.getY() - root.getHeight()); } @@ -168,6 +182,12 @@ public void updateItem(GameListItem item, boolean empty) { if (oldItem == item && oldEmpty == empty) return; + if (managementPopup != null) { + managementPopup.hide(); + managementPopup = null; + managementPopupItem = null; + } + this.graphic.releaseRippleImmediately(); this.imageView.imageProperty().unbind(); @@ -193,9 +213,21 @@ public void updateItem(GameListItem item, boolean empty) { } } - private static JFXPopup getPopup(GameListItem item) { + private JFXPopup getManagementPopup(GameListItem item) { + if (managementPopup == null || managementPopupItem != item) { + if (managementPopup != null) { + managementPopup.hide(); + } + managementPopup = createPopup(item); + managementPopupItem = item; + } + return managementPopup; + } + + private static JFXPopup createPopup(GameListItem item) { PopupMenu menu = new PopupMenu(); JFXPopup popup = new JFXPopup(menu); + popup.setConsumeAutoHidingEvents(true); menu.getContent().setAll( new IconedMenuItem(SVG.ROCKET_LAUNCH, i18n("instance.launch.test"), item::testGame, popup),