module Graphics.UI.Sifflet.Frame
(
CanvFrame(..), FrameType(..)
, argIoletCounter
, atLeastSizeFrame
, cfEvalReady
, cfPointInHeader
, cfPointInFooter
, cfRoot
, frameNewWithLayout
, frameBodyBox
, frameNodeAt
, frameOffset
, levelOrder
, nodeCompoundFunction
, pointIolet
, resizeFrame
, translateFrame
, grTranslateFrameNodes
)
where
import Data.Function
import Data.List
import Data.Graph.Inductive as G
import Data.Sifflet.Functoid
import Data.Sifflet.Geometry
import Data.Sifflet.Tree
import Data.Sifflet.TreeLayout
import Data.Sifflet.WGraph
import Language.Sifflet.Expr
import Language.Sifflet.ExprTree
import Language.Sifflet.Util
data CanvFrame = CanvFrame {
cfHeader :: TextBox
, cfFooter :: TextBox
, cfVarNames :: [String]
, cfParent :: Maybe G.Node
, cfFrameNode :: G.Node
, cfEnv :: Env
, cfBox :: BBox
, cfLevel :: Double
, cfFunctoid :: Functoid
, frameType :: FrameType
}
deriving (Show)
instance Eq CanvFrame where (==) = (==) `on` cfFrameNode
data FrameType = EditFrame | CallFrame deriving (Eq, Read, Show)
levelOrder :: CanvFrame -> CanvFrame -> Ordering
levelOrder f1 f2 = compare (cfLevel f1) (cfLevel f2)
cfRoot :: CanvFrame -> G.Node
cfRoot frame =
case frameType frame of
EditFrame -> error "cfRoot: an edit frame has no root"
CallFrame -> succ (cfFrameNode frame)
cfEvalReady :: CanvFrame -> Bool
cfEvalReady frame =
(frameType frame == CallFrame) && (cfParent frame == Nothing)
cfPointInHeader :: CanvFrame -> Double -> Double -> Bool
cfPointInHeader frame x y =
pointInBB (Position x y) (tbBoxBB (cfHeader frame))
cfPointInFooter :: CanvFrame -> Double -> Double -> Bool
cfPointInFooter frame x y =
pointInBB (Position x y) (tbBoxBB (cfFooter frame))
frameBodyBox :: CanvFrame -> BBox
frameBodyBox frame =
let hbb = tbBoxBB (cfHeader frame)
fbb = tbBoxBB (cfFooter frame)
top = bbBottom hbb
bottom = bbTop fbb
in BBox (bbLeft hbb) top (bbWidth hbb) (bottom top)
editFrameNodes :: CanvFrame -> [G.Node]
editFrameNodes frame =
case frameType frame of
EditFrame -> fpNodes (cfFunctoid frame)
CallFrame -> error "editFrameNodes: not an EditFrame"
frameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node
frameNodeAt frame graph point =
case frameType frame of
CallFrame -> callFrameNodeAt frame graph point
EditFrame -> editFrameNodeAt frame graph point
editFrameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node
editFrameNodeAt _frame _graph _point = Nothing
callFrameNodeAt :: CanvFrame -> WGraph -> Position -> Maybe G.Node
callFrameNodeAt frame graph point =
let search :: [G.Node] -> Maybe G.Node
search [] = Nothing
search (r:rs) =
case lab graph r of
Just (WFrame _) -> search rs
Just (WSimple layoutNode) ->
let LayoutNode rootGNode treeBB = layoutNode
in
if pointInBB point treeBB
then if pointInBB point (gnodeNodeBB rootGNode)
then Just r
else search (suc graph r)
else search rs
Nothing ->
errcats ["editFrameNodeAt: search: no label for node",
show r]
in search [cfRoot frame]
atLeastSizeFrame :: Size -> CanvFrame -> CanvFrame
atLeastSizeFrame (Size minW minH) frame =
let BBox _ _ width height = cfBox frame
dwidth = if minW > width then minW width else 0
dheight = if minH > height then minH height else 0
in resizeFrame frame dwidth dheight
resizeFrame :: CanvFrame -> Double -> Double -> CanvFrame
resizeFrame frame dw dh =
let BBox x y bwidth height = frameBodyBox frame
bwidth' = max 0 (bwidth + dw)
height' = max 0 (height + dh)
bodyBB' = BBox x y bwidth' height'
header' = alignHeader (cfHeader frame) bodyBB'
footer' = alignFooter (cfFooter frame) bodyBB'
frameBox' = bbMergeList [tbBoxBB header', tbBoxBB footer', bodyBB']
in frame {cfHeader = header', cfFooter = footer', cfBox = frameBox'}
translateFrame :: CanvFrame -> Double -> Double -> CanvFrame
translateFrame frame dx dy =
frame {cfHeader = translate dx dy (cfHeader frame),
cfFooter = translate dx dy (cfFooter frame),
cfBox = translate dx dy (cfBox frame)}
frameOffset :: Style -> CanvFrame -> Position
frameOffset style oldFrame =
let bb = cfBox oldFrame
in Position (bbRight bb + styleFramePad style) (bbTop bb 40)
nodeCompoundFunction :: WGraph -> CanvFrame -> Node -> Maybe Function
nodeCompoundFunction graph frame node =
case lab graph node of
Nothing -> error "nodeCompoundFunction: no label for node"
Just (WFrame _) -> error "nodeCompoundFunction: node has a WFrame label"
Just (WSimple layoutNode) ->
case gnodeValue (nodeGNode layoutNode) of
ENode (NSymbol (Symbol "if")) _mvalue -> Nothing
ENode (NSymbol (Symbol symbolName)) _mvalue ->
case envLookup (cfEnv frame) symbolName of
Nothing -> Nothing
Just (VFun func@(Function _ _ _ (Compound _ _))) -> Just func
Just _ -> Nothing
_ -> Nothing
grTranslateFrameNodes :: WGraph -> CanvFrame -> Double -> Double -> WGraph
grTranslateFrameNodes wgraph frame dx dy =
case frameType frame of
CallFrame -> translateTree dx dy wgraph (cfRoot frame)
EditFrame -> translateNodes dx dy wgraph (editFrameNodes frame)
pointIolet :: Position -> Int -> [Iolet] -> Maybe Int
pointIolet point n iolets =
case iolets of
[] -> Nothing
(p:ps) ->
if pointInIolet point p
then Just n
else pointIolet point (n + 1) ps
argIoletCounter :: [String] -> ExprNode -> (Int, Int)
argIoletCounter labels _exprNode = (length labels, 1)
alignHeader :: TextBox -> BBox -> TextBox
alignHeader header bodybox =
let headerBB = tbBoxBB header
y0 = bbBottom headerBB
y1 = bbTop bodybox
x0 = bbLeft headerBB
x1 = bbLeft bodybox
in translate (x1 x0) (y1 y0) (tbSetWidth header (bbWidth bodybox))
alignFooter :: TextBox -> BBox -> TextBox
alignFooter footer bodybox =
let footerBB = tbBoxBB footer
y0 = bbTop footerBB
y1 = bbBottom bodybox
x0 = bbLeft footerBB
x1 = bbLeft bodybox
in translate (x1 x0) (y1 y0) (tbSetWidth footer (bbWidth bodybox))
frameNewWithLayout :: Style -> Position -> Double
-> Functoid -> Maybe [Value] -> FrameType
-> Node -> Env -> Maybe G.Node
-> (CanvFrame, FunctoidLayout)
frameNewWithLayout style (Position x y) z
functoid mvalues mode frameNode prevEnv mparent =
let headerText = functoidHeader functoid
vars = functoidArgNames functoid
footerText = buildFooterText vars mvalues
env = case mvalues of
Nothing ->
extendEnv [] [] prevEnv
Just values ->
extendEnv vars values prevEnv
layout0 = flayout style functoid env mvalues
headerTB0 = makeTextBox style headerText
footerTB0 = makeTextBox style footerText
Size lw lh = flayoutSize layout0
fwidth = maximum [tbWidth headerTB0, lw, tbWidth footerTB0]
headerTB1 = translate x y (widen headerTB0 fwidth)
(dx, dy) = (x hpad style, tbBottom headerTB1 vpad style)
layout1 = translate dx dy (flayoutWiden layout0 fwidth)
footerTB1 = translate x (flayoutBottom layout1)
(widen footerTB0 fwidth)
frameBox = BBox x y fwidth
(tbHeight headerTB0 + lh + tbHeight footerTB0)
frame = CanvFrame {cfHeader = headerTB1,
cfFooter = footerTB1,
cfVarNames = vars,
cfParent = mparent,
cfFrameNode = frameNode,
cfEnv = env,
cfBox = frameBox,
cfLevel = z,
cfFunctoid = functoid,
frameType = mode}
in (frame, layout1)
buildFooterText :: [String] -> Maybe [Value] -> String
buildFooterText vars mvalues =
let items =
case mvalues of
Nothing -> vars
Just [] -> vars
Just values ->
[var ++ " = " ++ rvalue |
(var, rvalue) <- zip vars (map repr values ++ repeat "?")]
in concat (intersperse ", " items)