packages feed

swarm-0.4: src/Swarm/TUI/Editor/View.hs

module Swarm.TUI.Editor.View where

import Brick hiding (Direction)
import Brick.Focus
import Brick.Widgets.Center (hCenter)
import Brick.Widgets.List qualified as BL
import Control.Lens hiding (Const, from)
import Data.List qualified as L
import Swarm.Game.Scenario.Topography.Area qualified as EA
import Swarm.Game.Scenario.Topography.EntityFacade
import Swarm.Game.Terrain (TerrainType)
import Swarm.Game.Universe
import Swarm.Game.World qualified as W
import Swarm.TUI.Attr
import Swarm.TUI.Border
import Swarm.TUI.Editor.Model
import Swarm.TUI.Model
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.UI
import Swarm.TUI.Panel
import Swarm.TUI.View.CellDisplay (renderDisplay)
import Swarm.TUI.View.Util qualified as VU
import Swarm.Util (listEnums)

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

  controlsBox =
    padBottom Max $
      vBox
        [ brushWidget
        , entityWidget
        , clearEntityButtonWidget
        , areaWidget
        , outputWidget
        , str " "
        , saveButtonWidget
        ]

  innerWidget =
    padLeftRight 1 $
      hLimit 30 $
        controlsBox <=> statusBox

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

  -- TODO (#1150): Use withFocusRing?
  mkFormControl n w =
    clickable n $ transformation w
   where
    transformation =
      if Just n == maybeCurrentFocus
        then withAttr BL.listSelectedFocusedAttr
        else id

  swatchContent list drawFunc =
    maybe emptyWidget drawFunc selectedThing
   where
    selectedThing = snd <$> BL.listSelectedElement list

  brushWidget =
    mkFormControl (WorldEditorPanelControl BrushSelector) $
      padRight (Pad 1) (str "Brush:")
        <+> swatchContent (worldEditor ^. terrainList) VU.drawLabeledTerrainSwatch

  entityWidget =
    mkFormControl (WorldEditorPanelControl EntitySelector) $
      padRight (Pad 1) (str "Entity:")
        <+> swatchContent (worldEditor ^. entityPaintList) drawLabeledEntitySwatch

  clearEntityButtonWidget =
    if null $ worldEditor ^. entityPaintList . BL.listSelectedL
      then emptyWidget
      else
        mkFormControl (WorldEditorPanelControl ClearEntityButton)
          . hLimit 20
          . hCenter
          $ str "None"

  areaContent = case worldEditor ^. editingBounds . boundsSelectionStep of
    UpperLeftPending -> str "Click top-left"
    LowerRightPending _wcoords -> str "Click bottom-right"
    SelectionComplete -> maybe emptyWidget (renderBounds . view planar) maybeAreaBounds

  areaWidget =
    mkFormControl (WorldEditorPanelControl AreaSelector) $
      vBox
        [ str "Area:"
        , areaContent
        ]

  renderBounds (upperLeftCoord, lowerRightCoord) =
    str $
      unwords $
        L.intersperse
          "@"
          [ EA.renderRectDimensions rectArea
          , VU.locationToString upperLeftLoc
          ]
   where
    upperLeftLoc = W.coordsToLoc upperLeftCoord
    lowerRightLoc = W.coordsToLoc lowerRightCoord
    rectArea = EA.cornersToArea upperLeftLoc lowerRightLoc

  outputWidget =
    mkFormControl (WorldEditorPanelControl OutputPathSelector) $
      padRight (Pad 1) (str "Output:") <+> outputWidgetContent

  outputWidgetContent = str $ worldEditor ^. outputFilePath

  saveButtonWidget =
    mkFormControl (WorldEditorPanelControl MapSaveButton)
      . hLimit 20
      . hCenter
      $ str "Save"

  statusBox = maybe emptyWidget str $ worldEditor ^. lastWorldEditorMessage

drawLabeledEntitySwatch :: EntityFacade -> Widget Name
drawLabeledEntitySwatch (EntityFacade eName eDisplay) =
  tile <+> txt eName
 where
  tile = padRight (Pad 1) $ renderDisplay eDisplay

drawTerrainSelector :: AppState -> Widget Name
drawTerrainSelector s =
  padAll 1
    . hCenter
    . vLimit (length (listEnums :: [TerrainType]))
    . BL.renderListWithIndex listDrawTerrainElement True
    $ s ^. uiState . uiWorldEditor . terrainList

listDrawTerrainElement :: Int -> Bool -> TerrainType -> Widget Name
listDrawTerrainElement pos _isSelected a =
  clickable (TerrainListItem pos) $ VU.drawLabeledTerrainSwatch a

drawEntityPaintSelector :: AppState -> Widget Name
drawEntityPaintSelector s =
  padAll 1
    . hCenter
    . vLimit 10
    . BL.renderListWithIndex listDrawEntityPaintElement True
    $ s ^. uiState . uiWorldEditor . entityPaintList

listDrawEntityPaintElement :: Int -> Bool -> EntityFacade -> Widget Name
listDrawEntityPaintElement pos _isSelected a =
  clickable (EntityPaintListItem pos) $ drawLabeledEntitySwatch a