From 8a81bbdcaff572d070efd284e0d33a816397874e Mon Sep 17 00:00:00 2001 From: Rebecca Williams Date: Tue, 27 May 2025 16:53:06 +0100 Subject: [PATCH 1/4] Add a generic method for setting the 'disabled' widget look ... to apply the not_enabled css style to the widget and show the no_write cursor. --- .../javafx/widgets/JFXBaseRepresentation.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/JFXBaseRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/JFXBaseRepresentation.java index 8892ea4b67..c6ecdb754b 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/JFXBaseRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/JFXBaseRepresentation.java @@ -26,14 +26,17 @@ import org.csstudio.display.builder.model.widgets.TabsWidget; import org.csstudio.display.builder.model.widgets.TabsWidget.TabItemProperty; import org.csstudio.display.builder.representation.WidgetRepresentation; +import org.csstudio.display.builder.representation.javafx.Cursors; import org.csstudio.display.builder.representation.javafx.JFXRepresentation; import javafx.collections.ObservableList; import javafx.event.EventHandler; +import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.Parent; import org.phoebus.core.types.ProcessVariable; import org.phoebus.ui.dnd.DataFormats; +import org.phoebus.ui.javafx.Styles; /** Base class for all JavaFX widget representations * @param JFX Widget @@ -356,4 +359,22 @@ public void updateOrder() addToParent(parent); } } + + /** + * We do not want to disable widgets if they cannot be written to as this + * removes the context menu. Instead we replicate the disabled look from Java FX + * and set the cursor to the 'NO_WRITE' cursor to indicate this. + * + * @param enabled boolean as to whether widget interaction is allowed + * @param children list of children nodes under the parent widget + */ + public void setDisabledLook(Boolean enabled, ObservableList children) { + jfx_node.setCursor(enabled ? Cursor.DEFAULT : Cursors.NO_WRITE); + if (children != null) { + for (Node node : children) + { + Styles.update(node, Styles.NOT_ENABLED, !enabled); + } + } + } } From 712b5ca3ed1c96d9a64fd23e7b72105e0a1e44d3 Mon Sep 17 00:00:00 2001 From: Rebecca Williams Date: Tue, 27 May 2025 16:54:43 +0100 Subject: [PATCH 2/4] Update the not_enabled css style to remove mouse hover/click color changes --- core/ui/src/main/resources/org/phoebus/ui/javafx/csstudio.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/ui/src/main/resources/org/phoebus/ui/javafx/csstudio.css b/core/ui/src/main/resources/org/phoebus/ui/javafx/csstudio.css index 442931148e..a03ecc4b3c 100644 --- a/core/ui/src/main/resources/org/phoebus/ui/javafx/csstudio.css +++ b/core/ui/src/main/resources/org/phoebus/ui/javafx/csstudio.css @@ -17,6 +17,9 @@ .not_enabled { -fx-opacity: 0.4; + -fx-background-insets: 0; + -fx-color: -fx-base; + -fx-focus-color: -fx-base; } /** Tracker */ From f0c8588f76319848f293294e33ff50e15bcb4b42 Mon Sep 17 00:00:00 2001 From: Rebecca Williams Date: Tue, 27 May 2025 16:56:37 +0100 Subject: [PATCH 3/4] Update control widget representations to use generic 'disable style' method ... in parent for consistent and uniform behaviour across widgets. Some widgets were being disabled which removes the context menu so these have been switched ot just apply the style. In addition some widgets were still trying to write to 'no_write' PVs causing exceptions in the log. These have been fixed by changing whether the widget is enabled first. --- .../widgets/BoolButtonRepresentation.java | 12 ++- .../widgets/CheckBoxRepresentation.java | 2 +- .../widgets/ChoiceButtonRepresentation.java | 11 ++- .../javafx/widgets/ComboRepresentation.java | 3 +- .../javafx/widgets/RadioRepresentation.java | 2 +- .../widgets/ScaledSliderRepresentation.java | 5 +- .../widgets/ScrollBarRepresentation.java | 3 +- .../widgets/SlideButtonRepresentation.java | 2 +- .../javafx/widgets/SpinnerRepresentation.java | 98 ++++++++++--------- .../widgets/TextEntryRepresentation.java | 67 +++++++------ .../ThumbwheelWidgetRepresentation.java | 5 +- 11 files changed, 114 insertions(+), 96 deletions(-) diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/BoolButtonRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/BoolButtonRepresentation.java index 865b94b295..f452184e2c 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/BoolButtonRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/BoolButtonRepresentation.java @@ -83,6 +83,7 @@ public class BoolButtonRepresentation extends RegionBaseRepresentation confirm(pressed)); + if (enabled) { + logger.log(Level.FINE, "{0} pressed", model_widget); + Platform.runLater(() -> confirm(pressed)); + } } /** Check for confirmation, then perform the button action @@ -403,10 +406,9 @@ public void updateChanges() } if (dirty_enablement.checkAndClear()) { - final boolean enabled = model_widget.propEnabled().getValue() && + enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); - button.setDisable(! enabled); - Styles.update(button, Styles.NOT_ENABLED, !enabled); + setDisabledLook(enabled, jfx_node.getChildren()); } if (update_value) { diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/CheckBoxRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/CheckBoxRepresentation.java index c9d471c8f6..db73a336b6 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/CheckBoxRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/CheckBoxRepresentation.java @@ -229,7 +229,7 @@ public void updateChanges() // Just apply a style that matches the disabled look. enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); + setDisabledLook(enabled, jfx_node.getChildrenUnmodifiable()); if (model_widget.propAutoSize().getValue()) sizeChanged(null, null, null); } diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ChoiceButtonRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ChoiceButtonRepresentation.java index 08ca32577a..2e336eeb0d 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ChoiceButtonRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ChoiceButtonRepresentation.java @@ -181,6 +181,14 @@ private void selectionChanged(final ObservableValue obs, final { active = false; } + } + else if (!enabled && newval == null) + { + // If the choice button is not enabled (no write allowed) + // we still have to ensure the 'oldval' stays selected + // as otherwise clicking on the same value will set an + // unselected look on the ChoiceButton. + toggle.selectToggle(oldval); } } @@ -332,8 +340,7 @@ public void updateChanges() // Just apply a style that matches the disabled look. enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); - jfx_node.setCursor(enabled ? Cursor.DEFAULT : Cursors.NO_WRITE); + setDisabledLook(enabled, jfx_node.getChildren()); for (Node node : jfx_node.getChildren()) { final ButtonBase b = (ButtonBase) node; diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ComboRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ComboRepresentation.java index 4897df72cd..b462490dc6 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ComboRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ComboRepresentation.java @@ -296,8 +296,7 @@ public void updateChanges() // and the cursor will be ignored // jfx_node.setDisable(! enabled); // So keep enabled, but indicate that trying to operate the widget is futile - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); - jfx_node.setCursor(enabled ? Cursor.DEFAULT : Cursors.NO_WRITE); + setDisabledLook(enabled, jfx_node.getChildrenUnmodifiable()); if (model_widget.propEditable().getValue()) { jfx_node.getEditor().setEditable(enabled ? model_widget.propEditable().getValue() : false); diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/RadioRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/RadioRepresentation.java index b9b1de5d74..c92fb5b4c8 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/RadioRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/RadioRepresentation.java @@ -299,7 +299,7 @@ public void updateChanges() // Just apply a style that matches the disabled look. enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); + setDisabledLook(enabled, jfx_node.getChildren()); for (Node rb_node : jfx_node.getChildren()) { final RadioButton rb = (RadioButton) rb_node; diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScaledSliderRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScaledSliderRepresentation.java index 6d1abeb666..7217bdc386 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScaledSliderRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScaledSliderRepresentation.java @@ -407,8 +407,11 @@ private void valueChanged(final WidgetProperty property, final public void updateChanges() { super.updateChanges(); - if (dirty_enablement.checkAndClear()) + if (dirty_enablement.checkAndClear()) { slider.setDisable(!enabled); + setDisabledLook(enabled, jfx_node.getChildrenUnmodifiable()); + } + if (dirty_layout.checkAndClear()) { final boolean horizontal = model_widget.propHorizontal().getValue(); diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScrollBarRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScrollBarRepresentation.java index 2a02f560ea..847c2835fd 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScrollBarRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ScrollBarRepresentation.java @@ -335,8 +335,7 @@ public void updateChanges() // Don't disable the widget, because that would also remove the // context menu etc. // Just apply a style that matches the disabled look. - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); - jfx_node.setCursor(enabled ? Cursor.DEFAULT : Cursors.NO_WRITE); + setDisabledLook(enabled, jfx_node.getChildrenUnmodifiable()); } if (dirty_size.checkAndClear()) { diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SlideButtonRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SlideButtonRepresentation.java index 2d157a8f80..03d92d4bc7 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SlideButtonRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SlideButtonRepresentation.java @@ -99,7 +99,7 @@ public void updateChanges ( ) { // Just apply a style that matches the disabled look. enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); + setDisabledLook(enabled, jfx_node.getChildren()); // Since jfx_node.isManaged() == false, need to trigger layout jfx_node.layout(); diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SpinnerRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SpinnerRepresentation.java index 2b3b37ac49..74ec7a5c8a 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SpinnerRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/SpinnerRepresentation.java @@ -49,6 +49,7 @@ public class SpinnerRepresentation extends RegionBaseRepresentation value_max) - value = value_max; + if (enabled) { + //The value factory retains the old values, and will be updated as scheduled below. + final String text = jfx_node.getEditor().getText(); + Object value = + FormatOptionHandler.parse(model_widget.runtimePropValue().getValue(), text, model_widget.propFormat().getValue()); + if (value instanceof Number) + { + if (((Number)value).doubleValue() < value_min) + value = value_min; + else if (((Number)value).doubleValue() > value_max) + value = value_max; + } + logger.log(Level.FINE, "Writing '" + text + "' as " + value + " (" + value.getClass().getName() + ")"); + toolkit.fireWrite(model_widget, value); + + // Wrote value. Expected is either + // a) PV receives that value, PV updates to + // submitted value or maybe a 'clamped' value + // --> We'll receive contentChanged() and update the value factory. + // b) PV doesn't receive the value and never sends + // an update. The value factory retains the old value, + // --> Schedule an update to the new value. + // + // This could result in a little flicker: + // User enters "new_value". + // We send that, but retain "old_value" to handle case b) + // PV finally sends "new_value", and we show that. + // + // In practice, this rarely happens because we only schedule an update. + // By the time it executes, we already have case a. + // If it does turn into a problem, could introduce toolkit.scheduleDelayedUpdate() + // so that case b) only restores the old 'value_text' after some delay, + // increasing the chance of a) to happen. + dirty_content.mark(); + toolkit.scheduleUpdate(this); } - logger.log(Level.FINE, "Writing '" + text + "' as " + value + " (" + value.getClass().getName() + ")"); - toolkit.fireWrite(model_widget, value); - - // Wrote value. Expected is either - // a) PV receives that value, PV updates to - // submitted value or maybe a 'clamped' value - // --> We'll receive contentChanged() and update the value factory. - // b) PV doesn't receive the value and never sends - // an update. The value factory retains the old value, - // --> Schedule an update to the new value. - // - // This could result in a little flicker: - // User enters "new_value". - // We send that, but retain "old_value" to handle case b) - // PV finally sends "new_value", and we show that. - // - // In practice, this rarely happens because we only schedule an update. - // By the time it executes, we already have case a. - // If it does turn into a problem, could introduce toolkit.scheduleDelayedUpdate() - // so that case b) only restores the old 'value_text' after some delay, - // increasing the chance of a) to happen. - dirty_content.mark(); - toolkit.scheduleUpdate(this); } private SpinnerValueFactory createSVF() @@ -349,18 +352,20 @@ public void increment(int steps) private void writeResultingValue(double change) { - double value; - if (!(getVTypeValue() instanceof VNumber)) - { - scheduleContentUpdate(); - return; + if (enabled) { + double value; + if (!(getVTypeValue() instanceof VNumber)) + { + scheduleContentUpdate(); + return; + } + value = ((VNumber)getVTypeValue()).getValue().doubleValue(); + if (Double.isNaN(value) || Double.isInfinite(value)) return; + value += change; + if (value < getMin()) value = getMin(); + else if (value > getMax()) value = getMax(); + toolkit.fireWrite(model_widget, value); } - value = ((VNumber)getVTypeValue()).getValue().doubleValue(); - if (Double.isNaN(value) || Double.isInfinite(value)) return; - value += change; - if (value < getMin()) value = getMin(); - else if (value > getMax()) value = getMax(); - toolkit.fireWrite(model_widget, value); } }; @@ -505,11 +510,10 @@ public void updateChanges() jfx_node.resize(model_widget.propWidth().getValue(), model_widget.propHeight().getValue()); // Enable if enabled by user and there's write access - final boolean enabled = model_widget.propEnabled().getValue() && + enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); + setDisabledLook(enabled, jfx_node.getChildrenUnmodifiable()); jfx_node.setEditable(!toolkit.isEditMode() && enabled); - jfx_node.getEditor().setCursor(enabled ? Cursor.DEFAULT : Cursors.NO_WRITE); jfx_node.getEditor().setFont(JFXUtil.convert(model_widget.propFont().getValue())); diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/TextEntryRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/TextEntryRepresentation.java index e2d34d906b..3792dc2673 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/TextEntryRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/TextEntryRepresentation.java @@ -49,6 +49,7 @@ public class TextEntryRepresentation extends RegionBaseRepresentation We'll receive contentChanged() and display PV's latest. - // b) PV doesn't receive the value and never sends - // an update. JFX control is stuck with the 'text' - // the user entered, not reflecting the actual PV - // --> Request an update to the last known 'value_text'. - // - // This could result in a little flicker: - // User enters "new_value". - // We send that, but restore "old_value" to handle case b) - // PV finally sends "new_value", and we show that. - // - // In practice, this rarely happens because we only schedule an update. - // By the time it executes, we already have case a. - // If it does turn into a problem, could introduce toolkit.scheduleDelayedUpdate() - // so that case b) only restores the old 'value_text' after some delay, - // increasing the chance of a) to happen. - dirty_content.mark(); - toolkit.scheduleUpdate(this); + if (enabled) { + // Strip 'units' etc. from text + final String text = jfx_node.getText(); + + final Object value = FormatOptionHandler.parse(model_widget.runtimePropValue().getValue(), text, + model_widget.propFormat().getValue()); + logger.log(Level.FINE, "Writing '" + text + "' as " + value + " (" + value.getClass().getName() + ")"); + toolkit.fireWrite(model_widget, value); + + // Wrote value. Expected is either + // a) PV receives that value, PV updates to + // submitted value or maybe a 'clamped' value + // --> We'll receive contentChanged() and display PV's latest. + // b) PV doesn't receive the value and never sends + // an update. JFX control is stuck with the 'text' + // the user entered, not reflecting the actual PV + // --> Request an update to the last known 'value_text'. + // + // This could result in a little flicker: + // User enters "new_value". + // We send that, but restore "old_value" to handle case b) + // PV finally sends "new_value", and we show that. + // + // In practice, this rarely happens because we only schedule an update. + // By the time it executes, we already have case a. + // If it does turn into a problem, could introduce toolkit.scheduleDelayedUpdate() + // so that case b) only restores the old 'value_text' after some delay, + // increasing the chance of a) to happen. + dirty_content.mark(); + toolkit.scheduleUpdate(this); + } } @Override @@ -399,14 +402,14 @@ public void updateChanges() jfx_node.setFont(JFXUtil.convert(model_widget.propFont().getValue())); // Enable if enabled by user and there's write access - final boolean enabled = model_widget.propEnabled().getValue() && + enabled = model_widget.propEnabled().getValue() && model_widget.runtimePropPVWritable().getValue(); // Don't disable the widget, because that would also remove the // context menu etc. // Just apply a style that matches the disabled look. jfx_node.setEditable(enabled); - Styles.update(jfx_node, Styles.NOT_ENABLED, !enabled); - jfx_node.setCursor(enabled ? Cursor.DEFAULT : Cursors.NO_WRITE); + setDisabledLook(enabled, jfx_node.getChildrenUnmodifiable()); + if(jfx_node instanceof TextField){ ((TextField)jfx_node).setAlignment(pos); diff --git a/app/display/thumbwheel/src/main/java/org/csstudio/display/widget/ThumbwheelWidgetRepresentation.java b/app/display/thumbwheel/src/main/java/org/csstudio/display/widget/ThumbwheelWidgetRepresentation.java index cd68aab556..3aa5b0e7d0 100644 --- a/app/display/thumbwheel/src/main/java/org/csstudio/display/widget/ThumbwheelWidgetRepresentation.java +++ b/app/display/thumbwheel/src/main/java/org/csstudio/display/widget/ThumbwheelWidgetRepresentation.java @@ -109,7 +109,7 @@ protected void unregisterListeners() { public void updateChanges() { super.updateChanges(); if (dirty_enablement.checkAndClear()) { - jfx_node.setDisable(!enabled); + setDisabledLook(enabled, jfx_node.getChildren()); } if (dirty_style.checkAndClear()) { @@ -150,7 +150,8 @@ private void styleChanged(final WidgetProperty property, final Object old_val // decrementing the values via buttons private void writeValueToPV(final Number new_value) { - toolkit.fireWrite(model_widget, new_value); + if (enabled) + toolkit.fireWrite(model_widget, new_value); } // Value change is triggered when the PV value changes From 3c88e1b85ad35ede123b55b045fad91f0f096a27 Mon Sep 17 00:00:00 2001 From: Rebecca Williams Date: Thu, 29 May 2025 14:30:34 +0100 Subject: [PATCH 4/4] Update logic in ActionButton to identify when it should look disabled This should occur when there is only a single action to write to a PV that is non-writeable. In any other case the actionbutton should be enabled to run other actions attached to it. --- .../widgets/ActionButtonRepresentation.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ActionButtonRepresentation.java b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ActionButtonRepresentation.java index 24a85d5887..d72c31f938 100644 --- a/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ActionButtonRepresentation.java +++ b/app/display/representation-javafx/src/main/java/org/csstudio/display/builder/representation/javafx/widgets/ActionButtonRepresentation.java @@ -166,9 +166,17 @@ private ButtonBase makeBaseButton() { final Button button = new Button(); button.setOnAction(event -> confirm(() -> handleActions(actions.getActions()))); result = button; + if (actions.getActions().size() == 1) { + // If the ActionButton only has a single action and that is to + // write to a PV then is_writePV should be true. + // This means that if the PV is non-writable then the + // ActionButton will be disabled. + if (actions.getActions().get(0).getType().equals("write_pv")) + is_writePV = true; + } } else { // If there is at least one non-WritePVAction then is_writePV should be false - is_writePV = !has_non_writePVAction; + is_writePV = has_non_writePVAction; final MenuButton button = new MenuButton(); @@ -461,11 +469,7 @@ public void updateChanges() { // Don't disable the widget, because that would also remove the // tooltip // Just apply a style that matches the disabled look. - Styles.update(base, Styles.NOT_ENABLED, !enabled); - // Apply the cursor to the pane and not to the button - if (!toolkit.isEditMode()) { - jfx_node.setCursor(enabled ? Cursor.HAND : Cursors.NO_WRITE); - } + setDisabledLook(enabled, jfx_node.getChildren()); } } }