Skip to content

Commit c06f898

Browse files
authored
将 JFXTextField 右键菜单改为 JFX 样式 (#5292)
1 parent b09b915 commit c06f898

11 files changed

Lines changed: 1343 additions & 10 deletions

File tree

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package com.jfoenix.controls;
21+
22+
import com.jfoenix.converters.base.NodeConverter;
23+
import com.jfoenix.skins.JFXComboBoxListViewSkin;
24+
import javafx.beans.property.ObjectProperty;
25+
import javafx.beans.property.SimpleObjectProperty;
26+
import javafx.collections.ObservableList;
27+
import javafx.css.*;
28+
import javafx.css.converter.BooleanConverter;
29+
import javafx.css.converter.PaintConverter;
30+
import javafx.geometry.Insets;
31+
import javafx.geometry.Pos;
32+
import javafx.scene.Node;
33+
import javafx.scene.control.*;
34+
import javafx.scene.layout.Background;
35+
import javafx.scene.layout.BackgroundFill;
36+
import javafx.scene.layout.StackPane;
37+
import javafx.scene.paint.Color;
38+
import javafx.scene.paint.Paint;
39+
import javafx.util.StringConverter;
40+
41+
import java.util.ArrayList;
42+
import java.util.Collections;
43+
import java.util.List;
44+
45+
import static org.jackhuang.hmcl.ui.FXUtils.useJFXContextMenu;
46+
47+
/**
48+
* JFXComboBox is the material design implementation of a combobox.
49+
*
50+
* @author Shadi Shaheen
51+
* @version 1.0
52+
* @since 2016-03-09
53+
*/
54+
public class JFXComboBox<T> extends ComboBox<T> {
55+
56+
/**
57+
* {@inheritDoc}
58+
*/
59+
public JFXComboBox() {
60+
initialize();
61+
}
62+
63+
/**
64+
* {@inheritDoc}
65+
*/
66+
public JFXComboBox(ObservableList<T> items) {
67+
super(items);
68+
initialize();
69+
}
70+
71+
private void initialize() {
72+
getStyleClass().add(DEFAULT_STYLE_CLASS);
73+
this.setCellFactory(listView -> new JFXListCell<T>() {
74+
@Override
75+
public void updateItem(T item, boolean empty) {
76+
super.updateItem(item, empty);
77+
updateDisplayText(this, item, empty);
78+
}
79+
});
80+
81+
// had to refactor the code out of the skin class to allow
82+
// customization of the button cell
83+
this.setButtonCell(new ListCell<T>() {
84+
{
85+
// fixed clearing the combo box value is causing
86+
// java prompt text to be shown because the button cell is not updated
87+
JFXComboBox.this.valueProperty().addListener(observable -> {
88+
if (JFXComboBox.this.getValue() == null) {
89+
updateItem(null, true);
90+
}
91+
});
92+
}
93+
94+
@Override
95+
protected void updateItem(T item, boolean empty) {
96+
updateDisplayText(this, item, empty);
97+
this.setVisible(item != null || !empty);
98+
}
99+
100+
});
101+
102+
useJFXContextMenu(editorProperty().get());
103+
}
104+
105+
/**
106+
* {@inheritDoc}
107+
*/
108+
@Override
109+
protected Skin<?> createDefaultSkin() {
110+
return new JFXComboBoxListViewSkin<T>(this);
111+
}
112+
113+
/**
114+
* Initialize the style class to 'jfx-combo-box'.
115+
* <p>
116+
* This is the selector class from which CSS can be used to style
117+
* this control.
118+
*/
119+
private static final String DEFAULT_STYLE_CLASS = "jfx-combo-box";
120+
121+
/***************************************************************************
122+
* *
123+
* Node Converter Property *
124+
* *
125+
**************************************************************************/
126+
/**
127+
* Converts the user-typed input (when the ComboBox is
128+
* {@link #editableProperty() editable}) to an object of type T, such that
129+
* the input may be retrieved via the {@link #valueProperty() value} property.
130+
*/
131+
public ObjectProperty<NodeConverter<T>> nodeConverterProperty() {
132+
return nodeConverter;
133+
}
134+
135+
private ObjectProperty<NodeConverter<T>> nodeConverter = new SimpleObjectProperty<>(this, "nodeConverter",
136+
JFXComboBox.<T>defaultNodeConverter());
137+
138+
public final void setNodeConverter(NodeConverter<T> value) {
139+
nodeConverterProperty().set(value);
140+
}
141+
142+
public final NodeConverter<T> getNodeConverter() {
143+
return nodeConverterProperty().get();
144+
}
145+
146+
private static <T> NodeConverter<T> defaultNodeConverter() {
147+
return new NodeConverter<T>() {
148+
@Override
149+
public Node toNode(T object) {
150+
if (object == null) {
151+
return null;
152+
}
153+
StackPane selectedValueContainer = new StackPane();
154+
selectedValueContainer.getStyleClass().add("combo-box-selected-value-container");
155+
selectedValueContainer.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT, null, null)));
156+
Label selectedValueLabel = object instanceof Label ? new Label(((Label) object).getText()) : new Label(
157+
object.toString());
158+
selectedValueLabel.setTextFill(Color.BLACK);
159+
selectedValueContainer.getChildren().add(selectedValueLabel);
160+
StackPane.setAlignment(selectedValueLabel, Pos.CENTER_LEFT);
161+
StackPane.setMargin(selectedValueLabel, new Insets(0, 0, 0, 5));
162+
return selectedValueContainer;
163+
}
164+
165+
@SuppressWarnings("unchecked")
166+
@Override
167+
public T fromNode(Node node) {
168+
return (T) node;
169+
}
170+
171+
@Override
172+
public String toString(T object) {
173+
if (object == null) {
174+
return null;
175+
}
176+
if (object instanceof Label) {
177+
return ((Label) object).getText();
178+
}
179+
return object.toString();
180+
}
181+
};
182+
}
183+
184+
private boolean updateDisplayText(ListCell<T> cell, T item, boolean empty) {
185+
if (empty) {
186+
// create empty cell
187+
if (cell == null) {
188+
return true;
189+
}
190+
cell.setGraphic(null);
191+
cell.setText(null);
192+
return true;
193+
} else if (item instanceof Node) {
194+
Node currentNode = cell.getGraphic();
195+
Node newNode = (Node) item;
196+
// create a node from the selected node of the listview
197+
// using JFXComboBox {@link #nodeConverterProperty() NodeConverter})
198+
NodeConverter<T> nc = this.getNodeConverter();
199+
Node node = nc == null ? null : nc.toNode(item);
200+
if (currentNode == null || !currentNode.equals(newNode)) {
201+
cell.setText(null);
202+
cell.setGraphic(node == null ? newNode : node);
203+
}
204+
return node == null;
205+
} else {
206+
// run item through StringConverter if it isn't null
207+
StringConverter<T> c = this.getConverter();
208+
String s = item == null ? this.getPromptText() : (c == null ? item.toString() : c.toString(item));
209+
cell.setText(s);
210+
cell.setGraphic(null);
211+
return s == null || s.isEmpty();
212+
}
213+
}
214+
215+
/***************************************************************************
216+
* *
217+
* styleable Properties *
218+
* *
219+
**************************************************************************/
220+
221+
/**
222+
* set true to show a float the prompt text when focusing the field
223+
*/
224+
private StyleableBooleanProperty labelFloat = new SimpleStyleableBooleanProperty(StyleableProperties.LABEL_FLOAT,
225+
JFXComboBox.this,
226+
"lableFloat",
227+
false);
228+
229+
public final StyleableBooleanProperty labelFloatProperty() {
230+
return this.labelFloat;
231+
}
232+
233+
public final boolean isLabelFloat() {
234+
return this.labelFloatProperty().get();
235+
}
236+
237+
public final void setLabelFloat(final boolean labelFloat) {
238+
this.labelFloatProperty().set(labelFloat);
239+
}
240+
241+
/**
242+
* default color used when the field is unfocused
243+
*/
244+
private StyleableObjectProperty<Paint> unFocusColor = new SimpleStyleableObjectProperty<>(StyleableProperties.UNFOCUS_COLOR,
245+
JFXComboBox.this,
246+
"unFocusColor",
247+
Color.rgb(77,
248+
77,
249+
77));
250+
251+
public Paint getUnFocusColor() {
252+
return unFocusColor == null ? Color.rgb(77, 77, 77) : unFocusColor.get();
253+
}
254+
255+
public StyleableObjectProperty<Paint> unFocusColorProperty() {
256+
return this.unFocusColor;
257+
}
258+
259+
public void setUnFocusColor(Paint color) {
260+
this.unFocusColor.set(color);
261+
}
262+
263+
/**
264+
* default color used when the field is focused
265+
*/
266+
private StyleableObjectProperty<Paint> focusColor = new SimpleStyleableObjectProperty<>(StyleableProperties.FOCUS_COLOR,
267+
JFXComboBox.this,
268+
"focusColor",
269+
Color.valueOf("#4059A9"));
270+
271+
public Paint getFocusColor() {
272+
return focusColor == null ? Color.valueOf("#4059A9") : focusColor.get();
273+
}
274+
275+
public StyleableObjectProperty<Paint> focusColorProperty() {
276+
return this.focusColor;
277+
}
278+
279+
public void setFocusColor(Paint color) {
280+
this.focusColor.set(color);
281+
}
282+
283+
private final static class StyleableProperties {
284+
private static final CssMetaData<JFXComboBox<?>, Paint> UNFOCUS_COLOR = new CssMetaData<JFXComboBox<?>, Paint>(
285+
"-jfx-unfocus-color",
286+
PaintConverter.getInstance(),
287+
Color.valueOf("#A6A6A6")) {
288+
@Override
289+
public boolean isSettable(JFXComboBox<?> control) {
290+
return control.unFocusColor == null || !control.unFocusColor.isBound();
291+
}
292+
293+
@Override
294+
public StyleableProperty<Paint> getStyleableProperty(JFXComboBox<?> control) {
295+
return control.unFocusColorProperty();
296+
}
297+
};
298+
private static final CssMetaData<JFXComboBox<?>, Paint> FOCUS_COLOR = new CssMetaData<JFXComboBox<?>, Paint>(
299+
"-jfx-focus-color",
300+
PaintConverter.getInstance(),
301+
Color.valueOf("#3f51b5")) {
302+
@Override
303+
public boolean isSettable(JFXComboBox<?> control) {
304+
return control.focusColor == null || !control.focusColor.isBound();
305+
}
306+
307+
@Override
308+
public StyleableProperty<Paint> getStyleableProperty(JFXComboBox<?> control) {
309+
return control.focusColorProperty();
310+
}
311+
};
312+
private static final CssMetaData<JFXComboBox<?>, Boolean> LABEL_FLOAT = new CssMetaData<JFXComboBox<?>, Boolean>(
313+
"-jfx-label-float",
314+
BooleanConverter.getInstance(),
315+
false) {
316+
@Override
317+
public boolean isSettable(JFXComboBox<?> control) {
318+
return control.labelFloat == null || !control.labelFloat.isBound();
319+
}
320+
321+
@Override
322+
public StyleableBooleanProperty getStyleableProperty(JFXComboBox<?> control) {
323+
return control.labelFloatProperty();
324+
}
325+
};
326+
327+
328+
private static final List<CssMetaData<? extends Styleable, ?>> CHILD_STYLEABLES;
329+
330+
static {
331+
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(
332+
Control.getClassCssMetaData());
333+
Collections.addAll(styleables, UNFOCUS_COLOR, FOCUS_COLOR, LABEL_FLOAT);
334+
CHILD_STYLEABLES = Collections.unmodifiableList(styleables);
335+
}
336+
}
337+
338+
// inherit the styleable properties from parent
339+
private List<CssMetaData<? extends Styleable, ?>> STYLEABLES;
340+
341+
@Override
342+
public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {
343+
if (STYLEABLES == null) {
344+
final List<CssMetaData<? extends Styleable, ?>> styleables = new ArrayList<>(
345+
Control.getClassCssMetaData());
346+
styleables.addAll(getClassCssMetaData());
347+
styleables.addAll(Control.getClassCssMetaData());
348+
STYLEABLES = Collections.unmodifiableList(styleables);
349+
}
350+
return STYLEABLES;
351+
}
352+
353+
public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {
354+
return StyleableProperties.CHILD_STYLEABLES;
355+
}
356+
}
357+

0 commit comments

Comments
 (0)