Prevent a toggle group from not having a toggle selected - Java FX - javafx

I have a toggle group with two toggle buttons that should look like this (Yellow toggle is the selected toggle).
However when I click on the selected toggle both toggles become unselected and look like this.
Then if I try to get whether the toggle is selected, I get a nullPointerException.
(Boolean) toggleGroup.getSelectedToggle().getUserData();
Is it possible to prevent the selected toggle from being unselected when it is clicked?

This one works for me. If the new selected element is null that means theres no selected element, therefore just select the previous one (which is the "oldValue")
toggleGroup.selectedToggleProperty().addListener((obsVal, oldVal, newVal) -> {
if (newVal == null)
oldVal.setSelected(true);
});

You can try the following code to create a persistent toggle.
/**
* Create a toggle group of buttons where one toggle will always remain switched on.
*/
class PersistentButtonToggleGroup extends ToggleGroup {
PersistentButtonToggleGroup() {
super();
getToggles().addListener(new ListChangeListener<Toggle>() {
#Override public void onChanged(Change<? extends Toggle> c) {
while (c.next()) {
for (final Toggle addedToggle : c.getAddedSubList()) {
((ToggleButton) addedToggle).addEventFilter(MouseEvent.MOUSE_RELEASED, new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
if (addedToggle.equals(getSelectedToggle())) mouseEvent.consume();
}
});
}
}
}
});
}
}
This just reacts to mouse events, so perhaps not ideal if you want to account for keyboard events or changing toggles in code.
A similar, but perhaps more complete approach that I haven't tried may be defined in the blog entry Button of Choice: Use ToggleButtons as RadioButtons.
Perhaps the simplest behavior to accomplish what you desire might be to just use RadioButtons instead of ToggleButtons.
If, additionally, you want the buttons styled like ToggleButtons, instead of RadioButtons, then you can try the styling technique which is outlined in: How to make a RadioButton look like regular Button in JavaFX.
RadioButton radioButton=new RadioButton("Radio");
radioButton.getStyleClass().remove("radio-button");
radioButton.getStyleClass().add("toggle-button");

Related

JavaFX TreeView: remove expand / collapse button (disclosure node) & functionality

I want to have a TreeView that has all of its children permanently expanded, and I don't want the user to be able to expand or collapse any of the children.
To do this I've found that I need to do the following:
Remove icon with CSS (Done)
Change expand and collapse image TreeView JavaFX 2.2
[edit] Above link should be used to change image; to remove completely, use this solution: https://stackoverflow.com/a/27831191/4430591
Remove double click functionality (Done)
Disable TreeItem's default expand/collapse on double click JavaFX 2.2
[edit] Remove ability to collapse / expand using keyboard arrrow keys (Done)
Given in José Pereda's solution below ( https://stackoverflow.com/a/27831085/4430591 )
[edit] Remove ability to right click for a ContextMenu (Done)
Given in José Pereda's solution below ( https://stackoverflow.com/a/27831085/4430591 )
Remove icon's clickablity (How do I do this?)
[edit] solution: https://stackoverflow.com/a/27831191/4430591
Even though the icon is no longer visible, it's still clickable. I don't see any way of filtering this; I only see ways to be able to respond to it after the fact.
Also, if I'm missing anything else that I need to do to ensure this functionality, please let me know.
I feel quite silly. I think this was mostly just a matter of not knowing what that darn arrow was called. Apparently it's a disclosureNode? Maybe that's common knowledge.
In the custom defined TreeCell, all I did was add this line in the updateItem method:
setDisclosureNode(null);
The solution to avoid modifying the skin or the default behavior is more simple if we trap the clicks before they are dispatched, and consume the right ones.
For that we can use an EventDispatcher, to filter both the mouse pressed and the right click over the arrows, which are StackPane nodes:
class CellEventDispatcher implements EventDispatcher {
private final EventDispatcher original;
public CellEventDispatcher(EventDispatcher original) {
this.original = original;
}
#Override
public Event dispatchEvent(Event event, EventDispatchChain tail) {
if (event.getEventType().equals(MouseEvent.MOUSE_PRESSED) ||
event.getEventType().equals(ContextMenuEvent.ANY)){
event.consume();
}
if(event instanceof KeyEvent && event.getEventType().equals(KeyEvent.KEY_PRESSED)){
if((((KeyEvent)event).getCode().equals(KeyCode.LEFT) ||
((KeyEvent)event).getCode().equals(KeyCode.RIGHT))){
event.consume();
}
}
return original.dispatchEvent(event, tail);
}
}
Now we apply our custom dispatcher to the tree view:
#Override
public void start(Stage primaryStage) {
TreeView<String> tree = new TreeView<>();
...
EventDispatcher treeOriginal = tree.getEventDispatcher();
tree.setEventDispatcher(new CellEventDispatcher(treeOriginal));
Scene scene = new Scene(tree);
primaryStage.setScene(scene);
primaryStage.show();
}
This will consume any click (left or right) over the arrows on the tree.
EDIT
Added to the event dispatcher class the case where the user uses the keyboard to traverse the tree view, consuming the collapse/expand events with arrow LEFT or RIGHT.

JavaFX, change css tablecell on sort

