Skip to content

Commit aeb1205

Browse files
author
lcaouen
committed
add validation when loosing focus
1 parent 3ba99e5 commit aeb1205

File tree

3 files changed

+129
-40
lines changed

3 files changed

+129
-40
lines changed

app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/tree/TitleDetailDelayTable.java

Lines changed: 75 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99

1010
import java.util.ArrayList;
1111
import java.util.List;
12-
import java.util.concurrent.TimeUnit;
12+
import java.util.Objects;
1313

1414
import org.phoebus.applications.alarm.model.TitleDetailDelay;
1515
import org.phoebus.applications.alarm.ui.AlarmUI;
1616
import org.phoebus.ui.dialog.DialogHelper;
1717
import org.phoebus.ui.dialog.MultiLineInputDialog;
1818
import org.phoebus.ui.javafx.ImageCache;
19-
import org.phoebus.ui.javafx.UpdateThrottle;
2019

2120
import javafx.application.Platform;
2221
import javafx.beans.InvalidationListener;
@@ -35,7 +34,6 @@
3534
import javafx.scene.control.TableView;
3635
import javafx.scene.control.Tooltip;
3736
import javafx.scene.control.cell.ComboBoxTableCell;
38-
import javafx.scene.control.cell.TextFieldTableCell;
3937
import javafx.scene.layout.BorderPane;
4038
import javafx.scene.layout.VBox;
4139
import javafx.util.converter.DefaultStringConverter;
@@ -104,8 +102,49 @@ class DelayTableCell extends TableCell<TitleDetailDelay, Integer>
104102
public DelayTableCell()
105103
{
106104
this.spinner = new Spinner<>(0, 10000, 1);
107-
spinner.setEditable(true);
108-
this.spinner.valueProperty().addListener((observable, oldValue, newValue) -> commitEdit(newValue));
105+
this.spinner.setEditable(true);
106+
107+
// disable focus on buttons
108+
spinner.lookupAll(".increment-arrow-button, .decrement-arrow-button")
109+
.forEach(node -> node.setFocusTraversable(false));
110+
111+
this.spinner.valueProperty().addListener((obs, oldValue, newValue) -> {
112+
if (isEditing()) {
113+
commitEdit(newValue);
114+
}
115+
});
116+
117+
// validate when loosing focus
118+
spinner.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
119+
if (!isNowFocused) {
120+
Integer currentValue = spinner.getValue();
121+
Integer oldValue = getItem();
122+
123+
if (Objects.equals(currentValue, oldValue)) {
124+
cancelEdit();
125+
return;
126+
}
127+
128+
// if not currently editing, force table to enter edit mode first
129+
if (!isEditing()) {
130+
TableView<TitleDetailDelay> tv = getTableView();
131+
if (tv != null) {
132+
Platform.runLater(() -> {
133+
tv.getSelectionModel().clearAndSelect(getIndex());
134+
tv.edit(getIndex(), getTableColumn());
135+
// commit after we've asked the table to start editing
136+
commitEdit(currentValue);
137+
});
138+
} else {
139+
// fallback: commit anyway
140+
commitEdit(currentValue);
141+
}
142+
} else {
143+
// normal case
144+
commitEdit(currentValue);
145+
}
146+
}
147+
});
109148
}
110149

111150
@Override
@@ -135,6 +174,13 @@ public void updateItem(Integer item, boolean empty)
135174

136175
this.spinner.getValueFactory().setValue(item);
137176
setGraphic(spinner);
177+
// force focus on the textedit not buttons
178+
Platform.runLater(() -> {
179+
if (isEditing()) {
180+
spinner.getEditor().requestFocus();
181+
spinner.getEditor().end();
182+
}
183+
});
138184
}
139185
}
140186

@@ -147,20 +193,19 @@ private void createTable()
147193

