修复窗口重新获取焦点时 JFXRadioButton 显示阴影的问题 (#5153)

This commit is contained in:
Glavo
2026-01-06 22:25:59 +08:00
committed by GitHub
parent 4ad368560c
commit 243f3e7371
2 changed files with 37 additions and 2 deletions

View File

@@ -12,6 +12,7 @@ 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.beans.property.ReadOnlyBooleanProperty;
import javafx.geometry.HPos; import javafx.geometry.HPos;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.geometry.VPos; import javafx.geometry.VPos;
@@ -23,6 +24,7 @@ import javafx.scene.paint.Color;
import javafx.scene.shape.Circle; import javafx.scene.shape.Circle;
import javafx.scene.text.Text; import javafx.scene.text.Text;
import javafx.util.Duration; import javafx.util.Duration;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.animation.AnimationUtils; import org.jackhuang.hmcl.ui.animation.AnimationUtils;
public class JFXRadioButtonSkin extends RadioButtonSkin { public class JFXRadioButtonSkin extends RadioButtonSkin {
@@ -58,7 +60,12 @@ public class JFXRadioButtonSkin extends RadioButtonSkin {
AnchorPane.setRightAnchor(this.rippler, this.labelOffset); AnchorPane.setRightAnchor(this.rippler, this.labelOffset);
this.updateChildren(); this.updateChildren();
control.focusedProperty().addListener((o, oldVal, newVal) -> { ReadOnlyBooleanProperty focusVisibleProperty = FXUtils.focusVisibleProperty(control);
if (focusVisibleProperty == null) {
focusVisibleProperty = control.focusedProperty();
}
focusVisibleProperty.addListener((o, oldVal, newVal) -> {
if (newVal) { if (newVal) {
if (!this.getSkinnable().isPressed()) { if (!this.getSkinnable().isPressed()) {
this.rippler.showOverlay(); this.rippler.showOverlay();
@@ -66,7 +73,6 @@ public class JFXRadioButtonSkin extends RadioButtonSkin {
} else { } else {
this.rippler.hideOverlay(); this.rippler.hideOverlay();
} }
}); });
control.pressedProperty().addListener((o, oldVal, newVal) -> this.rippler.hideOverlay()); control.pressedProperty().addListener((o, oldVal, newVal) -> this.rippler.hideOverlay());
this.registerChangeListener(control.selectedColorProperty(), ignored -> updateColors()); this.registerChangeListener(control.selectedColorProperty(), ignored -> updateColors());

View File

@@ -137,6 +137,7 @@ public final class FXUtils {
public static final @Nullable ReadOnlyObjectProperty<Color> ACCENT_COLOR; public static final @Nullable ReadOnlyObjectProperty<Color> ACCENT_COLOR;
public static final @Nullable MethodHandle TEXT_TRUNCATED_PROPERTY; public static final @Nullable MethodHandle TEXT_TRUNCATED_PROPERTY;
public static final @Nullable MethodHandle FOCUS_VISIBLE_PROPERTY;
static { static {
String jfxVersion = System.getProperty("javafx.version"); String jfxVersion = System.getProperty("javafx.version");
@@ -203,6 +204,20 @@ public final class FXUtils {
} }
} }
TEXT_TRUNCATED_PROPERTY = textTruncatedProperty; TEXT_TRUNCATED_PROPERTY = textTruncatedProperty;
MethodHandle focusVisibleProperty = null;
if (JAVAFX_MAJOR_VERSION >= 19) {
try {
focusVisibleProperty = MethodHandles.publicLookup().findVirtual(
Node.class,
"focusVisibleProperty",
MethodType.methodType(ReadOnlyBooleanProperty.class)
);
} catch (Throwable e) {
LOG.warning("Failed to lookup focusVisibleProperty", e);
}
}
FOCUS_VISIBLE_PROPERTY = focusVisibleProperty;
} }
public static final String DEFAULT_MONOSPACE_FONT = OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "Consolas" : "Monospace"; public static final String DEFAULT_MONOSPACE_FONT = OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS ? "Consolas" : "Monospace";
@@ -423,6 +438,20 @@ public final class FXUtils {
} }
} }
public static @Nullable ReadOnlyBooleanProperty focusVisibleProperty(Node node) {
if (FOCUS_VISIBLE_PROPERTY != null) {
try {
return (ReadOnlyBooleanProperty) FOCUS_VISIBLE_PROPERTY.invokeExact(node);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new RuntimeException(e);
}
} else {
return null;
}
}
private static final Duration TOOLTIP_FAST_SHOW_DELAY = Duration.millis(50); private static final Duration TOOLTIP_FAST_SHOW_DELAY = Duration.millis(50);
private static final Duration TOOLTIP_SLOW_SHOW_DELAY = Duration.millis(500); private static final Duration TOOLTIP_SLOW_SHOW_DELAY = Duration.millis(500);
private static final Duration TOOLTIP_SHOW_DURATION = Duration.millis(5000); private static final Duration TOOLTIP_SHOW_DURATION = Duration.millis(5000);