Skip to content

Commit 97329d6

Browse files
committed
UI/UX: Create 'TagBar' components that making tag UI according to inputed text
1 parent 4e1e841 commit 97329d6

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package root.javafx.CustomView;
2+
3+
import java.util.ArrayList;
4+
import java.util.stream.Collectors;
5+
6+
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
7+
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView;
8+
import javafx.collections.FXCollections;
9+
import javafx.collections.ListChangeListener;
10+
import javafx.collections.ObservableList;
11+
import javafx.geometry.Insets;
12+
import javafx.scene.Node;
13+
import javafx.scene.control.Button;
14+
import javafx.scene.control.TextField;
15+
import javafx.scene.layout.FlowPane;
16+
import javafx.scene.layout.HBox;
17+
import javafx.scene.paint.Paint;
18+
import javafx.scene.text.Text;
19+
20+
public class TagBar extends FlowPane {
21+
22+
private final ObservableList<String> tags;
23+
private final TextField inputTextField;
24+
25+
public TagBar() {
26+
getStyleClass().setAll("tag-bar");
27+
getStylesheets().add(getClass().getResource("/css/javaFx.css").toExternalForm());
28+
29+
// Initialize
30+
tags = FXCollections.observableArrayList();
31+
inputTextField = new TextField();
32+
33+
// Set on action event on TextField (Add tag to tags and clear TextField text)
34+
inputTextField.setOnAction(e -> {
35+
String text = inputTextField.getText();
36+
if (!text.isEmpty() && !tags.contains(text)) {
37+
tags.add(text);
38+
inputTextField.clear();
39+
}
40+
});
41+
getChildren().add(inputTextField);
42+
43+
// Focusing when mouse click this FlowPane
44+
setOnMouseClicked(e -> {
45+
inputTextField.requestFocus();
46+
});
47+
48+
// Change TextField width according to length of input text
49+
inputTextField.textProperty().addListener((ob, oldValue, newValue) -> {
50+
Text textWrapper = new Text(newValue);
51+
textWrapper.setFont(inputTextField.getFont());
52+
inputTextField.setPrefWidth(Math.ceil(textWrapper.getLayoutBounds().getWidth()) + 20);
53+
});
54+
55+
// Set ListChangeListener on tags list
56+
tags.addListener((ListChangeListener.Change<? extends String> change) -> {
57+
while (change.next()) {
58+
if (change.wasPermutated()) {
59+
ArrayList<Node> newSublist = new ArrayList<>(change.getTo() - change.getFrom());
60+
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
61+
newSublist.add(null);
62+
}
63+
64+
for (int i = change.getFrom(), end = change.getTo(); i < end; i++) {
65+
newSublist.set(change.getPermutation(i), getChildren().get(i));
66+
}
67+
68+
getChildren().subList(change.getFrom(), change.getTo()).clear();
69+
getChildren().addAll(change.getFrom(), newSublist);
70+
} else {
71+
if (change.wasRemoved()) {
72+
getChildren().subList(change.getFrom(), change.getFrom() + change.getRemovedSize()).clear();
73+
}
74+
75+
if (change.wasAdded()) {
76+
getChildren().addAll(change.getFrom(),
77+
change.getAddedSubList().stream().map(Tag::new).collect(Collectors.toList()));
78+
}
79+
}
80+
}
81+
});
82+
}
83+
84+
public ObservableList<String> getTags() {
85+
return tags;
86+
}
87+
88+
private class Tag extends HBox {
89+
90+
public Tag(String tag) {
91+
getStyleClass().setAll("tag");
92+
FlowPane.setMargin(this, new Insets(2.5));
93+
94+
FontAwesomeIconView icon = new FontAwesomeIconView(FontAwesomeIcon.TIMES);
95+
icon.setFill(Paint.valueOf("gray"));
96+
97+
Button removeButton = new Button("", icon);
98+
removeButton.setOnAction((e) -> tags.remove(tag));
99+
100+
Text text = new Text(tag);
101+
HBox.setMargin(text, new Insets(0, 0, 0, 5));
102+
103+
getChildren().addAll(text, removeButton);
104+
}
105+
}
106+
107+
}

src/main/resources/css/javaFx.css

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,4 +298,38 @@
298298
.divider-exist:horizontal > .split-pane-divider {
299299
-fx-background-color: #dcdcdc;
300300
-fx-padding:0.5px;
301+
}
302+
303+
304+
/* tag-bar */
305+
.tag-bar {
306+
/*-fx-border-color: rgb(200, 200, 200);*/
307+
-fx-border-color: transparent;
308+
-fx-spacing: 3.0;
309+
-fx-padding: 3.0;
310+
-fx-max-height: 30.0;
311+
}
312+
313+
.tag-bar .text-field {
314+
-fx-border-width: 0.0px;
315+
-fx-background-color: transparent;
316+
}
317+
318+
.tag-bar .text-field:focused {
319+
-fx-focus-color: transparent;
320+
-fx-faint-focus-color: transparent;
321+
}
322+
323+
.tag-bar .tag {
324+
-fx-border-radius: 2.0px;
325+
-fx-border-width: 0.3px;
326+
-fx-border-color: gray;
327+
-fx-background-color: transparent;
328+
-fx-alignment: center;
329+
-fx-font-family: "Noto Sans Korean Regular";
330+
}
331+
332+
.tag-bar .tag .button {
333+
-fx-font-family: "Noto Sans Korean Regular";
334+
-fx-background-color: transparent;
301335
}

0 commit comments

Comments
 (0)