In a JavaFX stage, I want to validate user input when the focus leaves a textfield. If the user input is not a valid age (0 to 120), then a Dialog using ControlsFX Dialogs with an error message is displayed.
Here's the code:
participantAgeTextField.focusedProperty()
.addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> ov,
Boolean oldPropertyValue, Boolean newPropertyValue)
{
if( !newPropertyValue ) { // lost focus
if( !participantAgeTextField.getText().isEmpty() ) {
if ( participantAgeTextField.getText().matches("^\\d+$")) {
int val = Integer.
parseInt(participantAgeTextField.getText());
if( val <= 0 ) {
val = 1;
} else if( val > 120 ) {
val = 120;
}
participantAgeTextField.setText(""+val);
} else {
participantAgeTextField.setText("");
Dialogs.create()
.owner(null)
.title("Error")
.masthead(null)
.message("You must enter a valid age!")
.showError();
}
}
}
}
});
This works fine, except when a user enters an invalid value, and then tries to close the window by clicking the "X" button in the top right corner of the window (stage).
In this case the application 'hangs'. (Strangely enough only in Windows, does not happen in Linux).
I have been looking for a fix, like not displaying the message when the focus changes to the window's "X". However I did not find a way to detect this.
Other ideas how to fix this would be greatly appreciated!
Joris
EDIT
Probably ControlsFX causes the crash. I cannot use JavaFX dialogs (introduced in 8u40) because I'm using Javafx 8u25. Any alternatives welcomed!
EDIT 2
The crash can be avoided by not using ControlsFX Dialogs but creating the error message 'by hand' as suggested by DVarga. But this causes the error message to show up after the window has been closed. Any ideas on how to prevent that from happening?
I think I will circumvent the crash as follows:
In stead of showing a "popup" dialog window after loss of focus in case of wrong input, I will display an error message next to the input box in red. On re-gaining focus I can then remove the error message.
Related
I am building a Xamarin Forms mobile app that runs in Android on a Zebra scanner. I flip 2 different StackLayouts to IsVisble true/false to display different stuff in the UI. (StackLayout1 and StackLayout2)
The customer wants the user to be able to use the app entirely from the hardware keyboard on the scanner. So I have used the device Settings so that it never displays the virtual keyboard (I don’t think that matters for the issue I am having.)
I am overriding DispatchKeyEvent in a PageRenderer in the Android project and everything is working great … except.
The problem case:
StackLayout1 is displayed
the user taps an Entry control, putting the focus there
the user taps a button in the UI
the app displays StackLayout2
at this point the DispatchKeyEvent never fires no matter what key I press on the device keyboard
If an Entry box does NOT get the focus (step #2 above) the DispatchKeyEvent always fires in StackLayout2 and the StackLayouts display as expected.
If I programatically put the focus in an Entry box in StackLayout2 at step #3 above the DispatchKeyEvent fires fine.
That is not an OK solution. I have tried to progamatically put the focus on StackLayout2, and that code seems to do what is expected but DispatchKeyEvent does not fire.
Maybe I need to do something in the Android-project PageRenderer so that it is aware of StackLayout2 when it is made IsVisible = true.
Update 2: I found that I did NOT need custom StackLayouts. The solution which I posted below does not include any of this stuff I am describing in Update 1 (sorry, if that's confusing).
Update 1:
I added a ViewRenderer for both StackLayouts, and the code is hitting the OnElementChanged event when StackLayout2's IsVisible property flips to true, just great. Although the problem case is the same: DispatchKeyEvent does not fire once StackLayout2 is displayed, if an EntryBox had the focus in StackLayout1
Here is the OnElementChanged part of the new StackLayout ViewRenders
async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case "IsVisible":
if (Element.IsVisible)
{
if (sender is StackLayout)
{
this.FocusableViewAvailable(this); // if I comment these 2 lines out I get the same bad result
this.Focusable = true; // if I comment these 2 lines out I get the same bad result
this.FocusableInTouchMode = true;
var dd = this.RequestFocus(); // this is always false
var ee = this.IsFocused; // this is always false
}
}
break;
}
}
Also, as I am pointing out in the comments ^ there, IsFocused is always false.
Ideas?
My hunch, "Maybe I need to do something in the Android project PageRenderer" was correct. In the DispatchKeyEvent I had to make the MainPage have the focus when the keypress was handled.
Here is what the DispatchKeyEvent looks like now (notice the comments):
public override bool DispatchKeyEvent(KeyEvent ke)
{
// MainPage.ReceiveKeyPress(e); is the method that this method returns to
bool KeyPressWasHandled = false;
KeyPressWasHandled = (Element as MainPage).ReceiveKeyPress(ke);
if (KeyPressWasHandled)
{
// this next block seems to be needed so that this class
// continues to receive the keypress event after an Entry box has had the focus
this.Focusable = true;
this.FocusableInTouchMode = true;
this.RequestFocus();
return true; // returning true tells the parent class that the keypress has been handled
} else
{
try
{
return base.DispatchKeyEvent(ke);
}
Now the "problem case" in my initial post is no longer a problem.
NOTE: I found that I did NOT need the custom ViewRenderers that I had made for the StackLayouts.
I'm writing an app using Xamarin Forms and I have an issue I was hoping someone can help with.
My app contains a screen which has multiple icons that can be pressed which would then open a new screen.
My issue is that if you press the icon twice really fast, the app opens up 2 instances of the same screen (it's not just related to a double press, if you press the icon 6 times very fast it will open up 6 duplicate screens). Pressing the Back button, closes the top screen to reveal the duplicate screen underneath. Pressing the Back button again navigates you back to the original screen.
This issue seems to occur on any screen within my app so I'm hoping other people will have experienced it and know of a solution to prevent duplicate screens being displayed.
This is a known issue in TapEvents.
My hack is, in the code-behind, have a bool variable _canTap.
Inside the method you are calling to push new page, first you check if canTap, then set to false, and only set to true after navigating to another page. This way all taps will be disregarded.
Example:
private bool _canTap = true;
public void YourMethod()
{
if(_canTap)
{
_canTap = false;
YourMethodToNavigate();
_canTap = true;
}
}
In the Icon_Pressed method add this,
this.IsEnabled = false;
await Navigation.PushAsync(new MyPage());
this.IsEnabled = true;
It disables the page until the current Icon pressed event is finished
This is known problem with Xamarin apps. I've used a private variable combined with a try-finally pattern to solve this. Ex:
bool allowTap = true;
public void ButtonTapped()
{
try
{
if(allowTap)
{
allowTap = false;
// Do whatever...
}
}
finally
{
allowTap = true;
}
}
The finally makes sure allowTap gets set back to true no matter what happens, short of a complete crash. Note that you can also use a catch block between the try and finally blocks to grab any errors if needed.
I've disabled X button in Qt from my dialog using this line:
myDialog->setWindowFlags(Qt::Dialog | Qt::Desktop)
but I couldn't detect Alt + F4 using this code:
void myClass::keyPressEvent(QKeyEvent *e)
{
if ((e->key()==Qt::Key_F4) && (e->modifiers()==Qt::AltModifier))
doSomething();
}
what should I do to detect Alt+F4 or disable it in Qt?
Pressing Alt+F4 results in a close event being sent to your top level window. In your window class, you can override closeEvent() to ignore it and prevent your application from closing.
void MainWindow::closeEvent(QCloseEvent * event)
{
event->ignore();
}
If you left the close button (X) visible, this method would also disable it from closing your app.
This is usually used to give the application a chance to decide if it wants to close or not or ask the user by displaying an "Are you sure?" message box.
The code below prevents a dialog close when pressed Alt+F4, [X] or Escape, but not by calling SomeDialog::close() method.
void SomeDialog::closeEvent(QCloseEvent *evt) {
evt->setAccepted( !evt->spontaneous() );
}
void SomeDialog::keyPressEvent(QKeyEvent *evt) {
// must be overridden but empty if the only you need is to prevent closing by Escape
}
good luck to all of us ;)
Also you can handle the event in your dialog's class (at least if it's modal dlg):
void MyDialog::closeEvent(QCloseEvent* e)
{
if ( condition )
e->ignore();
else
__super::closeEvent(e);
}
my QComboBox-derived class lives in a QGraphicsScene at the bottom end of the (visible) screen - but it pops up downwards, thus out of view.
(How) is it possible to force the popup to open above the widget?
I've tried re-implementing showPopup like this:
void MyComboBox::showPopup()
{
QAbstractItemView *popupView = view();
popupView->move(0,-100);
//popupView->window->move(0,-100);
QComboBox::showPopup();
}
The result is, that the content seems to be shifted, but not the underlying popup object.
I think it might be possible to find a solution with styles as indicated in
this article, but I can't find any Styles control that might be helpful here. I am rather new to C++ as well as Qt, so I might be missing something obvious.
I'd appreciate any help on this matter!
Best regards,
Sebastian
With the information found here, I was able to get it done this way:
void SteuerQComboBox::showPopup() {
QComboBox::showPopup();
QWidget *popup = this->findChild<QFrame*>();
popup->move(popup->x(),popup->y()-this->height()-popup->height());
}
Note that it's crucially important to call the base classes "showPopup" first.
Thanks to everybody who was reading my question and thinking about it!
user1319422's solution isn't bad, but it has two problems.
If your platform has GUI animation, the listbox will animate opening downwards, then is moved above the text box.
If you disable combobox animation (or you don't have it), the call to QComboBox::showPopup() still makes the GUI element start to appear on the screen already. So, moving it there would cause it to flicker as it appears in the first place and moves to the next.
So, to address the first problem, I just switched off animation:
void MyComboBox::showPopup()
{
bool oldAnimationEffects = qApp->isEffectEnabled(Qt::UI_AnimateCombo);
qApp->setEffectEnabled(Qt::UI_AnimateCombo, false);
QComboBox::showPopup();
qApp->setEffectEnabled(Qt::UI_AnimateCombo, oldAnimationEffects);
}
Then, for the second problem, I moved the frame in the Show event:
bool MyComboBox::eventFilter(QObject *o, QEvent *e)
{
bool handled = false;
if (e->type() == QEvent::Show)
{
if (o == view())
{
QWidget *frame = findChild<QFrame*>();
//For some reason, the frame's geometry is GLOBAL, not relative to the QComboBox!
frame->move(frame->x(),
mapToGlobal(lineEdit()->geometry().topLeft()).y() - frame->height());
}
}
/*else if other filters here*/
if (!handled)
handled = QComboBox::eventFilter(o, e);
return handled;
}
if you want to force popup to open above only when it is out of view you can do this:
void SteuerQComboBox::showPopup() {
QComboBox::showPopup();
QWidget *popup = this->findChild<QFrame*>();
if((popup->y() + popup->height()) > this->window()->height())
popup->move(popup->x(),popup->y()-this->height()-popup->height());
}
I need to programmatically remove an alert.
This is why:
My application uses BrowserManager to enable deep linking based off of the content in the #hash part of the url. If an alert is currently up, and the user hits the back button, the application will revert back to its previous state. But the Alert will still be up, and in many cases irrelevant at that point.
So is there a way to programmatically remove the Alert? so when the hash fragment changes I can remove it.
Thanks!
It turns out the Alert.show function returns an Alert reference and then just uses PopUpManager to add it to the display list. so if you capture the return reference when you call Alert.show you can tell PopUpManager to remove it. :)
You can do this by keeping the Alert object as member data, and then setting its visible property to false when you're done with it. Next time you need to show an Alert, don't create a new one - grab the one you've already created and set its properties, then set visible to true again.
private var myAlert : Alert;
public void showAlert( message: String, title : String ) : void
{
hideAlert();
myAlert = Alert.show( message, title, Alert.OK | Alert.NONMODAL );
}
public void hideAlert() : void
{
if( myAlert != null && myAlert.visible ) {
myAlert.visible = false;
}
}
I don't think that is possible.
You can create your own alert component subclassing TitleWindow and then use PopupManager to show/hide them.