ui: acc: preserve account selection across reloads
This commit is contained in:
		
							parent
							
								
									423934b2b8
								
							
						
					
					
						commit
						45db0a28cc
					
				| @ -36,31 +36,30 @@ import qualified Hledger.UI.RegisterScreen as RS (screen) | |||||||
| import qualified Hledger.UI.ErrorScreen as ES (screen) | import qualified Hledger.UI.ErrorScreen as ES (screen) | ||||||
| 
 | 
 | ||||||
| screen = AccountsScreen{ | screen = AccountsScreen{ | ||||||
|    asState  = list "accounts" V.empty 1 |    asState  = (list "accounts" V.empty 1, "") | ||||||
|   ,sInitFn    = initAccountsScreen Nothing |   ,sInitFn    = initAccountsScreen | ||||||
|   ,sDrawFn    = drawAccountsScreen |   ,sDrawFn    = drawAccountsScreen | ||||||
|   ,sHandleFn = handleAccountsScreen |   ,sHandleFn = handleAccountsScreen | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| initAccountsScreen :: Maybe AccountName -> Day -> AppState -> AppState | initAccountsScreen :: Day -> AppState -> AppState | ||||||
| initAccountsScreen mselacct d st@AppState{ | initAccountsScreen d st@AppState{ | ||||||
|   aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}, |   aopts=uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}}, | ||||||
|   ajournal=j, |   ajournal=j, | ||||||
|   aScreen=s@AccountsScreen{} |   aScreen=s@AccountsScreen{asState=(_,selacct)} | ||||||
|   } = |   } = | ||||||
|   st{aopts=uopts', aScreen=s{asState=l'}} |   st{aopts=uopts', aScreen=s{asState=(l',selacct)}} | ||||||
|    where |    where | ||||||
|     l = list (Name "accounts") (V.fromList displayitems) 1 |     l = list (Name "accounts") (V.fromList displayitems) 1 | ||||||
| 
 | 
 | ||||||
|     -- hacky: when we're adjusting depth, mselacct is the account that was selected previously, |     -- keep the selection near the last known selected account if possible | ||||||
|     -- in which case try and keep the selection near where it was |     l' | null selacct = l | ||||||
|     l' = case mselacct of |        | otherwise = maybe l (flip listMoveTo l) midx | ||||||
|              Nothing -> l |       where | ||||||
|              Just a  -> -- vScrollToBeginning $ viewportScroll "accounts" |         midx = findIndex (\((a,_,_),_) -> a==selacctclipped) items | ||||||
|                            maybe l (flip listMoveTo l) mi |         selacctclipped = case depth_ ropts of | ||||||
|                where |                           Nothing -> selacct | ||||||
|                  mi = findIndex (\((acct,_,_),_) -> acct==a') items |                           Just d  -> clipAccountName d selacct | ||||||
|                  a' = maybe a (flip clipAccountName a) $ depth_ ropts |  | ||||||
| 
 | 
 | ||||||
|     uopts' = uopts{cliopts_=copts{reportopts_=ropts'}} |     uopts' = uopts{cliopts_=copts{reportopts_=ropts'}} | ||||||
|     ropts' = ropts { |     ropts' = ropts { | ||||||
| @ -92,10 +91,10 @@ initAccountsScreen mselacct d st@AppState{ | |||||||
|     displayitems = map displayitem items |     displayitems = map displayitem items | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| initAccountsScreen _ _ _ = error "init function called with wrong screen type, should not happen" | initAccountsScreen _ _ = error "init function called with wrong screen type, should not happen" | ||||||
| 
 | 
 | ||||||
| drawAccountsScreen :: AppState -> [Widget] | drawAccountsScreen :: AppState -> [Widget] | ||||||
| drawAccountsScreen _st@AppState{aopts=uopts, ajournal=j, aScreen=AccountsScreen{asState=l}} = | drawAccountsScreen _st@AppState{aopts=uopts, ajournal=j, aScreen=AccountsScreen{asState=(l,_)}} = | ||||||
|   [ui] |   [ui] | ||||||
|     where |     where | ||||||
|       toplabel = files |       toplabel = files | ||||||
| @ -196,7 +195,7 @@ drawAccountsItem (acctwidth, balwidth) selected (indent, _fullacct, displayacct, | |||||||
| 
 | 
 | ||||||
| handleAccountsScreen :: AppState -> Vty.Event -> EventM (Next AppState) | handleAccountsScreen :: AppState -> Vty.Event -> EventM (Next AppState) | ||||||
| handleAccountsScreen st@AppState{ | handleAccountsScreen st@AppState{ | ||||||
|   aScreen=scr@AccountsScreen{asState=l} |   aScreen=scr@AccountsScreen{asState=(l,selacct)} | ||||||
|   ,aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} |   ,aopts=UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} | ||||||
|   ,ajournal=j |   ,ajournal=j | ||||||
|   } e = do |   } e = do | ||||||
| @ -204,63 +203,59 @@ handleAccountsScreen st@AppState{ | |||||||
|     -- c <- getContext |     -- c <- getContext | ||||||
|     -- let h = c^.availHeightL |     -- let h = c^.availHeightL | ||||||
|     --     moveSel n l = listMoveBy n l |     --     moveSel n l = listMoveBy n l | ||||||
|     let |  | ||||||
|       acct = case listSelectedElement l of |  | ||||||
|               Just (_, (_, fullacct, _, _)) -> fullacct |  | ||||||
|               Nothing -> "" |  | ||||||
| 
 | 
 | ||||||
|       -- Customize reload to preserve the account selection while reloading. |     -- before we go anywhere, remember the currently selected account. | ||||||
|       -- XXX reloads only the current screen, not previous ones - ok for now as accounts screen is always the first |     -- (This is preserved across screen changes, unlike List's selection state) | ||||||
|       -- XXX won't have any effect when this screen is reloaded by a deeper screen's reload - should move selected acct into state |     let | ||||||
|       reload' = initAccountsScreen (Just acct) |       selacct' = case listSelectedElement l of | ||||||
|  |                   Just (_, (_, fullacct, _, _)) -> fullacct | ||||||
|  |                   Nothing -> selacct | ||||||
|  |       st' = st{aScreen=scr{asState=(l,selacct')}} | ||||||
| 
 | 
 | ||||||
