33{-# LANGUAGE OverloadedLabels #-}
44
55import Brick (
6- App (App ),
76 AttrMap ,
87 AttrName ,
9- BrickEvent (VtyEvent ),
10- EventM ,
118 VScrollBarOrientation (OnRight ),
12- Widget ,
9+ ViewportType ( Both ) ,
1310 attrMap ,
1411 attrName ,
1512 defaultMain ,
13+ hBox ,
1614 halt ,
1715 neverShowCursor ,
1816 on ,
1917 str ,
18+ viewport ,
2019 withAttr ,
2120 withVScrollBars ,
2221 zoom ,
@@ -30,15 +29,24 @@ import Brick.Widgets.List (
3029 handleListEvent ,
3130 list ,
3231 listSelectedAttr ,
32+ listSelectedElementL ,
3333 listSelectedFocusedAttr ,
3434 renderList ,
3535 )
3636import Control.Monad (void )
37+ import Data.Foldable (toList )
3738import Data.Function ((&) )
3839import Data.Generics.Labels ()
3940import Data.Vector qualified as Vector
4041import GHC.Generics (Generic )
41- import Graphics.Vty (Event (EvKey ), Key (KEsc ), black , defAttr , white )
42+ import Graphics.Vty (
43+ Event (EvKey ),
44+ Key (KChar , KEnter , KEsc ),
45+ black ,
46+ defAttr ,
47+ white ,
48+ )
49+ import Lens.Micro.Mtl (preuse , (.=) )
4250import RON.Storage.FS (runStorage )
4351import RON.Storage.FS qualified as StorageFS
4452
@@ -47,17 +55,40 @@ import FF.Config (loadConfig)
4755import FF.Types (Entity (Entity ), EntityDoc , Note (Note ))
4856import FF.Types qualified
4957
50- newtype Model = Model { notes :: List () (EntityDoc Note )} deriving (Generic )
58+ -- | Brick widget names
59+ data WN
60+ = NoteList
61+ | OpenNoteViewport
62+ deriving (Eq , Ord , Show )
63+
64+ data Model = Model
65+ { notes :: List WN (EntityDoc Note )
66+ -- ^ all notes
67+ , openNoteM :: Maybe (EntityDoc Note )
68+ -- ^ currently opened note
69+ }
70+ deriving (Generic )
71+
72+ type Widget = Brick. Widget WN
73+
74+ type App = Brick. App Model () WN
75+
76+ type EventM = Brick. EventM WN Model
77+
78+ type BrickEvent = Brick. BrickEvent WN ()
5179
5280main :: IO ()
5381main = do
5482 cfg <- loadConfig
55- dataDir <- getDataDir cfg
56- handleM <- traverse StorageFS. newHandle dataDir
83+ dataDirM <- getDataDir cfg
84+ handleM <- traverse StorageFS. newHandle dataDirM
5785 handle <- handleM `orElse` fail noDataDirectoryMessage
5886 notes <- runStorage handle loadAllNotes
5987 let initialModel =
60- Model {notes = list () (Vector. fromList notes) listItemHeight}
88+ Model
89+ { notes = list NoteList (Vector. fromList notes) listItemHeight
90+ , openNoteM = Nothing
91+ }
6192 void $ defaultMain app initialModel
6293
6394listItemHeight :: Int
@@ -66,9 +97,9 @@ listItemHeight = 1
6697orElse :: (Applicative m ) => Maybe a -> m a -> m a
6798orElse m n = maybe n pure m
6899
69- app :: App Model () ()
100+ app :: App
70101app =
71- App
102+ Brick. App
72103 { appAttrMap = const appAttrMap
73104 , appChooseCursor = neverShowCursor
74105 , appDraw
@@ -85,33 +116,57 @@ appAttrMap =
85116 , (highlightAttr, black `on` white)
86117 ]
87118
88- appDraw :: Model -> [Widget () ]
89- appDraw Model {notes} =
90- [ borderWithLabel
91- (str " Agenda" )
92- (renderList renderListItem True notes & withVScrollBars OnRight )
93- <=> (withAttr highlightAttr (str " Esc" ) <+> str " exit" )
94- ]
119+ appDraw :: Model -> [Widget ]
120+ appDraw Model {notes, openNoteM} = [mainWidget <=> keysHelpLine]
121+ where
122+ mainWidget = hBox $ agenda : toList openNoteWidget
123+
124+ agenda =
125+ borderWithLabel
126+ (str " Agenda" )
127+ (renderList renderListItem True notes & withVScrollBars OnRight )
128+
129+ openNoteWidget = do
130+ Entity {entityVal = note@ Note {note_text}} <- openNoteM
131+ let title = noteTitle note
132+ text = fromRgaM note_text
133+ pure $
134+ borderWithLabel (str title) $
135+ viewport OpenNoteViewport Both $
136+ str text
137+
138+ keysHelpLine =
139+ withAttr highlightAttr (str " Esc" )
140+ <+> str " "
141+ <+> withAttr highlightAttr (str " q" )
142+ <+> str " exit "
143+ <+> withAttr highlightAttr (str " Enter" )
144+ <+> str " open"
95145
96146highlightAttr :: AttrName
97147highlightAttr = attrName " highlight"
98148
99- appHandleEvent :: BrickEvent () () -> EventM () Model ()
149+ appHandleEvent :: BrickEvent -> EventM ()
100150appHandleEvent = \ case
101- VtyEvent e -> appHandleVtyEvent e
151+ Brick. VtyEvent e -> appHandleVtyEvent e
102152 _ -> pure ()
103153
104- appHandleVtyEvent :: Event -> EventM () Model ()
154+ appHandleVtyEvent :: Event -> EventM ()
105155appHandleVtyEvent = \ case
106- EvKey KEsc _ -> halt
156+ EvKey KEsc [] -> halt
157+ EvKey (KChar ' q' ) [] -> halt
158+ EvKey KEnter [] -> do
159+ -- open selected note
160+ selectedNoteM <- preuse $ # notes . listSelectedElementL
161+ # openNoteM .= selectedNoteM
107162 e -> zoom # notes $ handleListEvent e
108163
109- renderListItem :: Bool -> EntityDoc Note -> Widget ()
110- renderListItem _isSelected Entity {entityVal = Note {note_text}} =
111- str
112- case textLines of
113- [] -> " ... "
114- [singleLine] -> singleLine
115- firstLine : _ -> firstLine < > " ..."
116- where
117- textLines = filter ( not . null ) $ lines $ fromRgaM note_text
164+ renderListItem :: Bool -> EntityDoc Note -> Widget
165+ renderListItem _isSelected Entity {entityVal} = str $ noteTitle entityVal
166+
167+ noteTitle :: Note -> String
168+ noteTitle Note {note_text} =
169+ case filter ( not . null ) $ lines $ fromRgaM note_text of
170+ [] - > " ..."
171+ [singleLine] -> singleLine
172+ firstLine : _ -> firstLine <> " ... "
0 commit comments