Button of Choice: Use ToggleButtons as RadioButtons
For MQTT.fx I wanted to use ToggleButtons to e.g. choose the de coding of a MQTT Message or the QoS Level:
I found out that in context of a ToggleGroup ToggleButtons behave different than RadioButtons in terms of selection/deselection: unlike RadioButtons ToggleButtons can still be set to unselected state.
A RadioButton extends ToggleButton and overrides fire() (which is invoked when a user gesture indicates that an event for this ButtonBase
should occur aka “Button was clicked”):
RadioButton:
1 2 3 4 5 6 | @Override public void fire() { // we don't toggle from selected to not selected if part of a group if (getToggleGroup() == null || !isSelected()) { super .fire(); } } |
ToggleButton:
1 2 3 4 | @Override public void fire() { setSelected(!isSelected()); fireEvent( new ActionEvent()); } |
In a ToogleGroup ToggleButtons should behave like RadioButtons, so IMHO this is a bug worth a pull request for ToggleButton ��
One way to handle this is for sure to create a custom extension of ToggleButton implementing fire() in respect to the RadioButton.
But I like more to add behavior to existing controls.
This is my tweak to modify default behavior by adding filters to all ToogleButtons of a ToggleGroup consuming unwanted MouseEvents:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class JavaFXUtil { private static JavaFXUtil me; private JavaFXUtil() { } public static JavaFXUtil get() { if (me == null ) { me = new JavaFXUtil(); } return me; } public EventHandler<MouseEvent> consumeMouseEventfilter = (MouseEvent mouseEvent) -> { if (((Toggle) mouseEvent.getSource()).isSelected()) { mouseEvent.consume(); } }; public void addAlwaysOneSelectedSupport( final ToggleGroup toggleGroup) { toggleGroup.getToggles().addListener((Change<? extends Toggle> c) -> { while (c.next()) { for ( final Toggle addedToggle : c.getAddedSubList()) { addConsumeMouseEventfilter(addedToggle); } } }); toggleGroup.getToggles().forEach(t -> { addConsumeMouseEventfilter(t); }); } private void addConsumeMouseEventfilter(Toggle toggle) { ((ToggleButton) toggle).addEventFilter(MouseEvent.MOUSE_PRESSED, consumeMouseEventfilter); ((ToggleButton) toggle).addEventFilter(MouseEvent.MOUSE_RELEASED, consumeMouseEventfilter); ((ToggleButton) toggle).addEventFilter(MouseEvent.MOUSE_CLICKED, consumeMouseEventfilter); } } |
01 02 03 04 05 06 07 08 09 10 11 12 13 | public class ButtonDemoController { @FXML private ToggleGroup g2; @FXML private ToggleGroup g3; public void initialize() { JavaFXUtil.get().addAlwaysOneSelectedSupport(g2); JavaFXUtil.get().addAlwaysOneSelectedSupport(g3); } } |
Example code at GitHub.
Reference: | Button of Choice: Use ToggleButtons as RadioButtons from our JCG partner Jens Deters at the JavaFX Delight blog. |
I love toggle buttons just because they’re a break from the norm, but I think they might be confusing for people who aren’t familiar with them. And yes, of course I realize they’re totally intuitive and it’s fairly obvious how they work, but you know how it is…