|     case e of |     case e of | ||||||
|         Vty.EvKey Vty.KEsc []        -> halt st |         Vty.EvKey Vty.KEsc []        -> halt st' | ||||||
|         Vty.EvKey (Vty.KChar 'q') [] -> halt st |         Vty.EvKey (Vty.KChar 'q') [] -> halt st' | ||||||
|         -- Vty.EvKey (Vty.KChar 'l') [Vty.MCtrl] -> do |         -- Vty.EvKey (Vty.KChar 'l') [Vty.MCtrl] -> do | ||||||
| 
 | 
 | ||||||
|         Vty.EvKey (Vty.KChar 'g') [] -> do |         Vty.EvKey (Vty.KChar 'g') [] -> do | ||||||
|           ej <- liftIO $ journalReload j  -- (ej, changed) <- liftIO $ journalReloadIfChanged copts j |           ej <- liftIO $ journalReload j  -- (ej, changed) <- liftIO $ journalReloadIfChanged copts j | ||||||
|           case ej of |           case ej of | ||||||
|             Right j' -> continue $ reload j' d st |             Right j' -> continue $ reload j' d st' | ||||||
|             Left err -> continue $ screenEnter d ES.screen{esState=err} st |             Left err -> continue $ screenEnter d ES.screen{esState=err} st' | ||||||
| 
 | 
 | ||||||
