Rx - Отслеживание операции панорамирования мыши - PullRequest
0 голосов
/ 30 апреля 2020

Я реализую операцию типа щелчка и перетаскивания для панорамирования камеры в графическом приложении.

Я хотел бы отслеживать, панорамируем ли мы в другом потоке, это может быть проверяется другими операциями для целей фильтрации (например, мы не должны разрешать выбор резинки, если мы панорамируем).

Код выглядит примерно так:

MouseClicked
    .Where(startClick => startClick.Action == MouseAction.LeftDown)
    .SelectMany(_ =>
        MouseMoved
            .Select(endMove => (_.startClick, endMove))
            .TakeUntil(MouseReleased))
    .Subscribe(_ => PanCamera(_.startClick, _.endMove));

Мое решение состоит в том, чтобы добавить следующие две строки кода

MouseClicked
    .Where(startClick => startClick.Action == MouseAction.LeftDown)
    .Do(_ => _isPanning.OnNext(true))  // Add this
    .SelectMany(_ =>
        MouseMoved
            .Select(endMove => (_.startClick, endMove))
            .TakeUntil(MouseReleased)
            .Finally(() => _isPanning.OnNext(false))   // Add this
    )
    .Subscribe(_ => PanCamera(_.startClick, _.endMove));

, где _isPanning - это Subject. Это прекрасно работает, но мне было интересно, если бы был лучший подход, без необходимости использовать Subject.

1 Ответ

2 голосов
/ 30 апреля 2020

Вместо использования темы вы можете сложить состояние панорамирования в одну наблюдаемую с несколькими подписчиками. Например:

public IDisposable PanningBehaviour()
{
    var observable = MouseClicked
        .Where(startClick => startClick.Action == MouseAction.LeftDown)
        .SelectMany(start =>
            MouseMoved
                .Select(current => (Start: start.Position, Current: current.Position, Panning: PanningState.Panning))
                .TakeUntil(MouseReleased)
                .Concat(Observable.Return((Start: Point.Empty, Current: Point.Empty, Panning: PanningState.NotPanning))))
        .Publish();

    var cameraSubscription = observable
        .Where(tuple => tuple.Panning == PanningState.Panning)
        .Subscribe(tuple => PanCamera(tuple.Start, tuple.Current));

    var notPanningSubscription = observable
        .Select(tuple => tuple.Panning == PanningState.NotPanning)
        .Subscribe(allowOperations => { /* Allow / disallow actions */ });

    return new CompositeDisposable(
        notPanningSubscription,
        cameraSubscription,
        observable.Connect()
    );
}

В /* Allow / disallow actions */ вы можете положить что угодно. В приложении MVVM этот подход особенно хорошо работает с реализацией ICommand , которая позволяет внешнему коду контролировать его состояние "CanExecute".

FWIW, мой MVx.Observable пакет содержит Observable.Command , который может напрямую подписаться на статус "notPanning", как показано ниже:

private MVx.Observable.Command _allowSelection = new MVx.Observable.Command();

public IDisposable PanningBehaviour()
{
    var observable = MouseClicked
        .Where(startClick => startClick.Action == MouseAction.LeftDown)
        .SelectMany(start =>
            MouseMoved
                .Select(current => (Start: start.Position, Current: current.Position, Panning: PanningState.Panning))
                .TakeUntil(MouseReleased)
                .Concat(Observable.Return((Start: Point.Empty, Current: Point.Empty, Panning: PanningState.NotPanning))))
        .Publish();

    var cameraSubscription = observable
        .Where(tuple => tuple.Panning == PanningState.Panning)
        .Subscribe(tuple => PanCamera(tuple.Start, tuple.Current));

    var notPanningSubscription = observable
        .Select(tuple => tuple.Panning == PanningState.NotPanning)
        .Subscribe(_allowSelection);

    return new CompositeDisposable(
        notPanningSubscription,
        cameraSubscription,
        observable.Connect()
    );
}

public ICommand AllowSelection => _allowSelection;

Надеюсь, это поможет.

...