前回の書き込みで、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 件のコメント:
コメントを投稿