I have a stream IObservable<InputEvent> eventStream that represents a stream of keyboard inputs(KeyUp or KeyDown). By applying the Window operator I can isolate the duration of a held input:
public static IObservable<InputEvent> WhileHeld(this IObservable<InputEvent> source, String key) {
var k = source.Where(i => i.Key == key);
return source.Window(k.Press(), _ => k.Release()).Switch().Where(i => i.Key != key);
}
What I would now like to do is find the “overlap” of multiple windows. For example:
var ctrlHeld = eventStream.WhileHeld("ctrl");
var shiftHeld = eventStream.WhileHeld("shift");
I would like to apply an operator to find the overlap between these two sequences, as in the following marble diagram where
- K = event type, where
.is a press and'is a release - i = eventStream
- C = eventStream.WhileHeld(“ctrl”)
- S = eventStream.WhileHeld(“shift”)
- r = resultStream
Marble:
K |--.---.-.-.-'-'---.-.-.-'-'---.-.-'-.-.-.-'
i |--a---C-S-b-S-C---C-S-c-C-S---C-S-S-d-S-e-C-
C |--------S-b-S-------S-c---------S-S-d-S-e---
S |----------b-----------c-C---------------e-C
r |----------b-----------c-----------------e---
Does such an operator exist? Or how would it be composed?
EDIT:
To help visualise the actual event stream(I realise the above marble diagram is a little complex). Here is my event stream test code:
eventStream.Pump("a", EventType.Down); // should not propagate
eventStream.Pump("ctrl", EventType.Down);
eventStream.Pump("shift", EventType.Down);
eventStream.Pump("b", EventType.Down); // should propagate
eventStream.Pump("shift", EventType.Up);
eventStream.Pump("ctrl", EventType.Up);
eventStream.Pump("ctrl", EventType.Down);
eventStream.Pump("shift", EventType.Down);
eventStream.Pump("c", EventType.Down); // should propagate
eventStream.Pump("ctrl", EventType.Up);
eventStream.Pump("shift", EventType.Up);
eventStream.Pump("ctrl", EventType.Down);
eventStream.Pump("shift", EventType.Down);
eventStream.Pump("shift", EventType.Up);
eventStream.Pump("d", EventType.Down); // should not propagate
eventStream.Pump("shift", EventType.Down);
eventStream.Pump("e", EventType.Down); // should propagate
eventStream.Pump("ctrl", EventType.Up);
The solution that I eventually came up with offered the compositional syntax of
x.WhileHeld(ctrl).WhileHeld(shift). The trick was to pass in anIObservable<InputEvent>derived from the original source, rather than astring, then window it:This means that the first and last keypress of the held key are captured, but I decided that this is not necessarily a bad thing.