Skip to content

Commit cfcbe6e

Browse files
authored
修复修改主题色后 JFXCheckBox 颜色不会及时变化的问题 (#4871)
1 parent fe809ad commit cfcbe6e

1 file changed

Lines changed: 219 additions & 0 deletions

File tree

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
//
2+
// Source code recreated from a .class file by IntelliJ IDEA
3+
// (powered by FernFlower decompiler)
4+
//
5+
6+
package com.jfoenix.skins;
7+
8+
import com.jfoenix.controls.JFXCheckBox;
9+
import com.jfoenix.controls.JFXRippler;
10+
import com.jfoenix.controls.JFXRippler.RipplerMask;
11+
import com.jfoenix.controls.JFXRippler.RipplerPos;
12+
import com.jfoenix.transitions.CachedTransition;
13+
import com.jfoenix.transitions.JFXFillTransition;
14+
import com.jfoenix.utils.JFXNodeUtils;
15+
import javafx.animation.Interpolator;
16+
import javafx.animation.KeyFrame;
17+
import javafx.animation.KeyValue;
18+
import javafx.animation.Timeline;
19+
import javafx.animation.Transition;
20+
import javafx.geometry.HPos;
21+
import javafx.geometry.Insets;
22+
import javafx.geometry.VPos;
23+
import javafx.scene.control.CheckBox;
24+
import javafx.scene.control.skin.CheckBoxSkin;
25+
import javafx.scene.layout.AnchorPane;
26+
import javafx.scene.layout.Background;
27+
import javafx.scene.layout.BackgroundFill;
28+
import javafx.scene.layout.Border;
29+
import javafx.scene.layout.BorderStroke;
30+
import javafx.scene.layout.BorderStrokeStyle;
31+
import javafx.scene.layout.BorderWidths;
32+
import javafx.scene.layout.CornerRadii;
33+
import javafx.scene.layout.StackPane;
34+
import javafx.scene.paint.Color;
35+
import javafx.scene.paint.Paint;
36+
import javafx.scene.shape.SVGPath;
37+
import javafx.util.Duration;
38+
39+
public class JFXCheckBoxSkin extends CheckBoxSkin {
40+
private final StackPane box = new StackPane();
41+
private final StackPane mark = new StackPane();
42+
private final double lineThick = 2.0;
43+
private final double padding = 10.0;
44+
private final JFXRippler rippler;
45+
private final AnchorPane container = new AnchorPane();
46+
private final double labelOffset = -8.0;
47+
private final Transition transition;
48+
private boolean invalid = true;
49+
private JFXFillTransition select;
50+
51+
public JFXCheckBoxSkin(JFXCheckBox control) {
52+
super(control);
53+
this.box.setMinSize(18.0, 18.0);
54+
this.box.setPrefSize(18.0, 18.0);
55+
this.box.setMaxSize(18.0, 18.0);
56+
this.box.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, new CornerRadii(2.0), Insets.EMPTY)));
57+
this.box.setBorder(new Border(new BorderStroke(control.getUnCheckedColor(), BorderStrokeStyle.SOLID, new CornerRadii(2.0), new BorderWidths(this.lineThick))));
58+
StackPane boxContainer = new StackPane();
59+
boxContainer.getChildren().add(this.box);
60+
boxContainer.setPadding(new Insets(this.padding));
61+
this.rippler = new JFXRippler(boxContainer, RipplerMask.CIRCLE, RipplerPos.BACK);
62+
this.updateRippleColor();
63+
SVGPath shape = new SVGPath();
64+
shape.setContent("M384 690l452-452 60 60-512 512-238-238 60-60z");
65+
this.mark.setShape(shape);
66+
this.mark.setMaxSize(15.0, 12.0);
67+
this.mark.setStyle("-fx-background-color:WHITE; -fx-border-color:WHITE; -fx-border-width:2px;");
68+
this.mark.setVisible(false);
69+
this.mark.setScaleX(0.0);
70+
this.mark.setScaleY(0.0);
71+
boxContainer.getChildren().add(this.mark);
72+
this.container.getChildren().add(this.rippler);
73+
AnchorPane.setRightAnchor(this.rippler, this.labelOffset);
74+
control.selectedProperty().addListener((o, oldVal, newVal) -> {
75+
this.updateRippleColor();
76+
this.playSelectAnimation(newVal);
77+
});
78+
control.focusedProperty().addListener((o, oldVal, newVal) -> {
79+
if (newVal) {
80+
if (!this.getSkinnable().isPressed()) {
81+
this.rippler.showOverlay();
82+
}
83+
} else {
84+
this.rippler.hideOverlay();
85+
}
86+
});
87+
control.pressedProperty().addListener((o, oldVal, newVal) -> this.rippler.hideOverlay());
88+
this.updateChildren();
89+
this.registerChangeListener(control.checkedColorProperty(), ignored -> {
90+
if (select != null) {
91+
select.stop();
92+
}
93+
this.createFillTransition();
94+
updateColors();
95+
});
96+
this.registerChangeListener(control.unCheckedColorProperty(), ignored -> updateColors());
97+
this.transition = new CheckBoxTransition();
98+
this.createFillTransition();
99+
}
100+
101+
private void updateRippleColor() {
102+
var control = (JFXCheckBox) this.getSkinnable();
103+
this.rippler.setRipplerFill(control.isSelected()
104+
? control.getCheckedColor()
105+
: control.getUnCheckedColor());
106+
}
107+
108+
private void updateColors() {
109+
var control = (JFXCheckBox) getSkinnable();
110+
final Paint color = control.isSelected()
111+
? control.getCheckedColor()
112+
: control.getUnCheckedColor();
113+
JFXNodeUtils.updateBackground(box.getBackground(), box, control.isSelected() ? control.getCheckedColor() : Color.TRANSPARENT);
114+
rippler.setRipplerFill(color);
115+
final BorderStroke borderStroke = box.getBorder().getStrokes().get(0);
116+
box.setBorder(new Border(new BorderStroke(color,
117+
borderStroke.getTopStyle(),
118+
borderStroke.getRadii(),
119+
borderStroke.getWidths())));
120+
}
121+
122+
protected void updateChildren() {
123+
super.updateChildren();
124+
if (this.container != null) {
125+
this.getChildren().remove(1);
126+
this.getChildren().add(this.container);
127+
}
128+
}
129+
130+
protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
131+
return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset) + this.snapSizeX(this.box.minWidth(-1.0)) + this.labelOffset + 2.0 * this.padding;
132+
}
133+
134+
protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
135+
return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset) + this.snapSizeY(this.box.prefWidth(-1.0)) + this.labelOffset + 2.0 * this.padding;
136+
}
137+
138+
protected void layoutChildren(double x, double y, double w, double h) {
139+
CheckBox checkBox = this.getSkinnable();
140+
double boxWidth = this.snapSizeX(this.container.prefWidth(-1.0));
141+
double boxHeight = this.snapSizeY(this.container.prefHeight(-1.0));
142+
double computeWidth = Math.min(checkBox.prefWidth(-1.0), checkBox.minWidth(-1.0)) + this.labelOffset + 2.0 * this.padding;
143+
double labelWidth = Math.min(computeWidth - boxWidth, w - this.snapSizeX(boxWidth)) + this.labelOffset + 2.0 * this.padding;
144+
double labelHeight = Math.min(checkBox.prefHeight(labelWidth), h);
145+
double maxHeight = Math.max(boxHeight, labelHeight);
146+
double xOffset = computeXOffset(w, labelWidth + boxWidth, checkBox.getAlignment().getHpos()) + x;
147+
double yOffset = computeYOffset(h, maxHeight, checkBox.getAlignment().getVpos()) + x;
148+
if (this.invalid) {
149+
if (this.getSkinnable().isSelected()) {
150+
this.playSelectAnimation(true);
151+
}
152+
153+
this.invalid = false;
154+
}
155+
156+
this.layoutLabelInArea(xOffset + boxWidth, yOffset, labelWidth, maxHeight, checkBox.getAlignment());
157+
this.container.resize(boxWidth, boxHeight);
158+
this.positionInArea(this.container, xOffset, yOffset, boxWidth, maxHeight, 0.0, checkBox.getAlignment().getHpos(), checkBox.getAlignment().getVpos());
159+
}
160+
161+
static double computeXOffset(double width, double contentWidth, HPos hpos) {
162+
return switch (hpos) {
163+
case LEFT -> 0.0;
164+
case CENTER -> (width - contentWidth) / 2.0;
165+
case RIGHT -> width - contentWidth;
166+
};
167+
}
168+
169+
static double computeYOffset(double height, double contentHeight, VPos vpos) {
170+
return switch (vpos) {
171+
case TOP -> 0.0;
172+
case CENTER -> (height - contentHeight) / 2.0;
173+
case BOTTOM -> height - contentHeight;
174+
default -> 0.0;
175+
};
176+
}
177+
178+
private void playSelectAnimation(Boolean selection) {
179+
if (selection == null) {
180+
selection = false;
181+
}
182+
183+
JFXCheckBox control = (JFXCheckBox) this.getSkinnable();
184+
this.transition.setRate(selection ? 1.0 : -1.0);
185+
this.select.setRate(selection ? 1.0 : -1.0);
186+
this.transition.play();
187+
this.select.play();
188+
this.box.setBorder(new Border(new BorderStroke(selection ? control.getCheckedColor() : control.getUnCheckedColor(), BorderStrokeStyle.SOLID, new CornerRadii(2.0), new BorderWidths(this.lineThick))));
189+
}
190+
191+
private void createFillTransition() {
192+
this.select = new JFXFillTransition(Duration.millis(120.0), this.box, Color.TRANSPARENT,
193+
(Color) ((JFXCheckBox) this.getSkinnable()).getCheckedColor());
194+
this.select.setInterpolator(Interpolator.EASE_OUT);
195+
}
196+
197+
private final class CheckBoxTransition extends CachedTransition {
198+
CheckBoxTransition() {
199+
super(JFXCheckBoxSkin.this.mark,
200+
new Timeline(
201+
new KeyFrame(Duration.ZERO,
202+
new KeyValue(JFXCheckBoxSkin.this.mark.visibleProperty(), false, Interpolator.EASE_OUT),
203+
new KeyValue(JFXCheckBoxSkin.this.mark.scaleXProperty(), (double) 0.5F, Interpolator.EASE_OUT),
204+
new KeyValue(JFXCheckBoxSkin.this.mark.scaleYProperty(), (double) 0.5F, Interpolator.EASE_OUT)),
205+
new KeyFrame(Duration.millis(400.0),
206+
new KeyValue(JFXCheckBoxSkin.this.mark.visibleProperty(), true, Interpolator.EASE_OUT),
207+
new KeyValue(JFXCheckBoxSkin.this.mark.scaleXProperty(), (double) 0.5F, Interpolator.EASE_OUT),
208+
new KeyValue(JFXCheckBoxSkin.this.mark.scaleYProperty(), (double) 0.5F, Interpolator.EASE_OUT)),
209+
new KeyFrame(Duration.millis(1000.0),
210+
new KeyValue(JFXCheckBoxSkin.this.mark.visibleProperty(), true, Interpolator.EASE_OUT),
211+
new KeyValue(JFXCheckBoxSkin.this.mark.scaleXProperty(), 1, Interpolator.EASE_OUT),
212+
new KeyValue(JFXCheckBoxSkin.this.mark.scaleYProperty(), 1, Interpolator.EASE_OUT))
213+
)
214+
);
215+
this.setCycleDuration(Duration.seconds(0.12));
216+
this.setDelay(Duration.seconds(0.05));
217+
}
218+
}
219+
}

0 commit comments

Comments
 (0)