Skip to content

Commit

Permalink
Decompose UIState record
Browse files Browse the repository at this point in the history
  • Loading branch information
kostmo committed Feb 18, 2024
1 parent a1bab6d commit cfdbb4c
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 376 deletions.
276 changes: 143 additions & 133 deletions src/Swarm/TUI/Controller.hs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/Swarm/TUI/Controller/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ openModal :: ModalType -> EventM Name AppState ()
openModal mt = do
newModal <- gets $ flip generateModal mt
ensurePause
uiState . uiModal ?= newModal
uiState . uiGameplay . uiModal ?= newModal
-- Beep
case mt of
ScenarioEndModal _ -> do
Expand All @@ -68,7 +68,7 @@ isRunningModal = \case
_ -> False

setFocus :: FocusablePanel -> EventM Name AppState ()
setFocus name = uiState . uiFocusRing %= focusSetCurrent (FocusablePanel name)
setFocus name = uiState . uiGameplay . uiFocusRing %= focusSetCurrent (FocusablePanel name)

immediatelyRedrawWorld :: EventM Name AppState ()
immediatelyRedrawWorld = do
Expand Down
49 changes: 25 additions & 24 deletions src/Swarm/TUI/Editor/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,46 +40,46 @@ activateWorldEditorFunction :: WorldEditorFocusable -> EventM Name AppState ()
activateWorldEditorFunction BrushSelector = openModal TerrainPaletteModal
activateWorldEditorFunction EntitySelector = openModal EntityPaletteModal
activateWorldEditorFunction AreaSelector = do
selectorStage <- use $ uiState . uiWorldEditor . editingBounds . boundsSelectionStep
selectorStage <- use $ uiState . uiGameplay . uiWorldEditor . editingBounds . boundsSelectionStep
case selectorStage of
SelectionComplete -> uiState . uiWorldEditor . editingBounds . boundsSelectionStep .= UpperLeftPending
SelectionComplete -> uiState . uiGameplay . uiWorldEditor . editingBounds . boundsSelectionStep .= UpperLeftPending
_ -> return ()
activateWorldEditorFunction OutputPathSelector =
-- TODO: #1371
liftIO $ putStrLn "File selection"
activateWorldEditorFunction MapSaveButton = saveMapFile
activateWorldEditorFunction ClearEntityButton =
uiState . uiWorldEditor . entityPaintList . BL.listSelectedL .= Nothing
uiState . uiGameplay . uiWorldEditor . entityPaintList . BL.listSelectedL .= Nothing

handleCtrlLeftClick :: B.Location -> EventM Name AppState ()
handleCtrlLeftClick mouseLoc = do
worldEditor <- use $ uiState . uiWorldEditor
worldEditor <- use $ uiState . uiGameplay . uiWorldEditor
_ <- runMaybeT $ do
guard $ worldEditor ^. worldOverdraw . isWorldEditorEnabled
let getSelected x = snd <$> BL.listSelectedElement x
maybeTerrainType = getSelected $ worldEditor ^. terrainList
maybeEntityPaint = getSelected $ worldEditor ^. entityPaintList
terrain <- hoistMaybe maybeTerrainType
mouseCoords <- MaybeT $ Brick.zoom gameState $ mouseLocToWorldCoords mouseLoc
uiState . uiWorldEditor . worldOverdraw . paintedTerrain %= M.insert (mouseCoords ^. planar) (terrain, maybeToErasable maybeEntityPaint)
uiState . uiWorldEditor . lastWorldEditorMessage .= Nothing
uiState . uiGameplay . uiWorldEditor . worldOverdraw . paintedTerrain %= M.insert (mouseCoords ^. planar) (terrain, maybeToErasable maybeEntityPaint)
uiState . uiGameplay . uiWorldEditor . lastWorldEditorMessage .= Nothing
immediatelyRedrawWorld
return ()

handleRightClick :: B.Location -> EventM Name AppState ()
handleRightClick mouseLoc = do
worldEditor <- use $ uiState . uiWorldEditor
worldEditor <- use $ uiState . uiGameplay . uiWorldEditor
_ <- runMaybeT $ do
guard $ worldEditor ^. worldOverdraw . isWorldEditorEnabled
mouseCoords <- MaybeT $ Brick.zoom gameState $ mouseLocToWorldCoords mouseLoc
uiState . uiWorldEditor . worldOverdraw . paintedTerrain %= M.delete (mouseCoords ^. planar)
uiState . uiGameplay . uiWorldEditor . worldOverdraw . paintedTerrain %= M.delete (mouseCoords ^. planar)
immediatelyRedrawWorld
return ()

