pkg: ui: use/require brick 1.0+ (#1889)

This commit is contained in:
Simon Michael 2022-08-17 11:04:50 +01:00
parent b636eb78a9
commit 2a594b7fb7
11 changed files with 262 additions and 274 deletions

View File

@ -152,7 +152,7 @@ asDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}
ishistorical = balanceaccum_ ropts == Historical ishistorical = balanceaccum_ ropts == Historical
toplabel = toplabel =
withAttr ("border" <> "filename") files withAttr (attrName "border" <> attrName "filename") files
<+> toggles <+> toggles
<+> str (" account " ++ if ishistorical then "balances" else "changes") <+> str (" account " ++ if ishistorical then "balances" else "changes")
<+> borderPeriodStr (if ishistorical then "at end of" else "in") (period_ ropts) <+> borderPeriodStr (if ishistorical then "at end of" else "in") (period_ ropts)
@ -160,7 +160,7 @@ asDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}
<+> borderDepthStr mdepth <+> borderDepthStr mdepth
<+> str (" ("++curidx++"/"++totidx++")") <+> str (" ("++curidx++"/"++totidx++")")
<+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts
then withAttr ("border" <> "query") (str " ignoring balance assertions") then withAttr (attrName "border" <> attrName "query") (str " ignoring balance assertions")
else str "") else str "")
where where
files = case journalFilePaths j of files = case journalFilePaths j of
@ -168,7 +168,7 @@ asDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}
f:_ -> str $ takeFileName f f:_ -> str $ takeFileName f
-- [f,_:[]] -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str " (& 1 included file)" -- [f,_:[]] -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str " (& 1 included file)"
-- f:fs -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str (" (& " ++ show (length fs) ++ " included files)") -- f:fs -> (withAttr ("border" <> "bold") $ str $ takeFileName f) <+> str (" (& " ++ show (length fs) ++ " included files)")
toggles = withAttr ("border" <> "query") $ str $ unwords $ concat [ toggles = withAttr (attrName "border" <> attrName "query") $ str $ unwords $ concat [
[""] [""]
,if empty_ ropts then [] else ["nonzero"] ,if empty_ ropts then [] else ["nonzero"]
,uiShowStatus copts $ statuses_ ropts ,uiShowStatus copts $ statuses_ ropts
@ -220,22 +220,24 @@ asDrawItem (acctwidth, balwidth) selected AccountsScreenItem{..} =
balspace = T.replicate (2 + balwidth - wbWidth balBuilder) " " balspace = T.replicate (2 + balwidth - wbWidth balBuilder) " "
splitAmounts = foldr1 (<+>) . intersperse (str ", ") . map renderamt . T.splitOn ", " . wbToText splitAmounts = foldr1 (<+>) . intersperse (str ", ") . map renderamt . T.splitOn ", " . wbToText
renderamt :: T.Text -> Widget Name renderamt :: T.Text -> Widget Name
renderamt a | T.any (=='-') a = withAttr (sel $ "list" <> "balance" <> "negative") $ txt a renderamt a | T.any (=='-') a = withAttr (sel $ attrName "list" <> attrName "balance" <> attrName "negative") $ txt a
| otherwise = withAttr (sel $ "list" <> "balance" <> "positive") $ txt a | otherwise = withAttr (sel $ attrName "list" <> attrName "balance" <> attrName "positive") $ txt a
sel | selected = (<> "selected") sel | selected = (<> attrName "selected")
| otherwise = id | otherwise = id
asHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) asHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
asHandle ui0@UIState{ asHandle ev = do
ui0@UIState{
aScreen=scr@AccountsScreen{..} aScreen=scr@AccountsScreen{..}
,aopts=UIOpts{uoCliOpts=copts} ,aopts=UIOpts{uoCliOpts=copts}
,ajournal=j ,ajournal=j
,aMode=mode ,aMode=mode
} ev = do } <- get -- PARTIAL: should not fail
let let
d = copts^.rsDay d = copts^.rsDay
nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ _asList^.listElementsL nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ _asList^.listElementsL
lastnonblankidx = max 0 (length nonblanks - 1) lastnonblankidx = max 0 (length nonblanks - 1)
journalspan = journalDateSpan False j
-- save the currently selected account, in case we leave this screen and lose the selection -- save the currently selected account, in case we leave this screen and lose the selection
let let
@ -247,87 +249,81 @@ asHandle ui0@UIState{
case mode of case mode of
Minibuffer _ ed -> Minibuffer _ ed ->
case ev of case ev of
VtyEvent (EvKey KEsc []) -> continue $ closeMinibuffer ui VtyEvent (EvKey KEsc []) -> put $ closeMinibuffer ui
VtyEvent (EvKey KEnter []) -> continue $ regenerateScreens j d $ VtyEvent (EvKey KEnter []) -> put $ regenerateScreens j d $
case setFilter s $ closeMinibuffer ui of case setFilter s $ closeMinibuffer ui of
Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui
Right ui' -> ui' Right ui' -> ui'
where s = chomp $ unlines $ map strip $ getEditContents ed where s = chomp $ unlines $ map strip $ getEditContents ed
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
VtyEvent ev -> do VtyEvent ev -> do
ed' <- handleEditorEvent ed' <- nestEventM' ed $ handleEditorEvent (VtyEvent ev)
#if MIN_VERSION_brick(0,72,0) put ui{aMode=Minibuffer "filter" ed'}
(VtyEvent ev) AppEvent _ -> return ()
#else MouseDown{} -> return ()
ev MouseUp{} -> return ()
#endif
ed
continue $ ui{aMode=Minibuffer "filter" ed'}
AppEvent _ -> continue ui
MouseDown{} -> continue ui
MouseUp{} -> continue ui
Help -> Help ->
case ev of case ev of
-- VtyEvent (EvKey (KChar 'q') []) -> halt ui -- VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
_ -> helpHandle ui ev _ -> helpHandle ev
Normal -> Normal ->
case ev of case ev of
VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey (KChar 'q') []) -> halt
-- EvKey (KChar 'l') [MCtrl] -> do -- EvKey (KChar 'l') [MCtrl] -> do
VtyEvent (EvKey KEsc []) -> continue $ resetScreens d ui VtyEvent (EvKey KEsc []) -> put $ resetScreens d ui
VtyEvent (EvKey (KChar c) []) | c == '?' -> continue $ setMode Help ui VtyEvent (EvKey (KChar c) []) | c == '?' -> put $ setMode Help ui
-- XXX AppEvents currently handled only in Normal mode -- XXX AppEvents currently handled only in Normal mode
-- XXX be sure we don't leave unconsumed events piling up -- XXX be sure we don't leave unconsumed events piling up
AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old -> AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old ->
continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui put $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
where where
p = reportPeriod ui p = reportPeriod ui
e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->
liftIO (uiReloadJournal copts d ui) >>= continue liftIO (uiReloadJournal copts d ui) >>= put
VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) VtyEvent (EvKey (KChar 'I') []) -> put $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)
VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui
VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui
VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor endPosition (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui
VtyEvent (EvKey (KChar 'B') []) -> continue $ regenerateScreens j d $ toggleConversionOp ui VtyEvent (EvKey (KChar 'B') []) -> put $ regenerateScreens j d $ toggleConversionOp ui
VtyEvent (EvKey (KChar 'V') []) -> continue $ regenerateScreens j d $ toggleValue ui VtyEvent (EvKey (KChar 'V') []) -> put $ regenerateScreens j d $ toggleValue ui
VtyEvent (EvKey (KChar '0') []) -> continue $ regenerateScreens j d $ setDepth (Just 0) ui VtyEvent (EvKey (KChar '0') []) -> put $ regenerateScreens j d $ setDepth (Just 0) ui
VtyEvent (EvKey (KChar '1') []) -> continue $ regenerateScreens j d $ setDepth (Just 1) ui VtyEvent (EvKey (KChar '1') []) -> put $ regenerateScreens j d $ setDepth (Just 1) ui
VtyEvent (EvKey (KChar '2') []) -> continue $ regenerateScreens j d $ setDepth (Just 2) ui VtyEvent (EvKey (KChar '2') []) -> put $ regenerateScreens j d $ setDepth (Just 2) ui
VtyEvent (EvKey (KChar '3') []) -> continue $ regenerateScreens j d $ setDepth (Just 3) ui VtyEvent (EvKey (KChar '3') []) -> put $ regenerateScreens j d $ setDepth (Just 3) ui
VtyEvent (EvKey (KChar '4') []) -> continue $ regenerateScreens j d $ setDepth (Just 4) ui VtyEvent (EvKey (KChar '4') []) -> put $ regenerateScreens j d $ setDepth (Just 4) ui
VtyEvent (EvKey (KChar '5') []) -> continue $ regenerateScreens j d $ setDepth (Just 5) ui VtyEvent (EvKey (KChar '5') []) -> put $ regenerateScreens j d $ setDepth (Just 5) ui
VtyEvent (EvKey (KChar '6') []) -> continue $ regenerateScreens j d $ setDepth (Just 6) ui VtyEvent (EvKey (KChar '6') []) -> put $ regenerateScreens j d $ setDepth (Just 6) ui
VtyEvent (EvKey (KChar '7') []) -> continue $ regenerateScreens j d $ setDepth (Just 7) ui VtyEvent (EvKey (KChar '7') []) -> put $ regenerateScreens j d $ setDepth (Just 7) ui
VtyEvent (EvKey (KChar '8') []) -> continue $ regenerateScreens j d $ setDepth (Just 8) ui VtyEvent (EvKey (KChar '8') []) -> put $ regenerateScreens j d $ setDepth (Just 8) ui
VtyEvent (EvKey (KChar '9') []) -> continue $ regenerateScreens j d $ setDepth (Just 9) ui VtyEvent (EvKey (KChar '9') []) -> put $ regenerateScreens j d $ setDepth (Just 9) ui
VtyEvent (EvKey (KChar '-') []) -> continue $ regenerateScreens j d $ decDepth ui VtyEvent (EvKey (KChar '-') []) -> put $ regenerateScreens j d $ decDepth ui
VtyEvent (EvKey (KChar '_') []) -> continue $ regenerateScreens j d $ decDepth ui VtyEvent (EvKey (KChar '_') []) -> put $ regenerateScreens j d $ decDepth ui
VtyEvent (EvKey (KChar c) []) | c `elem` ['+','='] -> continue $ regenerateScreens j d $ incDepth ui VtyEvent (EvKey (KChar c) []) | c `elem` ['+','='] -> put $ regenerateScreens j d $ incDepth ui
VtyEvent (EvKey (KChar 'T') []) -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui VtyEvent (EvKey (KChar 'T') []) -> put $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
-- display mode/query toggles -- display mode/query toggles
VtyEvent (EvKey (KChar 'H') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui VtyEvent (EvKey (KChar 'H') []) -> modify (regenerateScreens j d . toggleHistorical) >> asCenterAndContinue
VtyEvent (EvKey (KChar 't') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleTree ui VtyEvent (EvKey (KChar 't') []) -> modify (regenerateScreens j d . toggleTree) >> asCenterAndContinue
VtyEvent (EvKey (KChar c) []) | c `elem` ['z','Z'] -> asCenterAndContinue $ regenerateScreens j d $ toggleEmpty ui VtyEvent (EvKey (KChar c) []) | c `elem` ['z','Z'] -> modify (regenerateScreens j d . toggleEmpty) >> asCenterAndContinue
VtyEvent (EvKey (KChar 'R') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleReal ui VtyEvent (EvKey (KChar 'R') []) -> modify (regenerateScreens j d . toggleReal) >> asCenterAndContinue
VtyEvent (EvKey (KChar 'U') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui VtyEvent (EvKey (KChar 'U') []) -> modify (regenerateScreens j d . toggleUnmarked) >> asCenterAndContinue
VtyEvent (EvKey (KChar 'P') []) -> asCenterAndContinue $ regenerateScreens j d $ togglePending ui VtyEvent (EvKey (KChar 'P') []) -> modify (regenerateScreens j d . togglePending) >> asCenterAndContinue
VtyEvent (EvKey (KChar 'C') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleCleared ui VtyEvent (EvKey (KChar 'C') []) -> modify (regenerateScreens j d . toggleCleared) >> asCenterAndContinue
VtyEvent (EvKey (KChar 'F') []) -> continue $ regenerateScreens j d $ toggleForecast d ui VtyEvent (EvKey (KChar 'F') []) -> modify (regenerateScreens j d . toggleForecast d)
VtyEvent (EvKey (KDown) [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui VtyEvent (EvKey (KDown) [MShift]) -> put $ regenerateScreens j d $ shrinkReportPeriod d ui
VtyEvent (EvKey (KUp) [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui VtyEvent (EvKey (KUp) [MShift]) -> put $ regenerateScreens j d $ growReportPeriod d ui
VtyEvent (EvKey (KRight) [MShift]) -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui VtyEvent (EvKey (KRight) [MShift]) -> put $ regenerateScreens j d $ nextReportPeriod journalspan ui
VtyEvent (EvKey (KLeft) [MShift]) -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui VtyEvent (EvKey (KLeft) [MShift]) -> put $ regenerateScreens j d $ previousReportPeriod journalspan ui
VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui VtyEvent (EvKey (KChar '/') []) -> put $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui
VtyEvent (EvKey k []) | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui) VtyEvent (EvKey k []) | k `elem` [KBS, KDel] -> (put $ regenerateScreens j d $ resetFilter ui)
VtyEvent e | e `elem` moveLeftEvents -> continue $ popScreen ui VtyEvent e | e `elem` moveLeftEvents -> put $ popScreen ui
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle _asList >> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle _asList >> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
-- enter register screen for selected account (if there is one), -- enter register screen for selected account (if there is one),
@ -338,7 +334,7 @@ asHandle ui0@UIState{
-- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347 -- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347
-- just use it to move the selection -- just use it to move the selection
MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickedacct -> do MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickedacct -> do
continue ui{aScreen=scr{_asList=listMoveTo y _asList}} case scr of AccountsScreen{} -> put ui{aScreen=scr}; _ -> fail "" -- PARTIAL: should not happen
where clickedacct = maybe "" asItemAccountName $ listElements _asList !? y where clickedacct = maybe "" asItemAccountName $ listElements _asList !? y
-- and on MouseUp, enter the subscreen -- and on MouseUp, enter the subscreen
MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickedacct -> do MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickedacct -> do
@ -347,42 +343,35 @@ asHandle ui0@UIState{
-- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled -- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled
VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do
vScrollBy (viewportScroll $ _asList^.listNameL) 1 >> continue ui vScrollBy (viewportScroll $ _asList^.listNameL) 1 >> put ui
where mnextelement = listSelectedElement $ listMoveDown _asList where mnextelement = listSelectedElement $ listMoveDown _asList
-- mouse scroll wheel scrolls the viewport up or down to its maximum extent, -- mouse scroll wheel scrolls the viewport up or down to its maximum extent,
-- pushing the selection when necessary. -- pushing the selection when necessary.
MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do
let scrollamt = if btn==BScrollUp then -1 else 1 let scrollamt = if btn==BScrollUp then -1 else 1
list' <- listScrollPushingSelection name _asList (asListSize _asList) scrollamt list' <- nestEventM' _asList $ listScrollPushingSelection name (asListSize _asList) scrollamt
continue ui{aScreen=scr{_asList=list'}} case scr of AccountsScreen{} -> put ui{aScreen=scr{_asList=list'}}; _ -> fail "" -- PARTIAL: should not fail
-- if page down or end leads to a blank padding item, stop at last non-blank -- if page down or end leads to a blank padding item, stop at last non-blank
VtyEvent e@(EvKey k []) | k `elem` [KPageDown, KEnd] -> do VtyEvent e@(EvKey k []) | k `elem` [KPageDown, KEnd] -> do
list <- handleListEvent e _asList list <- nestEventM' _asList $ handleListEvent e
if isBlankElement $ listSelectedElement list if isBlankElement $ listSelectedElement list
then do then do
let list' = listMoveTo lastnonblankidx list let list' = listMoveTo lastnonblankidx list
scrollSelectionToMiddle list' scrollSelectionToMiddle list'
continue ui{aScreen=scr{_asList=list'}} case scr of AccountsScreen{} -> put ui{aScreen=scr{_asList=list'}}; _ -> fail "" -- PARTIAL: should not fail
else else
continue ui{aScreen=scr{_asList=list}} case scr of AccountsScreen{} -> put ui{aScreen=scr{_asList=list}}; _ -> fail "" -- PARTIAL: should not fail
-- fall through to the list's event handler (handles up/down) -- fall through to the list's event handler (handles up/down)
VtyEvent ev -> do VtyEvent ev -> do
newitems <- handleListEvent (normaliseMovementKeys ev) _asList list' <- nestEventM' _asList $ handleListEvent (normaliseMovementKeys ev)
continue $ ui{aScreen=scr & asList .~ newitems put $ ui{aScreen=scr & asList .~ list' & asSelectedAccount .~ selacct }
& asSelectedAccount .~ selacct
}
MouseDown{} -> continue ui MouseDown{} -> put ui
MouseUp{} -> continue ui MouseUp{} -> put ui
AppEvent _ -> continue ui AppEvent _ -> put ui
where
journalspan = journalDateSpan False j
asHandle _ _ = error "event handler called with wrong screen type, should not happen" -- PARTIAL:
asEnterRegister d selacct ui = do asEnterRegister d selacct ui = do
rsCenterAndContinue $ rsCenterAndContinue $
@ -399,8 +388,9 @@ asSetSelectedAccount _ s = s
isBlankElement mel = ((asItemAccountName . snd) <$> mel) == Just "" isBlankElement mel = ((asItemAccountName . snd) <$> mel) == Just ""
asCenterAndContinue ui = do asCenterAndContinue :: EventM Name UIState ()
scrollSelectionToMiddle $ _asList $ aScreen ui asCenterAndContinue = do
continue ui ui <- get
scrollSelectionToMiddle (_asList $ aScreen ui)
asListSize = V.length . V.takeWhile ((/="").asItemAccountName) . listElements asListSize = V.length . V.takeWhile ((/="").asItemAccountName) . listElements

View File

@ -54,10 +54,10 @@ esDraw UIState{aopts=UIOpts{uoCliOpts=copts}
_ -> [maincontent] _ -> [maincontent]
where where
maincontent = Widget Greedy Greedy $ do maincontent = Widget Greedy Greedy $ do
render $ defaultLayout toplabel bottomlabel $ withAttr "error" $ str $ esError render $ defaultLayout toplabel bottomlabel $ withAttr (attrName "error") $ str $ esError
where where
toplabel = toplabel =
withAttr ("border" <> "bold") (str "Oops. Please fix this problem then press g to reload") withAttr (attrName "border" <> attrName "bold") (str "Oops. Please fix this problem then press g to reload")
-- <+> (if ignore_assertions_ copts then withAttr ("border" <> "query") (str " ignoring") else str " not ignoring") -- <+> (if ignore_assertions_ copts then withAttr ("border" <> "query") (str " ignoring") else str " not ignoring")
bottomlabel = quickhelp bottomlabel = quickhelp
@ -75,44 +75,42 @@ esDraw UIState{aopts=UIOpts{uoCliOpts=copts}
esDraw _ = error "draw function called with wrong screen type, should not happen" -- PARTIAL: esDraw _ = error "draw function called with wrong screen type, should not happen" -- PARTIAL:
esHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) esHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
esHandle ui@UIState{aScreen=ErrorScreen{..} esHandle ev = do
ui@UIState{aScreen=ErrorScreen{..}
,aopts=UIOpts{uoCliOpts=copts} ,aopts=UIOpts{uoCliOpts=copts}
,ajournal=j ,ajournal=j
,aMode=mode ,aMode=mode
} } <- get
ev =
case mode of case mode of
Help -> Help ->
case ev of case ev of
VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
_ -> helpHandle ui ev _ -> helpHandle ev
_ -> do _ -> do
let d = copts^.rsDay let d = copts^.rsDay
case ev of case ev of
VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey KEsc []) -> continue $ uiCheckBalanceAssertions d $ resetScreens d ui VtyEvent (EvKey KEsc []) -> put $ uiCheckBalanceAssertions d $ resetScreens d ui
VtyEvent (EvKey (KChar c) []) | c `elem` ['h','?'] -> continue $ setMode Help ui VtyEvent (EvKey (KChar c) []) | c `elem` ['h','?'] -> put $ setMode Help ui
VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j (popScreen ui) VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j (popScreen ui)
where where
(pos,f) = case parsewithString hledgerparseerrorpositionp esError of (pos,f) = case parsewithString hledgerparseerrorpositionp esError of
Right (f,l,c) -> (Just (l, Just c),f) Right (f,l,c) -> (Just (l, Just c),f)
Left _ -> (endPosition, journalFilePath j) Left _ -> (endPosition, journalFilePath j)
e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->
liftIO (uiReloadJournal copts d (popScreen ui)) >>= continue . uiCheckBalanceAssertions d liftIO (uiReloadJournal copts d (popScreen ui)) >>= put . uiCheckBalanceAssertions d
-- (ej, _) <- liftIO $ journalReloadIfChanged copts d j -- (ej, _) <- liftIO $ journalReloadIfChanged copts d j
-- case ej of -- case ej of
-- Left err -> continue ui{aScreen=s{esError=err}} -- show latest parse error -- Left err -> continue ui{aScreen=s{esError=err}} -- show latest parse error
-- Right j' -> continue $ regenerateScreens j' d $ popScreen ui -- return to previous screen, and reload it -- Right j' -> continue $ regenerateScreens j' d $ popScreen ui -- return to previous screen, and reload it
VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (popScreen $ toggleIgnoreBalanceAssertions ui) VtyEvent (EvKey (KChar 'I') []) -> put $ uiCheckBalanceAssertions d (popScreen $ toggleIgnoreBalanceAssertions ui)
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
_ -> continue ui _ -> return ()
esHandle _ _ = error "event handler called with wrong screen type, should not happen" -- PARTIAL:
-- | Parse the file name, line and column number from a hledger parse error message, if possible. -- | Parse the file name, line and column number from a hledger parse error message, if possible.
-- Temporary, we should keep the original parse error location. XXX -- Temporary, we should keep the original parse error location. XXX

View File

@ -1,3 +1,5 @@
-- TODO: brick 1 support
-- https://hackage.haskell.org/package/brick-1.0/changelog
{-| {-|
hledger-ui - a hledger add-on providing a curses-style interface. hledger-ui - a hledger add-on providing a curses-style interface.
Copyright (c) 2007-2015 Simon Michael <simon@joyful.com> Copyright (c) 2007-2015 Simon Michael <simon@joyful.com>
@ -159,11 +161,11 @@ runBrickUi uopts@UIOpts{uoCliOpts=copts@CliOpts{inputopts_=_iopts,reportspec_=rs
brickapp :: App UIState AppEvent Name brickapp :: App UIState AppEvent Name
brickapp = App { brickapp = App {
appStartEvent = return appStartEvent = return ()
, appAttrMap = const $ fromMaybe defaultTheme $ getTheme =<< uoTheme uopts' , appAttrMap = const $ fromMaybe defaultTheme $ getTheme =<< uoTheme uopts'
, appChooseCursor = showFirstCursor , appChooseCursor = showFirstCursor
, appHandleEvent = \ui ev -> sHandle (aScreen ui) ui ev , appHandleEvent = sHandle (aScreen ui)
, appDraw = \ui -> sDraw (aScreen ui) ui , appDraw = sDraw (aScreen ui)
} }
-- print (length (show ui)) >> exitSuccess -- show any debug output to this point & quit -- print (length (show ui)) >> exitSuccess -- show any debug output to this point & quit

View File

@ -4,7 +4,6 @@
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}
module Hledger.UI.RegisterScreen module Hledger.UI.RegisterScreen
@ -199,7 +198,7 @@ rsDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}
-- inclusive = tree_ ropts || rsForceInclusive -- inclusive = tree_ ropts || rsForceInclusive
toplabel = toplabel =
withAttr ("border" <> "bold") (str $ T.unpack $ replaceHiddenAccountsNameWith "All" rsAccount) withAttr (attrName "border" <> attrName "bold") (str $ T.unpack $ replaceHiddenAccountsNameWith "All" rsAccount)
-- <+> withAttr ("border" <> "query") (str $ if inclusive then "" else " exclusive") -- <+> withAttr ("border" <> "query") (str $ if inclusive then "" else " exclusive")
<+> togglefilters <+> togglefilters
<+> str " transactions" <+> str " transactions"
@ -212,7 +211,7 @@ rsDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}
<+> str "/" <+> str "/"
<+> total <+> total
<+> str ")" <+> str ")"
<+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr ("border" <> "query") (str " ignoring balance assertions") else str "") <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr (attrName "border" <> attrName "query") (str " ignoring balance assertions") else str "")
where where
togglefilters = togglefilters =
case concat [ case concat [
@ -221,7 +220,7 @@ rsDraw UIState{aopts=_uopts@UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec}}
,if empty_ ropts then [] else ["nonzero"] ,if empty_ ropts then [] else ["nonzero"]
] of ] of
[] -> str "" [] -> str ""
fs -> withAttr ("border" <> "query") (str $ " " ++ intercalate ", " fs) fs -> withAttr (attrName "border" <> attrName "query") (str $ " " ++ intercalate ", " fs)
cur = str $ case rsList ^. listSelectedL of cur = str $ case rsList ^. listSelectedL of
Nothing -> "-" Nothing -> "-"
Just i -> show (i + 1) Just i -> show (i + 1)
@ -271,20 +270,21 @@ rsDrawItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected Regist
where where
changeAmt = wbToText rsItemChangeAmount changeAmt = wbToText rsItemChangeAmount
balanceAmt = wbToText rsItemBalanceAmount balanceAmt = wbToText rsItemBalanceAmount
changeattr | T.any (=='-') changeAmt = sel $ "list" <> "amount" <> "decrease" changeattr | T.any (=='-') changeAmt = sel $ attrName "list" <> attrName "amount" <> attrName "decrease"
| otherwise = sel $ "list" <> "amount" <> "increase" | otherwise = sel $ attrName "list" <> attrName "amount" <> attrName "increase"
balattr | T.any (=='-') balanceAmt = sel $ "list" <> "balance" <> "negative" balattr | T.any (=='-') balanceAmt = sel $ attrName "list" <> attrName "balance" <> attrName "negative"
| otherwise = sel $ "list" <> "balance" <> "positive" | otherwise = sel $ attrName "list" <> attrName "balance" <> attrName "positive"
sel | selected = (<> "selected") sel | selected = (<> attrName "selected")
| otherwise = id | otherwise = id
rsHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) rsHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
rsHandle ui@UIState{ rsHandle ev = do
ui@UIState{
aScreen=s@RegisterScreen{..} aScreen=s@RegisterScreen{..}
,aopts=UIOpts{uoCliOpts=copts} ,aopts=UIOpts{uoCliOpts=copts}
,ajournal=j ,ajournal=j
,aMode=mode ,aMode=mode
} ev = do } <- get
let let
d = copts^.rsDay d = copts^.rsDay
journalspan = journalDateSpan False j journalspan = journalDateSpan False j
@ -294,50 +294,44 @@ rsHandle ui@UIState{
case mode of case mode of
Minibuffer _ ed -> Minibuffer _ ed ->
case ev of case ev of
VtyEvent (EvKey KEsc []) -> continue $ closeMinibuffer ui VtyEvent (EvKey KEsc []) -> modify closeMinibuffer
VtyEvent (EvKey KEnter []) -> continue $ regenerateScreens j d $ VtyEvent (EvKey KEnter []) -> put $ regenerateScreens j d $
case setFilter s $ closeMinibuffer ui of case setFilter s $ closeMinibuffer ui of
Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui Left bad -> showMinibuffer "Cannot compile regular expression" (Just bad) ui
Right ui' -> ui' Right ui' -> ui'
where s = chomp . unlines . map strip $ getEditContents ed where s = chomp . unlines . map strip $ getEditContents ed
-- VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui -- VtyEvent (EvKey (KChar '/') []) -> put $ regenerateScreens j d $ showMinibuffer ui
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
VtyEvent ev -> do VtyEvent ev -> do
ed' <- handleEditorEvent ed' <- nestEventM' ed $ handleEditorEvent (VtyEvent ev)
#if MIN_VERSION_brick(0,72,0) put ui{aMode=Minibuffer "filter" ed'}
(VtyEvent ev) AppEvent _ -> return ()
#else MouseDown{} -> return ()
ev MouseUp{} -> return ()
#endif
ed
continue $ ui{aMode=Minibuffer "filter" ed'}
AppEvent _ -> continue ui
MouseDown{} -> continue ui
MouseUp{} -> continue ui
Help -> Help ->
case ev of case ev of
-- VtyEvent (EvKey (KChar 'q') []) -> halt ui -- VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
_ -> helpHandle ui ev _ -> helpHandle ev
Normal -> Normal ->
case ev of case ev of
VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey KEsc []) -> continue $ resetScreens d ui VtyEvent (EvKey KEsc []) -> put $ resetScreens d ui
VtyEvent (EvKey (KChar c) []) | c == '?' -> continue $ setMode Help ui VtyEvent (EvKey (KChar c) []) | c == '?' -> put $ setMode Help ui
AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old -> AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old ->
continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui put $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
where where
p = reportPeriod ui p = reportPeriod ui
e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] ->
liftIO (uiReloadJournal copts d ui) >>= continue liftIO (uiReloadJournal copts d ui) >>= put
VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) VtyEvent (EvKey (KChar 'I') []) -> put $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)
VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'a') []) -> suspendAndResume $ clearScreen >> setCursorPosition 0 0 >> add copts j >> uiReloadJournalIfChanged copts d j ui
VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'A') []) -> suspendAndResume $ void (runIadd (journalFilePath j)) >> uiReloadJournalIfChanged copts d j ui
VtyEvent (EvKey (KChar 'T') []) -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui VtyEvent (EvKey (KChar 'T') []) -> put $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui
where where
(pos,f) = case listSelectedElement rsList of (pos,f) = case listSelectedElement rsList of
@ -357,78 +351,76 @@ rsHandle ui@UIState{
VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui
VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleForecast d ui VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleForecast d ui
VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui VtyEvent (EvKey (KChar '/') []) -> put $ regenerateScreens j d $ showMinibuffer "filter" Nothing ui
VtyEvent (EvKey (KDown) [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui VtyEvent (EvKey (KDown) [MShift]) -> put $ regenerateScreens j d $ shrinkReportPeriod d ui
VtyEvent (EvKey (KUp) [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui VtyEvent (EvKey (KUp) [MShift]) -> put $ regenerateScreens j d $ growReportPeriod d ui
VtyEvent (EvKey (KRight) [MShift]) -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui VtyEvent (EvKey (KRight) [MShift]) -> put $ regenerateScreens j d $ nextReportPeriod journalspan ui
VtyEvent (EvKey (KLeft) [MShift]) -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui VtyEvent (EvKey (KLeft) [MShift]) -> put $ regenerateScreens j d $ previousReportPeriod journalspan ui
VtyEvent (EvKey k []) | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui) VtyEvent (EvKey k []) | k `elem` [KBS, KDel] -> (put $ regenerateScreens j d $ resetFilter ui)
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle rsList >> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle rsList >> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
-- exit screen on LEFT -- exit screen on LEFT
VtyEvent e | e `elem` moveLeftEvents -> continue $ popScreen ui VtyEvent e | e `elem` moveLeftEvents -> put $ popScreen ui
-- or on a click in the app's left margin. This is a VtyEvent since not in a clickable widget. -- or on a click in the app's left margin. This is a VtyEvent since not in a clickable widget.
VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> continue $ popScreen ui VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> put $ popScreen ui
-- or on clicking a blank list item. -- or on clicking a blank list item.
MouseUp _ (Just BLeft) Location{loc=(_,y)} | clickeddate == "" -> continue $ popScreen ui MouseUp _ (Just BLeft) Location{loc=(_,y)} | clickeddate == "" -> put $ popScreen ui
where clickeddate = maybe "" rsItemDate $ listElements rsList !? y where clickeddate = maybe "" rsItemDate $ listElements rsList !? y
-- enter transaction screen on RIGHT -- enter transaction screen on RIGHT
VtyEvent e | e `elem` moveRightEvents -> VtyEvent e | e `elem` moveRightEvents ->
case listSelectedElement rsList of case listSelectedElement rsList of
Just _ -> continue $ screenEnter d transactionScreen{tsAccount=rsAccount} ui Just _ -> put $ screenEnter d transactionScreen{tsAccount=rsAccount} ui
Nothing -> continue ui Nothing -> put ui
-- or on transaction click -- or on transaction click
-- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347 -- MouseDown is sometimes duplicated, https://github.com/jtdaugherty/brick/issues/347
-- just use it to move the selection -- just use it to move the selection
MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickeddate -> do MouseDown _n BLeft _mods Location{loc=(_x,y)} | not $ (=="") clickeddate -> do
continue $ ui{aScreen=s{rsList=listMoveTo y rsList}} put $ ui{aScreen=s{rsList=listMoveTo y rsList}}
where clickeddate = maybe "" rsItemDate $ listElements rsList !? y where clickeddate = maybe "" rsItemDate $ listElements rsList !? y
-- and on MouseUp, enter the subscreen -- and on MouseUp, enter the subscreen
MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickeddate -> do MouseUp _n (Just BLeft) Location{loc=(_x,y)} | not $ (=="") clickeddate -> do
continue $ screenEnter d transactionScreen{tsAccount=rsAccount} ui put $ screenEnter d transactionScreen{tsAccount=rsAccount} ui
where clickeddate = maybe "" rsItemDate $ listElements rsList !? y where clickeddate = maybe "" rsItemDate $ listElements rsList !? y
-- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled -- when selection is at the last item, DOWN scrolls instead of moving, until maximally scrolled
VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do
vScrollBy (viewportScroll $ rsList ^. listNameL) 1 >> continue ui vScrollBy (viewportScroll $ rsList ^. listNameL) 1 >> put ui
where mnextelement = listSelectedElement $ listMoveDown rsList where mnextelement = listSelectedElement $ listMoveDown rsList
-- mouse scroll wheel scrolls the viewport up or down to its maximum extent, -- mouse scroll wheel scrolls the viewport up or down to its maximum extent,
-- pushing the selection when necessary. -- pushing the selection when necessary.
MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do MouseDown name btn _mods _loc | btn `elem` [BScrollUp, BScrollDown] -> do
let scrollamt = if btn==BScrollUp then -1 else 1 let scrollamt = if btn==BScrollUp then -1 else 1
list' <- listScrollPushingSelection name rsList (rsListSize rsList) scrollamt list' <- nestEventM' rsList $ listScrollPushingSelection name (rsListSize rsList) scrollamt
continue ui{aScreen=s{rsList=list'}} put ui{aScreen=s{rsList=list'}}
-- if page down or end leads to a blank padding item, stop at last non-blank -- if page down or end leads to a blank padding item, stop at last non-blank
VtyEvent e@(EvKey k []) | k `elem` [KPageDown, KEnd] -> do VtyEvent e@(EvKey k []) | k `elem` [KPageDown, KEnd] -> do
list <- handleListEvent e rsList list <- nestEventM' rsList $ handleListEvent e
if isBlankElement $ listSelectedElement list if isBlankElement $ listSelectedElement list
then do then do
let list' = listMoveTo lastnonblankidx list let list' = listMoveTo lastnonblankidx list
scrollSelectionToMiddle list' scrollSelectionToMiddle list'
continue ui{aScreen=s{rsList=list'}} put ui{aScreen=s{rsList=list'}}
else else
continue ui{aScreen=s{rsList=list}} put ui{aScreen=s{rsList=list}}
-- fall through to the list's event handler (handles other [pg]up/down events) -- fall through to the list's event handler (handles other [pg]up/down events)
VtyEvent ev -> do VtyEvent ev -> do
let ev' = normaliseMovementKeys ev let ev' = normaliseMovementKeys ev
newitems <- handleListEvent ev' rsList newitems <- nestEventM' rsList $ handleListEvent ev'
continue ui{aScreen=s{rsList=newitems}} put ui{aScreen=s{rsList=newitems}}
MouseDown{} -> continue ui MouseDown{} -> put ui
MouseUp{} -> continue ui MouseUp{} -> put ui
AppEvent _ -> continue ui AppEvent _ -> put ui
rsHandle _ _ = error "event handler called with wrong screen type, should not happen" -- PARTIAL:
isBlankElement mel = ((rsItemDate . snd) <$> mel) == Just "" isBlankElement mel = ((rsItemDate . snd) <$> mel) == Just ""
rsCenterAndContinue ui = do rsCenterAndContinue ui = do
scrollSelectionToMiddle $ rsList $ aScreen ui scrollSelectionToMiddle $ rsList $ aScreen ui
continue ui put ui
rsListSize = V.length . V.takeWhile ((/="").rsItemDate) . listElements rsListSize = V.length . V.takeWhile ((/="").rsItemDate) . listElements

View File

@ -65,42 +65,42 @@ select = black `on` selectbg
themesList :: [(String, AttrMap)] themesList :: [(String, AttrMap)]
themesList = [ themesList = [
("default", attrMap (black `on` white) [ ("default", attrMap (black `on` white) [
("border" , white `on` black & dim) (attrName "border" , white `on` black & dim)
,("border" <> "bold" , currentAttr & bold) ,(attrName "border" <> attrName "bold" , currentAttr & bold)
,("border" <> "depth" , active) ,(attrName "border" <> attrName "depth" , active)
,("border" <> "filename" , currentAttr) ,(attrName "border" <> attrName "filename" , currentAttr)
,("border" <> "key" , active) ,(attrName "border" <> attrName "key" , active)
,("border" <> "minibuffer" , white `on` black & bold) ,(attrName "border" <> attrName "minibuffer" , white `on` black & bold)
,("border" <> "query" , active) ,(attrName "border" <> attrName "query" , active)
,("border" <> "selected" , active) ,(attrName "border" <> attrName "selected" , active)
,("error" , fg red) ,(attrName "error" , fg red)
,("help" , white `on` black & dim) ,(attrName "help" , white `on` black & dim)
,("help" <> "heading" , fg yellow) ,(attrName "help" <> attrName "heading" , fg yellow)
,("help" <> "key" , active) ,(attrName "help" <> attrName "key" , active)
-- ,("list" , black `on` white) -- ,(attrName "list" , black `on` white)
-- ,("list" <> "amount" , currentAttr) -- ,(attrName "list" <> attrName "amount" , currentAttr)
,("list" <> "amount" <> "decrease" , fg red) ,(attrName "list" <> attrName "amount" <> attrName "decrease" , fg red)
-- ,("list" <> "amount" <> "increase" , fg green) -- ,(attrName "list" <> attrName "amount" <> attrName "increase" , fg green)
,("list" <> "amount" <> "decrease" <> "selected" , red `on` selectbg & bold) ,(attrName "list" <> attrName "amount" <> attrName "decrease" <> attrName "selected" , red `on` selectbg & bold)
-- ,("list" <> "amount" <> "increase" <> "selected" , green `on` selectbg & bold) -- ,(attrName "list" <> attrName "amount" <> attrName "increase" <> attrName "selected" , green `on` selectbg & bold)
,("list" <> "balance" , currentAttr & bold) ,(attrName "list" <> attrName "balance" , currentAttr & bold)
,("list" <> "balance" <> "negative" , fg red) ,(attrName "list" <> attrName "balance" <> attrName "negative" , fg red)
,("list" <> "balance" <> "positive" , fg black) ,(attrName "list" <> attrName "balance" <> attrName "positive" , fg black)
,("list" <> "balance" <> "negative" <> "selected" , red `on` selectbg & bold) ,(attrName "list" <> attrName "balance" <> attrName "negative" <> attrName "selected" , red `on` selectbg & bold)
,("list" <> "balance" <> "positive" <> "selected" , select & bold) ,(attrName "list" <> attrName "balance" <> attrName "positive" <> attrName "selected" , select & bold)
,("list" <> "selected" , select) ,(attrName "list" <> attrName "selected" , select)
-- ,("list" <> "accounts" , white `on` brightGreen) -- ,(attrName "list" <> attrName "accounts" , white `on` brightGreen)
-- ,("list" <> "selected" , black `on` brightYellow) -- ,(attrName "list" <> attrName "selected" , black `on` brightYellow)
]) ])
,("greenterm", attrMap (green `on` black) [ ,("greenterm", attrMap (green `on` black) [
("list" <> "selected" , black `on` green) (attrName "list" <> attrName "selected" , black `on` green)
]) ])
,("terminal", attrMap defAttr [ ,("terminal", attrMap defAttr [
("border" , white `on` black), (attrName "border" , white `on` black),
("list" , defAttr), (attrName "list" , defAttr),
("list" <> "selected" , defAttr & reverseVideo) (attrName "list" <> attrName "selected" , defAttr & reverseVideo)
]) ])
] ]

View File

@ -107,12 +107,12 @@ tsDraw UIState{aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec
-- <+> str (" ("++show i++" of "++show (length nts)++" in "++acct++")") -- <+> str (" ("++show i++" of "++show (length nts)++" in "++acct++")")
<+> (str $ "#" ++ show (tindex t)) <+> (str $ "#" ++ show (tindex t))
<+> str " (" <+> str " ("
<+> withAttr ("border" <> "bold") (str $ show i) <+> withAttr (attrName "border" <> attrName "bold") (str $ show i)
<+> str (" of "++show (length nts)) <+> str (" of "++show (length nts))
<+> togglefilters <+> togglefilters
<+> borderQueryStr (unwords . map (quoteIfNeeded . T.unpack) $ querystring_ ropts) <+> borderQueryStr (unwords . map (quoteIfNeeded . T.unpack) $ querystring_ ropts)
<+> str (" in "++T.unpack (replaceHiddenAccountsNameWith "All" acct)++")") <+> str (" in "++T.unpack (replaceHiddenAccountsNameWith "All" acct)++")")
<+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr ("border" <> "query") (str " ignoring balance assertions") else str "") <+> (if ignore_assertions_ . balancingopts_ $ inputopts_ copts then withAttr (attrName "border" <> attrName "query") (str " ignoring balance assertions") else str "")
where where
togglefilters = togglefilters =
case concat [ case concat [
@ -121,7 +121,7 @@ tsDraw UIState{aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec
,if empty_ ropts then [] else ["nonzero"] ,if empty_ ropts then [] else ["nonzero"]
] of ] of
[] -> str "" [] -> str ""
fs -> withAttr ("border" <> "query") (str $ " " ++ intercalate ", " fs) fs -> withAttr (attrName "border" <> attrName "query") (str $ " " ++ intercalate ", " fs)
bottomlabel = quickhelp bottomlabel = quickhelp
-- case mode of -- case mode of
@ -141,20 +141,20 @@ tsDraw UIState{aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec
tsDraw _ = error "draw function called with wrong screen type, should not happen" -- PARTIAL: tsDraw _ = error "draw function called with wrong screen type, should not happen" -- PARTIAL:
tsHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) tsHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
tsHandle ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransactions=nts} tsHandle ev = do
ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransactions=nts}
,aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}} ,aopts=UIOpts{uoCliOpts=copts@CliOpts{reportspec_=rspec@ReportSpec{_rsReportOpts=ropts}}}
,ajournal=j ,ajournal=j
,aMode=mode ,aMode=mode
} } <- get
ev =
case mode of case mode of
Help -> Help ->
case ev of case ev of
-- VtyEvent (EvKey (KChar 'q') []) -> halt ui -- VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
_ -> helpHandle ui ev _ -> helpHandle ev
_ -> do _ -> do
let let
@ -162,49 +162,47 @@ tsHandle ui@UIState{aScreen=TransactionScreen{tsTransaction=(i,t), tsTransaction
(iprev,tprev) = maybe (i,t) ((i-1),) $ lookup (i-1) nts (iprev,tprev) = maybe (i,t) ((i-1),) $ lookup (i-1) nts
(inext,tnext) = maybe (i,t) ((i+1),) $ lookup (i+1) nts (inext,tnext) = maybe (i,t) ((i+1),) $ lookup (i+1) nts
case ev of case ev of
VtyEvent (EvKey (KChar 'q') []) -> halt ui VtyEvent (EvKey (KChar 'q') []) -> halt
VtyEvent (EvKey KEsc []) -> continue $ resetScreens d ui VtyEvent (EvKey KEsc []) -> put $ resetScreens d ui
VtyEvent (EvKey (KChar c) []) | c == '?' -> continue $ setMode Help ui VtyEvent (EvKey (KChar c) []) | c == '?' -> put $ setMode Help ui
VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui VtyEvent (EvKey (KChar 'E') []) -> suspendAndResume $ void (runEditor pos f) >> uiReloadJournalIfChanged copts d j ui
where where
(pos,f) = case tsourcepos t of (pos,f) = case tsourcepos t of
(SourcePos f l1 c1,_) -> (Just (unPos l1, Just $ unPos c1),f) (SourcePos f l1 c1,_) -> (Just (unPos l1, Just $ unPos c1),f)
AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old -> AppEvent (DateChange old _) | isStandardPeriod p && p `periodContainsDate` old ->
continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui put $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui
where where
p = reportPeriod ui p = reportPeriod ui
e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> do e | e `elem` [VtyEvent (EvKey (KChar 'g') []), AppEvent FileChange] -> do
-- plog (if e == AppEvent FileChange then "file change" else "manual reload") "" `seq` return () -- plog (if e == AppEvent FileChange then "file change" else "manual reload") "" `seq` return ()
ej <- liftIO . runExceptT $ journalReload copts ej <- liftIO . runExceptT $ journalReload copts
case ej of case ej of
Left err -> continue $ screenEnter d errorScreen{esError=err} ui Left err -> put $ screenEnter d errorScreen{esError=err} ui
Right j' -> continue $ regenerateScreens j' d ui Right j' -> put $ regenerateScreens j' d ui
VtyEvent (EvKey (KChar 'I') []) -> continue $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui) VtyEvent (EvKey (KChar 'I') []) -> put $ uiCheckBalanceAssertions d (toggleIgnoreBalanceAssertions ui)
-- for toggles that may change the current/prev/next transactions, -- for toggles that may change the current/prev/next transactions,
-- we must regenerate the transaction list, like the g handler above ? with regenerateTransactions ? TODO WIP -- we must regenerate the transaction list, like the g handler above ? with regenerateTransactions ? TODO WIP
-- EvKey (KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty ui -- EvKey (KChar 'E') [] -> put $ regenerateScreens j d $ stToggleEmpty ui
-- EvKey (KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared ui -- EvKey (KChar 'C') [] -> put $ regenerateScreens j d $ stToggleCleared ui
-- EvKey (KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal ui -- EvKey (KChar 'R') [] -> put $ regenerateScreens j d $ stToggleReal ui
VtyEvent (EvKey (KChar 'B') []) -> continue . regenerateScreens j d $ toggleConversionOp ui VtyEvent (EvKey (KChar 'B') []) -> put . regenerateScreens j d $ toggleConversionOp ui
VtyEvent (EvKey (KChar 'V') []) -> continue . regenerateScreens j d $ toggleValue ui VtyEvent (EvKey (KChar 'V') []) -> put . regenerateScreens j d $ toggleValue ui
VtyEvent e | e `elem` moveUpEvents -> continue $ tsSelect iprev tprev ui VtyEvent e | e `elem` moveUpEvents -> put $ tsSelect iprev tprev ui
VtyEvent e | e `elem` moveDownEvents -> continue $ tsSelect inext tnext ui VtyEvent e | e `elem` moveDownEvents -> put $ tsSelect inext tnext ui
-- exit screen on LEFT -- exit screen on LEFT
VtyEvent e | e `elem` moveLeftEvents -> continue . popScreen $ tsSelect i t ui -- Probably not necessary to tsSelect here, but it's safe. VtyEvent e | e `elem` moveLeftEvents -> put . popScreen $ tsSelect i t ui -- Probably not necessary to tsSelect here, but it's safe.
-- or on a click in the app's left margin. -- or on a click in the app's left margin.
VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> continue . popScreen $ tsSelect i t ui VtyEvent (EvMouseUp x _y (Just BLeft)) | x==0 -> put . popScreen $ tsSelect i t ui
-- or on clicking the blank area below the transaction. -- or on clicking the blank area below the transaction.
MouseUp _ (Just BLeft) Location{loc=(_,y)} | y+1 > numentrylines -> continue . popScreen $ tsSelect i t ui MouseUp _ (Just BLeft) Location{loc=(_,y)} | y+1 > numentrylines -> put . popScreen $ tsSelect i t ui
where numentrylines = length (T.lines $ showTxn ropts rspec j t) - 1 where numentrylines = length (T.lines $ showTxn ropts rspec j t) - 1
VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> redraw
VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui VtyEvent (EvKey (KChar 'z') [MCtrl]) -> suspend ui
_ -> continue ui _ -> return ()
tsHandle _ _ = error "event handler called with wrong screen type, should not happen" -- PARTIAL:
-- | Select a new transaction and update the previous register screen -- | Select a new transaction and update the previous register screen
tsSelect i t ui@UIState{aScreen=s@TransactionScreen{}} = case aPrevScreens ui of tsSelect i t ui@UIState{aScreen=s@TransactionScreen{}} = case aPrevScreens ui of

View File

@ -33,6 +33,7 @@ Brick.defaultMain brickapp st
{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
@ -100,7 +101,7 @@ data Screen =
AccountsScreen { AccountsScreen {
sInit :: Day -> Bool -> UIState -> UIState -- ^ function to initialise or update this screen's state sInit :: Day -> Bool -> UIState -> UIState -- ^ function to initialise or update this screen's state
,sDraw :: UIState -> [Widget Name] -- ^ brick renderer for this screen ,sDraw :: UIState -> [Widget Name] -- ^ brick renderer for this screen
,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) -- ^ brick event handler for this screen ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState () -- ^ brick event handler for this screen
-- state fields.These ones have lenses: -- state fields.These ones have lenses:
,_asList :: List Name AccountsScreenItem -- ^ list widget showing account names & balances ,_asList :: List Name AccountsScreenItem -- ^ list widget showing account names & balances
,_asSelectedAccount :: AccountName -- ^ a backup of the account name from the list widget's selected item (or "") ,_asSelectedAccount :: AccountName -- ^ a backup of the account name from the list widget's selected item (or "")
@ -108,7 +109,7 @@ data Screen =
| RegisterScreen { | RegisterScreen {
sInit :: Day -> Bool -> UIState -> UIState sInit :: Day -> Bool -> UIState -> UIState
,sDraw :: UIState -> [Widget Name] ,sDraw :: UIState -> [Widget Name]
,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
-- --
,rsList :: List Name RegisterScreenItem -- ^ list widget showing transactions affecting this account ,rsList :: List Name RegisterScreenItem -- ^ list widget showing transactions affecting this account
,rsAccount :: AccountName -- ^ the account this register is for ,rsAccount :: AccountName -- ^ the account this register is for
@ -119,7 +120,7 @@ data Screen =
| TransactionScreen { | TransactionScreen {
sInit :: Day -> Bool -> UIState -> UIState sInit :: Day -> Bool -> UIState -> UIState
,sDraw :: UIState -> [Widget Name] ,sDraw :: UIState -> [Widget Name]
,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
-- --
,tsTransaction :: NumberedTransaction -- ^ the transaction we are currently viewing, and its position in the list ,tsTransaction :: NumberedTransaction -- ^ the transaction we are currently viewing, and its position in the list
,tsTransactions :: [NumberedTransaction] -- ^ list of transactions we can step through ,tsTransactions :: [NumberedTransaction] -- ^ list of transactions we can step through
@ -128,7 +129,7 @@ data Screen =
| ErrorScreen { | ErrorScreen {
sInit :: Day -> Bool -> UIState -> UIState sInit :: Day -> Bool -> UIState -> UIState
,sDraw :: UIState -> [Widget Name] ,sDraw :: UIState -> [Widget Name]
,sHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) ,sHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
-- --
,esError :: String -- ^ error message to show ,esError :: String -- ^ error message to show
} }
@ -154,6 +155,10 @@ data RegisterScreenItem = RegisterScreenItem {
} }
deriving (Show) deriving (Show)
instance MonadFail (EventM Name UIState) where fail _ = wrongScreenTypeError
wrongScreenTypeError = error' "handler called with wrong screen type, should not happen"
type NumberedTransaction = (Integer, Transaction) type NumberedTransaction = (Integer, Transaction)
-- dummy monoid instance needed make lenses work with List fields not common across constructors -- dummy monoid instance needed make lenses work with List fields not common across constructors

View File

@ -35,7 +35,7 @@ import Brick.Widgets.Border
import Brick.Widgets.Border.Style import Brick.Widgets.Border.Style
import Brick.Widgets.Dialog import Brick.Widgets.Dialog
import Brick.Widgets.Edit import Brick.Widgets.Edit
import Brick.Widgets.List (List, listSelectedL, listNameL, listItemHeightL, listSelected, listMoveDown, listMoveUp, GenericList, Splittable) import Brick.Widgets.List (List, listSelectedL, listNameL, listItemHeightL, listSelected, listMoveDown, listMoveUp, GenericList)
import Control.Monad.IO.Class import Control.Monad.IO.Class
import Data.Bifunctor (second) import Data.Bifunctor (second)
import Data.List import Data.List
@ -60,6 +60,7 @@ suspendSignal :: IO ()
suspendSignal = return () suspendSignal = return ()
#else #else
import System.Posix.Signals import System.Posix.Signals
import Data.Vector (Vector)
suspendSignal :: IO () suspendSignal :: IO ()
suspendSignal = raiseSignal sigSTOP suspendSignal = raiseSignal sigSTOP
#endif #endif
@ -68,12 +69,12 @@ suspendSignal = raiseSignal sigSTOP
-- like control-z in bash, returning to the original shell prompt, -- like control-z in bash, returning to the original shell prompt,
-- and when resumed, continue where we left off. -- and when resumed, continue where we left off.
-- On windows, does nothing. -- On windows, does nothing.
suspend :: s -> EventM a (Next s) suspend :: Ord a => s -> EventM a s ()
suspend st = suspendAndResume $ suspendSignal >> return st suspend st = suspendAndResume $ suspendSignal >> return st
-- | Tell vty to redraw the whole screen, and continue. -- | Tell vty to redraw the whole screen.
redraw :: s -> EventM a (Next s) redraw :: EventM a s ()
redraw st = getVtyHandle >>= liftIO . refresh >> continue st redraw = getVtyHandle >>= liftIO . refresh
-- | Wrap a widget in the default hledger-ui screen layout. -- | Wrap a widget in the default hledger-ui screen layout.
defaultLayout :: Widget Name -> Widget Name -> Widget Name -> Widget Name defaultLayout :: Widget Name -> Widget Name -> Widget Name -> Widget Name
@ -90,14 +91,14 @@ helpDialog _copts =
Widget Fixed Fixed $ do Widget Fixed Fixed $ do
c <- getContext c <- getContext
render $ render $
withDefAttr "help" $ withDefAttr (attrName "help") $
renderDialog (dialog (Just "Help (LEFT/ESC/?/q to close help)") Nothing (c^.availWidthL)) $ -- (Just (0,[("ok",())])) renderDialog (dialog (Just "Help (LEFT/ESC/?/q to close help)") Nothing (c^.availWidthL)) $ -- (Just (0,[("ok",())]))
padTop (Pad 0) $ padLeft (Pad 1) $ padRight (Pad 1) $ padTop (Pad 0) $ padLeft (Pad 1) $ padRight (Pad 1) $
vBox [ vBox [
hBox [ hBox [
padRight (Pad 1) $ padRight (Pad 1) $
vBox [ vBox [
withAttr ("help" <> "heading") $ str "Navigation" withAttr (attrName "help" <> attrName "heading") $ str "Navigation"
,renderKey ("UP/DOWN/PUP/PDN/HOME/END/k/j/C-p/C-n", "") ,renderKey ("UP/DOWN/PUP/PDN/HOME/END/k/j/C-p/C-n", "")
,str " move selection up/down" ,str " move selection up/down"
,renderKey ("RIGHT/l/C-f", "show txns, or txn detail") ,renderKey ("RIGHT/l/C-f", "show txns, or txn detail")
@ -105,23 +106,23 @@ helpDialog _copts =
,renderKey ("ESC ", "cancel, or reset app state") ,renderKey ("ESC ", "cancel, or reset app state")
,str " " ,str " "
,withAttr ("help" <> "heading") $ str "Accounts screen" ,withAttr (attrName "help" <> attrName "heading") $ str "Accounts screen"
,renderKey ("1234567890-+ ", "set/adjust depth limit") ,renderKey ("1234567890-+ ", "set/adjust depth limit")
,renderKey ("t ", "toggle accounts tree/list mode") ,renderKey ("t ", "toggle accounts tree/list mode")
,renderKey ("H ", "toggle historical balance/change") ,renderKey ("H ", "toggle historical balance/change")
,str " " ,str " "
,withAttr ("help" <> "heading") $ str "Register screen" ,withAttr (attrName "help" <> attrName "heading") $ str "Register screen"
,renderKey ("t ", "toggle subaccount txns\n(and accounts tree/list mode)") ,renderKey ("t ", "toggle subaccount txns\n(and accounts tree/list mode)")
,renderKey ("H ", "toggle historical/period total") ,renderKey ("H ", "toggle historical/period total")
,str " " ,str " "
,withAttr ("help" <> "heading") $ str "Help" ,withAttr (attrName "help" <> attrName "heading") $ str "Help"
,renderKey ("? ", "toggle this help") ,renderKey ("? ", "toggle this help")
,renderKey ("p/m/i", "while help is open:\nshow manual in pager/man/info") ,renderKey ("p/m/i", "while help is open:\nshow manual in pager/man/info")
,str " " ,str " "
] ]
,padLeft (Pad 1) $ padRight (Pad 0) $ ,padLeft (Pad 1) $ padRight (Pad 0) $
vBox [ vBox [
withAttr ("help" <> "heading") $ str "Filtering" withAttr (attrName "help" <> attrName "heading") $ str "Filtering"
,renderKey ("/ ", "set a filter query") ,renderKey ("/ ", "set a filter query")
,renderKey ("F ", "show future & periodic txns") ,renderKey ("F ", "show future & periodic txns")
,renderKey ("R ", "show real/all postings") ,renderKey ("R ", "show real/all postings")
@ -132,7 +133,7 @@ helpDialog _copts =
,renderKey ("T ", "set period to today") ,renderKey ("T ", "set period to today")
,renderKey ("DEL ", "reset filters") ,renderKey ("DEL ", "reset filters")
,str " " ,str " "
,withAttr ("help" <> "heading") $ str "Other" ,withAttr (attrName "help" <> attrName "heading") $ str "Other"
,renderKey ("a ", "add transaction (hledger add)") ,renderKey ("a ", "add transaction (hledger add)")
,renderKey ("A ", "add transaction (hledger-iadd)") ,renderKey ("A ", "add transaction (hledger-iadd)")
,renderKey ("B ", "show amounts/costs") ,renderKey ("B ", "show amounts/costs")
@ -160,39 +161,40 @@ helpDialog _copts =
-- ] -- ]
] ]
where where
renderKey (key,desc) = withAttr ("help" <> "key") (str key) <+> str " " <+> str desc renderKey (key,desc) = withAttr (attrName "help" <> attrName "key") (str key) <+> str " " <+> str desc
-- | Event handler used when help mode is active. -- | Event handler used when help mode is active.
-- May invoke $PAGER, less, man or info, which is likely to fail on MS Windows, TODO. -- May invoke $PAGER, less, man or info, which is likely to fail on MS Windows, TODO.
helpHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) helpHandle :: BrickEvent Name AppEvent -> EventM Name UIState ()
helpHandle ui ev = do helpHandle ev = do
ui <- get
let ui' = setMode Normal ui
case ev of case ev of
VtyEvent e | e `elem` closeHelpEvents -> continue $ setMode Normal ui VtyEvent e | e `elem` closeHelpEvents -> put ui'
VtyEvent (EvKey (KChar 'p') []) -> suspendAndResume $ runPagerForTopic "hledger-ui" Nothing >> return ui' VtyEvent (EvKey (KChar 'p') []) -> suspendAndResume $ runPagerForTopic "hledger-ui" Nothing >> return ui'
VtyEvent (EvKey (KChar 'm') []) -> suspendAndResume $ runManForTopic "hledger-ui" Nothing >> return ui' VtyEvent (EvKey (KChar 'm') []) -> suspendAndResume $ runManForTopic "hledger-ui" Nothing >> return ui'
VtyEvent (EvKey (KChar 'i') []) -> suspendAndResume $ runInfoForTopic "hledger-ui" Nothing >> return ui' VtyEvent (EvKey (KChar 'i') []) -> suspendAndResume $ runInfoForTopic "hledger-ui" Nothing >> return ui'
_ -> continue ui _ -> return ()
where where
ui' = setMode Normal ui
closeHelpEvents = moveLeftEvents ++ [EvKey KEsc [], EvKey (KChar '?') [], EvKey (KChar 'q') []] closeHelpEvents = moveLeftEvents ++ [EvKey KEsc [], EvKey (KChar '?') [], EvKey (KChar 'q') []]
-- | Draw the minibuffer with the given label. -- | Draw the minibuffer with the given label.
minibuffer :: T.Text -> Editor String Name -> Widget Name minibuffer :: T.Text -> Editor String Name -> Widget Name
minibuffer string ed = minibuffer string ed =
forceAttr ("border" <> "minibuffer") $ forceAttr (attrName "border" <> attrName "minibuffer") $
hBox [txt $ string <> ": ", renderEditor (str . unlines) True ed] hBox [txt $ string <> ": ", renderEditor (str . unlines) True ed]
borderQueryStr :: String -> Widget Name borderQueryStr :: String -> Widget Name
borderQueryStr "" = str "" borderQueryStr "" = str ""
borderQueryStr qry = str " matching " <+> withAttr ("border" <> "query") (str qry) borderQueryStr qry = str " matching " <+> withAttr (attrName "border" <> attrName "query") (str qry)
borderDepthStr :: Maybe Int -> Widget Name borderDepthStr :: Maybe Int -> Widget Name
borderDepthStr Nothing = str "" borderDepthStr Nothing = str ""
borderDepthStr (Just d) = str " to depth " <+> withAttr ("border" <> "query") (str $ show d) borderDepthStr (Just d) = str " to depth " <+> withAttr (attrName "border" <> attrName "query") (str $ show d)
borderPeriodStr :: String -> Period -> Widget Name borderPeriodStr :: String -> Period -> Widget Name
borderPeriodStr _ PeriodAll = str "" borderPeriodStr _ PeriodAll = str ""
borderPeriodStr preposition p = str (" "++preposition++" ") <+> withAttr ("border" <> "query") (str . T.unpack $ showPeriod p) borderPeriodStr preposition p = str (" "++preposition++" ") <+> withAttr (attrName "border" <> attrName "query") (str . T.unpack $ showPeriod p)
borderKeysStr :: [(String,String)] -> Widget Name borderKeysStr :: [(String,String)] -> Widget Name
borderKeysStr = borderKeysStr' . map (second str) borderKeysStr = borderKeysStr' . map (second str)
@ -201,7 +203,7 @@ borderKeysStr' :: [(String,Widget Name)] -> Widget Name
borderKeysStr' keydescs = borderKeysStr' keydescs =
hBox $ hBox $
intersperse sep $ intersperse sep $
[withAttr ("border" <> "key") (str keys) <+> str ":" <+> desc | (keys, desc) <- keydescs] [withAttr (attrName "border" <> attrName "key") (str keys) <+> str ":" <+> desc | (keys, desc) <- keydescs]
where where
-- sep = str " | " -- sep = str " | "
sep = str " " sep = str " "
@ -209,7 +211,7 @@ borderKeysStr' keydescs =
-- | Show both states of a toggle ("aaa/bbb"), highlighting the active one. -- | Show both states of a toggle ("aaa/bbb"), highlighting the active one.
renderToggle :: Bool -> String -> String -> Widget Name renderToggle :: Bool -> String -> String -> Widget Name
renderToggle isright l r = renderToggle isright l r =
let bold = withAttr ("border" <> "selected") in let bold = withAttr (attrName "border" <> attrName "selected") in
if isright if isright
then str (l++"/") <+> bold (str r) then str (l++"/") <+> bold (str r)
else bold (str l) <+> str ("/"++r) else bold (str l) <+> str ("/"++r)
@ -217,7 +219,7 @@ renderToggle isright l r =
-- | Show a toggle's label, highlighted (bold) when the toggle is active. -- | Show a toggle's label, highlighted (bold) when the toggle is active.
renderToggle1 :: Bool -> String -> Widget Name renderToggle1 :: Bool -> String -> Widget Name
renderToggle1 isactive l = renderToggle1 isactive l =
let bold = withAttr ("border" <> "selected") in let bold = withAttr (attrName "border" <> attrName "selected") in
if isactive if isactive
then bold (str l) then bold (str l)
else str l else str l
@ -262,11 +264,11 @@ topBottomBorderWithLabels toplabel bottomlabel body =
"" ""
-- " debug: "++show (_w,h') -- " debug: "++show (_w,h')
render $ render $
hBorderWithLabel (withAttr "border" $ toplabel <+> str debugmsg) hBorderWithLabel (withAttr (attrName "border") $ toplabel <+> str debugmsg)
<=> <=>
body' body'
<=> <=>
hBorderWithLabel (withAttr "border" bottomlabel) hBorderWithLabel (withAttr (attrName "border") bottomlabel)
---- XXX should be equivalent to the above, but isn't (page down goes offscreen) ---- XXX should be equivalent to the above, but isn't (page down goes offscreen)
--_topBottomBorderWithLabel2 :: Widget Name -> Widget Name -> Widget Name --_topBottomBorderWithLabel2 :: Widget Name -> Widget Name -> Widget Name
@ -303,7 +305,7 @@ margin h v mcolour w = Widget Greedy Greedy $ do
-- applyN n border -- applyN n border
withBorderAttr :: Attr -> Widget Name -> Widget Name withBorderAttr :: Attr -> Widget Name -> Widget Name
withBorderAttr attr = updateAttrMap (applyAttrMappings [("border", attr)]) withBorderAttr attr = updateAttrMap (applyAttrMappings [(attrName "border", attr)])
---- | Like brick's continue, but first run some action to modify brick's state. ---- | Like brick's continue, but first run some action to modify brick's state.
---- This action does not affect the app state, but might eg adjust a widget's scroll position. ---- This action does not affect the app state, but might eg adjust a widget's scroll position.
@ -319,7 +321,7 @@ withBorderAttr attr = updateAttrMap (applyAttrMappings [("border", attr)])
-- | Scroll a list's viewport so that the selected item is centered in the -- | Scroll a list's viewport so that the selected item is centered in the
-- middle of the display area. -- middle of the display area.
scrollSelectionToMiddle :: List Name e -> EventM Name () scrollSelectionToMiddle :: List Name item -> EventM Name UIState ()
scrollSelectionToMiddle list = do scrollSelectionToMiddle list = do
case list^.listSelectedL of case list^.listSelectedL of
Nothing -> return () Nothing -> return ()
@ -364,9 +366,9 @@ reportSpecSetFutureAndForecast d forecast rspec =
-- Vertically scroll the named list's viewport with the given number of non-empty items -- Vertically scroll the named list's viewport with the given number of non-empty items
-- by the given positive or negative number of items (usually 1 or -1). -- by the given positive or negative number of items (usually 1 or -1).
-- The selection will be moved when necessary to keep it visible and allow the scroll. -- The selection will be moved when necessary to keep it visible and allow the scroll.
listScrollPushingSelection :: (Ord n, Foldable t, Splittable t) => listScrollPushingSelection :: Name -> Int -> Int -> EventM Name (List Name item) (GenericList Name Vector item)
n -> GenericList n t e -> Int -> Int -> EventM n (GenericList n t e) listScrollPushingSelection name listheight scrollamt = do
listScrollPushingSelection name list listheight scrollamt = do list <- get
viewportScroll name `vScrollBy` scrollamt viewportScroll name `vScrollBy` scrollamt
mvp <- lookupViewport name mvp <- lookupViewport name
case mvp of case mvp of

View File

@ -68,7 +68,7 @@ executable hledger-ui
ansi-terminal >=0.9 ansi-terminal >=0.9
, async , async
, base >=4.11 && <4.17 , base >=4.11 && <4.17
, brick >=0.23 , brick >=1.0
, cmdargs >=0.8 , cmdargs >=0.8
, containers >=0.5.9 , containers >=0.5.9
, data-default , data-default

View File

@ -76,7 +76,7 @@ dependencies:
- transformers - transformers
- vector - vector
# not installable on windows, cf buildable flag below # not installable on windows, cf buildable flag below
- brick >=0.23 && <1 - brick >=1.0
- vty >=5.15 - vty >=5.15
- unix - unix

View File

@ -1,6 +1,6 @@
# stack build plan using GHC 9.2.4 # stack build plan using GHC 9.2.4
resolver: nightly-2022-08-04 resolver: nightly-2022-08-14
packages: packages:
- hledger-lib - hledger-lib
@ -8,10 +8,11 @@ packages:
- hledger-ui - hledger-ui
- hledger-web - hledger-web
# extra-deps: extra-deps:
# for hledger-lib: # for hledger-lib:
# for hledger: # for hledger:
# for hledger-ui: # for hledger-ui:
- brick-1.0
# for hledger-web: # for hledger-web:
# for Shake.hs: # for Shake.hs: