From 72d8b7564349ec3535ca59a8fd2fcf20304c4304 Mon Sep 17 00:00:00 2001 From: Haowei Wen Date: Thu, 16 Jan 2025 11:15:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E7=BB=84=E5=88=97=E8=A1=A8=E9=A1=B5?= =?UTF-8?q?=E4=B8=AD=E6=8C=89=E4=B8=8B=20Esc=20=E6=97=B6=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E9=80=89=E4=B8=AD=20(#3523)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 模组列表页中按下 Esc 时取消选中 目前在模组列表页中选中模组后,按下 Esc 是不会有任何反应的,因为 [ListView](https://github.com/openjdk/jfx/blob/5b074c4c2b4f539301813322a6589cec28121553/modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/ListViewBehavior.java#L108) 会处理这个事件,认为是取消编辑,而不论是否处于编辑模式中。 这个 PR 添加了 `FXUtils.ignoreEvent()` 函数,用于忽略掉对特定事件的处理。然后,我们就可以让 ListView 不处理 Esc 按下事件,然后在模组页添加 Esc 处理逻辑(必须让 ListView 忽略掉这个事件,不然就会被它优先处理)。 理论上所有用了 ListView / JFXListView 的地方都会有这个问题,即当焦点在 ListView 中时,按下 Esc 不会触发正常的事件处理(如返回上一页、关闭对话框)。比如在游戏下载页面中,对某个版本按下右键后,焦点就会转移到其上,此时再按 Esc 就毫无反应。 * 修复 Esc 在版本下载页/模组下载页/数据包页不工作 --- .../java/org/jackhuang/hmcl/ui/FXUtils.java | 19 +++++++++++++++++++ .../hmcl/ui/download/VersionsPage.java | 6 ++++++ .../ui/versions/DatapackListPageSkin.java | 6 ++++++ .../hmcl/ui/versions/DownloadListPage.java | 6 ++++++ .../hmcl/ui/versions/ModListPageSkin.java | 17 +++++++++++++++++ 5 files changed, 54 insertions(+) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java index 7759337bb..20efb21eb 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/FXUtils.java @@ -27,6 +27,9 @@ import javafx.beans.WeakInvalidationListener; import javafx.beans.property.BooleanProperty; import javafx.beans.property.Property; import javafx.beans.value.*; +import javafx.event.Event; +import javafx.event.EventDispatcher; +import javafx.event.EventType; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Cursor; @@ -86,6 +89,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Function; +import java.util.function.Predicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -234,6 +238,21 @@ public final class FXUtils { }); } + @SuppressWarnings("unchecked") + public static void ignoreEvent(Node node, EventType type, Predicate filter) { + EventDispatcher oldDispatcher = node.getEventDispatcher(); + node.setEventDispatcher((event, tail) -> { + EventType t = event.getEventType(); + while (t != null && t != type) + t = t.getSuperType(); + if (t == type && filter.test((T) event)) { + return tail.dispatchEvent(event); + } else { + return oldDispatcher.dispatchEvent(event, tail); + } + }); + } + public static void setupCellValueFactory(JFXTreeTableColumn column, Function> mapper) { column.setCellValueFactory(param -> { if (column.validateValue(param)) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index 8a94ab970..754ad121c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -29,6 +29,8 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.control.ListCell; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import javafx.util.Duration; import org.jackhuang.hmcl.download.DownloadProvider; @@ -66,6 +68,7 @@ import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.wrap; import static org.jackhuang.hmcl.util.logging.Logger.LOG; @@ -150,6 +153,9 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres list.getStyleClass().add("jfx-list-view-float"); VBox.setVgrow(list, Priority.ALWAYS); + // ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here + ignoreEvent(list, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE); + centrePane.getContent().setAll(checkPane, list); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java index 4baa9f316..dc6241f23 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java @@ -27,6 +27,8 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.SelectionMode; import javafx.scene.control.SkinBase; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Region; @@ -38,6 +40,7 @@ import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; import org.jackhuang.hmcl.util.StringUtils; +import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent; import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -110,6 +113,9 @@ class DatapackListPageSkin extends SkinBase { listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); Bindings.bindContent(listView.getItems(), skinnable.getItems()); + // ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here + ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE); + center.setContent(listView); root.setCenter(center); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java index dffe893a3..e1fe3ae8f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadListPage.java @@ -36,6 +36,8 @@ import javafx.scene.control.Label; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.*; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.mod.RemoteMod; @@ -64,6 +66,7 @@ import java.util.Locale; import java.util.Optional; import java.util.stream.Collectors; +import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent; import static org.jackhuang.hmcl.ui.FXUtils.stringConverter; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.javafx.ExtendedProperties.selectedItemPropertyFor; @@ -493,6 +496,9 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP RemoteMod selectedItem = listView.getSelectionModel().getSelectedItem(); Controllers.navigate(new DownloadPage(getSkinnable(), selectedItem, getSkinnable().getProfileVersion(), getSkinnable().callback)); }); + + // ListViewBehavior would consume ESC pressed event, preventing us from handling it, so we ignore it here + ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE); listView.setCellFactory(x -> new FloatListCell(listView) { TwoLineListItem content = new TwoLineListItem(); ImageView imageView = new ImageView(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 61674d3ed..66d3d6e1d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -31,6 +31,8 @@ import javafx.scene.control.SelectionMode; import javafx.scene.control.SkinBase; import javafx.scene.image.Image; import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; @@ -70,6 +72,7 @@ import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.jackhuang.hmcl.ui.FXUtils.ignoreEvent; import static org.jackhuang.hmcl.ui.FXUtils.onEscPressed; import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton2; import static org.jackhuang.hmcl.util.Lang.mapOf; @@ -170,6 +173,16 @@ class ModListPageSkin extends SkinBase { changeToolbar(toolbarSelecting); }); root.getContent().add(toolbarPane); + + // Clear selection when pressing ESC + root.addEventHandler(KeyEvent.KEY_PRESSED, e -> { + if (e.getCode() == KeyCode.ESCAPE) { + if (listView.getSelectionModel().getSelectedItem() != null) { + listView.getSelectionModel().clearSelection(); + e.consume(); + } + } + }); } { @@ -196,6 +209,10 @@ class ModListPageSkin extends SkinBase { } }); + // ListViewBehavior would consume ESC pressed event, preventing us from handling it + // So we ignore it here + ignoreEvent(listView, KeyEvent.KEY_PRESSED, e -> e.getCode() == KeyCode.ESCAPE); + center.setContent(listView); root.getContent().add(center); }