-- | "Eye Dropper" tool:
handleMiddleClick :: B.Location -> EventM Name AppState ()
handleMiddleClick mouseLoc = do
worldEditor <- use $ uiState . uiWorldEditor
worldEditor <- use $ uiState . uiGameplay . uiWorldEditor
when (worldEditor ^. worldOverdraw . isWorldEditorEnabled) $ do
w <- use $ gameState . landscape . multiWorld
let setTerrainPaint coords = do
Expand All @@ -88,65 +88,66 @@ handleMiddleClick mouseLoc = do
(worldEditor ^. worldOverdraw)
w
coords
uiState . uiWorldEditor . terrainList %= BL.listMoveToElement terrain
uiState . uiGameplay . uiWorldEditor . terrainList %= BL.listMoveToElement terrain
forM_ maybeElementPaint $ \elementPaint ->
let p = case elementPaint of
Facade efd -> efd
Ref r -> mkFacade r
in uiState . uiWorldEditor . entityPaintList %= BL.listMoveToElement p
in uiState . uiGameplay . uiWorldEditor . entityPaintList %= BL.listMoveToElement p

mouseCoordsM <- Brick.zoom gameState $ mouseLocToWorldCoords mouseLoc
whenJust mouseCoordsM setTerrainPaint

-- | Handle user input events in the robot panel.
handleWorldEditorPanelEvent :: BrickEvent Name AppEvent -> EventM Name AppState ()
handleWorldEditorPanelEvent = \case
Key V.KEsc -> uiState . uiWorldEditor . editingBounds . boundsSelectionStep .= SelectionComplete
Key V.KEsc -> uiState . uiGameplay . uiWorldEditor . editingBounds . boundsSelectionStep .= SelectionComplete
Key V.KEnter -> do
fring <- use $ uiState . uiWorldEditor . editorFocusRing
fring <- use $ uiState . uiGameplay . uiWorldEditor . editorFocusRing
case focusGetCurrent fring of
Just (WorldEditorPanelControl x) -> activateWorldEditorFunction x
_ -> return ()
ControlChar 's' -> saveMapFile
CharKey '\t' -> uiState . uiWorldEditor . editorFocusRing %= focusNext
Key V.KBackTab -> uiState . uiWorldEditor . editorFocusRing %= focusPrev
CharKey '\t' -> uiState . uiGameplay . uiWorldEditor . editorFocusRing %= focusNext
Key V.KBackTab -> uiState . uiGameplay . uiWorldEditor . editorFocusRing %= focusPrev
_ -> return ()

