-
Notifications
You must be signed in to change notification settings - Fork 52
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
525 additions
and
148 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
{-# LANGUAGE PatternSynonyms #-} | ||
|
||
module Swarm.TUI.Controller.ControllerUtils where | ||
|
||
import Brick hiding (Direction) | ||
import Control.Lens | ||
import Control.Monad (unless) | ||
import Graphics.Vty qualified as V | ||
import Swarm.Game.State | ||
import Swarm.TUI.Model | ||
import Swarm.TUI.View.ViewUtils (generateModal) | ||
|
||
-- | Pattern synonyms to simplify brick event handler | ||
pattern Key :: V.Key -> BrickEvent n e | ||
pattern Key k = VtyEvent (V.EvKey k []) | ||
|
||
pattern CharKey, ControlChar, MetaChar :: Char -> BrickEvent n e | ||
pattern CharKey c = VtyEvent (V.EvKey (V.KChar c) []) | ||
pattern ControlChar c = VtyEvent (V.EvKey (V.KChar c) [V.MCtrl]) | ||
pattern MetaChar c = VtyEvent (V.EvKey (V.KChar c) [V.MMeta]) | ||
|
||
pattern ShiftKey :: V.Key -> BrickEvent n e | ||
pattern ShiftKey k = VtyEvent (V.EvKey k [V.MShift]) | ||
|
||
pattern EscapeKey :: BrickEvent n e | ||
pattern EscapeKey = VtyEvent (V.EvKey V.KEsc []) | ||
|
||
pattern FKey :: Int -> BrickEvent n e | ||
pattern FKey c = VtyEvent (V.EvKey (V.KFun c) []) | ||
|
||
openModal :: ModalType -> EventM Name AppState () | ||
openModal mt = do | ||
newModal <- gets $ flip generateModal mt | ||
ensurePause | ||
uiState . uiModal ?= newModal | ||
where | ||
-- Set the game to AutoPause if needed | ||
ensurePause = do | ||
pause <- use $ gameState . paused | ||
unless (pause || isRunningModal mt) $ do | ||
gameState . runStatus .= AutoPause | ||
|
||
-- | The running modals do not autopause the game. | ||
isRunningModal :: ModalType -> Bool | ||
isRunningModal mt = mt `elem` [RobotsModal, MessagesModal] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
module Swarm.TUI.Editor.EditorController where | ||
|
||
import Brick hiding (Direction) | ||
import Brick.Focus | ||
import Control.Lens | ||
import Control.Monad.IO.Class (liftIO) | ||
import Graphics.Vty qualified as V | ||
import Swarm.Game.State | ||
import Swarm.TUI.Controller.ControllerUtils | ||
import Swarm.TUI.Editor.Util qualified as EU | ||
import Swarm.TUI.Model | ||
|
||
------------------------------------------------------------ | ||
-- World Editor panel events | ||
------------------------------------------------------------ | ||
|
||
activateWorldEditorFunction :: WorldEditorFocusable -> EventM Name AppState () | ||
activateWorldEditorFunction BrushSelector = openModal TerrainPaletteModal | ||
activateWorldEditorFunction EntitySelector = | ||
liftIO $ putStrLn "TODO" | ||
activateWorldEditorFunction AreaSelector = do | ||
selectorStage <- use $ uiState . uiWorldEditor . boundsSelectionStep | ||
case selectorStage of | ||
SelectionComplete -> uiState . uiWorldEditor . boundsSelectionStep .= UpperLeftPending | ||
_ -> return () | ||
activateWorldEditorFunction OutputPathSelector = | ||
liftIO $ putStrLn "File selection" | ||
|
||
-- | Handle user input events in the robot panel. | ||
handleWorldEditorPanelEvent :: BrickEvent Name AppEvent -> EventM Name AppState () | ||
handleWorldEditorPanelEvent = \case | ||
Key V.KEsc -> uiState . uiWorldEditor . boundsSelectionStep .= SelectionComplete | ||
Key V.KEnter -> do | ||
fring <- use $ uiState . uiWorldEditor . editorFocusRing | ||
case focusGetCurrent fring of | ||
Just (WorldEditorPanelControl x) -> activateWorldEditorFunction x | ||
_ -> return () | ||
ControlChar 's' -> do | ||
worldEditor <- use $ uiState . uiWorldEditor | ||
let fp = worldEditor ^. outputFilePath | ||
maybeBounds <- use $ uiState . uiWorldEditor . editingBounds | ||
w <- use $ gameState . world | ||
liftIO $ writeFile fp $ EU.getEditedMapAsString worldEditor maybeBounds w | ||
CharKey '\t' -> uiState . uiWorldEditor . editorFocusRing %= focusNext | ||
Key V.KBackTab -> uiState . uiWorldEditor . editorFocusRing %= focusPrev | ||
_ -> return () |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
module Swarm.TUI.Editor.EditorView where | ||
|
||
import Brick hiding (Direction) | ||
import Brick.Focus | ||
import Brick.Widgets.List qualified as BL | ||
import Control.Lens hiding (Const, from) | ||
import Data.List qualified as L | ||
import Swarm.Game.World qualified as W | ||
import Swarm.TUI.Attr | ||
import Swarm.TUI.Border | ||
import Swarm.TUI.Model | ||
import Swarm.TUI.Panel | ||
|
||
import Swarm.TUI.View.ViewUtils | ||
|
||
drawWorldEditor :: FocusRing Name -> UIState -> Widget Name | ||
drawWorldEditor toplevelFocusRing uis = | ||
if worldEditor ^. isWorldEditorEnabled | ||
then | ||
panel | ||
highlightAttr | ||
toplevelFocusRing | ||
(FocusablePanel WorldEditorPanel) | ||
plainBorder | ||
innerWidget | ||
else emptyWidget | ||
where | ||
privateFocusRing = worldEditor ^. editorFocusRing | ||
maybeCurrentFocus = focusGetCurrent privateFocusRing | ||
|
||
innerWidget = | ||
padLeftRight 1 $ | ||
hLimit 30 $ | ||
vBox | ||
[ brushWidget | ||
, -- , entityWidget | ||
areaWidget | ||
, outputWidget | ||
] | ||
|
||
worldEditor = uis ^. uiWorldEditor | ||
maybeSelectedTerrain = fmap snd $ BL.listSelectedElement $ worldEditor ^. terrainList | ||
maybeAreaBounds = worldEditor ^. editingBounds | ||
|
||
-- TODO: Use withFocusRing | ||
mkFormControl n w = | ||
clickable n $ transformation w | ||
where | ||
transformation = | ||
if Just n == maybeCurrentFocus | ||
then withAttr BL.listSelectedFocusedAttr | ||
else id | ||
|
||
brushWidget = | ||
mkFormControl (WorldEditorPanelControl BrushSelector) $ | ||
padRight (Pad 1) (str "Brush:") <+> brushWidgetContent | ||
|
||
brushWidgetContent = | ||
maybe emptyWidget drawLabeledTerrainSwatch maybeSelectedTerrain | ||
|
||
-- entityWidget = | ||
-- mkFormControl (WorldEditorPanelControl EntitySelector) $ | ||
-- padRight (Pad 1) (str "Entity:") <+> entityWidgetContent | ||
|
||
-- entityWidgetContent = | ||
-- maybe emptyWidget drawLabeledTerrainSwatch maybeSelectedTerrain | ||
|
||
areaContent = case worldEditor ^. boundsSelectionStep of | ||
UpperLeftPending -> str "Click top-left" | ||
LowerRightPending _wcoords -> str "Click bottom-right" | ||
SelectionComplete -> maybe emptyWidget renderBounds maybeAreaBounds | ||
|
||
areaWidget = | ||
mkFormControl (WorldEditorPanelControl AreaSelector) $ | ||
vBox | ||
[ str "Area:" | ||
, areaContent | ||
] | ||
|
||
renderBounds (W.Coords primaryCorner@(x1, y1), W.Coords (x2, y2)) = | ||
str $ L.intercalate " @ " [rectSize, show primaryCorner] | ||
where | ||
width = x2 - x1 | ||
-- NOTE: The height coordinate is inverted so we do opposite subtraction order here: | ||
height = y1 - y2 | ||
rectSize = L.intercalate "x" [show width, show height] | ||
|
||
outputWidget = | ||
mkFormControl (WorldEditorPanelControl OutputPathSelector) $ | ||
padRight (Pad 1) (str "Output:") <+> outputWidgetContent | ||
|
||
outputWidgetContent = str $ worldEditor ^. outputFilePath |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
module Swarm.TUI.Editor.Util where | ||
|
||
import Control.Lens hiding (Const, from) | ||
import Data.Char qualified as DC | ||
import Data.Int (Int64) | ||
import Data.Map qualified as Map | ||
import Data.Maybe qualified as Maybe | ||
import Swarm.Game.Entity (Entity) | ||
import Swarm.Game.Terrain (TerrainType) | ||
import Swarm.Game.World qualified as W | ||
import Swarm.TUI.Model | ||
|
||
getTerrainAt :: WorldEditor -> W.World Int Entity -> W.Coords -> TerrainType | ||
getTerrainAt editor w coords = case editor ^. isWorldEditorEnabled of | ||
True -> Maybe.fromMaybe underlyingCell $ Map.lookup coords paintMap | ||
False -> underlyingCell | ||
where | ||
paintMap = editor ^. paintedTerrain | ||
underlyingCell = toEnum $ W.lookupTerrain coords w | ||
|
||
getEditedMapAsString :: WorldEditor -> Maybe (W.Coords, W.Coords) -> W.World Int Entity -> String | ||
getEditedMapAsString _ Nothing _ = "EMPTY BOUNDS" | ||
getEditedMapAsString worldEditor (Just (W.Coords (xLeft, yTop), W.Coords (xRight, yBottom))) w = | ||
unlines $ map renderLine [yTop .. yBottom] | ||
where | ||
getTerrain = getTerrainAt worldEditor w | ||
drawCell :: Int64 -> Int64 -> Char | ||
drawCell rowIndex = DC.chr . (+ DC.ord '0') . fromEnum . getTerrain . W.Coords . (rowIndex,) | ||
renderLine rowIndex = map (drawCell rowIndex) [xLeft .. xRight] |
Oops, something went wrong.