aboutsummaryrefslogtreecommitdiff
path: root/lib/Daffm/View.hs
blob: 387fffcd593d86db249d36e2ef04284048765e1b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
module Daffm.View where

import Brick.Types (Widget)
import Brick.Widgets.Core (Padding (Max, Pad), emptyWidget, hBox, hLimit, padLeft, padRight, str, txt, vBox, vLimit, withAttr, (<+>))
import Brick.Widgets.Edit (renderEditor)
import qualified Brick.Widgets.List as L
import Daffm.Attrs (directoryAttr, directorySelectedAttr, fileAttr, fileSelectedAttr)
import Daffm.Keymap (showKeySequence)
import Daffm.Types (AppState (..), FileInfo (..), FileType (..), FocusTarget (..))
import Data.Int (Int64)
import qualified Data.Set as Set
import qualified Data.Text as Text
import qualified Data.Vector as Vec
import System.Posix.Types (FileMode)
import qualified System.PosixCompat as Posix
import Text.Printf (printf)

appView :: AppState -> [Widget FocusTarget]
appView appState@(AppState {stateFiles}) = [ui]
  where
    ui = vBox [vLimit 1 header, box, vLimit 1 cmdline]
    header = headerView appState
    cmdline = cmdlineView appState
    box = L.renderList (fileItemView appState) True stateFiles

hFixed :: Int -> Widget n -> Widget n
hFixed w = hLimit w . padRight Max

headerView :: AppState -> Widget n
headerView (AppState {stateCwd}) = txt stateCwd

fileItemView :: AppState -> Bool -> FileInfo -> Widget FocusTarget
fileItemView appState sel fileInfo@(FileInfo {filePath, fileSize, fileType, fileMode}) =
  hBox
    [ hFixed 2 fileSelectionView,
      hFixed 10 $ fileModeView fileMode,
      hFixed 6 $ fileTypeView fileType,
      hFixed 7 $ fileSizeView fileSize,
      fileNameView sel fileInfo
    ]
  where
    fileSizeView = txt . prettyFileSize . fromIntegral
    fileTypeView = txt . showFileType
    fileModeView = txt . showFileMode
    fileSelectionView = txt $ if Set.member filePath $ stateFileSelections appState then ">" else " "

showFileType :: FileType -> Text.Text
showFileType Directory = "dir"
showFileType SymbolicLink = "link"
showFileType UnixSocket = "sock"
showFileType NamedPipe = "pipe"
showFileType CharacterDevice = "cdev"
showFileType BlockDevice = "bdev"
showFileType RegularFile = "file"
showFileType UnknownFileType = "?"

showFileMode :: FileMode -> Text.Text
showFileMode mode = permchars
  where
    perm m c = if Posix.intersectFileModes mode m == m then c else '-'
    permchars =
      Text.pack
        [ perm Posix.ownerReadMode 'r',
          perm Posix.ownerWriteMode 'w',
          perm Posix.ownerExecuteMode 'x',
          perm Posix.groupReadMode 'r',
          perm Posix.groupWriteMode 'w',
          perm Posix.groupExecuteMode 'x',
          perm Posix.otherReadMode 'r',
          perm Posix.otherWriteMode 'w',
          perm Posix.otherExecuteMode 'x'
        ]

fileNameView :: Bool -> FileInfo -> Widget FocusTarget
fileNameView True (FileInfo {fileName, fileType = Directory}) = withAttr directorySelectedAttr $ txt $ fileName <> "/"
fileNameView False (FileInfo {fileName, fileType = Directory}) = withAttr directoryAttr $ txt $ fileName <> "/"
fileNameView True (FileInfo {fileName}) = withAttr fileSelectedAttr $ txt fileName
fileNameView False (FileInfo {fileName}) = withAttr fileAttr $ txt fileName

cmdlineView :: AppState -> Widget FocusTarget
cmdlineView (AppState {stateFocusTarget = FocusCmdline, stateCmdlineEditor}) =
  txt ":" <+> renderEditor (txt . Text.unlines) True stateCmdlineEditor
cmdlineView (AppState {stateFocusTarget = FocusMain, stateFiles, stateFileSelections, stateKeySequence}) =
  hBox
    [ txt ":",
      rightAligned [keysView, padLeft (Pad 2) selectionsCountView, padLeft (Pad 2) posIndicatorView]
    ]
  where
    keysView = txt $ showKeySequence stateKeySequence
    rightAligned = padLeft Max . padRight (Pad 1) . hBox
    posIndicatorView = str $ cur <> "/" <> total
    selectionsCountView =
      if Set.null stateFileSelections
        then emptyWidget
        else str $ show $ Set.size stateFileSelections
    cur = case L.listSelected stateFiles of
      Nothing -> "-"
      Just n -> show (n + 1)
    total = show $ Vec.length $ L.listElements stateFiles

prettyFileSize :: Int64 -> Text.Text
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 = Text.pack $ show i
  where
    format = Text.pack . printf "%0.1f"
    divBy :: Int64 -> Double -> Double
    divBy a b = (fromIntegral a :: Double) / b