Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

navigable robots table #2140

Merged
merged 18 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .hlint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
- {name: Data.List.head, within: []}
- {name: Prelude.head, within: [Swarm.Web.Tournament.Database.Query]}
- {name: Prelude.tail, within: []}
- {name: Prelude.maximum, within: [Swarm.Util]}
- {name: Prelude.minimum, within: []}
- {name: Prelude.!!, within: [Swarm.Util.indexWrapNonEmpty, TestEval]}
- {name: undefined, within: [Swarm.Language.Key, TestUtil]}
- {name: fromJust, within: []}
Expand Down
1 change: 1 addition & 0 deletions app/game/Swarm/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import Swarm.Language.Pretty (prettyText)
import Swarm.Log (LogSource (SystemLog), Severity (..))
import Swarm.TUI.Controller
import Swarm.TUI.Model
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.StateUpdate
import Swarm.TUI.Model.UI (uiAttrMap)
import Swarm.TUI.View
Expand Down
4 changes: 2 additions & 2 deletions src/swarm-doc/Swarm/Doc/Wiki/Cheatsheet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import Swarm.Language.Syntax (Const (..))
import Swarm.Language.Syntax qualified as Syntax
import Swarm.Language.Text.Markdown as Markdown (docToMark)
import Swarm.Language.Typecheck (inferConst)
import Swarm.Util (showT)
import Swarm.Util (maximum0, showT)

-- * Types

Expand Down Expand Up @@ -99,7 +99,7 @@ listToRow mw xs = wrap '|' . T.intercalate "|" $ zipWith format mw xs
format w x = wrap ' ' x <> T.replicate (w - T.length x) " "

maxWidths :: [[Text]] -> [Int]
maxWidths = map (maximum . map T.length) . transpose
maxWidths = map (maximum0 . map T.length) . transpose

-- ** COMMANDS

Expand Down
18 changes: 8 additions & 10 deletions src/swarm-engine/Swarm/Game/State/Initialize.hs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ import Swarm.Game.World.Gen (Seed)
import Swarm.Language.Capability (constCaps)
import Swarm.Language.Syntax (allConst, erase)
import Swarm.Language.Types
import Swarm.Util (binTuples, (?))
import Swarm.Util (applyJust, applyWhen, binTuples, (?))
import System.Clock qualified as Clock
import System.Random (mkStdGen)

Expand Down Expand Up @@ -137,20 +137,18 @@ pureScenarioToGameState scenario theSeed now toRun gsc =
-- of the scenario description).
& ix baseID
. machine
%~ case initialCodeToRun of
Nothing -> id
Just t -> const $ initMachine t
%~ applyJust (const . initMachine <$> initialCodeToRun)
kostmo marked this conversation as resolved.
Show resolved Hide resolved
-- If we are in creative mode, give base all the things
& ix baseID
. robotInventory
%~ case scenario ^. scenarioOperation . scenarioCreative of
False -> id
True -> union (fromElems (map (0,) things))
%~ applyWhen
kostmo marked this conversation as resolved.
Show resolved Hide resolved
(scenario ^. scenarioOperation . scenarioCreative)
(union (fromElems (map (0,) things)))
& ix baseID
. equippedDevices
%~ case scenario ^. scenarioOperation . scenarioCreative of
False -> id
True -> const (fromList devices)
%~ applyWhen
(scenario ^. scenarioOperation . scenarioCreative)
(const (fromList devices))

running = case robotList of
[] -> False
Expand Down
15 changes: 15 additions & 0 deletions src/swarm-topography/Swarm/Game/Universe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Control.Lens (makeLenses, view)
import Data.Function (on)
import Data.Int (Int32)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Yaml (FromJSON, ToJSON, Value (Object), parseJSON, withText, (.:))
import GHC.Generics (Generic)
import Linear (V2 (..))
Expand Down Expand Up @@ -82,3 +83,17 @@ defaultCosmicLocation = Cosmic DefaultRootSubworld origin

offsetBy :: Cosmic Location -> V2 Int32 -> Cosmic Location
offsetBy loc v = fmap (.+^ v) loc

-- ** Rendering

locationToString :: Location -> String
locationToString (Location x y) =
unwords $ map show [x, y]

