1

我有一个IObservable<InputEvent> eventStream表示键盘输入流(KeyUp 或 KeyDown)的流。通过应用Window 运算符,我可以隔离保持输入的持续时间:

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);
}

我现在想做的是找到多个窗口的“重叠”。例如:

var ctrlHeld = eventStream.WhileHeld("ctrl");
var shiftHeld = eventStream.WhileHeld("shift");

我想应用一个运算符来查找这两个序列之间的重叠,如下面的大理石图所示

  • K = 事件类型,其中.是新闻,'是发布
  • 我 = 事件流
  • C = eventStream.WhileHeld("ctrl")
  • S = eventStream.WhileHeld("shift")
  • r = 结果流

大理石:

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---

这样的运营商存在吗?或者它将如何组成?

编辑:

为了帮助可视化实际的事件流(我意识到上面的大理石图有点复杂)。这是我的事件流测试代码:

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);
4

3 回答 3

1

我最终提出的解决方案提供了x.WhileHeld(ctrl).WhileHeld(shift). 诀窍是传入一个IObservable<InputEvent>派生自原始源,而不是 a string,然后将其窗口化:

public static IObservable<InputEvent> WhileHeld(this IObservable<InputEvent> source, IObservable<InputEvent> held) {
    return source.Window(held.Press(), _ => held.Release()).Switch();
}

...

var x = InputStream.Where(i => i.Key == "X");
var shift = InputStream.Where(i => i.Key == "Shift");
var ctrl = InputStream.Where(i => i.Key == "Ctrl");

// gets all key presses of X while shift and control are held
var ctrlShiftX = x.WhileHeld(shift).WhileHeld(ctrl);

这意味着捕获了按住键的第一个和最后一个按键,但我认为这不一定是坏事。

于 2013-02-05T09:54:56.220 回答
1

(注:将在电脑前测试/修改一次)

认为您可以使用 获得“重叠窗口”行为GroupJoin,如下所示:

var ctrlShift = ctrlHeld.GroupJoin(
        shiftHeld,
        e => eventStream.Key("ctrl").Release(),   // "left duration" selector
        e => eventStream.Key("shift").Release(),  // "right duration" selector
        (a,b) => b.Key(a.Key).Press())
    .Switch();

但我永远无法GroupJoin从视觉上得到正确的语法,所以很可能这不是它......基本上,想法是:

  • 从 ctrlHeld 中的事件开始(直到发生 ctrl 释放)
  • 从 shiftHeld 中的事件开始(直到发生轮班释放)
  • 选择重叠 (b),仅在两个窗口中按下
  • 展平那些嵌套的 observables 并返回

(编辑:进一步思考,您可能可以在持续时间内重用窗口选择器......)

var ctrlShift = ctrlHeld.GroupJoin(
        shiftHeld,
        e => ctrlHeld,   // "left duration" selector
        e => shiftHeld,  // "right duration" selector
        (a,b) => b.Key(a.Key).Press())
    .Switch();

啊哈,我知道有一个链接可以解释这一点:

不可估量的 Lee Campbell 在 Windows 和 Rx 中的缓冲区

于 2013-02-01T17:47:16.467 回答
0

到目前为止,我最接近的是:

var ctrlShift = Observable.CombineLatest(ctrlHeld, shiftHeld)
    .SelectMany(list =>
        {
            if (list.Any(o => o != list[0]))
                return Observable.Empty<InputEvent>();
            else
                return Observable.Return(list[0]);
        });

它可以完成工作,但有点丑陋。如果有比这更简洁或更具表现力的答案,我会接受

于 2013-02-01T13:45:58.603 回答