|         Vty.EvKey (Vty.KChar '-') [] -> continue $ reload' d $ decDepth st |         Vty.EvKey (Vty.KChar '-') [] -> continue $ reload j d $ decDepth st' | ||||||
|         Vty.EvKey (Vty.KChar '+') [] -> continue $ reload' d $ incDepth st |         Vty.EvKey (Vty.KChar '+') [] -> continue $ reload j d $ incDepth st' | ||||||
|         Vty.EvKey (Vty.KChar '=') [] -> continue $ reload' d $ incDepth st |         Vty.EvKey (Vty.KChar '=') [] -> continue $ reload j d $ incDepth st' | ||||||
|         Vty.EvKey (Vty.KChar '1') [] -> continue $ reload' d $ setDepth 1 st |         Vty.EvKey (Vty.KChar '1') [] -> continue $ reload j d $ setDepth 1 st' | ||||||
|         Vty.EvKey (Vty.KChar '2') [] -> continue $ reload' d $ setDepth 2 st |         Vty.EvKey (Vty.KChar '2') [] -> continue $ reload j d $ setDepth 2 st' | ||||||
|         Vty.EvKey (Vty.KChar '3') [] -> continue $ reload' d $ setDepth 3 st |         Vty.EvKey (Vty.KChar '3') [] -> continue $ reload j d $ setDepth 3 st' | ||||||
|         Vty.EvKey (Vty.KChar '4') [] -> continue $ reload' d $ setDepth 4 st |         Vty.EvKey (Vty.KChar '4') [] -> continue $ reload j d $ setDepth 4 st' | ||||||
|         Vty.EvKey (Vty.KChar '5') [] -> continue $ reload' d $ setDepth 5 st |         Vty.EvKey (Vty.KChar '5') [] -> continue $ reload j d $ setDepth 5 st' | ||||||
|         Vty.EvKey (Vty.KChar '6') [] -> continue $ reload' d $ setDepth 6 st |         Vty.EvKey (Vty.KChar '6') [] -> continue $ reload j d $ setDepth 6 st' | ||||||
|         Vty.EvKey (Vty.KChar '7') [] -> continue $ reload' d $ setDepth 7 st |         Vty.EvKey (Vty.KChar '7') [] -> continue $ reload j d $ setDepth 7 st' | ||||||
|         Vty.EvKey (Vty.KChar '8') [] -> continue $ reload' d $ setDepth 8 st |         Vty.EvKey (Vty.KChar '8') [] -> continue $ reload j d $ setDepth 8 st' | ||||||
|         Vty.EvKey (Vty.KChar '9') [] -> continue $ reload' d $ setDepth 9 st |         Vty.EvKey (Vty.KChar '9') [] -> continue $ reload j d $ setDepth 9 st' | ||||||
|         Vty.EvKey (Vty.KChar '0') [] -> continue $ reload' d $ setDepth 0 st |         Vty.EvKey (Vty.KChar '0') [] -> continue $ reload j d $ setDepth 0 st' | ||||||
|         Vty.EvKey (Vty.KChar 'f') [] -> continue $ reload' d $ st' |         Vty.EvKey (Vty.KChar 'f') [] -> continue $ reload j d $ st'' | ||||||
|           where |           where | ||||||
|             st' = st{ |             st'' = st'{ | ||||||
|               aopts=(aopts st){ |               aopts=(aopts st'){ | ||||||
|                  cliopts_=copts{ |                  cliopts_=copts{ | ||||||
|                     reportopts_=toggleFlatMode ropts |                     reportopts_=toggleFlatMode ropts | ||||||
|                     } |                     } | ||||||
|                  } |                  } | ||||||
|               } |               } | ||||||
|         Vty.EvKey (Vty.KLeft) []     -> continue $ popScreen st |         Vty.EvKey (Vty.KLeft) []     -> continue $ popScreen st' | ||||||
|         Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do |         Vty.EvKey (k) [] | k `elem` [Vty.KRight, Vty.KEnter] -> do | ||||||
|           let st' = screenEnter d RS.screen{rsAcct=acct} st |           let st'' = screenEnter d RS.screen{rsAcct=selacct'} st' | ||||||
|           vScrollToBeginning $ viewportScroll "register" |           vScrollToBeginning $ viewportScroll "register" | ||||||
|           continue st' |           continue st'' | ||||||
| 
 |  | ||||||
|         -- Vty.EvKey (Vty.KPageDown) [] -> continue $ st{aScreen=scr{asState=moveSel h l}} |  | ||||||
|         -- Vty.EvKey (Vty.KPageUp) []   -> continue $ st{aScreen=scr{asState=moveSel (-h) l}} |  | ||||||
| 
 | 
 | ||||||