renderCoordsString :: Cosmic Location -> String
renderCoordsString (Cosmic sw coords) =
unwords $ locationToString coords : suffix
where
suffix = case sw of
DefaultRootSubworld -> []
SubworldName swName -> ["in", T.unpack swName]
23 changes: 20 additions & 3 deletions src/swarm-tui/Swarm/TUI/Controller.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ module Swarm.TUI.Controller (
) where

-- See Note [liftA2 re-export from Prelude]
import Prelude hiding (Applicative (..))

import Brick hiding (Direction, Location)
import Brick.Focus
Expand All @@ -36,10 +35,11 @@ import Brick.Widgets.Dialog
import Brick.Widgets.Edit (Editor, applyEdit, handleEditorEvent)
import Brick.Widgets.List (handleListEvent)
import Brick.Widgets.List qualified as BL
import Brick.Widgets.TabularList.Mixed
import Control.Applicative (pure)
import Control.Category ((>>>))
import Control.Lens as Lens
import Control.Monad (unless, void, when)
import Control.Monad (forM_, unless, void, when)
import Control.Monad.Extra (whenJust)
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.State (MonadState, execState)
Expand Down Expand Up @@ -87,7 +87,7 @@ import Swarm.Language.Value (Value (VKey), envTypes)
import Swarm.Log
import Swarm.TUI.Controller.EventHandlers
import Swarm.TUI.Controller.SaveScenario (saveScenarioInfoOnQuit)
import Swarm.TUI.Controller.UpdateUI (updateAndRedrawUI)
import Swarm.TUI.Controller.UpdateUI
import Swarm.TUI.Controller.Util
import Swarm.TUI.Editor.Controller qualified as EC
import Swarm.TUI.Editor.Model
Expand All @@ -101,7 +101,10 @@ import Swarm.TUI.Model.Name
import Swarm.TUI.Model.Repl
import Swarm.TUI.Model.StateUpdate
import Swarm.TUI.Model.UI
import Swarm.TUI.View.Robot (getList)
import Swarm.TUI.View.Robot.Type
import Swarm.Util hiding (both, (<<.=))
import Prelude hiding (Applicative (..))

-- ~~~~ Note [liftA2 re-export from Prelude]
--
Expand Down Expand Up @@ -418,6 +421,20 @@ handleModalEvent = \case
refreshList $ uiState . uiGameplay . uiDialogs . uiStructure . structurePanelListWidget
StructureSummary -> handleInfoPanelEvent modalScroll (VtyEvent ev)
_ -> handleInfoPanelEvent modalScroll (VtyEvent ev)
Just RobotsModal -> Brick.zoom (uiState . uiGameplay . uiDialogs . uiRobot) $ case ev of
V.EvKey (V.KChar '\t') [] -> robotDetailsFocus %= focusNext
_ -> do
foc <- use robotDetailsFocus
case focusGetCurrent foc of
(Just (RobotsListDialog (SingleRobotDetails RobotLogPane))) ->
Brick.zoom (robotListContent . robotDetailsPaneState . logsList) $ handleListEvent ev
_ -> do
Brick.zoom (robotListContent . robotsListWidget) $
handleMixedListEvent ev