148194
TableColumn<TitleDetailDelay, String> col = new TableColumn<>("Title");
149195
col.setCellValueFactory(cell -> new SimpleStringProperty(cell.getValue().title));
150-
col.setCellFactory(column -> new TextFieldTableCell<>(new DefaultStringConverter()));
196+
col.setCellFactory(ValidatingTextFieldTableCell.forTableColumn(new DefaultStringConverter()));
151197
col.setOnEditCommit(event ->
152198
{
153199
final int row = event.getTablePosition().getRow();
154200
items.set(row, new TitleDetailDelay(event.getNewValue(), items.get(row).detail, items.get(row).delay));
155201

156202
// Trigger editing the detail
157-
UpdateThrottle.TIMER.schedule(() ->
158-
Platform.runLater(() ->
159-
{
160-
table.getSelectionModel().clearAndSelect(row);
161-
table.edit(row, table.getColumns().get(1));
162-
}),
163-
200, TimeUnit.MILLISECONDS);
203+
Platform.runLater(() -> {
204+
TableColumn<TitleDetailDelay, ?> detailCol = table.getColumns().get(1);
205+
TableColumn<TitleDetailDelay, ?> optionCol = detailCol.getColumns().get(0);
206+
table.getSelectionModel().clearAndSelect(row);
207+
table.edit(row, optionCol);
208+
});
164209
});
165210
col.setSortable(false);
166211
table.getColumns().add(col);
@@ -182,18 +227,20 @@ private void createTable()
182227
items.set(row, newTitleDetailDelay);
183228
// Trigger editing the delay.
184229
if (newTitleDetailDelay.hasDelay())
185-
UpdateThrottle.TIMER.schedule(() -> Platform.runLater(() -> {
186-
table.getSelectionModel().clearAndSelect(row);
187-
table.edit(row, table.getColumns().get(2));
188-
}), 200, TimeUnit.MILLISECONDS);
230+
Platform.runLater(() -> {
231+
TableColumn<TitleDetailDelay, ?> detailCol = table.getColumns().get(1);
232+
TableColumn<TitleDetailDelay, ?> infoCol = detailCol.getColumns().get(1);
233+
table.getSelectionModel().clearAndSelect(row);
234+
table.edit(row, infoCol);
235+
});
189236
});
190237
tmpOptionCol.setEditable(true);
191238
col.getColumns().add(tmpOptionCol);
192239

193240
// Use a textfield to set info for detail
194241
TableColumn<TitleDetailDelay, String> infoCol = new TableColumn<>("Info");
195242
infoCol.setCellValueFactory(cell -> new SimpleStringProperty(getInfoFromDetail(cell.getValue())));
196-
infoCol.setCellFactory(column -> new TextFieldTableCell<>(new DefaultStringConverter()));
243+
infoCol.setCellFactory(ValidatingTextFieldTableCell.forTableColumn(new DefaultStringConverter()));
197244
infoCol.setOnEditCommit(event -> {
198245
final int row = event.getTablePosition().getRow();
199246
TitleDetailDelay tmpT = items.get(row);
@@ -202,10 +249,10 @@ private void createTable()
202249
items.set(row, newTitleDetailDelay);
203250
// Trigger editing the delay.
204251
if (newTitleDetailDelay.hasDelay())
205-
UpdateThrottle.TIMER.schedule(() -> Platform.runLater(() -> {
206-
table.getSelectionModel().clearAndSelect(row);
207-
table.edit(row, table.getColumns().get(2));
208-
}), 200, TimeUnit.MILLISECONDS);
252+
Platform.runLater(() -> {
253+
table.getSelectionModel().clearAndSelect(row);
254+
table.edit(row, table.getColumns().get(2));
255+
});
209256
});
210257
infoCol.setSortable(false);
211258
col.getColumns().add(infoCol);
@@ -306,14 +353,12 @@ private void createButtons()
306353
items.add(new TitleDetailDelay("", "", 0));
307354

308355
// Trigger editing the title of new item
309-
UpdateThrottle.TIMER.schedule(() ->
310-
Platform.runLater(() ->
311-
{
312-
final int row = items.size()-1;
313-
table.getSelectionModel().clearAndSelect(row);
314-
table.edit(row, table.getColumns().get(0));
315-
}),
316-
200, TimeUnit.MILLISECONDS);
356+
Platform.runLater(() ->
357+
{
358+
final int row = items.size()-1;
359+
table.getSelectionModel().clearAndSelect(row);
360+
table.edit(row, table.getColumns().get(0));
361+
});
317362
});
318363