|         -- fall through to the list's event handler (handles up/down) |         -- fall through to the list's event handler (handles up/down) | ||||||
|         ev                       -> do |         ev                       -> do | ||||||
|                                      l' <- handleEvent ev l |                                      l' <- handleEvent ev l | ||||||
|                                      continue $ st{aScreen=scr{asState=l'}} |                                      continue $ st'{aScreen=scr{asState=(l',selacct')}} | ||||||
|                                  -- continue =<< handleEventLensed st someLens ev |                                  -- continue =<< handleEventLensed st' someLens ev | ||||||
| handleAccountsScreen _ _ = error "event handler called with wrong screen type, should not happen" | handleAccountsScreen _ _ = error "event handler called with wrong screen type, should not happen" | ||||||
| 
 | 
 | ||||||
| -- | Toggle between flat and tree mode. If in the third "default" mode, go to flat mode. | -- | Toggle between flat and tree mode. If in the third "default" mode, go to flat mode. | ||||||
|  | |||||||
| @ -102,14 +102,17 @@ runBrickUi uopts@UIOpts{cliopts_=copts@CliOpts{reportopts_=ropts}} j = do | |||||||
|           -- Initialising the accounts screen is awkward, requiring |           -- Initialising the accounts screen is awkward, requiring | ||||||
|           -- another temporary AppState value.. |           -- another temporary AppState value.. | ||||||
|           ascr' = aScreen $ |           ascr' = aScreen $ | ||||||
|                   AS.initAccountsScreen (Just acct) d  -- acct will be selected |                   AS.initAccountsScreen d $ | ||||||
|                   AppState{ |                   AppState{ | ||||||
|                     aopts=uopts' |                     aopts=uopts' | ||||||
|                    ,ajournal=j |                    ,ajournal=j | ||||||
|                    ,aScreen=AS.screen |                    ,aScreen=setAccountsScreenSelection acct AS.screen | ||||||
|                    ,aPrevScreens=[] |                    ,aPrevScreens=[] | ||||||
|                    } |                    } | ||||||
| 
 |           -- ugh | ||||||
|  |           setAccountsScreenSelection a scr@AccountsScreen{asState=(l,_)} = scr{asState=(l,a)} | ||||||
|  |           setAccountsScreenSelection _ scr = scr | ||||||
|  |    | ||||||
|     st = (sInitFn scr) d |     st = (sInitFn scr) d | ||||||
|          AppState{ |          AppState{ | ||||||
|             aopts=uopts' |             aopts=uopts' | ||||||
|  | |||||||
| @ -25,13 +25,15 @@ data AppState = AppState { | |||||||
| -- of their state (which must have unique accessor names). | -- of their state (which must have unique accessor names). | ||||||
| data Screen = | data Screen = | ||||||
|     AccountsScreen { |     AccountsScreen { | ||||||
|      asState :: List (Int,String,String,[String])  -- ^ list of (indent level, full account name, full or short account name to display, rendered amounts) |      asState :: (List (Int,String,String,[String]), AccountName)  -- ^ list widget holding (indent level, full account name, full or short account name to display, rendered amounts); | ||||||
|  |                                                                   --   the currently selected account's full name (or "") | ||||||
|     ,sInitFn :: Day -> AppState -> AppState                         -- ^ function to initialise the screen's state on entry |     ,sInitFn :: Day -> AppState -> AppState                         -- ^ function to initialise the screen's state on entry | ||||||
|     ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState) -- ^ brick event handler to use for this screen |     ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState) -- ^ brick event handler to use for this screen | ||||||
|     ,sDrawFn :: AppState -> [Widget]                                -- ^ brick renderer to use for this screen |     ,sDrawFn :: AppState -> [Widget]                                -- ^ brick renderer to use for this screen | ||||||
|     } |     } | ||||||
|   | RegisterScreen { |   | RegisterScreen { | ||||||
|      rsState :: List (String,String,String,String,String) -- ^ list of (date, description, other accts, change amt, balance amt) |      rsState :: List (String,String,String,String,String) -- ^ list widget holding (date, description, other accts, change amt, balance amt) | ||||||
|  |      -- XXX move into rsState ? | ||||||
|     ,rsAcct :: AccountName              -- ^ the account we are showing a register for |     ,rsAcct :: AccountName              -- ^ the account we are showing a register for | ||||||
|     ,sInitFn :: Day -> AppState -> AppState |     ,sInitFn :: Day -> AppState -> AppState | ||||||
|     ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState) |     ,sHandleFn :: AppState -> V.Event -> EventM (Next AppState) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user