27
27

More than 5 years have passed since last update.

Gyazoライクな画面範囲選択をC#とReactive Extensions (Rx)で書いた

Last updated at Posted at 2015-05-12

どうもGyazoのような画面範囲選択について「このUIをWindowsで実装しようとすると、千行近いコードをCか何かで書かなければなら」ないらしいですが、雑に作ったScreenCaptureWrapper初版(→画面を動画キャプチャするツールScreenCaptureWrapperを公開)でもとてもそんな長さにならなかったよなぁと思いつつも、Rxを使うとさらにすっきり書けそうだったので、書いてみました。

public static Task<Rect> SelectScreenPositionAsync()
{
    var shapeRect = new Rectangle() { Fill = new SolidColorBrush(Color.FromArgb(0x44, 0x99, 0, 0)), Stroke = Brushes.Red };
    var canvas = new Canvas();
    canvas.Children.Add(shapeRect);

    var window = new Window()
    {
        Title = "Select Screen Position", Top = 0, Left = 0,
        Width = SystemParameters.VirtualScreenWidth, Height = SystemParameters.VirtualScreenHeight,
        WindowStyle = WindowStyle.None, Topmost = true, ShowInTaskbar = false,
        AllowsTransparency = true,
        Background = new SolidColorBrush(Color.FromArgb(1, 0xff, 0xff, 0xff)),
        Cursor = Cursors.Cross, Content = canvas
    };

    var rectObservable = Observable.FromEventPattern<MouseEventArgs>(window, "MouseLeftButtonDown")
        .Select(ev => ev.EventArgs.GetPosition(window))
        .CombineLatest(Observable.FromEventPattern<MouseEventArgs>(window, "MouseMove"),
           (downPos, ev) =>
           {
               var movePos = ev.EventArgs.GetPosition(window);
               return new Rect(Math.Min(downPos.X, movePos.X), Math.Min(downPos.Y, movePos.Y),
                               Math.Abs(downPos.X - movePos.X), Math.Abs(downPos.Y - movePos.Y));
           })
        .TakeUntil(Observable.FromEventPattern<MouseEventArgs>(window, "MouseLeftButtonUp"));

    rectObservable.Subscribe(r =>
    {
        Canvas.SetLeft(shapeRect, r.X);
        Canvas.SetTop(shapeRect, r.Y);
        shapeRect.Width = r.Width;
        shapeRect.Height = r.Height;
    }, window.Close);

    window.Show();

    return rectObservable.ToTask(); // return a last value
}

本当は最初のほうはXAMLで書いてあげたほうがいろいろいじりやすくて便利だとは思いますが、そこはお好みでどうぞ。

(追記)
なお、HTML+JSがダメだと言っているわけではないです。ElectronはブラウザのレンダラプロセスのメッセージループにNode(io.js)を統合するとかcrazyで面白いので、それはそれで使っていきたいと思っています。

元アプリはReactで書かれていて、これはこれでまぁ良いんじゃないかと思いますが、このウィンドウであれば、以下のあたりのようなRx系ライブラリで、イベントからのDOMへ反映という形にすると、それはそれですっきりしそうだなぁという気がします。なお、私は(今のところ)Bacon.jsが好きです。

27
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
27