packages feed

tinytools 0.1.0.5 → 0.1.0.6

raw patch · 38 files changed

+448/−151 lines, 38 filesdep −MonadRandomdep −hashabledep −listsafe

Dependencies removed: MonadRandom, hashable, listsafe, patch, random-shuffle

Files

ChangeLog.md view
@@ -2,6 +2,10 @@  ## [unreleased] +## [0.1.0.6]+- fix several bugs+- remove patch dependency+   ## [0.1.0.5] - various internal improvements to how previews are staged and committed - fix crash issue with undoing drags that contained folders
src/Potato/Data/Text/Zipper.hs view
@@ -492,7 +492,7 @@           else (nextacc, [Span tag t])       ((_, ecpos_out), curlinespans) = if T.null curlinetext         -- manually handle empty case because mapaccumlfn doesn't handle it-        then ((0, Right (0, alignmentOffset alignment width "")), [[Span tag ""]])+        then ((0, Right (0, alignmentOffset alignment width "")), [[Span cursorTag ""]])         else L.mapAccumL mapaccumlfn (0, Left 0) curwrappedlines        (cursorY', cursorX) = case ecpos_out of
src/Potato/Flow/BroadPhase.hs view
@@ -27,6 +27,7 @@ import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types +import qualified Text.Show import qualified Data.IntMap.Strict        as IM  type AABB = LBox@@ -37,10 +38,13 @@ data BPTree = BPTree {   -- TODO you want something sortable too...   _bPTree_potato_tree :: REltIdMap AABB-} deriving (Show, Eq)+} deriving (Eq)  emptyBPTree :: BPTree emptyBPTree = BPTree IM.empty++instance Show BPTree where+  show BPTree {..} = "BPTree:\n" <> IM.foldrWithKey (\k v acc -> show k <> ": " <> show v <> "\n" <> acc) "" _bPTree_potato_tree  -- TODO --bPTreeFromPFState :: PFState -> BPTree
src/Potato/Flow/Controller/Goat.hs view
@@ -49,7 +49,6 @@ import           Potato.Flow.OwlWorkspace import           Potato.Flow.Render import           Potato.Flow.Methods.SEltMethods-import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import  Potato.Flow.Preview  import Potato.Flow.Methods.LlamaWorks@@ -130,8 +129,14 @@       , _renderContext_renderedCanvasRegion = initialemptyrcr       , _renderContext_cache = emptyRenderCache     }+    +    -- TODO this is incorrect becaues it doesn't use pan or screenx/y     initialrc = _renderContext_renderedCanvasRegion $ render initialCanvasBox initialselts initialrendercontext +    -- TODO use this, except this breaks the initial render whne opening tinytools-vty for some reason (it should rerender in the endoSetCanvasSide function aftre postbuild where the screenx/y get set corrcetly but it dosen't)+    --initialrc = _renderContext_renderedCanvasRegion $ render_new (LBox (-(_controllerMeta_pan controllermeta)) sr) (traceShowId $ initialselts) initialrendercontext++     goat = GoatState {         _goatState_workspace      = fst $ loadOwlPFStateIntoWorkspace (initialstate) emptyWorkspace         , _goatState_pan             = _controllerMeta_pan controllermeta@@ -298,12 +303,14 @@  endoGoatCmdLoad :: (SPotatoFlow, ControllerMeta) -> GoatState -> GoatState endoGoatCmdLoad (spf, cm) gs = r where-  gs' = goat_applyWSEvent WSEventType_Local_Refresh (WSELoad spf) gs-  r = gs' {+  gs_after_load = goat_applyWSEvent WSEventType_Local_Refresh (WSELoad spf) gs+  gs_for_move = gs_after_load {       _goatState_pan = _controllerMeta_pan cm-      , _goatState_layersState = makeLayersStateFromOwlPFState (goatState_pFState gs') (_controllerMeta_layers cm)+      , _goatState_layersState = makeLayersStateFromOwlPFState (goatState_pFState gs_after_load) (_controllerMeta_layers cm)       -- NOTE _goatState_layersHandler gets set by goat_applyWSEvent during refresh     }+  -- rerenderAfterMove since we may have loaded a different pan+  r = goat_rerenderAfterMove gs_for_move  goat_setFocusedArea :: GoatFocusedArea -> GoatState -> GoatState goat_setFocusedArea gfa goatState = r where@@ -316,7 +323,7 @@     then let         goatState_afterAction = case pHandleKeyboard (_goatState_layersHandler goatState) potatoHandlerInput (KeyboardData KeyboardKey_Return []) of           Nothing -> noactionneeded-          Just pho -> traceShow "press enter" $ goat_processLayersHandlerOutput pho goatstatewithnewfocus+          Just pho -> goat_processLayersHandlerOutput pho goatstatewithnewfocus       in assert (_goatState_focusedArea goatState == GoatFocusedArea_Layers) $ goatState_afterAction     else noactionneeded @@ -386,7 +393,7 @@   endoGoatCmdKeyboard :: KeyboardData -> GoatState -> GoatState-endoGoatCmdKeyboard kbd' goatState = r where+endoGoatCmdKeyboard kbd' goatState = r_d0 where   -- TODO you need to do reset logic for this (basically, reset it anytime there was a non-keyboard event)   last_unbrokenInput = _goatState_unbrokenInput goatState   next_unbrokenInput = case kbd' of@@ -399,7 +406,7 @@   -- TODO rename to canvasHandler   handler = _goatState_handler goatState_withKeyboard -  r = case mkbd of+  r_d0 = case mkbd of     Nothing -> goatState_withKeyboard     -- special case, treat escape cancel mouse drag as a mouse input     Just (KeyboardData KeyboardKey_Esc _) | mouseDrag_isActive (_goatState_mouseDrag goatState_withKeyboard) -> r where@@ -480,6 +487,7 @@                 'p' -> Just Tool_Pan                 'b' -> Just Tool_Box                 'l' -> Just Tool_Line+                't' -> Just Tool_Text                 'n' -> Just Tool_TextArea                 _   -> Nothing               newHandler = maybe (_goatState_handler goatState_withKeyboard) (makeHandlerFromNewTool goatState_withKeyboard) mtool@@ -497,7 +505,7 @@     else (rc, False)  -goat_renderCanvas_update :: (HasCallStack) => RenderContext -> NeedsUpdateSet -> SuperOwlChanges -> RenderContext+goat_renderCanvas_update :: RenderContext -> NeedsUpdateSet -> SuperOwlChanges -> RenderContext goat_renderCanvas_update rc needsupdateaabbs cslmap = r where   r = if IM.null cslmap     then rc@@ -572,7 +580,7 @@       , _goatState_renderedSelection = _renderContext_renderedCanvasRegion rc_afterselection     } -computeCanvasSelection :: (HasCallStack) => GoatState -> CanvasSelection+computeCanvasSelection :: GoatState -> CanvasSelection computeCanvasSelection goatState = r where   pfs = goatState_pFState goatState   filterHiddenOrLocked sowl = not $ layerMetaMap_isInheritHiddenOrLocked (_owlPFState_owlTree pfs) (_superOwl_id sowl) (_layersState_meta (_goatState_layersState goatState))@@ -657,9 +665,23 @@     HOA_Nothing -> goatState  + goat_processLayersHandlerOutput :: PotatoHandlerOutput -> GoatState -> GoatState-goat_processLayersHandlerOutput pho goatState = goat_processHandlerOutput_noSetHandler pho $ goatState { _goatState_layersHandler = fromMaybe (_goatState_layersHandler goatState) (_potatoHandlerOutput_nextHandler pho) }+goat_processLayersHandlerOutput pho goatState = r where+  gs' = goatState { +      _goatState_layersHandler = fromMaybe (_goatState_layersHandler goatState) (_potatoHandlerOutput_nextHandler pho) +    }+  gs'' = goat_processHandlerOutput_noSetHandler pho gs' +  -- layers output may have changed our selection so update the handler+  prevcanvasselection = _goatState_canvasSelection goatState+  nextcanvasselection = computeCanvasSelection gs''+  r = if prevcanvasselection == nextcanvasselection+    then gs''+    else gs'' { _goatState_handler = makeHandlerFromSelection nextcanvasselection }+++ goat_processCanvasHandlerOutput :: PotatoHandlerOutput -> GoatState -> GoatState goat_processCanvasHandlerOutput pho goatState = r where   canvasSelection = computeCanvasSelection goatState@@ -736,9 +758,6 @@     then let         layersHandler = _goatState_layersHandler goatState_afterSelection         canvasHandler = _goatState_handler goatState_afterSelection -        -- TODO remove this assert, this will happen for stuff like boxtexthandler-        -- since we don't have multi-user events, the handler should never be active when this happens-        checkvalid = assert (pIsHandlerActive canvasHandler /= HAS_Active_Mouse && pIsHandlerActive layersHandler /= HAS_Active_Mouse)          -- safe for now, since `potatoHandlerInputFromGoatState` does not use `_goatState_handler/_goatState_layersHandler finalGoatState` which is set to `next_handler/next_layersHandler`         next_potatoHandlerInput = potatoHandlerInputFromGoatState goatState_afterSelection@@ -749,7 +768,7 @@         -- TODO cancel the preview          -      in checkvalid goatState_afterSelection {+      in goatState_afterSelection {           _goatState_handler = refreshedCanvasHandler           , _goatState_layersHandler = refreshedLayersHandler         }@@ -762,12 +781,11 @@   -- | set the new handler based on the new Selection and LayersState   next_canvasSelection = computeCanvasSelection goatState_afterSetLayersState -- (TODO pretty sure this is the same as `canvasSelection = computeCanvasSelection goatState_afterSelection` above..) -   -- we check both the pIsHandlerActive and goatState_hasLocalPreview condition to see if we want to recreate the handler   -- actually, we could only check pIsHandlerActive if all handlers properly reported their state-   -- TODO the issue with not (goatState_hasLocalPreview goatState_afterSetLayersState) is that we may actually want to regen the handler we just haven't commit its preview yet (i.e. box creation)-  -- 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+  --  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+  --  ☝🏽 pretty this TODO is not relevant anymore 🤷🏼‍♀️   (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 (makeHandlerFromSelection next_canvasSelection, maybeCommitLocalPreviewToLlamaStackAndClear $ _goatState_workspace goatState_afterSetLayersState)
src/Potato/Flow/Controller/Handler.hs view
@@ -13,14 +13,12 @@ import           Potato.Flow.Owl import           Potato.Flow.Render import           Potato.Flow.OwlState-import           Potato.Flow.OwlWorkspace import           Potato.Flow.Serialization.Snake import qualified Potato.Flow.Preview as Preview  import qualified Potato.Data.Text.Zipper          as TZ  import           Data.Default-import qualified Data.IntMap                      as IM import qualified Data.Sequence                    as Seq import qualified Data.Text                        as T import qualified Text.Show@@ -31,7 +29,6 @@   | HOA_Pan XY    | HOA_Select Bool Selection   | HOA_Layers LayersState SuperOwlChanges-  | HOA_LayersScroll Int   | HOA_Preview Preview.Preview    deriving (Show) 
src/Potato/Flow/Controller/Manipulator/Box.hs view
@@ -24,7 +24,6 @@ import           Potato.Flow.OwlItem import Potato.Flow.Owl import Potato.Flow.OwlState-import Potato.Flow.OwlWorkspace import Potato.Flow.Methods.Types import Potato.Flow.Llama import           Potato.Flow.Methods.LlamaWorks@@ -46,11 +45,18 @@ superOwl_isTransformable sowl ot = case _owlItem_subItem (_superOwl_elt sowl) of   OwlSubItemNone -> False   OwlSubItemFolder _ -> False-  OwlSubItemLine sline -> not $-    (fromMaybe False $ _sAutoLine_attachStart sline <&> (\att -> hasOwlTree_exists ot (_attachment_target att)))-    && (fromMaybe False $ _sAutoLine_attachEnd sline <&> (\att -> hasOwlTree_exists ot (_attachment_target att)))++  -- THE REASON YOU DID THIS IS TO PREVENT FULLY ATTACHED LINES FROM BEING MOVED (because then thei'r endpoints jump around when the attached objects are destroyed)+  -- TODO FIX THE ABOVE+  -- I forgot why I added this, but in any case, we definitely DO want to transform lines if all it's attached parents are also being transformed+  --OwlSubItemLine sline -> not $+  --  (fromMaybe False $ _sAutoLine_attachStart sline <&> (\att -> hasOwlTree_exists ot (_attachment_target att)))+  --  && (fromMaybe False $ _sAutoLine_attachEnd sline <&> (\att -> hasOwlTree_exists ot (_attachment_target att)))++     _ -> True +-- TODO you MAY want to consider adding all lines that have both attachments in the selection to the modify set  transformableSelection :: PotatoHandlerInput -> Seq SuperOwl transformableSelection PotatoHandlerInput {..} = transformableSelection' _potatoHandlerInput_pFState _potatoHandlerInput_canvasSelection @@ -180,7 +186,7 @@ -- 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+constrainDeltaLBox minsize d1@(DeltaLBox (V2 dx dy) (V2 dw dh)) (LBox _ (V2 w h)) = r where   optuple e = (e, -e)    (ndx, ndw) = if dx /= 0 @@ -224,12 +230,13 @@ 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+  --pfs = _potatoHandlerInput_pFState phi+  --lboxes = fmap (\sowl -> _sEltDrawer_box (getDrawer . hasOwlItem_toOwlSubItem $ sowl) pfs) selectionl+  --constraineddbox = foldl' (constrainDeltaLBox 0) dbox lboxes    makeController _ = cmd where     cmd = CTagBoundingBox :=> (Identity $ CBoundingBox {
src/Potato/Flow/Controller/Manipulator/BoxText.hs view
@@ -27,7 +27,6 @@ import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.Owl-import Potato.Flow.OwlWorkspace import Potato.Flow.Llama import           Potato.Flow.Preview @@ -367,8 +366,8 @@   }  -inputBoxLabel :: TextInputState -> Bool -> SuperOwl -> KeyboardKey -> (TextInputState, Maybe Llama)-inputBoxLabel tais undoFirst sowl kk = (newtais, mop) where+inputBoxLabel :: TextInputState -> SuperOwl -> KeyboardKey -> (TextInputState, Maybe Llama)+inputBoxLabel tais sowl kk = (newtais, mop) where   (changed, newtais) = inputSingleLineZipper tais kk   newtext = TZ.value (_textInputState_zipper newtais)   controller = CTagBoxLabelText :=> (Identity $ CMaybeText (DeltaMaybeText (_textInputState_original tais, if newtext == "" then Nothing else Just newtext)))@@ -428,7 +427,7 @@        -- TODO decide what to do with mods -      (nexttais, mllama) = inputBoxLabel _boxLabelHandler_state _boxLabelHandler_undoFirst sowl k+      (nexttais, mllama) = inputBoxLabel _boxLabelHandler_state sowl k       r = def {           _potatoHandlerOutput_nextHandler = Just $ SomePotatoHandler tah {               _boxLabelHandler_state  = nexttais
src/Potato/Flow/Controller/Manipulator/Layers.hs view
@@ -190,32 +190,33 @@       (MouseDragState_Dragging, LDS_Dragging) -> r where          -- we will always place between dropSowl and justAboveDropSowl-        mDropSowlWithOffset = do-          (downsowl, _, offset') <- clickLayerNew lentries leposxy-          return (downsowl, offset')+        mDropSowl = do+          (downsowl, _, _) <- clickLayerNew lentries leposxy+          return downsowl          mJustAboveDropSowl = do-          lentry <- case mDropSowlWithOffset of+          lentry <- case mDropSowl of             Nothing -> Seq.lookup (Seq.length lentries - 1) lentries             Just _  -> Seq.lookup (lepos-1) lentries           return $ _layerEntry_superOwl lentry  -        nparentoffset = case mDropSowlWithOffset of+        nparentoffset = case mDropSowl of           Nothing -> case mJustAboveDropSowl of             Nothing    -> error "this should never happen"             -- we are at the very bottom             Just asowl -> rawxoffset - superOwl_depth asowl -          Just (dsowl, x) -> case mJustAboveDropSowl of+          Just dsowl -> case mJustAboveDropSowl of             -- we are at the very top             Nothing    -> 0             -- limit how deep in the hierarchy we can move based on what's below the cursor-            Just asowl -> max x (superOwl_depth dsowl - superOwl_depth asowl)+            Just asowl -> max x diff where+              x = rawxoffset - superOwl_depth asowl+              diff = superOwl_depth dsowl - superOwl_depth asowl          nsibling = max 0 (- (min 0 nparentoffset)) -         targetspot = case mJustAboveDropSowl of           -- we are dropping at the top of our LayerEntries           Nothing -> OwlSpot noOwl Nothing@@ -253,6 +254,7 @@         }        (MouseDragState_Up, LDS_Selecting leposdown) -> r where+        -- TODO do proper shift selecting         shift = elem KeyModifier_Shift _mouseDrag_modifiers         sowl = _layerEntry_superOwl $ Seq.index lentries leposdown         r = Just $ def {@@ -271,11 +273,16 @@             -- TODO great place for TZ.selectAll when you add selection capability into TZ             zipper = TZ.fromText $ hasOwlItem_name downsowl -            r = Just $ setHandlerOnly LayersRenameHandler {-                _layersRenameHandler_original = resetLayersHandler lh-                , _layersRenameHandler_renaming   = downsowl-                , _layersRenameHandler_index = lepos-                , _layersRenameHandler_zipper   = zipper+            r = if Seq.length (unSuperOwlParliament selection) == 1+              then Just $ setHandlerOnly LayersRenameHandler {+                  _layersRenameHandler_original = resetLayersHandler lh+                  , _layersRenameHandler_renaming   = downsowl+                  , _layersRenameHandler_index = lepos+                  , _layersRenameHandler_zipper   = zipper+                }+              else Just $ def {+                _potatoHandlerOutput_nextHandler = Just $ SomePotatoHandler (resetLayersHandler lh)+                , _potatoHandlerOutput_action = HOA_Select False (SuperOwlParliament $ Seq.singleton downsowl)               }  @@ -412,6 +419,7 @@             else (False, selstack)       newlhre = if childSelected         then case lhre of+          -- TODO this is wrong            LayersHandlerRenderEntryNormal _ mdots renaming lentry -> LayersHandlerRenderEntryNormal LHRESS_ChildSelected mdots renaming lentry           x -> x         else lhre
src/Potato/Flow/Controller/Manipulator/Line.hs view
@@ -23,7 +23,6 @@ import           Potato.Flow.Owl import           Potato.Flow.OwlItem import           Potato.Flow.OwlState-import           Potato.Flow.OwlWorkspace import           Potato.Flow.Serialization.Snake import Potato.Flow.Methods.LlamaWorks import Potato.Flow.Preview@@ -457,6 +456,7 @@   -- TODO finish+{- adjustLineLabelPositionsAfterModifyingOrAddingMidpoint ::   (HasOwlTree a)   => a@@ -484,7 +484,7 @@   -- adjust distance by the change in ratio    r = undefined-+-}   
src/Potato/Flow/Controller/Manipulator/Pan.hs view
@@ -38,7 +38,6 @@           MouseDragState_Up -> case _panHandler_maybePrevHandler of             Nothing -> Just $ SomePotatoHandler (def :: PanHandler)             Just x  -> Just x-          _ -> error "not posible"         , _potatoHandlerOutput_action = HOA_Pan (delta - _panHandler_panDelta)         --, _potatoHandlerOutput_pan = trace (show x <> " delta " <> show delta <> " pan " <> show _panHandler_panDelta <> " from " <> show _mouseDrag_from <> " to " <> show _mouseDrag_to) $ Just (delta - _panHandler_panDelta)       } where delta = _mouseDrag_to - _mouseDrag_from
src/Potato/Flow/Controller/Manipulator/TextArea.hs view
@@ -16,7 +16,6 @@ import           Potato.Flow.Llama import           Potato.Flow.Math import           Potato.Flow.Owl-import           Potato.Flow.OwlWorkspace import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.Preview
src/Potato/Flow/Controller/OwlLayers.hs view
@@ -109,7 +109,7 @@       then if _layerEntry_isCollapsed         then ">"         else "v"-      else " "+      else "●"     hideText = case _layerEntry_hideState of       LHS_True              -> "▓"       LHS_False             -> " "
src/Potato/Flow/Controller/Types.hs view
@@ -38,8 +38,6 @@   }  --- TODO remove Tool_TextArea--- TOOL data Tool = Tool_Select | Tool_Pan | Tool_Box | Tool_Line | Tool_Text | Tool_TextArea deriving (Eq, Show, Enum)  tool_isCreate :: Tool -> Bool
src/Potato/Flow/Llama.hs view
@@ -69,7 +69,7 @@       else Left $ ApplyLLamaError_Soft $ "Invalid canvas size operation " <> show x    r = case r' of -    Right (newState, changes) -> assert (owlPFState_isValid newState) r'+    Right (newState, _) -> assert (owlPFState_isValid newState) r'     Left e -> Left e  undoCmdState :: OwlPFCmd -> OwlPFState -> Either ApplyLlamaError (OwlPFState, SuperOwlChanges)@@ -90,7 +90,7 @@       else Left $ ApplyLLamaError_Soft $ "Invalid canvas size operation " <> show x    r = case r' of-    Right (newState, changes) -> assert (owlPFState_isValid newState) r'+    Right (newState, _) -> assert (owlPFState_isValid newState) r'     Left e                    -> Left e  
src/Potato/Flow/Methods/LineDrawer.hs view
@@ -160,7 +160,7 @@   revgo acc ((cd,d,False):[]) = (flipCartDir cd,d,True):acc   revgo _ ((_,_,True):[]) = error "unexpected subsegment starting anchor at end"   revgo acc ((cd,d,False):xs) = revgo ((flipCartDir cd, d, False):acc) xs-  revgo _ ((_,_,True):xs) = error "TODO this does not handle midpoint subsegment starting anchors correctly (not that it needs to right now)"+  revgo _ ((_,_,True):_) = error "TODO this does not handle midpoint subsegment starting anchors correctly (not that it needs to right now)"   revgostart [] = []   revgostart ((cd,d,True):xs) = revgo [(flipCartDir cd,d,False)] xs   revgostart _ = error "unexpected non-subsegment starting anchor at start"@@ -674,7 +674,7 @@   walk (x@(cd,d,_):rest) curbegin traveld = r2 where     nextbegin = curbegin + cartDirWithDistanceToV2 x     r2 = if traveld + d >= targetd-      then curbegin + cartDirWithDistanceToV2 (cd, targetd - traveld, undefined)+      then curbegin + cartDirWithDistanceToV2 (cd, targetd - traveld, undefined) -- why undefined -__- lol       else walk rest nextbegin (traveld + d)   r = walk (_lineAnchorsForRender_rest lar) (_lineAnchorsForRender_start lar) 0 
src/Potato/Flow/Methods/LlamaWorks.hs view
@@ -7,11 +7,8 @@  import           Relude -import           Potato.Flow.Math import           Potato.Flow.Owl import Potato.Flow.OwlItem-import Potato.Flow.Attachments-import Potato.Flow.OwlWorkspace import Potato.Flow.OwlState import Potato.Flow.Llama import           Potato.Flow.Serialization.Snake@@ -19,7 +16,6 @@  import           Control.Exception (assert) -import qualified Data.Text         as T import qualified Data.IntMap as IM import qualified Data.IntSet as IS import qualified Data.Sequence as Seq
src/Potato/Flow/Owl.hs view
@@ -122,6 +122,10 @@  type SuperOwlChanges = REltIdMap (Maybe SuperOwl) ++instance PotatoShow SuperOwlChanges where+  potatoShow = (<>) "SuperOwlChanges:\n" . foldr (\msowl acc -> maybe "Nothing" potatoShow msowl <> "\n" <> acc) ""+ -- updates AttachmeentMap with a list of SuperOwls (that may be attached to stuff) attachmentMap_addSuperOwls' :: (Foldable f) => (Attachment -> Bool) -> f SuperOwl -> AttachmentMap -> AttachmentMap attachmentMap_addSuperOwls' filterfn sowls am = r where@@ -979,13 +983,13 @@   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+    OwlItem _ (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+    _ -> 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 
src/Potato/Flow/OwlState.hs view
@@ -18,10 +18,8 @@ import           Data.List.Ordered       (isSortedBy) import           Data.Maybe import qualified Data.Text as T-import qualified Data.Sequence as Seq  - -- prob not the best place for these... maybeGetAttachmentPosition :: (HasCallStack) => Bool -> OwlPFState -> Attachment -> Maybe XY maybeGetAttachmentPosition offsetBorder pfs a = do@@ -115,7 +113,7 @@ owlPFState_to_SuperOwlParliament :: OwlPFState -> SuperOwlParliament owlPFState_to_SuperOwlParliament OwlPFState {..} = owlParliament_toSuperOwlParliament _owlPFState_owlTree $ OwlParliament $ _owlTree_topOwls _owlPFState_owlTree -do_newElts :: (HasCallStack) => [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges)+do_newElts :: [(REltId, OwlSpot, OwlItem)] -> OwlPFState -> (OwlPFState, SuperOwlChanges) do_newElts seltls pfs@OwlPFState {..} = r where    -- 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)
src/Potato/Flow/OwlWorkspace.hs view
@@ -18,18 +18,13 @@ import           Relude  import           Potato.Flow.Llama-import           Potato.Flow.Math import           Potato.Flow.Owl-import           Potato.Flow.OwlItem import           Potato.Flow.OwlState-import           Potato.Flow.Serialization.Snake import           Potato.Flow.Types import Potato.Flow.Preview  import           Control.Exception    (assert) import qualified Data.IntMap.Strict   as IM-import qualified Data.IntSet          as IS-import qualified Data.Sequence        as Seq  -- TODO get rid of this, now needed data OwlPFWorkspace = OwlPFWorkspace {@@ -178,16 +173,6 @@ doCmdWorkspace cmd pfw = force r where   r = doLlamaWorkspace (makePFCLlama cmd) pfw -doCmdOwlPFWorkspaceUndoPermanentFirst :: (OwlPFState -> OwlPFCmd) -> OwlPFWorkspace -> (OwlPFWorkspace, SuperOwlChanges)-doCmdOwlPFWorkspaceUndoPermanentFirst cmdFn ws = r where-  -- undoPermanent is actually not necessary as the next action clears the redo stack anyways-  (undoedws, undochanges) = undoPermanentWorkspace ws-  undoedpfs = _owlPFWorkspace_owlPFState undoedws-  cmd = cmdFn undoedpfs-  (newpfs, changes) = doLlamaWorkspace (makePFCLlama cmd) undoedws-  r = (newpfs, IM.union changes undochanges)-- ------ update functions via commands data WSEvent =   -- TODO DELETE@@ -206,16 +191,13 @@ debugPrintBeforeAfterState stateBefore stateAfter = fromString $ "BEFORE: " <> debugPrintOwlPFState stateBefore <> "\nAFTER: " <> debugPrintOwlPFState stateAfter  -noChanges :: OwlPFWorkspace -> (OwlPFWorkspace, SuperOwlChanges)-noChanges ws = (ws, IM.empty)- clearLocalPreview :: (OwlPFWorkspace, SuperOwlChanges) -> (OwlPFWorkspace, SuperOwlChanges) clearLocalPreview (ws, changes) = (ws { _owlPFWorkspace_localPreview = Nothing }, changes)  maybeCommitLocalPreviewToLlamaStackAndClear :: OwlPFWorkspace -> OwlPFWorkspace maybeCommitLocalPreviewToLlamaStackAndClear ws = case _owlPFWorkspace_localPreview ws of   Nothing -> ws-  Just (shep, shift, undollama) -> r_1 where+  Just (_, _, undollama) -> r_1 where     newstack = moveLlamaStackDone undollama (_owlPFWorkspace_llamaStack ws)     r_1 = ws {          _owlPFWorkspace_llamaStack = newstack 
src/Potato/Flow/Preview.hs view
@@ -55,6 +55,7 @@ previewOperation_toUndoFirst :: PreviewOperation -> Bool previewOperation_toUndoFirst po = case po of   PO_Start -> False+  PO_CommitAndStart -> False   PO_Continue -> True   PO_StartAndCommit -> False   PO_ContinueAndCommit -> True
src/Potato/Flow/Render.hs view
@@ -18,6 +18,7 @@   , printRenderedCanvasRegion   , potatoRenderWithOwlTree   , potatoRenderPFState+  , renderedCanvasRegion_getAt   , renderedCanvasToText   , renderedCanvasRegionToText @@ -43,7 +44,6 @@ import Potato.Flow.Owl import           Potato.Flow.Controller.Types import           Potato.Flow.Controller.OwlLayers-import           Potato.Flow.Methods.LineTypes   import qualified Data.IntMap             as IM@@ -72,6 +72,7 @@     r = fmap (,hidden) $ owlTree_findSuperOwl ot rid   sortForRendering (ot,_) sowls = sortForRendering ot sowls + -- RenderContext is a helper container type that provides both read and write data for various render operations data RenderContext = RenderContext {   _renderContext_cache :: RenderCache -- r/w@@ -95,13 +96,8 @@   hasOwlTree_owlTree = hasOwlTree_owlTree . _renderContext_owlTree  instance OwlRenderSet RenderContext where-  findSuperOwl RenderContext {..} rid = findSuperOwl (_renderContext_owlTree, _renderContext_layerMetaMap) rid-  sortForRendering RenderContext {..} sowls = sortForRendering (_renderContext_owlTree, _renderContext_layerMetaMap) sowls----emptyChar :: PChar-emptyChar = ' '+  findSuperOwl RenderContext {..} = findSuperOwl (_renderContext_owlTree, _renderContext_layerMetaMap)+  sortForRendering RenderContext {..} = sortForRendering (_renderContext_owlTree, _renderContext_layerMetaMap)   -- TODO for selection rendering you want to make it V.Vector (Maybe PChar) or maybe you can just use a map?@@ -227,6 +223,9 @@ renderedCanvasToText rcr = renderedCanvasRegionToText (_renderedCanvasRegion_box rcr) rcr  +renderedCanvasRegion_getAt :: RenderedCanvasRegion -> V2 Int -> MWidePChar+renderedCanvasRegion_getAt rcr = (V.!) (_renderedCanvasRegion_contents rcr) . toIndex (_renderedCanvasRegion_box rcr)+ -- TODO this does not handle wide chars at all fack -- | assumes region LBox is strictly contained in _renderedCanvasRegion_box renderedCanvasRegionToText :: LBox -> RenderedCanvasRegion -> Text@@ -326,14 +325,21 @@     Just aabb -> r where       rids = broadPhase_cull aabb (_broadPhaseState_bPTree _renderContext_broadPhase) +      -- NOTE you could just call findSuperOwlForRendering here but you wouldn't get the error+      getSowlFromContext :: REltId -> Maybe SuperOwl+      getSowlFromContext rid = case findSuperOwl rctx rid of+        Nothing -> error "this should never happen, because broadPhase_cull should only give existing seltls"+        -- changes could indicate hidden, if that's the case, give a dummy object to render+        Just (sowl, hidden) -> if hidden then Nothing else Just sowl+       msowls = flip fmap rids $ \rid -> case IM.lookup rid cslmap of-        Nothing -> case findSuperOwl _renderContext_owlTree rid of-          Nothing -> error "this should never happen, because broadPhase_cull should only give existing seltls"-          -- changes could indicate hidden, if that's the case, give a dummy object to render-          Just (sowl, hidden) -> if hidden then Nothing else Just sowl+        Nothing -> getSowlFromContext rid          Just msowl -> case msowl of           Nothing -> error "this should never happen, because deleted seltl would have been culled in broadPhase_cull"-          Just sowl -> Just sowl+          -- this way is a little more performant but doesn't cover the edge case where an element got moved into a hidden folder (you need to set msowl to Nothing outside of this function)+          --Just sowl -> Just sowl+          Just _ -> getSowlFromContext rid+       sowls = sortForRendering _renderContext_owlTree $ Seq.fromList (catMaybes msowls)       sortedrids = fmap _superOwl_id $ toList sowls       r = render_new aabb sortedrids rctx
src/Potato/Flow/RenderCache.hs view
@@ -13,7 +13,6 @@   import qualified Data.IntMap             as IM-import qualified Data.Text.IO as T import qualified Data.Vector.Unboxed     as V import Control.Exception (assert) 
src/Potato/Flow/Serialization/Versions/V1/SElts.hs view
@@ -74,7 +74,7 @@   show = superStyle_toListFormat  superStyle_fromListFormat :: [PChar] -> SuperStyle-superStyle_fromListFormat chars = assert (l == 7 || l == 8) $ r where+superStyle_fromListFormat chars = assert (l == 6 || l == 7) $ r where   l = length chars   r = SuperStyle {     _superStyle_tl = Just $ chars L.!! 0@@ -84,7 +84,7 @@     , _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_fill = if l == 6 then FillStyle_Blank else FillStyle_Simple (chars `debugBangBang` 6)   }  -- superStyle_fromListFormat "╔╗╚╝║═█" `shouldBe` def
src/Potato/Flow/Types.hs view
@@ -67,6 +67,7 @@   + type LayerPos = Int type REltIdMap a = IM.IntMap a type SuperSEltLabel = (REltId, LayerPos, SEltLabel)
test/Potato/Data/Text/UnicodeSpec.hs view
@@ -13,6 +13,8 @@ import           Potato.Data.Text.Unicode  +doubleWidthChars :: String+doubleWidthChars = "there are no space between adjacent characters"  spec :: Spec spec = describe "Unicode" $ do@@ -36,3 +38,6 @@   it "containsGraphemeCluster" $ do     containsGraphemeCluster "👎👎👎👎👎" `shouldBe` False     containsGraphemeCluster "👎👎👎🏿👎👎👎" `shouldBe` True+  forM_ doubleWidthChars $ \c -> do+    it ("getCharWidth " <> show c) $ do+      getCharWidth c `shouldBe` 2
test/Potato/Data/Text/ZipperSpec.hs view
@@ -85,7 +85,9 @@       dl1 = displayLinesWithAlignment TextAlignment_Right 10 () () (fromText "aoeu\n\n\naoeu")       dl2 = displayLinesWithAlignment TextAlignment_Right 10 () () (fromText "\n\n\naoeu")       dl3 = displayLinesWithAlignment TextAlignment_Right 10 () () (fromText "aoeu\n\n\n")+      dl4 = displayLinesWithAlignment TextAlignment_Right 10 () () empty +     insertcharnewlinesentence `shouldBe` fromText newlineSentence      -- NOTE last " " is the generated cursor span char@@ -93,7 +95,14 @@     _displayLines_spans dl1 `shouldBe` makespans [["aoeu"],[""],[""],["aoeu", ""]]     _displayLines_spans dl2 `shouldBe` makespans [[""],[""],[""],["aoeu", ""]]     _displayLines_spans dl3 `shouldBe` makespans [["aoeu"],[""],[""],[""]]-    +    _displayLines_spans dl4 `shouldBe` makespans [[""]]++  it "displayLinesWithAlignment - cursor tag" $ do +    let+      dl0 = displayLinesWithAlignment TextAlignment_Right 10 0 1 (fromText "abc")+      dl1 = displayLinesWithAlignment TextAlignment_Right 10 0 1 empty+    _displayLines_spans dl0 `shouldBe` [[Span 0 "abc", Span 1 ""]]+    _displayLines_spans dl1 `shouldBe` [[Span 1 ""]]     it "displayLines - cursorPos" $ do
test/Potato/Flow/AttachmentsSpec.hs view
@@ -9,8 +9,6 @@ import           Test.Hspec  import           Potato.Flow.Attachments-import Potato.Flow.Methods.LineDrawer-import Potato.Flow.Methods.LineTypes import           Potato.Flow.Math import Potato.Flow.Serialization.Snake 
+ test/Potato/Flow/Controller/GeneralSpec.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RecursiveDo     #-}++module Potato.Flow.Controller.GeneralSpec (+  spec+) where++import           Relude                                         hiding (empty,+                                                                 fromList)++import           Test.Hspec++import           Potato.Flow.GoatTester++import           Potato.Flow+import           Potato.Flow.Controller.Manipulator.TestHelpers++verifyHasUnsavedChanges :: Bool -> GoatTester ()+verifyHasUnsavedChanges b = do+  gs <- getGoatState+  let b' = goatState_hasUnsavedChanges gs+  verify "has unsaved changes" $ if b' == b then Nothing else Just $ "expected " <> show b <> " got " <> show b'++has_unsaved_changes_basic :: Spec+has_unsaved_changes_basic = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do+  setMarker "draw a box"+  drawCanvasBox (0,0,10,10)+  verifyHasUnsavedChanges True++  setMarker "save"+  markSaved+  verifyHasUnsavedChanges False++  setMarker "undo"+  pressUndo +  verifyHasUnsavedChanges True++  setMarker "redo"+  pressRedo+  verifyHasUnsavedChanges False+++spec :: Spec+spec = do+  describe "General" $ do+    describe "has_unsaved_changes_basic" $ has_unsaved_changes_basic+
test/Potato/Flow/Controller/Manipulator/BoxSpec.hs view
@@ -13,7 +13,6 @@ import           Potato.Flow.GoatTester  import           Potato.Flow-import           Potato.Flow.Common import           Potato.Flow.Controller.Manipulator.TestHelpers  
test/Potato/Flow/Controller/Manipulator/LayersSpec.hs view
@@ -11,14 +11,13 @@ import           Test.Hspec  import           Potato.Flow.GoatTester- import           Potato.Flow import           Potato.Flow.Controller.Manipulator.TestHelpers -import qualified Data.List                                      as L import qualified Data.Sequence as Seq  + someFolderName :: Text someFolderName = "testfolder" @@ -32,7 +31,21 @@ 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 +verifyNthEntryInLayersPropertyIs :: (Show a, Eq a) => Int -> (LayerEntry -> a) -> a -> GoatTester()+verifyNthEntryInLayersPropertyIs n f v = verifyState (show n <> " entry in layers has property " <> show v) $ 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 == v then Nothing else Just $ "expected " <> show v <> ", got " <> show firstentry where+        firstentry = f lentry+ verifyNthEntryInLayersIs :: Int -> REltId -> GoatTester()+verifyNthEntryInLayersIs n rid = verifyNthEntryInLayersPropertyIs n (_superOwl_id . _layerEntry_superOwl) rid++{--+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@@ -41,11 +54,16 @@       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      +verifyNthEntryInLayersHasDepth :: Int -> Int -> GoatTester()+verifyNthEntryInLayersHasDepth n d = verifyNthEntryInLayersPropertyIs n layerEntry_depth d++ create_select_test :: Spec create_select_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do @@ -63,7 +81,36 @@   layerMouseDownUp (5,0)   verifyFolderSelected someFolderName +shift_select_test :: Spec+shift_select_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do +  let xoff = 10++  setMarker "draw several boxes"+  forM_ [0..9] $ \_ -> drawCanvasBox (0, 0, 100, 100)+  verifyOwlCount 10+  pressEscape+  verifySelectionCount 0++  setMarker "shift select some stuff"+  forM_ [0..9] $ \n -> layerMouseDownUpWithModifier (xoff, n) KeyModifier_Shift+  verifySelectionCount 10+  +  setMarker "shift unselect stuff"+  layerMouseDownUpWithModifier (xoff, 5) KeyModifier_Shift+  verifySelectionCount 9+  layerMouseDownUpWithModifier (xoff, 8) KeyModifier_Shift+  verifySelectionCount 8+  +  setMarker "regular select one already selected element"+  layerMouseDownUp (xoff, 2)+  verifySelectionCount 1+  +  setMarker "deselect everything"+  pressEscape+  verifySelectionCount 0++ rename_focus_clickCanvas_test :: Spec rename_focus_clickCanvas_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do @@ -332,10 +379,75 @@     +drag_folder_depth_test :: Spec+drag_folder_depth_test = hSpecGoatTesterWithOwlPFState emptyOwlPFState $ do++  setMarker "setup scene"+  drawCanvasBox (0,0,10,10) -- 1+  addFolder someFolderName -- 2+  addFolder someFolderName -- 3+  addFolder someFolderName -- 4+  addFolder someFolderName -- 5+  addFolder someFolderName -- 6+  addFolder someFolderName -- 7+  addFolder someFolderName -- 8++  -- 2testfolder+  --   3testfolder+  --     4testfolder+  --       5testfolder+  --         6testfolder+  --           7testfolder+  --             8testfolder+  -- 1<box>++  setMarker "drag the box into the 8th folder"+  layerMouseDownUpRel LMO_Normal 7 0+  layerMouseDownRel LMO_Normal 7 0+  -- we are outside of the last folder so we need to drag above ourselves+  layerMouseDownUpRel (LMO_DropInFolder 0) 7 7+  verifyNthEntryInLayersIs 7 1+  verifyNthEntryInLayersHasDepth 7 7++  -- 2testfolder+  --   3testfolder+  --     4testfolder+  --       5testfolder+  --         6testfolder+  --           7testfolder+  --             8testfolder+  --               1<box>++  setMarker "deselect"+  pressEscape++  setMarker "drag the box into the 5th folder"+  layerMouseDownUpRel LMO_Normal 7 7+  layerMouseDownRel LMO_Normal 7 7++  -- we are inside the last folder, we are dragging BELOW ourselves+  layerMouseDownUpRel (LMO_DropInFolder (-3)) 8 7+  verifyNthEntryInLayersIs 7 1+  verifyNthEntryInLayersHasDepth 7 4++++  -- 2testfolder+  --   3testfolder+  --     4testfolder+  --       5testfolder+  --         6testfolder+  --           7testfolder+  --             8testfolder+  --         1<box>+++ spec :: Spec spec = do   describe "Layers" $ do     describe "create_select_test" $ create_select_test+    describe "shift_select_test" $ shift_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@@ -345,3 +457,4 @@     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+    describe "drag_folder_depth_test" $ drag_folder_depth_test
test/Potato/Flow/Controller/Manipulator/LineSpec.hs view
@@ -565,7 +565,8 @@     describe "midpoint_modify_basic" $  midpoint_modify_basic_test     describe "midpoint_double_adjacent_delete" $  midpoint_double_adjacent_delete_test     describe "attaching_delete_test" $ attaching_delete_test-    describe "attaching_fully_attached_wont_move_test" $ attaching_fully_attached_wont_move_test+    -- TODO reenable once you fix the issue in Box.hs -- THE REASON YOU DID THIS IS TO PREVENT FULLY ATTACHED LINES FROM BEING MOVED+    --describe "attaching_fully_attached_wont_move_test" $ attaching_fully_attached_wont_move_test     describe "label_undo_test" $ label_undo_test      describe "label_cursor_test" $ label_cursor_test
test/Potato/Flow/Controller/Manipulator/TestHelpers.hs view
@@ -16,7 +16,7 @@   let     f sowl = case _superOwl_elt sowl of       OwlItem _ (OwlSubItemBox _) -> Nothing-      x                           -> Just ("expected box, got " <> show x)+      xx                           -> Just ("expected box, got " <> show xx)   verifySelectionIsAndOnlyIs "box is selected" f  
test/Potato/Flow/Deprecated/Controller/EverythingWidgetSpec.hs view
@@ -284,26 +284,6 @@      ] --- TODO FINISH-everything_newfolder_test :: Test-everything_newfolder_test = constructTest "new folder" owlpfstate_basic1 bs expected where-  bs = [-      EWCLabel "New Folder (no selection)"-      , EWCNewFolder--      , EWCLabel "New Folder (with selection)"-      -- TODO open up some folder, and select some stuff-      --, EWCNewFolder-    ]-  expected = [-      LabelCheck "New Folder (no selection)"-      , firstSelectedSuperOwlPredicate (Just "<folder>") (\sowl -> case hasOwlItem_toSElt_hack sowl of-        SEltFolderStart -> True-        _                                        -> False)--      , LabelCheck "New Folder (with selection)"-    ]- moveOffset :: Int moveOffset = 100 
test/Potato/Flow/GoatTester.hs view
@@ -238,8 +238,23 @@   layerMouseDown (x,y)   layerMouseUp (x,y) -data LayerMouseOp = LMO_Collapse | LMO_Hide | LMO_Lock | LMO_Normal deriving (Show, Eq) +layerMouseDownWithModifier :: (Monad m) =>  (Int, Int) -> KeyModifier -> GoatTesterT m ()+layerMouseDownWithModifier (x,y) modifier = mouse False False [modifier] MouseButton_Left (V2 x y)++layerMouseUpWithModifier :: (Monad m) =>  (Int, Int) -> KeyModifier -> GoatTesterT m ()+layerMouseUpWithModifier (x,y) modifier = mouse False True [modifier] MouseButton_Left (V2 x y)++layerMouseDownUpWithModifier :: (Monad m) =>  (Int, Int) -> KeyModifier -> GoatTesterT m ()+layerMouseDownUpWithModifier (x,y) modifier = do+  layerMouseDownWithModifier (x,y) modifier +  layerMouseUpWithModifier (x,y) modifier+++data LayerMouseOp = LMO_Collapse | LMO_Hide | LMO_Lock | LMO_Normal +  | LMO_DropInFolder Int -- desired position relative to depth+  deriving (Show, Eq)+ layerMouseRel' :: (Monad m) => Bool -> LayerMouseOp -> Int -> Int -> GoatTesterT m () layerMouseRel' isup op y' depth = do   scrollPos <- (_layersState_scrollPos . _goatState_layersState . _goatTesterState_goatState) <$> get@@ -249,6 +264,7 @@       LMO_Hide     -> 1       LMO_Lock     -> 2       LMO_Normal -> 3+      LMO_DropInFolder rel -> rel     y = y' - scrollPos     x = x' + depth   putRecord "validate y pos" (if y < 0 then Just ("y: " <> show y <> " outside of scroll position: " <> show scrollPos) else Nothing)@@ -263,11 +279,13 @@ layerMouseUpRel = layerMouseRel' True  layerMouseDownUpRel :: (Monad m) => LayerMouseOp -> Int -> Int -> GoatTesterT m ()-layerMouseDownUpRel op x y = do-  layerMouseDownRel op x y-  layerMouseUpRel op x y+layerMouseDownUpRel op y depth = do+  layerMouseDownRel op y depth+  layerMouseUpRel op y depth  ++ pressKeyboardKey :: (Monad m) => KeyboardKey -> GoatTesterT m () pressKeyboardKey k = runEndo $ endoGoatCmdKeyboard (KeyboardData k []) @@ -352,6 +370,26 @@       then Just $ "failed, expected 1 selected 🦉, got " <> show nselection       else (\m -> "failed with message: " <> m <> "\ngot:\n" <> potatoShow (_superOwl_elt sowl)) <$> f sowl +++verifyRenderNonEmptyCount :: (Monad m) => Int -> GoatTesterT m ()+verifyRenderNonEmptyCount expected = verifyState "verifyRenderNonEmptyCount" f where+  f gs = if n == expected+    then Nothing+    else Just $ "got " <> show n <> " non-empty chars, expected " <> show expected+    where n =  renderedCanvasRegion_nonEmptyCount (_goatState_renderedCanvas gs)++verifyCharRenderedAt :: (Monad m) => V2 Int -> Maybe Char -> GoatTesterT m ()+verifyCharRenderedAt pos mchar = verifyState "verifyCharRenderedAt" f where+  f gs = if mchar == mchar'+    then Nothing+    else Just $ "expected: " <> show mchar <> "\ngot: " <> show mchar'+    where+      mchar' = case renderedCanvasRegion_getAt (_goatState_renderedCanvas gs) pos of+        (-1, _) -> Nothing+        (_, c) -> Just c++ -- otheruseful stuff  -- export as part of this module becaues it's super useful@@ -364,3 +402,16 @@ alwaysFail :: (Monad m) => Text -> GoatTesterT m () alwaysFail msg = GoatTesterT $ do   unGoatTesterT $ putRecord "this test always fails" (Just msg)+++debugFailWithLayers :: (Monad m) => GoatTesterT m ()+debugFailWithLayers = do+  gs <- getGoatState+  putRecord "debugFailWithLayers" (Just $ potatoShow $ _goatState_layersState gs)++debugFailWithRenderedCanvas :: (Monad m) => GoatTesterT m ()+debugFailWithRenderedCanvas = do+  gs <- getGoatState+  let+    canvas = _goatState_renderedCanvas gs+  putRecord "debugFailWithRenderedCanvas" (Just $ renderedCanvasToText canvas)
test/Potato/Flow/LlamaSpec.hs view
@@ -7,7 +7,7 @@  import           Test.Hspec -import Potato.Flow.Llama+--import Potato.Flow.Llama  spec :: Spec spec = do
test/Potato/Flow/Methods/LineDrawerSpec.hs view
@@ -131,7 +131,7 @@       fmap fst3 (getSortedSAutoLineLabelPositions owlpfstate_zero someSAutoLine_withLabels) `shouldBe` [V2 0 0, V2 25 0, V2 50 0]  -  describe "Lines - rendering" $ it "autorendercase" $ forM_ generateTestCases $ \pfs -> do+  describe "Lines - rendering" $ it "autorendercase" $ forM_ generateTestCases $ \_ -> do     --putTextLn (renderedCanvasToText (potatoRenderPFState pfs))     True `shouldBe` True 
test/Potato/Flow/RenderSpec.hs view
@@ -14,7 +14,10 @@  import           Potato.Flow import           Potato.Flow.TestStates+import Potato.Flow.GoatTester+import           Potato.Flow.Controller.Manipulator.TestHelpers + testCanvas :: Int -> Int -> Int -> Int -> RenderedCanvasRegion testCanvas x y w h = emptyRenderedCanvasRegion (LBox (V2 x y) (V2 w h)) @@ -33,8 +36,10 @@ testsstyle :: SuperStyle testsstyle = def { _superStyle_fill = FillStyle_Simple '@' } -spec :: Spec-spec = do+++spec1 :: Spec+spec1 = do   describe "Canvas" $ do     it "potato renders blank text" $ do       let@@ -164,3 +169,79 @@         canvas1 = _renderContext_renderedCanvasRegion rendercontext1       -- TODO test something       canvas1 `shouldBe` canvas1+++emptyOwlStateWithSize :: (Int, Int) -> OwlPFState+emptyOwlStateWithSize (x, y) = OwlPFState emptyOwlTree (SCanvas (LBox 0 (V2 x y)))++render_basic :: Spec+render_basic = hSpecGoatTesterWithOwlPFState (emptyOwlStateWithSize (20,20)) $ do++  setMarker "verify empty initial state"+  verifyRenderNonEmptyCount 0++  setMarker "draw a box"+  drawCanvasBox (0, 0, 2, 2)+  verifyRenderNonEmptyCount 4++  setMarker "hide the box"+  layerMouseDownUpRel LMO_Hide 0 0+  verifyRenderNonEmptyCount 0++  setMarker "unhide the box"+  layerMouseDownUpRel LMO_Hide 0 0+  verifyRenderNonEmptyCount 4++  setMarker "delete the box"+  pressBackspace+  verifyRenderNonEmptyCount 0+++render_hide_basic :: Spec+render_hide_basic = hSpecGoatTesterWithOwlPFState (emptyOwlStateWithSize (20,20)) $ do++  setMarker "verify empty initial state"+  verifyRenderNonEmptyCount 0++  setMarker "make a folder"+  addFolder "hide me"++  setMarker "draw a box"+  drawCanvasBox (0, 0, 2, 2)+  verifyRenderNonEmptyCount 4++  setMarker "hide the folder"+  layerMouseDownUpRel LMO_Hide 0 0+  verifyRenderNonEmptyCount 0++  setMarker "deselect evertyhing"+  pressEscape+  verifySelectionCount 0++  setMarker "drag the box out of the folder"+  layerMouseDownUpRel LMO_Normal 1 1+  verifySelectionCount 1+  layerMouseDownRel LMO_Normal 1 1+  layerMouseDownUpRel (LMO_DropInFolder 0) 2 0+  verifyRenderNonEmptyCount 4++  setMarker "drag the box into the folder"+  layerMouseDownRel LMO_Normal 1 0+  layerMouseDownUpRel (LMO_DropInFolder 0) 1 1+  verifyRenderNonEmptyCount 0++  +++spec2 :: Spec+spec2 = do+  describe "Render" $ do+    describe "render_basic" $ render_basic+    describe "render_hide_basic" $ render_hide_basic++++spec :: Spec+spec = do+  spec1+  spec2
tinytools.cabal view
@@ -1,7 +1,7 @@ cabal-version: 1.12  name:           tinytools-version:        0.1.0.5+version:        0.1.0.6 description:    tinytools is a mono-space unicode diagram editor homepage:       https://github.com/minimapletinytools/tinytools#readme bug-reports:    https://github.com/minimapletinytools/tinytools/issues@@ -17,6 +17,7 @@     README.md     ChangeLog.md +Tested-With: GHC ==9.2.5  source-repository head   type: git@@ -115,8 +116,7 @@       NoImplicitPrelude   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -fno-ignore-asserts   build-depends:-      MonadRandom-    , aeson+    aeson     , aeson-pretty     , base >=4.7 && <5     , bimap@@ -131,15 +131,11 @@     , dependent-sum     , dependent-sum-template     , extra-    , hashable     , ilist     , lens     , linear-    , listsafe     , mtl-    , patch     , pretty-simple-    , random-shuffle     , ref-tf     , reflex >= 0.9.2 && < 1     , reflex-potatoes >=0.1@@ -169,6 +165,7 @@       Potato.Flow.Controller.Manipulator.LayersSpec       Potato.Flow.Controller.Manipulator.LineSpec       Potato.Flow.Controller.Manipulator.PanSpec+      Potato.Flow.Controller.GeneralSpec       Potato.Flow.Controller.Manipulator.TestHelpers       Potato.Flow.Deprecated.Controller.EverythingWidgetSpec       Potato.Flow.Deprecated.Controller.LayersSpec@@ -230,7 +227,6 @@   ghc-options: -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N -fno-ignore-asserts   build-depends:       HUnit-    , MonadRandom     , aeson     , base >=4.7 && <5     , bimap@@ -245,17 +241,13 @@     , dependent-sum     , dependent-sum-template     , extra-    , hashable     , hspec     , hspec-contrib     , ilist     , lens     , linear-    , listsafe     , mtl-    , patch     , pretty-simple-    , random-shuffle     , ref-tf     , reflex >= 0.9.2 && < 1     , reflex-potatoes >=0.1@@ -268,4 +260,5 @@     , tinytools     , vector     , vty+  build-tool-depends: hspec-discover:hspec-discover   default-language: Haskell2010