From 4e3c0cc93645adf1b2174f5901729f9d21dc56a6 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Fri, 12 Aug 2016 17:44:55 -0700 Subject: [PATCH] ui: historical/period toggle, help updates accounts and register screens can now switch between "historical" (default) and "period" modes title header wording is clearer quick help footer now indicates historical/period and tree/flat modes, saving space in the title help screen is more explanatory the enter key has been reserved for later and is no longer an alias for right/l --- hledger-ui/Hledger/UI/AccountsScreen.hs | 52 ++++++++++----- hledger-ui/Hledger/UI/RegisterScreen.hs | 41 +++++++----- hledger-ui/Hledger/UI/UIState.hs | 8 +++ hledger-ui/Hledger/UI/UIUtils.hs | 85 +++++++++++++++---------- hledger-ui/doc/hledger-ui.1.m4.md | 52 +++++++-------- 5 files changed, 142 insertions(+), 96 deletions(-) diff --git a/hledger-ui/Hledger/UI/AccountsScreen.hs b/hledger-ui/Hledger/UI/AccountsScreen.hs index b4c6646c6..2892c75ba 100644 --- a/hledger-ui/Hledger/UI/AccountsScreen.hs +++ b/hledger-ui/Hledger/UI/AccountsScreen.hs @@ -79,7 +79,13 @@ asInit d reset ui@UIState{ valuedate = fromMaybe d $ queryEndDate False q -- run the report - (items,_total) = convert $ singleBalanceReport ropts' q j + (items,_total) = convert $ report ropts' q j + where + -- still using the old balanceReport for change reports as it + -- does not include every account from before the report period + report | balancetype_ ropts == HistoricalBalance = singleBalanceReport + | otherwise = balanceReport + -- pre-render the list items displayitem (fullacct, shortacct, indent, bal) = @@ -118,7 +124,7 @@ asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} maxacctwidthseen = -- ltrace "maxacctwidthseen" $ V.maximum $ - V.map (\AccountsScreenItem{..} -> asItemIndentLevel*2 + textWidth asItemDisplayAccountName) $ + V.map (\AccountsScreenItem{..} -> asItemIndentLevel + textWidth asItemDisplayAccountName) $ -- V.filter (\(indent,_,_,_) -> (indent-1) <= fromMaybe 99999 mdepth) $ displayitems maxbalwidthseen = @@ -145,14 +151,17 @@ asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} render $ defaultLayout toplabel bottomlabel $ renderList (asDrawItem colwidths) True (_asList s) where + ishistorical = balancetype_ ropts == HistoricalBalance + toplabel = files + -- <+> withAttr (borderAttr <> "query") (str (if flat_ ropts then " flat" else "")) <+> nonzero - <+> str " accounts" - <+> withAttr (borderAttr <> "query") (str (if flat_ ropts then " (flat)" else "")) + <+> str (if ishistorical then " accounts" else " account changes") + -- <+> str (if ishistorical then " balances" else " changes") + <+> borderPeriodStr (if ishistorical then "at end of" else "in") (period_ ropts) <+> borderQueryStr querystr <+> togglefilters - <+> borderPeriodStr (period_ ropts) <+> borderDepthStr mdepth <+> str " (" <+> cur @@ -176,7 +185,7 @@ asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} ,if real_ ropts then ["real"] else [] ] of [] -> str "" - fs -> str " with " <+> withAttr (borderAttr <> "query") (str $ intercalate ", " fs) <+> str " txns" + fs -> str " from " <+> withAttr (borderAttr <> "query") (str $ intercalate ", " fs) <+> str " txns" nonzero | empty_ ropts = str "" | otherwise = withAttr (borderAttr <> "query") (str " nonzero") cur = str (case _asList s ^. listSelectedL of @@ -188,17 +197,25 @@ asDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} Minibuffer ed -> minibuffer ed _ -> quickhelp where - quickhelp = borderKeysStr [ - ("?", "help") - ,("right", "register") - ,("F", "flat?") - ,("-+0123456789", "depth") + selectedstr = withAttr (borderAttr <> "query") . str + quickhelp = borderKeysStr' [ + ("?", str "help") + ,("right", str "register") + ,("H" + ,if ishistorical + then selectedstr "historical" <+> str "/period" + else str "historical" <+> selectedstr "/period") + ,("F" + ,if flat_ ropts + then str "tree/" <+> selectedstr "flat" + else selectedstr "tree" <+> str "/flat") + ,("-+", str "depth") --,("/", "filter") --,("DEL", "unfilter") --,("ESC", "cancel/top") - ,("a", "add") - ,("g", "reload") - ,("q", "quit") + ,("a", str "add") +-- ,("g", "reload") + ,("q", str "quit") ] asDraw _ = error "draw function called with wrong screen type, should not happen" @@ -210,7 +227,7 @@ asDrawItem (acctwidth, balwidth) selected AccountsScreenItem{..} = -- let showitem = intercalate "\n" . balanceReportItemAsText defreportopts fmt render $ addamts asItemRenderedAmounts $ - str (T.unpack $ fitText (Just acctwidth) (Just acctwidth) True True $ T.replicate (2*asItemIndentLevel) " " <> asItemDisplayAccountName) <+> + str (T.unpack $ fitText (Just acctwidth) (Just acctwidth) True True $ T.replicate (asItemIndentLevel) " " <> asItemDisplayAccountName) <+> str " " <+> str (balspace asItemRenderedAmounts) where @@ -287,6 +304,7 @@ asHandle ui0@UIState{ EvKey (KChar '_') [] -> continue $ regenerateScreens j d $ decDepth ui EvKey (KChar c) [] | c `elem` ['+','='] -> continue $ regenerateScreens j d $ incDepth ui EvKey (KChar 't') [] -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui + EvKey (KChar 'H') [] -> continue $ regenerateScreens j d $ toggleHistorical ui EvKey (KChar 'F') [] -> continue $ regenerateScreens j d $ toggleFlat ui EvKey (KChar 'Z') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui) EvKey (KChar 'C') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui) @@ -298,8 +316,8 @@ asHandle ui0@UIState{ EvKey (KLeft) [MShift] -> continue $ regenerateScreens j d $ previousReportPeriod ui EvKey (KChar '/') [] -> continue $ regenerateScreens j d $ showMinibuffer ui EvKey k [] | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui) - EvKey k [] | k `elem` [KLeft, KChar 'h'] -> continue $ popScreen ui - EvKey k [] | k `elem` [KRight, KChar 'l', KEnter] -> scrollTopRegister >> continue (screenEnter d scr ui) + EvKey k [] | k `elem` [KLeft, KChar 'h'] -> continue $ popScreen ui + EvKey k [] | k `elem` [KRight, KChar 'l'] -> scrollTopRegister >> continue (screenEnter d scr ui) where scr = rsSetAccount selacct isdepthclipped registerScreen isdepthclipped = case getDepth ui of diff --git a/hledger-ui/Hledger/UI/RegisterScreen.hs b/hledger-ui/Hledger/UI/RegisterScreen.hs index e4c4265ea..2f062223a 100644 --- a/hledger-ui/Hledger/UI/RegisterScreen.hs +++ b/hledger-ui/Hledger/UI/RegisterScreen.hs @@ -62,7 +62,6 @@ rsInit d reset ui@UIState{aopts=UIOpts{cliopts_=CliOpts{reportopts_=ropts}}, ajo thisacctq = Acct $ (if inclusive then accountNameToAccountRegex else accountNameToAccountOnlyRegex) rsAccount ropts' = ropts{ depth_=Nothing - ,balancetype_=HistoricalBalance } q = queryFromOpts d ropts' -- reportq = filterQuery (not . queryIsDepth) q @@ -158,14 +157,18 @@ rsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} render $ defaultLayout toplabel bottomlabel $ renderList (rsDrawItem colwidths) True rsList where + ishistorical = balancetype_ ropts == HistoricalBalance + inclusive = not (flat_ ropts) || rsForceInclusive + toplabel = withAttr ("border" <> "bold") (str $ T.unpack $ replaceHiddenAccountsNameWith "All" rsAccount) - <+> withAttr (borderAttr <> "query") (str $ if inclusive then "" else " exclusive") +-- <+> withAttr (borderAttr <> "query") (str $ if inclusive then "" else " exclusive") <+> togglefilters <+> str " transactions" + -- <+> str (if ishistorical then " historical total" else " period total") <+> borderQueryStr (query_ ropts) -- <+> str " and subs" - <+> borderPeriodStr (period_ ropts) + <+> borderPeriodStr "in" (period_ ropts) <+> str " (" <+> cur <+> str "/" @@ -173,7 +176,6 @@ rsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} <+> str ")" <+> (if ignore_assertions_ copts then withAttr (borderAttr <> "query") (str " ignoring balance assertions") else str "") where - inclusive = not (flat_ ropts) || rsForceInclusive togglefilters = case concat [ uiShowClearedStatus $ clearedstatus_ ropts @@ -193,16 +195,22 @@ rsDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} Minibuffer ed -> minibuffer ed _ -> quickhelp where - quickhelp = borderKeysStr [ - ("?", "help") - ,("left", "back") - ,("right", "transaction") - ,("/", "filter") - ,("DEL", "unfilter") - --,("ESC", "reset") - ,("a", "add") - ,("g", "reload") - ,("q", "quit") + selectedstr = withAttr (borderAttr <> "query") . str + quickhelp = borderKeysStr' [ + ("?", str "help") + ,("left", str "back") + ,("right", str "transaction") + ,("H" + ,if ishistorical + then selectedstr "historical" <+> str "/period" + else str "historical" <+> selectedstr "/period") + ,("F" + ,if inclusive + then selectedstr "inclusive" <+> str "/exclusive" + else str "inclusive/" <+> selectedstr "exclusive") +-- ,("a", "add") +-- ,("g", "reload") +-- ,("q", "quit") ] rsDraw _ = error "draw function called with wrong screen type, should not happen" @@ -265,6 +273,7 @@ rsHandle ui@UIState{ (pos,f) = case listSelectedElement rsList of Nothing -> (endPos, journalFilePath j) Just (_, RegisterScreenItem{rsItemTransaction=Transaction{tsourcepos=GenericSourcePos f l c}}) -> (Just (l, Just c),f) + EvKey (KChar 'H') [] -> continue $ regenerateScreens j d $ toggleHistorical ui EvKey (KChar 'F') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleFlat ui) EvKey (KChar 'Z') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui) EvKey (KChar 'C') [] -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui) @@ -276,8 +285,8 @@ rsHandle ui@UIState{ EvKey (KRight) [MShift] -> continue $ regenerateScreens j d $ nextReportPeriod ui EvKey (KLeft) [MShift] -> continue $ regenerateScreens j d $ previousReportPeriod ui EvKey k [] | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui) - EvKey k [] | k `elem` [KLeft, KChar 'h'] -> continue $ popScreen ui - EvKey k [] | k `elem` [KRight, KChar 'l', KEnter] -> do + EvKey k [] | k `elem` [KLeft, KChar 'h'] -> continue $ popScreen ui + EvKey k [] | k `elem` [KRight, KChar 'l'] -> do case listSelectedElement rsList of Just (_, RegisterScreenItem{rsItemTransaction=t}) -> let diff --git a/hledger-ui/Hledger/UI/UIState.hs b/hledger-ui/Hledger/UI/UIState.hs index 3c7aaad95..6112833c2 100644 --- a/hledger-ui/Hledger/UI/UIState.hs +++ b/hledger-ui/Hledger/UI/UIState.hs @@ -56,6 +56,14 @@ toggleFlat ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropt toggleFlatMode ropts@ReportOpts{accountlistmode_=ALFlat} = ropts{accountlistmode_=ALTree} toggleFlatMode ropts = ropts{accountlistmode_=ALFlat} +-- | Toggle between historical balances and period balances. +toggleHistorical :: UIState -> UIState +toggleHistorical ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = + ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{balancetype_=b}}}} + where + b | balancetype_ ropts == HistoricalBalance = PeriodChange + | otherwise = HistoricalBalance + -- | Toggle between showing all and showing only real (non-virtual) items. toggleReal :: UIState -> UIState toggleReal ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} = diff --git a/hledger-ui/Hledger/UI/UIUtils.hs b/hledger-ui/Hledger/UI/UIUtils.hs index 9a078d43c..c27207cee 100644 --- a/hledger-ui/Hledger/UI/UIUtils.hs +++ b/hledger-ui/Hledger/UI/UIUtils.hs @@ -8,7 +8,7 @@ where import Brick import Brick.Widgets.Border import Brick.Widgets.Border.Style -import Brick.Widgets.Center +-- import Brick.Widgets.Center import Brick.Widgets.Dialog import Brick.Widgets.Edit import Data.List @@ -48,9 +48,9 @@ helpDialog = padLeftRight 1 $ vBox [ str "NAVIGATION" - ,renderKey ("UP/k/DOWN/j/PGUP/PGDN/HOME/END", "") + ,renderKey ("UP/DOWN/k/j/PGUP/PGDN/HOME/END", "") ,str " move selection" - ,renderKey ("RIGHT/l/ENTER", "drill down") + ,renderKey ("RIGHT/l", "more detail") ,renderKey ("LEFT/h", "previous screen") ,renderKey ("ESC", "cancel / reset") ,str " " @@ -61,40 +61,50 @@ helpDialog = ,renderKey ("g", "reload data") ,renderKey ("I", "toggle balance assertions") ,renderKey ("q", "quit") - ] + ,str " " + ,str "MANUAL" + ,str "from help dialog:" + ,renderKey ("t", "text") + ,renderKey ("m", "man page") + ,renderKey ("i", "info") + ] ,padLeftRight 1 $ vBox [ str "FILTERING" - ,renderKey ("/", "set a filter query") - ,renderKey ("C", "toggle cleared filter") - ,renderKey ("U", "toggle uncleared filter") - ,renderKey ("R", "toggle real filter") - ,renderKey ("Z", "toggle nonzero filter") - ,renderKey ("F", "toggle flat/exclusive mode") + ,renderKey ("SHIFT-DOWN/UP", "shrink/grow report period") + ,renderKey ("SHIFT-RIGHT/LEFT", "next/previous report period") + ,renderKey ("t", "set report period to today") ,str " " - ,renderKey ("t", " set report period to today") - ,renderKey ("d/u", "decrease/increase report period") - ,renderKey ("n/p", "next/previous report period") + ,renderKey ("/", "set a filter query") + ,renderKey ("C", "toggle cleared/all") + ,renderKey ("U", "toggle uncleared/all") + ,renderKey ("R", "toggle real/all") + ,renderKey ("Z", "toggle nonzero/all") + ,renderKey ("DEL/BS", "remove filters") ,str " " ,str "accounts screen:" ,renderKey ("-+0123456789", "set depth limit") + ,renderKey ("H", "toggle period balance (shows change) or\nhistorical balance (includes older postings)") + ,renderKey ("F", "toggle tree (amounts include subaccounts) or\nflat mode (amounts exclude subaccounts\nexcept when account is depth-clipped)") ,str " " - ,renderKey ("DEL/BS", "remove filters") + ,str "register screen:" + ,renderKey ("H", "toggle period or historical total") + ,renderKey ("F", "toggle subaccount transaction inclusion\n(and tree/flat mode)") ] ] - ,vBox [ - str " " - ,hCenter $ padLeftRight 1 $ - hCenter (str "MANUAL") - <=> - hCenter (hBox [ - renderKey ("t", "text") - ,str " " - ,renderKey ("m", "man page") - ,str " " - ,renderKey ("i", "info") - ]) - ] +-- ,vBox [ +-- str " " +-- ,hCenter $ padLeftRight 1 $ +-- hCenter (str "MANUAL") +-- <=> +-- hCenter (hBox [ +-- renderKey ("t", "text") +-- ,str " " +-- ,renderKey ("m", "man page") +-- ,str " " +-- ,renderKey ("i", "info") +-- ]) +-- ] ] where renderKey (key,desc) = withAttr (borderAttr <> "keys") (str key) <+> str " " <+> str desc @@ -104,10 +114,12 @@ helpHandle :: UIState -> Event -> EventM Name (Next UIState) helpHandle ui ev = case ev of EvKey k [] | k `elem` [KEsc, KLeft, KChar 'h', KChar '?'] -> continue $ setMode Normal ui - EvKey (KChar 't') [] -> suspendAndResume $ runHelp >> return (setMode Normal ui) - EvKey (KChar 'm') [] -> suspendAndResume $ runMan >> return (setMode Normal ui) - EvKey (KChar 'i') [] -> suspendAndResume $ runInfo >> return (setMode Normal ui) + EvKey (KChar 't') [] -> suspendAndResume $ runHelp >> return ui' + EvKey (KChar 'm') [] -> suspendAndResume $ runMan >> return ui' + EvKey (KChar 'i') [] -> suspendAndResume $ runInfo >> return ui' _ -> continue ui + where + ui' = setMode Normal ui -- | Draw the minibuffer. minibuffer :: Editor Name -> Widget Name @@ -133,15 +145,18 @@ borderDepthStr :: Maybe Int -> Widget Name borderDepthStr Nothing = str "" borderDepthStr (Just d) = str " to " <+> withAttr (borderAttr <> "query") (str $ "depth "++show d) -borderPeriodStr :: Period -> Widget Name -borderPeriodStr PeriodAll = str "" -borderPeriodStr p = str " in " <+> withAttr (borderAttr <> "query") (str $ showPeriod p) +borderPeriodStr :: String -> Period -> Widget Name +borderPeriodStr _ PeriodAll = str "" +borderPeriodStr preposition p = str (" "++preposition++" ") <+> withAttr (borderAttr <> "query") (str $ showPeriod p) borderKeysStr :: [(String,String)] -> Widget Name -borderKeysStr keydescs = +borderKeysStr = borderKeysStr' . map (\(a,b) -> (a, str b)) + +borderKeysStr' :: [(String,Widget Name)] -> Widget Name +borderKeysStr' keydescs = hBox $ intersperse sep $ - [withAttr (borderAttr <> "keys") (str keys) <+> str ":" <+> str desc | (keys, desc) <- keydescs] + [withAttr (borderAttr <> "keys") (str keys) <+> str ":" <+> desc | (keys, desc) <- keydescs] where -- sep = str " | " sep = str " " diff --git a/hledger-ui/doc/hledger-ui.1.m4.md b/hledger-ui/doc/hledger-ui.1.m4.md index f6c69fc7d..5da760518 100644 --- a/hledger-ui/doc/hledger-ui.1.m4.md +++ b/hledger-ui/doc/hledger-ui.1.m4.md @@ -137,9 +137,17 @@ To see less detail, set a depth limit by pressing a number key, `1` to `9`. `-` and `+` (or `=`) decrease and increase the depth limit. To remove the depth limit, set it higher than the maximum account depth, or press `ESCAPE`. -`F` toggles flat mode on and off. In flat mode, accounts are listed without indentation, -and show their subaccount-excluding balances, except for accounts which have been clipped -by a depth limit, which show their inclusive balances (as with hledger's balance command). +`F` toggles flat mode, in which accounts are shown as a flat list, with their full names. +In this mode, account balances exclude subaccounts, except for accounts at the depth limit +(as with hledger's balance command). + +`H` toggles between showing historical balances or period balances. +Historical balances (the default) are ending balances at the end of the report period, +taking into account all transactions before that date (filtered by the filter query if any), +including transactions before the start of the report period. In other words, historical +balances are what you would see on a bank statement for that account (unless disturbed by +a filter query). Period balances ignore transactions before the report start date, so they +show the change in balance during the report period. They are more useful eg when viewing a time log. `C` toggles cleared mode, in which [uncleared transactions and postings](/journal.html#transactions) are @@ -166,32 +174,20 @@ Each line represents one transaction and shows: - the overall change to the current account's balance; positive for an inflow to this account, negative for an outflow. -- the current account's historic balance (if no query other than a date limit is in effect) - or the running total starting from zero (otherwise), after the transaction. - Eg, these will show historic balances: +- the running historical total or period total for the current account, after the transaction. +This can be toggled with `H`. +Similar to the accounts screen, the historical total is affected by transactions +(filtered by the filter query) before the report start date, while the period total is not. +If the historical total is not disturbed by a filter query, it will be the +running historical balance you would see on a bank register for the current account. - ``` - $ hledger-ui - $ hledger-ui --begin 'this month' - $ hledger-ui --register checking date:2015/10 - ``` - - while these will show a running total, since the queries are not just date limits: - - ``` - $ hledger-ui checking - $ hledger-ui --begin 'this month' desc:market - $ hledger-ui --register checking --cleared - ``` - -The register screen normally shows transactions in the current account -and any of its subaccounts (inclusive mode). -If it was entered from the accounts screen in flat mode, where the -selected account was not depth-clipped and therefore was showing its -subaccount-excluding balance, the register too will omit the transactions -of subaccounts (exclusive mode). This means the register always shows -the transactions responsible for the balance being displayed on the -accounts screen. +If the accounts screen was in tree mode, +the register screen will include transactions from both the current account and its subaccounts. +If the accounts screen was in flat mode, and a non-depth-clipped account was selected, +the register screen will exclude transactions from subaccounts. +In other words, the register always shows the transactions responsible for the period balance +shown on the accounts screen. +As on the accounts screen, this can be toggled with `F`. `C` toggles cleared mode, in which [uncleared transactions and postings](/journal.html#transactions) are