packages feed

hledger-ui 1.2 → 1.3

raw patch · 13 files changed

+548/−242 lines, 13 filesdep ~brickdep ~hledgerdep ~hledger-lib

Dependency ranges changed: brick, hledger, hledger-lib

Files

CHANGES view
@@ -2,8 +2,50 @@ See also the hledger and project change logs.  -# 1.2 (2016/3/31)+# 1.3 (2017/6/30) +Deps: allow brick 0.19 (#575, Felix Yan, Simon Michael)++The P key toggles pending mode.+Also there is a temporary --status-toggles flag for testing different+toggle styles, see `hledger-ui -h`. (#564)++There is now less "warping" of selection when lists change:++- When the selected account disappears, eg when toggling zero+  accounts, the selection moves to the alphabetically preceding item,+  instead of the first one.++- When the selected transaction disappears, eg when toggling status+  filters, the selection moves to the nearest transaction by date (and+  if several have the same date, by journal order), instead of the+  last one.++In the accounts and register screens, you can now scroll down further+so that the last item need not always be shown at the bottom of the+screen.  Also we now try to center the selected item in the following+situations:++-   after moving to the end with Page down/End+-   after toggling filters (status, real, historical..)+-   on pressing the control-l key (should force a screen redraw, also)+-   on entering the register screen from the accounts screen (there's a+    known problem with this: it doesn't work the first time).++Items near the top of the list can't be centered, as we don't scroll+higher than the top of the list.++Emacs movement keys are now supported, as well as VI keys.+hjkl and CTRL-bfnp should work wherever unmodified arrow keys work.++The register screen now shows transaction status marks.++In the transaction screen, amounts are now better aligned, eg when+there are posting status marks or virtual postings.+++# 1.2 (2017/3/31)+ Fix a pattern match failure when pressing E on the transaction screen (fixes #508)  Accounts with ? in name had empty registers (fixes #498) (Bryan Richter)@@ -20,6 +62,7 @@ -   allow brick 0.16 (Joshua Chia)  -   drop obsolete --no-elide flag+  # 1.1 (2016/12/31) 
Hledger/UI/AccountsScreen.hs view
@@ -24,6 +24,7 @@ import qualified Data.Vector as V import Graphics.Vty (Event(..),Key(..),Modifier(..)) import Lens.Micro.Platform+import Safe import System.Console.ANSI import System.FilePath (takeFileName) @@ -54,19 +55,27 @@   } =   ui{aopts=uopts', aScreen=s & asList .~ newitems'}    where-    newitems = list AccountsList (V.fromList displayitems) 1+    newitems = list AccountsList (V.fromList $ displayitems ++ blankitems) 1 -    -- keep the selection near the last selected account-    -- (may need to move to the next leaf account when entering flat mode)+    -- decide which account is selected:+    -- if reset is true, the first account;+    -- otherwise, the previously selected account if possible;+    -- otherwise, the first account with the same prefix (eg first leaf account when entering flat mode);+    -- otherwise, the alphabetically preceding account.     newitems' = listMoveTo selidx newitems       where         selidx = case (reset, listSelectedElement $ _asList s) of                    (True, _)               -> 0                    (_, Nothing)            -> 0-                   (_, Just (_,AccountsScreenItem{asItemAccountName=a})) -> fromMaybe (fromMaybe 0 mprefixmatch) mexactmatch-                     where-                       mexactmatch  = findIndex ((a ==)                      . asItemAccountName) displayitems-                       mprefixmatch = findIndex ((a `isAccountNamePrefixOf`) . asItemAccountName) displayitems+                   (_, Just (_,AccountsScreenItem{asItemAccountName=a})) -> +                     headDef 0 $ catMaybes [+                       findIndex (a ==) as+                      ,findIndex (a `isAccountNamePrefixOf`) as+                      ,Just $ max 0 (length (filter (< a) as) - 1)+                      ]+                      where+                        as = map asItemAccountName displayitems+     uopts' = uopts{cliopts_=copts{reportopts_=ropts'}}     ropts' = ropts{accountlistmode_=if flat_ ropts then ALFlat else ALTree} @@ -98,6 +107,13 @@         Mixed amts = normaliseMixedAmountSquashPricesForDisplay $ stripPrices bal         stripPrices (Mixed as) = Mixed $ map stripprice as where stripprice a = a{aprice=NoPrice}     displayitems = map displayitem items+    -- blanks added for scrolling control, cf RegisterScreen +    blankitems = replicate 100+      AccountsScreenItem{asItemIndentLevel        = 0+                        ,asItemAccountName        = ""+                        ,asItemDisplayAccountName = ""+                        ,asItemRenderedAmounts    = []+                        }   asInit _ _ _ = error "init function called with wrong screen type, should not happen"@@ -109,7 +125,7 @@                            ,aMode=mode                            } =   case mode of-    Help       -> [helpDialog, maincontent]+    Help       -> [helpDialog copts, maincontent]     -- Minibuffer e -> [minibuffer e, maincontent]     _          -> [maincontent]   where@@ -181,7 +197,7 @@             mdepth = depth_ ropts             togglefilters =               case concat [-                   uiShowClearedStatus $ clearedstatus_ ropts+                   uiShowStatus copts $ statuses_ ropts                   ,if real_ ropts then ["real"] else []                   ] of                 [] -> str ""@@ -191,7 +207,8 @@             cur = str (case _asList s ^. listSelectedL of                         Nothing -> "-"                         Just i -> show (i + 1))-            total = str $ show $ V.length $ s ^. asList . listElementsL+            total = str $ show $ V.length nonblanks +            nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ s ^. asList . listElementsL          bottomlabel = case mode of                         Minibuffer ed -> minibuffer ed@@ -255,9 +272,9 @@   ,aMode=mode   } ev = do   d <- liftIO getCurrentDay-  -- c <- getContext-  -- let h = c^.availHeightL-  --     moveSel n l = listMoveBy n l+  let+    nonblanks = V.takeWhile (not . T.null . asItemAccountName) $ _asList^.listElementsL+    lastnonblankidx = max 0 (length nonblanks - 1)    -- save the currently selected account, in case we leave this screen and lose the selection   let@@ -315,50 +332,70 @@         VtyEvent (EvKey (KChar '_') []) -> continue $ regenerateScreens j d $ decDepth ui         VtyEvent (EvKey (KChar c)   []) | c `elem` ['+','='] -> continue $ regenerateScreens j d $ incDepth ui         VtyEvent (EvKey (KChar 't') [])    -> continue $ regenerateScreens j d $ setReportPeriod (DayPeriod d) ui-        VtyEvent (EvKey (KChar 'H') []) -> continue $ regenerateScreens j d $ toggleHistorical ui-        VtyEvent (EvKey (KChar 'F') []) -> continue $ regenerateScreens j d $ toggleFlat ui-        VtyEvent (EvKey (KChar 'Z') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui)-        VtyEvent (EvKey (KChar 'C') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui)-        VtyEvent (EvKey (KChar 'U') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleUncleared ui)-        VtyEvent (EvKey (KChar 'R') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleReal ui)++        -- display mode/query toggles+        VtyEvent (EvKey (KChar 'H') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui+        VtyEvent (EvKey (KChar 'F') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleFlat ui+        VtyEvent (EvKey (KChar 'Z') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleEmpty ui+        VtyEvent (EvKey (KChar 'R') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleReal ui+        VtyEvent (EvKey (KChar 'U') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui+        VtyEvent (EvKey (KChar 'P') []) -> asCenterAndContinue $ regenerateScreens j d $ togglePending ui+        VtyEvent (EvKey (KChar 'C') []) -> asCenterAndContinue $ regenerateScreens j d $ toggleCleared ui+         VtyEvent (EvKey (KDown)     [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui         VtyEvent (EvKey (KUp)       [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui         VtyEvent (EvKey (KRight)    [MShift]) -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui         VtyEvent (EvKey (KLeft)     [MShift]) -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui         VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui         VtyEvent (EvKey k           []) | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui)-        VtyEvent (EvKey k           []) | k `elem` [KLeft, KChar 'h']  -> continue $ popScreen ui-        VtyEvent (EvKey k           []) | k `elem` [KRight, KChar 'l'] -> scrollTopRegister >> continue (screenEnter d scr ui)+        VtyEvent e | e `elem` moveLeftEvents -> continue $ popScreen ui+        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle _asList >> invalidateCache >> continue ui++        -- enter register screen for selected account (if there is one), +        -- centering its selected transaction if possible+        VtyEvent e | e `elem` moveRightEvents +                   , not $ isBlankElement $ listSelectedElement _asList->+          -- TODO center selection after entering register screen; neither of these works till second time entering; easy strictifications didn't help +          rsCenterAndContinue $  +          -- flip rsHandle (VtyEvent (EvKey (KChar 'l') [MCtrl])) $+            screenEnter d regscr ui            where-            scr = rsSetAccount selacct isdepthclipped registerScreen+            regscr = rsSetAccount selacct isdepthclipped registerScreen             isdepthclipped = case getDepth ui of                                 Just d  -> accountNameLevel selacct >= d                                 Nothing -> False +        -- prevent moving down over blank padding items;+        -- instead scroll down by one, until maximally scrolled - shows the end has been reached+        VtyEvent (EvKey (KDown)     []) | isBlankElement mnextelement -> do+          vScrollBy (viewportScroll $ _asList^.listNameL) 1 +          continue ui+          where +            mnextelement = listSelectedElement $ listMoveDown _asList++        -- 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+          list <- handleListEvent e _asList+          if isBlankElement $ listSelectedElement list+          then do+            let list' = listMoveTo lastnonblankidx list+            scrollSelectionToMiddle list'+            continue ui{aScreen=scr{_asList=list'}}+          else+            continue ui{aScreen=scr{_asList=list}}+                   -- fall through to the list's event handler (handles up/down)-        VtyEvent ev ->-              do-                let ev' = case ev of-                            EvKey (KChar 'k') [] -> EvKey (KUp) []-                            EvKey (KChar 'j') [] -> EvKey (KDown) []-                            _                    -> ev-                newitems <- handleListEvent ev' _asList-                continue $ ui{aScreen=scr & asList .~ newitems-                                          & asSelectedAccount .~ selacct-                                          }-                -- continue =<< handleEventLensed ui someLens ev+        VtyEvent ev -> do+          newitems <- handleListEvent (normaliseMovementKeys ev) _asList+          continue $ ui{aScreen=scr & asList .~ newitems+                                    & asSelectedAccount .~ selacct+                                    }+         AppEvent _        -> continue ui         MouseDown _ _ _ _ -> continue ui         MouseUp _ _ _     -> continue ui    where-    -- Encourage a more stable scroll position when toggling list items.-    -- We scroll to the top, and the viewport will automatically-    -- scroll down just far enough to reveal the selection, which-    -- usually leaves it at bottom of screen).-    -- XXX better: scroll so selection is in middle of screen ?-    scrollTop         = vScrollToBeginning $ viewportScroll AccountsViewport-    scrollTopRegister = vScrollToBeginning $ viewportScroll RegisterViewport     journalspan = journalDateSpan False j  asHandle _ _ = error "event handler called with wrong screen type, should not happen"@@ -366,3 +403,8 @@ asSetSelectedAccount a s@AccountsScreen{} = s & asSelectedAccount .~ a asSetSelectedAccount _ s = s +isBlankElement mel = ((asItemAccountName . snd) <$> mel) == Just "" ++asCenterAndContinue ui = do+  scrollSelectionToMiddle $ _asList $ aScreen ui+  continue ui
Hledger/UI/ErrorScreen.hs view
@@ -39,11 +39,11 @@ esInit _ _ _ = error "init function called with wrong screen type, should not happen"  esDraw :: UIState -> [Widget Name]-esDraw UIState{ --aopts=UIOpts{cliopts_=copts@CliOpts{}}-               aScreen=ErrorScreen{..}+esDraw UIState{aopts=UIOpts{cliopts_=copts@CliOpts{}}+              ,aScreen=ErrorScreen{..}               ,aMode=mode} =   case mode of-    Help       -> [helpDialog, maincontent]+    Help       -> [helpDialog copts, maincontent]     -- Minibuffer e -> [minibuffer e, maincontent]     _          -> [maincontent]   where
Hledger/UI/RegisterScreen.hs view
@@ -4,11 +4,12 @@  module Hledger.UI.RegisterScreen  (registerScreen+ ,rsHandle  ,rsSetAccount+ ,rsCenterAndContinue  ) where -import Lens.Micro.Platform ((^.)) import Control.Monad import Control.Monad.IO.Class (liftIO) import Data.List@@ -16,13 +17,15 @@ import Data.Monoid import Data.Maybe import qualified Data.Text as T-import Data.Time.Calendar (Day)+import Data.Time.Calendar import qualified Data.Vector as V import Graphics.Vty (Event(..),Key(..),Modifier(..)) import Brick import Brick.Widgets.List import Brick.Widgets.Edit import Brick.Widgets.Border (borderAttr)+import Lens.Micro.Platform+import Safe import System.Console.ANSI  @@ -76,6 +79,7 @@       where         displayitem (t, _, _issplit, otheracctsstr, change, bal) =           RegisterScreenItem{rsItemDate          = showDate $ transactionRegisterDate q thisacctq t+                            ,rsItemStatus        = tstatus t                             ,rsItemDescription   = T.unpack $ tdescription t                             ,rsItemOtherAccounts = case splitOn ", " otheracctsstr of                                                      [s] -> s@@ -85,20 +89,41 @@                             ,rsItemBalanceAmount = showMixedAmountOneLineWithoutPrice bal                             ,rsItemTransaction   = t                             }-+    -- blank items are added to allow more control of scroll position; we won't allow movement over these+    blankitems = replicate 100  -- 100 ought to be enough for anyone+          RegisterScreenItem{rsItemDate          = ""+                            ,rsItemStatus        = Unmarked+                            ,rsItemDescription   = ""+                            ,rsItemOtherAccounts = ""+                            ,rsItemChangeAmount  = ""+                            ,rsItemBalanceAmount = ""+                            ,rsItemTransaction   = nulltransaction+                            }     -- build the List-    newitems = list RegisterList (V.fromList displayitems) 1+    newitems = list RegisterList (V.fromList $ displayitems ++ blankitems) 1 -    -- keep the selection on the previously selected transaction if possible,-    -- (eg after toggling nonzero mode), otherwise select the last element.+    -- decide which transaction is selected:+    -- if reset is true, the last (latest) transaction;+    -- otherwise, the previously selected transaction if possible;+    -- otherwise, the transaction nearest in date to it;+    -- or if there's several with the same date, the nearest in journal order;+    -- otherwise, the last (latest) transaction.      newitems' = listMoveTo newselidx newitems       where-        newselidx = case (reset, listSelectedElement rsList) of-                      (True, _)    -> endidx-                      (_, Nothing) -> endidx-                      (_, Just (_,RegisterScreenItem{rsItemTransaction=Transaction{tindex=ti}}))-                                   -> fromMaybe endidx $ findIndex ((==ti) . tindex . rsItemTransaction) displayitems-        endidx = length displayitems+        newselidx = +          case (reset, listSelectedElement rsList) of+            (True, _)    -> endidx+            (_, Nothing) -> endidx+            (_, Just (_, RegisterScreenItem{rsItemTransaction=Transaction{tindex=prevselidx, tdate=prevseld}})) ->+              headDef endidx $ catMaybes [+                 findIndex ((==prevselidx) . tindex . rsItemTransaction) displayitems+                ,findIndex ((==nearestidbydatethenid) . Just . tindex . rsItemTransaction) displayitems+                ]+              where+                nearestidbydatethenid = third3 <$> (headMay $ sort+                  [(abs $ diffDays (tdate t) prevseld, abs (tindex t - prevselidx), tindex t) | t <- ts])+                ts = map rsItemTransaction displayitems+        endidx = length displayitems - 1  rsInit _ _ _ = error "init function called with wrong screen type, should not happen" @@ -108,7 +133,7 @@                             ,aMode=mode                             } =   case mode of-    Help       -> [helpDialog, maincontent]+    Help       -> [helpDialog copts, maincontent]     -- Minibuffer e -> [minibuffer e, maincontent]     _          -> [maincontent]   where@@ -127,7 +152,7 @@         -- columns and whitespace.  If they don't get all they need,         -- allocate it to them proportionally to their maximum widths.         whitespacewidth = 10 -- inter-column whitespace, fixed width-        minnonamtcolswidth = datewidth + 2 + 2 -- date column plus at least 2 for desc and accts+        minnonamtcolswidth = datewidth + 1 + 2 + 2 -- date column plus at least 1 for status and 2 for desc and accts         maxamtswidth = max 0 (totalwidth - minnonamtcolswidth - whitespacewidth)         maxchangewidthseen = maximum' $ map (strWidth . rsItemChangeAmount) displayitems         maxbalwidthseen = maximum' $ map (strWidth . rsItemBalanceAmount) displayitems@@ -140,7 +165,7 @@         -- maxdescacctswidth = totalwidth - (whitespacewidth - 4) - changewidth - balwidth         maxdescacctswidth =           -- trace (show (totalwidth, datewidth, changewidth, balwidth, whitespacewidth)) $-          max 0 (totalwidth - datewidth - changewidth - balwidth - whitespacewidth)+          max 0 (totalwidth - datewidth - 1 - changewidth - balwidth - whitespacewidth)         -- allocating proportionally.         -- descwidth' = maximum' $ map (strWidth . second6) displayitems         -- acctswidth' = maximum' $ map (strWidth . third6) displayitems@@ -178,7 +203,7 @@           where             togglefilters =               case concat [-                   uiShowClearedStatus $ clearedstatus_ ropts+                   uiShowStatus copts $ statuses_ ropts                   ,if real_ ropts then ["real"] else []                   ,if empty_ ropts then [] else ["nonzero"]                   ] of@@ -187,7 +212,8 @@             cur = str $ case rsList ^. listSelectedL of                          Nothing -> "-"                          Just i -> show (i + 1)-            total = str $ show $ length displayitems+            total = str $ show $ length nonblanks+            nonblanks = V.takeWhile (not . null . rsItemDate) $ rsList^.listElementsL              -- query = query_ $ reportopts_ $ cliopts_ opts @@ -220,7 +246,9 @@   Widget Greedy Fixed $ do     render $       str (fitString (Just datewidth) (Just datewidth) True True rsItemDate) <+>-      str "  " <+>+      str " " <+>+      str (fitString (Just 1) (Just 1) True True (show rsItemStatus)) <+>+      str " " <+>       str (fitString (Just descwidth) (Just descwidth) True True rsItemDescription) <+>       str "  " <+>       str (fitString (Just acctswidth) (Just acctswidth) True True rsItemOtherAccounts) <+>@@ -244,7 +272,11 @@   ,aMode=mode   } ev = do   d <- liftIO getCurrentDay-+  let +    journalspan = journalDateSpan False j+    nonblanks = V.takeWhile (not . null . rsItemDate) $ rsList^.listElementsL+    lastnonblankidx = max 0 (length nonblanks - 1)+     case mode of     Minibuffer ed ->       case ev of@@ -285,24 +317,31 @@                           rsItemTransaction=Transaction{tsourcepos=GenericSourcePos f l c}}) -> (Just (l, Just c),f)                         Just (_, RegisterScreenItem{                           rsItemTransaction=Transaction{tsourcepos=JournalSourcePos f (l,_)}}) -> (Just (l, Nothing),f)-        VtyEvent (EvKey (KChar 'H') []) -> continue $ regenerateScreens j d $ toggleHistorical ui-        VtyEvent (EvKey (KChar 'F') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleFlat ui)-        VtyEvent (EvKey (KChar 'Z') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleEmpty ui)-        VtyEvent (EvKey (KChar 'C') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleCleared ui)-        VtyEvent (EvKey (KChar 'U') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleUncleared ui)-        VtyEvent (EvKey (KChar 'R') []) -> scrollTop >> (continue $ regenerateScreens j d $ toggleReal ui)-        VtyEvent (EvKey (KChar '/') []) -> (continue $ regenerateScreens j d $ showMinibuffer ui)++        -- display mode/query toggles+        VtyEvent (EvKey (KChar 'H') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleHistorical ui+        VtyEvent (EvKey (KChar 'F') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleFlat ui+        VtyEvent (EvKey (KChar 'Z') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleEmpty ui+        VtyEvent (EvKey (KChar 'R') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleReal ui+        VtyEvent (EvKey (KChar 'U') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleUnmarked ui+        VtyEvent (EvKey (KChar 'P') []) -> rsCenterAndContinue $ regenerateScreens j d $ togglePending ui+        VtyEvent (EvKey (KChar 'C') []) -> rsCenterAndContinue $ regenerateScreens j d $ toggleCleared ui++        VtyEvent (EvKey (KChar '/') []) -> continue $ regenerateScreens j d $ showMinibuffer ui         VtyEvent (EvKey (KDown)     [MShift]) -> continue $ regenerateScreens j d $ shrinkReportPeriod d ui         VtyEvent (EvKey (KUp)       [MShift]) -> continue $ regenerateScreens j d $ growReportPeriod d ui         VtyEvent (EvKey (KRight)    [MShift]) -> continue $ regenerateScreens j d $ nextReportPeriod journalspan ui         VtyEvent (EvKey (KLeft)     [MShift]) -> continue $ regenerateScreens j d $ previousReportPeriod journalspan ui         VtyEvent (EvKey k           []) | k `elem` [KBS, KDel] -> (continue $ regenerateScreens j d $ resetFilter ui)-        VtyEvent (EvKey k           []) | k `elem` [KLeft, KChar 'h']  -> continue $ popScreen ui-        VtyEvent (EvKey k           []) | k `elem` [KRight, KChar 'l'] -> do+        VtyEvent e | e `elem` moveLeftEvents  -> continue $ popScreen ui+        VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle rsList >> invalidateCache >> continue ui++        -- enter transaction screen for selected transaction+        VtyEvent e | e `elem` moveRightEvents -> do           case listSelectedElement rsList of             Just (_, RegisterScreenItem{rsItemTransaction=t}) ->               let-                ts = map rsItemTransaction $ V.toList $ listElements rsList+                ts = map rsItemTransaction $ V.toList $ nonblanks                 numberedts = zip [1..] ts                 i = fromIntegral $ maybe 0 (+1) $ elemIndex t ts -- XXX               in@@ -310,21 +349,40 @@                                                           ,tsTransactions=numberedts                                                           ,tsAccount=rsAccount} ui             Nothing -> continue ui-        -- fall through to the list's event handler (handles [pg]up/down)++        -- prevent moving down over blank padding items;+        -- instead scroll down by one, until maximally scrolled - shows the end has been reached+        VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do+          vScrollBy (viewportScroll $ rsList^.listNameL) 1 +          continue ui+          where +            mnextelement = listSelectedElement $ listMoveDown rsList++        -- 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+          list <- handleListEvent e rsList+          if isBlankElement $ listSelectedElement list+          then do+            let list' = listMoveTo lastnonblankidx list+            scrollSelectionToMiddle list'+            continue ui{aScreen=s{rsList=list'}}+          else+            continue ui{aScreen=s{rsList=list}}+          +        -- fall through to the list's event handler (handles other [pg]up/down events)         VtyEvent ev -> do-                let ev' = case ev of-                            EvKey (KChar 'k') [] -> EvKey (KUp) []-                            EvKey (KChar 'j') [] -> EvKey (KDown) []-                            _                    -> ev-                newitems <- handleListEvent ev' rsList-                continue ui{aScreen=s{rsList=newitems}}-                -- continue =<< handleEventLensed ui someLens ev+          let ev' = normaliseMovementKeys ev+          newitems <- handleListEvent ev' rsList+          continue ui{aScreen=s{rsList=newitems}}+         AppEvent _        -> continue ui         MouseDown _ _ _ _ -> continue ui         MouseUp _ _ _     -> continue ui-      where-        -- Encourage a more stable scroll position when toggling list items (cf AccountsScreen.hs)-        scrollTop = vScrollToBeginning $ viewportScroll RegisterViewport-        journalspan = journalDateSpan False j  rsHandle _ _ = error "event handler called with wrong screen type, should not happen"++isBlankElement mel = ((rsItemDate . snd) <$> mel) == Just "" ++rsCenterAndContinue ui = do+  scrollSelectionToMiddle $ rsList $ aScreen ui+  continue ui
Hledger/UI/TransactionScreen.hs view
@@ -53,7 +53,7 @@                                   ,tsAccount=acct}                               ,aMode=mode} =   case mode of-    Help       -> [helpDialog, maincontent]+    Help       -> [helpDialog copts, maincontent]     -- Minibuffer e -> [minibuffer e, maincontent]     _          -> [maincontent]   where@@ -78,7 +78,7 @@           where             togglefilters =               case concat [-                   uiShowClearedStatus $ clearedstatus_ ropts+                   uiShowStatus copts $ statuses_ ropts                   ,if real_ ropts then ["real"] else []                   ,if empty_ ropts then [] else ["nonzero"]                   ] of@@ -168,9 +168,9 @@         -- EvKey (KChar 'E') [] -> continue $ regenerateScreens j d $ stToggleEmpty ui         -- EvKey (KChar 'C') [] -> continue $ regenerateScreens j d $ stToggleCleared ui         -- EvKey (KChar 'R') [] -> continue $ regenerateScreens j d $ stToggleReal ui-        VtyEvent (EvKey k           []) | k `elem` [KUp, KChar 'k']   -> continue $ regenerateScreens j d ui{aScreen=s{tsTransaction=(iprev,tprev)}}-        VtyEvent (EvKey k           []) | k `elem` [KDown, KChar 'j'] -> continue $ regenerateScreens j d ui{aScreen=s{tsTransaction=(inext,tnext)}}-        VtyEvent (EvKey k           []) | k `elem` [KLeft, KChar 'h'] -> continue ui''+        VtyEvent e | e `elem` moveUpEvents   -> continue $ regenerateScreens j d ui{aScreen=s{tsTransaction=(iprev,tprev)}}+        VtyEvent e | e `elem` moveDownEvents -> continue $ regenerateScreens j d ui{aScreen=s{tsTransaction=(inext,tnext)}}+        VtyEvent e | e `elem` moveLeftEvents -> continue ui''           where             ui'@UIState{aScreen=scr} = popScreen ui             ui'' = ui'{aScreen=rsSelect (fromIntegral i) scr}
Hledger/UI/UIOptions.hs view
@@ -39,6 +39,15 @@   -- ,flagReq ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "with --flat, omit this many leading account name components"   -- ,flagReq  ["format"] (\s opts -> Right $ setopt "format" s opts) "FORMATSTR" "use this custom line format"   -- ,flagNone ["no-elide"] (\opts -> setboolopt "no-elide" opts) "don't compress empty parent accounts on one line"+  ,flagReq  ["status-toggles"] (\s opts -> Right $ setopt "status-toggles" s opts) "N"+    (intercalate "\n"+      ["choose how status toggles work:"+      ," 1 UPC toggles X/all"+      ," 2 UPC cycles X/not-X/all"+      ," 3 UPC toggles each X"+--      ," 4 upc sets X, UPC sets not-X"+--      ," 5 upc toggles X, UPC toggles not-X"+      ])  ]  --uimode :: Mode [([Char], [Char])]
Hledger/UI/UIState.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {- | UIState operations. -}  {-# LANGUAGE OverloadedStrings #-}@@ -6,7 +7,9 @@ module Hledger.UI.UIState where +#if !MIN_VERSION_brick(0,19,0) import Brick+#endif import Brick.Widgets.Edit import Data.List import Data.Text.Zipper (gotoEOL)@@ -17,30 +20,87 @@ import Hledger.UI.UITypes import Hledger.UI.UIOptions --- | Toggle between showing only cleared items or all items.-toggleCleared :: UIState -> UIState-toggleCleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =-  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleCleared ropts}}}-  where-    toggleCleared ropts@ReportOpts{clearedstatus_=Just Cleared} = ropts{clearedstatus_=Nothing}-    toggleCleared ropts = ropts{clearedstatus_=Just Cleared}+-- | Toggle between showing only unmarked items or all items.+toggleUnmarked :: UIState -> UIState+toggleUnmarked ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =+  ui{aopts=uopts{cliopts_=copts{reportopts_=reportOptsToggleStatusSomehow Unmarked copts ropts}}}  -- | Toggle between showing only pending items or all items. togglePending :: UIState -> UIState togglePending ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =-  ui{aopts=uopts{cliopts_=copts{reportopts_=togglePending ropts}}}-  where-    togglePending ropts@ReportOpts{clearedstatus_=Just Pending} = ropts{clearedstatus_=Nothing}-    togglePending ropts = ropts{clearedstatus_=Just Pending}+  ui{aopts=uopts{cliopts_=copts{reportopts_=reportOptsToggleStatusSomehow Pending copts ropts}}} --- | Toggle between showing only uncleared items or all items.-toggleUncleared :: UIState -> UIState-toggleUncleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =-  ui{aopts=uopts{cliopts_=copts{reportopts_=toggleUncleared ropts}}}+-- | Toggle between showing only cleared items or all items.+toggleCleared :: UIState -> UIState+toggleCleared ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =+  ui{aopts=uopts{cliopts_=copts{reportopts_=reportOptsToggleStatusSomehow Cleared copts ropts}}}++-- TODO testing different status toggle styles ++-- | Generate zero or more indicators of the status filters currently active, +-- which will be shown comma-separated as part of the indicators list.+uiShowStatus :: CliOpts -> [Status] -> [String]+uiShowStatus copts ss =+  case style of+    -- in style 2, instead of "Y, Z" show "not X" +    Just 2 | length ss == numstatuses-1 +      -> map (("not "++). showstatus) $ sort $ complement ss  -- should be just one+    _ -> map showstatus $ sort ss   where-    toggleUncleared ropts@ReportOpts{clearedstatus_=Just Uncleared} = ropts{clearedstatus_=Nothing}-    toggleUncleared ropts = ropts{clearedstatus_=Just Uncleared}+    numstatuses = length [minBound..maxBound::Status]+    style = maybeintopt "status-toggles" $ rawopts_ copts+    showstatus Cleared  = "cleared"+    showstatus Pending  = "pending"+    showstatus Unmarked = "unmarked" +reportOptsToggleStatusSomehow :: Status -> CliOpts -> ReportOpts -> ReportOpts+reportOptsToggleStatusSomehow s copts ropts =+  case maybeintopt "status-toggles" $ rawopts_ copts of +     Just 2 -> reportOptsToggleStatus2 s ropts+     Just 3 -> reportOptsToggleStatus3 s ropts+--     Just 4 -> reportOptsToggleStatus4 s ropts+--     Just 5 -> reportOptsToggleStatus5 s ropts+     _      -> reportOptsToggleStatus1 s ropts++-- 1 UPC toggles only X/all+reportOptsToggleStatus1 s ropts@ReportOpts{statuses_=ss}+  | ss == [s]  = ropts{statuses_=[]}+  | otherwise = ropts{statuses_=[s]}++-- 2 UPC cycles X/not-X/all+-- repeatedly pressing X cycles:+-- [] U [u]+-- [u] U [pc]+-- [pc] U []+-- pressing Y after first or second step starts new cycle:+-- [u] P [p]+-- [pc] P [p]+reportOptsToggleStatus2 s ropts@ReportOpts{statuses_=ss}+  | ss == [s]            = ropts{statuses_=complement [s]}+  | ss == complement [s] = ropts{statuses_=[]}+  | otherwise            = ropts{statuses_=[s]}  -- XXX assume only three values ++-- 3 UPC toggles each X+reportOptsToggleStatus3 s ropts@ReportOpts{statuses_=ss}+  | s `elem` ss = ropts{statuses_=filter (/= s) ss}+  | otherwise   = ropts{statuses_=simplifyStatuses (s:ss)}++-- 4 upc sets X, UPC sets not-X+--reportOptsToggleStatus4 s ropts@ReportOpts{statuses_=ss}+--  | s `elem` ss = ropts{statuses_=filter (/= s) ss}+--  | otherwise   = ropts{statuses_=simplifyStatuses (s:ss)}+--+-- 5 upc toggles X, UPC toggles not-X+--reportOptsToggleStatus5 s ropts@ReportOpts{statuses_=ss}+--  | s `elem` ss = ropts{statuses_=filter (/= s) ss}+--  | otherwise   = ropts{statuses_=simplifyStatuses (s:ss)}++-- | Given a list of unique enum values, list the other possible values of that enum.+complement :: (Bounded a, Enum a, Eq a) => [a] -> [a]+complement = ([minBound..maxBound] \\)++--+ -- | Toggle between showing all and showing only nonempty (more precisely, nonzero) items. toggleEmpty :: UIState -> UIState toggleEmpty ui@UIState{aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}} =@@ -125,7 +185,7 @@   ui{aopts=uopts{cliopts_=copts{reportopts_=ropts{      accountlistmode_=ALTree     ,empty_=True-    ,clearedstatus_=Nothing+    ,statuses_=[]     ,real_=False     ,query_=""     --,period_=PeriodAll@@ -178,7 +238,11 @@ showMinibuffer :: UIState -> UIState showMinibuffer ui = setMode (Minibuffer e) ui   where+#if MIN_VERSION_brick(0,19,0)+    e = applyEdit gotoEOL $ editor MinibufferEditor (Just 1) oldq+#else     e = applyEdit gotoEOL $ editor MinibufferEditor (str . unlines) (Just 1) oldq+#endif     oldq = query_ $ reportopts_ $ cliopts_ $ aopts ui  -- | Close the minibuffer, discarding any edit in progress.
Hledger/UI/UITypes.hs view
@@ -142,6 +142,7 @@ -- | An item in the register screen's list of transactions in the current account. data RegisterScreenItem = RegisterScreenItem {    rsItemDate           :: String           -- ^ date+  ,rsItemStatus         :: Status           -- ^ transaction status   ,rsItemDescription    :: String           -- ^ description   ,rsItemOtherAccounts  :: String           -- ^ other accounts   ,rsItemChangeAmount   :: String           -- ^ the change to the current account from this transaction
Hledger/UI/UIUtils.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {- | Rendering & misc. helpers. -}  {-# LANGUAGE OverloadedStrings #-}@@ -11,13 +12,16 @@ -- import Brick.Widgets.Center import Brick.Widgets.Dialog import Brick.Widgets.Edit+import Brick.Widgets.List import Data.List+import Data.Maybe import Data.Monoid-import Graphics.Vty (Event(..),Key(..),Color,Attr,currentAttr)+import Graphics.Vty (Event(..),Key(..),Modifier(..),Color,Attr,currentAttr) import Lens.Micro.Platform import System.Process -import Hledger+import Hledger hiding (Color)+import Hledger.Cli (CliOpts(rawopts_)) import Hledger.UI.UITypes import Hledger.UI.UIState @@ -28,16 +32,9 @@  -- ui -uiShowClearedStatus mc =- case mc of-   Just Cleared   -> ["cleared"]-   Just Pending   -> ["pending"]-   Just Uncleared -> ["uncleared"]-   Nothing        -> []- -- | Draw the help dialog, called when help mode is active.-helpDialog :: Widget Name-helpDialog =+helpDialog :: CliOpts -> Widget Name+helpDialog copts =   Widget Fixed Fixed $ do     c <- getContext     render $@@ -48,10 +45,11 @@               padLeftRight 1 $                 vBox [                    str "NAVIGATION"-                  ,renderKey ("UP/DOWN/k/j/PGUP/PGDN/HOME/END", "")+                  ,renderKey ("UP/DOWN/PGUP/PGDN/HOME/END", "")                   ,str "  move selection"-                  ,renderKey ("RIGHT/l", "more detail")-                  ,renderKey ("LEFT/h", "previous screen")+                  ,renderKey ("RIGHT", "more detail")+                  ,renderKey ("LEFT", "previous screen")+                  ,str "  (or vi/emacs movement keys)"                   ,renderKey ("ESC", "cancel / reset to top")                   ,str " "                   ,str "MISC"@@ -59,8 +57,9 @@                   ,renderKey ("a", "add transaction (hledger add)")                   ,renderKey ("A", "add transaction (hledger-iadd)")                   ,renderKey ("E", "open editor")-                  ,renderKey ("g", "reload data")                   ,renderKey ("I", "toggle balance assertions")+                  ,renderKey ("g", "reload data")+                  ,renderKey ("CTRL-l", "redraw & recenter")                   ,renderKey ("q", "quit")                   ,str " "                   ,str "MANUAL"@@ -77,8 +76,21 @@                   ,renderKey ("t", "set report period to today")                   ,str " "                   ,renderKey ("/", "set a filter query")-                  ,renderKey ("C", "toggle cleared/all")-                  ,renderKey ("U", "toggle uncleared/all")+                  ,renderKey ("U", +                    ["toggle unmarked/all"+                    ,"cycle unmarked/not unmarked/all"+                    ,"toggle unmarked filter"+                    ] !! (statusstyle-1))+                  ,renderKey ("P",+                    ["toggle pending/all"+                    ,"cycle pending/not pending/all"+                    ,"toggle pending filter"+                    ] !! (statusstyle-1))+                  ,renderKey ("C",+                    ["toggle cleared/all"+                    ,"cycle cleared/not cleared/all"+                    ,"toggle cleared filter"+                    ] !! (statusstyle-1))                   ,renderKey ("R", "toggle real/all")                   ,renderKey ("Z", "toggle nonzero/all")                   ,renderKey ("DEL/BS", "remove filters")@@ -109,12 +121,13 @@           ]   where     renderKey (key,desc) = withAttr (borderAttr <> "keys") (str key) <+> str " " <+> str desc+    statusstyle = min 3 $ fromMaybe 1 $ maybeintopt "status-toggles" $ rawopts_ copts   -- | Event handler used when help mode is active. helpHandle :: UIState -> BrickEvent Name AppEvent -> EventM Name (Next UIState) helpHandle ui ev =   case ev of-    VtyEvent (EvKey k []) | k `elem` [KEsc, KLeft, KChar 'h', KChar '?'] -> continue $ setMode Normal ui+    VtyEvent e | e `elem` (moveLeftEvents ++ [EvKey KEsc [], EvKey (KChar '?') []]) -> continue $ setMode Normal ui     VtyEvent (EvKey (KChar 't') []) -> suspendAndResume $ runHelp >> return ui'     VtyEvent (EvKey (KChar 'm') []) -> suspendAndResume $ runMan  >> return ui'     VtyEvent (EvKey (KChar 'i') []) -> suspendAndResume $ runInfo >> return ui'@@ -127,7 +140,11 @@ minibuffer ed =   forceAttr (borderAttr <> "minibuffer") $   hBox $+#if MIN_VERSION_brick(0,19,0)+  [txt "filter: ", renderEditor (str . unlines) True ed]+#else   [txt "filter: ", renderEditor True ed]+#endif  -- | Wrap a widget in the default hledger-ui screen layout. defaultLayout :: Widget Name -> Widget Name -> Widget Name -> Widget Name@@ -246,3 +263,44 @@ withBorderAttr :: Attr -> Widget Name -> Widget Name withBorderAttr attr = updateAttrMap (applyAttrMappings [(borderAttr, attr)]) +-- | 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.+continueWith :: EventM n () -> ui -> EventM n (Next ui)+continueWith brickaction ui = brickaction >> continue ui++-- | Scroll a list's viewport so that the selected item is centered in the+-- middle of the display area.+scrollToTop :: List Name e -> EventM Name ()+scrollToTop list = do+  let vpname = list^.listNameL+  setTop (viewportScroll vpname) 0 ++-- | Scroll a list's viewport so that the selected item is centered in the+-- middle of the display area.+scrollSelectionToMiddle :: List Name e -> EventM Name ()+scrollSelectionToMiddle list = do+  let mselectedrow = list^.listSelectedL +      vpname = list^.listNameL+  mvp <- lookupViewport vpname+  case (mselectedrow, mvp) of+    (Just selectedrow, Just vp) -> do+      let+        itemheight   = dbg4 "itemheight" $ list^.listItemHeightL+        vpheight     = dbg4 "vpheight" $ vp^.vpSize._2+        itemsperpage = dbg4 "itemsperpage" $ vpheight `div` itemheight+        toprow       = dbg4 "toprow" $ max 0 (selectedrow - (itemsperpage `div` 2)) -- assuming ViewportScroll's row offset is measured in list items not screen rows+      setTop (viewportScroll vpname) toprow +    _ -> return ()++--                 arrow keys       vi keys               emacs keys+moveUpEvents    = [EvKey KUp []   , EvKey (KChar 'k') [], EvKey (KChar 'p') [MCtrl]]+moveDownEvents  = [EvKey KDown [] , EvKey (KChar 'j') [], EvKey (KChar 'n') [MCtrl]]+moveLeftEvents  = [EvKey KLeft [] , EvKey (KChar 'h') [], EvKey (KChar 'b') [MCtrl]]+moveRightEvents = [EvKey KRight [], EvKey (KChar 'l') [], EvKey (KChar 'f') [MCtrl]]++normaliseMovementKeys ev+  | ev `elem` moveUpEvents    = EvKey KUp []+  | ev `elem` moveDownEvents  = EvKey KDown []+  | ev `elem` moveLeftEvents  = EvKey KLeft []+  | ev `elem` moveRightEvents = EvKey KRight []+  | otherwise = ev
doc/hledger-ui.1 view
@@ -1,5 +1,5 @@ -.TH "hledger\-ui" "1" "March 2017" "hledger\-ui 1.2" "hledger User Manuals"+.TH "hledger\-ui" "1" "June 2017" "hledger\-ui 1.3" "hledger User Manuals"   @@ -146,18 +146,18 @@ .RS .RE .TP-.B \f[C]\-C\ \-\-cleared\f[]-include only cleared postings/txns+.B \f[C]\-U\ \-\-unmarked\f[]+include only unmarked postings/txns (can combine with \-P or \-C) .RS .RE .TP-.B \f[C]\-\-pending\f[]+.B \f[C]\-P\ \-\-pending\f[] include only pending postings/txns .RS .RE .TP-.B \f[C]\-U\ \-\-uncleared\f[]-include only uncleared (and pending) postings/txns+.B \f[C]\-C\ \-\-cleared\f[]+include only cleared postings/txns .RS .RE .TP@@ -232,8 +232,9 @@ deeper, \f[C]left\f[] returns to the previous screen, \f[C]up\f[]/\f[C]down\f[]/\f[C]page\ up\f[]/\f[C]page\ down\f[]/\f[C]home\f[]/\f[C]end\f[] move up and down through lists.-Vi\-style \f[C]h\f[]/\f[C]j\f[]/\f[C]k\f[]/\f[C]l\f[] movement keys are-also supported.+Vi\-style (\f[C]h\f[]/\f[C]j\f[]/\f[C]k\f[]/\f[C]l\f[]) and Emacs\-style+(\f[C]CTRL\-p\f[]/\f[C]CTRL\-n\f[]/\f[C]CTRL\-f\f[]/\f[C]CTRL\-b\f[])+movement keys are also supported. A tip: movement speed is limited by your keyboard repeat rate, to move faster you may want to adjust it. (If you\[aq]re on a mac, the Karabiner app is one way to do that.)@@ -255,13 +256,17 @@ While editing the query, you can use CTRL\-a/e/d/k, BS, cursor keys; press \f[C]ENTER\f[] to set it, or \f[C]ESCAPE\f[]to cancel. There are also keys for quickly adjusting some common filters like-account depth and cleared/uncleared (see below).+account depth and transaction status (see below). \f[C]BACKSPACE\f[] or \f[C]DELETE\f[] removes all filters, showing all transactions. .PP \f[C]ESCAPE\f[] removes all filters and jumps back to the top screen. Or, it cancels a minibuffer edit or help dialog in progress. .PP+\f[C]CTRL\-l\f[] redraws the screen and centers the selection if+possible (selections near the top won\[aq]t be centered, since we+don\[aq]t scroll above the top).+.PP \f[C]g\f[] reloads from the data file(s) and updates the current screen and any previous screens. (With large files, this could cause a noticeable pause.)@@ -322,10 +327,13 @@ they show the change in balance during the report period. They are more useful eg when viewing a time log. .PP-\f[C]C\f[] toggles cleared mode, in which uncleared transactions and-postings are not shown.-\f[C]U\f[] toggles uncleared mode, in which only uncleared-transactions/postings are shown.+\f[C]U\f[] toggles filtering by unmarked status, including or excluding+unmarked postings in the balances.+Similarly, \f[C]P\f[] toggles pending postings, and \f[C]C\f[] toggles+cleared postings.+(By default, balances include all postings; if you activate one or two+status filters, only those postings are included; and if you activate+all three, the filter is removed.) .PP \f[C]R\f[] toggles real mode, in which virtual postings are ignored. .PP@@ -367,10 +375,13 @@ for the period balance shown on the accounts screen. As on the accounts screen, this can be toggled with \f[C]F\f[]. .PP-\f[C]C\f[] toggles cleared mode, in which uncleared transactions and-postings are not shown.-\f[C]U\f[] toggles uncleared mode, in which only uncleared-transactions/postings are shown.+\f[C]U\f[] toggles filtering by unmarked status, showing or hiding+unmarked transactions.+Similarly, \f[C]P\f[] toggles pending transactions, and \f[C]C\f[]+toggles cleared transactions.+(By default, transactions with all statuses are shown; if you activate+one or two status filters, only those transactions are shown; and if you+activate all three, the filter is removed.)q .PP \f[C]R\f[] toggles real mode, in which virtual postings are ignored. .PP
doc/hledger-ui.1.info view
@@ -3,7 +3,7 @@  File: hledger-ui.1.info,  Node: Top,  Next: OPTIONS,  Up: (dir) -hledger-ui(1) hledger-ui 1.2+hledger-ui(1) hledger-ui 1.3 ****************************  hledger-ui is hledger's curses-style interface, providing an efficient@@ -104,15 +104,15 @@ '--date2'       show, and match with -b/-e/-p/date:, secondary dates instead-'-C --cleared'+'-U --unmarked' -     include only cleared postings/txns-'--pending'+     include only unmarked postings/txns (can combine with -P or -C)+'-P --pending'       include only pending postings/txns-'-U --uncleared'+'-C --cleared' -     include only uncleared (and pending) postings/txns+     include only cleared postings/txns '-R --real'       include only non-virtual postings@@ -167,10 +167,10 @@    The cursor keys navigate: 'right' (or 'enter') goes deeper, 'left' returns to the previous screen, 'up'/'down'/'page up'/'page down'/'home'/'end' move up and down through lists.  Vi-style-'h'/'j'/'k'/'l' movement keys are also supported.  A tip: movement speed-is limited by your keyboard repeat rate, to move faster you may want to-adjust it.  (If you're on a mac, the Karabiner app is one way to do-that.)+('h'/'j'/'k'/'l') and Emacs-style ('CTRL-p'/'CTRL-n'/'CTRL-f'/'CTRL-b')+movement keys are also supported.  A tip: movement speed is limited by+your keyboard repeat rate, to move faster you may want to adjust it.+(If you're on a mac, the Karabiner app is one way to do that.)     With shift pressed, the cursor keys adjust the report period, limiting the transactions to be shown (by default, all are shown).@@ -186,13 +186,17 @@ using the same query terms as in hledger and hledger-web.  While editing the query, you can use CTRL-a/e/d/k, BS, cursor keys; press 'ENTER' to set it, or 'ESCAPE'to cancel.  There are also keys for quickly adjusting-some common filters like account depth and cleared/uncleared (see+some common filters like account depth and transaction status (see below).  'BACKSPACE' or 'DELETE' removes all filters, showing all transactions.     'ESCAPE' removes all filters and jumps back to the top screen.  Or, it cancels a minibuffer edit or help dialog in progress. +   'CTRL-l' redraws the screen and centers the selection if possible+(selections near the top won't be centered, since we don't scroll above+the top).+    'g' reloads from the data file(s) and updates the current screen and any previous screens.  (With large files, this could cause a noticeable pause.)@@ -260,9 +264,12 @@ 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 are not shown.  'U' toggles uncleared mode, in which only-uncleared transactions/postings are shown.+   'U' toggles filtering by unmarked status, including or excluding+unmarked postings in the balances.  Similarly, 'P' toggles pending+postings, and 'C' toggles cleared postings.  (By default, balances+include all postings; if you activate one or two status filters, only+those postings are included; and if you activate all three, the filter+is removed.)     'R' toggles real mode, in which virtual postings are ignored. @@ -304,9 +311,12 @@ 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 are not shown.  'U' toggles uncleared mode, in which only-uncleared transactions/postings are shown.+   'U' toggles filtering by unmarked status, showing or hiding unmarked+transactions.  Similarly, 'P' toggles pending transactions, and 'C'+toggles cleared transactions.  (By default, transactions with all+statuses are shown; if you activate one or two status filters, only+those transactions are shown; and if you activate all three, the filter+is removed.)q     'R' toggles real mode, in which virtual postings are ignored. @@ -357,17 +367,17 @@ Node: Top73 Node: OPTIONS825 Ref: #options924-Node: KEYS3650-Ref: #keys3747-Node: SCREENS6335-Ref: #screens6422-Node: Accounts screen6512-Ref: #accounts-screen6642-Node: Register screen8691-Ref: #register-screen8848-Node: Transaction screen10737-Ref: #transaction-screen10897-Node: Error screen11767-Ref: #error-screen11891+Node: KEYS3665+Ref: #keys3762+Node: SCREENS6558+Ref: #screens6645+Node: Accounts screen6735+Ref: #accounts-screen6865+Node: Register screen9095+Ref: #register-screen9252+Node: Transaction screen11326+Ref: #transaction-screen11486+Node: Error screen12356+Ref: #error-screen12480  End Tag Table
doc/hledger-ui.1.txt view
@@ -101,14 +101,14 @@        --date2               show, and match with -b/-e/-p/date:, secondary dates instead -       -C --cleared-              include only cleared postings/txns+       -U --unmarked+              include only unmarked postings/txns (can combine with -P or -C) -       --pending+       -P --pending               include only pending postings/txns -       -U --uncleared-              include only uncleared (and pending) postings/txns+       -C --cleared+              include only cleared postings/txns         -R --real               include only non-virtual postings@@ -151,45 +151,50 @@         The cursor keys navigate: right (or enter) goes deeper, left returns to        the previous screen,  up/down/page up/page down/home/end  move  up  and-       down through lists.  Vi-style h/j/k/l movement keys are also supported.-       A tip: movement speed is limited by your keyboard repeat rate, to  move-       faster  you  may want to adjust it.  (If you're on a mac, the Karabiner-       app is one way to do that.)+       down    through    lists.     Vi-style    (h/j/k/l)   and   Emacs-style+       (CTRL-p/CTRL-n/CTRL-f/CTRL-b) movement keys are also supported.  A tip:+       movement  speed is limited by your keyboard repeat rate, to move faster+       you may want to adjust it.  (If you're on a mac, the Karabiner  app  is+       one way to do that.) -       With shift pressed, the cursor keys adjust the report period,  limiting-       the   transactions   to   be   shown   (by  default,  all  are  shown).-       shift-down/up steps downward and upward through these  standard  report+       With  shift pressed, the cursor keys adjust the report period, limiting+       the  transactions  to  be  shown   (by   default,   all   are   shown).+       shift-down/up  steps  downward and upward through these standard report        period   durations:   year,   quarter,   month,   week,   day.    Then,-       shift-left/right moves to the previous/next period.  t sets the  report-       period  to  today.   With  the --watch option, when viewing a "current"-       period (the current day, week, month, quarter,  or  year),  the  period-       will  move automatically to track the current date.  To set a non-stan-+       shift-left/right  moves to the previous/next period.  t sets the report+       period to today.  With the --watch option,  when  viewing  a  "current"+       period  (the  current  day,  week, month, quarter, or year), the period+       will move automatically to track the current date.  To set a  non-stan-        dard period, you can use / and a date: query. -       / lets you set a general filter query limiting the  data  shown,  using-       the  same query terms as in hledger and hledger-web.  While editing the-       query, you can use CTRL-a/e/d/k, BS, cursor keys; press  ENTER  to  set+       /  lets  you  set a general filter query limiting the data shown, using+       the same query terms as in hledger and hledger-web.  While editing  the+       query,  you  can  use CTRL-a/e/d/k, BS, cursor keys; press ENTER to set        it, or ESCAPEto cancel.  There are also keys for quickly adjusting some-       common filters like account depth and  cleared/uncleared  (see  below).+       common  filters  like account depth and transaction status (see below).        BACKSPACE or DELETE removes all filters, showing all transactions. -       ESCAPE  removes  all  filters and jumps back to the top screen.  Or, it+       ESCAPE removes all filters and jumps back to the top  screen.   Or,  it        cancels a minibuffer edit or help dialog in progress. -       g reloads from the data file(s) and updates the current screen and  any-       previous  screens.   (With  large  files, this could cause a noticeable+       CTRL-l redraws the screen and centers the selection if possible (selec-+       tions near the top won't be centered, since we don't scroll  above  the+       top).++       g  reloads from the data file(s) and updates the current screen and any+       previous screens.  (With large files, this  could  cause  a  noticeable        pause.) -       I toggles balance assertion  checking.   Disabling  balance  assertions+       I  toggles  balance  assertion  checking.  Disabling balance assertions        temporarily can be useful for troubleshooting. -       a  runs  command-line  hledger's  add  command, and reloads the updated+       a runs command-line hledger's add  command,  and  reloads  the  updated        file.  This allows some basic data entry. -       E  runs  $HLEDGER_UI_EDITOR,  or   $EDITOR,   or   a   default   (emac-+       E   runs   $HLEDGER_UI_EDITOR,   or   $EDITOR,   or  a  default  (emac-        sclient -a "" -nw) on the journal file.  With some editors (emacs, vi),-       the cursor will be positioned at the current transaction  when  invoked-       from  the  register  and transaction screens, and at the error location+       the  cursor  will be positioned at the current transaction when invoked+       from the register and transaction screens, and at  the  error  location        (if possible) when invoked from the error screen.         q quits the application.@@ -198,42 +203,44 @@  SCREENS    Accounts screen-       This is normally the first screen displayed.   It  lists  accounts  and-       their  balances,  like hledger's balance command.  By default, it shows-       all accounts and their latest ending balances (including  the  balances-       of  subaccounts).  if you specify a query on the command line, it shows+       This  is  normally  the  first screen displayed.  It lists accounts and+       their balances, like hledger's balance command.  By default,  it  shows+       all  accounts  and their latest ending balances (including the balances+       of subaccounts).  if you specify a query on the command line, it  shows        just the matched accounts and the balances from matched transactions. -       Account names are normally indented to show the hierarchy (tree  mode).+       Account  names are normally indented to show the hierarchy (tree mode).        To see less detail, set a depth limit by pressing a number key, 1 to 9.        0 shows even less detail, collapsing all accounts to a single total.  --       and  +  (or  =)  decrease  and increase the depth limit.  To remove the-       depth limit, set it higher than the maximum  account  depth,  or  press+       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, 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 com-+       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  com-        mand).         H toggles between showing historical balances or period balances.  His--       torical  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+       torical 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-       are  not  shown.   U  toggles  uncleared  mode, in which only uncleared-       transactions/postings are shown.+       U toggles filtering by unmarked status, including or excluding unmarked+       postings in the balances.  Similarly, P toggles pending postings, and C+       toggles  cleared postings.  (By default, balances include all postings;+       if you activate one or two status  filters,  only  those  postings  are+       included; and if you activate all three, the filter is removed.)         R toggles real mode, in which virtual postings are ignored. -       Z toggles nonzero mode, in which only accounts  with  nonzero  balances-       are  shown (hledger-ui shows zero items by default, unlike command-line+       Z  toggles  nonzero  mode, in which only accounts with nonzero balances+       are shown (hledger-ui shows zero items by default, unlike  command-line        hledger).         Press right or enter to view an account's transactions register.@@ -242,32 +249,35 @@        This screen shows the transactions affecting a particular account, like        a check register.  Each line represents one transaction and shows: -       o the  other  account(s)  involved, in abbreviated form.  (If there are-         both real and virtual postings, it shows only the  accounts  affected+       o the other account(s) involved, in abbreviated form.   (If  there  are+         both  real  and virtual postings, it shows only the accounts affected          by real postings.) -       o the  overall change to the current account's balance; positive for an+       o the overall change to the current account's balance; positive for  an          inflow to this account, negative for an outflow.         o 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+         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+         a filter query, it will be the running historical balance  you  would          see on a bank register for the current account. -       If the accounts screen was in  tree  mode,  the  register  screen  will+       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+       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 trans--       actions  responsible  for  the  period  balance  shown  on the accounts+       actions 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-       are  not  shown.   U  toggles  uncleared  mode, in which only uncleared-       transactions/postings are shown.+       U  toggles  filtering  by  unmarked  status, showing or hiding unmarked+       transactions.  Similarly, P toggles pending transactions, and C toggles+       cleared  transactions.  (By default, transactions with all statuses are+       shown; if you activate one or two status filters, only  those  transac-+       tions  are  shown;  and  if  you  activate  all  three,  the  filter is+       removed.)q         R toggles real mode, in which virtual postings are ignored. @@ -359,4 +369,4 @@   -hledger-ui 1.2                    March 2017                     hledger-ui(1)+hledger-ui 1.3                     June 2017                     hledger-ui(1)
hledger-ui.cabal view
@@ -3,7 +3,7 @@ -- see: https://github.com/sol/hpack  name:           hledger-ui-version:        1.2+version:        1.3 synopsis:       Curses-style user interface for the hledger accounting tool description:    This is hledger's curses-style interface.                 It is simpler and more convenient for browsing data than the command-line interface,@@ -55,10 +55,10 @@   hs-source-dirs:       ./.   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans-  cpp-options: -DVERSION="1.2"+  cpp-options: -DVERSION="1.3"   build-depends:-      hledger >= 1.2 && < 1.3-    , hledger-lib >= 1.2 && < 1.3+      hledger >= 1.3+    , hledger-lib >= 1.3     , ansi-terminal >= 0.6.2.3 && < 0.7     , async     , base >= 4.8 && < 5@@ -72,7 +72,7 @@     , HUnit     , microlens >= 0.4 && < 0.5     , microlens-platform >= 0.2.3.1 && < 0.4-    , megaparsec >=5.0 && < 5.3+    , megaparsec >=5.0 && < 5.4     , pretty-show >=1.6.4     , process >= 1.2     , safe >= 0.2@@ -85,7 +85,7 @@     buildable: False   else     build-depends:-        brick >= 0.12 && < 0.18+        brick >= 0.12 && < 0.20       , vty >= 5.5 && < 5.16   if flag(threaded)     ghc-options: -threaded