JavaFX how to return to state where waiting for user event - events

I'm currently implementing a checkers game with Groovy and JavaFX. The window that contains the board is presented once primaryStage.show() is executed and waits for an event to fire.
I have set up a sequence of functions that execute once a user clicks to make a legal move:
blackSquare.onMouseClicked = { e ->
commenceTurns(pos, view)
}
Conveniently this has allowed me to implement the whole game from this initial click (the AI move just executes after the user move).
However, I've now hit a problem. I need to implement multi-takes for the user and this requires me to return to the state where the board is waiting for the click event to fire. I have no idea how to accomplish this.
The basic program flow is:
// WAITING FOR CLICK EVENT
// CLICK EVENT CALLS:
commenceTurns()
userTurn()
makeMove()
if (takeMade && canTakeFurther) {
// EITHER JUMP TO END OF STACK OR RETURN TO WAITING STATE
}
computerTurn()
makeMove()
redrawBoard()
// END OF STACK
Hopefully this makes it clear what I'm looking for. Appreciate any help!

Related

JavaFX Button detecting event as well as time elapsed

I implemented another version of this project with Java Swing, but because it could not support multi-touch functionality, I am rewriting it using JavaFX.
So I want the program to wait either for a user clicks a Button object or for a timeout, say 5 seconds - this means that the program would present a Button on the screen at one point and does not proceed to the next phase until either the Button is pressed or 5 seconds pass without it being pressed.
Clearly, detecting a click is elementary because adding ActionEvent would solve it. The tricky part is measuring the time. When implementing with Swing objects, I would have a while loop and have Thread.sleep(very_short_time_interval) inside to periodically check the elapsed time:
lastUpdate = System.currentTimeMillis();
while ((!pressed) && (System.currentTimeMillis() - lastUpdate <= timeout)) {
Thread.sleep(50);
}
The purpose of the Thread.sleep() in the pseudo-code above was to prevent the while loop from executing too often. Though it does not seem like the best practice, this trick apparently worked when used with Swing objects. But I realized after trying out the same thing with JavaFX Button objects, it turns the shape of the mouse pointer to a circulating ring, indicating the process is busy on Windows. What is worse is the button would not recognize mouse inputs during this busy phase. I am guessing JavaFX objects are heavier than Swing objects and is causing this problem as a result.
So my question is, would there be another way, possibly native function in JavaFX to make a Button expire without requiring a while loop? Or would there be some lighter weight objects than Button that works in a similar manner (listening to mouse clicks) that could work with the original while approach?
You can use a PauseTransition to wait for n-seconds. On its setOnFinished you can add an action to be performed. If the button has been pressed, you can cancel the transition on its action.
final PauseTransition pt = new PauseTransition(Duration.millis(5000));
pt.setOnFinished( ( ActionEvent event ) -> {
doSomething();
});
button.setOnAction( (ActionEvent event) -> {
doSomething();
pt.stop(); // If the button has been pressed, stop the Transition
});
pt.play();

How modal dialog is implemented?

