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