Exercise 10.4


A quick look at Graphics.SOE reveals that getWindowEvent is the right function to trap mouse down, up, and move events separately.

{-
maybeClear is useful for avoiding flicker when not dragging objects.
A real flicker-free solution should use double-buffering of course -
this code still flickers terribly while dragging.
-}

maybeClear :: Bool -> Window -> IO ()
maybeClear b w
    = if b
      then do clearWindow w
      else return ()

{-
The program is in two halves: either we're waiting for a click (not dragging),
or we're waiting for a release (dragging). Waiting for a click is almost
identical to the previous definition of loop, except for the event
function.
-}

waitForClick :: Window -> [(Color, Region)] -> Bool -> IO ()
waitForClick w regs b
    = do maybeClear b w
         sequence_ [drawRegionInWindow w c r | (c,r) <- reverse regs]
         event <- getWindowEvent w
         case event of
           (Button (x,y) True True) ->
               let aux (_,r) = r `containsR` (pixelToInch (x - xWin2),
                                              pixelToInch (yWin2 - y))
               in case (break aux regs) of
                    (_, []) -> closeWindow w
                    (top, hit:bot) -> waitForRelease (x,y) (x,y) w 
                                     (hit : (top++bot)) True
           _ -> waitForClick w regs False

{-
While dragging, we keep track of the initial click coordinates and translate
the Region at the head of the list by the offset of the current mouse 
coordinates. When the mouse button is released, we go back to waiting 
for a click, leaving the head Region where it is.
-}

waitForRelease :: Point -> Point -> Window -> [(Color, Region)] -> Bool -> IO ()
waitForRelease (origx, origy) (x,y) w regs b
    = do maybeClear b w
         sequence_ [drawRegionInWindow w c r | (c,r) <- reverse $ tail regs]
         let (c,r) = head regs
             newHeadReg = (Translate (pixelToInch (x - origx), pixelToInch(origy - y)) r)
         drawRegionInWindow w c newHeadReg
         event <- getWindowEvent w
         case event of
           (Button (x,y) True False) -> waitForClick w ((c,newHeadReg) : (tail regs)) True
           (MouseMove pt) -> waitForRelease (origx,origy) pt w regs True
           _ -> waitForRelease (origx,origy) (x,y) w regs False

{-
All that remains is to drive the drag and drop functionality with some boilerplate
draw and main functions.
-}

draw3 :: String -> Picture -> IO ()
draw3 s p = runGraphics $
            do w <- openWindow s (xWin, yWin)
               waitForClick w (pictToList p) True

main = draw3 "Drag and Drop" pic

Leave a Reply

Your email address will not be published. Required fields are marked *