For a long time I have been wondering how modal dialog is implemented.
Let me take Qt as an example. (Nearly all GUI toolkit has this mechanism)
In the main event loop, a slot is called, and in this slot a modal dialog is opened. Before the dialog is closed, the slot doesn't return control to the main event loop. So I thought that the main event loop is blocked and become unresponsive. Apparently this is not true, since when you open a modal dialog, the background main window is still working, like repainting its UI or keep displaying a curve or some graph. It just becomes not to accept any user input.
I did an experiment. I didn't open a modal dialog in the slot, but start a new thread there, and wait for the thread to finish in that slot. This definitely blocked the main event loop.
How modal dialog is implemented after all? How does it keep main event loop unblocked but at the same time blocked the calling slot?
There is only ever a need for a single event loop, and it does not block when a modal dialog appears. Though, I suppose, different toolkits may handle this differently. You would need to consult the documentation to know for sure. Conceptually, however, it all works in the same way.
Every event has a source where the event occured. When a modal dialog appears, the event loop either ignores or redirects all events that originate outside of the dialog. There's really no magic to it. Generally speaking, it's like an if statement in the event loop code that says "if (modal_is_shown() and !event_is_in_modal_window()) {ignore_and_wait_for_next_event()}". Of course, the logic is a bit more complicated, but that's the gist of it.
If you're looking for examples here's another one:
In Tk, there is only ever one event loop. Modal behavior (doesn't have to be dialog, can also be tooltips, textbox etc) is simply implemented by making the main window ignore mouse and keyboard events. All other events like redraws etc. can still be serviced because the event loop is still running.
Tk implements this via the [grab] function. Calling grab on a UI object makes it the only object able to respond to keyboard and mouse events. Essentially blocking all other objects. This doesn't mess with the event loop. It merely temporarily disables event handlers until the grab is released.
It should be noted that Unix-like operating systems running X also has grab built in to the windowing system. So it's not necessarily implemented merely by UI toolkit libraries but is sometimes also a built in feature of the OS. Again, this is implemented by simple blocking/disabling of events instead of instantiating separate event loops. I believe this also used to be the case for the older MacOS before OSX. Not sure about OSX or Windows though. Even though modality is often implemented by the OS itself, toolkits like Qt and Tk often implement their own mechanisms to standardize behaviors across different platforms.
So the conclusion is, it is not necessary to block the main event loop to implement modality. You just need to block the events and/or event handlers.
The answer by https://stackoverflow.com/users/893/greg-hewgill is correct.
However, reading the follow-up discussion between him and https://stackoverflow.com/users/188326/solotim , I feel that there is room for further clarification, by means of prose and some pseudo-code.
I'll handle the prose part with a fact-list:
The main message loop does not run until the modal activity is finished
However, events are still delivered while the modal activity is running
This is because there is a nested event loop within the modal activity.
So far I just repeated Greg's answer, please bear with me as it is for continuity's sake. Below is where I hope to contribute additional, useful info.
The nested event loop is part of the GUI toolkit, and as such, it knows the callback functions related to every window in existence
When the nested event loop raises an event (such as a repaint event directed to the main window), it invokes the callback function associated with that event. Note that "callback" here may refer to a method of a class representing a window, in object-oriented systems.
the callback function does what is needed (e.g., repaint), and returns right back to the nested message loop (the one within the modal activity)
Last, but not least, here's pseudo-code to hopefully illustrate further, using a fictitious "GuiToolkit":
void GuiToolkit::RunModal( ModalWindow *m )
{
// main event loop
while( !GuiToolkit::IsFinished() && m->IsOpen() )
{
GuiToolkit::ProcessEvent(); // this will call
// MainWindow::OnRepaint
// as needed, via the virtual
// method of the base class
// NonModalWindow::OnRepaint
}
}
class AboutDialog: public ModalWindow
{
}
class MainWindow: public NonModalWindow
{
virtual void OnRepaint()
{
...
}
virtual void OnAboutBox()
{
AboutDialog about;
GuiToolkit::RunModal(&about); // blocks here!!
}
}
main()
{
MainWindow window;
GuiToolkit::Register( &window ) // GuiToolkit knows how to
// invoke methods of window
// main event loop
while( !GuiToolkit::IsFinished() )
{
GuiToolkit::ProcessEvent(); // this will call
// MainWindow::OnAboutBox
// at some point
}
}
In general, a modal dialog box of this type is implemented by running its own message loop instead of your application's message loop. Messages directed to your main window (such as timer or paint messages) will still get delivered, even during the modal operation.
In some situations, you may have to be careful that you don't recursively do the same thing repeatedly. For example, if you trigger a modal dialog box on a timer message combined with some persistent flag, you'll want to make sure you don't keep bringing up the same dialog box repeatedly whenever the timer message fires.

How can one detect Mission Control or Command-Tab switcher superseding one's program in OS X?

I'm trying to use CGAssociateMouseAndMouseCursorPosition(NO) in a program. This disconnects the mouse from the on screen cursor when your application is "in the foreground". Unfortunately it also disconnects it when Mission Control or the application switcher or who knows what else comes up.
So far I know:
The application is still active.
The window is still key.
Nothing is sent to the default notification center when these things come up.
The application stops receiving mouse moved events, but an NSEvent addGlobalMonitorForEventsMatchingMask:handler: also does not receive them, which is strange to say the least. It should receive any events not delivered to my application. (I was planning to detect the missing events to know when to associate the mouse again.
So, is there a way to detect when my application is no longer in control, specifically because Mission Control or the switch has taken over? They really expect the mouse to work and I need to restore that association for them.
I share your surprise that a global event monitor isn't seeing the events. In a similar situation, I used a Quartz Event Tap for a similar purpose. The Cocoa global event monitor is quite similar to event taps, so I figured it would work.
I put the tap on kCGAnnotatedSessionEventTap and compared the result from CGEventGetIntegerValueField(event, kCGEventTargetUnixProcessID) to getpid() to determine when the events were going to another app (e.g. Mission Control or Exposé). (I disable the tab when my app resigns active status, so it should only receive events destined for another app when this sort of overlay UI is presented.)
By the way, you mentioned monitoring the default notification center, but, if there's a notification about Mission Control or the like, it's more likely to come to the distributed notification center (NSDistributedNotificationCenter). So, it's worth checking that.
I needed to check for mission control being active and ended up with an approach along the lines of Ken's answer.
Sharing is caring so here is the smallest sensible complete code that worked for me: (Swift 5)
import Foundation
import AppKit
let dockPid = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.dock").first?.processIdentifier
var eventTargetPid: Int32?
let eventTap = CGEvent.tapCreate(
tap: .cgAnnotatedSessionEventTap,
place: .headInsertEventTap,
options: .listenOnly,
eventsOfInterest: CGEventMask(
(1 << CGEventType.mouseMoved.rawValue)
| (1 << CGEventType.keyDown.rawValue)
),
callback: { (tapProxy, type, event, _:UnsafeMutableRawPointer?) -> Unmanaged<CGEvent>? in
// Now, each time the mouse moves this var will receive the event's target pid
eventTargetPid = Int32(event.getIntegerValueField(.eventTargetUnixProcessID))
return nil
},
userInfo: nil
)!
// Add the event tap to our runloop
CFRunLoopAddSource(
CFRunLoopGetCurrent(),
CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0),
.commonModes
)
let periodSeconds = 1.0
// Add a timer for periodic checking
CFRunLoopAddTimer(CFRunLoopGetCurrent(), CFRunLoopTimerCreateWithHandler(
kCFAllocatorDefault,
CFAbsoluteTimeGetCurrent() + periodSeconds, periodSeconds, 0, 0,
{ timer in
guard eventTargetPid != dockPid else {
print("Dock")
return
}
print("Not dock")
// Do things. This code will not run if the dock is getting events, which seems to always be the case if mission control or command switcher are active
}), .commonModes)
CFRunLoopRun()
This simply checks whether the dock was the one to receive the last event of interest (here that includes mouse movement and key-downs).
It covers most cases, but will report the wrong value between the command switcher or mission-control hiding and the first event being sent to a non-dock app. This is fine in my use-case but could be an issue for other ones.
Also, of course, when the dock at the bottom is active, this will detect that too.
Have you tried asking NSRunningApplication?

Intercepting and Disabling Global Mouse Events

I have a problem with my mouse. Every now and then it will double click when I only single click. I know this is a problem with the mouse, and I've contacted the manufacturer, still waiting for a reply. But in the meantime I was wondering if there was a way that I could find out when the left mouse button had been clicked twice within a very short period (probably 1-10 milliseconds) of time, and disable the second click.
I mostly know how to use hooks, so that's not the problem, my main question is how to stop an event from happening, if that's possible.
The information on how to prevent the mouse message from being processed is in the documentation of the "LowLevelMouseProc callback function" in MSDN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644986(v=vs.85).aspx
Specifically, it says: "If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure." So, if you know about windows hooks, you know how to do it.
EDIT: Actually, now that I think more about it, you don't want to discard any event. You simply want to transform the doubleclick event into just another left-button-down event. I believe you can do it from within the hook handler, and it will work. Have you tried it?
In C#'s WinForms, you write an event handler involving the mouse receiving a MouseEventArgs object. Inside it, you can access certain info such as the number of times it was clicked, for example.
protected void RowClicked(object sender, MouseEventArgs evt)
{
// Trigger it when the mouse was clicked only once
if( evt.Button.Clicks == 1 ) {
// ... more things ...
}
return;
}
Other GUI libraries have other possibilities. That said, your problem has nothing to do with GUI libraries. You have to change the sensitivity of your mouse, in the configuration options of your operating system. For example, in the Windows' control panel, you can change how much time has to pass between a click and another one to be considered a doble-click. In lUbuntu, you can do the very same, in System menu >> Preferences >> Keyboard and Mouse.

How to continuously do an action as the user holds down their finger in XNA

I have an action for when the user holds down their finger, but how do I continuously do this action as the user holds down his finger? For example I want to: moveRight += 10; continuously while the user holds down their finger on the right part of the screen. Does anyone know how to do this in XNA?
Use either the mouse input API (for easy, single-touch input on WP7), specifically check:
MouseState ms = Mouse.GetState();
if(ms.LeftButton == ButtonState.Pressed && ms.X > centerOfScreen)
{
DoSomething();
}
Or use the touch input API on WP7:
TouchCollection touches = TouchPanel.GetState();
foreach(TouchLocation touch in touches)
{
if((touch.State == TouchLocationState.Pressed
|| touch.State == TouchLocationState.Moved)
&& touch.Position.X > centerOfScreen)
{
DoSomething();
break;
}
}
Is there an action for when they release their finger? If so, then use a looping structure (game loop) and add some input state, rather than only responding to events.
Set that state to "true" when you get the "hold" event
Set it to "false" when you get the event stating that the "hold" event is over
Check that boolean in your loop, and increment your x value (or x velocity) if it is set
Looking at The docs for Gesture Support for Windows Phone.
Maybe your "release" event that you want to handle is the ManipulationCompleted event. I can't tell if it is Silverlight-only, or both Silverlight and XNA, though...
And if that fails, maybe there are lower level events you can handle.
Gestures are abstractions for entire sets of motion. The API is designed to do all the dirty work of figuring out what happened, and telling you that it did happen, not how it happened. If you want to have finer grain control than that, then you need a finer grained API.
See: http://msdn.microsoft.com/en-us/library/ff607287(v=VS.95).aspx (possibly the Touch class).

Resources