-- | Return value: whether the cursor position should be updated
updateAreaBounds :: Maybe (Cosmic W.Coords) -> EventM Name AppState Bool
updateAreaBounds = \case
Nothing -> return True
Just mouseCoords -> do
selectorStage <- use $ uiState . uiWorldEditor . editingBounds . boundsSelectionStep
selectorStage <- use $ uiState . uiGameplay . uiWorldEditor . editingBounds . boundsSelectionStep
case selectorStage of
UpperLeftPending -> do
uiState . uiWorldEditor . editingBounds . boundsSelectionStep .= LowerRightPending mouseCoords
uiState . uiGameplay . uiWorldEditor . editingBounds . boundsSelectionStep .= LowerRightPending mouseCoords
return False
-- TODO (#1152): Validate that the lower-right click is below and to the right of
-- the top-left coord and that they are within the same subworld
LowerRightPending upperLeftMouseCoords -> do
uiState
. uiGameplay
. uiWorldEditor
. editingBounds
. boundsRect
.= Just (fmap (,view planar mouseCoords) upperLeftMouseCoords)
uiState . uiWorldEditor . lastWorldEditorMessage .= Nothing
uiState . uiWorldEditor . editingBounds . boundsSelectionStep .= SelectionComplete
uiState . uiGameplay . uiWorldEditor . lastWorldEditorMessage .= Nothing
uiState . uiGameplay . uiWorldEditor . editingBounds . boundsSelectionStep .= SelectionComplete
t <- liftIO $ getTime Monotonic
uiState . uiWorldEditor . editingBounds . boundsPersistDisplayUntil .= t + TimeSpec 2 0
uiState . uiGameplay . uiWorldEditor . editingBounds . boundsPersistDisplayUntil .= t + TimeSpec 2 0
setFocus WorldEditorPanel
return False
SelectionComplete -> return True

saveMapFile :: EventM Name AppState ()
saveMapFile = do
worldEditor <- use $ uiState . uiWorldEditor
maybeBounds <- use $ uiState . uiWorldEditor . editingBounds . boundsRect
worldEditor <- use $ uiState . uiGameplay . uiWorldEditor
maybeBounds <- use $ uiState . uiGameplay . uiWorldEditor . editingBounds . boundsRect
w <- use $ gameState . landscape . multiWorld
let mapCellGrid = EU.getEditedMapRectangle (worldEditor ^. worldOverdraw) maybeBounds w

let fp = worldEditor ^. outputFilePath
maybeScenarioPair <- use $ uiState . scenarioRef
maybeScenarioPair <- use $ uiState . uiGameplay . scenarioRef
liftIO $ Y.encodeFile fp $ constructScenario (fst <$> maybeScenarioPair) mapCellGrid

uiState . uiWorldEditor . lastWorldEditorMessage .= Just "Saved."
uiState . uiGameplay . uiWorldEditor . lastWorldEditorMessage .= Just "Saved."
4 changes: 2 additions & 2 deletions src/Swarm/TUI/Editor/Masking.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import Swarm.TUI.Editor.Model
import Swarm.TUI.Editor.Util qualified as EU
import Swarm.TUI.Model.UI

shouldHideWorldCell :: UIState -> W.Coords -> Bool
shouldHideWorldCell :: UIGameplay -> W.Coords -> Bool
shouldHideWorldCell ui coords =
isOutsideSingleSelectedCorner || isOutsideMapSaveBounds
where
we = ui ^. uiWorldEditor
withinTimeout = ui ^. lastFrameTime < we ^. editingBounds . boundsPersistDisplayUntil
withinTimeout = ui ^. uiTiming . lastFrameTime < we ^. editingBounds . boundsPersistDisplayUntil

isOutsideMapSaveBounds =
withinTimeout
Expand Down
6 changes: 3 additions & 3 deletions src/Swarm/TUI/Editor/View.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ drawWorldEditor toplevelFocusRing uis =
hLimit 30 $
controlsBox <=> statusBox

worldEditor = uis ^. uiWorldEditor
worldEditor = uis ^. uiGameplay . uiWorldEditor
maybeAreaBounds = worldEditor ^. editingBounds . boundsRect

-- TODO (#1150): Use withFocusRing?
Expand Down Expand Up @@ -143,7 +143,7 @@ drawTerrainSelector s =
. hCenter
. vLimit (length (listEnums :: [TerrainType]))
. BL.renderListWithIndex listDrawTerrainElement True
$ s ^. uiState . uiWorldEditor . terrainList
$ s ^. uiState . uiGameplay . uiWorldEditor . terrainList

listDrawTerrainElement :: Int -> Bool -> TerrainType -> Widget Name
listDrawTerrainElement pos _isSelected a =
Expand All @@ -155,7 +155,7 @@ drawEntityPaintSelector s =
. hCenter
. vLimit 10
. BL.renderListWithIndex listDrawEntityPaintElement True
$ s ^. uiState . uiWorldEditor . entityPaintList
$ s ^. uiState . uiGameplay . uiWorldEditor . entityPaintList

listDrawEntityPaintElement :: Int -> Bool -> EntityFacade -> Widget Name
listDrawEntityPaintElement pos _isSelected a =
Expand Down
10 changes: 5 additions & 5 deletions src/Swarm/TUI/Model.hs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ runtimeState :: Lens' AppState RuntimeState
-- info panel (if any).
focusedItem :: AppState -> Maybe InventoryListEntry
focusedItem s = do
list <- s ^? uiState . uiInventory . _Just . _2
list <- s ^? uiState . uiGameplay . uiInventory . uiInventoryList . _Just . _2
(_, entry) <- BL.listSelectedElement list
return entry

Expand All @@ -233,10 +233,10 @@ focusedEntity =

-- | Given the focused robot, populate the UI inventory list in the info
-- panel with information about its inventory.
populateInventoryList :: (MonadState UIState m) => Maybe Robot -> m ()
populateInventoryList Nothing = uiInventory .= Nothing
populateInventoryList :: (MonadState UIInventory m) => Maybe Robot -> m ()
populateInventoryList Nothing = uiInventoryList .= Nothing
populateInventoryList (Just r) = do
mList <- preuse (uiInventory . _Just . _2)
mList <- preuse $ uiInventoryList . _Just . _2
showZero <- use uiShowZero
sortOptions <- use uiInventorySort
search <- use uiInventorySearch
Expand Down Expand Up @@ -285,7 +285,7 @@ populateInventoryList (Just r) = do

-- Finally, populate the newly created list in the UI, and remember
-- the hash of the current robot.
uiInventory .= Just (r ^. inventoryHash, lst)
uiInventoryList .= Just (r ^. inventoryHash, lst)

------------------------------------------------------------
-- App state (= UI state + game state) initialization
Expand Down
30 changes: 15 additions & 15 deletions src/Swarm/TUI/Model/StateUpdate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ constructAppState ::
constructAppState rs ui opts@(AppOpts {..}) = do
let gs = initGameState (mkGameStateConfig rs)
case skipMenu opts of
False -> return $ AppState gs (ui & lgTicksPerSecond .~ defaultInitLgTicksPerSecond) rs
False -> return $ AppState gs (ui & uiGameplay . uiTiming . lgTicksPerSecond .~ defaultInitLgTicksPerSecond) rs
True -> do
(scenario, path) <- loadScenario (fromMaybe "classic" userScenario) (gs ^. landscape . entityMap) (rs ^. worlds)
maybeRunScript <- traverse parseCodeFile scriptToRun
Expand Down Expand Up @@ -253,22 +253,22 @@ scenarioToUIState isAutoplaying siPair@(scenario, _) gs u = do
return $
u
& uiPlaying .~ True
& uiGoal .~ emptyGoalDisplay
& uiGameplay . uiGoal .~ emptyGoalDisplay
& uiCheatMode ||~ isAutoplaying
& uiHideGoals .~ (isAutoplaying && not (u ^. uiCheatMode))
& uiFocusRing .~ initFocusRing
& uiInventory .~ Nothing
& uiInventorySort .~ defaultSortOptions
& uiShowFPS .~ False
& uiShowZero .~ True
& uiREPL .~ initREPLState (u ^. uiREPL . replHistory)
& uiREPL . replHistory %~ restartREPLHistory
& uiGameplay . uiHideGoals .~ (isAutoplaying && not (u ^. uiCheatMode))
& uiGameplay . uiFocusRing .~ initFocusRing
& uiGameplay . uiInventory . uiInventoryList .~ Nothing
& uiGameplay . uiInventory . uiInventorySort .~ defaultSortOptions
& uiGameplay . uiInventory . uiShowZero .~ True
& uiGameplay . uiTiming . uiShowFPS .~ False
& uiGameplay . uiREPL .~ initREPLState (u ^. uiGameplay . uiREPL . replHistory)
& uiGameplay . uiREPL . replHistory %~ restartREPLHistory
& uiAttrMap .~ applyAttrMappings (map (first getWorldAttrName . toAttrPair) $ fst siPair ^. scenarioAttrs) swarmAttrMap
& scenarioRef ?~ siPair
& lastFrameTime .~ curTime
& uiWorldEditor . EM.entityPaintList %~ BL.listReplace entityList Nothing
& uiWorldEditor . EM.editingBounds . EM.boundsRect %~ setNewBounds
& uiStructure
& uiGameplay . scenarioRef ?~ siPair
& uiGameplay . uiTiming . lastFrameTime .~ curTime
& uiGameplay . uiWorldEditor . EM.entityPaintList %~ BL.listReplace entityList Nothing
& uiGameplay . uiWorldEditor . EM.editingBounds . EM.boundsRect %~ setNewBounds
& uiGameplay . uiStructure
.~ StructureDisplay
(SR.makeListWidget . M.elems $ gs ^. discovery . structureRecognition . automatons . originalStructureDefinitions)
(focusSetCurrent (StructureWidgets StructuresList) $ focusRing $ map StructureWidgets listEnums)
Expand Down
Loading

0 comments on commit cfdbb4c

Please sign in to comment.