319364
edit.setTooltip(new Tooltip("Edit the detail field of table item."));

app/alarm/ui/src/main/java/org/phoebus/applications/alarm/ui/tree/TitleDetailTable.java

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
import javafx.scene.control.TableColumn;
3131
import javafx.scene.control.TableView;
3232
import javafx.scene.control.Tooltip;
33-
import javafx.scene.control.cell.TextFieldTableCell;
3433
import javafx.scene.layout.BorderPane;
3534
import javafx.scene.layout.VBox;
3635
import javafx.util.converter.DefaultStringConverter;
@@ -83,27 +82,25 @@ private void createTable()
8382

8483
TableColumn<TitleDetail, String> col = new TableColumn<>("Title");
8584
col.setCellValueFactory(cell -> new SimpleStringProperty(cell.getValue().title));
86-
col.setCellFactory(column -> new TextFieldTableCell<>(new DefaultStringConverter()));
85+
col.setCellFactory(ValidatingTextFieldTableCell.forTableColumn(new DefaultStringConverter()));
8786
col.setOnEditCommit(event ->
8887
{
8988
final int row = event.getTablePosition().getRow();
9089
items.set(row, new TitleDetail(event.getNewValue(), items.get(row).detail));
9190

9291
// Trigger editing the detail
93-
UpdateThrottle.TIMER.schedule(() ->
94-
Platform.runLater(() ->
95-
{
96-
table.getSelectionModel().clearAndSelect(row);
97-
table.edit(row, table.getColumns().get(1));
98-
}),
99-
200, TimeUnit.MILLISECONDS);
92+
Platform.runLater(() ->
93+
{
94+
table.getSelectionModel().clearAndSelect(row);
95+
table.edit(row, table.getColumns().get(1));
96+
});
10097
});
10198
col.setSortable(false);
10299
table.getColumns().add(col);
103100

104101
col = new TableColumn<>("Detail");
105102
col.setCellValueFactory(cell -> new SimpleStringProperty(cell.getValue().detail.replace("\n", "\\n")));
106-
col.setCellFactory(column -> new TextFieldTableCell<>(new DefaultStringConverter()));
103+
col.setCellFactory(ValidatingTextFieldTableCell.forTableColumn(new DefaultStringConverter()));
107104
col.setOnEditCommit(event ->
108105
{
109106
final int row = event.getTablePosition().getRow();
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package org.phoebus.applications.alarm.ui.tree;
2+
3+
import javafx.scene.control.TableCell;
4+
import javafx.scene.control.TableColumn;
5+
import javafx.scene.control.TextField;
6+
import javafx.scene.control.cell.TextFieldTableCell;
7+
import javafx.util.Callback;
8+
import javafx.util.StringConverter;
9+
10+
public class ValidatingTextFieldTableCell<S, T> extends TextFieldTableCell<S, T> {
11+
12+
private TextField textField;
13+
14+
public ValidatingTextFieldTableCell(StringConverter<T> converter) {
15+
super(converter);
16+
}
17+
18+
@Override
19+
public void startEdit() {
20+
super.startEdit();
21+
if (isEditing() && getGraphic() instanceof TextField tf) {
22+
textField = tf;
23+
// add listener
24+
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
25+
if (!isNowFocused && textField != null) {
26+
T newValue = getConverter().fromString(textField.getText());
27+
if (newValue.equals(getItem())) {
28+
// nothing changed so cancel
29+
cancelEdit();
30+
} else {
31+
// changed so validate
32+
commitEdit(newValue);
33+
}
34+
}
35+
});
36+
}
37+
}
38+
39+
// utility method to simplify usage in column
40+
public static <S> Callback<TableColumn<S, String>, TableCell<S, String>> forTableColumn() {
41+
return forTableColumn(new javafx.util.converter.DefaultStringConverter());
42+
}
43+
44+
public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(StringConverter<T> converter) {
45+
return column -> new ValidatingTextFieldTableCell<>(converter);
46+
}
47+
}

0 commit comments

Comments
 (0)