I have to change the styleclass of a TableCell in function of the data displayed, for example: if the value of a cell is the same in two contiguous rows, the cell has to be highlighted (i.e.: background red). This must work both adding data to the table and sorting by any column.
To do so, on sorting, I added a listener to tableview.getSortOrder() and in its onChange method the logic to do what I described above. By example:
public void onChanged(ListChangeListener.Change<? extends TableColumn> change) {
if (change.getList().size() > 0) {
Platform.runLater(new Runnable() {
#Override
public void run() {
/* set some css on tableview cells */
}
});
}
}
The problem is that doing so css changes are applied on the next refresh of the table not immediately. My suspects are on doing this inside Platform.runLater but I need it to have the data sorted as they should be, before changing styles.
Did I do anything wrong? Does exist a better way to do what I described?
You could do that if you have a specific css file for it and add it.
final String css = getClass.getResource("the_css.css").toExternalForm();
and add this in the Platform.runLater:
scene.getStylesheet.add(css);
remove if necessary with:
scene.getStylesheet.remove(css);
Not exactly sure if this does the trick, but it should change the background color right away and not after a refresh. i hope it's helping you in the right direction.

javafx setFocus after tabPaine change

Problem:
Have tabPane tabs OK.
In the first tab there is a text field. I am able to get focus on this field when starting the application.
After changing the tabs and coming back to the first tab I want focus to be on this textfield (barcodereader should be active in this field) without having to select the field with the mouse.
I am able to catch event from tabs with
tp.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>()
{ etc
(could not post with code)
and I am able to trigger en event for the first tab.
But field.requestFocus(); does not work. Probably because this method comes before rendering the textfield.
So here is my question:
How do you set focus on a control after clicking tabs in TabPane?
If you handle the mouse release event, it works: (The doFocus enables the requestFocus handling only when a tab selection changed before, otherwise it kicks in every time you click somewhere in the TabPane.)
final SimpleBooleanProperty doFocus = new SimpleBooleanProperty(false);
tabPane.setOnMouseReleased(new EventHandler<Event>() {
#Override
public void handle(Event event) {
if (!doFocus.get()) {
return;
}
doFocus.set(false);
switch (tabPane.selectionModelProperty().getValue().selectedIndexProperty().intValue()) {
case 0: tf1b.requestFocus(); break;
case 1: tf2a.requestFocus(); break;
default: break;
}
}
});
tabPane.selectionModelProperty().getValue().selectedIndexProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
doFocus.set(true);
}
});
When the TabPane has focus, one can change tab selection with the cursor keys and there the TextFields also won't get the focus with selection based approach. This probably should be handled too, if you need it.
(Recently I had a similar problem. I noticed, that the TabPane switches tabs immediately when you press the mouse button. My guess would be, that the selection based approach requests focus on the TextField right after mouse down, but the continued mouse down steals the focus back to the TabPane. Or maybe even the single mouse down event which changes selection causes the focus to go back to TabPane. However, my assumptions regarding the reasons may not be correct, as I am a newbie to JavaFX.)
EDIT: That handling certainly is not optimal. For instance, if you change tabs with the keys, the doFocus will be enabled and then clicking anywhere in the TabPane will trigger the requestFocus call. I thought this should be mentioned.
Also, take a look at my solution for setting focus on TextArea, when user changes selected tab(using mouse or keyboard) https://stackoverflow.com/a/19046535/2791746

Flex checkBox: disable state change when clicked on label

I would like the native Flex checkBox to change state only, when the box is clicked. If user clicks the label the state shouldn't change.
The click event cannot be muted as it is utilized in parenting components.
Any ideas how to obtain such functionality? How to detect, that user has clicked the label?
Thanks,
Rafal
Marty Pitt was very close to the right answer. I've added an event handler to his code, that stops the propagation - and now it works perfectly (as expected)!
The code below is a class that extends mx:CheckBox:
override protected function createChildren():void {
super.createChildren();
this.mouseChildren = true;
textField.mouseEnabled = false;
textField.addEventListener(MouseEvent.CLICK, textFieldClickHandler);
}
protected function textFieldClickHandler(me:MouseEvent):void{
me.stopImmediatePropagation();
}
Thank you.
Workaround - checkbox without label and separate label nearby.
If it's a Halo checkbox, I would create a subclass, and override createChildren(), with something like:
override protected function createChildren():void {
super.createChildren();
// in Button, this is false by default, however we want to restrict
// clicking to the button itself, not the label, so allow the children
// to recieve mouse events, to prevent the button from dispatching them.
this.mouseChildren = true;
textField.mouseEnabled = false;
}
That seems like a pretty kludgy hack, but it may work (I haven't tested it).
If it's a Spark checkbox, then you can just create a seperate skin. Much cleaner!

How can I tab accross a ButtonBar component in Flex?

I have a button bar inf flex along with several other input controls, I have set the tabIndex property for each control and all goes well until I tab to the ButtonBar.
The ButtonBar has 3 buttons but tabbing to it, only the first button gets focus, tab again and the focus goes back to the top control...
How can I make tabbing go through ALL buttons in a Flex Button bar? Is there a way to do this or do I need to create individual buttons for this?
This seems like a possible bug to me...
The component is written so the user must press the left/right arrow keys when focus is within the bar to traverse the buttons--this is a fairly standard GUI behavior (you also see this in other places like radio button groups). If you look into the SDK source for ButtonBar, you can see where they've explicitly disabled tab focus for each child button as it's created:
override protected function createNavItem(
label:String,
icon:Class = null):IFlexDisplayObject
{
var newButton:Button = Button(navItemFactory.newInstance());
// Set tabEnabled to false so individual buttons don't get focus.
newButton.focusEnabled = false;
...
If you really want to change this behavior, you can make a subclass to do it, something like this:
package {
import mx.controls.Button;
import mx.controls.ButtonBar;
import mx.core.IFlexDisplayObject;
public class FocusableButtonBar extends ButtonBar {
public function FocusableButtonBar()
{
super();
this.focusEnabled = false;
}
override protected function createNavItem(
label:String, icon:Class=null):IFlexDisplayObject
{
var btn:Button = Button(super.createNavItem(label, icon));
btn.focusEnabled = true;
return btn;
}
}
}

Resources