Listen on event during other event only - events

How do I listen to MouseMove event, only after I have pressed my mouse button (MouseDown event)
Im basicly looking for Click->Drag->Release functionality in a F# forms application

This behavior can be nicely captured using the F# async workflows mechanism. I wrote an article about this (implementing exactly drag & drop kind of functionality) where you can find the details - See Programming user interfaces with F# async workflows.
The sample application implements drawing where you push a button, then move the mouse (to define a rectangle) and then eventually release the button:
let rec drawingLoop(clr, from) = async {
// Wait for the first MouseMove occurrence
let! move = Async.AwaitObservable(form.MouseMove)
if (move.Button &&& MouseButtons.Left) = MouseButtons.Left then
// Refresh the window & continue looping
drawRectangle(clr, from, (move.X, move.Y))
return! drawingLoop(clr, from)
else
// Return the end position of rectangle
return (move.X, move.Y) }
let waitingLoop() = async {
while true do
let! down = Async.AwaitObservable(form.MouseDown)
let downPos = (down.X, down.Y)
if (down.Button &&& MouseButtons.Left) = MouseButtons.Left then
let! upPos = drawingLoop(Color.IndianRed, downPos)
do printfn "Drawn rectangle (%A, %A)" downPos upPos }
The nice thing here is that you can pretty nicely express the logic - wait for MouseDown (inside waitingLoop), then call the drawingLoop function which waits for MouseMove until the button is released (and then transfers the control back to waitingLoop which starts waiting for another mouse down event).
Another version of a similar piece of code is in Phil Trelford's fractal zoom, which uses the same gesture for zooming.
The complete source code is a part of the Chapter 16 source code of Real-World Functional Programming.

Attach the event listeners for move and up in the mouse down event, remove it in the up. This does mean the handler method needs to be a method (or you need to keep hold of the delegate used) to pass to the event's remove method.
(If this were for C# I would point you towards Rx—the Reactive Extensions—as this is perhaps the defining example, but I'm unsure how well Rx works with F#.)

You can also use something like this:
let setupDrag(target, fn) =
let isDown = ref false
target.MouseDown |> Event.add(fun _ -> isDown := true)
target.MouseMove |> Event.filter(fun _ -> !isDown) |> Event.add(fn)
target.MouseUp |> Event.add(fun( _ -> isDown := false)
In a real implementation, you may want to actually do other stuff when the transition starts, stops, and all those things. You may for example want to capture the pointer on target.

Related

macOS Key Event Slow Repeat

I'm trying to create a small WASD demo game in macOS. I'm using NSEvent for handling the key events. To detect the key presses, I'm searching for keyDown events. Here's what I have:
NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
(keyEvent) -> NSEvent? in
if self.keyDown(with: keyEvent) {
return nil
} else {
return keyEvent
}
}
func keyDown(with event: NSEvent) -> Bool {
userInt.keyDown(key: event.characters)
return true
}
So here, I'm holding the keys down (as you'd expect in a game), and I'm getting some very slow movement. Like, when I'm holding it down, it's very janky. Upon further inspection, I saw that the key repeat interval was 0.1s, which was set in my system preferences. This means that it's skipping frames. However, in a game, I don't want this setting to affect the movement. So how can I detect a key holding event without being held up by the key repeat interval?
You should ignore key-repeat events (with isARepeat true). Instead, when you get a key-down event, start a timer that fires however often you want to advance your game state. Advance the game state in that timer's firing code. When you get a key-up event, stop the timer.

How to pass data from onClick function [duplicate]

Trying to learn to write applications with Gtk2Hs I'm getting difficulties bridging the gap between the event driven Gtk2HS and the persistent state of my model. So to simplify, lets say that I have this simple application
module Main where
import Graphics.UI.Gtk
import Control.Monad.State
main = do
initGUI
window <- windowNew
button <- buttonNew
set button [buttonLabel := "Press me"]
containerAdd window button
-- Events
onDestroy window mainQuit
onClicked button (putStrLn ---PUT MEANINGFUL CODE HERE---)
widgetShowAll window
mainGUI
and the state of my application is how many times the button has been pressed. Seeing other posts like this they rely on MVars or IORefs which do not seem satisfactory to me, because in the future maybe I will want to refactor the code so the state lives on its own context.
I think that the solution should use the State monad using a step function like:
State $ \s -> ((),s+1)
but I'm not sure about the implications, how to do that in the above code or even if that monad is the right solution to my problem.
There's basically two approaches:
Use a pointer of some kind. This is your IORef or MVar approach. You can hide this behind a MonadState-like interface if you like:
newtype GtkT s m a = GtkT { unGtkT :: ReaderT (IORef s) m a } deriving (Functor, Applicative, Monad, MonadIO)
runGtkT = runReaderT . unGtkT
instance MonadIO m => MonadState s (GtkT s m) where
get = GtkT (ask >>= liftIO . readIORef)
put s = GtkT (ask >>= liftIO . flip writeIORef s)
Pull an "inversion of control" style trick. Write a callback that prints a number, then replaces itself with a new callback that prints a higher number.
If you try to use State or StateT directly, you're gonna have a bad time.

Double tap recognition takes too long? (Hammer.js 2.0)