-- Ensure list widget content is updated immediately
widget <- use $ robotListContent . robotsListWidget
forM_ (BL.listSelectedElement $ getList widget) $ updateRobotDetailsPane . snd
_ -> handleInfoPanelEvent modalScroll (VtyEvent ev)
where
refreshGoalList lw = nestEventM' lw $ handleListEventWithSeparators ev shouldSkipSelection
Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Controller/EventHandlers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import Swarm.TUI.Controller.EventHandlers.Robot (handleRobotPanelEvent, robotEve
import Swarm.TUI.Controller.EventHandlers.World (worldEventHandlers)
import Swarm.TUI.Model
import Swarm.TUI.Model.Event (SwarmEvent, swarmEvents)
import Swarm.TUI.Model.Name
import Swarm.Util (parens, squote)

-- ~~~~ Note [how Swarm event handlers work]
Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Controller/EventHandlers/Frame.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Swarm.TUI.Controller.UpdateUI
import Swarm.TUI.Controller.Util
import Swarm.TUI.Model
import Swarm.TUI.Model.Achievements (popupAchievement)
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.UI
import System.Clock

Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Controller/EventHandlers/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Swarm.TUI.Model
import Swarm.TUI.Model.DebugOption (DebugOption (ToggleCreative, ToggleWorldEditor))
import Swarm.TUI.Model.Dialog.Goal
import Swarm.TUI.Model.Event (MainEvent (..), SwarmEvent (..))
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.UI
import System.Clock (Clock (..), TimeSpec (..), getTime)

Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Controller/EventHandlers/REPL.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Swarm.Game.State.Substate
import Swarm.TUI.Controller.Util
import Swarm.TUI.Model
import Swarm.TUI.Model.Event
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.Repl
import Swarm.TUI.Model.UI

Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Controller/EventHandlers/Robot.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import Swarm.TUI.Inventory.Sorting (cycleSortDirection, cycleSortOrder)
import Swarm.TUI.List
import Swarm.TUI.Model
import Swarm.TUI.Model.Event
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.UI
import Swarm.TUI.View.Util (generateModal)

Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Controller/EventHandlers/World.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Swarm.Language.Syntax.Direction (Direction (..), directionSyntax)
import Swarm.TUI.Controller.Util
import Swarm.TUI.Model
import Swarm.TUI.Model.Event
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.UI

-- | Handle a user input event in the world view panel.
Expand Down
49 changes: 45 additions & 4 deletions src/swarm-tui/Swarm/TUI/Controller/UpdateUI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,28 @@
module Swarm.TUI.Controller.UpdateUI (
updateUI,
updateAndRedrawUI,
updateRobotDetailsPane,
) where

import Brick hiding (Direction, Location)
import Brick.Focus

-- See Note [liftA2 re-export from Prelude]
import Brick hiding (Direction, Location, on)
import Brick.Focus
import Brick.Widgets.List qualified as BL
import Control.Applicative (liftA2, pure)
import Control.Lens as Lens
import Control.Monad (unless, when)
import Control.Monad (forM_, unless, when)
import Control.Monad.IO.Class (liftIO)
import Data.Foldable (toList)
import Data.Function (on)
import Data.List.Extra (enumerate)
import Data.Map qualified as M
import Data.Maybe (isNothing)
import Data.String (fromString)
import Data.Text qualified as T
import Data.Vector qualified as V
import Swarm.Game.Entity hiding (empty)
import Swarm.Game.Robot
import Swarm.Game.Robot.Activity
import Swarm.Game.Robot.Concrete
import Swarm.Game.State
import Swarm.Game.State.Landscape
Expand All @@ -43,6 +47,9 @@ import Swarm.TUI.Model.Name
import Swarm.TUI.Model.Repl
import Swarm.TUI.Model.UI
import Swarm.TUI.View.Objective qualified as GR
import Swarm.TUI.View.Robot
import Swarm.TUI.View.Robot.Type
import Swarm.Util (applyJust)
import Witch (into)
import Prelude hiding (Applicative (..))

Expand Down Expand Up @@ -165,6 +172,8 @@ updateUI = do

newPopups <- generateNotificationPopups

doRobotListUpdate g

let redraw =
g ^. needsRedraw
|| inventoryUpdated
Expand All @@ -174,6 +183,38 @@ updateUI = do
|| newPopups
pure redraw

doRobotListUpdate :: GameState -> EventM Name AppState ()
doRobotListUpdate g = do
gp <- use $ uiState . uiGameplay
dOps <- use $ uiState . uiDebugOptions

let rd =
mkRobotDisplay $
RobotRenderingContext
{ _mygs = g
, _gameplay = gp
, _timing = gp ^. uiTiming
, _uiDbg = dOps
}
oldList = getList $ gp ^. uiDialogs . uiRobot . robotListContent . robotsListWidget
maybeOldSelected = snd <$> BL.listSelectedElement oldList

-- Since we're replacing the entire contents of the list, we need to preserve the
-- selected row here.
maybeModificationFunc =
updateList . BL.listFindBy . ((==) `on` view (rob . robotID)) <$> maybeOldSelected

uiState . uiGameplay . uiDialogs . uiRobot . robotListContent . robotsListWidget .= applyJust maybeModificationFunc rd

Brick.zoom (uiState . uiGameplay . uiDialogs . uiRobot) $
forM_ maybeOldSelected updateRobotDetailsPane

updateRobotDetailsPane :: RobotWidgetRow -> EventM Name RobotDisplay ()
updateRobotDetailsPane robotPayload =
Brick.zoom robotListContent $ do
robotDetailsPaneState . cmdHistogramList . BL.listElementsL .= V.fromList (M.toList (robotPayload ^. rob . activityCounts . commandsHistogram))
robotDetailsPaneState . logsList . BL.listElementsL .= robotPayload ^. rob . robotLog

-- | Either pops up the updated Goals modal
-- or pops up the Congratulations (Win) modal, or pops
-- up the Condolences (Lose) modal.
Expand Down
3 changes: 1 addition & 2 deletions src/swarm-tui/Swarm/TUI/Controller/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,12 @@ import Swarm.Language.Capability (Capability (CDebug))
import Swarm.Language.Syntax hiding (Key)
import Swarm.TUI.Model (
AppState,
FocusablePanel,
ModalType (..),
Name (..),
gameState,
modalScroll,
uiState,
)
import Swarm.TUI.Model.Name
import Swarm.TUI.Model.Repl (REPLHistItem, REPLPrompt, REPLState, addREPLItem, replHistory, replPromptText, replPromptType)
import Swarm.TUI.Model.UI
import Swarm.TUI.View.Util (generateModal)
Expand Down
2 changes: 1 addition & 1 deletion src/swarm-tui/Swarm/TUI/Editor/Masking.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Swarm.Game.Universe
import Swarm.Game.World.Coords
import Swarm.TUI.Editor.Model
import Swarm.TUI.Editor.Util qualified as EU
import Swarm.TUI.Model.UI
import Swarm.TUI.Model.UI.Gameplay

shouldHideWorldCell :: UIGameplay -> Coords -> Bool
shouldHideWorldCell ui coords =
Expand Down
2 changes: 1 addition & 1 deletion src/swarm-tui/Swarm/TUI/Editor/View.hs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ drawWorldEditor toplevelFocusRing uis =
L.intersperse
"@"
[ EA.renderRectDimensions rectArea
, VU.locationToString upperLeftLoc
, locationToString upperLeftLoc
]
where
upperLeftLoc = coordsToLoc upperLeftCoord
Expand Down
1 change: 0 additions & 1 deletion src/swarm-tui/Swarm/TUI/Model.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ module Swarm.TUI.Model (
-- $uilabel
AppEvent (..),
FocusablePanel (..),
Name (..),
kostmo marked this conversation as resolved.
Show resolved Hide resolved

-- ** Web command
WebCommand (..),
Expand Down
1 change: 1 addition & 0 deletions src/swarm-tui/Swarm/TUI/Model/KeyBindings.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Swarm.Language.Pretty (prettyText)
import Swarm.TUI.Controller.EventHandlers
import Swarm.TUI.Model
import Swarm.TUI.Model.Event (SwarmEvent, defaultSwarmBindings, swarmEvents)
import Swarm.TUI.Model.Name

-- See Note [how Swarm event handlers work]

Expand Down
13 changes: 13 additions & 0 deletions src/swarm-tui/Swarm/TUI/Model/Name.hs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,17 @@ data Button
| NextButton
deriving (Eq, Ord, Show, Read, Bounded, Enum)

-- | Robot details
data RobotDetailSubpane
= RobotLogPane
| RobotCommandHistogramPane
deriving (Eq, Ord, Show, Read, Bounded, Enum)

data RobotsDisplayMode
= RobotList
| SingleRobotDetails RobotDetailSubpane
deriving (Eq, Ord, Show, Read)

-- | 'Name' represents names to uniquely identify various components
-- of the UI, such as forms, panels, caches, extents, lists, and buttons.
data Name
Expand Down Expand Up @@ -106,6 +117,8 @@ data Name
StructureWidgets StructureWidget
| -- | The list of scenario choices.
ScenarioList
| -- | The robots list
RobotsListDialog RobotsDisplayMode
| -- | The scrollable viewport for the info panel.
InfoViewport
| -- | The scrollable viewport for any modal dialog.
Expand Down
Loading