diff options
Diffstat (limited to '')
| -rw-r--r-- | lib/Daffm.hs | 74 | ||||
| -rw-r--r-- | lib/Daffm/Attrs.hs | 22 | ||||
| -rw-r--r-- | lib/Daffm/Types.hs | 8 | ||||
| -rw-r--r-- | lib/Daffm/View.hs | 75 |
4 files changed, 127 insertions, 52 deletions
diff --git a/lib/Daffm.hs b/lib/Daffm.hs index 39a43d0..5bd0830 100644 --- a/lib/Daffm.hs +++ b/lib/Daffm.hs @@ -2,11 +2,12 @@ module Daffm where import qualified Brick.Main as M import qualified Brick.Types as T +import qualified Brick.Widgets.Edit as Editor import qualified Brick.Widgets.List as L import Control.Monad (forM) -import Control.Monad.State (MonadIO (liftIO), get, gets, modify) +import Control.Monad.State (MonadIO (liftIO), MonadState, get, gets, modify, put) import Daffm.Attrs (appAttrMap) -import Daffm.Types (AppState (..), FileInfo (..), FileType (..)) +import Daffm.Types (AppState (..), FileInfo (..), FileType (..), FocusTarget (FocusCmdline, FocusMain)) import Daffm.View (appView) import Data.Char (toLower) import Data.List (sortBy) @@ -18,48 +19,52 @@ import System.Directory (listDirectory, makeAbsolute, setCurrentDirectory) import System.FilePath (takeDirectory) import qualified System.PosixCompat as Posix -openSelectedFile :: T.EventM () AppState () +type AppEvent = T.EventM FocusTarget AppState + +modifyM :: (MonadState s m) => (s -> m s) -> m () +modifyM f = get >>= f >>= put + +openSelectedFile :: AppEvent () openSelectedFile = do - state <- get - let indexM = L.listSelected . stateFiles $ state - let files = L.listElements . stateFiles $ state - let fileM = indexM >>= (files !?) - case fileM of - Just (FileInfo {filePath, fileType = Directory}) -> do - let parentDir = stateCwd state - nextState <- liftIO $ loadDirInAppState filePath parentDir state - modify (const nextState) - pure () + AppState {stateFiles, stateCwd} <- get + let indexM = L.listSelected stateFiles + let files = L.listElements stateFiles + case indexM >>= (files !?) of + Just (FileInfo {filePath, fileType = Directory}) -> + modifyM (liftIO . loadDirInAppState filePath stateCwd) Just (FileInfo {filePath, fileType}) -> do liftIO . putStrLn $ "Opening " <> show fileType <> ": " <> filePath pure () Nothing -> pure () pure () -goBackToParentDir :: T.EventM () AppState () +goBackToParentDir :: AppEvent () goBackToParentDir = do - state <- get - let dir = stateParentDir state - let parentDir = takeDirectory dir - nextState <- liftIO $ loadDirInAppState dir parentDir state - modify (const nextState) + dir <- gets stateParentDir + modifyM (liftIO . loadDirInAppState dir (takeDirectory dir)) -appEvent :: T.BrickEvent () e -> T.EventM () AppState () -appEvent (T.VtyEvent e) = - case e of - V.EvKey V.KEsc [] -> M.halt - V.EvKey (V.KChar 'q') [] -> M.halt - V.EvKey (V.KChar 'l') [] -> openSelectedFile - V.EvKey (V.KChar 'h') [] -> goBackToParentDir - V.EvKey V.KEnter [] -> openSelectedFile - V.EvKey V.KBS [] -> goBackToParentDir - ev -> do +appEvent :: T.BrickEvent FocusTarget e -> AppEvent () +appEvent brickevent@(T.VtyEvent event) = do + focusTarget <- gets stateFocusTarget + case (focusTarget, event) of + (_, V.EvKey V.KEsc []) -> modify (\st -> st {stateFocusTarget = FocusMain}) + (_, V.EvKey (V.KChar ':') []) -> modify (\st -> st {stateFocusTarget = FocusCmdline}) + (FocusMain, V.EvKey (V.KChar 'q') []) -> M.halt + (FocusMain, V.EvKey (V.KChar 'l') []) -> openSelectedFile + (FocusMain, V.EvKey (V.KChar 'h') []) -> goBackToParentDir + (FocusMain, V.EvKey V.KEnter []) -> openSelectedFile + (FocusMain, V.EvKey V.KBS []) -> goBackToParentDir + (FocusMain, _) -> do files <- gets stateFiles - newFiles <- T.nestEventM' files (L.handleListEventVi L.handleListEvent ev) + newFiles <- T.nestEventM' files (L.handleListEventVi L.handleListEvent event) modify (\appState -> appState {stateFiles = newFiles}) + (FocusCmdline, _) -> do + editor <- gets stateCmdlineEditor + newEditor <- T.nestEventM' editor (Editor.handleEditorEvent brickevent) + modify (\appState -> appState {stateCmdlineEditor = newEditor}) appEvent _ = pure () -app :: M.App AppState e () +app :: M.App AppState e FocusTarget app = M.App { M.appDraw = appView, @@ -112,7 +117,7 @@ loadDirInAppState dir parentDir appState = do files <- listFilesInDir dir pure $ appState - { stateFiles = L.list () (Vec.fromList files) 1, + { stateFiles = L.list FocusMain (Vec.fromList files) 1, stateCwd = dir, stateParentDir = parentDir } @@ -120,7 +125,10 @@ loadDirInAppState dir parentDir appState = do mkEmptyAppState :: AppState mkEmptyAppState = AppState - { stateFiles = L.list () (Vec.fromList []) 1, + { stateFiles = L.list FocusMain (Vec.fromList []) 1, + stateCmdlineEditor = Editor.editor FocusCmdline Nothing "", + stateFocusTarget = FocusMain, + -- stateFocusRing = focusRing [FocusMain, FocusCmdline], stateCwd = "", stateParentDir = "" } diff --git a/lib/Daffm/Attrs.hs b/lib/Daffm/Attrs.hs index 93a4163..77f9ee6 100644 --- a/lib/Daffm/Attrs.hs +++ b/lib/Daffm/Attrs.hs @@ -1,17 +1,31 @@ module Daffm.Attrs where import qualified Brick.AttrMap as A -import Brick.Util (fg) +import Brick.Util (bg, fg) +import Brick.Widgets.List (listAttr, listSelectedAttr) import qualified Brick.Widgets.List as L import qualified Graphics.Vty as V -selectedFileAttr :: A.AttrName -selectedFileAttr = A.attrName "selected-file" +fileAttr :: A.AttrName +fileAttr = listAttr <> A.attrName "file" + +fileSelectedAttr :: A.AttrName +fileSelectedAttr = listSelectedAttr <> fileAttr + +directoryAttr :: A.AttrName +directoryAttr = listAttr <> A.attrName "directory" + +directorySelectedAttr :: A.AttrName +directorySelectedAttr = listSelectedAttr <> directoryAttr appAttrMap :: A.AttrMap appAttrMap = A.attrMap V.defAttr [ (L.listAttr, fg V.white), - (selectedFileAttr, fg V.cyan) + (listSelectedAttr, bg V.black), + (directoryAttr, fg V.brightCyan), + (directorySelectedAttr, fg V.brightCyan), + (fileAttr, fg V.white), + (fileSelectedAttr, fg V.white) ] diff --git a/lib/Daffm/Types.hs b/lib/Daffm/Types.hs index 2a8e97d..5462851 100644 --- a/lib/Daffm/Types.hs +++ b/lib/Daffm/Types.hs @@ -1,5 +1,6 @@ module Daffm.Types where +import qualified Brick.Widgets.Edit as Editor import qualified Brick.Widgets.List as L import System.Posix.Types (FileOffset) @@ -21,8 +22,13 @@ data FileInfo = FileInfo } deriving (Show) +data FocusTarget = FocusCmdline | FocusMain deriving (Show, Eq, Ord) + data AppState = AppState - { stateFiles :: L.List () FileInfo, + { stateFiles :: L.List FocusTarget FileInfo, + stateCmdlineEditor :: Editor.Editor String FocusTarget, + stateFocusTarget :: FocusTarget, + -- stateFocusRing :: FocusRing FocusTarget, stateCwd :: FilePath, stateParentDir :: FilePath } diff --git a/lib/Daffm/View.hs b/lib/Daffm/View.hs index 02529ac..db26a7d 100644 --- a/lib/Daffm/View.hs +++ b/lib/Daffm/View.hs @@ -1,25 +1,72 @@ module Daffm.View where import Brick.Types (Widget) -import Brick.Widgets.Core (str, vBox, vLimit, withAttr, (<+>)) +import Brick.Widgets.Core (Padding (Max, Pad), TextWidth (textWidth), hBox, hLimit, padLeft, padRight, str, vBox, vLimit, withAttr, (<+>)) +import Brick.Widgets.Edit (renderEditor) import qualified Brick.Widgets.List as L -import Daffm.Attrs (selectedFileAttr) -import Daffm.Types (AppState (..), FileInfo (..)) +import Daffm.Attrs (directoryAttr, directorySelectedAttr, fileAttr, fileSelectedAttr) +import Daffm.Types (AppState (..), FileInfo (..), FileType (..), FocusTarget (..)) +import Data.Int (Int64) import qualified Data.Vector as Vec +import Text.Printf (printf) -appView :: AppState -> [Widget ()] -appView (AppState {stateFiles, stateCwd}) = [ui] +appView :: AppState -> [Widget FocusTarget] +appView appState@(AppState {stateFiles, stateCwd}) = [ui] where + ui :: Widget FocusTarget ui = vBox [vLimit 1 header, box, vLimit 1 cmdline] header = str stateCwd - cmdline = str "Item " <+> cur <+> str " of " <+> total - cur = case L.listSelected stateFiles of - Nothing -> str "-" - Just i -> str (show (i + 1)) - total = str $ show $ Vec.length $ L.listElements stateFiles + cmdline = cmdlineView appState + box :: Widget FocusTarget box = L.renderList fileItemView True stateFiles -fileItemView :: Bool -> FileInfo -> Widget () -fileItemView sel (FileInfo {fileName, fileSize, fileType}) = - let wrap w = if sel then withAttr selectedFileAttr w else w - in wrap (str fileName) <+> str (" : " <> show fileSize <> " | " <> show fileType) +fixedColumnsStr :: Int -> Widget n -> Widget n +fixedColumnsStr w s = hLimit w $ padRight Max s + +fileItemView :: Bool -> FileInfo -> Widget FocusTarget +fileItemView sel fileInfo@(FileInfo {fileSize, fileType}) = + hBox + [ fixedColumnsStr 5 (fileTypeView fileType), + fixedColumnsStr 7 (fileSizeView fileSize), + fileNameView sel fileInfo + ] + where + fileSizeView = str . prettyFileSize . fromIntegral + fileTypeView = str . showFileType + showFileType Directory = "dir" + showFileType SymbolicLink = "link" + showFileType UnixSocket = "sock" + showFileType NamedPipe = "pipe" + showFileType CharacterDevice = "cdev" + showFileType BlockDevice = "bdev" + showFileType RegularFile = "file" + +fileNameView :: Bool -> FileInfo -> Widget FocusTarget +fileNameView True (FileInfo {fileName, fileType = Directory}) = withAttr directorySelectedAttr $ str $ fileName <> "/" +fileNameView False (FileInfo {fileName, fileType = Directory}) = withAttr directoryAttr $ str $ fileName <> "/" +fileNameView True (FileInfo {fileName}) = withAttr fileSelectedAttr $ str fileName +fileNameView False (FileInfo {fileName}) = withAttr fileAttr $ str fileName + +cmdlineView :: AppState -> Widget FocusTarget +cmdlineView (AppState {stateFocusTarget = FocusCmdline, stateCmdlineEditor}) = + str ":" <+> renderEditor (str . unlines) True stateCmdlineEditor +cmdlineView (AppState {stateFiles}) = + hBox [str ":", padLeft Max $ padRight (Pad 1) posIndicator] + where + posIndicator = str $ cur <> "/" <> total + cur = case L.listSelected stateFiles of + Nothing -> "-" + Just n -> show (n + 1) + total = show $ Vec.length $ L.listElements stateFiles + +prettyFileSize :: Int64 -> String +prettyFileSize i + | i >= 2 ^ (40 :: Int64) = format (i `divBy` (2 ** 40)) <> "T" + | i >= 2 ^ (30 :: Int64) = format (i `divBy` (2 ** 30)) <> "G" + | i >= 2 ^ (20 :: Int64) = format (i `divBy` (2 ** 20)) <> "M" + | i >= 2 ^ (10 :: Int64) = format (i `divBy` (2 ** 10)) <> "K" + | otherwise = show i + where + format = printf "%0.1f" + divBy :: Int64 -> Double -> Double + divBy a b = (fromIntegral a :: Double) / b |
