2011年12月18日日曜日

gtk2hs で落書きしてみる

前回の書き込みで、gtk2hs の準備ができたので、もう一歩進めてみる。

10年以上前、Visual C++を使ってた頃、MFC のチュートリアルで Scribble というのがあった。これを、Haskell で試してみた。

ただし、MDI とか ドキュメントの保存とかは割愛して、とりあえず描画の API とマウスイベントのハンドリングだけを気にすることにした。

仕組みは簡単で、マウスボタンが押下されたときから放されるまでのマウスポインタの軌跡を記録し、ウィンドウ描画時に各点を線でつないでいくというもの。コードは以下のようになる。

import qualified Graphics.UI.Gtk as G
import Graphics.UI.Gtk.Gdk.GC
import Graphics.UI.Gtk.Gdk.Events
import Data.IORef
import Graphics.UI.Gtk.Gdk.Drawable

main = do
    G.initGUI
    w <- G.windowNew
    G.onDestroy w G.mainQuit

    da <- G.drawingAreaNew

    isDrawingIORef <- newIORef False
    figuresIORef   <- newIORef [[]]

    G.onExposeRect da (const $ do dw <- G.widgetGetDrawWindow da
                                  gc <- gcNew dw
                                  f <- readIORef figuresIORef
                                  drawFigure dw gc f)

    G.onMotionNotify da True $ \Motion {eventX = x, eventY = y}-> do
      isDrawing <- readIORef isDrawingIORef
      case isDrawing of
        True  -> do  modifyIORef figuresIORef (\f->addPoint f x y)
                     G.widgetQueueDraw da
        _     -> return ()
      return True
  
    G.onButtonPress da $ \Button {eventX = x, eventY = y}-> do
      writeIORef isDrawingIORef True
      modifyIORef figuresIORef (\f->[point x y]: f)  
      return True

    G.onButtonRelease da $ \Button {eventX = x, eventY = y}-> do
      writeIORef isDrawingIORef False
      modifyIORef figuresIORef (\f->addPoint f x y)
      return True
  
    G.containerAdd w da
    G.widgetShowAll w
    G.mainGUI

  where
    point x y = (round x, round y)
    addPoint figures x y = (point x y: head figures): tail figures
    drawFigure _ _ [] = return ()
    drawFigure d g (f:fs) = do {
      G.drawLines d g f; drawFigure d g fs }

Haskell 自体にまだ慣れていないので、いかにも未熟な感じだけど、イベント処理と描画の作法がなんとなく分かってきた。ネット上で API を調べる事にも、だんだん慣れてきた。

ただ、IORef というのが、なんだかしっくり来ない。結局、状態を持ってしまってる事になり、どうも気持ち悪い。初心者なりに勝手に純粋関数型言語に期待していたのは、もっと、引数と戻り値だけでつながっていく感じなんだけど、まだまだ勉強が足りないのかもしれない。

結果としては、マウスボタンの判別をしていないので、右でも左でも書けてしまうけど、一応思ったとおりに動作するものができた。スプーの絵もこんな風に書く事ができる(頭頂部の突起がやや足りない事を除けば、我ながらよく描けたと思う)。

0 件のコメント:

コメントを投稿