packages feed

tinytools 0.1.0.4 → 0.1.0.5

raw patch · 51 files changed

+1652/−1230 lines, 51 filesdep −template-haskelldep ~reflexdep ~reflex-test-host

Dependencies removed: template-haskell

Dependency ranges changed: reflex, reflex-test-host

Files

ChangeLog.md view
@@ -1,6 +1,11 @@ # Changelog -## [Unreleased]+## [unreleased]++## [0.1.0.5]+- various internal improvements to how previews are staged and committed+- fix crash issue with undoing drags that contained folders+- fix several other crashes  ## [0.1.0.4] ### Changed
src/Potato/Data/Text/Zipper2.hs view
@@ -4,7 +4,7 @@ import           Prelude  import Control.Exception (assert)-import Control.Monad.State (evalState, forM, get, put)+import Control.Monad.State (evalState, get, put) import Data.Char (isSpace) import Data.Map (Map) import Data.Maybe (fromMaybe)
src/Potato/Flow.hs view
@@ -1,7 +1,7 @@ module Potato.Flow (-  module Potato.Flow.SElts+  module Potato.Flow.Serialization.Snake   , module Potato.Flow.Math-  , module Potato.Flow.SEltMethods+  , module Potato.Flow.Methods.SEltMethods   , module Potato.Flow.Types   , module Potato.Flow.Methods.Types   , module Potato.Flow.Render@@ -21,9 +21,9 @@ import           Potato.Flow.Controller import           Potato.Flow.Math import           Potato.Flow.Render-import           Potato.Flow.SEltMethods+import           Potato.Flow.Methods.SEltMethods import           Potato.Flow.Methods.Types-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.OwlHelpers import Potato.Flow.OwlWorkspace
src/Potato/Flow/Attachments.hs view
@@ -18,7 +18,7 @@  import           Potato.Flow.Math import           Potato.Flow.OwlItem-import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake import Potato.Flow.Methods.LineTypes  import Data.List (minimumBy)
src/Potato/Flow/BroadPhase.hs view
@@ -23,8 +23,8 @@ import           Potato.Flow.Owl import           Potato.Flow.OwlItem import           Potato.Flow.OwlState-import           Potato.Flow.SEltMethods-import           Potato.Flow.SElts+import           Potato.Flow.Methods.SEltMethods+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types  import qualified Data.IntMap.Strict        as IM
src/Potato/Flow/Controller/Goat.hs view
@@ -48,8 +48,8 @@ import           Potato.Flow.OwlState import           Potato.Flow.OwlWorkspace import           Potato.Flow.Render-import           Potato.Flow.SEltMethods-import           Potato.Flow.SElts+import           Potato.Flow.Methods.SEltMethods+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import  Potato.Flow.Preview  import Potato.Flow.Methods.LlamaWorks@@ -282,9 +282,11 @@   gs_1 = gs {       _goatState_screenRegion = x     }-  r = goat_setPan (_goatState_pan gs_1) gs_1+  r = goat_rerenderAfterMove gs_1+    + endoGoatCmdWSEvent :: WSEvent -> GoatState -> GoatState endoGoatCmdWSEvent wsev gs = goat_applyWSEvent WSEventType_Local_Refresh wsev gs @@ -303,23 +305,28 @@       -- NOTE _goatState_layersHandler gets set by goat_applyWSEvent during refresh     } -endoGoatCmdSetFocusedArea :: GoatFocusedArea -> GoatState -> GoatState-endoGoatCmdSetFocusedArea gfa goatState = r where+goat_setFocusedArea :: GoatFocusedArea -> GoatState -> GoatState+goat_setFocusedArea gfa goatState = r where   didchange = gfa /= _goatState_focusedArea goatState   goatstatewithnewfocus = goatState { _goatState_focusedArea = gfa }   noactionneeded = goatstatewithnewfocus   potatoHandlerInput = potatoHandlerInputFromGoatState goatState   r = if didchange && pHandlerName (_goatState_layersHandler goatState) == handlerName_layersRename+    -- special case to force confirmation on layersRename handler which is special because it does not generate a Preview     then let         goatState_afterAction = case pHandleKeyboard (_goatState_layersHandler goatState) potatoHandlerInput (KeyboardData KeyboardKey_Return []) of           Nothing -> noactionneeded-          Just pho -> goat_processLayersHandlerOutput pho goatstatewithnewfocus-      in assert (_goatState_focusedArea goatState == GoatFocusedArea_Layers) $ goatState_afterAction  +          Just pho -> traceShow "press enter" $ goat_processLayersHandlerOutput pho goatstatewithnewfocus+      in assert (_goatState_focusedArea goatState == GoatFocusedArea_Layers) $ goatState_afterAction     else noactionneeded  +endoGoatCmdSetFocusedArea :: GoatFocusedArea -> GoatState -> GoatState+endoGoatCmdSetFocusedArea = goat_setFocusedArea++ endoGoatCmdMouse :: LMouseData -> GoatState -> GoatState-endoGoatCmdMouse mouseData goatState = trace ("endomouse: " <> show mouseData) $ r where+endoGoatCmdMouse mouseData goatState = r where   sameSource = _mouseDrag_isLayerMouse (_goatState_mouseDrag goatState) == _lMouseData_isLayerMouse mouseData   mouseSourceFailure = _mouseDrag_state (_goatState_mouseDrag goatState) /= MouseDragState_Up && not sameSource   mouseDrag = case _mouseDrag_state (_goatState_mouseDrag goatState) of@@ -328,9 +335,9 @@      _                        ->  continueDrag mouseData (_goatState_mouseDrag goatState)   canvasDrag = toRelMouseDrag last_pFState (_goatState_pan goatState) mouseDrag-  goatState_withNewMouse = goatState {+  goatState_withFocusedArea = goat_setFocusedArea (if isLayerMouse then GoatFocusedArea_Layers else GoatFocusedArea_Canvas) goatState+  goatState_withNewMouse = goatState_withFocusedArea {       _goatState_mouseDrag = mouseDrag-      , _goatState_focusedArea = if isLayerMouse then GoatFocusedArea_Layers else GoatFocusedArea_Canvas     }   noChangeOutput = goatState_withNewMouse   -- TODO maybe split this case out to endoGoatCmdLayerMouse@@ -539,25 +546,32 @@     , _renderContext_renderedCanvasRegion = _goatState_renderedCanvas goatState   } -goat_setPan :: XY -> GoatState -> GoatState-goat_setPan (V2 dx dy) goatState = r where+goat_addPan :: XY -> GoatState -> GoatState+goat_addPan (V2 dx dy) goatState = r where   -- set the pan   -- render move   -- render selection   V2 cx0 cy0 = _goatState_pan goatState   next_pan = V2 (cx0+dx) (cy0 + dy)  +  gs_1 = goatState {+      _goatState_pan = next_pan+    }+  +  r = goat_rerenderAfterMove gs_1++goat_rerenderAfterMove :: GoatState -> GoatState+goat_rerenderAfterMove goatState = r where+  -- render move+  -- render selection   rc = renderContextFromGoatState goatState-  (rc_aftermove, _) = goat_renderCanvas_move rc next_pan (_goatState_screenRegion goatState)+  (rc_aftermove, _) = goat_renderCanvas_move rc (_goatState_pan goatState) (_goatState_screenRegion goatState)   rc_afterselection = goat_renderCanvas_selection rc_aftermove (_goatState_selection goatState)   r = goatState {-      _goatState_pan = next_pan-      , _goatState_renderedCanvas = _renderContext_renderedCanvasRegion rc_aftermove+      _goatState_renderedCanvas = _renderContext_renderedCanvasRegion rc_aftermove       , _goatState_renderedSelection = _renderContext_renderedCanvasRegion rc_afterselection     } -- computeCanvasSelection :: (HasCallStack) => GoatState -> CanvasSelection computeCanvasSelection goatState = r where   pfs = goatState_pFState goatState@@ -633,11 +647,11 @@   goat_processHandlerOutput_noSetHandler :: PotatoHandlerOutput -> GoatState -> GoatState-goat_processHandlerOutput_noSetHandler pho goatState = trace ("goat_processHandlerOutput_noSetHandler HANDLER OUTPUT: " <> show pho <> " CURRENT HANDLER: " <> show (_goatState_handler goatState)) $ r where+goat_processHandlerOutput_noSetHandler pho goatState = r where    r = case _potatoHandlerOutput_action pho of     HOA_Select x y -> goat_setSelection x y goatState-    HOA_Pan x -> goat_setPan x goatState+    HOA_Pan x -> goat_addPan x goatState     HOA_Layers x y -> goat_setLayersStateWithChangesFromToggleHide x y goatState     HOA_Preview p -> goat_applyWSEvent WSEventType_Local_NoRefresh (WSEApplyPreview dummyShepard dummyShift p) goatState     HOA_Nothing -> goatState@@ -650,16 +664,11 @@ goat_processCanvasHandlerOutput pho goatState = r where   canvasSelection = computeCanvasSelection goatState -  wasNoAction = handlerOutputAction_isNothing $ _potatoHandlerOutput_action pho-  -- TODO you can DELETE workspace stuff here once all handlers properly return commit actions when they are done-  -- you need this right now otherwise, for example, drag creating a box via BoxHandler will not commit after mouseup as it returns (handler: Nothing, action: HOA_Nothing)-  (next_handler, next_workspace) = case _potatoHandlerOutput_nextHandler pho of-    -- if we replaced the handler and there was HOA_Nothing, commit its local preview if there was one-    Nothing -> (makeHandlerFromSelection canvasSelection, if wasNoAction then maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState else _goatState_workspace goatState)-    Just x -> (x, _goatState_workspace goatState)+  next_handler = case _potatoHandlerOutput_nextHandler pho of+    Nothing -> makeHandlerFromSelection canvasSelection+    Just x -> x   goatState' = goatState {        _goatState_handler = next_handler -      , _goatState_workspace = next_workspace     }   r = goat_processHandlerOutput_noSetHandler pho $ goatState' @@ -761,7 +770,7 @@   -- NO that's not true because in those cases you can return an explicit commit action or you return HOA_Nothing which should also commit when the handler is replaced   (next_handler, next_workspace) = if (not . handlerActiveState_isActive) (pIsHandlerActive (_goatState_handler goatState_afterSetLayersState)) && not (goatState_hasLocalPreview goatState_afterSetLayersState)     -- if we replaced the handler, commit its local preview if there was one-    then trace "COMMIT: " $ (makeHandlerFromSelection next_canvasSelection, maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState_afterSetLayersState)+    then (makeHandlerFromSelection next_canvasSelection, maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState_afterSetLayersState)     else (_goatState_handler goatState_afterSetLayersState, _goatState_workspace goatState_afterSetLayersState)   goatState_afterSetHandler = goatState_afterSetLayersState {       _goatState_handler = next_handler
src/Potato/Flow/Controller/Handler.hs view
@@ -14,7 +14,7 @@ import           Potato.Flow.Render import           Potato.Flow.OwlState import           Potato.Flow.OwlWorkspace-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import qualified Potato.Flow.Preview as Preview  import qualified Potato.Data.Text.Zipper          as TZ@@ -51,8 +51,6 @@      , _potatoHandlerOutput_action                  :: HandlerOutputAction   } deriving (Show)--  instance Default PotatoHandlerOutput where   def = PotatoHandlerOutput {
src/Potato/Flow/Controller/Manipulator/Box.hs view
@@ -7,14 +7,7 @@ {-# OPTIONS_GHC -fno-warn-unused-record-wildcards #-}  -module Potato.Flow.Controller.Manipulator.Box (-  BoxHandleType(..)-  , BoxHandler(..)-  , BoxCreationType(..)-  , makeHandleBox-  , makeDeltaBox-  --, MouseManipulator(..)-) where+module Potato.Flow.Controller.Manipulator.Box where  import           Relude @@ -25,8 +18,8 @@ import           Potato.Flow.Controller.Manipulator.Common import           Potato.Flow.Controller.Types import           Potato.Flow.Math-import           Potato.Flow.SEltMethods-import           Potato.Flow.SElts+import           Potato.Flow.Methods.SEltMethods+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import           Potato.Flow.OwlItem import Potato.Flow.Owl@@ -156,6 +149,9 @@ data BoxHandler = BoxHandler {      _boxHandler_handle      :: BoxHandleType -- the current handle we are dragging++    -- TODO this is wrong as makeDragOperation does not always return a Llama+    -- rename this to mouseActive or something     , _boxHandler_undoFirst :: Bool      -- with this you can use same code for both create and manipulate (create the handler and immediately pass input to it)@@ -164,6 +160,9 @@      , _boxHandler_downOnLabel :: Bool ++    , _boxHandler_prevDeltaLBox :: Maybe DeltaLBox+   } deriving (Show)  makeDragDeltaBox :: BoxHandleType -> RelMouseDrag -> DeltaLBox@@ -178,18 +177,68 @@    r = makeDeltaBox bht boxRestrictedDelta +-- reduces the DeltaLBox such that the LBox does not invert+-- assumes LBox is canonical and that LBox is not already smaller than the desired constrained size+constrainDeltaLBox :: Int -> DeltaLBox -> LBox -> DeltaLBox+constrainDeltaLBox minsize d1@(DeltaLBox (V2 dx dy) (V2 dw dh)) d2@((LBox (V2 x y) (V2 w h))) = r where+  optuple e = (e, -e)++  (ndx, ndw) = if dx /= 0 +    then optuple (min (w-minsize) dx)+    else (dx, (max minsize (w+dw)) - w)+  +  (ndy, ndh) = if dy /= 0+    then optuple (min (h-minsize) dy)+    else (dy, (max minsize (h+dh)) - h)++  istranslateonly = dw == 0 && dh == 0+  +  r = if istranslateonly +    then d1 +    else DeltaLBox (V2 ndx ndy) (V2 ndw ndh)++-- OR you remove the delta portion that already modified the box in preview+makeDragOperationNew :: PotatoHandlerInput -> DeltaLBox -> Maybe Llama+makeDragOperationNew phi dbox = op where+  selection = transformableSelection phi+  selectionl = toList $ transformableSelection phi+  pfs = _potatoHandlerInput_pFState phi+  lboxes = fmap (\sowl -> _sEltDrawer_box (getDrawer . hasOwlItem_toOwlSubItem $ sowl) pfs) selectionl++  -- go through each element in selection and ensure that dbox does not invert that element+  -- DANGER you need to make sure you have sensible bounding box functions or you might put things in a non-resizeable state+  constraineddbox = foldl' (constrainDeltaLBox 1) dbox lboxes++  fmapfn sowl = makeSetLlama (rid, newselt) where+    rid = _superOwl_id sowl+    oldselt = superOwl_toSElt_hack sowl+    -- TODO don't use the CBoundingBox version of that funciton, it's deprecated, write a new one.+    newselt = modify_sElt_with_cBoundingBox True oldselt (CBoundingBox constraineddbox)++  op = if Seq.null selection+    then Nothing+    else Just $ makeCompositionLlama . toList $ (fmap fmapfn selectionl)++ makeDragOperation :: PotatoHandlerInput -> DeltaLBox -> Maybe Llama makeDragOperation phi dbox = op where   selection = transformableSelection phi+  selectionl = toList $ transformableSelection phi+  pfs = _potatoHandlerInput_pFState phi+  lboxes = fmap (\sowl -> _sEltDrawer_box (getDrawer . hasOwlItem_toOwlSubItem $ sowl) pfs) selectionl +  -- go through each element in selection and ensure that dbox does not invert that element+  -- DANGER you need to make sure you have sensible bounding box functions or you might put things in a non-resizeable state+  constraineddbox = foldl' (constrainDeltaLBox 0) dbox lboxes+   makeController _ = cmd where     cmd = CTagBoundingBox :=> (Identity $ CBoundingBox {-      _cBoundingBox_deltaBox = dbox+      _cBoundingBox_deltaBox = dbox -- constraineddbox     })    op = if Seq.null selection     then Nothing-    else Just $ makePFCLlama . OwlPFCManipulate $ IM.fromList (fmap (\s -> (_superOwl_id s, makeController s)) (toList selection))+    else Just $ makePFCLlama . OwlPFCManipulate $ IM.fromList (fmap (\s -> (_superOwl_id s, makeController s)) selectionl)  -- TODO split this handler in two handlers -- one for resizing selection (including boxes)@@ -201,6 +250,7 @@       , _boxHandler_creation = BoxCreationType_None       , _boxHandler_active = False       , _boxHandler_downOnLabel = False+      , _boxHandler_prevDeltaLBox = Nothing       -- TODO whatever       --, _boxHandler_wasDragged = False     }@@ -223,6 +273,8 @@     then True     else False +minusDeltaLBox :: DeltaLBox -> DeltaLBox -> DeltaLBox+minusDeltaLBox (DeltaLBox (V2 dx1 dy1) (V2 dw1 dh1)) (DeltaLBox (V2 dx2 dy2) (V2 dw2 dh2)) = DeltaLBox (V2 (dx1-dx2) (dy1-dy2)) (V2 (dw1-dw2) (dh1-dh2))  instance PotatoHandler BoxHandler where   pHandlerName _ = handlerName_box@@ -295,16 +347,18 @@         BoxCreationType_TextArea -> "<textarea>"         _ -> error "invalid BoxCreationType" +      mdd = makeDragDeltaBox _boxHandler_handle rmd        mop = case _boxHandler_creation of         x | x == BoxCreationType_Box || x == BoxCreationType_Text -> Just $ makeAddEltLlama _potatoHandlerInput_pFState newEltPos (OwlItem (OwlInfo nameToAdd) (OwlSubItemBox boxToAdd))         BoxCreationType_TextArea -> Just $ makeAddEltLlama _potatoHandlerInput_pFState newEltPos (OwlItem (OwlInfo nameToAdd) (OwlSubItemTextArea textAreaToAdd))-        _ -> makeDragOperation phi (makeDragDeltaBox _boxHandler_handle rmd)+        _ -> makeDragOperationNew phi (minusDeltaLBox mdd (fromMaybe (DeltaLBox 0 0) _boxHandler_prevDeltaLBox))        newbh = bh {           _boxHandler_undoFirst = True           -- if we drag, we are no longer in label case           , _boxHandler_downOnLabel = False+          , _boxHandler_prevDeltaLBox = Just mdd         }        -- NOTE, that if we did create a new box, it wil get auto selected and a new BoxHandler will be created for it@@ -357,7 +411,11 @@            -- This clears the handler and causes selection to regenerate a new handler.           -- Why do we do it this way instead of returning a handler? Not sure, doesn't matter.-          else Just def+          else Just def {+              _potatoHandlerOutput_action = HOA_Preview Preview_MaybeCommit+              -- doesn't work, see comments where _boxHandler_undoFirst is defined+              --_potatoHandlerOutput_action = if _boxHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing+            }          -- TODO if this was a text box creation case, consider entering text edit mode @@ -388,7 +446,7 @@       else case mmove of         Nothing -> Nothing         Just move -> Just r2 where-          mop = makeDragOperation phi move+          mop = makeDragOperationNew phi move           r2 = def {               _potatoHandlerOutput_nextHandler = Just $ SomePotatoHandler bh               , _potatoHandlerOutput_action = case mop of
src/Potato/Flow/Controller/Manipulator/BoxText.hs view
@@ -24,7 +24,7 @@ import           Potato.Flow.Controller.Input import           Potato.Flow.Controller.Manipulator.Common import           Potato.Flow.Math-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.Owl import Potato.Flow.OwlWorkspace
src/Potato/Flow/Controller/Manipulator/Common.hs view
@@ -16,7 +16,7 @@  import           Potato.Flow.Controller.Types import           Potato.Flow.Math-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.OwlItem import Potato.Flow.Owl import Potato.Flow.DebugHelpers
src/Potato/Flow/Controller/Manipulator/Layers.hs view
@@ -18,7 +18,7 @@ import           Potato.Flow.OwlItem import           Potato.Flow.OwlState import           Potato.Flow.OwlWorkspace-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import           Potato.Flow.Preview 
src/Potato/Flow/Controller/Manipulator/Line.hs view
@@ -24,7 +24,7 @@ import           Potato.Flow.OwlItem import           Potato.Flow.OwlState import           Potato.Flow.OwlWorkspace-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import Potato.Flow.Methods.LlamaWorks import Potato.Flow.Preview @@ -182,7 +182,7 @@  -- | -- see indexing information in 'whichSubSegmentDidClick'-getAnchorPosition :: Bool -> OwlPFState -> SAutoLine -> Int -> XY+getAnchorPosition :: (HasCallStack) => Bool -> OwlPFState -> SAutoLine -> Int -> XY getAnchorPosition offsetAttach pfs sline@SAutoLine {..} anchorindex = r where   mps = _sAutoLine_midpoints   endindex = length mps + 1@@ -191,7 +191,7 @@     else if anchorindex == endindex       then getEndpointPosition offsetAttach pfs sline False       else if anchorindex > 0 && anchorindex < endindex-        then case mps L.!! (anchorindex-1) of+        then case mps `debugBangBang` (anchorindex-1) of           SAutoLineConstraintFixed xy -> xy         else error $ "out of bounds anchor index " <> show anchorindex @@ -440,7 +440,9 @@             , _potatoHandlerOutput_action = HOA_Preview $ Preview (previewOperation_fromUndoFirst _autoLineEndPointHandler_undoFirst) op           }       -- no need to return AutoLineHandler, it will be recreated from selection by goat-      MouseDragState_Up -> Just def+      MouseDragState_Up -> Just def {+          _potatoHandlerOutput_action = if _autoLineEndPointHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing+        }       MouseDragState_Cancelled -> if _autoLineEndPointHandler_undoFirst then Just def { _potatoHandlerOutput_action = HOA_Preview Preview_Cancel } else Just def    pHandleKeyboard _ PotatoHandlerInput {..} _ = Nothing@@ -591,7 +593,9 @@           , _potatoHandlerOutput_action = HOA_Preview $ Preview (previewOperation_fromUndoFirst _autoLineMidPointHandler_undoFirst) event         }     -- no need to return AutoLineHandler, it will be recreated from selection by goat-    MouseDragState_Up -> Just def+    MouseDragState_Up -> Just def {+        _potatoHandlerOutput_action = if _autoLineMidPointHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing+      }     MouseDragState_Cancelled -> if _autoLineMidPointHandler_undoFirst then Just def { _potatoHandlerOutput_action = HOA_Preview Preview_Cancel } else Just def   pRenderHandler AutoLineMidPointHandler {..} phi@PotatoHandlerInput {..} = r where     boxes = maybeRenderPoints (False, False) _autoLineMidPointHandler_offsetAttach _autoLineMidPointHandler_midPointIndex phi@@ -640,9 +644,6 @@           }        MouseDragState_Up -> Just def {--          -- TODO Preview_Commit -           -- go back to AutoLineLabelHandler on completion           _potatoHandlerOutput_nextHandler = if not _autoLineLabelMoverHandler_undoFirst             -- if _autoLineLabelMoverHandler_undoFirst is false, this means we didn't drag at all, in which case go to label edit handler@@ -650,6 +651,8 @@               makeAutoLineLabelHandler_from_labelIndex _autoLineLabelMoverHandler_labelIndex _autoLineLabelMoverHandler_prevHandler phi rmd             -- TODO consider also going into edit handler after dragging an endpoint, but for now, just go back to the previous handler (which will be AutoLineHandler)             else Just (_autoLineLabelMoverHandler_prevHandler)++          , _potatoHandlerOutput_action = if _autoLineLabelMoverHandler_undoFirst then HOA_Preview Preview_Commit else HOA_Nothing         }        MouseDragState_Cancelled -> Just def {
src/Potato/Flow/Controller/Manipulator/Select.hs view
@@ -21,7 +21,7 @@ import           Potato.Flow.OwlItem import           Potato.Flow.OwlState import Potato.Flow.RenderCache-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake  import           Control.Exception                      (assert) import           Data.Default
src/Potato/Flow/Controller/Manipulator/TextArea.hs view
@@ -17,7 +17,7 @@ import           Potato.Flow.Math import           Potato.Flow.Owl import           Potato.Flow.OwlWorkspace-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.Preview 
src/Potato/Flow/Controller/Manipulator/TextInputState.hs view
@@ -5,7 +5,7 @@ import Relude  import           Potato.Flow.Math-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Controller.Input import           Potato.Flow.Controller.Handler 
src/Potato/Flow/Controller/OwlLayers.hs view
@@ -9,7 +9,7 @@  import           Potato.Flow.Controller.Types import           Potato.Flow.Types-import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake import           Potato.Flow.OwlItem import Potato.Flow.Owl import Potato.Flow.OwlState@@ -136,8 +136,12 @@     r = "LayersState: "       <> show _layersState_meta       <> "\nLayerEntries:\n"-      <> showFoldable _layersState_entries+      <> layerEntriesToPrettyText _layersState_entries +--instance Show LayersState where+--  show ls = T.unpack (potatoShow ls)++ data LockHideCollapseOp = LHCO_ToggleLock | LHCO_ToggleHide | LHCO_ToggleCollapse deriving (Show)  alterWithDefault :: (Eq a, Default a) => (a -> a) -> REltId -> REltIdMap a -> REltIdMap a@@ -199,7 +203,7 @@   le = Seq.index _layersState_entries lepos   lerid = layerEntry_rEltId le   ledepth = layerEntry_depth le-  childFrom nextLayerEntry = layerEntry_depth nextLayerEntry /= ledepth+  childFrom nextLayerEntry = layerEntry_depth nextLayerEntry > ledepth   -- visible children of le   childles = Seq.takeWhileL childFrom . Seq.drop (lepos+1) $ _layersState_entries   -- everything before le
src/Potato/Flow/Controller/Types.hs view
@@ -21,7 +21,7 @@  import           Potato.Flow.Math import           Potato.Flow.Owl-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types  import           Data.Aeson
src/Potato/Flow/DebugHelpers.hs view
@@ -25,8 +25,8 @@   then b   else error $ "assert failed:\n" <> potatoShow a -traceWith :: (a -> String) -> a -> a-traceWith f x = trace (f x) x+traceIdWith :: (a -> String) -> a -> a+traceIdWith f x = trace (f x) x  traceShowIdWithLabel :: (Show a) => String -> a -> a traceShowIdWithLabel label x = trace (label <> ": " <> show x) x
src/Potato/Flow/Deprecated/State.hs view
@@ -40,8 +40,8 @@  import           Potato.Flow.Deprecated.Layers import           Potato.Flow.Math-import           Potato.Flow.SEltMethods-import           Potato.Flow.SElts+import           Potato.Flow.Methods.SEltMethods+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types  import           Control.Exception       (assert)
src/Potato/Flow/Llama.hs view
@@ -10,7 +10,7 @@ import           Potato.Flow.Owl import           Potato.Flow.OwlItem import           Potato.Flow.OwlState-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types  
src/Potato/Flow/Methods/LineDrawer.hs view
@@ -38,7 +38,8 @@ import           Potato.Flow.Methods.Types import           Potato.Flow.Owl import           Potato.Flow.OwlItem-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake+import Potato.Flow.DebugHelpers  import qualified Data.List                      as L import qualified Data.List.Index                as L@@ -691,13 +692,13 @@ -- the SAutoLineLabel does not have to be one of labels contained in the SAutoLine _sAutoLine_labels -- which is useful for positioning SAutoLineLabel before adding them to SAutoLine -- however the midpoint index in SAutoLineLabel is expected to map correctly to the SAutoLine-getSAutoLineLabelPosition :: (HasOwlTree a) => a -> SAutoLine -> SAutoLineLabel -> XY+getSAutoLineLabelPosition :: (HasCallStack, HasOwlTree a) => a -> SAutoLine -> SAutoLineLabel -> XY getSAutoLineLabelPosition ot sal sall = getSAutoLineLabelPositionFromLineAnchorsForRender lar sal sall where-  lar = sAutoLine_to_lineAnchorsForRenderList ot sal L.!! (_sAutoLineLabel_index sall)+  lar = sAutoLine_to_lineAnchorsForRenderList ot sal `debugBangBang` (_sAutoLineLabel_index sall)  -- get SAutoLineLabel positions in visual order (which may not be the same as logical order) -- return includes SAutoLineLabel and its original logical index for convenience-getSortedSAutoLineLabelPositions :: (HasOwlTree a) => a -> SAutoLine -> [(XY, Int, SAutoLineLabel)]+getSortedSAutoLineLabelPositions :: (HasCallStack, HasOwlTree a) => a -> SAutoLine -> [(XY, Int, SAutoLineLabel)] getSortedSAutoLineLabelPositions ot sal@SAutoLine {..} = r where   sortfn (_,a) (_,b) = case compare (_sAutoLineLabel_index a) (_sAutoLineLabel_index b) of     EQ -> case _sAutoLineLabel_position a of@@ -708,7 +709,7 @@    larlist = sAutoLine_to_lineAnchorsForRenderList ot sal -  r = fmap (\(i, sall) -> (internal_getSAutoLineLabelPosition (larlist L.!! _sAutoLineLabel_index sall) sal sall, i, sall)) sortedlls+  r = fmap (\(i, sall) -> (internal_getSAutoLineLabelPosition (larlist `debugBangBang` _sAutoLineLabel_index sall) sal sall, i, sall)) sortedlls   -- takes a list of line anchors as returned by sAutoLine_to_lineAnchorsForRenderList and a position
src/Potato/Flow/Methods/LineTypes.hs view
@@ -5,7 +5,7 @@ import           Relude  import           Potato.Flow.Math-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake  import Linear.Vector ((^*)) import Linear.Matrix (M22, (!*))
src/Potato/Flow/Methods/LlamaWorks.hs view
@@ -14,7 +14,7 @@ import Potato.Flow.OwlWorkspace import Potato.Flow.OwlState import Potato.Flow.Llama-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types  import           Control.Exception (assert)
+ src/Potato/Flow/Methods/SEltMethods.hs view
@@ -0,0 +1,384 @@+{-# LANGUAGE RecordWildCards #-}++module Potato.Flow.Methods.SEltMethods where++import           Relude++import           Potato.Flow.Math+import           Potato.Flow.Methods.LineDrawer+import           Potato.Flow.Methods.TextCommon+import           Potato.Flow.Methods.Types+import           Potato.Flow.Owl+import Potato.Flow.RenderCache+import           Potato.Flow.OwlItem+import           Potato.Flow.Serialization.Snake+import           Potato.Flow.Types++import           Data.Dependent.Sum             (DSum ((:=>)))+import qualified Data.Map                       as Map+import           Data.Maybe                     (fromJust)+import qualified Data.Text                      as T+import qualified Potato.Data.Text.Zipper        as TZ+import Control.Exception (assert)+++-- DisplayLines tag is Int, 0 for no cursor 1 for cursor+noTrailngCursorDisplayLines :: Int -> TextAlign -> T.Text -> TZ.DisplayLines Int+noTrailngCursorDisplayLines width alignment text = r where+  -- force TZ to top so that displayLinesWithAlignment doesn't create trailing space for cursor+  tz = TZ.top (TZ.fromText text)++  -- hack to get rid of trailing cursor if text is ""+  r = if T.null text+    then TZ.DisplayLines {+        _displayLines_spans = []+        , _displayLines_offsetMap = Map.empty+        , _displayLines_cursorPos   = (0,0)+      }+    else TZ.displayLinesWithAlignment (convertTextAlignToTextZipperTextAlignment alignment) width 0 1 tz++makeDisplayLinesFromSBox :: SBox -> TZ.DisplayLines Int+makeDisplayLinesFromSBox sbox = r where+  alignment = _textStyle_alignment . _sBoxText_style . _sBox_text $ sbox+  text = _sBoxText_text . _sBox_text $ sbox+  LBox _ (V2 width' _) = _sBox_box sbox+  width = case _sBox_boxType sbox of+    SBoxType_BoxText   -> max 0 (width'-2)+    SBoxType_NoBoxText -> width'+    _                  -> error "wrong type"+  r = noTrailngCursorDisplayLines width alignment text++++-- TODO DELETE use doesOwlSubItemIntersectBox instead+doesSEltIntersectBox_DEPRECATED :: LBox -> SElt -> Bool+doesSEltIntersectBox_DEPRECATED lbox selt = case selt of+  SEltNone                     -> False+  SEltFolderStart              -> False+  SEltFolderEnd                -> False+  SEltBox x                    -> does_lBox_intersect_include_zero_area lbox (_sBox_box x)+  SEltTextArea x                   -> does_lBox_intersect_include_zero_area lbox (_sTextArea_box x)+  -- TODO this is wrong, do it correctly...+  -- we use does_lBox_intersect since it's impossible for a SAutoLine to have zero sized box+  SEltLine sline -> does_lBox_intersect lbox (fromJust $ getSEltBox_naive (SEltLine sline))++doesSEltIntersectPoint :: XY -> SElt -> Bool+doesSEltIntersectPoint pos selt = doesSEltIntersectBox_DEPRECATED (LBox pos (V2 1 1)) selt++getSEltSuperStyle :: SElt -> Maybe SuperStyle+getSEltSuperStyle selt = case selt of+  SEltBox SBox {..}       -> Just _sBox_superStyle+  SEltLine SAutoLine {..} -> Just _sAutoLine_superStyle+  _                       -> Nothing++getSEltLabelSuperStyle :: SEltLabel -> Maybe SuperStyle+getSEltLabelSuperStyle (SEltLabel _ x) = getSEltSuperStyle x++getSEltLineStyle :: SElt -> Maybe LineStyle+getSEltLineStyle selt = case selt of+  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyle+  _                       -> Nothing++getSEltLineStyleEnd :: SElt -> Maybe LineStyle+getSEltLineStyleEnd selt = case selt of+  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyleEnd+  _                       -> Nothing++getSEltLabelLineStyle :: SEltLabel -> Maybe LineStyle+getSEltLabelLineStyle (SEltLabel _ x) = getSEltLineStyle x++getSEltLabelLineStyleEnd :: SEltLabel -> Maybe LineStyle+getSEltLabelLineStyleEnd (SEltLabel _ x) = getSEltLineStyleEnd x+++getSEltBoxTextStyle :: SElt -> Maybe TextStyle+getSEltBoxTextStyle = \case+  SEltBox SBox {..}         -> Just . _sBoxText_style $ _sBox_text+  _ -> Nothing++getSEltLabelBoxTextStyle :: SEltLabel -> Maybe TextStyle+getSEltLabelBoxTextStyle (SEltLabel _ x) = getSEltBoxTextStyle x++getSEltBoxType :: SElt -> Maybe SBoxType+getSEltBoxType = \case+  SEltBox SBox {..} -> Just _sBox_boxType+  _ -> Nothing++getSEltLabelBoxType :: SEltLabel -> Maybe SBoxType+getSEltLabelBoxType (SEltLabel _ x) = getSEltBoxType x++sBox_drawer :: SBox -> SEltDrawer+sBox_drawer sbox@SBox {..} = r where+  CanonicalLBox _ _ lbox@(LBox (V2 x y) (V2 w h)) = canonicalLBox_from_lBox _sBox_box++  titlewidth = max 0 (w-2)++  fillfn _ = case _superStyle_fill _sBox_superStyle of+    FillStyle_Simple c -> Just c+    FillStyle_Blank    -> Nothing++  rfntext (V2 x' y') = case _sBox_boxType of+    SBoxType_Box -> Nothing+    SBoxType_NoBox -> Nothing+    _ -> outputChar where++      -- 😰😰😰 for now we just do the below for every cell+      dl = makeDisplayLinesFromSBox sbox++      offs = case _sBox_boxType of+        SBoxType_NoBoxText -> (0,0)+        _                  -> (1,1)++      outputChar = displayLinesToChar (x, y) dl (x', y') offs++  -- TODO test+  rfnlabel (V2 x' y') = case _sBoxTitle_title _sBox_title of+    Nothing -> Nothing+    Just title -> outputChar where+      -- TODO we want to crop instead of wrap here+      -- however using infinite width trick will break AlignRight :(+      dl = noTrailngCursorDisplayLines titlewidth (_sBoxTitle_align _sBox_title) title+      -- note that y' will ultimately resolve to a yindex of 0 inside of displayLinesToChar+      outputChar = displayLinesToChar (x, y) dl (x', y') (1,0)++  rfnnoborder pt+    | not (does_lBox_contains_XY lbox pt) = Nothing+    | otherwise = case rfntext pt of+      -- 'Just Nothing' means don't use fill char (this happens when there are wide chars)+      Just mx -> mx+      Nothing -> fillfn pt++  rfnborder pt@(V2 x' y')+    | not (does_lBox_contains_XY lbox pt) = Nothing+    | w == 1 && h == 1 = _superStyle_point _sBox_superStyle+    | w == 1 = _superStyle_vertical _sBox_superStyle+    | h == 1 = _superStyle_horizontal _sBox_superStyle+    | x' == x && y' == y = _superStyle_tl _sBox_superStyle+    | x' == x && y' == y+h-1 = _superStyle_bl _sBox_superStyle+    | x' == x+w-1 && y' == y = _superStyle_tr _sBox_superStyle+    | x' == x+w-1 && y' == y+h-1 = _superStyle_br _sBox_superStyle+    | x' == x || x' == x+w-1 = _superStyle_vertical _sBox_superStyle+    -- label shows up at top horizontal portion+    | y' == y = case rfnlabel pt of+      Nothing    -> _superStyle_horizontal _sBox_superStyle+      Just pchar -> pchar+    | y' == y+h-1 = _superStyle_horizontal _sBox_superStyle+    | otherwise = rfnnoborder pt++  r = SEltDrawer {+      _sEltDrawer_box = const lbox+      , _sEltDrawer_renderFn = \_ -> case _sBox_boxType of+        SBoxType_NoBoxText -> rfnnoborder+        SBoxType_NoBox     -> rfnnoborder+        _                  -> rfnborder+      +      -- TODO +      , _sEltDrawer_maxCharWidth = 1+    }++sTextArea_drawer :: STextArea -> SEltDrawer+sTextArea_drawer STextArea {..} = r where++  lbox@(LBox p _) = _sTextArea_box++  renderfn p' = outputChar where+    inbounds = does_lBox_contains_XY lbox p'+    outputChar = if inbounds+      then case Map.lookup (p' - p) _sTextArea_text of+        Nothing -> if _sTextArea_transparent+          then Nothing+          else Just ' '+        Just c -> Just c+      else Nothing++  r = SEltDrawer {+      _sEltDrawer_box = const lbox+      , _sEltDrawer_renderFn = \_ -> renderfn++      -- TODO+      , _sEltDrawer_maxCharWidth = 1+    }++-- NOTE that there is not a 1-1 mapping between `OwlSubItem` and `OwlItemCache` as the `OwlItemCache` is dependent on the OwlTree+-- this function assumes that you are requesting the drawer with the intent of passing in an OwlTree+-- TODO it would have been better for SEltDrawer to be created based on an OwlTree rather than return a function that takes an OwlTree+getDrawerWithCache :: OwlSubItem -> Maybe OwlItemCache -> SEltDrawer+getDrawerWithCache osubitem mcache = case osubitem of +  OwlSubItemNone        -> nilDrawer+  OwlSubItemFolder _ -> nilDrawer+  OwlSubItemBox sbox    -> sBox_drawer sbox+  OwlSubItemLine sline -> case mcache of +    Just (OwlItemCache_Line lars _) -> sSimpleLineNewRenderFn sline (Just lars)+    Nothing -> sSimpleLineNewRenderFn sline Nothing+    _ -> assert False (sSimpleLineNewRenderFn sline Nothing)+  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea++-- TODO pass in cache here+getDrawer :: OwlSubItem -> SEltDrawer+getDrawer = \case+  OwlSubItemNone        -> nilDrawer+  OwlSubItemFolder _ -> nilDrawer+  OwlSubItemBox sbox    -> sBox_drawer sbox+  OwlSubItemLine sline -> sSimpleLineNewRenderFn sline Nothing+  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea+  {-+  where+    potatoDrawer = SEltDrawer {+        _sEltDrawer_box = const $ fromJust (getSEltBox_naive selt)+        , _sEltDrawer_renderFn =  makePotatoRenderer $ fromJust (getSEltBox_naive selt)+      }+  -}++getDrawerFromSEltForTest :: SElt -> SEltDrawer+getDrawerFromSEltForTest = getDrawer . sElt_to_owlSubItem++updateOwlSubItemCache :: (HasOwlTree a) => a -> OwlSubItem -> Maybe OwlItemCache+updateOwlSubItemCache ot x = r where+  r = case x of+    -- TODO use sAutoLine_to_lineAnchorsForRenderList here instead+    (OwlSubItemLine sline) -> cache where+      cache = Just $ OwlItemCache_Line (sSimpleLineNewRenderFnComputeCache ot sline) prerender+      seltdrawer = getDrawerWithCache x cache+      prerender = makePreRender ot seltdrawer +    _ -> Just $ OwlItemCache_Generic prerender where+      seltdrawer = getDrawerWithCache x Nothing+      prerender = makePreRender ot seltdrawer +++-- TODO move modify methods to another file++modify_sAutoLineConstraint_with_cBoundingBox :: Bool -> SAutoLineConstraint -> CBoundingBox -> SAutoLineConstraint+modify_sAutoLineConstraint_with_cBoundingBox isDo constraint CBoundingBox {..} = case constraint of+  SAutoLineConstraintFixed xy -> SAutoLineConstraintFixed $ modifyDelta isDo xy (_deltaLBox_translate _cBoundingBox_deltaBox)++modify_sElt_with_cBoundingBox :: Bool -> SElt -> CBoundingBox -> SElt+modify_sElt_with_cBoundingBox isDo selt cbb@CBoundingBox {..} = case selt of+  SEltBox sbox  -> SEltBox $ sbox {+      _sBox_box = modifyDelta isDo (_sBox_box sbox) _cBoundingBox_deltaBox+    }+  -- TODO handle resize parameter+  SEltLine sline@SAutoLine {..} -> SEltLine $ sline {+      _sAutoLine_start = modifyDelta isDo _sAutoLine_start+        (_deltaLBox_translate _cBoundingBox_deltaBox)+      , _sAutoLine_end = modifyDelta isDo _sAutoLine_end+        (_deltaLBox_translate _cBoundingBox_deltaBox)+      , _sAutoLine_midpoints = fmap (\slc -> modify_sAutoLineConstraint_with_cBoundingBox isDo slc cbb) _sAutoLine_midpoints+    }+  SEltTextArea stext -> SEltTextArea $ stext {+      _sTextArea_box     = modifyDelta isDo (_sTextArea_box stext) _cBoundingBox_deltaBox+    }+  x          -> x++modify_sElt_with_cSuperStyle :: Bool -> SElt -> CSuperStyle -> SElt+modify_sElt_with_cSuperStyle isDo selt (CSuperStyle style) = case selt of+  SEltBox sbox -> SEltBox $ sbox {+      _sBox_superStyle = modifyDelta isDo (_sBox_superStyle sbox) style+    }+  -- TODO handle resize parameter+  SEltLine sline -> SEltLine $ sline {+      _sAutoLine_superStyle = modifyDelta isDo (_sAutoLine_superStyle sline) style+    }+  _ -> error $ "Controller - SElt type mismatch: CTagSuperStyle - " <> show selt+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk+  --x -> x++-- TODO DELETE use llama instead+modify_sElt_with_cLineStyle :: Bool -> SElt -> CLineStyle -> SElt+modify_sElt_with_cLineStyle isDo selt (CLineStyle style) = case selt of+  SEltLine sline -> SEltLine $ sline {+      _sAutoLine_lineStyle = modifyDelta isDo (_sAutoLine_lineStyle sline) style+    }+  _ -> error $ "Controller - SElt type mismatch: CTagLineStyle - " <> show selt+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk+  --x -> x++modify_sElt_with_cTextStyle :: Bool -> SElt -> CTextStyle -> SElt+modify_sElt_with_cTextStyle isDo selt (CTextStyle style) = case selt of+  SEltBox sbox -> SEltBox $ sbox {+      _sBox_text = (_sBox_text sbox) {+          _sBoxText_style = modifyDelta isDo (_sBoxText_style . _sBox_text $ sbox) style+        }+    }+  _ -> error $ "Controller - SElt type mismatch: CTagBoxTextStyle - " <> show selt+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk+  --x -> x++modify_sEltBox_label_with_cTextAlign :: Bool -> SElt -> CTextAlign -> SElt+modify_sEltBox_label_with_cTextAlign isDo selt (CTextAlign align) = case selt of+  SEltBox sbox -> SEltBox $ sbox {+      _sBox_title = sboxtitle {+          _sBoxTitle_align = modifyDelta isDo (_sBoxTitle_align sboxtitle) align+        }+    } where+      sboxtitle = _sBox_title sbox+  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt+  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk+  --x -> x++modify_sEltBox_label_with_cMaybeText :: Bool -> SElt -> CMaybeText -> SElt+modify_sEltBox_label_with_cMaybeText isDo selt (CMaybeText text) = case selt of+  SEltBox sbox -> SEltBox $ sbox {+      _sBox_title = sboxtitle {+          _sBoxTitle_title = modifyDelta isDo (_sBoxTitle_title sboxtitle) text+        }+    } where+      sboxtitle = _sBox_title sbox+  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt++modify_sEltTextArea_with_cTextArea :: Bool -> SElt -> CTextArea -> SElt+modify_sEltTextArea_with_cTextArea isDo selt (CTextArea dt) = case selt of+  SEltTextArea stextarea -> SEltTextArea $ stextarea {+      _sTextArea_text = modifyDelta isDo (_sTextArea_text stextarea) dt+    }+  _ -> error $ "Controller - SElt type mismatch: CTagTextArea - " <> show selt++modify_sEltTextArea_with_cTextAreaToggle :: Bool -> SElt -> CTextAreaToggle -> SElt+modify_sEltTextArea_with_cTextAreaToggle isDo selt (CTextAreaToggle toggle) = case selt of+  -- double toggle is idempotent but we disallow it for now+  SEltTextArea _ -> error $ "Controller - SElt type mismatch: CTagTextAreaToggle - " <> show selt+  x -> modifyDelta isDo x toggle+++modifyDelta :: (Delta x dx) => Bool -> x ->  dx -> x+modifyDelta isDo x dx = if isDo+  then plusDelta x dx+  else minusDelta x dx++updateFnFromController :: Bool -> Controller -> (SEltLabel -> SEltLabel)+updateFnFromController isDo = \case+  (CTagRename :=> Identity d) -> \seltl -> modifyDelta isDo seltl d+  (CTagLine :=> Identity d) -> \(SEltLabel sname selt) -> case selt of+    SEltLine s -> SEltLabel sname (SEltLine $ modifyDelta isDo s d)+    _ -> error $ "Controller - SElt type mismatch: CTagLine - " <> show selt+  (CTagBoxText :=> Identity d) -> \(SEltLabel sname selt) -> case selt of+    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)+    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt+  (CTagBoxType :=> Identity d) -> \(SEltLabel sname selt) -> case selt of+    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)+    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt+  (CTagBoundingBox :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sElt_with_cBoundingBox isDo selt d)+  (CTagSuperStyle :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sElt_with_cSuperStyle isDo selt d)+  (CTagLineStyle :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sElt_with_cLineStyle isDo selt d)+  (CTagBoxTextStyle :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sElt_with_cTextStyle isDo selt d)++  (CTagBoxLabelAlignment :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sEltBox_label_with_cTextAlign isDo selt d)+  (CTagBoxLabelText :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sEltBox_label_with_cMaybeText isDo selt d)++  (CTagTextArea :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sEltTextArea_with_cTextArea isDo selt d)+  (CTagTextAreaToggle :=> Identity d) -> \(SEltLabel sname selt) ->+    SEltLabel sname (modify_sEltTextArea_with_cTextAreaToggle isDo selt d)+++-- | helper method used in copy pasta+offsetSEltTree :: XY -> SEltTree -> SEltTree+offsetSEltTree offset stree = r where+  op = CBoundingBox (DeltaLBox offset 0)+  offsetfn (rid, seltl) = (rid, updateFnFromController True (CTagBoundingBox :=> Identity op) seltl)+  r = fmap offsetfn stree
src/Potato/Flow/Methods/TextCommon.hs view
@@ -6,7 +6,7 @@  import           Relude -import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake  import qualified Data.Map as Map import qualified Data.Text                      as T
src/Potato/Flow/Methods/Types.hs view
@@ -11,7 +11,7 @@  import           Potato.Flow.Math import           Potato.Flow.Owl-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake  import qualified Data.Text         as T 
src/Potato/Flow/Owl.hs view
@@ -16,7 +16,7 @@ import qualified Data.IntSet as IS import qualified Data.Text as T import Potato.Flow.OwlItem-import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake import Potato.Flow.Types import Potato.Flow.DebugHelpers @@ -611,6 +611,7 @@               (rpass, rmsg) = (rpass1 && rpass2, rmsg2)         r = Seq.foldlWithIndex foldfn (True, msg0) kiddos +-- oops, this should have been -1 but it's 0 so we start indexing stuff at 1 🤷🏼‍♀️ owlTree_maxId :: OwlTree -> REltId owlTree_maxId s = maybe 0 fst (IM.lookupMax (_owlTree_mapping s)) @@ -781,21 +782,25 @@   where     r = owlParliament_toSuperOwlParliament od . OwlParliament $ _owlTree_topOwls -owlTree_removeREltId :: REltId -> OwlTree -> OwlTree-owlTree_removeREltId rid od = owlTree_removeSuperOwl (owlTree_mustFindSuperOwl od rid) od+owlTree_removeREltId :: Bool -> REltId -> OwlTree -> OwlTree+owlTree_removeREltId keepChildren rid od = owlTree_removeSuperOwl keepChildren (owlTree_mustFindSuperOwl od rid) od -owlTree_removeSuperOwl :: SuperOwl -> OwlTree -> OwlTree-owlTree_removeSuperOwl sowl OwlTree {..} = r+owlTree_removeSuperOwl :: Bool -> SuperOwl -> OwlTree -> OwlTree+owlTree_removeSuperOwl keepChildren sowl OwlTree {..} = r   where     -- remove the element itself     newMapping'' = IM.delete (_superOwl_id sowl) _owlTree_mapping -    -- remove all children recursively++    -- remove all children recursively if desired+    -- NOTE if keepChildren is true, this will put the OwlTree in an invalid state (presumably so that you can fix it later)     removeEltWithoutAdjustMommyFn rid mapping = case IM.lookup rid mapping of       Nothing -> error $ errorMsg_owlMapping_lookupFail mapping rid       Just (_, OwlItem _ (OwlSubItemFolder kiddos)) -> foldr removeEltWithoutAdjustMommyFn (IM.delete rid mapping) kiddos       Just _ -> IM.delete rid mapping-    newMapping' = case _superOwl_elt sowl of+    newMapping' = if keepChildren+      then newMapping''+      else case _superOwl_elt sowl of       OwlItem _ (OwlSubItemFolder kiddos) -> foldr removeEltWithoutAdjustMommyFn newMapping'' kiddos       _ -> newMapping'' @@ -841,7 +846,7 @@     -- NOTE, that _owlItemMeta_position in sowls may be incorrect in the middle of this fold     -- this forces us to do linear search in the owlTree_removeSuperOwl call rather than use sibling position as index into children D:     -- TODO fix by always sort from right to left to avoid this-    removedOd = foldl (\acc sowl -> owlTree_removeSuperOwl sowl acc) od sowls+    removedOd = foldl (\acc sowl -> owlTree_removeSuperOwl False sowl acc) od sowls      -- WIP start     -- ??? I can't remember what this is anymore, did I aready fix this or no? Pretty sure I can just delet all of this@@ -872,10 +877,12 @@       . filter ((== _owlSpot_parent) . _owlItemMeta_parent . _superOwl_meta)       . toList       $ sowls+     -- list of all siblings on the spot we are dragging to     origSiblings = fromMaybe (error "expected siblings") $ if _owlSpot_parent == noOwl       then mommyOwl_kiddos $ od       else mommyOwl_kiddos $ owlTree_mustFindSuperOwl od _owlSpot_parent+     -- now we will walk from right to left picking out the first elt that is on or after the target spot we are dragging to (_owlSpot_leftSibling) and isn't in the removed list     findPos ::       REltId -- ^ original _owlSpot_leftSibling@@ -896,9 +903,11 @@       else if x == y         then findPos targetrid xs ys past         else findPos targetrid (x:xs) ys past+     newLeftSibling = case _owlSpot_leftSibling of       Nothing -> Nothing       Just target -> findPos target (reverse $ toList removed) (reverse $ toList origSiblings) False+     correctedSpot = spot { _owlSpot_leftSibling = newLeftSibling}      selttree = superOwlParliament_toSEltTree od sop@@ -961,16 +970,28 @@       x -> x    -- go from left to right such that parents/left siblings are added first-  r = mapAccumL mapaccumlfn od0 $ toList $ fmap (\sowl -> (owlTree_owlItemMeta_toOwlSpot miniot (_superOwl_meta sowl), sowl)) (owliterateall miniot)+  (newot, addedSowls) = mapAccumL mapaccumlfn od0 $ toList $ fmap (\sowl -> (owlTree_owlItemMeta_toOwlSpot miniot (_superOwl_meta sowl), sowl)) (owliterateall miniot)+  r = (newot, addedSowls) --- parents NOT allowed :O-internal_owlTree_addOwlItem :: OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, SuperOwl)-internal_owlTree_addOwlItem OwlSpot {..} rid oitem OwlTree {..} = assert nochildrenifaddingfolder r++internal_owlTree_recalculateChildrenDepths_recursive :: OwlTree -> REltId -> OwlTree+internal_owlTree_recalculateChildrenDepths_recursive ot rid = r_0 where+  sowl = owlTree_mustFindSuperOwl ot rid+  adjustdepthfn (oem, oitem) = (,oitem) $ oem { _owlItemMeta_depth = _owlItemMeta_depth (_superOwl_meta sowl) + 1 }+  r_0 = case _superOwl_elt sowl of+    OwlItem oinfo (OwlSubItemFolder kiddos) -> r_1 where+      -- update the depth of all its kiddos+      omwithadjusteddepth = foldr (\kid om -> IM.adjust adjustdepthfn kid om) (_owlTree_mapping ot) kiddos+      newot_0 = ot {_owlTree_mapping = omwithadjusteddepth}+      -- recurse+      r_1 = foldl (internal_owlTree_recalculateChildrenDepths_recursive) newot_0 kiddos+    x -> ot++-- parents allowed ONLY if all the children already exist in the tree (as orphans) or it will crash+-- returns the added element for convenience but NOT its children +internal_owlTree_addOwlItem :: (HasCallStack) => OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, SuperOwl)+internal_owlTree_addOwlItem OwlSpot {..} rid oitem OwlTree {..} = r   where-    -- if we're adding a folder (in the normal case), ensure it has no children-    nochildrenifaddingfolder = case oitem of-      OwlItem _ (OwlSubItemFolder kiddos) -> Seq.null kiddos-      _ -> True      -- first add the OwlItem to the mapping     meta =@@ -987,8 +1008,8 @@      newMapping' = IM.insertWithKey (\k _ ov -> error ("key " <> show k <> " already exists with value " <> show ov)) rid (meta, oitem) _owlTree_mapping -    -- modify kiddos of the parent we are adding to-    modifyKiddos kiddos = Seq.insertAt position rid kiddos+    -- update siblings in the parent we are adding to, leaving the tree in an invalid state (siblings have wrong position index in their OwlItemMeta)+    updateSiblings kiddos = Seq.insertAt position rid kiddos       where         position = case _owlSpot_leftSibling of           Nothing -> 0@@ -996,32 +1017,42 @@             Nothing -> error $ "expected to find leftmost sibling " <> show leftsibrid <> " in " <> show kiddos             Just x -> x + 1     adjustfn (oem, oitem') = case oitem' of-      OwlItem oinfo (OwlSubItemFolder kiddos) -> (oem, OwlItem oinfo (OwlSubItemFolder (modifyKiddos kiddos)))+      OwlItem oinfo (OwlSubItemFolder kiddos) -> (oem, OwlItem oinfo (OwlSubItemFolder (updateSiblings kiddos)))       _ -> error $ "expected OwlItemFolder"     newMapping = case _owlSpot_parent of       x | x == noOwl -> newMapping'-      _ -> IM.adjust adjustfn _owlSpot_parent newMapping'+      _ -> assert (IM.member _owlSpot_parent newMapping') $ IM.adjust adjustfn _owlSpot_parent newMapping'     -- or top owls if there is no parent     newTopOwls = case _owlSpot_parent of-      x | x == noOwl -> modifyKiddos _owlTree_topOwls+      x | x == noOwl -> updateSiblings _owlTree_topOwls       _ -> _owlTree_topOwls -    r' =+    tree_1 =       OwlTree         { _owlTree_mapping = newMapping,           _owlTree_topOwls = newTopOwls         } -    newtree = internal_owlTree_reorgKiddos r' _owlSpot_parent+    -- correct the sibling indices+    tree_2 = internal_owlTree_reorgKiddos tree_1 _owlSpot_parent -    newsowl = owlTree_mustFindSuperOwl newtree rid+    -- correct the children depths+    tree_3 = internal_owlTree_recalculateChildrenDepths_recursive tree_2 rid -    r = (newtree, newsowl)+    newsowl = owlTree_mustFindSuperOwl tree_3 rid --- OwlItem must not be a parent-owlTree_addOwlItem :: OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, SuperOwl)-owlTree_addOwlItem = internal_owlTree_addOwlItem+    r = (tree_3, newsowl) +-- NOTE parents are allowed here IF all the children already exist in the tree+-- returns the added element and all its children+owlTree_addOwlItem :: OwlSpot -> REltId -> OwlItem -> OwlTree -> (OwlTree, [SuperOwl])+owlTree_addOwlItem ospot rid oitem ot = r where+  (newot, addedSowls) = internal_owlTree_addOwlItem ospot rid oitem ot+  addedSowlsWithChildren = toList . owliterateat newot . _superOwl_id $ addedSowls+  r = (newot, addedSowlsWithChildren)++  + -- this method works for parents IF all children are included in the list and sorted from left to right owlTree_addOwlItemList :: [(REltId, OwlSpot, OwlItem)] -> OwlTree -> (OwlTree, [SuperOwl]) owlTree_addOwlItemList seltls od0 = r where@@ -1031,14 +1062,13 @@   mapaccumlfn od (rid,ospot,oitem) = internal_owlTree_addOwlItem ospot rid oitemmodded od where     osubitemmodded = case _owlItem_subItem oitem of       -- temp remove kiddos from parent as needed by internal_owlTree_addOwlItem+      -- the kiddos will be set correctly when the children get added further down the seltls list       OwlSubItemFolder _ -> OwlSubItemFolder Seq.empty       x -> x     oitemmodded = OwlItem (_owlItem_info oitem) osubitemmodded    -- go from left to right such that parents are added first-  (newot, changes) = mapAccumL mapaccumlfn od0 seltls--  r = (newot, changes)+  r = mapAccumL mapaccumlfn od0 seltls   -- TODO TEST
src/Potato/Flow/OwlHelpers.hs view
@@ -3,11 +3,11 @@  import Relude -import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake import Potato.Flow.Owl import Potato.Flow.OwlItem import Potato.Flow.Llama-import Potato.Flow.SEltMethods+import Potato.Flow.Methods.SEltMethods  superOwl_mustGetSLine :: SuperOwl -> SAutoLine superOwl_mustGetSLine sowl = case _owlItem_subItem$ _superOwl_elt sowl of
src/Potato/Flow/OwlItem.hs view
@@ -4,7 +4,7 @@  import Relude -import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake import Potato.Flow.DebugHelpers  data OwlInfo = OwlInfo {
src/Potato/Flow/OwlState.hs view
@@ -8,8 +8,8 @@ import Potato.Flow.Attachments import Potato.Flow.OwlItem import           Potato.Flow.Math-import           Potato.Flow.SElts-import           Potato.Flow.SEltMethods+import           Potato.Flow.Serialization.Snake+import           Potato.Flow.Methods.SEltMethods import           Potato.Flow.Types import Potato.Flow.DebugHelpers @@ -18,6 +18,7 @@ import           Data.List.Ordered       (isSortedBy) import           Data.Maybe import qualified Data.Text as T+import qualified Data.Sequence as Seq   @@ -114,10 +115,10 @@ owlPFState_to_SuperOwlParliament :: OwlPFState -> SuperOwlParliament owlPFState_to_SuperOwlParliament OwlPFState {..} = owlParliament_toSuperOwlParliament _owlPFState_owlTree $ OwlParliament $ _owlTree_topOwls _owlPFState_owlTree -do_newElts :: [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges)+do_newElts :: (HasCallStack) => [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges) do_newElts seltls pfs@OwlPFState {..} = r where -  -- parents are allowed, but seltls must be sortefd from left -> right such that leftmost sibling/parent of OwlSpot exists (assuming elts are added to the tree from left to right)+  -- notably, passing in a list here rather than SuperOwlParialment allows us to add parents with children so long as everything is sorted from left to right (such that the parent/sibling exist when the child gets added)   (newot, changes') = owlTree_addOwlItemList seltls _owlPFState_owlTree    changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) changes'@@ -125,7 +126,7 @@  undo_newElts :: [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges) undo_newElts seltls pfs@OwlPFState {..} = r where-  foldfn (rid,_,_) od = owlTree_removeREltId rid od+  foldfn (rid,_,_) od = owlTree_removeREltId False rid od   -- assumes seltls sorted from left to right so that no parent is deleted before its child   newot = foldr foldfn _owlPFState_owlTree seltls   changes = IM.fromList $ fmap (\(rid,_,_) -> (rid, Nothing)) seltls@@ -145,7 +146,7 @@  undo_newMiniOwlTree :: (MiniOwlTree, OwlSpot) -> OwlPFState -> (OwlPFState, SuperOwlChanges) undo_newMiniOwlTree (mot, _) pfs@OwlPFState {..} = r where-  foldfn rid od = owlTree_removeREltId rid od+  foldfn rid od = owlTree_removeREltId False rid od   newot = foldr foldfn _owlPFState_owlTree (_owlTree_topOwls mot)   changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Nothing)) $ toList $ owliterateall mot   r = (pfs { _owlPFState_owlTree = newot}, changes)@@ -174,9 +175,10 @@   op = superOwlParliament_toOwlParliament sop   (newot, changes') = owlTree_moveOwlParliament op os _owlPFState_owlTree   changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) changes'+   r = (pfs { _owlPFState_owlTree = newot}, changes) -undo_move :: (OwlSpot, SuperOwlParliament) -> OwlPFState -> (OwlPFState, SuperOwlChanges)+undo_move :: (HasCallStack) => (OwlSpot, SuperOwlParliament) -> OwlPFState -> (OwlPFState, SuperOwlChanges) undo_move (_, sop) pfs@OwlPFState {..} = assert isUndoFriendly r where    -- NOTE that sop is likely invalid in pfs at this point@@ -184,17 +186,17 @@   -- make sure SuperOwlParliament is ordered in an undo-friendly way   isUndoFriendly = isSuperOwlParliamentUndoFriendly sop -  -- first remove all elements we moved-  removefoldfn tree' so = owlTree_removeREltId (_superOwl_id so) tree'+  -- first remove all elements we moved, but keep their children putting the tree in an invalid state+  removefoldfn tree' so = owlTree_removeREltId True (_superOwl_id so) tree'   removedTree = foldl' removefoldfn _owlPFState_owlTree (unSuperOwlParliament sop) -  -- then add them back in in order+  -- then add them back in in order (any children are still in the tree with missing parent)   addmapaccumlfn tree' so = owlTree_addOwlItem ospot (_superOwl_id so) (_superOwl_elt so) tree' where-    -- NOTE that because we are ordered from left to right, _superOwl_meta so is valid in tree'+    -- NOTE that because we are ordered from left to right, `_superOwl_meta so` points to a vlaid spot in the tree' (parents comes before children)     ospot = owlTree_owlItemMeta_toOwlSpot tree' $ _superOwl_meta so   (addedTree, changes') = mapAccumL addmapaccumlfn removedTree (unSuperOwlParliament sop)--  changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) (toList changes')+  changes = IM.fromList $ fmap (\sowl -> (_superOwl_id sowl, Just sowl)) (join . toList $ changes')+     r = (pfs { _owlPFState_owlTree = addedTree}, changes)  
src/Potato/Flow/OwlWorkspace.hs view
@@ -22,7 +22,7 @@ import           Potato.Flow.Owl import           Potato.Flow.OwlItem import           Potato.Flow.OwlState-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.Preview @@ -278,6 +278,7 @@                 Preview_Commit -> assert (owlPFWorkspace_hasLocalPreview ws) $ (ws_afterCommit, IM.empty)+      Preview_MaybeCommit -> (ws_afterCommit, IM.empty)       Preview_Cancel -> case _owlPFWorkspace_localPreview ws of          Nothing -> error "expected local preview"         Just (_, _, undollama) -> clearLocalPreview $ doLlamaWorkspace' False undollama ws
src/Potato/Flow/Preview.hs view
@@ -41,6 +41,8 @@   Preview PreviewOperation Llama    -- commit the last operation   | Preview_Commit+  -- same as above but does not assert that there is a local preview+  | Preview_MaybeCommit    -- cancel the preview    | Preview_Cancel    deriving (Show, Generic)
src/Potato/Flow/Reflex/GoatWidget.hs view
@@ -124,11 +124,8 @@      -- new Endo folding     endoStyle = [ -        -- these be run before _goatWidgetConfig_mouse because sometimes we want to set params/change focus and input a mouse at the same time (i.e. clicking away from params widget to canvas widget causing params to send an update)-        fmap endoGoatCmdSetFocusedArea _goatWidgetConfig_setFocusedArea--        -- the order of the rest doesn't matter-        , fmap endoGoatCmdSetDefaultParams _goatWidgetConfig_setPotatoDefaultParameters+        -- the order of these ones don't matter+        fmap endoGoatCmdSetDefaultParams _goatWidgetConfig_setPotatoDefaultParameters         , fmap endoGoatCmdMarkSaved _goatWidgetConfig_markSaved         , fmap endoGoatCmdSetTool _goatWidgetConfig_selectTool         , fmap endoGoatCmdSetDebugLabel _goatWidgetConfig_setDebugLabel@@ -136,10 +133,13 @@         , fmap endoGoatCmdLoad _goatWidgetConfig_load         , fmap (\_ -> endoGoatCmdNewFolder "folder") _goatWidgetConfig_newFolder         , fmap endoGoatCmdWSEvent _goatWidgetConfig_bypassEvent-        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_paramsEvent $ \llama -> WSEApplyLlama (False, llama)-        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_canvasSize $ \xy -> WSEApplyLlama (False, makePFCLlama $ OwlPFCResizeCanvas (DeltaLBox 0 xy))         , fmap endoGoatCmdMouse _goatWidgetConfig_mouse         , fmap endoGoatCmdKeyboard _goatWidgetConfig_keyboard++        -- these be run before _goatWidgetConfig_mouse because sometimes we want to set params/change focus and input a mouse at the same time (i.e. clicking away from params widget to canvas widget causing params to send an update)+        , fmap endoGoatCmdSetFocusedArea _goatWidgetConfig_setFocusedArea+        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_paramsEvent $ \llama -> WSEApplyLlama (False, llama)+        , fmap endoGoatCmdWSEvent $ ffor _goatWidgetConfig_canvasSize $ \xy -> WSEApplyLlama (False, makePFCLlama $ OwlPFCResizeCanvas (DeltaLBox 0 xy))       ]    -- DELETE
src/Potato/Flow/Render.hs view
@@ -34,9 +34,9 @@ import           Potato.Flow.RenderCache import           Potato.Flow.BroadPhase import           Potato.Flow.Math-import           Potato.Flow.SEltMethods+import           Potato.Flow.Methods.SEltMethods import           Potato.Flow.Methods.Types-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import Potato.Flow.Types import Potato.Flow.OwlState import           Potato.Flow.OwlItem
src/Potato/Flow/RenderCache.hs view
@@ -6,7 +6,7 @@  import           Potato.Flow.Math import           Potato.Flow.Methods.Types-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake import Potato.Flow.Types import Potato.Flow.Owl import           Potato.Flow.Methods.LineTypes
− src/Potato/Flow/SEltMethods.hs
@@ -1,384 +0,0 @@-{-# LANGUAGE RecordWildCards #-}--module Potato.Flow.SEltMethods where--import           Relude--import           Potato.Flow.Math-import           Potato.Flow.Methods.LineDrawer-import           Potato.Flow.Methods.TextCommon-import           Potato.Flow.Methods.Types-import           Potato.Flow.Owl-import Potato.Flow.RenderCache-import           Potato.Flow.OwlItem-import           Potato.Flow.SElts-import           Potato.Flow.Types--import           Data.Dependent.Sum             (DSum ((:=>)))-import qualified Data.Map                       as Map-import           Data.Maybe                     (fromJust)-import qualified Data.Text                      as T-import qualified Potato.Data.Text.Zipper        as TZ-import Control.Exception (assert)----- DisplayLines tag is Int, 0 for no cursor 1 for cursor-noTrailngCursorDisplayLines :: Int -> TextAlign -> T.Text -> TZ.DisplayLines Int-noTrailngCursorDisplayLines width alignment text = r where-  -- force TZ to top so that displayLinesWithAlignment doesn't create trailing space for cursor-  tz = TZ.top (TZ.fromText text)--  -- hack to get rid of trailing cursor if text is ""-  r = if T.null text-    then TZ.DisplayLines {-        _displayLines_spans = []-        , _displayLines_offsetMap = Map.empty-        , _displayLines_cursorPos   = (0,0)-      }-    else TZ.displayLinesWithAlignment (convertTextAlignToTextZipperTextAlignment alignment) width 0 1 tz--makeDisplayLinesFromSBox :: SBox -> TZ.DisplayLines Int-makeDisplayLinesFromSBox sbox = r where-  alignment = _textStyle_alignment . _sBoxText_style . _sBox_text $ sbox-  text = _sBoxText_text . _sBox_text $ sbox-  LBox _ (V2 width' _) = _sBox_box sbox-  width = case _sBox_boxType sbox of-    SBoxType_BoxText   -> max 0 (width'-2)-    SBoxType_NoBoxText -> width'-    _                  -> error "wrong type"-  r = noTrailngCursorDisplayLines width alignment text------ TODO DELETE use doesOwlSubItemIntersectBox instead-doesSEltIntersectBox_DEPRECATED :: LBox -> SElt -> Bool-doesSEltIntersectBox_DEPRECATED lbox selt = case selt of-  SEltNone                     -> False-  SEltFolderStart              -> False-  SEltFolderEnd                -> False-  SEltBox x                    -> does_lBox_intersect_include_zero_area lbox (_sBox_box x)-  SEltTextArea x                   -> does_lBox_intersect_include_zero_area lbox (_sTextArea_box x)-  -- TODO this is wrong, do it correctly...-  -- we use does_lBox_intersect since it's impossible for a SAutoLine to have zero sized box-  SEltLine sline -> does_lBox_intersect lbox (fromJust $ getSEltBox_naive (SEltLine sline))--doesSEltIntersectPoint :: XY -> SElt -> Bool-doesSEltIntersectPoint pos selt = doesSEltIntersectBox_DEPRECATED (LBox pos (V2 1 1)) selt--getSEltSuperStyle :: SElt -> Maybe SuperStyle-getSEltSuperStyle selt = case selt of-  SEltBox SBox {..}       -> Just _sBox_superStyle-  SEltLine SAutoLine {..} -> Just _sAutoLine_superStyle-  _                       -> Nothing--getSEltLabelSuperStyle :: SEltLabel -> Maybe SuperStyle-getSEltLabelSuperStyle (SEltLabel _ x) = getSEltSuperStyle x--getSEltLineStyle :: SElt -> Maybe LineStyle-getSEltLineStyle selt = case selt of-  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyle-  _                       -> Nothing--getSEltLineStyleEnd :: SElt -> Maybe LineStyle-getSEltLineStyleEnd selt = case selt of-  SEltLine SAutoLine {..} -> Just _sAutoLine_lineStyleEnd-  _                       -> Nothing--getSEltLabelLineStyle :: SEltLabel -> Maybe LineStyle-getSEltLabelLineStyle (SEltLabel _ x) = getSEltLineStyle x--getSEltLabelLineStyleEnd :: SEltLabel -> Maybe LineStyle-getSEltLabelLineStyleEnd (SEltLabel _ x) = getSEltLineStyleEnd x---getSEltBoxTextStyle :: SElt -> Maybe TextStyle-getSEltBoxTextStyle = \case-  SEltBox SBox {..}         -> Just . _sBoxText_style $ _sBox_text-  _ -> Nothing--getSEltLabelBoxTextStyle :: SEltLabel -> Maybe TextStyle-getSEltLabelBoxTextStyle (SEltLabel _ x) = getSEltBoxTextStyle x--getSEltBoxType :: SElt -> Maybe SBoxType-getSEltBoxType = \case-  SEltBox SBox {..} -> Just _sBox_boxType-  _ -> Nothing--getSEltLabelBoxType :: SEltLabel -> Maybe SBoxType-getSEltLabelBoxType (SEltLabel _ x) = getSEltBoxType x--sBox_drawer :: SBox -> SEltDrawer-sBox_drawer sbox@SBox {..} = r where-  CanonicalLBox _ _ lbox@(LBox (V2 x y) (V2 w h)) = canonicalLBox_from_lBox _sBox_box--  titlewidth = max 0 (w-2)--  fillfn _ = case _superStyle_fill _sBox_superStyle of-    FillStyle_Simple c -> Just c-    FillStyle_Blank    -> Nothing--  rfntext (V2 x' y') = case _sBox_boxType of-    SBoxType_Box -> Nothing-    SBoxType_NoBox -> Nothing-    _ -> outputChar where--      -- 😰😰😰 for now we just do the below for every cell-      dl = makeDisplayLinesFromSBox sbox--      offs = case _sBox_boxType of-        SBoxType_NoBoxText -> (0,0)-        _                  -> (1,1)--      outputChar = displayLinesToChar (x, y) dl (x', y') offs--  -- TODO test-  rfnlabel (V2 x' y') = case _sBoxTitle_title _sBox_title of-    Nothing -> Nothing-    Just title -> outputChar where-      -- TODO we want to crop instead of wrap here-      -- however using infinite width trick will break AlignRight :(-      dl = noTrailngCursorDisplayLines titlewidth (_sBoxTitle_align _sBox_title) title-      -- note that y' will ultimately resolve to a yindex of 0 inside of displayLinesToChar-      outputChar = displayLinesToChar (x, y) dl (x', y') (1,0)--  rfnnoborder pt-    | not (does_lBox_contains_XY lbox pt) = Nothing-    | otherwise = case rfntext pt of-      -- 'Just Nothing' means don't use fill char (this happens when there are wide chars)-      Just mx -> mx-      Nothing -> fillfn pt--  rfnborder pt@(V2 x' y')-    | not (does_lBox_contains_XY lbox pt) = Nothing-    | w == 1 && h == 1 = _superStyle_point _sBox_superStyle-    | w == 1 = _superStyle_vertical _sBox_superStyle-    | h == 1 = _superStyle_horizontal _sBox_superStyle-    | x' == x && y' == y = _superStyle_tl _sBox_superStyle-    | x' == x && y' == y+h-1 = _superStyle_bl _sBox_superStyle-    | x' == x+w-1 && y' == y = _superStyle_tr _sBox_superStyle-    | x' == x+w-1 && y' == y+h-1 = _superStyle_br _sBox_superStyle-    | x' == x || x' == x+w-1 = _superStyle_vertical _sBox_superStyle-    -- label shows up at top horizontal portion-    | y' == y = case rfnlabel pt of-      Nothing    -> _superStyle_horizontal _sBox_superStyle-      Just pchar -> pchar-    | y' == y+h-1 = _superStyle_horizontal _sBox_superStyle-    | otherwise = rfnnoborder pt--  r = SEltDrawer {-      _sEltDrawer_box = const lbox-      , _sEltDrawer_renderFn = \_ -> case _sBox_boxType of-        SBoxType_NoBoxText -> rfnnoborder-        SBoxType_NoBox     -> rfnnoborder-        _                  -> rfnborder-      -      -- TODO -      , _sEltDrawer_maxCharWidth = 1-    }--sTextArea_drawer :: STextArea -> SEltDrawer-sTextArea_drawer STextArea {..} = r where--  lbox@(LBox p _) = _sTextArea_box--  renderfn p' = outputChar where-    inbounds = does_lBox_contains_XY lbox p'-    outputChar = if inbounds-      then case Map.lookup (p' - p) _sTextArea_text of-        Nothing -> if _sTextArea_transparent-          then Nothing-          else Just ' '-        Just c -> Just c-      else Nothing--  r = SEltDrawer {-      _sEltDrawer_box = const lbox-      , _sEltDrawer_renderFn = \_ -> renderfn--      -- TODO-      , _sEltDrawer_maxCharWidth = 1-    }---- NOTE that there is not a 1-1 mapping between `OwlSubItem` and `OwlItemCache` as the `OwlItemCache` is dependent on the OwlTree--- this function assumes that you are requesting the drawer with the intent of passing in an OwlTree--- TODO it would have been better for SEltDrawer to be created based on an OwlTree rather than return a function that takes an OwlTree-getDrawerWithCache :: OwlSubItem -> Maybe OwlItemCache -> SEltDrawer-getDrawerWithCache osubitem mcache = case osubitem of -  OwlSubItemNone        -> nilDrawer-  OwlSubItemFolder _ -> nilDrawer-  OwlSubItemBox sbox    -> sBox_drawer sbox-  OwlSubItemLine sline -> case mcache of -    Just (OwlItemCache_Line lars _) -> sSimpleLineNewRenderFn sline (Just lars)-    Nothing -> sSimpleLineNewRenderFn sline Nothing-    _ -> assert False (sSimpleLineNewRenderFn sline Nothing)-  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea---- TODO pass in cache here-getDrawer :: OwlSubItem -> SEltDrawer-getDrawer = \case-  OwlSubItemNone        -> nilDrawer-  OwlSubItemFolder _ -> nilDrawer-  OwlSubItemBox sbox    -> sBox_drawer sbox-  OwlSubItemLine sline -> sSimpleLineNewRenderFn sline Nothing-  OwlSubItemTextArea stextarea  -> sTextArea_drawer stextarea-  {--  where-    potatoDrawer = SEltDrawer {-        _sEltDrawer_box = const $ fromJust (getSEltBox_naive selt)-        , _sEltDrawer_renderFn =  makePotatoRenderer $ fromJust (getSEltBox_naive selt)-      }-  -}--getDrawerFromSEltForTest :: SElt -> SEltDrawer-getDrawerFromSEltForTest = getDrawer . sElt_to_owlSubItem--updateOwlSubItemCache :: (HasOwlTree a) => a -> OwlSubItem -> Maybe OwlItemCache-updateOwlSubItemCache ot x = r where-  r = case x of-    -- TODO use sAutoLine_to_lineAnchorsForRenderList here instead-    (OwlSubItemLine sline) -> cache where-      cache = Just $ OwlItemCache_Line (sSimpleLineNewRenderFnComputeCache ot sline) prerender-      seltdrawer = getDrawerWithCache x cache-      prerender = makePreRender ot seltdrawer -    _ -> Just $ OwlItemCache_Generic prerender where-      seltdrawer = getDrawerWithCache x Nothing-      prerender = makePreRender ot seltdrawer ----- TODO move modify methods to another file--modify_sAutoLineConstraint_with_cBoundingBox :: Bool -> SAutoLineConstraint -> CBoundingBox -> SAutoLineConstraint-modify_sAutoLineConstraint_with_cBoundingBox isDo constraint CBoundingBox {..} = case constraint of-  SAutoLineConstraintFixed xy -> SAutoLineConstraintFixed $ modifyDelta isDo xy (_deltaLBox_translate _cBoundingBox_deltaBox)--modify_sElt_with_cBoundingBox :: Bool -> SElt -> CBoundingBox -> SElt-modify_sElt_with_cBoundingBox isDo selt cbb@CBoundingBox {..} = case selt of-  SEltBox sbox  -> SEltBox $ sbox {-      _sBox_box = modifyDelta isDo (_sBox_box sbox) _cBoundingBox_deltaBox-    }-  -- TODO handle resize parameter-  SEltLine sline@SAutoLine {..} -> SEltLine $ sline {-      _sAutoLine_start = modifyDelta isDo _sAutoLine_start-        (_deltaLBox_translate _cBoundingBox_deltaBox)-      , _sAutoLine_end = modifyDelta isDo _sAutoLine_end-        (_deltaLBox_translate _cBoundingBox_deltaBox)-      , _sAutoLine_midpoints = fmap (\slc -> modify_sAutoLineConstraint_with_cBoundingBox isDo slc cbb) _sAutoLine_midpoints-    }-  SEltTextArea stext -> SEltTextArea $ stext {-      _sTextArea_box     = modifyDelta isDo (_sTextArea_box stext) _cBoundingBox_deltaBox-    }-  x          -> x--modify_sElt_with_cSuperStyle :: Bool -> SElt -> CSuperStyle -> SElt-modify_sElt_with_cSuperStyle isDo selt (CSuperStyle style) = case selt of-  SEltBox sbox -> SEltBox $ sbox {-      _sBox_superStyle = modifyDelta isDo (_sBox_superStyle sbox) style-    }-  -- TODO handle resize parameter-  SEltLine sline -> SEltLine $ sline {-      _sAutoLine_superStyle = modifyDelta isDo (_sAutoLine_superStyle sline) style-    }-  _ -> error $ "Controller - SElt type mismatch: CTagSuperStyle - " <> show selt-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk-  --x -> x---- TODO DELETE use llama instead-modify_sElt_with_cLineStyle :: Bool -> SElt -> CLineStyle -> SElt-modify_sElt_with_cLineStyle isDo selt (CLineStyle style) = case selt of-  SEltLine sline -> SEltLine $ sline {-      _sAutoLine_lineStyle = modifyDelta isDo (_sAutoLine_lineStyle sline) style-    }-  _ -> error $ "Controller - SElt type mismatch: CTagLineStyle - " <> show selt-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk-  --x -> x--modify_sElt_with_cTextStyle :: Bool -> SElt -> CTextStyle -> SElt-modify_sElt_with_cTextStyle isDo selt (CTextStyle style) = case selt of-  SEltBox sbox -> SEltBox $ sbox {-      _sBox_text = (_sBox_text sbox) {-          _sBoxText_style = modifyDelta isDo (_sBoxText_style . _sBox_text $ sbox) style-        }-    }-  _ -> error $ "Controller - SElt type mismatch: CTagBoxTextStyle - " <> show selt-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk-  --x -> x--modify_sEltBox_label_with_cTextAlign :: Bool -> SElt -> CTextAlign -> SElt-modify_sEltBox_label_with_cTextAlign isDo selt (CTextAlign align) = case selt of-  SEltBox sbox -> SEltBox $ sbox {-      _sBox_title = sboxtitle {-          _sBoxTitle_align = modifyDelta isDo (_sBoxTitle_align sboxtitle) align-        }-    } where-      sboxtitle = _sBox_title sbox-  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt-  -- maybe we want silent failure case in the future, so you can easily restyle a big selection in bulk-  --x -> x--modify_sEltBox_label_with_cMaybeText :: Bool -> SElt -> CMaybeText -> SElt-modify_sEltBox_label_with_cMaybeText isDo selt (CMaybeText text) = case selt of-  SEltBox sbox -> SEltBox $ sbox {-      _sBox_title = sboxtitle {-          _sBoxTitle_title = modifyDelta isDo (_sBoxTitle_title sboxtitle) text-        }-    } where-      sboxtitle = _sBox_title sbox-  _ -> error $ "Controller - SElt type mismatch: CTagBoxLabelAlignment - " <> show selt--modify_sEltTextArea_with_cTextArea :: Bool -> SElt -> CTextArea -> SElt-modify_sEltTextArea_with_cTextArea isDo selt (CTextArea dt) = case selt of-  SEltTextArea stextarea -> SEltTextArea $ stextarea {-      _sTextArea_text = modifyDelta isDo (_sTextArea_text stextarea) dt-    }-  _ -> error $ "Controller - SElt type mismatch: CTagTextArea - " <> show selt--modify_sEltTextArea_with_cTextAreaToggle :: Bool -> SElt -> CTextAreaToggle -> SElt-modify_sEltTextArea_with_cTextAreaToggle isDo selt (CTextAreaToggle toggle) = case selt of-  -- double toggle is idempotent but we disallow it for now-  SEltTextArea _ -> error $ "Controller - SElt type mismatch: CTagTextAreaToggle - " <> show selt-  x -> modifyDelta isDo x toggle---modifyDelta :: (Delta x dx) => Bool -> x ->  dx -> x-modifyDelta isDo x dx = if isDo-  then plusDelta x dx-  else minusDelta x dx--updateFnFromController :: Bool -> Controller -> (SEltLabel -> SEltLabel)-updateFnFromController isDo = \case-  (CTagRename :=> Identity d) -> \seltl -> modifyDelta isDo seltl d-  (CTagLine :=> Identity d) -> \(SEltLabel sname selt) -> case selt of-    SEltLine s -> SEltLabel sname (SEltLine $ modifyDelta isDo s d)-    _ -> error $ "Controller - SElt type mismatch: CTagLine - " <> show selt-  (CTagBoxText :=> Identity d) -> \(SEltLabel sname selt) -> case selt of-    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)-    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt-  (CTagBoxType :=> Identity d) -> \(SEltLabel sname selt) -> case selt of-    SEltBox s -> SEltLabel sname (SEltBox $ modifyDelta isDo s d)-    _ -> error $ "Controller - SElt type mismatch: CTagBoxText - " <> show selt-  (CTagBoundingBox :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sElt_with_cBoundingBox isDo selt d)-  (CTagSuperStyle :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sElt_with_cSuperStyle isDo selt d)-  (CTagLineStyle :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sElt_with_cLineStyle isDo selt d)-  (CTagBoxTextStyle :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sElt_with_cTextStyle isDo selt d)--  (CTagBoxLabelAlignment :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sEltBox_label_with_cTextAlign isDo selt d)-  (CTagBoxLabelText :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sEltBox_label_with_cMaybeText isDo selt d)--  (CTagTextArea :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sEltTextArea_with_cTextArea isDo selt d)-  (CTagTextAreaToggle :=> Identity d) -> \(SEltLabel sname selt) ->-    SEltLabel sname (modify_sEltTextArea_with_cTextAreaToggle isDo selt d)----- | helper method used in copy pasta-offsetSEltTree :: XY -> SEltTree -> SEltTree-offsetSEltTree offset stree = r where-  op = CBoundingBox (DeltaLBox offset 0)-  offsetfn (rid, seltl) = (rid, updateFnFromController True (CTagBoundingBox :=> Identity op) seltl)-  r = fmap offsetfn stree
− src/Potato/Flow/SElts.hs
@@ -1,458 +0,0 @@-{-# LANGUAGE RecordWildCards #-}--module Potato.Flow.SElts where--import           Relude--import           Potato.Flow.Math-import Potato.Data.Text.Unicode--import           Control.Exception (assert)-import           Data.Aeson-import           Data.Binary-import           Data.Default-import qualified Text.Show-import qualified Data.Text as T-import qualified Data.List         as L-import qualified Data.Map as Map-import qualified Potato.Data.Text.Zipper as TZ-import Data.Ratio---type REltId = Int-type PChar = Char-type MPChar = Maybe PChar--getPCharWidth :: Char -> Int8-getPCharWidth = getCharWidth---data FillStyle = FillStyle_Blank | FillStyle_Simple PChar deriving (Eq, Generic, Show)--instance FromJSON FillStyle-instance ToJSON FillStyle-instance Binary FillStyle-instance NFData FillStyle--instance Default FillStyle where-  def = FillStyle_Simple ' '---- TODO add line ends?--- TODO add line thickness?--- TODO add line fill?-data SuperStyle = SuperStyle {-  _superStyle_tl           :: MPChar-  , _superStyle_tr         :: MPChar-  , _superStyle_bl         :: MPChar-  , _superStyle_br         :: MPChar-  , _superStyle_vertical   :: MPChar-  , _superStyle_horizontal :: MPChar-  , _superStyle_point      :: MPChar -- used for 1x1 boxes and 1x lines-  , _superStyle_fill       :: FillStyle-} deriving (Eq, Generic)--instance FromJSON SuperStyle-instance ToJSON SuperStyle-instance Binary SuperStyle-instance NFData SuperStyle--instance Default SuperStyle where-  def = SuperStyle {-    _superStyle_tl = Just '╔'-    , _superStyle_tr = Just '╗'-    , _superStyle_bl = Just '╚'-    , _superStyle_br = Just '╝'-    , _superStyle_vertical   = Just '║'-    , _superStyle_horizontal = Just '═'-    , _superStyle_point = Just '█'-    , _superStyle_fill = def-  }--instance Show SuperStyle where-  show = superStyle_toListFormat--superStyle_fromListFormat :: [PChar] -> SuperStyle-superStyle_fromListFormat chars = assert (l == 7 || l == 8) $ r where-  l = length chars-  r = SuperStyle {-    _superStyle_tl = Just $ chars L.!! 0-    , _superStyle_tr = Just $ chars L.!! 1-    , _superStyle_bl = Just $ chars L.!! 2-    , _superStyle_br = Just $ chars L.!! 3-    , _superStyle_vertical   = Just $ chars L.!! 4-    , _superStyle_horizontal = Just $ chars L.!! 5-    , _superStyle_point = Just $ chars L.!! 6-    , _superStyle_fill = if l == 7 then FillStyle_Blank else FillStyle_Simple (chars L.!! 7)-  }---- superStyle_fromListFormat "╔╗╚╝║═█" `shouldBe` def--- empty styles are converted to space character-superStyle_toListFormat :: SuperStyle -> [PChar]-superStyle_toListFormat SuperStyle {..} = r where-  mfill = case _superStyle_fill of-    FillStyle_Blank    -> []-    FillStyle_Simple c -> [c]-  r = [-      fromMaybe ' ' _superStyle_tl-      ,fromMaybe ' ' _superStyle_tr-      ,fromMaybe ' ' _superStyle_bl-      ,fromMaybe ' ' _superStyle_br-      ,fromMaybe ' ' _superStyle_vertical-      ,fromMaybe ' ' _superStyle_horizontal-      ,fromMaybe ' ' _superStyle_point-    ] <> mfill---- |-data TextAlign = TextAlign_Left | TextAlign_Right | TextAlign_Center deriving (Eq, Generic, Show)--instance FromJSON TextAlign-instance ToJSON TextAlign-instance Binary TextAlign-instance NFData TextAlign--instance Default TextAlign where-  def = TextAlign_Left--convertTextAlignToTextZipperTextAlignment :: TextAlign -> TZ.TextAlignment-convertTextAlignToTextZipperTextAlignment = \case-  TextAlign_Left -> TZ.TextAlignment_Left-  TextAlign_Right -> TZ.TextAlignment_Right-  TextAlign_Center -> TZ.TextAlignment_Center---- |-data TextStyle = TextStyle {-  -- margins-  _textStyle_alignment :: TextAlign-} deriving (Eq, Generic)--instance FromJSON TextStyle-instance ToJSON TextStyle-instance Binary TextStyle-instance NFData TextStyle--instance Default TextStyle where-  def = TextStyle { _textStyle_alignment = def }--instance Show TextStyle where-  show TextStyle {..} = show _textStyle_alignment----- TODO you need support for AL_Any and maybe AL_Center--- TODO lowercase plz-data AttachmentLocation = AL_Top | AL_Bot | AL_Left | AL_Right | AL_Any deriving (Eq, Generic, Show)--instance FromJSON AttachmentLocation-instance ToJSON AttachmentLocation-instance Binary AttachmentLocation-instance NFData AttachmentLocation--type AttachmentOffsetRatio = Ratio Int--data Attachment = Attachment {-  _attachment_target :: REltId-  , _attachment_location :: AttachmentLocation-  -- you can prob just delete these, don't think we need them.-  -- 1 is right/down most, 0 is left/top most, `1 % 2` is the middle-  , _attachment_offset_rel :: AttachmentOffsetRatio-} deriving (Eq, Generic, Show)--instance FromJSON Attachment-instance ToJSON Attachment-instance Binary Attachment-instance NFData Attachment---attachment_offset_rel_default :: Ratio Int-attachment_offset_rel_default =  1 % 2--attachment_create_default :: REltId -> AttachmentLocation -> Attachment-attachment_create_default rid al = Attachment {-    _attachment_target = rid-    , _attachment_location = al-    , _attachment_offset_rel = attachment_offset_rel_default-  }---- |-data SBoxTitle = SBoxTitle {-  _sBoxTitle_title   :: Maybe Text-  , _sBoxTitle_align :: TextAlign-} deriving (Eq, Generic)--instance FromJSON SBoxTitle-instance ToJSON SBoxTitle-instance Binary SBoxTitle-instance NFData SBoxTitle--instance Default SBoxTitle where-  def = SBoxTitle {-      _sBoxTitle_title = Nothing-      , _sBoxTitle_align = def-    }--instance Show SBoxTitle where-  show SBoxTitle {..} = "SBoxTitle: " <> show _sBoxTitle_align <> " " <> show _sBoxTitle_title---- |-data SBoxText = SBoxText {-  _sBoxText_text    :: Text-  , _sBoxText_style :: TextStyle-} deriving (Eq, Generic)--instance FromJSON SBoxText-instance ToJSON SBoxText-instance Binary SBoxText-instance NFData SBoxText--instance Default SBoxText where-  def = SBoxText {-      _sBoxText_text = ""-      , _sBoxText_style = def-    }--instance Show SBoxText where-  show SBoxText {..} = "SBoxText: " <> T.unpack _sBoxText_text <> " " <> show _sBoxText_style---data SBoxType = SBoxType_Box | SBoxType_NoBox | SBoxType_BoxText | SBoxType_NoBoxText deriving (Eq, Generic, Show)--instance FromJSON SBoxType-instance ToJSON SBoxType-instance Binary SBoxType-instance NFData SBoxType--instance Default SBoxType where-  def = SBoxType_Box--sBoxType_isText :: SBoxType -> Bool-sBoxType_isText sbt = sbt == SBoxType_BoxText || sbt == SBoxType_NoBoxText--sBoxType_hasBorder :: SBoxType -> Bool-sBoxType_hasBorder sbt = sbt == SBoxType_Box || sbt == SBoxType_BoxText--make_sBoxType :: Bool -> Bool -> SBoxType-make_sBoxType border text = if border-  then if text-    then SBoxType_BoxText-    else SBoxType_Box-  else if text-    then SBoxType_NoBoxText-    else SBoxType_NoBox---- |-data SBox = SBox {-  _sBox_box       :: LBox-  , _sBox_superStyle   :: SuperStyle-  , _sBox_title   :: SBoxTitle-  , _sBox_text    :: SBoxText-  , _sBox_boxType :: SBoxType-} deriving (Eq, Generic)--instance FromJSON SBox-instance ToJSON SBox-instance Binary SBox-instance NFData SBox--instance Default SBox where-  def = SBox {-      _sBox_box     = LBox 0 0-      , _sBox_superStyle = def-      , _sBox_title = def-      , _sBox_text  = def-      , _sBox_boxType = SBoxType_Box-    }--instance Show SBox where-  show SBox {..} = "SBox: " <> show _sBox_box <> " " <> show _sBox_title <> " " <> show _sBox_text <> " " <> show _sBox_boxType <> " " <> show _sBox_superStyle--sBox_hasLabel :: SBox -> Bool-sBox_hasLabel sbox = sBoxType_hasBorder (_sBox_boxType sbox) && (isJust . _sBoxTitle_title ._sBox_title $ sbox)---- TODO DELETE no longer used with SAutoLine-data LineAutoStyle =-  LineAutoStyle_Auto-  | LineAutoStyle_AutoStraight-  | LineAutoStyle_StraightAlwaysHorizontal-  | LineAutoStyle_StraightAlwaysVertical-  deriving (Eq, Generic, Show)--instance FromJSON LineAutoStyle-instance ToJSON LineAutoStyle-instance Binary LineAutoStyle-instance NFData LineAutoStyle--instance Default LineAutoStyle where-  def = LineAutoStyle_AutoStraight---data LineStyle = LineStyle {-  _lineStyle_leftArrows    :: Text-  , _lineStyle_rightArrows :: Text-  , _lineStyle_upArrows    :: Text-  , _lineStyle_downArrows  :: Text-} deriving (Eq, Generic)--instance FromJSON LineStyle-instance ToJSON LineStyle-instance Binary LineStyle-instance NFData LineStyle--instance Default LineStyle where-  def = LineStyle {-      _lineStyle_leftArrows    = "<"-      , _lineStyle_rightArrows = ">"-      , _lineStyle_upArrows    = "^"-      , _lineStyle_downArrows  = "v"-    }--lineStyle_fromListFormat :: ([PChar], [PChar], [PChar], [PChar]) -> LineStyle-lineStyle_fromListFormat (l,r,u,d) = LineStyle {-    _lineStyle_leftArrows    = T.pack l-    , _lineStyle_rightArrows = T.pack r-    , _lineStyle_upArrows    = T.pack u-    , _lineStyle_downArrows  = T.pack d-  }--lineStyle_toListFormat :: LineStyle -> ([PChar], [PChar], [PChar], [PChar])-lineStyle_toListFormat LineStyle {..} = (T.unpack _lineStyle_leftArrows, T.unpack _lineStyle_rightArrows, T.unpack _lineStyle_upArrows, T.unpack _lineStyle_downArrows)---instance Show LineStyle where-  show ls = r where-    (a, b, c, d) = lineStyle_toListFormat ls-    r = "LineStyle: " <> a <> " " <> b <> " " <> c <> " " <> d---- someday we might have more than one constraint...-data SAutoLineConstraint = SAutoLineConstraintFixed XY deriving (Eq, Generic, Show)--instance FromJSON SAutoLineConstraint-instance ToJSON SAutoLineConstraint-instance Binary SAutoLineConstraint-instance NFData SAutoLineConstraint---- TODO provide absolute and relative positioning args-data SAutoLineLabelPosition =-  SAutoLineLabelPositionRelative Float -- 0 is at "left" anchor point and 1 is at "right" anchor point-  deriving (Eq, Generic, Show)--instance FromJSON SAutoLineLabelPosition-instance ToJSON SAutoLineLabelPosition-instance Binary SAutoLineLabelPosition-instance NFData SAutoLineLabelPosition--data SAutoLineLabel = SAutoLineLabel {-  _sAutoLineLabel_index :: Int -- index relative to _sAutoLine_midpoints for where the midpoint lives-  , _sAutoLineLabel_position :: SAutoLineLabelPosition-  , _sAutoLineLabel_text :: Text-  --, _sAutoLineLabel_vertical :: Bool -- WIP true if vertically oriented-} deriving (Eq, Generic)--instance Show SAutoLineLabel where-  show SAutoLineLabel {..} = "SAutoLineLabel: " <> show _sAutoLineLabel_index <> " " <> show _sAutoLineLabel_position <> " " <> show _sAutoLineLabel_text--instance FromJSON SAutoLineLabel-instance ToJSON SAutoLineLabel-instance Binary SAutoLineLabel-instance NFData SAutoLineLabel--instance Default SAutoLineLabel where-  def = SAutoLineLabel {-      -- anchor index, text shows AFTER index-      _sAutoLineLabel_index = 0-      , _sAutoLineLabel_position = SAutoLineLabelPositionRelative 0-      , _sAutoLineLabel_text = ""-      --, _sAutoLineLabel_vertical = False-    }----- |-data SAutoLine = SAutoLine {-  _sAutoLine_start       :: XY-  , _sAutoLine_end       :: XY-  , _sAutoLine_superStyle     :: SuperStyle--  -- TODO you need one for start/end of line (LineStyle, LineStyle)-  , _sAutoLine_lineStyle :: LineStyle-  , _sAutoLine_lineStyleEnd :: LineStyle--  -- NOTE attachments currently are not guaranteed to exist-  -- in particular, if you copy a line, delete its target and paste, it will be attached to something that doesn't exist-  -- tinytools will attempt to correct attachment in some cases but don't get too cozy about it!-  , _sAutoLine_attachStart :: Maybe Attachment-  , _sAutoLine_attachEnd :: Maybe Attachment--  , _sAutoLine_midpoints :: [SAutoLineConstraint]-  , _sAutoLine_labels :: [SAutoLineLabel] -- WIP currently does nothing-} deriving (Eq, Generic)--instance FromJSON SAutoLine-instance ToJSON SAutoLine-instance Binary SAutoLine-instance NFData SAutoLine--instance Show SAutoLine where-  show SAutoLine {..} = r where-    start = maybe (show _sAutoLine_start) show _sAutoLine_attachStart-    end = maybe (show _sAutoLine_end) show _sAutoLine_attachEnd-    r = "SAutoLine: " <> start <> " " <> end <> " " <> show _sAutoLine_midpoints <> " " <> show _sAutoLine_labels---- makes writing tests easier...-instance Default SAutoLine where-  def = SAutoLine {-      _sAutoLine_start       = 0-      , _sAutoLine_end       = 0-      , _sAutoLine_superStyle     = def-      , _sAutoLine_lineStyle = def-      , _sAutoLine_lineStyleEnd = def-      , _sAutoLine_attachStart = Nothing-      , _sAutoLine_attachEnd = Nothing-      , _sAutoLine_midpoints = []-      , _sAutoLine_labels = []-    }-    -type TextAreaMapping = Map XY PChar---- | abitrary text confined to a box-data STextArea = STextArea {-  _sTextArea_box           :: LBox-  , _sTextArea_text        :: TextAreaMapping-  -- TODO consider using SuperStyle here instead and using Fill property only-  , _sTextArea_transparent :: Bool-} deriving (Eq, Generic, Show)--instance Default STextArea where-  def = STextArea {-      _sTextArea_box   =        LBox 0 0-      , _sTextArea_text        = Map.empty-      , _sTextArea_transparent = True-    }--instance FromJSON STextArea-instance ToJSON STextArea-instance Binary STextArea-instance NFData STextArea---- TODO consider removing this all together and serializing Owl stuff directly-data SElt =-  SEltNone-  | SEltFolderStart-  | SEltFolderEnd-  | SEltBox SBox-  | SEltLine SAutoLine-  | SEltTextArea STextArea-  deriving (Eq, Generic, Show)--instance FromJSON SElt-instance ToJSON SElt-instance Binary SElt-instance NFData SElt---- TODO consider removing this all together and serializing Owl stuff directly-data SEltLabel = SEltLabel {- _sEltLabel_name   :: Text- , _sEltLabel_sElt :: SElt-} deriving (Eq, Generic, Show)--instance FromJSON SEltLabel-instance ToJSON SEltLabel-instance Binary SEltLabel-instance NFData SEltLabel
src/Potato/Flow/Serialization/Snake.hs view
@@ -1,83 +1,7 @@-{-# LANGUAGE RecordWildCards #-}--module Potato.Flow.Serialization.Snake where--import           Relude--import           Potato.Flow.Types-import Potato.Flow.Controller.Types--import qualified Data.Aeson               as Aeson-import qualified Data.Aeson.Encode.Pretty as PrettyAeson-import qualified Data.Binary as Binary-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Text.Encoding as Text------ | list of all version supported-versions :: [Int]-versions = [1]---- | version of the current build-currentVersion :: Int-currentVersion = 1--data Snake = Snake {-  _snake_version :: Int-  -- string instead of enum type to ensure compatibility if new formats are ever added-  -- currently supports "binary" and "json"-  , _snake_format :: String-  , _snake_data :: Text-} deriving (Eq, Generic, Show)--instance Aeson.FromJSON Snake-instance Aeson.ToJSON Snake-instance Binary.Binary Snake-instance NFData Snake--data SnakeFormat = SF_Json | SF_Binary--serialize :: SnakeFormat -> (SPotatoFlow, ControllerMeta) -> LBS.ByteString-serialize f x = r where-  (inner, format) = case f of-    SF_Json -> (PrettyAeson.encodePretty x, "json")-    SF_Binary -> (Binary.encode x, "binary")-  outer = Snake {-    _snake_version = currentVersion-    , _snake_format = format-    , _snake_data = Text.decodeUtf8 (LBS.toStrict inner)-  }-  r = PrettyAeson.encodePretty outer--deserialize :: LBS.ByteString -> Either String (SPotatoFlow, ControllerMeta)-deserialize lbs = case Aeson.eitherDecode lbs of-  Left e -> Left $ "failed to decode Snake with error " <> e-  Right vso -> deserialize_internal vso---deserialize_internal :: Snake -> Either String (SPotatoFlow, ControllerMeta)-deserialize_internal Snake {..} = do-  if _snake_version /= currentVersion -    then Left $ "version mismatch, got: " <> show _snake_version <> " expected: " <> show currentVersion-    else return ()-  case _snake_format of-    "json" -> Aeson.eitherDecodeStrict (Text.encodeUtf8 _snake_data)-    "binary" -> case Binary.decode (LBS.fromStrict $ Text.encodeUtf8 _snake_data) of-      Just x -> Right x-      Nothing -> Left "failed to decode binary"-    x -> Left $ "unrecognized fromat" <> show x---decodeFile :: FilePath -> IO (Either String (SPotatoFlow, ControllerMeta))-decodeFile fp = do-  vsobs <- LBS.readFile fp-  return $ deserialize vsobs+module Potato.Flow.Serialization.Snake (+   +  module Potato.Flow.Serialization.Versions.V1.SElts+) where -decodeFileMaybe :: FilePath -> IO (Maybe (SPotatoFlow, ControllerMeta))-decodeFileMaybe fp = do-  x <- decodeFile fp-  case x of-    Left _ -> return Nothing-    Right x -> return (Just x)+import           Potato.Flow.Serialization.Versions.V1.SElts
+ src/Potato/Flow/Serialization/SnakeWrangler.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE RecordWildCards #-}++module Potato.Flow.Serialization.SnakeWrangler where++import           Relude++import           Potato.Flow.Types+import Potato.Flow.Controller.Types++import qualified Data.Aeson               as Aeson+import qualified Data.Aeson.Encode.Pretty as PrettyAeson+import qualified Data.Binary as Binary+import qualified Data.ByteString.Lazy as LBS+import qualified Data.Text.Encoding as Text++++-- | list of all version supported+versions :: [Int]+versions = [1]++-- | version of the current build+currentVersion :: Int+currentVersion = 1++data Snake = Snake {+  _snake_version :: Int+  -- string instead of enum type to ensure compatibility if new formats are ever added+  -- currently supports "binary" and "json"+  , _snake_format :: String+  , _snake_data :: Text+} deriving (Eq, Generic, Show)++instance Aeson.FromJSON Snake+instance Aeson.ToJSON Snake+instance Binary.Binary Snake+instance NFData Snake++data SnakeFormat = SF_Json | SF_Binary++serialize :: SnakeFormat -> (SPotatoFlow, ControllerMeta) -> LBS.ByteString+serialize f x = r where+  (inner, format) = case f of+    SF_Json -> (PrettyAeson.encodePretty x, "json")+    SF_Binary -> (Binary.encode x, "binary")+  outer = Snake {+    _snake_version = currentVersion+    , _snake_format = format+    , _snake_data = Text.decodeUtf8 (LBS.toStrict inner)+  }+  r = PrettyAeson.encodePretty outer++deserialize :: LBS.ByteString -> Either String (SPotatoFlow, ControllerMeta)+deserialize lbs = case Aeson.eitherDecode lbs of+  Left e -> Left $ "failed to decode Snake with error " <> e+  Right vso -> deserialize_internal vso+++deserialize_internal :: Snake -> Either String (SPotatoFlow, ControllerMeta)+deserialize_internal Snake {..} = do+  if _snake_version /= currentVersion +    then Left $ "version mismatch, got: " <> show _snake_version <> " expected: " <> show currentVersion+    else return ()+  case _snake_format of+    "json" -> Aeson.eitherDecodeStrict (Text.encodeUtf8 _snake_data)+    "binary" -> case Binary.decode (LBS.fromStrict $ Text.encodeUtf8 _snake_data) of+      Just x -> Right x+      Nothing -> Left "failed to decode binary"+    x -> Left $ "unrecognized fromat" <> show x+++decodeFile :: FilePath -> IO (Either String (SPotatoFlow, ControllerMeta))+decodeFile fp = do+  vsobs <- LBS.readFile fp+  return $ deserialize vsobs+++decodeFileMaybe :: FilePath -> IO (Maybe (SPotatoFlow, ControllerMeta))+decodeFileMaybe fp = do+  x <- decodeFile fp+  case x of+    Left _ -> return Nothing+    Right x -> return (Just x)
+ src/Potato/Flow/Serialization/Versions/V1/SElts.hs view
@@ -0,0 +1,460 @@+{-# LANGUAGE RecordWildCards #-}++module Potato.Flow.Serialization.Versions.V1.SElts where++import           Relude++import           Potato.Flow.Math+import Potato.Data.Text.Unicode+import Potato.Flow.DebugHelpers++import           Control.Exception (assert)+import           Data.Aeson+import           Data.Binary+import           Data.Default+import qualified Text.Show+import qualified Data.Text as T+import qualified Data.List         as L+import qualified Data.Map as Map+import qualified Potato.Data.Text.Zipper as TZ+import Data.Ratio++++type REltId = Int+type PChar = Char+type MPChar = Maybe PChar++getPCharWidth :: Char -> Int8+getPCharWidth = getCharWidth+++data FillStyle = FillStyle_Blank | FillStyle_Simple PChar deriving (Eq, Generic, Show)++instance FromJSON FillStyle+instance ToJSON FillStyle+instance Binary FillStyle+instance NFData FillStyle++instance Default FillStyle where+  def = FillStyle_Simple ' '++-- TODO add line ends?+-- TODO add line thickness?+-- TODO add line fill?+data SuperStyle = SuperStyle {+  _superStyle_tl           :: MPChar+  , _superStyle_tr         :: MPChar+  , _superStyle_bl         :: MPChar+  , _superStyle_br         :: MPChar+  , _superStyle_vertical   :: MPChar+  , _superStyle_horizontal :: MPChar+  , _superStyle_point      :: MPChar -- used for 1x1 boxes and 1x lines+  , _superStyle_fill       :: FillStyle+} deriving (Eq, Generic)++instance FromJSON SuperStyle+instance ToJSON SuperStyle+instance Binary SuperStyle+instance NFData SuperStyle++instance Default SuperStyle where+  def = SuperStyle {+    _superStyle_tl = Just '╔'+    , _superStyle_tr = Just '╗'+    , _superStyle_bl = Just '╚'+    , _superStyle_br = Just '╝'+    , _superStyle_vertical   = Just '║'+    , _superStyle_horizontal = Just '═'+    , _superStyle_point = Just '█'+    , _superStyle_fill = def+  }++instance Show SuperStyle where+  show = superStyle_toListFormat++superStyle_fromListFormat :: [PChar] -> SuperStyle+superStyle_fromListFormat chars = assert (l == 7 || l == 8) $ r where+  l = length chars+  r = SuperStyle {+    _superStyle_tl = Just $ chars L.!! 0+    , _superStyle_tr = Just $ chars L.!! 1+    , _superStyle_bl = Just $ chars L.!! 2+    , _superStyle_br = Just $ chars L.!! 3+    , _superStyle_vertical   = Just $ chars L.!! 4+    , _superStyle_horizontal = Just $ chars L.!! 5+    , _superStyle_point = Just $ chars L.!! 6+    , _superStyle_fill = if l == 7 then FillStyle_Blank else FillStyle_Simple (chars `debugBangBang` 7)+  }++-- superStyle_fromListFormat "╔╗╚╝║═█" `shouldBe` def+-- empty styles are converted to space character+superStyle_toListFormat :: SuperStyle -> [PChar]+superStyle_toListFormat SuperStyle {..} = r where+  mfill = case _superStyle_fill of+    FillStyle_Blank    -> []+    FillStyle_Simple c -> [c]+  r = [+      fromMaybe ' ' _superStyle_tl+      ,fromMaybe ' ' _superStyle_tr+      ,fromMaybe ' ' _superStyle_bl+      ,fromMaybe ' ' _superStyle_br+      ,fromMaybe ' ' _superStyle_vertical+      ,fromMaybe ' ' _superStyle_horizontal+      ,fromMaybe ' ' _superStyle_point+    ] <> mfill++-- |+data TextAlign = TextAlign_Left | TextAlign_Right | TextAlign_Center deriving (Eq, Generic, Show)++instance FromJSON TextAlign+instance ToJSON TextAlign+instance Binary TextAlign+instance NFData TextAlign++instance Default TextAlign where+  def = TextAlign_Left++convertTextAlignToTextZipperTextAlignment :: TextAlign -> TZ.TextAlignment+convertTextAlignToTextZipperTextAlignment = \case+  TextAlign_Left -> TZ.TextAlignment_Left+  TextAlign_Right -> TZ.TextAlignment_Right+  TextAlign_Center -> TZ.TextAlignment_Center++-- |+data TextStyle = TextStyle {+  -- margins+  _textStyle_alignment :: TextAlign+} deriving (Eq, Generic)++instance FromJSON TextStyle+instance ToJSON TextStyle+instance Binary TextStyle+instance NFData TextStyle++instance Default TextStyle where+  def = TextStyle { _textStyle_alignment = def }++instance Show TextStyle where+  show TextStyle {..} = show _textStyle_alignment+++-- TODO you need support for AL_Any and maybe AL_Center+-- TODO lowercase plz+data AttachmentLocation = AL_Top | AL_Bot | AL_Left | AL_Right | AL_Any deriving (Eq, Generic, Show)++instance FromJSON AttachmentLocation+instance ToJSON AttachmentLocation+instance Binary AttachmentLocation+instance NFData AttachmentLocation++type AttachmentOffsetRatio = Ratio Int++data Attachment = Attachment {+  _attachment_target :: REltId+  , _attachment_location :: AttachmentLocation+  -- you can prob just delete these, don't think we need them.+  -- 1 is right/down most, 0 is left/top most, `1 % 2` is the middle+  , _attachment_offset_rel :: AttachmentOffsetRatio+} deriving (Eq, Generic, Show)++instance FromJSON Attachment+instance ToJSON Attachment+instance Binary Attachment+instance NFData Attachment+++attachment_offset_rel_default :: Ratio Int+attachment_offset_rel_default =  1 % 2++attachment_create_default :: REltId -> AttachmentLocation -> Attachment+attachment_create_default rid al = Attachment {+    _attachment_target = rid+    , _attachment_location = al+    , _attachment_offset_rel = attachment_offset_rel_default+  }++-- |+data SBoxTitle = SBoxTitle {+  _sBoxTitle_title   :: Maybe Text+  , _sBoxTitle_align :: TextAlign+} deriving (Eq, Generic)++instance FromJSON SBoxTitle+instance ToJSON SBoxTitle+instance Binary SBoxTitle+instance NFData SBoxTitle++instance Default SBoxTitle where+  def = SBoxTitle {+      _sBoxTitle_title = Nothing+      , _sBoxTitle_align = def+    }++instance Show SBoxTitle where+  show SBoxTitle {..} = "SBoxTitle: " <> show _sBoxTitle_align <> " " <> show _sBoxTitle_title++-- |+data SBoxText = SBoxText {+  _sBoxText_text    :: Text+  , _sBoxText_style :: TextStyle+} deriving (Eq, Generic)++instance FromJSON SBoxText+instance ToJSON SBoxText+instance Binary SBoxText+instance NFData SBoxText++instance Default SBoxText where+  def = SBoxText {+      _sBoxText_text = ""+      , _sBoxText_style = def+    }++instance Show SBoxText where+  show SBoxText {..} = "SBoxText: " <> T.unpack _sBoxText_text <> " " <> show _sBoxText_style+++data SBoxType = SBoxType_Box | SBoxType_NoBox | SBoxType_BoxText | SBoxType_NoBoxText deriving (Eq, Generic, Show)++instance FromJSON SBoxType+instance ToJSON SBoxType+instance Binary SBoxType+instance NFData SBoxType++instance Default SBoxType where+  def = SBoxType_Box++sBoxType_isText :: SBoxType -> Bool+sBoxType_isText sbt = sbt == SBoxType_BoxText || sbt == SBoxType_NoBoxText++sBoxType_hasBorder :: SBoxType -> Bool+sBoxType_hasBorder sbt = sbt == SBoxType_Box || sbt == SBoxType_BoxText++make_sBoxType :: Bool -> Bool -> SBoxType+make_sBoxType border text = if border+  then if text+    then SBoxType_BoxText+    else SBoxType_Box+  else if text+    then SBoxType_NoBoxText+    else SBoxType_NoBox++-- |+data SBox = SBox {+  _sBox_box       :: LBox+  , _sBox_superStyle   :: SuperStyle+  , _sBox_title   :: SBoxTitle+  , _sBox_text    :: SBoxText+  , _sBox_boxType :: SBoxType+} deriving (Eq, Generic)++instance FromJSON SBox+instance ToJSON SBox+instance Binary SBox+instance NFData SBox++instance Default SBox where+  def = SBox {+      _sBox_box     = LBox 0 0+      , _sBox_superStyle = def+      , _sBox_title = def+      , _sBox_text  = def+      , _sBox_boxType = SBoxType_Box+    }++instance Show SBox where+  show SBox {..} = "SBox: " <> show _sBox_box <> " " <> show _sBox_title <> " " <> show _sBox_text <> " " <> show _sBox_boxType <> " " <> show _sBox_superStyle++sBox_hasLabel :: SBox -> Bool+sBox_hasLabel sbox = sBoxType_hasBorder (_sBox_boxType sbox) && (isJust . _sBoxTitle_title ._sBox_title $ sbox)++-- TODO DELETE no longer used with SAutoLine+data LineAutoStyle =+  LineAutoStyle_Auto+  | LineAutoStyle_AutoStraight+  | LineAutoStyle_StraightAlwaysHorizontal+  | LineAutoStyle_StraightAlwaysVertical+  deriving (Eq, Generic, Show)++instance FromJSON LineAutoStyle+instance ToJSON LineAutoStyle+instance Binary LineAutoStyle+instance NFData LineAutoStyle++instance Default LineAutoStyle where+  def = LineAutoStyle_AutoStraight+++data LineStyle = LineStyle {+  _lineStyle_leftArrows    :: Text+  , _lineStyle_rightArrows :: Text+  , _lineStyle_upArrows    :: Text+  , _lineStyle_downArrows  :: Text+} deriving (Eq, Generic)++instance FromJSON LineStyle+instance ToJSON LineStyle+instance Binary LineStyle+instance NFData LineStyle++instance Default LineStyle where+  def = LineStyle {+      _lineStyle_leftArrows    = "<"+      , _lineStyle_rightArrows = ">"+      , _lineStyle_upArrows    = "^"+      , _lineStyle_downArrows  = "v"+    }++lineStyle_fromListFormat :: ([PChar], [PChar], [PChar], [PChar]) -> LineStyle+lineStyle_fromListFormat (l,r,u,d) = LineStyle {+    _lineStyle_leftArrows    = T.pack l+    , _lineStyle_rightArrows = T.pack r+    , _lineStyle_upArrows    = T.pack u+    , _lineStyle_downArrows  = T.pack d+  }++lineStyle_toListFormat :: LineStyle -> ([PChar], [PChar], [PChar], [PChar])+lineStyle_toListFormat LineStyle {..} = (T.unpack _lineStyle_leftArrows, T.unpack _lineStyle_rightArrows, T.unpack _lineStyle_upArrows, T.unpack _lineStyle_downArrows)+++instance Show LineStyle where+  show ls = r where+    (a, b, c, d) = lineStyle_toListFormat ls+    r = "LineStyle: " <> a <> " " <> b <> " " <> c <> " " <> d++-- someday we might have more than one constraint...+data SAutoLineConstraint = SAutoLineConstraintFixed XY deriving (Eq, Generic, Show)++instance FromJSON SAutoLineConstraint+instance ToJSON SAutoLineConstraint+instance Binary SAutoLineConstraint+instance NFData SAutoLineConstraint++-- TODO provide absolute and relative positioning args+data SAutoLineLabelPosition =+  SAutoLineLabelPositionRelative Float -- 0 is at "left" anchor point and 1 is at "right" anchor point+  deriving (Eq, Generic, Show)++instance FromJSON SAutoLineLabelPosition+instance ToJSON SAutoLineLabelPosition+instance Binary SAutoLineLabelPosition+instance NFData SAutoLineLabelPosition++data SAutoLineLabel = SAutoLineLabel {+  _sAutoLineLabel_index :: Int -- index relative to _sAutoLine_midpoints for where the midpoint lives+  , _sAutoLineLabel_position :: SAutoLineLabelPosition+  , _sAutoLineLabel_text :: Text+  --, _sAutoLineLabel_vertical :: Bool -- WIP true if vertically oriented+} deriving (Eq, Generic)++instance Show SAutoLineLabel where+  show SAutoLineLabel {..} = "SAutoLineLabel: " <> show _sAutoLineLabel_index <> " " <> show _sAutoLineLabel_position <> " " <> show _sAutoLineLabel_text++instance FromJSON SAutoLineLabel+instance ToJSON SAutoLineLabel+instance Binary SAutoLineLabel+instance NFData SAutoLineLabel++instance Default SAutoLineLabel where+  def = SAutoLineLabel {+      -- anchor index, text shows AFTER index+      _sAutoLineLabel_index = 0+      , _sAutoLineLabel_position = SAutoLineLabelPositionRelative 0+      , _sAutoLineLabel_text = ""+      --, _sAutoLineLabel_vertical = False+    }+++-- |+data SAutoLine = SAutoLine {+  _sAutoLine_start       :: XY+  , _sAutoLine_end       :: XY+  , _sAutoLine_superStyle     :: SuperStyle++  -- TODO you need one for start/end of line (LineStyle, LineStyle)+  , _sAutoLine_lineStyle :: LineStyle+  , _sAutoLine_lineStyleEnd :: LineStyle++  -- NOTE attachments currently are not guaranteed to exist+  -- in particular, if you copy a line, delete its target and paste, it will be attached to something that doesn't exist+  -- tinytools will attempt to correct attachment in some cases but don't get too cozy about it!+  , _sAutoLine_attachStart :: Maybe Attachment+  , _sAutoLine_attachEnd :: Maybe Attachment++  , _sAutoLine_midpoints :: [SAutoLineConstraint]+  , _sAutoLine_labels :: [SAutoLineLabel] -- WIP currently does nothing+} deriving (Eq, Generic)++instance FromJSON SAutoLine+instance ToJSON SAutoLine+instance Binary SAutoLine+instance NFData SAutoLine++instance Show SAutoLine where+  show SAutoLine {..} = r where+    start = maybe (show _sAutoLine_start) show _sAutoLine_attachStart+    end = maybe (show _sAutoLine_end) show _sAutoLine_attachEnd+    r = "SAutoLine: " <> start <> " " <> end <> " " <> show _sAutoLine_midpoints <> " " <> show _sAutoLine_labels++-- makes writing tests easier...+instance Default SAutoLine where+  def = SAutoLine {+      _sAutoLine_start       = 0+      , _sAutoLine_end       = 0+      , _sAutoLine_superStyle     = def+      , _sAutoLine_lineStyle = def+      , _sAutoLine_lineStyleEnd = def+      , _sAutoLine_attachStart = Nothing+      , _sAutoLine_attachEnd = Nothing+      , _sAutoLine_midpoints = []+      , _sAutoLine_labels = []+    }+    +type TextAreaMapping = Map XY PChar++-- | abitrary text confined to a box+data STextArea = STextArea {+  _sTextArea_box           :: LBox+  , _sTextArea_text        :: TextAreaMapping+  -- TODO consider using SuperStyle here instead and using Fill property only+  , _sTextArea_transparent :: Bool+} deriving (Eq, Generic, Show)++instance Default STextArea where+  def = STextArea {+      _sTextArea_box   =        LBox 0 0+      , _sTextArea_text        = Map.empty+      , _sTextArea_transparent = True+    }++instance FromJSON STextArea+instance ToJSON STextArea+instance Binary STextArea+instance NFData STextArea++-- TODO consider removing this all together and serializing Owl stuff directly+data SElt =+  SEltNone+  | SEltFolderStart+  | SEltFolderEnd+  | SEltBox SBox+  | SEltLine SAutoLine+  | SEltTextArea STextArea+  deriving (Eq, Generic, Show)++instance FromJSON SElt+instance ToJSON SElt+instance Binary SElt+instance NFData SElt++-- TODO consider removing this all together and serializing Owl stuff directly+data SEltLabel = SEltLabel {+ _sEltLabel_name   :: Text+ , _sEltLabel_sElt :: SElt+} deriving (Eq, Generic, Show)++instance FromJSON SEltLabel+instance ToJSON SEltLabel+instance Binary SEltLabel+instance NFData SEltLabel
src/Potato/Flow/Types.hs view
@@ -50,7 +50,7 @@ import           Relude  import           Potato.Flow.Math-import           Potato.Flow.SElts+import           Potato.Flow.Serialization.Snake  import           Control.Exception         (assert) import           Data.Aeson
test/Potato/Flow/AttachmentsSpec.hs view
@@ -12,7 +12,7 @@ import Potato.Flow.Methods.LineDrawer import Potato.Flow.Methods.LineTypes import           Potato.Flow.Math-import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake  spec :: Spec spec = do
test/Potato/Flow/Controller/Manipulator/BoxSpec.hs view
@@ -16,6 +16,44 @@ import           Potato.Flow.Common import           Potato.Flow.Controller.Manipulator.TestHelpers ++constrainDeltaLBox_test :: Spec+constrainDeltaLBox_test = do+  it "constrains as expected" $ do+    constrainDeltaLBox 1 (DeltaLBox 0 (V2 (-10) (-10))) ((LBox 0 (V2 5 5))) `shouldBe` DeltaLBox 0 (V2 (-4) (-4))+    constrainDeltaLBox 1 (DeltaLBox 0 (V2 (-10) 5)) ((LBox 0 (V2 5 5))) `shouldBe` DeltaLBox 0 (V2 (-4) 5)+    constrainDeltaLBox 1 (DeltaLBox (V2 10 0) (V2 (-10) 0)) ((LBox 0 (V2 5 5))) `shouldBe` DeltaLBox (V2 4 0) (V2 (-4) 0)++fetchLatestBox :: OwlPFState -> Either Text SBox+fetchLatestBox pfs = do+  sowl <- case maybeGetMostRecentlyCreatedOwl' pfs of+    Nothing -> Left "failed, no 🦉s"+    Just x  -> Right x+  case _owlItem_subItem (_superOwl_elt sowl) of+    OwlSubItemBox x -> Right x+    x                -> Left $ "expected SBox got: " <> show x++verifyMostRecentlyCreatedBoxLabelHasSize :: (Int, Int) -> GoatTester ()+verifyMostRecentlyCreatedBoxLabelHasSize (x, y) = verifyStateObjectHasProperty "verifyMostRecentlyCreatedBoxLabelHasSize" fetchLatestBox checkfn where +  checkfn sbox = r where+    LBox _ (V2 x' y') = _sBox_box sbox+    r = if x == x' && y == y'+      then Nothing+      else Just $ "got size " <> show (x', y') <> " expected " <> show (x, y)++verifyMostRecentlyCreatedBoxHasText :: Maybe Text -> GoatTester ()+verifyMostRecentlyCreatedBoxHasText mt = verifyStateObjectHasProperty "verifyMostRecentlyCreatedBoxHasText" fetchLatestBox checkfn where+  checkfn sbox = r where+    istext = sBoxType_isText (_sBox_boxType sbox)+    r = case +      mt of +        Nothing -> if istext then Just "expected no text" else Nothing+        Just t -> if not istext +          then Just "expected text" +          else if t == _sBoxText_text (_sBox_text sbox)+            then Nothing +            else Just $ "got text " <> show (_sBox_text sbox) <> " expected " <> t+ initSimpleBox :: GoatTester () initSimpleBox = drawCanvasBox (0, 0, 100, 100) @@ -26,7 +64,6 @@    -- TODO - basic_cancel_test :: Spec basic_cancel_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do @@ -51,9 +88,42 @@    initSimpleBox +noinvert_test :: Spec+noinvert_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do +  setMarker "draw a box"+  drawCanvasBox (0, 0, 101, 101)++  setMarker "resize the box"+  canvasMouseDown (101,101)+  canvasMouseDown (-10,-10)+  canvasMouseUp (-10,-10)+  verifyMostRecentlyCreatedBoxLabelHasSize (1, 1)++  +boxtext_test :: Spec+boxtext_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do++  setMarker "draw a box"+  drawCanvasBox (0, 0, 101, 101)++  verifyMostRecentlyCreatedBoxHasText Nothing++  setMarker "click in the box to convert it to a text box"+  canvasMouseDown (50,50)+  canvasMouseUp (50,50)+  verifyMostRecentlyCreatedBoxHasText (Just "")++  setMarker "write some text"+  pressKeys "meow meow meow meow"+  verifyMostRecentlyCreatedBoxHasText (Just "meow meow meow meow")++ spec :: Spec spec = do   describe "Box" $ do     describe "basic" $ basic_test     describe "basic_cancel" $ basic_cancel_test+    describe "constrainDeltaLBox" $ constrainDeltaLBox_test+    describe "noinvert" $ noinvert_test+    describe "boxtext" $ boxtext_test
test/Potato/Flow/Controller/Manipulator/LayersSpec.hs view
@@ -32,9 +32,23 @@ verifyLayersCount n =  verifyState ("layers count is " <> show n) $ \s -> if (countentriesfn s) == n then Nothing else Just $ "expected " <> show n <> " elts, got " <> show (countentriesfn s)   where countentriesfn = Seq.length . _layersState_entries . _goatState_layersState -basic_test :: Spec-basic_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do+verifyNthEntryInLayersIs :: Int -> REltId -> GoatTester()+verifyNthEntryInLayersIs n rid = verifyState (show n <> " entry in layers is " <> show rid) $ vf where+  vf s = r where+    lentries = _layersState_entries . _goatState_layersState $ s+    mfirstentry = Seq.lookup n lentries+    r = case mfirstentry of+      Nothing -> Just $ "no " <> show n <> " entry in layers"+      Just lentry -> if firstentry == rid then Nothing else Just $ "expected REltId " <> show rid <> ", got " <> show firstentry where+        firstentry = _superOwl_id . _layerEntry_superOwl $ lentry +verifyFirstEntryInLayersIs :: REltId -> GoatTester()+verifyFirstEntryInLayersIs = verifyNthEntryInLayersIs 0+    ++create_select_test :: Spec+create_select_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do+   setMarker "create a folder"   verifyOwlCount 0   addFolder someFolderName@@ -50,10 +64,37 @@   verifyFolderSelected someFolderName  +rename_focus_clickCanvas_test :: Spec+rename_focus_clickCanvas_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do -rename_focus_test :: Spec-rename_focus_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do+  setMarker "draw a box"+  drawCanvasBox (0, 0, 100, 100)+  verifyOwlCount 1 +  setMarker "select the box via layers"+  layerMouseDownUpRel LMO_Normal 0 0+  verifySelectionCount 1++  setMarker "begin renaming the box"+  layerMouseDownUpRel LMO_Normal 0 0+  pressKeys "aoeu"++  let+    origname = "<box>"+    -- NOTE, when when have multi-select, we should auto-select the previous name such that it gets deleted+    expectedname = "aoeu<box>"++  setMarker "verify the name has not been changed yet (no preview)"+  verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == origname then Nothing else Just $ "expected name " <> show origname <> " got " <> show (hasOwlItem_name sowl)++  setMarker "change focus by clicking on canvas and ensure rename took effect"+  canvasMouseDownUp (50, 50)+  verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == expectedname then Nothing else Just $ "expected name " <> show expectedname <> " got " <> show (hasOwlItem_name sowl)++-- same as rename_focus_clickCanvas_test but calls setFocus instead+rename_focus_setFocus_test :: GoatFocusedArea -> Spec+rename_focus_setFocus_test gfa = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do+   setMarker "draw a box"   drawCanvasBox (0, 0, 100, 100)   verifyOwlCount 1@@ -75,7 +116,7 @@   verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == origname then Nothing else Just $ "expected name " <> show origname <> " got " <> show (hasOwlItem_name sowl)      setMarker "change focus and ensure rename took effect"-  setFocusArea GoatFocusedArea_Other+  setFocusArea gfa   verifyMostRecentlyCreatedOwl $ \sowl -> if hasOwlItem_name sowl == expectedname then Nothing else Just $ "expected name " <> show expectedname <> " got " <> show (hasOwlItem_name sowl)  create_in_folder_and_collapse_test :: Spec@@ -132,6 +173,7 @@   layerMouseDownUpRel LMO_Collapse 0 0   verifyLayersCount 2 +   setMarker "expand the second folder"   layerMouseDownUpRel LMO_Collapse 1 1   verifyLayersCount 4@@ -171,15 +213,135 @@   canvasMouseDownUp (5,5)   verifySelectionCount 1 +drag_folder_test :: Spec+drag_folder_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do+  setMarker "create a folder"+  verifyOwlCount 0+  addFolder someFolderName+  verifyOwlCount 1+  verifyFolderSelected someFolderName+  folderrid <- _superOwl_id <$> mustGetMostRecentlyCreatedOwl +  verifyFirstEntryInLayersIs folderrid +  setMarker "create box 1"+  drawCanvasBox (0,0,10,10)+  verifySelectionCount 1+  -- TODO verify the box is in the folder+  verifyFirstEntryInLayersIs folderrid++  setMarker "unselect the box so that the next box is not in the folder"+  pressEscape+  verifySelectionCount 0++  setMarker "create box 2"+  drawCanvasBox (10,10,10,10)+  verifySelectionCount 1+  boxrid <- _superOwl_id <$> mustGetMostRecentlyCreatedOwl +  verifyFirstEntryInLayersIs boxrid++  beforeDragTheFolder <- getLayersState    +  setMarker "select and drag the folder"+  layerMouseDownUpRel LMO_Normal 1 0+  layerMouseDownRel LMO_Normal 1 0+  layerMouseDownRel LMO_Normal 0 0+  layerMouseUpRel LMO_Normal 0 0+  -- TODO verify the folder is in the first position in layers+  verifyFirstEntryInLayersIs folderrid++  beforeUndo <- getLayersState++  setMarker "undo"+  pressUndo+  afterPressUndo <- getLayersState+  verifyEqual "undo state is same as before drag the folder" beforeDragTheFolder afterPressUndo++  setMarker "redo"+  pressRedo+  afterPressRedo <- getLayersState+  verifyEqual "undo state is same as before undo redo" beforeUndo afterPressRedo++drag_folder2_test :: Spec+drag_folder2_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do++  setMarker "setup scene"+  drawCanvasBox (0,0,10,10) -- 1+  addFolder someFolderName -- 2+  drawCanvasBox (10,10,10,10) -- 3+  addFolder "innerfolder" -- 4+  -- 2testfolder +  --   4innerfolder+  --   3<box>+  -- 1<box> ++  setMarker "drag second box into inner folder"+  layerMouseDownUpRel LMO_Normal 2 1+  layerMouseDownRel LMO_Normal 2 1+  -- need to drag away first in order to move it into the folder+  layerMouseDownRel LMO_Normal 1 1+  layerMouseDownRel LMO_Normal 2 1+  layerMouseDownRel LMO_Normal 2 1+  layerMouseUpRel LMO_Normal 2 1+  verifyNthEntryInLayersIs 2 3+  -- 2testfolder+  --   4innerfolder+  --     3<box>+  -- 1<box>++  beforeDrag <- getLayersState++  -- DELETE+  --failWithMessage ("\n" <> show beforeDrag)++  setMarker "drag inner folder outside of outer folder"+  layerMouseDownUpRel LMO_Normal 1 1+  layerMouseDownRel LMO_Normal 1 1+  layerMouseDownRel LMO_Normal 5 0+  layerMouseUpRel LMO_Normal 5 0+  verifyNthEntryInLayersIs 2 4+  -- 2testfolder+  -- 1<box>+  -- 4innerfolder+  --   3<box>++  -- DELETE+  --afterDrag <- getLayersState+  --failWithMessage ("\n" <> show afterDrag)++  setMarker "undo"+  pressUndo+  afterUndo <- getLayersState+  verifyEqual "undo state is same as before drag the folder" beforeDrag afterUndo++  -- DELETE+  --failWithMessage ("\n" <> show afterUndo)++  setMarker "collapse the inner folder"+  layerMouseDownUpRel LMO_Collapse 1 1++  -- DELETE+  --ls <- getLayersState+  --failWithMessage ("\n" <> show ls)++  verifyNthEntryInLayersIs 1 4+  verifyNthEntryInLayersIs 2 1+  -- 2testfolder+  --   4innerfolder+  -- 1<box>+  ++ spec :: Spec spec = do   describe "Layers" $ do-    describe "basic" $ basic_test-    describe "rename_focus_test" $ rename_focus_test+    describe "create_select_test" $ create_select_test+    describe "rename_focus_clickCanvas_test" $ rename_focus_clickCanvas_test +    describe "rename_focus_setFocus_test canvas" $ rename_focus_setFocus_test GoatFocusedArea_Canvas+    describe "rename_focus_setFocus_test other" $ rename_focus_setFocus_test GoatFocusedArea_Other     describe "create_in_folder_and_collapse_test" $ create_in_folder_and_collapse_test     describe "folder_collapse_test" $ folder_collapse_test-    describe "hide_select_test" $ lock_or_hide_select_test LMO_Hide-    describe "lock_select_test" $ lock_or_hide_select_test LMO_Lock+    describe "lock_or_hide_select_test hide" $ lock_or_hide_select_test LMO_Hide+    describe "lock_or_hide_select_test lock" $ lock_or_hide_select_test LMO_Lock+    describe "drag_folder_test" $ drag_folder_test+    describe "drag_folder2_test" $ drag_folder2_test
test/Potato/Flow/Controller/Manipulator/PanSpec.hs view
@@ -25,7 +25,16 @@       pan = (x,y)  +verifyScreenSize :: V2 Int -> GoatTester ()+verifyScreenSize expected = verifyState "verifyScreenSize" f where+  f gs = if screensize == expected+    then Nothing+    else Just $ "got " <> show screensize <> " expected " <> show expected+    where+      screensize = _goatState_screenRegion gs ++ -- TODO broken... basic_test :: Spec basic_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do@@ -34,17 +43,22 @@   drawCanvasBox (0, 0, 10, 10)   -- TODO verify box position -  let pan = (5, -7)+  let +    pan1 = (1, 1)+    pan2 = (5, -7)+    -- after panning to pan1 when we pan to pan2 it's actually panning to pan2+pan1+    pan2p1 = (6,-6)   setMarker "pan"   verifyPan (0,0)   setTool Tool_Pan   canvasMouseDown (0, 0)-  canvasMouseDown (1, 1)-  verifyPan (1, 1)-  canvasMouseDown pan-  verifyPan pan-  canvasMouseUp pan-  verifyPan pan+  canvasMouseDown pan1+  verifyPan pan1+  canvasMouseDown pan2+  verifyPan pan2p1+  -- now we are at +  canvasMouseUp (0, 0)+  verifyPan pan2p1    setMarker "draw a box"   drawCanvasBox (0, 0, 10, 10)@@ -82,9 +96,45 @@   verifyOwlCount 1  +pan_resize_pan_test :: Spec+pan_resize_pan_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do++  let+    pan = (5, 10)+    minuspan = (-5, -10)+    newscreensize = V2 150 150++  setMarker "draw a box"+  drawCanvasBox (0, 0, 10, 10)+  +  setMarker "pan"+  verifyPan (0,0)+  setTool Tool_Pan+  canvasMouseDown (0, 0)+  canvasMouseUp pan+  verifyPan pan++  setMarker "resize"+  verifyScreenSize goatTesterInitialScreenSize+  resizeScreen newscreensize+  verifyScreenSize newscreensize+  +  setMarker "pan back"+  setTool Tool_Pan+  canvasMouseDown (0, 0)+  canvasMouseUp minuspan+  verifyPan (0, 0)+  -- TODO verify box position on screen++  setMarker "resize back"+  resizeScreen goatTesterInitialScreenSize+  verifyScreenSize goatTesterInitialScreenSize+  -- TODO verify something+ spec :: Spec spec = do   describe "Pan" $ do-    --describe "basic_test" $ basic_test+    describe "basic_test" $ basic_test     describe "cancel_test" $ cancel_test     describe "middle_button_pan_test" $ middle_button_pan_test+    describe "pan_resize_pan_test" $ pan_resize_pan_test
test/Potato/Flow/Deprecated/Controller/Manipulator/BoxTextSpec.hs view
@@ -265,7 +265,7 @@       , EWCLabel "exit BoxText"       , EWCKeyboard (KeyboardData KeyboardKey_Esc []) -      , EWCLabel "resize box to 0"+      , EWCLabel "resize box to 1"       , EWCMouse (LMouseData (V2 12 12) False MouseButton_Left [] False)       , EWCMouse (LMouseData (V2 10 10) False MouseButton_Left [] False)       , EWCMouse (LMouseData (V2 10 10) True MouseButton_Left [] False)@@ -287,11 +287,11 @@       , LabelCheck "exit BoxText"       , checkHandlerNameAndState handlerName_box False -      , LabelCheck "resize box to 0"+      , LabelCheck "resize box to 1"       , AlwaysPass       , AlwaysPass       , firstSuperOwlPredicate (Just "<text>") $ \sowl -> case hasOwlItem_toSElt_hack sowl of-        SEltBox (SBox lbox _ _ _ _) -> lbox == LBox (V2 10 10) (V2 0 0)+        SEltBox (SBox lbox _ _ _ _) -> lbox == LBox (V2 10 10) (V2 1 1)         _                           -> False      ]
test/Potato/Flow/GoatTester.hs view
@@ -20,6 +20,10 @@ import           Data.Tuple.Extra  +goatTesterInitialScreenSize :: V2 Int+goatTesterInitialScreenSize = V2 100 100++ type GoatTesterTrackingState = (Text, Int, Int) -- (marker, operations since start of test, operations since last marker)  -- TODO include ref to rendered canvas and then you can render it on failure cases!!@@ -42,7 +46,7 @@  instance Default GoatTesterState where   def = GoatTesterState {-      _goatTesterState_goatState = makeGoatState (V2 100 100) (emptyOwlPFState, emptyControllerMeta)+      _goatTesterState_goatState = makeGoatState goatTesterInitialScreenSize (emptyOwlPFState, emptyControllerMeta)       , _goatTesterState_rawOperationCount = 0       , _goatTesterState_marker = "__START__"       , _goatTesterState_rawOperationCountSinceLastMarker = 0@@ -99,6 +103,14 @@ verify desc mf = GoatTesterT $ do   unGoatTesterT $ putRecord desc mf +verifyBool :: (Monad m) => Text -> Bool -> GoatTesterT m ()+verifyBool desc b = GoatTesterT $ do+  unGoatTesterT $ putRecord desc (if b then Nothing else Just "failed")++verifyEqual :: (Monad m, Show a, Eq a) => Text -> a -> a -> GoatTesterT m ()+verifyEqual desc a b = GoatTesterT $ do+  unGoatTesterT $ putRecord desc (if a == b then Nothing else Just $ "expected: " <> show a <> "\ngot: " <> show b)+ verifyState' :: (Monad m) => Text -> (GoatState -> Maybe Text) -> GoatTesterT m Bool verifyState' desc fn = GoatTesterT $ do   gts <- get@@ -112,6 +124,9 @@ --verifyStateFatal :: (GoatState -> Maybe Text) -> GoatTesterT m () --verifyStateFatal = undefined +failWithMessage :: (Monad m) => Text -> GoatTesterT m ()+failWithMessage = putRecord "failed: " . Just+ runGoatTesterT :: (Monad m) => GoatState -> GoatTesterT m a -> m [GoatTesterRecord] runGoatTesterT gs m = do   gts <- execStateT (unGoatTesterT m) $ def { _goatTesterState_goatState = gs }@@ -166,6 +181,10 @@   pfs <- getOwlPFState   return $ fromJust $ maybeGetMostRecentlyCreatedOwl' pfs +getLayersState :: (Monad m) => GoatTesterT m LayersState+getLayersState = GoatTesterT $ do+  gts <- get+  return $ _goatState_layersState $ _goatTesterState_goatState gts  -- operation helpers @@ -276,9 +295,12 @@ pressRedo :: (Monad m) => GoatTesterT m () pressRedo = runEndo $ endoGoatCmdKeyboard  (KeyboardData (KeyboardKey_Char 'y') [KeyModifier_Ctrl]) - markSaved :: (Monad m) => GoatTesterT m () markSaved = runEndo $ endoGoatCmdMarkSaved ()++resizeScreen :: (Monad m) => V2 Int -> GoatTesterT m ()+resizeScreen (V2 w h) = runEndo $ endoGoatCmdSetCanvasRegionDim (V2 w h)+  -- verification helpers 
+ test/Potato/Flow/Methods/SEltMethodsSpec.hs view
@@ -0,0 +1,119 @@+{-# LANGUAGE RecordWildCards #-}+{-# OPTIONS_GHC -fno-warn-unused-local-binds #-}++module Potato.Flow.Methods.SEltMethodsSpec(+  spec+) where++import           Relude       hiding (empty, fromList)++import           Test.Hspec++import qualified Data.Map as Map+import           Data.Default (def)++import           Potato.Flow+++spec :: Spec+spec = do+  let+    testsstyle = def { _superStyle_fill = FillStyle_Simple '@' }+    renderfn sd = _sEltDrawer_renderFn sd emptyOwlTree+  describe "SEltMethod" $ do+    describe "getDrawerFromSEltForTest" $ do+      describe "SBox" $ do+        let+          somesbox1 style = def {+              _sBox_box       = LBox 0 (V2 5 5)+              , _sBox_text    = def {+                  _sBoxText_text = "m ow meow meow"+                }+              , _sBox_boxType = style+              , _sBox_superStyle = testsstyle+            }+          somesbox2 style = def {+              _sBox_box       = LBox 0 (V2 1 1)+              , _sBox_boxType = style+              , _sBox_superStyle = testsstyle+            }+          somesbox3 style = def {+              _sBox_box       = LBox 0 (V2 10 10)+              , _sBox_text    = def {+                  _sBoxText_text = "m ow meeuew ee  meow hello boop no12 meow whatever"+                  , _sBoxText_style = TextStyle TextAlign_Right+                }+              , _sBox_boxType = style+              , _sBox_superStyle = testsstyle+            }+          somesbox4 style = def {+              _sBox_box       = LBox 0 (V2 10 10)+              , _sBox_text    = def {+                  _sBoxText_text = "there are no space between adjacent characters"+                  , _sBoxText_style = TextStyle TextAlign_Left+                }+              , _sBox_boxType = style+              , _sBox_superStyle = testsstyle+            }+          somesbox5 style = def {+            _sBox_box       = LBox 0 (V2 10 5)+            , _sBox_text    = def {+                _sBoxText_text = "aoeuaoeu"+              }+            , _sBox_title = SBoxTitle {+                _sBoxTitle_title = Just "boop"+                , _sBoxTitle_align = TextAlign_Right+              }+            , _sBox_boxType = style+            , _sBox_superStyle = testsstyle+          }+        it "SBoxType_NoBoxText" $ do+          let+            sd = getDrawerFromSEltForTest (SEltBox (somesbox1 SBoxType_NoBoxText))+          renderfn sd (V2 100 0) `shouldBe` Nothing+          renderfn sd (V2 (-1) 0) `shouldBe` Nothing+          renderfn sd (V2 0 0) `shouldBe` Just 'm'+          renderfn sd (V2 1 0) `shouldBe` Just ' '+          renderfn sd (V2 2 0) `shouldBe` Just 'o'+          -- TODO it seems EOL space characters are not rendered and this gives the fill char. Maybe this is desired behavior thought?+          --renderfn sd (V2 3 0) `shouldBe` Just ' '+          renderfn sd (V2 1 1) `shouldBe` Just 'e'+          renderfn sd (V2 4 4) `shouldBe` Just '@'+        it "SBoxType_Box" $ do+          let+            sd = getDrawerFromSEltForTest (SEltBox (somesbox2 SBoxType_Box))+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn+          renderfn sd (V2 0 0) `shouldBe` _superStyle_point def+        it "SBoxType_NoBoxText_alignRight" $ do+          let+            sd = getDrawerFromSEltForTest (SEltBox (somesbox3 SBoxType_NoBoxText))+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn+          renderfn sd (V2 0 0) `shouldBe` Just '@'+        it "SBoxType_NoBoxText_widechar" $ do+          let+            sd = getDrawerFromSEltForTest (SEltBox (somesbox4 SBoxType_NoBoxText))+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn+          renderfn sd (V2 0 0) `shouldBe` Just 't'+          renderfn sd (V2 1 0) `shouldBe` Nothing+          renderfn sd (V2 2 0) `shouldBe` Just 'h'+          renderfn sd (V2 3 0) `shouldBe` Nothing+        it "box label" $ do+          let+            sd = getDrawerFromSEltForTest (SEltBox (somesbox5 SBoxType_BoxText))+          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn+          renderfn sd (V2 0 0) `shouldBe` Just '╔'+          renderfn sd (V2 8 0) `shouldBe` Just 'p'+      describe "STextArea" $ do+        let+          sometextarea = STextArea {+              _sTextArea_box         = LBox (V2 1 1) (V2 10 10)+              , _sTextArea_text      = Map.fromList [((V2 0 0),'a'),((V2 1 1),'b'),((V2 9 9),'c')]+              , _sTextArea_transparent = False+            }+        it "basic" $ do+          let+            sd = getDrawerFromSEltForTest (SEltTextArea $ sometextarea)+          --forM_ ((sEltDrawer_renderToLines sd) emptyOwlTree sd) putTextLn+          renderfn sd (V2 1 1) `shouldBe` Just 'a'+          renderfn sd (V2 10 10) `shouldBe` Just 'c'+          renderfn sd (V2 100 0) `shouldBe` Nothing
test/Potato/Flow/OwlSpec.hs view
@@ -15,7 +15,7 @@ import           Potato.Flow.Deprecated.TestStates import           Potato.Flow.Deprecated.State import Potato.Flow.Types-import Potato.Flow.SElts+import Potato.Flow.Serialization.Snake   -- | 'owlTree_toSEltTree' may change SEltFolderEnd name and REltId during reconstruction@@ -55,8 +55,8 @@         let           sowl1 = owlTree_mustFindSuperOwl owlTree0 2 -- b1           sowl2 = owlTree_mustFindSuperOwl owlTree0 1 -- fstart2-          ot1 = owlTree_removeSuperOwl sowl1 owlTree0-          ot2 = owlTree_removeSuperOwl sowl2 owlTree0+          ot1 = owlTree_removeSuperOwl False sowl1 owlTree0+          ot2 = owlTree_removeSuperOwl False sowl2 owlTree0         owlTree_owlCount ot1 `shouldBe` 8         owlTree_owlCount ot2 `shouldBe` 4         fst (owlTree_validate ot1) `shouldBe` True
− test/Potato/Flow/SEltMethodsSpec.hs
@@ -1,119 +0,0 @@-{-# LANGUAGE RecordWildCards #-}-{-# OPTIONS_GHC -fno-warn-unused-local-binds #-}--module Potato.Flow.SEltMethodsSpec(-  spec-) where--import           Relude       hiding (empty, fromList)--import           Test.Hspec--import qualified Data.Map as Map-import           Data.Default (def)--import           Potato.Flow---spec :: Spec-spec = do-  let-    testsstyle = def { _superStyle_fill = FillStyle_Simple '@' }-    renderfn sd = _sEltDrawer_renderFn sd emptyOwlTree-  describe "SEltMethod" $ do-    describe "getDrawerFromSEltForTest" $ do-      describe "SBox" $ do-        let-          somesbox1 style = def {-              _sBox_box       = LBox 0 (V2 5 5)-              , _sBox_text    = def {-                  _sBoxText_text = "m ow meow meow"-                }-              , _sBox_boxType = style-              , _sBox_superStyle = testsstyle-            }-          somesbox2 style = def {-              _sBox_box       = LBox 0 (V2 1 1)-              , _sBox_boxType = style-              , _sBox_superStyle = testsstyle-            }-          somesbox3 style = def {-              _sBox_box       = LBox 0 (V2 10 10)-              , _sBox_text    = def {-                  _sBoxText_text = "m ow meeuew ee  meow hello boop no12 meow whatever"-                  , _sBoxText_style = TextStyle TextAlign_Right-                }-              , _sBox_boxType = style-              , _sBox_superStyle = testsstyle-            }-          somesbox4 style = def {-              _sBox_box       = LBox 0 (V2 10 10)-              , _sBox_text    = def {-                  _sBoxText_text = "there are no space between adjacent characters"-                  , _sBoxText_style = TextStyle TextAlign_Left-                }-              , _sBox_boxType = style-              , _sBox_superStyle = testsstyle-            }-          somesbox5 style = def {-            _sBox_box       = LBox 0 (V2 10 5)-            , _sBox_text    = def {-                _sBoxText_text = "aoeuaoeu"-              }-            , _sBox_title = SBoxTitle {-                _sBoxTitle_title = Just "boop"-                , _sBoxTitle_align = TextAlign_Right-              }-            , _sBox_boxType = style-            , _sBox_superStyle = testsstyle-          }-        it "SBoxType_NoBoxText" $ do-          let-            sd = getDrawerFromSEltForTest (SEltBox (somesbox1 SBoxType_NoBoxText))-          renderfn sd (V2 100 0) `shouldBe` Nothing-          renderfn sd (V2 (-1) 0) `shouldBe` Nothing-          renderfn sd (V2 0 0) `shouldBe` Just 'm'-          renderfn sd (V2 1 0) `shouldBe` Just ' '-          renderfn sd (V2 2 0) `shouldBe` Just 'o'-          -- TODO it seems EOL space characters are not rendered and this gives the fill char. Maybe this is desired behavior thought?-          --renderfn sd (V2 3 0) `shouldBe` Just ' '-          renderfn sd (V2 1 1) `shouldBe` Just 'e'-          renderfn sd (V2 4 4) `shouldBe` Just '@'-        it "SBoxType_Box" $ do-          let-            sd = getDrawerFromSEltForTest (SEltBox (somesbox2 SBoxType_Box))-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn-          renderfn sd (V2 0 0) `shouldBe` _superStyle_point def-        it "SBoxType_NoBoxText_alignRight" $ do-          let-            sd = getDrawerFromSEltForTest (SEltBox (somesbox3 SBoxType_NoBoxText))-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn-          renderfn sd (V2 0 0) `shouldBe` Just '@'-        it "SBoxType_NoBoxText_widechar" $ do-          let-            sd = getDrawerFromSEltForTest (SEltBox (somesbox4 SBoxType_NoBoxText))-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn-          renderfn sd (V2 0 0) `shouldBe` Just 't'-          renderfn sd (V2 1 0) `shouldBe` Nothing-          renderfn sd (V2 2 0) `shouldBe` Just 'h'-          renderfn sd (V2 3 0) `shouldBe` Nothing-        it "box label" $ do-          let-            sd = getDrawerFromSEltForTest (SEltBox (somesbox5 SBoxType_BoxText))-          --forM_ (sEltDrawer_renderToLines sd emptyOwlTree) putTextLn-          renderfn sd (V2 0 0) `shouldBe` Just '╔'-          renderfn sd (V2 8 0) `shouldBe` Just 'p'-      describe "STextArea" $ do-        let-          sometextarea = STextArea {-              _sTextArea_box         = LBox (V2 1 1) (V2 10 10)-              , _sTextArea_text      = Map.fromList [((V2 0 0),'a'),((V2 1 1),'b'),((V2 9 9),'c')]-              , _sTextArea_transparent = False-            }-        it "basic" $ do-          let-            sd = getDrawerFromSEltForTest (SEltTextArea $ sometextarea)-          --forM_ ((sEltDrawer_renderToLines sd) emptyOwlTree sd) putTextLn-          renderfn sd (V2 1 1) `shouldBe` Just 'a'-          renderfn sd (V2 10 10) `shouldBe` Just 'c'-          renderfn sd (V2 100 0) `shouldBe` Nothing
tinytools.cabal view
@@ -1,17 +1,14 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.4.------ see: https://github.com/sol/hpack- name:           tinytools-version:        0.1.0.4+version:        0.1.0.5 description:    tinytools is a mono-space unicode diagram editor homepage:       https://github.com/minimapletinytools/tinytools#readme bug-reports:    https://github.com/minimapletinytools/tinytools/issues author:         minimaple maintainer:     minimapletinytools@gmail.com-synopsis:       tinytools is a mono-space unicode diagram editor+synopsis:       tinytools is a monospace unicode diagram editor+category:       User Interfaces copyright:      2023 minimaple (Peter Lu) license:        BSD3 license-file:   LICENSE@@ -72,9 +69,10 @@       Potato.Flow.Reflex.GoatWidget       Potato.Flow.Render       Potato.Flow.RenderCache-      Potato.Flow.SEltMethods-      Potato.Flow.SElts+      Potato.Flow.Methods.SEltMethods       Potato.Flow.Serialization.Snake+      Potato.Flow.Serialization.SnakeWrangler+      Potato.Flow.Serialization.Versions.V1.SElts       Potato.Flow.TestStates       Potato.Flow.Types   hs-source-dirs:@@ -143,12 +141,11 @@     , pretty-simple     , random-shuffle     , ref-tf-    , reflex >= 0.8 && <1+    , reflex >= 0.9.2 && < 1     , reflex-potatoes >=0.1-    , reflex-test-host >=0.1.2+    , reflex-test-host >=0.1.2.3     , relude     , semialign-    , template-haskell     , text     , text-icu     , these@@ -190,7 +187,7 @@       Potato.Flow.Methods.LineDrawerSpec       Potato.Flow.OwlSpec       Potato.Flow.RenderSpec-      Potato.Flow.SEltMethodsSpec+      Potato.Flow.Methods.SEltMethodsSpec       Paths_tinytools   hs-source-dirs:       test@@ -260,12 +257,11 @@     , pretty-simple     , random-shuffle     , ref-tf-    , reflex >= 0.8 && <1+    , reflex >= 0.9.2 && < 1     , reflex-potatoes >=0.1-    , reflex-test-host >=0.1.2+    , reflex-test-host >=0.1.2.3     , relude     , semialign-    , template-haskell     , text     , text-icu     , these