I am programming a highly responsive web application and came across the issue that most time is used to recognize double taps.
I am using this code from the website:
var singleTap = new Hammer.Tap({ event: 'singletap' });
var doubleTap = new Hammer.Tap({event: 'doubletap', taps: 2 });
hammer.add([doubleTap, singleTap]);
doubleTap.recognizeWith(singleTap);
singleTap.requireFailure(doubleTap);
This basically works quite fine. However, due to the timeouts/intervals the recognition of a double tap takes quite "long". I guess its about 2 times the interval - one for each tap.
The waiting for the last interval (waiting for a third tap) is senseless in my scenario.
Is there any "ok tapCount == 2, we fire now and don't wait any longer"-TapRecognizer option?
Update, I have done some logging:
First column: passed ms since first event
0 input: mousedown
74ms input: mouseup
145ms input: mousedown
218ms input: mouseup
520ms double tap
-
0 input: mousedown
64ms input: mouseup
366ms single tap
This confirms my theory that double tap is waiting for a third click but I don't think there's an option to disable this.
I share my solution to the problem:
I copied the TapRecognizer and named it DblTapRecognizer. The interesting source code lines are:
if (tapCount === 0) {
// no failing requirements, immediately trigger the tap event
// or wait as long as the multitap interval to trigger
if (!this.hasRequireFailures()) {
return STATE_RECOGNIZED;
} else {
this._timer = setTimeoutContext(function() {
this.state = STATE_RECOGNIZED;
this.tryEmit();
}, options.interval, this);
return STATE_BEGAN;
}
}
"if (!this.hasRequireFailures())" seems to misbehave in my situation, since the comment hints at immediate firing... So just "return STATE_RECOGNIZED;" and delete the rest for the DblTapRecognizer.
We ran into similar slowness issues. Apparently there is an inherent lag on tap action on touch devices.
We ended up using FastClick
All you need to do is FastClick.attach(document.body);
This improved the "tap performance" for us.

d3.js - transition interruption event?

I was wondering how to handle the fact that an interrupted transition within d3.js does not trigger an end event. As the API doc says
Note that if the transition is superseded by a later-scheduled
transition on a given element, no end event will be dispatched for
that element; interrupted transitions do not trigger end events.
from: https://github.com/mbostock/d3/wiki/Transitions#control
In my case transitions are triggered by user interaction. These transitions might be interrupted when the user triggers a new transition through mouse click. Let's say in the first transition an element was meant to fade out and be removed at the end of the transition. If this transition is interrupted the element will never be removed. I could disallow further user interaction during the time a transition happens yet that is not really what I want (particular as i have back and forward buttons which allow the user to click through previous states of my svg graph quickly ... ) Basically I would need an "Interruption Event"
Thanks
martin
I think there is no really satisfactory way to do this. A little bit painful workaround would be counting the number of transitions currently taking place and reasoning from that.
So, initialize:
var transitionCount = 0;
And whenever you schedule new transitions:
if ( transitionCount != 0 ) {
// handle interrupted transitions here somehow
transitionCount = 0;
}
var myTransition = selection.transition().... ;
transitionCount += myTransition.size();
myTransition.each('end', function() { transitionCount --; });
If you can handle manually cleaning up interrupted transitions like this, this would be fine. Notice, that you can't use 'start' events to increment the counter as there is a delay between scheduling a transition and it being started so you'd get a race condition there.

Proper way how to prepare data in async cancellable workflow with responsive UI

This question is based on Async.TryCancelled doesn't work with Async.RunSynchronously that looks complex, so I will cut a simple part that I try to solve.
Suppose I have this functions:
let prepareModel () =
async {
// this might take a lot of time (1-50seconds)
let! a = ...
let! b = ...
let! res = combine a b
return res
}
let updateUI model =
runOnUIThread model
prepareModel prepares data that should be displayed to the user. updateUI refreshes the UI (removes old controls and creates new ctls based on new data).
Question: How should I call the two functions so that prepareModel is cancellable any time?
The flow is
user clicks refresh
prepareModel(1) started and is running asynchronously, so the UI is responsive and user can work with the application
user changes data and clicks refresh again
prepareModel(1) from is cancelled and
new prepareModel(2) is started
user changes data and clicks refresh again
prepareModel(2) is cancelled and
new prepareModel(3) is started
..
prepareModel(n) finished
updateUI is ran on UI thread, redraws the UI
(My first solution is based on MailboxProcessor that ensures that only one prepareModel is executed, see at Async.TryCancelled doesn't work with Async.RunSynchronously but as I experimented with this, it's not bug free)
One possible approach would be to start the workflow asynchronously in the background using Async.Start (then it should be cancellable). To redraw the UI at the end, you can use Async.SwitchToContext to make sure that the last part of the workflow executes on the UI. Here is a sketch:
// Capture current synchronization context of the UI
// (this should run on the UI thread, i.e. when starting)
let syncContext = System.Threading.SynchronizationContext.Current
// Cancellation token source that is used for cancelling the
// currently running workflow (this can be mutable)
let cts = ref (new CancellationTokenSource())
// Workflow that does some calculations and then updates gui
let updateModel () =
async {
// this might take a lot of time (1-50seconds)
let! a = ...
let! b = ...
let! res = combine a b
// switch to the GUI thread and update UI
do! Async.SwitchToContext(syncContext)
updateUserInterface res
}
// This would be called in the click handler - cancel the previous
// computation, creat new cancellation token & start the new one
cts.Cancel()
cts := new CancellationTokenSource()
Async.Start(updateModel(), cts.Token)

Resources