I'm trying to create a simple memory match game using Java 11 and JavaFX.
I have a scenario where two cards don't match. Before the non-matching cards are flipped back I'd like to have 800 milliseconds delay, so the user can see what was the second selected card. I'm using this code and it works fine:
CompletableFuture.delayedExecutor(800, TimeUnit.MILLISECONDS).execute(() -> {
firstRevealedCard.setFaceDown(); // sets an ImageView's setImage() method
secondRevealedCard.setFaceDown(); // sets an ImageView's setImage() method
firstRevealedCard = null; // sets instance variable to null
secondRevealedCcard = null; // sets instance variable to null
});
All the cards in the game are in a 4 column and 2 row GridPane.
Each cell in the grid is an ImageView set to an Image.
Each card object has a Mouse (click) event attached to it:
// Add all cards to the deck
for ( int card_ID = 1; card_ID < unique_cards+1; card_ID++ ) {
Card card1 = new Card( card_ID );
card1.setOnMouseClicked( (MouseEvent event) -> { onMouseClicked(card1); });
addCard(card1);
Card card2 = new Card( card_ID );
card2.setOnMouseClicked( (MouseEvent event) -> { onMouseClicked(card2); });
addCard( card2 );
In the code above card1 and card2 are two cards having the same picture. They're matching cards.
The cards are stored in an ArrayList. After all the cards has been added to the ArrayList, the ArrayList is shuffled.
The ArrayList store Card objects and each object has its own mouse event listener attached.
THE ISSUE: When two cards don't match, the non-matching cards need to flip back and the 800 milliseconds timeout works well using the CompletableFuture.delayedExecutor, but I want the "mouse event" to be blocked or locked until the Executor fininshes, otherwise, let's say the user clicks an other card, but the non-matching cards didn't have the chance to flip back.
First card selected - Apple
Second card selected - Banana
Two non-matching cards
Executor starts
100 milliseconds
100 milliseconds
100 milliseconds
100 milliseconds
the user clicks another card (throws an exception)
just because user clicked another card before executor finishes
100 millisecond
...
...
at 800 milliseconds
Executor finishes, cards are flipped back
Exception in thread "ForkJoinPool.commonPool-worker-3" java.lang.NullPointerException
at memorygamefx.CardDeck.lambda$onMouseClicked$0(CardDeck.java:85)
at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1426)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
I would be grateful for any suggestion. Thank you!
Don't use delayedExecutor for this. If you continue to do so, wrap the delayed code you invoke in Platform.runLater, so that it runs on the JavaFX thread otherwise weird stuff might happen.
Instead use a PauseTransition such as this:
PauseTransiton pause = new PauseTransition(Duration.millis(800));
Node disabledNode = scene.getRoot();
disabledNode.setDisable(true);
pause.setOnFinished(e -> {
disabledNode.setDisable(false);
// delayed code that you would like to run.
});
pause.play();
The example disables the entire scene when the pause is executing.
A binding can also be used instead, but manually setting and unsetting the disable property is probably better:
scene.getRoot().disableProperty().bind(
Bindings.equal(pause.statusProperty(), Animation.Status.RUNNING)
);
Disabling the scene will, by default, grey it out to give feedback that it is disabled. If you don't want that, you could instead add or remove a mouse event filter (please read attached link to understand this if you don't know what it is) on a scene or a hierarchy or nodes to prevent input that you don't want the app to process during the pause.
Another thing you could do to disable mouse input, is to set the root node to mouseTransparent while the pause is running, that way mouse input will be ignored. That would be similar to defining your own filters to ignore mouse events, but much easier to implement.
Some advantage of a PauseTransition are:
Everything runs on the JavaFX thread, so you don't need to deal with multi-threading issues.
It has a rich API so it is quite flexible.
It is reusable.
Related
I have a function that looks like the following
void MainWindow::CreateEnvironment()
{
MdiWindow* sub = createSubWindow();
MyQTWidget* widget = CreateWidget();
..
..
}
I want that during this function a progress bar will be shown at the beggining and will close at the end of this function.
However adding
void MainWindow::CreateEnvironment()
{
**progressBarDialog->show();**
MdiWindow* sub = createSubWindow();
MyQTWidget* widget = CreateWidget();
..
..
**progressBarDialog->hide();**
}
Does not work, maybe because the function needs to exit first or something.
What is the best way to do this?
I assume that you use QProgressDialog?
You need to first setup the dialog with the correct number of steps you expect, how long you want to wait before it actually shows and more importantly: you need to call setValue() to update the progress bar.
Here is an example of how I would solve that (as far as I understand it)
void MainWindow::CreateEnvironment()
{
auto bar = new QProgressBarDialog(this);
bar->setLabelText(tr("Creating Environment..."));
bar->setCancelButton(nullptr); // No cancel button
bar->setRange(0, 10); // Let's say you have 10 steps
bar->setMinimumDuration(100); // If it takes more than 0.1 sec -> show the dialog
bar->setValue(0);
MdiWindow* sub = createSubWindow();
bar->setValue(1);
MyQTWidget* widget = CreateWidget();
..
..
bar->setValue(10);
MyLastWidget* last = CreateLastWidget();
bar->deleteLater(); // Not needed anymore, let's get rid of it
}
And don't worry too much if the dialog never shows. Unless you're doing really heavy computation (such as allocating / initialising huge portion of memory), creating widgets is very fast and would finish before the 100ms times out.
EDIT: Another thing to be aware of: QProgressDialog is meant to work after the main event loop started. (That is after the call to app.exec() in your main())
If you plan to show call this function in the constructor of your MainWindow, the dialog might even never show up because the window itself is not fully created and operational.
If you intended to call this function later, when the main window is already displayed on screen and the user hit a New Document button of some sort: you can ignore this part of the answer.
My Dialog is a simple Frame with an Image, a label to display a question and two more labels (Yes / No) with TapCommand.
I've set up the container with the DialogPage.xaml and DialogPageViewModel and injected in the ViewModel I want to open the dialog.
Here is the code I'm using to call the Dialog:
public void ShowDialog()
{
_dialogService.ShowDialog("DiscardPopup", CloseDialogCallback);
}
void CloseDialogCallback(IDialogResult dialogResult)
{
var goBack = dialogResult.Parameters.GetValue<bool>("GoBack");
if (goBack)
NavigationService.GoBackAsync();
}
If the user taps over the "Yes label", I execute this command:
YesCommand = new DelegateCommand(() => YesTapped());
private void YesTapped()
{
IDialogParameters pa = new DialogParameters();
pa.Add("GoBack", true);
RequestClose(pa);
}
If the user taps over the "No label", I simply call:
NoCommand = new DelegateCommand(() => RequestClose(null));
The "problem" is when the ShowDialog is fired, the DiscardPopup is taking up to 3 seconds to show up.
Is there a way to make it faster?
The same happens with the TapCommands, 2 - 3 seconds when the RequestClose is invoked.
Without actual code telling you exactly what the issue is, is going to be best guess. Based on your feedback to my comments above I would suggest the following:
Try displaying the dialog on a test page that doesn't have a complex layout. My guess is that you won't see such a long load time. If that's the case this would point to your layout being overly complex and that the lag time is due to the device struggling to re-render the View
Try using Prism.Plugin.Popups. You'll need to initialize Rg.Plugins.Popup and register the DialogService. You can see docs on that at http://popups.prismplugins.com
I'm currently validating a date box widget to prevent filing after a 30 days grace period. The date validation was working but after the alert prompted it wasn't going down(I was stock in here even after a couple of clicks). Also the date box is not going null.
function checkDateValidate(widget) {
var form = app.pages.Newitem.descendants;
var otdate = form.otDateBox.value;
var date = new Date();
var pastdate = date.setDate(date.getDate() - 31);
if(otdate <= pastdate) {
alert('Date exceeds within 30 days grace period is invalid.');
form.otDateBox.value = null;
}
}
I expected to clear the date box widget.
This error sometimes happens because there is an infinite loop going on and this is your case. It is very important that you understand the difference between the onValueChange event handler and the onValueEdit event handler.
The onValueChange:
This script will run on the client whenever the value property of this widget changes. The widget can be referenced using parameter widget and the new value of the widget is stored in newValue.
The onValueEdit:
This script will run on the client whenever the value of this widget is edited by the user. The widget can be referenced using parameter widget and the new value of the widget is stored in newValue. Unlike onValueChange(), this runs only when a user changes the value of the widget; it won't run in response to bindings or when the value is set programmatically.
Why is this happening?
Since your logic is set on the onValueChange event handler, this will be triggered everytime the dateBox widget value is changed, even programmatically; Hence, form.otDateBox.value = null; is triggering the logic over and over again. The reason why it is being triggered over and over again is due to your comparison logic:
if(otdate <= pastdate)
Here, the value of otdate has become null wich when converted to number Number(null) the value is 0(zero). The value of pastdate is obviously a number greater than zero, eg 1555413900712. So obviously, zero is less than or equal to 1555413900712 and that is why you are triggering an infinite loop.
So, in summary, there is only one way to fix this. Set the logic inside the onValueEdit event handler instead of the onValueChange.
Fist of all, I'm really sorry for my bad English and I pretty new to game maker .
I have 2 object in the game : obj_apple and obj_door( I unchecked visible box)
my question is
how can I make an obj_door visible in the room when all the obj_apple are destroyed?
Object obj_door, Step event, place code (Add event -> Step -> Step -> tab Control -> section Code, first icon (Execute code)):
if !instance_exists(obj_apple) visible = true;
Another option, so you aren't making a check in every step event, is to put the check for the number of obj_apple in the destroy event of obj_apple.
For example, in the destroy event of obj_apple you would have:
if (instance_number(object_index) == 0) {
with (obj_door) {
visible = true;
}
}
EDIT 4:
EDIT 3
EDIT 2
string currentWindow = driver.CurrentWindowHandle;
driver.SwitchTo().Window("");
string childTitle = driver.Title;
driver.SwitchTo().Window(currentWindow);
string parentTitle = driver.Title;
the above code gives me the same title for parent window or child window.
EDIT:
<a id="ctl00_ctl00_Features_ctl03_lnkPage" class="title" target="_blank" href="websiteaddress">Stay Around</a>
how to verify the title of a newly window open and once i verified then close the opened new window?
so in my page I have a link and click on the link and it opens a new window and now I am not sure how to verify the title of that window.
here is what i have done so far.
GoToMysiteUrl();
IWebElement addtoList = driver.FindElement(By.XPath(_pageName));
addtoList.Click();
//it opens a new window
now i want to switch focus on the new window and verify the title and close the new window
back to the previous window.
The piece that most people miss when dealing with popup windows in IE is that a click on an element is asynchronous. That is to say, if you check the .WindowHandles property immediately after a click, you may lose the race condition, because you're checking for the existence of a new window before IE has had the chance to create it, and the driver has had a chance to register it exists.
Here's the C# code I would use to perform the same operation:
string foundHandle = null;
string originalWindowHandle = driver.CurrentWindowHandle;
// Get the list of existing window handles.
IList<string> existingHandles = driver.WindowHandles;
IWebElement addtoList = driver.FindElement(By.XPath(_pageName));
addtoList.Click();
// Use a timeout. Alternatively, you could use a WebDriverWait
// for this operation.
DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(5));
while(DateTime.Now < timeout)
{
// This method uses LINQ, so it presupposes you are running on
// .NET 3.5 or above. Alternatively, it's possible to do this
// without LINQ, but the code is more verbose.
IList<string> currentHandles = driver.WindowHandles;
IList<string> differentHandles = currentHandles.Except(existingHandles).ToList();
if (differentHandles.Count > 0)
{
// There will ordinarily only be one handle in this list,
// so it should be safe to return the first one here.
foundHandle = differentHandles[0];
break;
}
// Sleep for a very short period of time to prevent starving the driver thread.
System.Threading.Thread.Sleep(250);
}
if (string.IsNullOrEmpty(foundHandle))
{
throw new Exception("didn't find popup window within timeout");
}
driver.SwitchToWindow(foundHandle);
// Do whatever verification on the popup window you need to, then...
driver.Close();
// And switch back to the original window handle.
driver.SwitchToWindow(originalWindowHandle);
Incidentally, if you're using the .NET bindings, you have access to a PopupWindowFinder class in the WebDriver.Support.dll assembly, which uses a very similar approach to the locating popup windows. You may find that class meets your needs exactly, and can use it without modification.
GoToMysiteUrl();
IWebElement addtoList = driver.FindElement(By.XPath(_pageName));
addtoList.Click();
// Post above operation a new window would open as described in problem
// Get hold of Main window's handle
string currentWindow = Driver.CurrentWindowHandle;
// Switch to the newly opened window
Driver.SwitchTo().Window("Your Window Name");
// Perform required Actions/Assertions here and close the window
// Switch to Main window
Driver.SwitchTo().Window(currentWindow);