ui: convert screen states to records
This is highly verbose, but potentially clearer, allows better haddock docs, and hopefully will improve after lensification.
This commit is contained in:
parent
5259605e82
commit
5c343a5848
@ -41,36 +41,38 @@ import Hledger.UI.ErrorScreen
|
||||
|
||||
accountsScreen :: Screen
|
||||
accountsScreen = AccountsScreen{
|
||||
asState = (list "accounts" V.empty 1, "")
|
||||
asState = AccountsScreenState{asItems=list "accounts" V.empty 1
|
||||
,asSelectedAccount=""
|
||||
}
|
||||
,sInitFn = initAccountsScreen
|
||||
,sDrawFn = drawAccountsScreen
|
||||
,sHandleFn = handleAccountsScreen
|
||||
}
|
||||
|
||||
asSetSelectedAccount a scr@AccountsScreen{asState=(l,_)} = scr{asState=(l,a)}
|
||||
asSetSelectedAccount a scr@AccountsScreen{asState=st} = scr{asState=st{asSelectedAccount=a}}
|
||||
asSetSelectedAccount _ scr = scr
|
||||
|
||||
initAccountsScreen :: Day -> Bool -> AppState -> AppState
|
||||
initAccountsScreen d reset st@AppState{
|
||||
aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}},
|
||||
ajournal=j,
|
||||
aScreen=s@AccountsScreen{asState=(oldl,selacct)}
|
||||
aScreen=s@AccountsScreen{asState=asState@AccountsScreenState{..}}
|
||||
} =
|
||||
st{aopts=uopts', aScreen=s{asState=(newl',selacct)}}
|
||||
st{aopts=uopts', aScreen=s{asState=asState{asItems=newitems'}}}
|
||||
where
|
||||
newl = list (Name "accounts") (V.fromList displayitems) 1
|
||||
newitems = list (Name "accounts") (V.fromList displayitems) 1
|
||||
|
||||
-- keep the selection near the last selected account
|
||||
-- (may need to move to the next leaf account when entering flat mode)
|
||||
newl' = listMoveTo selidx newl
|
||||
newitems' = listMoveTo selidx newitems
|
||||
where
|
||||
selidx = case (reset, listSelectedElement oldl) of
|
||||
selidx = case (reset, listSelectedElement asItems) of
|
||||
(True, _) -> 0
|
||||
(_, Nothing) -> 0
|
||||
(_, Just (_,(_,a,_,_))) -> fromMaybe (fromMaybe 0 mprefixmatch) mexactmatch
|
||||
(_, Just (_,AccountsScreenItem{asItemAccountName=a})) -> fromMaybe (fromMaybe 0 mprefixmatch) mexactmatch
|
||||
where
|
||||
mexactmatch = findIndex ((a ==) . second4) displayitems
|
||||
mprefixmatch = findIndex ((a `isAccountNamePrefixOf`) . second4) displayitems
|
||||
mexactmatch = findIndex ((a ==) . asItemAccountName) displayitems
|
||||
mprefixmatch = findIndex ((a `isAccountNamePrefixOf`) . asItemAccountName) displayitems
|
||||
uopts' = uopts{cliopts_=copts{reportopts_=ropts'}}
|
||||
ropts' = ropts {
|
||||
-- XXX balanceReport doesn't respect this yet
|
||||
@ -90,11 +92,11 @@ initAccountsScreen d reset st@AppState{
|
||||
|
||||
-- pre-render the list items
|
||||
displayitem ((fullacct, shortacct, indent), bal) =
|
||||
(indent
|
||||
,fullacct
|
||||
,if flat_ ropts' then fullacct else shortacct
|
||||
,map showAmountWithoutPrice amts -- like showMixedAmountOneLineWithoutPrice
|
||||
)
|
||||
AccountsScreenItem{asItemIndentLevel = indent
|
||||
,asItemAccountName = fullacct
|
||||
,asItemDisplayAccountName = if flat_ ropts' then fullacct else shortacct
|
||||
,asItemRenderedAmounts = map showAmountWithoutPrice amts -- like showMixedAmountOneLineWithoutPrice
|
||||
}
|
||||
where
|
||||
Mixed amts = normaliseMixedAmountSquashPricesForDisplay $ stripPrices bal
|
||||
stripPrices (Mixed as) = Mixed $ map stripprice as where stripprice a = a{aprice=NoPrice}
|
||||
@ -106,7 +108,7 @@ initAccountsScreen _ _ _ = error "init function called with wrong screen type, s
|
||||
drawAccountsScreen :: AppState -> [Widget]
|
||||
drawAccountsScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
,ajournal=j
|
||||
,aScreen=AccountsScreen{asState=(l,_)}
|
||||
,aScreen=AccountsScreen{asState=AccountsScreenState{..}}
|
||||
,aMinibuffer=mbuf} =
|
||||
[ui]
|
||||
where
|
||||
@ -139,10 +141,10 @@ drawAccountsScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
fs -> str " with " <+> withAttr (borderAttr <> "query") (str $ intercalate ", " fs) <+> str " txns"
|
||||
nonzero | empty_ ropts = str ""
|
||||
| otherwise = withAttr (borderAttr <> "query") (str " nonzero")
|
||||
cur = str (case l^.listSelectedL of
|
||||
cur = str (case asItems ^. listSelectedL of
|
||||
Nothing -> "-"
|
||||
Just i -> show (i + 1))
|
||||
total = str $ show $ V.length $ l^.listElementsL
|
||||
total = str $ show $ V.length $ asItems ^. listElementsL
|
||||
|
||||
bottomlabel = borderKeysStr [
|
||||
-- ("up/down/pgup/pgdown/home/end", "move")
|
||||
@ -171,16 +173,16 @@ drawAccountsScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
-- ltrace "availwidth" $
|
||||
c^.availWidthL
|
||||
- 2 -- XXX due to margin ? shouldn't be necessary (cf UIUtils)
|
||||
displayitems = listElements l
|
||||
displayitems = listElements asItems
|
||||
maxacctwidthseen =
|
||||
-- ltrace "maxacctwidthseen" $
|
||||
V.maximum $
|
||||
V.map (\(indent,_,displayacct,_) -> indent*2 + textWidth displayacct) $
|
||||
V.map (\AccountsScreenItem{..} -> asItemIndentLevel*2 + textWidth asItemDisplayAccountName) $
|
||||
-- V.filter (\(indent,_,_,_) -> (indent-1) <= fromMaybe 99999 mdepth) $
|
||||
displayitems
|
||||
maxbalwidthseen =
|
||||
-- ltrace "maxbalwidthseen" $
|
||||
V.maximum $ V.map (\(_,_,_,amts) -> sum (map strWidth amts) + 2 * (length amts-1)) displayitems
|
||||
V.maximum $ V.map (\AccountsScreenItem{..} -> sum (map strWidth asItemRenderedAmounts) + 2 * (length asItemRenderedAmounts - 1)) displayitems
|
||||
maxbalwidth =
|
||||
-- ltrace "maxbalwidth" $
|
||||
max 0 (availwidth - 2 - 4) -- leave 2 whitespace plus least 4 for accts
|
||||
@ -199,20 +201,20 @@ drawAccountsScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
|
||||
colwidths = (acctwidth, balwidth)
|
||||
|
||||
render $ defaultLayout toplabel bottomarea $ renderList l (drawAccountsItem colwidths)
|
||||
render $ defaultLayout toplabel bottomarea $ renderList asItems (drawAccountsItem colwidths)
|
||||
|
||||
drawAccountsScreen _ = error "draw function called with wrong screen type, should not happen"
|
||||
|
||||
drawAccountsItem :: (Int,Int) -> Bool -> (Int, AccountName, AccountName, [String]) -> Widget
|
||||
drawAccountsItem (acctwidth, balwidth) selected (indent, _fullacct, displayacct, balamts) =
|
||||
drawAccountsItem :: (Int,Int) -> Bool -> AccountsScreenItem -> Widget
|
||||
drawAccountsItem (acctwidth, balwidth) selected AccountsScreenItem{..} =
|
||||
Widget Greedy Fixed $ do
|
||||
-- c <- getContext
|
||||
-- let showitem = intercalate "\n" . balanceReportItemAsText defreportopts fmt
|
||||
render $
|
||||
addamts balamts $
|
||||
str (T.unpack $ fitText (Just acctwidth) (Just acctwidth) True True $ T.replicate (2*indent) " " <> displayacct) <+>
|
||||
addamts asItemRenderedAmounts $
|
||||
str (T.unpack $ fitText (Just acctwidth) (Just acctwidth) True True $ T.replicate (2*asItemIndentLevel) " " <> asItemDisplayAccountName) <+>
|
||||
str " " <+>
|
||||
str (balspace balamts)
|
||||
str (balspace asItemRenderedAmounts)
|
||||
where
|
||||
balspace as = replicate n ' '
|
||||
where n = max 0 (balwidth - (sum (map strWidth as) + 2 * (length as - 1)))
|
||||
@ -232,7 +234,7 @@ drawAccountsItem (acctwidth, balwidth) selected (indent, _fullacct, displayacct,
|
||||
|
||||
handleAccountsScreen :: AppState -> Vty.Event -> EventM (Next AppState)
|
||||
handleAccountsScreen st@AppState{
|
||||
aScreen=scr@AccountsScreen{asState=(l,selacct)}
|
||||
aScreen=scr@AccountsScreen{asState=asState@AccountsScreenState{..}}
|
||||
,aopts=UIOpts{cliopts_=copts}
|
||||
,ajournal=j
|
||||
,aMinibuffer=mbuf
|
||||
@ -245,10 +247,10 @@ handleAccountsScreen st@AppState{
|
||||
-- before we go anywhere, remember the currently selected account.
|
||||
-- (This is preserved across screen changes, unlike List's selection state)
|
||||
let
|
||||
selacct' = case listSelectedElement l of
|
||||
Just (_, (_, fullacct, _, _)) -> fullacct
|
||||
Nothing -> selacct
|
||||
st' = st{aScreen=scr{asState=(l,selacct')}}
|
||||
selacct = case listSelectedElement asItems of
|
||||
Just (_, AccountsScreenItem{..}) -> asItemAccountName
|
||||
Nothing -> asSelectedAccount
|
||||
st' = st{aScreen=scr{asState=asState{asSelectedAccount=selacct}}}
|
||||
|
||||
case mbuf of
|
||||
Nothing ->
|
||||
@ -281,15 +283,15 @@ handleAccountsScreen st@AppState{
|
||||
Vty.EvKey (Vty.KLeft) [] -> continue $ popScreen st'
|
||||
Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do
|
||||
let
|
||||
scr = rsSetCurrentAccount selacct' registerScreen
|
||||
scr = rsSetCurrentAccount selacct registerScreen
|
||||
st'' = screenEnter d scr st'
|
||||
scrollTopRegister
|
||||
continue st''
|
||||
|
||||
-- fall through to the list's event handler (handles up/down)
|
||||
ev -> do
|
||||
l' <- handleEvent ev l
|
||||
continue $ st'{aScreen=scr{asState=(l',selacct')}}
|
||||
newitems <- handleEvent ev asItems
|
||||
continue $ st'{aScreen=scr{asState=asState{asItems=newitems,asSelectedAccount=selacct}}}
|
||||
-- continue =<< handleEventLensed st' someLens ev
|
||||
|
||||
Just ed ->
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- The error screen, showing a current error condition (such as a parse error after reloading the journal)
|
||||
|
||||
{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
|
||||
{-# LANGUAGE OverloadedStrings, FlexibleContexts, RecordWildCards #-}
|
||||
|
||||
module Hledger.UI.ErrorScreen
|
||||
(errorScreen
|
||||
@ -30,7 +30,7 @@ import Hledger.UI.UIUtils
|
||||
|
||||
errorScreen :: Screen
|
||||
errorScreen = ErrorScreen{
|
||||
esState = ""
|
||||
esState = ErrorScreenState{esError=""}
|
||||
,sInitFn = initErrorScreen
|
||||
,sDrawFn = drawErrorScreen
|
||||
,sHandleFn = handleErrorScreen
|
||||
@ -42,7 +42,7 @@ initErrorScreen _ _ _ = error "init function called with wrong screen type, shou
|
||||
|
||||
drawErrorScreen :: AppState -> [Widget]
|
||||
drawErrorScreen AppState{ -- aopts=_uopts@UIOpts{cliopts_=_copts@CliOpts{reportopts_=_ropts@ReportOpts{query_=querystr}}},
|
||||
aScreen=ErrorScreen{esState=err}} = [ui]
|
||||
aScreen=ErrorScreen{esState=ErrorScreenState{..}}} = [ui]
|
||||
where
|
||||
toplabel = withAttr ("border" <> "bold") (str "Oops. Please fix this problem then press g to reload")
|
||||
-- <+> str " transactions"
|
||||
@ -75,7 +75,7 @@ drawErrorScreen AppState{ -- aopts=_uopts@UIOpts{cliopts_=_copts@CliOpts{reporto
|
||||
-- totalwidth = c^.availWidthL
|
||||
-- - 2 -- XXX due to margin ? shouldn't be necessary (cf UIUtils)
|
||||
|
||||
render $ defaultLayout toplabel bottomlabel $ withAttr "error" $ str err
|
||||
render $ defaultLayout toplabel bottomlabel $ withAttr "error" $ str $ esError
|
||||
|
||||
drawErrorScreen _ = error "draw function called with wrong screen type, should not happen"
|
||||
|
||||
@ -114,7 +114,7 @@ handleErrorScreen st@AppState{
|
||||
Vty.EvKey (Vty.KChar 'g') [] -> do
|
||||
(ej, _) <- liftIO $ journalReloadIfChanged copts d j
|
||||
case ej of
|
||||
Left err -> continue st{aScreen=s{esState=err}} -- show latest parse error
|
||||
Left err -> continue st{aScreen=s{esState=ErrorScreenState{esError=err}}} -- show latest parse error
|
||||
Right j' -> continue $ regenerateScreens j' d $ popScreen st -- return to previous screen, and reload it
|
||||
|
||||
-- Vty.EvKey (Vty.KLeft) [] -> continue $ popScreen st
|
||||
@ -133,5 +133,5 @@ stReloadJournalIfChanged copts d j st = do
|
||||
(ej, _) <- journalReloadIfChanged copts d j
|
||||
return $ case ej of
|
||||
Right j' -> regenerateScreens j' d st
|
||||
Left err -> screenEnter d errorScreen{esState=err} st
|
||||
Left err -> screenEnter d errorScreen{esState=ErrorScreenState{esError=err}} st
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- The account register screen, showing transactions in an account, like hledger-web's register.
|
||||
|
||||
{-# LANGUAGE OverloadedStrings, FlexibleContexts #-}
|
||||
{-# LANGUAGE OverloadedStrings, FlexibleContexts, RecordWildCards #-}
|
||||
|
||||
module Hledger.UI.RegisterScreen
|
||||
(registerScreen
|
||||
@ -37,18 +37,20 @@ import Hledger.UI.ErrorScreen
|
||||
|
||||
registerScreen :: Screen
|
||||
registerScreen = RegisterScreen{
|
||||
rsState = (list "register" V.empty 1, "")
|
||||
rsState = RegisterScreenState{rsItems=list "register" V.empty 1
|
||||
,rsSelectedAccount=""
|
||||
}
|
||||
,sInitFn = initRegisterScreen
|
||||
,sDrawFn = drawRegisterScreen
|
||||
,sHandleFn = handleRegisterScreen
|
||||
}
|
||||
|
||||
rsSetCurrentAccount a scr@RegisterScreen{rsState=(l,_)} = scr{rsState=(l,a)}
|
||||
rsSetCurrentAccount a scr@RegisterScreen{..} = scr{rsState=rsState{rsSelectedAccount=a}}
|
||||
rsSetCurrentAccount _ scr = scr
|
||||
|
||||
initRegisterScreen :: Day -> Bool -> AppState -> AppState
|
||||
initRegisterScreen d reset st@AppState{aopts=opts, ajournal=j, aScreen=s@RegisterScreen{rsState=(oldl,acct)}} =
|
||||
st{aScreen=s{rsState=(newl',acct)}}
|
||||
initRegisterScreen d reset st@AppState{aopts=opts, ajournal=j, aScreen=s@RegisterScreen{rsState=rsState@RegisterScreenState{..}}} =
|
||||
st{aScreen=s{rsState=rsState{rsItems=newitems'}}}
|
||||
where
|
||||
-- gather arguments and queries
|
||||
ropts = (reportopts_ $ cliopts_ opts)
|
||||
@ -57,7 +59,7 @@ initRegisterScreen d reset st@AppState{aopts=opts, ajournal=j, aScreen=s@Registe
|
||||
balancetype_=HistoricalBalance
|
||||
}
|
||||
-- XXX temp
|
||||
thisacctq = Acct $ accountNameToAccountRegex acct -- includes subs
|
||||
thisacctq = Acct $ accountNameToAccountRegex rsSelectedAccount -- includes subs
|
||||
q = filterQuery (not . queryIsDepth) $ queryFromOpts d ropts
|
||||
|
||||
(_label,items) = accountTransactionsReport ropts j q thisacctq
|
||||
@ -65,43 +67,44 @@ initRegisterScreen d reset st@AppState{aopts=opts, ajournal=j, aScreen=s@Registe
|
||||
reverse -- most recent last
|
||||
items
|
||||
|
||||
-- pre-render all items; these will be the List elements. This helps calculate column widths.
|
||||
-- generate pre-rendered list items. This helps calculate column widths.
|
||||
displayitems = map displayitem items'
|
||||
where
|
||||
displayitem (t, _, _issplit, otheracctsstr, change, bal) =
|
||||
(showDate $ tdate t
|
||||
,T.unpack $ tdescription t
|
||||
,case splitOn ", " otheracctsstr of
|
||||
[s] -> s
|
||||
ss -> intercalate ", " ss
|
||||
-- _ -> "<split>" -- should do this if accounts field width < 30
|
||||
,showMixedAmountOneLineWithoutPrice change
|
||||
,showMixedAmountOneLineWithoutPrice bal
|
||||
,t
|
||||
)
|
||||
RegisterScreenItem{rsItemDate = showDate $ tdate t
|
||||
,rsItemDescription = T.unpack $ tdescription t
|
||||
,rsItemOtherAccounts = case splitOn ", " otheracctsstr of
|
||||
[s] -> s
|
||||
ss -> intercalate ", " ss
|
||||
-- _ -> "<split>" -- should do this if accounts field width < 30
|
||||
,rsItemChangeAmount = showMixedAmountOneLineWithoutPrice change
|
||||
,rsItemBalanceAmount = showMixedAmountOneLineWithoutPrice bal
|
||||
,rsItemTransaction = t
|
||||
}
|
||||
|
||||
-- build the List
|
||||
newl = list (Name "register") (V.fromList displayitems) 1
|
||||
newitems = list (Name "register") (V.fromList displayitems) 1
|
||||
|
||||
-- keep the selection on the previously selected transaction if possible,
|
||||
-- (eg after toggling nonzero mode), otherwise select the last element.
|
||||
newl' = listMoveTo newselidx newl
|
||||
newitems' = listMoveTo newselidx newitems
|
||||
where
|
||||
newselidx = case (reset, listSelectedElement oldl) of
|
||||
(True, _) -> 0
|
||||
(_, Nothing) -> endidx
|
||||
(_, Just (_,(_,_,_,_,_,Transaction{tindex=ti}))) -> fromMaybe endidx $ findIndex ((==ti) . tindex . sixth6) displayitems
|
||||
newselidx = case (reset, listSelectedElement rsItems) of
|
||||
(True, _) -> 0
|
||||
(_, Nothing) -> endidx
|
||||
(_, Just (_,RegisterScreenItem{rsItemTransaction=Transaction{tindex=ti}}))
|
||||
-> fromMaybe endidx $ findIndex ((==ti) . tindex . rsItemTransaction) displayitems
|
||||
endidx = length displayitems
|
||||
|
||||
initRegisterScreen _ _ _ = error "init function called with wrong screen type, should not happen"
|
||||
|
||||
drawRegisterScreen :: AppState -> [Widget]
|
||||
drawRegisterScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
,aScreen=RegisterScreen{rsState=(l,acct)}
|
||||
,aScreen=RegisterScreen{rsState=RegisterScreenState{..}}
|
||||
,aMinibuffer=mbuf}
|
||||
= [ui]
|
||||
where
|
||||
toplabel = withAttr ("border" <> "bold") (str $ T.unpack acct)
|
||||
toplabel = withAttr ("border" <> "bold") (str $ T.unpack rsSelectedAccount)
|
||||
<+> togglefilters
|
||||
<+> str " transactions"
|
||||
<+> borderQueryStr (query_ ropts)
|
||||
@ -121,11 +124,11 @@ drawRegisterScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
] of
|
||||
[] -> str ""
|
||||
fs -> withAttr (borderAttr <> "query") (str $ " " ++ intercalate ", " fs)
|
||||
cur = str $ case l^.listSelectedL of
|
||||
cur = str $ case rsItems ^. listSelectedL of
|
||||
Nothing -> "-"
|
||||
Just i -> show (i + 1)
|
||||
total = str $ show $ length displayitems
|
||||
displayitems = V.toList $ l^.listElementsL
|
||||
displayitems = V.toList $ rsItems ^. listElementsL
|
||||
|
||||
-- query = query_ $ reportopts_ $ cliopts_ opts
|
||||
|
||||
@ -148,8 +151,8 @@ drawRegisterScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
whitespacewidth = 10 -- inter-column whitespace, fixed width
|
||||
minnonamtcolswidth = datewidth + 2 + 2 -- date column plus at least 2 for desc and accts
|
||||
maxamtswidth = max 0 (totalwidth - minnonamtcolswidth - whitespacewidth)
|
||||
maxchangewidthseen = maximum' $ map (strWidth . fourth6) displayitems
|
||||
maxbalwidthseen = maximum' $ map (strWidth . fifth6) displayitems
|
||||
maxchangewidthseen = maximum' $ map (strWidth . rsItemChangeAmount) displayitems
|
||||
maxbalwidthseen = maximum' $ map (strWidth . rsItemBalanceAmount) displayitems
|
||||
changewidthproportion = fromIntegral maxchangewidthseen / fromIntegral (maxchangewidthseen + maxbalwidthseen)
|
||||
maxchangewidth = round $ changewidthproportion * fromIntegral maxamtswidth
|
||||
maxbalwidth = maxamtswidth - maxchangewidth
|
||||
@ -193,34 +196,34 @@ drawRegisterScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
Nothing -> bottomlabel
|
||||
Just ed -> minibuffer ed
|
||||
|
||||
render $ defaultLayout toplabel bottomarea $ renderList l (drawRegisterItem colwidths)
|
||||
render $ defaultLayout toplabel bottomarea $ renderList rsItems (drawRegisterItem colwidths)
|
||||
|
||||
drawRegisterScreen _ = error "draw function called with wrong screen type, should not happen"
|
||||
|
||||
drawRegisterItem :: (Int,Int,Int,Int,Int) -> Bool -> (String,String,String,String,String,Transaction) -> Widget
|
||||
drawRegisterItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected (date,desc,accts,change,bal,_) =
|
||||
drawRegisterItem :: (Int,Int,Int,Int,Int) -> Bool -> RegisterScreenItem -> Widget
|
||||
drawRegisterItem (datewidth,descwidth,acctswidth,changewidth,balwidth) selected RegisterScreenItem{..} =
|
||||
Widget Greedy Fixed $ do
|
||||
render $
|
||||
str (fitString (Just datewidth) (Just datewidth) True True date) <+>
|
||||
str (fitString (Just datewidth) (Just datewidth) True True rsItemDate) <+>
|
||||
str " " <+>
|
||||
str (fitString (Just descwidth) (Just descwidth) True True desc) <+>
|
||||
str (fitString (Just descwidth) (Just descwidth) True True rsItemDescription) <+>
|
||||
str " " <+>
|
||||
str (fitString (Just acctswidth) (Just acctswidth) True True accts) <+>
|
||||
str (fitString (Just acctswidth) (Just acctswidth) True True rsItemOtherAccounts) <+>
|
||||
str " " <+>
|
||||
withAttr changeattr (str (fitString (Just changewidth) (Just changewidth) True False change)) <+>
|
||||
withAttr changeattr (str (fitString (Just changewidth) (Just changewidth) True False rsItemChangeAmount)) <+>
|
||||
str " " <+>
|
||||
withAttr balattr (str (fitString (Just balwidth) (Just balwidth) True False bal))
|
||||
withAttr balattr (str (fitString (Just balwidth) (Just balwidth) True False rsItemBalanceAmount))
|
||||
where
|
||||
changeattr | '-' `elem` change = sel $ "list" <> "amount" <> "decrease"
|
||||
| otherwise = sel $ "list" <> "amount" <> "increase"
|
||||
balattr | '-' `elem` bal = sel $ "list" <> "balance" <> "negative"
|
||||
| otherwise = sel $ "list" <> "balance" <> "positive"
|
||||
changeattr | '-' `elem` rsItemChangeAmount = sel $ "list" <> "amount" <> "decrease"
|
||||
| otherwise = sel $ "list" <> "amount" <> "increase"
|
||||
balattr | '-' `elem` rsItemBalanceAmount = sel $ "list" <> "balance" <> "negative"
|
||||
| otherwise = sel $ "list" <> "balance" <> "positive"
|
||||
sel | selected = (<> "selected")
|
||||
| otherwise = id
|
||||
|
||||
handleRegisterScreen :: AppState -> Vty.Event -> EventM (Next AppState)
|
||||
handleRegisterScreen st@AppState{
|
||||
aScreen=s@RegisterScreen{rsState=(l,acct)}
|
||||
aScreen=s@RegisterScreen{rsState=rsState@RegisterScreenState{..}}
|
||||
,aopts=UIOpts{cliopts_=copts}
|
||||
,ajournal=j
|
||||
,aMinibuffer=mbuf
|
||||
@ -242,20 +245,22 @@ handleRegisterScreen st@AppState{
|
||||
Vty.EvKey (Vty.KLeft) [] -> continue $ popScreen st
|
||||
|
||||
Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do
|
||||
case listSelectedElement l of
|
||||
Just (_, (_, _, _, _, _, t)) ->
|
||||
case listSelectedElement rsItems of
|
||||
Just (_, RegisterScreenItem{rsItemTransaction=t}) ->
|
||||
let
|
||||
ts = map sixth6 $ V.toList $ listElements l
|
||||
ts = map rsItemTransaction $ V.toList $ listElements rsItems
|
||||
numberedts = zip [1..] ts
|
||||
i = fromIntegral $ maybe 0 (+1) $ elemIndex t ts -- XXX
|
||||
in
|
||||
continue $ screenEnter d transactionScreen{tsState=((i,t),numberedts,acct)} st
|
||||
continue $ screenEnter d transactionScreen{tsState=TransactionScreenState{tsTransaction=(i,t)
|
||||
,tsTransactions=numberedts
|
||||
,tsSelectedAccount=rsSelectedAccount}} st
|
||||
Nothing -> continue st
|
||||
|
||||
-- fall through to the list's event handler (handles [pg]up/down)
|
||||
ev -> do
|
||||
l' <- handleEvent ev l
|
||||
continue st{aScreen=s{rsState=(l',acct)}}
|
||||
newitems <- handleEvent ev rsItems
|
||||
continue st{aScreen=s{rsState=rsState{rsItems=newitems}}}
|
||||
-- continue =<< handleEventLensed st someLens ev
|
||||
|
||||
Just ed ->
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
-- The transaction screen, showing a single transaction's general journal entry.
|
||||
|
||||
{-# LANGUAGE OverloadedStrings, TupleSections #-} -- , FlexibleContexts
|
||||
{-# LANGUAGE OverloadedStrings, TupleSections, RecordWildCards #-} -- , FlexibleContexts
|
||||
|
||||
module Hledger.UI.TransactionScreen
|
||||
(transactionScreen
|
||||
@ -37,7 +37,9 @@ import Hledger.UI.ErrorScreen
|
||||
|
||||
transactionScreen :: Screen
|
||||
transactionScreen = TransactionScreen{
|
||||
tsState = ((1,nulltransaction),[(1,nulltransaction)],"")
|
||||
tsState = TransactionScreenState{tsTransaction=(1,nulltransaction)
|
||||
,tsTransactions=[(1,nulltransaction)]
|
||||
,tsSelectedAccount=""}
|
||||
,sInitFn = initTransactionScreen
|
||||
,sDrawFn = drawTransactionScreen
|
||||
,sHandleFn = handleTransactionScreen
|
||||
@ -45,15 +47,17 @@ transactionScreen = TransactionScreen{
|
||||
|
||||
initTransactionScreen :: Day -> Bool -> AppState -> AppState
|
||||
initTransactionScreen _d _reset st@AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=_ropts}}
|
||||
,ajournal=_j
|
||||
,aScreen=s@TransactionScreen{tsState=((n,t),nts,a)}} =
|
||||
st{aScreen=s{tsState=((n,t),nts,a)}}
|
||||
|
||||
,ajournal=_j
|
||||
,aScreen=TransactionScreen{..}} = st
|
||||
initTransactionScreen _ _ _ = error "init function called with wrong screen type, should not happen"
|
||||
|
||||
drawTransactionScreen :: AppState -> [Widget]
|
||||
drawTransactionScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
,aScreen=TransactionScreen{tsState=((i,t),nts,acct)}} = [ui]
|
||||
,aScreen=TransactionScreen{
|
||||
tsState=TransactionScreenState{tsTransaction=(i,t)
|
||||
,tsTransactions=nts
|
||||
,tsSelectedAccount=acct}}} =
|
||||
[ui]
|
||||
where
|
||||
-- datedesc = show (tdate t) ++ " " ++ tdescription t
|
||||
toplabel =
|
||||
@ -95,11 +99,14 @@ drawTransactionScreen AppState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}
|
||||
drawTransactionScreen _ = error "draw function called with wrong screen type, should not happen"
|
||||
|
||||
handleTransactionScreen :: AppState -> Vty.Event -> EventM (Next AppState)
|
||||
handleTransactionScreen st@AppState{
|
||||
aScreen=s@TransactionScreen{tsState=((i,t),nts,acct)}
|
||||
,aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
|
||||
,ajournal=j
|
||||
} e = do
|
||||
handleTransactionScreen
|
||||
st@AppState{aScreen=s@TransactionScreen{tsState=tsState@TransactionScreenState{tsTransaction=(i,t)
|
||||
,tsTransactions=nts
|
||||
,tsSelectedAccount=acct}}
|
||||
,aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}
|
||||
,ajournal=j
|
||||
}
|
||||
e = do
|
||||
d <- liftIO getCurrentDay
|
||||
let
|
||||
(iprev,tprev) = maybe (i,t) ((i-1),) $ lookup (i-1) nts
|
||||
@ -131,18 +138,20 @@ handleTransactionScreen st@AppState{
|
||||
Nothing | null numberedts -> (0,nulltransaction)
|
||||
| i > fst (last numberedts) -> last numberedts
|
||||
| otherwise -> head numberedts
|
||||
st' = st{aScreen=s{tsState=((i',t'),numberedts,acct)}}
|
||||
st' = st{aScreen=s{tsState=TransactionScreenState{tsTransaction=(i',t')
|
||||
,tsTransactions=numberedts
|
||||
,tsSelectedAccount=acct}}}
|
||||
continue $ regenerateScreens j' d st'
|
||||
|
||||
Left err -> continue $ screenEnter d errorScreen{esState=err} st
|
||||
Left err -> continue $ screenEnter d errorScreen{esState=ErrorScreenState{esError=err}} st
|
||||
|
||||
-- if allowing toggling here, we should refresh the txn list from the parent register screen
|
||||
-- Vty.EvKey (Vty.KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty st
|
||||
-- Vty.EvKey (Vty.KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared st
|
||||
-- Vty.EvKey (Vty.KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal st
|
||||
|
||||
Vty.EvKey (Vty.KUp) [] -> continue $ regenerateScreens j d st{aScreen=s{tsState=((iprev,tprev),nts,acct)}}
|
||||
Vty.EvKey (Vty.KDown) [] -> continue $ regenerateScreens j d st{aScreen=s{tsState=((inext,tnext),nts,acct)}}
|
||||
Vty.EvKey (Vty.KUp) [] -> continue $ regenerateScreens j d st{aScreen=s{tsState=tsState{tsTransaction=(iprev,tprev)}}}
|
||||
Vty.EvKey (Vty.KDown) [] -> continue $ regenerateScreens j d st{aScreen=s{tsState=tsState{tsTransaction=(inext,tnext)}}}
|
||||
|
||||
Vty.EvKey (Vty.KLeft) [] -> continue st''
|
||||
where
|
||||
@ -153,7 +162,7 @@ handleTransactionScreen st@AppState{
|
||||
|
||||
handleTransactionScreen _ _ = error "event handler called with wrong screen type, should not happen"
|
||||
|
||||
rsSetSelectedTransaction i scr@RegisterScreen{rsState=(l,a)} = scr{rsState=(l',a)}
|
||||
where l' = listMoveTo (i-1) l
|
||||
rsSetSelectedTransaction i scr@RegisterScreen{rsState=rsState@RegisterScreenState{..}} = scr{rsState=rsState{rsItems=l'}}
|
||||
where l' = listMoveTo (i-1) rsItems
|
||||
rsSetSelectedTransaction _ scr = scr
|
||||
|
||||
|
||||
@ -62,51 +62,75 @@ data AppState = AppState {
|
||||
-- partial functions, so take care.
|
||||
data Screen =
|
||||
AccountsScreen {
|
||||
asState :: (List -- list widget holding:
|
||||
(Int -- indent level
|
||||
,AccountName -- full account name
|
||||
,AccountName -- full or short account name to display
|
||||
,[String] -- rendered amounts
|
||||
)
|
||||
,AccountName -- full name of the currently selected account (or "")
|
||||
)
|
||||
,sInitFn :: Day -> Bool -> AppState -> AppState -- ^ function to generate/update the screen's state,
|
||||
-- takes the current date and whether to reset the selection.
|
||||
,sDrawFn :: AppState -> [Widget] -- ^ brick renderer to use for this screen
|
||||
,sHandleFn :: AppState -> V.Event -> EventM (Next AppState) -- ^ brick event handler to use for this screen
|
||||
asState :: AccountsScreenState
|
||||
,sInitFn :: Day -> Bool -> AppState -> AppState -- ^ function to generate the screen's state on entry or change
|
||||
,sDrawFn :: AppState -> [Widget] -- ^ brick renderer for this screen
|
||||
,sHandleFn :: AppState -> V.Event -> EventM (Next AppState) -- ^ brick event handler for this screen
|
||||
}
|
||||
| RegisterScreen {
|
||||
rsState :: (List -- list widget holding:
|
||||
(String -- date
|
||||
,String -- description
|
||||
,String -- other accts
|
||||
,String -- change amt
|
||||
,String -- balance amt
|
||||
,Transaction -- the full transaction
|
||||
)
|
||||
,AccountName -- full name of the acct we are showing a register for
|
||||
)
|
||||
rsState :: RegisterScreenState
|
||||
,sInitFn :: Day -> Bool -> AppState -> AppState
|
||||
,sDrawFn :: AppState -> [Widget]
|
||||
,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)
|
||||
}
|
||||
| TransactionScreen {
|
||||
tsState :: ((Integer, Transaction) -- the (numbered) transaction we are currently viewing
|
||||
,[(Integer, Transaction)] -- the list of numbered transactions we can step through
|
||||
,AccountName -- the account whose register we entered this screen from
|
||||
)
|
||||
tsState :: TransactionScreenState
|
||||
,sInitFn :: Day -> Bool -> AppState -> AppState
|
||||
,sDrawFn :: AppState -> [Widget]
|
||||
,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)
|
||||
}
|
||||
}
|
||||
| ErrorScreen {
|
||||
esState :: String -- error message to display
|
||||
esState :: ErrorScreenState
|
||||
,sInitFn :: Day -> Bool -> AppState -> AppState
|
||||
,sDrawFn :: AppState -> [Widget]
|
||||
,sHandleFn :: AppState -> V.Event -> EventM (Next AppState)
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
-- | Render state for this type of screen.
|
||||
data AccountsScreenState = AccountsScreenState {
|
||||
asItems :: List AccountsScreenItem -- ^ list of account names & balances
|
||||
,asSelectedAccount :: AccountName -- ^ full name of the currently selected account (or "")
|
||||
} deriving (Show)
|
||||
|
||||
-- | An item in the accounts screen's list of accounts and balances.
|
||||
data AccountsScreenItem = AccountsScreenItem {
|
||||
asItemIndentLevel :: Int -- ^ indent level
|
||||
,asItemAccountName :: AccountName -- ^ full account name
|
||||
,asItemDisplayAccountName :: AccountName -- ^ full or short account name to display
|
||||
,asItemRenderedAmounts :: [String] -- ^ rendered amounts
|
||||
}
|
||||
|
||||
-- | Render state for this type of screen.
|
||||
data RegisterScreenState = RegisterScreenState {
|
||||
rsItems :: List RegisterScreenItem -- ^ list of transactions affecting this account
|
||||
,rsSelectedAccount :: AccountName -- ^ full name of the account we are showing a register for
|
||||
} deriving (Show)
|
||||
|
||||
-- | An item in the register screen's list of transactions in the current account.
|
||||
data RegisterScreenItem = RegisterScreenItem {
|
||||
rsItemDate :: String -- ^ date
|
||||
,rsItemDescription :: String -- ^ description
|
||||
,rsItemOtherAccounts :: String -- ^ other accounts
|
||||
,rsItemChangeAmount :: String -- ^ the change to the current account from this transaction
|
||||
,rsItemBalanceAmount :: String -- ^ the balance or running total after this transaction
|
||||
,rsItemTransaction :: Transaction -- ^ the full transaction
|
||||
}
|
||||
|
||||
-- | Render state for this type of screen.
|
||||
data TransactionScreenState = TransactionScreenState {
|
||||
tsTransaction :: NumberedTransaction -- ^ the transaction we are currently viewing, and its position in the list
|
||||
,tsTransactions :: [NumberedTransaction] -- ^ the list of transactions we can step through
|
||||
,tsSelectedAccount :: AccountName -- ^ the account whose register we entered this screen from
|
||||
} deriving (Show)
|
||||
|
||||
type NumberedTransaction = (Integer, Transaction)
|
||||
|
||||
-- | Render state for this type of screen.
|
||||
data ErrorScreenState = ErrorScreenState {
|
||||
esError :: String -- ^ error message to show
|
||||
} deriving (Show)
|
||||
|
||||
instance Show (List a) where show _ = "<List>"
|
||||
instance Show Editor where show _ = "<Editor>"
|
||||
|
||||
|
||||
@ -133,10 +133,10 @@ regenerateScreens j d st@AppState{aScreen=s,aPrevScreens=ss} =
|
||||
-- remove all the screens from the appstate and then add them back
|
||||
-- one at a time, regenerating as we go.
|
||||
let
|
||||
first:rest = reverse $ s:ss
|
||||
st0 = st{ajournal=j, aScreen=first, aPrevScreens=[]}
|
||||
st1 = (sInitFn first) d False st0
|
||||
st2 = foldl' (\st s -> (sInitFn s) d False $ pushScreen s st) st1 rest
|
||||
first:rest = reverse $ s:ss :: [Screen]
|
||||
st0 = st{ajournal=j, aScreen=first, aPrevScreens=[]} :: AppState
|
||||
st1 = (sInitFn first) d False st0 :: AppState
|
||||
st2 = foldl' (\st s -> (sInitFn s) d False $ pushScreen s st) st1 rest :: AppState
|
||||
in
|
||||
st2
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user