I have an app where, when the user is typing in a text input, typing a normal letter causes the menu bar to activate (drop down its sub-menu) instead of filling the input as it should.
The easiest way to see this issue is to grab SceneBuilder, drop down a panel and an area chart and start typing into the Title field. I typed "The", and the "e" caused the "Edit" menu to drop down as shown.
It doesn't always happen, and it goes away if you Alt your way out of the menu bar, so I presume it's some kind of MenuBar state issue.
I'm wondering how to make the MenuBar respect the input's sovereignty and if there is a fix, why does it behave this way under SceneBuilder?
Currently using SceneBuilder v18 on Windows, though I've seen this going back to v12 or v13. Always on Windows.
EDIT: Going on #Slaw's suggestion, I think the real issue is that once the Menu bar is activated with an Alt, it never deactivates until you press Alt again. So, you can hit Alt, give various inputs the focus and type into them, drag and drop new controls...basically do anything all while the Menu Bar is waiting to eat the keystroke.
The desired behavior is for either the menu bar to only activate WHILE Alt is being pressed or for it to give up the focus if the next key doesn't activate. Not to lie in wait, letting all keystrokes pass by until it can pounce on one.
When the stage regains focus, send an Esc key press event to the menu bar. You'll need to change up the variable names in the code below to match those from your own code base.
// After the app loses focus, when the user switches back using Alt+Tab,
// the menu is engaged on Windows. Simulate an ESC keypress to the menu
// to disable the menu, giving focus back to the application proper.
//
// JavaFX Bug: https://bugs.openjdk.java.net/browse/JDK-8090647
stage.focusedProperty().addListener( ( c, lost, found ) -> {
if( found ) {
mMainScene.getMenuBar().fireEvent( keyDown( ESCAPE, false ) );
}
} );
public static Event keyDown( final KeyCode code, final boolean shift ) {
return keyEvent( KEY_PRESSED, code, shift );
}
private static Event keyEvent(
final EventType<KeyEvent> type, final KeyCode code, final boolean shift ) {
return new KeyEvent(
type, "", "", code, shift, false, false, false
);
}
Tested on Windows 10, Java 19, and JavaFX 19.
Related
I have a page with a collection view and a search bar that filters its contents.
I want the filtering to happen as the user types in the search bar, so I bind the TextChanged event of the SearchBar to a command in the view model, like so
var eventToCommandBehavior = new EventToCommandBehavior()
{
EventName = nameof(searchBar.TextChanged),
};
eventToCommandBehavior.SetBinding(EventToCommandBehavior.CommandProperty, nameof(MyViewModel.StartOrResetSearchTimerCommand));
searchBar.Behaviors.Add(eventToCommandBehavior);
In the view model:
public ICommand StartOrResetSearchTimerCommand => new Command(() =>
{
StartOrResetSearchTimer();
});
private void StartOrResetSearchTimer()
{
if (!timerStarted)
{
searchTimer = new Timer(_ => PerformSearch(), null, searchTimeout, searchTimeout);
timerStarted = true;
}
else
ResetTimer();
}
private void PerformSearch()
{
//my code
OnPropertyChanged(collectionViewItemsSourceBinding);
}
The StartOrResetSearchTimerCommand filters the ItemsSource binding, and calls OnPropertyChanged(itemsSourceBinding) to update the UI.
On Android and UWP everything works as expected. However on iOS, when OnPropertyChanged is called, the focus moves out of the search bar, resulting to the soft keyboard being closed after each keyboard input.
Has anyone else encountered this? Any suggestions?
I have already tried not using this approach, and only filter the ItemsSource when the search button is pressed, which works, when there is something to search for (ie, there is some input in the search bar)
When the search bar text is empty (ie after Backspace) then the search button is greyed out.
Update
For now, I am using this workaround:
Perfom the search only when the search button is pressed, and on TextChanged, check if the text is empty and reset the ItemsSourceworka
TextChanged is called anytime the text in the query box is changed. You can use this event to update your ItemsSource when the Text of the searchbar changes. You can refer to this part of the official example. First, given your ItemsSource, when you enter text in the searchbar, call the OnTextChanged event to update your ItemsSource, so that the real-time search keyboard will not lose focus.
I am trying to disable double clicking a Help label which is an anchor to open the Help window.
<p:a id="helpClick" onClick="help()">
<label value="Help" style="color:#FFFFFF;" />
</p:a>
When onClick() event is triggered once, either by Mouse click or Tapping the touchpad once, the help() method is being invoked.
void help() {
flag = true;
this.helpClick.setDisabled(true);
Window popupWindow = null;
popupWindow = (Window) Executions.createComponents("/zul/mainHelp.zul",
null, null);
this.popupWindow.setClosable(true);
popupWindow.addEventListener("onClose", new EventListener() {
void onEvent(Event event) throws Exception {
this.helpClick.setDisabled(false);
}
});
}
is the code which i added to handle the anchor tag with the id helpClick.
This is working perfectly fine when i use mouse clicks. For the first click, the window gets opened and simultaneously the Label is not taking any more click events.
When i try the same with mouse tap(using the touchpad), two single clicks are being triggered.
I have used onClick() to capture the event.
I am trying to disable the Label once it is clicked and the window is opened. Only after the window gets closed, i am enabling the label.
This is working totally fine when i use mouse clicks but not when i use tap.
With tapping, the label is taking multiple clicks which isnt the case with Mouse Click.
Without seeing code it's difficult to provide advice but maybe you can capture the onDoubleClick event and ignore it or forward it to the to the same listener as your onClick event.
... forward="onClick=onHelpClick,onDoubleClick=onHelpClick" ...
After question edit:
It sounds like a bug if you can double click a disabled component. One thing you could try is set your link to autodisable <p:a id="helpClick" onClick="help()" autodisable="self"> as per A component documentation
I am creating an application that should work on desktop and some mobile platforms.
The following example creates and connects my portrait/landscape buttons, in a group, to a slot, on the release signal.
m_landscapeRadio = new QRadioButton(QObject::tr("Landscape "));
m_portraitRadio = new QRadioButton(QObject::tr("Portrait "));
m_orientationGroup.addButton(m_landscapeRadio, 0);
m_orientationGroup.addButton(m_portraitRadio, 1);
m_orientationGroup.setExclusive(true);
m_landscapeRadio->setChecked(true);
connect(&m_orientationGroup, SIGNAL(buttonReleased(int)), this, SLOT(orientationSlot(int)));
But I found a weird situation:
Assume landscape button is checked. If I press and drag away from the portrait radio button, the slot action is performed (for the portrait option) but the portrait button is not checked.
I would like the action not to be performed.
For now...
In the orientationSlot I test the argument and set the checked value myself... Though I really expected the buttons to know to do that themselves.
But I think it is more expected by users that, if the press a button and change their mind, to be able to drag away from the button and not have the action be performed.
I can handle verifying if the check really happened in the action slot, and either check or discard the action depending on how I will think the user experience is better...
If I want the buttons to be checked and to perform the action as well:
void MyWidget::orientationSlot(int checked)
{
if(checked) m_portraitRadio->setChecked(true);
else m_landscapeRadio->setChecked(true);
.... actual actions
}
If I want the action not to be performed when the user drags away from the button (my preferred option):
void MyWidget::orientationSlot(int checked)
{
if(m_orientationGroup.checkedId() != checked) return;
.... actual actions
}
I use QRadioButton and handle mouse button being released event for reacting
on radio button being switched. It causes problems altogether with dragging event. I would like to either get the button
to be checked, or the action not to be performed.
http://doc.qt.io/qt-5/qradiobutton.html
Whenever a button is switched on or off, it emits the toggled()
signal. Connect to this signal if you want to trigger an action each
time the button changes state. Use isChecked() to see if a particular
button is selected.
Either you connect the radio button to the handler explicitly or the whole group: http://doc.qt.io/qt-5/qbuttongroup.html#buttonToggled
void QButtonGroup::buttonToggled(QAbstractButton *button, bool
checked)
This signal is emitted when the given button is toggled. checked is
true if the button is checked, or false if the button is unchecked.
Note: Signal buttonToggled is overloaded in this class. To connect to
this one using the function pointer syntax, you must specify the
signal type in a static cast, as shown in this example:
connect(buttonGroup, static_cast<void(QButtonGroup::*)
(QAbstractButton *, bool)>(&QButtonGroup::buttonToggled),
[=](QAbstractButton *button, bool checked) {
if (button == m_portraitRadio) {
// Portrait (un)checked
if (checked)
{
// checked!
}
}
/* ... */ });
I would like to use the standard JavaFX Alert class for a confirmation dialog that includes a check box for "Do not ask again". Is this possible, or do I have to create a custom Dialog from scratch?
I tried using the DialogPane.setExpandableContent() method, but that's not really what I want - this adds a Hide/Show button in the button bar, and the check box appears in the main body of the dialog, whereas I want the check box to appear in the button bar.
Yes, it is possible, with a little bit of work. You can override DialogPane.createDetailsButton() to return any node you want in place of the Hide/Show button. The trick is that you need to reconstruct the Alert after that, because you will have got rid of the standard contents created by the Alert. You also need to fool the DialogPane into thinking there is expanded content so that it shows your checkbox. Here's an example of a factory method to create an Alert with an opt-out check box. The text and action of the check box are customizable.
public static Alert createAlertWithOptOut(AlertType type, String title, String headerText,
String message, String optOutMessage, Consumer<Boolean> optOutAction,
ButtonType... buttonTypes) {
Alert alert = new Alert(type);
// Need to force the alert to layout in order to grab the graphic,
// as we are replacing the dialog pane with a custom pane
alert.getDialogPane().applyCss();
Node graphic = alert.getDialogPane().getGraphic();
// Create a new dialog pane that has a checkbox instead of the hide/show details button
// Use the supplied callback for the action of the checkbox
alert.setDialogPane(new DialogPane() {
#Override
protected Node createDetailsButton() {
CheckBox optOut = new CheckBox();
optOut.setText(optOutMessage);
optOut.setOnAction(e -> optOutAction.accept(optOut.isSelected()));
return optOut;
}
});
alert.getDialogPane().getButtonTypes().addAll(buttonTypes);
alert.getDialogPane().setContentText(message);
// Fool the dialog into thinking there is some expandable content
// a Group won't take up any space if it has no children
alert.getDialogPane().setExpandableContent(new Group());
alert.getDialogPane().setExpanded(true);
// Reset the dialog graphic using the default style
alert.getDialogPane().setGraphic(graphic);
alert.setTitle(title);
alert.setHeaderText(headerText);
return alert;
}
And here is an example of the factory method being used, where prefs is some preference store that saves the user's choice
Alert alert = createAlertWithOptOut(AlertType.CONFIRMATION, "Exit", null,
"Are you sure you wish to exit?", "Do not ask again",
param -> prefs.put(KEY_AUTO_EXIT, param ? "Always" : "Never"), ButtonType.YES, ButtonType.NO);
if (alert.showAndWait().filter(t -> t == ButtonType.YES).isPresent()) {
System.exit();
}
And here's what the dialog looks like:
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