1

キーボード入力(KeyUpまたはKeyDown)のストリームIObservable<InputEvent> eventStreamを表すストリームがあります。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");

次の大理石の図のように、これら2つのシーケンス間のオーバーラップを見つけるために演算子を適用したいと思います。

  • K =イベントタイプ。ここ.で、はプレス、'はリリースです
  • i = eventStream
  • C = eventStream.WhileHeld( "ctrl")
  • S = eventStream.WhileHeld( "shift")
  • r = resultStream

大理石:

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

(注:コンピューターの前で一度テスト/改訂します)

次のように、「オーバーラップウィンドウ」の動作を取得できると思い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)を選択し、両方のウィンドウにあるプレスのみを選択します
  • それらのネストされたオブザーバブルをフラット化し、

(編集:さらに考えてみると、ウィンドウセレクターを一定期間再利用できる可能性があります...)

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

ああ、私はこれを説明するリンクがそこにあることを知っていました:

Windows上の計り知れないLeeCampbellとRxのバッファ

于 2013-02-01T17:47:16.467 に答える
1

私が最終的に思いついた解決策は、の構成構文を提供しましたx.WhileHeld(ctrl).WhileHeld(shift)。秘訣は、ではなく元のIObservable<InputEvent>ソースから派生したものを渡し、それをウィンドウ化することでした。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 に答える
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 に答える