From fc30d08a614652f34d8234cb648c8825d87b3f84 Mon Sep 17 00:00:00 2001 From: georgweiss Date: Mon, 18 Aug 2025 15:19:06 +0200 Subject: [PATCH] Update table cell rendering in snapshot view --- .../ui/snapshot/SnapshotController.java | 68 ++++++++---- .../ui/snapshot/TableCellColors.java | 102 +++++++++++++++--- .../ui/snapshot/TableEntry.java | 32 +++--- .../ui/snapshot/VDeltaCellEditor.java | 17 +-- .../ui/snapshot/SnapshotView.fxml | 4 +- 5 files changed, 160 insertions(+), 63 deletions(-) diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java index e7bbef4720..5d83be0928 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotController.java @@ -51,6 +51,7 @@ import javafx.scene.text.Text; import javafx.util.converter.DoubleStringConverter; import org.epics.vtype.Alarm; +import org.epics.vtype.AlarmSeverity; import org.epics.vtype.Display; import org.epics.vtype.Time; import org.epics.vtype.VEnum; @@ -271,6 +272,12 @@ public class SnapshotController extends SaveAndRestoreBaseController implements @FXML protected TableColumn baseSnapshotColumn; + @FXML + private TableColumn storedSeverityColumn; + + @FXML + private TableColumn liveSeverityColumn; + @FXML protected TooltipTableColumn baseSnapshotValueColumn; @@ -357,7 +364,6 @@ public SnapshotController(SnapshotTab snapshotTab) { } - @FXML public void initialize() { @@ -396,7 +402,7 @@ public void initialize() { saveSnapshotButton.disableProperty().bind(Bindings.createBooleanBinding(() -> // TODO: support save (=update) a composite snapshot from the snapshot view. In the meanwhile, disable save button. - snapshotDataDirty.not().get() || + snapshotDataDirty.not().get() || snapshotNameProperty.isEmpty().get() || snapshotCommentProperty.isEmpty().get() || userIdentity.isNull().get(), @@ -468,7 +474,7 @@ public void initialize() { showLiveReadbackButton.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("/icons/show_live_readback_column.png")))); showLiveReadbackButton.selectedProperty() - .addListener((a, o, n) ->{ + .addListener((a, o, n) -> { this.showReadbacks.set(n); actionResultReadbackColumn.visibleProperty().setValue(actionResultReadbackColumn.getGraphic() != null); }); @@ -668,6 +674,9 @@ protected void updateItem(TableEntry item, boolean empty) { storedReadbackColumn.setCellFactory(e -> new VTypeCellEditor<>()); readbackColumn.visibleProperty().bind(showReadbacks); + liveSeverityColumn.setCellFactory(a -> new AlarmSeverityCell()); + storedSeverityColumn.setCellFactory(a -> new AlarmSeverityCell()); + timeColumn.visibleProperty().bind(compareViewEnabled.not()); firstDividerColumn.visibleProperty().bind(compareViewEnabled); statusColumn.visibleProperty().bind(compareViewEnabled.not()); @@ -763,7 +772,7 @@ public void takeSnapshot() { * Restores snapshot meta-data properties to indicate that the UI * is not showing persisted {@link Snapshot} data. */ - private void resetMetaData(){ + private void resetMetaData() { tabTitleProperty.setValue(Messages.unnamedSnapshot); snapshotNameProperty.setValue(null); snapshotCommentProperty.setValue(null); @@ -1141,7 +1150,7 @@ private void showTakeSnapshotResult(List snapshotItems) { break; } } - for(SnapshotItem snapshotItem : snapshotItems){ + for (SnapshotItem snapshotItem : snapshotItems) { if (snapshotItem.getConfigPv().getReadbackPvName() != null && snapshotItem.getReadbackValue() != null && snapshotItem.getReadbackValue().equals(VDisconnectedData.INSTANCE)) { disconnectedReadbackPvEncountered.set(true); @@ -1155,7 +1164,7 @@ private void showTakeSnapshotResult(List snapshotItems) { if (!disconnectedPvEncountered.get()) { actionResultColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/ok.png"))); } - if(!disconnectedReadbackPvEncountered.get()){ + if (!disconnectedReadbackPvEncountered.get()) { actionResultReadbackColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/ok.png"))); } }); @@ -1329,8 +1338,7 @@ private void showRestoreResult(List restoreResultList) { Platform.runLater(() -> { if (!disconnectedPvEncountered.get()) { actionResultColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/ok.png"))); - } - else{ + } else { actionResultColumn.setGraphic(new ImageView(ImageCache.getImage(SnapshotController.class, "/icons/error.png"))); } }); @@ -1508,15 +1516,13 @@ private void showSnapshotInTable() { tableEntry.setStoredReadbackValue(entry.getReadbackValue(), 0); if (entry.getValue() == null || entry.getValue().equals(VDisconnectedData.INSTANCE)) { tableEntry.setActionResult(ActionResult.FAILED); - } - else { + } else { tableEntry.setActionResult(ActionResult.OK); } - if (entry.getConfigPv().getReadbackPvName() != null){ - if(entry.getReadbackValue() == null || entry.getReadbackValue().equals(VDisconnectedData.INSTANCE)) { + if (entry.getConfigPv().getReadbackPvName() != null) { + if (entry.getReadbackValue() == null || entry.getReadbackValue().equals(VDisconnectedData.INSTANCE)) { tableEntry.setActionResultReadback(ActionResult.FAILED); - } - else{ + } else { tableEntry.setActionResultReadback(ActionResult.OK); } } @@ -1590,22 +1596,20 @@ private void connectPVs() { } /** - * * @param configurationData {@link ConfigurationData} obejct of a {@link org.phoebus.applications.saveandrestore.model.Configuration} * @return true if any if the {@link ConfigPv} items in {@link ConfigurationData#getPvList()} defines a non-null read-back * PV name, otherwise false. */ - private boolean configurationHasReadbackPvs(ConfigurationData configurationData){ + private boolean configurationHasReadbackPvs(ConfigurationData configurationData) { return configurationData.getPvList().stream().anyMatch(cp -> cp.getReadbackPvName() != null); } /** - * * @param snapshotData {@link SnapshotData} obejct of a {@link org.phoebus.applications.saveandrestore.model.Snapshot} * @return true if any if the {@link ConfigPv} items in {@link SnapshotData#getSnapshotItems()} defines a non-null read-back * PV name, otherwise false. */ - private boolean configurationHasReadbackPvs(SnapshotData snapshotData){ + private boolean configurationHasReadbackPvs(SnapshotData snapshotData) { return snapshotData.getSnapshotItems().stream().anyMatch(si -> si.getConfigPv().getReadbackPvName() != null); } @@ -1660,4 +1664,32 @@ public void updateItem(org.phoebus.applications.saveandrestore.ui.snapshot.Actio } } } + + /** + * {@link TableCell} customized for the alarm severity column such that alarm information is + * decorated in the same manner as in other applications. + */ + private static class AlarmSeverityCell extends TableCell { + + @Override + public void updateItem(AlarmSeverity alarmSeverity, boolean empty) { + if (empty) { + setText(null); + setStyle(TableCellColors.REGULAR_CELL_STYLE); + } else if (alarmSeverity == null) { + setText("---"); + setStyle(TableCellColors.REGULAR_CELL_STYLE); + } else { + setText(alarmSeverity.toString()); + switch (alarmSeverity) { + case NONE -> setStyle(TableCellColors.ALARM_NONE_STYLE); + case UNDEFINED -> setStyle(TableCellColors.ALARM_UNDEFINED_STYLE); + case MINOR -> setStyle(TableCellColors.ALARM_MINOR_STYLE); + case MAJOR -> setStyle(TableCellColors.ALARM_MAJOR_STYLE); + case INVALID -> setStyle(TableCellColors.ALARM_INVALID_STYLE); + } + } + + } + } } \ No newline at end of file diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableCellColors.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableCellColors.java index 19752ad45b..2b2329e290 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableCellColors.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableCellColors.java @@ -5,7 +5,6 @@ package org.phoebus.applications.saveandrestore.ui.snapshot; import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; import org.phoebus.ui.Preferences; import org.phoebus.ui.javafx.Brightness; import org.phoebus.ui.javafx.JFXUtil; @@ -16,34 +15,111 @@ */ public class TableCellColors { - public static final Color DISCONNECTED_COLOR = Color.rgb(Preferences.undefined_severity_background_color[0], + private static final Color DISCONNECTED_COLOR = Color.rgb(Preferences.undefined_severity_background_color[0], Preferences.undefined_severity_background_color[1], Preferences.undefined_severity_background_color[2]); + private static final Color ALARM_NONE_COLOR = Color.rgb(Preferences.alarm_area_panel_ok_severity_background_color[0], + Preferences.alarm_area_panel_ok_severity_background_color[1], + Preferences.alarm_area_panel_ok_severity_background_color[2]); + private static final Color ALARM_MINOR_COLOR = Color.rgb(Preferences.alarm_area_panel_minor_severity_background_color[0], + Preferences.alarm_area_panel_minor_severity_background_color[1], + Preferences.alarm_area_panel_minor_severity_background_color[2]); + private static final Color ALARM_MAJOR_COLOR = Color.rgb(Preferences.alarm_area_panel_major_severity_background_color[0], + Preferences.alarm_area_panel_major_severity_background_color[1], + Preferences.alarm_area_panel_major_severity_background_color[2]); + private static final Color ALARM_UNDEFINED_COLOR = Color.rgb(Preferences.alarm_area_panel_undefined_severity_background_color[0], + Preferences.alarm_area_panel_major_severity_background_color[1], + Preferences.alarm_area_panel_major_severity_background_color[2]); + private static final Color ALARM_INVALID_COLOR = Color.rgb(Preferences.alarm_area_panel_invalid_severity_background_color[0], + Preferences.alarm_area_panel_invalid_severity_background_color[1], + Preferences.alarm_area_panel_invalid_severity_background_color[2]); - public static final Paint DISCONNECTED_PAINT; private static final Color DISCONNECTED_TEXT_COLOR; - private static final Color DISCONNECTED_BORDER_COLOR; + private static final Color ALARM_NONE_TEXT_COLOR; + private static final Color ALARM_MINOR_TEXT_COLOR; + private static final Color ALARM_MAJOR_TEXT_COLOR; + private static final Color ALARM_UNDEFINED_TEXT_COLOR; + private static final Color ALARM_INVALID_TEXT_COLOR; public static final String REGULAR_CELL_STYLE = "-fx-text-fill: black; -fx-background-color: transparent"; public static final String DISCONNECTED_STYLE; - public static final String DISCONNECTED_STYLE_SMALL; + public static final String ALARM_NONE_STYLE; + public static final String ALARM_MINOR_STYLE; + public static final String ALARM_MAJOR_STYLE; + public static final String ALARM_UNDEFINED_STYLE; + public static final String ALARM_INVALID_STYLE; static { - DISCONNECTED_PAINT = Paint.valueOf(JFXUtil.webRGB(DISCONNECTED_COLOR)); if (Brightness.of(DISCONNECTED_COLOR) < Brightness.BRIGHT_THRESHOLD) { DISCONNECTED_TEXT_COLOR = Color.WHITE; - DISCONNECTED_BORDER_COLOR = Color.WHITE; - } else { + } + else { DISCONNECTED_TEXT_COLOR = Color.BLACK; - DISCONNECTED_BORDER_COLOR = Color.GRAY; } + if(Brightness.of(ALARM_NONE_COLOR) < Brightness.BRIGHT_THRESHOLD){ + ALARM_NONE_TEXT_COLOR = Color.WHITE; + } + else{ + ALARM_NONE_TEXT_COLOR = Color.BLACK; + } + + if(Brightness.of(ALARM_MINOR_COLOR) < Brightness.BRIGHT_THRESHOLD){ + ALARM_MINOR_TEXT_COLOR = Color.WHITE; + } + else{ + ALARM_MINOR_TEXT_COLOR = Color.BLACK; + } + + if(Brightness.of(ALARM_MAJOR_COLOR) < Brightness.BRIGHT_THRESHOLD){ + ALARM_MAJOR_TEXT_COLOR = Color.WHITE; + } + else{ + ALARM_MAJOR_TEXT_COLOR = Color.BLACK; + } - DISCONNECTED_STYLE = "-fx-border-color: transparent; -fx-border-width: 2 0 2 0; -fx-background-insets: 2 0 2 0; -fx-text-fill: " + + if(Brightness.of(ALARM_UNDEFINED_COLOR) < Brightness.BRIGHT_THRESHOLD){ + ALARM_UNDEFINED_TEXT_COLOR = Color.WHITE; + } + else{ + ALARM_UNDEFINED_TEXT_COLOR = Color.BLACK; + } + + if(Brightness.of(ALARM_INVALID_COLOR) < Brightness.BRIGHT_THRESHOLD){ + ALARM_INVALID_TEXT_COLOR = Color.WHITE; + } + else{ + ALARM_INVALID_TEXT_COLOR = Color.BLACK; + } + + + DISCONNECTED_STYLE = "-fx-background-insets: 2 2 2 2; -fx-text-fill: " + JFXUtil.webRGB(DISCONNECTED_TEXT_COLOR) + "; -fx-background-color: " + JFXUtil.webRGB(DISCONNECTED_COLOR); - DISCONNECTED_STYLE_SMALL = "-fx-border-color: " + JFXUtil.webRGB(DISCONNECTED_BORDER_COLOR) +"; -fx-border-width: 2 2 2 2;" + - " -fx-background-color: " + - JFXUtil.webRGB(DISCONNECTED_COLOR); + + ALARM_NONE_STYLE = "-fx-background-insets: 2 2 2 2; -fx-text-fill: " + + JFXUtil.webRGB(ALARM_NONE_TEXT_COLOR) + + "; -fx-background-color: " + + JFXUtil.webRGB(ALARM_NONE_COLOR); + + ALARM_MINOR_STYLE = "-fx-background-insets: 2 2 2 2; -fx-text-fill: " + + JFXUtil.webRGB(ALARM_MINOR_TEXT_COLOR) + + "; -fx-background-color: " + + JFXUtil.webRGB(ALARM_MINOR_COLOR); + + ALARM_MAJOR_STYLE = "-fx-background-insets: 2 2 2 2; -fx-text-fill: " + + JFXUtil.webRGB(ALARM_MAJOR_TEXT_COLOR) + + "; -fx-background-color: " + + JFXUtil.webRGB(ALARM_MAJOR_COLOR); + + ALARM_UNDEFINED_STYLE = "-fx-background-insets: 2 2 2 2; -fx-text-fill: " + + JFXUtil.webRGB(ALARM_UNDEFINED_TEXT_COLOR) + + "; -fx-background-color: " + + JFXUtil.webRGB(ALARM_UNDEFINED_COLOR); + + ALARM_INVALID_STYLE = "-fx-background-insets: 2 2 2 2; -fx-text-fill: " + + JFXUtil.webRGB(ALARM_INVALID_TEXT_COLOR) + + "; -fx-background-color: " + + JFXUtil.webRGB(ALARM_INVALID_COLOR); } } diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableEntry.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableEntry.java index 8687d40c90..0f5f88b420 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableEntry.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/TableEntry.java @@ -61,8 +61,8 @@ public class TableEntry { private final ObjectProperty timestamp = new SimpleObjectProperty<>(this, "timestamp"); private final StringProperty liveStatus = new SimpleStringProperty(this, "liveStatus", "---"); private final StringProperty storedStatus = new SimpleStringProperty(this, "storedStatus", PVAAlarm.AlarmStatus.UNDEFINED.name()); - private final StringProperty liveSeverity = new SimpleStringProperty(this, "liveSeverity", "---"); - private final StringProperty storedSeverity = new SimpleStringProperty(this, "storedSeverity", AlarmSeverity.UNDEFINED.toString()); + private final ObjectProperty liveSeverity = new SimpleObjectProperty<>(this, "liveSeverity", null); + private final ObjectProperty storedSeverity = new SimpleObjectProperty<>(this, "storedSeverity", null); /** * Snapshot value set either when user takes snapshot, or when snapshot data is loaded from remote service. Note that this * can be modified if user chooses to use a multiplier before triggering a restore operation, or if the value is @@ -185,7 +185,7 @@ public StringProperty liveStatusProperty() { * @return the property providing the alarm severity of the PV value */ @SuppressWarnings("unused") - public StringProperty liveSeverityProperty() { + public ObjectProperty liveSeverityProperty() { return liveSeverity; } @@ -254,7 +254,7 @@ public ObjectProperty storedSnapshotValue() { } @SuppressWarnings("unused") - public StringProperty storedSeverityProperty() { + public ObjectProperty storedSeverityProperty() { return storedSeverity; } @@ -294,27 +294,27 @@ public void setSnapshotValue(VType snapshotValue, int index) { if (index == 0) { if (val instanceof VNumber) { storedStatus.set(((VNumber) val).getAlarm().getStatus().name()); - storedSeverity.set(((VNumber) val).getAlarm().getSeverity().toString()); + storedSeverity.set(((VNumber) val).getAlarm().getSeverity()); timestamp.set(((VNumber) val).getTime().getTimestamp()); } else if (val instanceof VNumberArray) { storedStatus.set(((VNumberArray) val).getAlarm().getStatus().name()); - storedSeverity.set(((VNumberArray) val).getAlarm().getSeverity().toString()); + storedSeverity.set(((VNumberArray) val).getAlarm().getSeverity()); timestamp.set(((VNumberArray) val).getTime().getTimestamp()); } else if (val instanceof VEnum) { storedStatus.set(((VEnum) val).getAlarm().getStatus().name()); - storedSeverity.set(((VEnum) val).getAlarm().getSeverity().toString()); + storedSeverity.set(((VEnum) val).getAlarm().getSeverity()); timestamp.set(((VEnum) val).getTime().getTimestamp()); } else if (val instanceof VEnumArray) { storedStatus.set(((VEnumArray) val).getAlarm().getStatus().name()); - storedSeverity.set(((VEnumArray) val).getAlarm().getSeverity().toString()); + storedSeverity.set(((VEnumArray) val).getAlarm().getSeverity()); timestamp.set(((VEnumArray) val).getTime().getTimestamp()); } else if (val instanceof VNoData) { storedStatus.set("---"); - storedSeverity.set("---"); + storedSeverity.set(null); timestamp.set(null); } else { storedStatus.set(AlarmSeverity.NONE.toString()); - storedSeverity.set("---"); + storedSeverity.set(null); timestamp.set(null); } snapshotVal.set(val); @@ -388,26 +388,26 @@ public void setLiveValue(VType val) { liveStoredEqual.set(Utilities.areValuesEqual(val, stored, threshold)); if (val instanceof VNumber) { liveStatus.set(((VNumber) val).getAlarm().getStatus().name()); - liveSeverity.set(((VNumber) val).getAlarm().getSeverity().toString()); + liveSeverity.set(((VNumber) val).getAlarm().getSeverity()); timestamp.set(((VNumber) val).getTime().getTimestamp()); } else if (val instanceof VNumberArray) { liveStatus.set(((VNumberArray) val).getAlarm().getStatus().name()); - liveSeverity.set(((VNumberArray) val).getAlarm().getSeverity().toString()); + liveSeverity.set(((VNumberArray) val).getAlarm().getSeverity()); timestamp.set(((VNumberArray) val).getTime().getTimestamp()); } else if (val instanceof VEnum) { liveStatus.set(((VEnum) val).getAlarm().getStatus().name()); - liveSeverity.set(((VEnum) val).getAlarm().getSeverity().toString()); + liveSeverity.set(((VEnum) val).getAlarm().getSeverity()); timestamp.set(((VEnum) val).getTime().getTimestamp()); } else if (val instanceof VEnumArray) { liveStatus.set(((VEnumArray) val).getAlarm().getStatus().name()); - liveSeverity.set(((VEnumArray) val).getAlarm().getSeverity().toString()); + liveSeverity.set(((VEnumArray) val).getAlarm().getSeverity()); timestamp.set(((VEnumArray) val).getTime().getTimestamp()); } else if (val instanceof VDisconnectedData) { - liveSeverity.set("---"); + liveSeverity.set(AlarmSeverity.UNDEFINED); liveStatus.set("---"); timestamp.set(null); } else { - liveSeverity.set(AlarmSeverity.NONE.toString()); + liveSeverity.set(null); liveStatus.set("---"); timestamp.set(null); } diff --git a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/VDeltaCellEditor.java b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/VDeltaCellEditor.java index f6d8750cd0..a3a8d0ead3 100644 --- a/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/VDeltaCellEditor.java +++ b/app/save-and-restore/app/src/main/java/org/phoebus/applications/saveandrestore/ui/snapshot/VDeltaCellEditor.java @@ -19,19 +19,11 @@ package org.phoebus.applications.saveandrestore.ui.snapshot; -import javafx.geometry.Insets; import javafx.scene.control.Tooltip; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; -import javafx.scene.paint.Color; -import org.phoebus.saveandrestore.util.Utilities; -import org.phoebus.saveandrestore.util.VNoData; import org.phoebus.applications.saveandrestore.ui.VTypePair; import org.phoebus.core.vtypes.VDisconnectedData; -import org.phoebus.ui.Preferences; +import org.phoebus.saveandrestore.util.Utilities; +import org.phoebus.saveandrestore.util.VNoData; import java.util.Formatter; @@ -44,8 +36,6 @@ */ public class VDeltaCellEditor extends VTypeCellEditor { - private static final Image WARNING_IMAGE = new Image( - SnapshotController.class.getResourceAsStream("/icons/hprio_tsk.png")); private final Tooltip tooltip = new Tooltip(); private boolean showDeltaPercentage = false; @@ -92,10 +82,9 @@ public void updateItem(T item, boolean empty) { setText(vtc.getString()); } if (!vtc.isWithinThreshold()) { - setGraphic(new ImageView(WARNING_IMAGE)); + setStyle(TableCellColors.ALARM_MAJOR_STYLE); } } - tooltip.setText(item.toString()); setTooltip(tooltip); } diff --git a/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotView.fxml b/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotView.fxml index 6e8ad7a882..0202f5bd1b 100644 --- a/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotView.fxml +++ b/app/save-and-restore/app/src/main/resources/org/phoebus/applications/saveandrestore/ui/snapshot/SnapshotView.fxml @@ -232,12 +232,12 @@ save-and-restore UI, this fxml does not make use of TabPane and Tab elements. - + - +