From 97964eb2fca848888a99d70d0cc047436082fa43 Mon Sep 17 00:00:00 2001 From: Simon Michael Date: Fri, 30 Jun 2017 16:37:10 +0100 Subject: [PATCH] ui: consistently support vi & emacs movement keys hjkl and CTRL-bfnp should now work wherever unmodified arrow keys work. (You must still use arrow keys with SHIFT for adjusting report period). --- hledger-ui/Hledger/UI/AccountsScreen.hs | 23 ++++++++------------- hledger-ui/Hledger/UI/RegisterScreen.hs | 16 ++++++--------- hledger-ui/Hledger/UI/TransactionScreen.hs | 6 +++--- hledger-ui/Hledger/UI/UIUtils.hs | 24 +++++++++++++++++----- 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/hledger-ui/Hledger/UI/AccountsScreen.hs b/hledger-ui/Hledger/UI/AccountsScreen.hs index 757841de1..01745c3bf 100644 --- a/hledger-ui/Hledger/UI/AccountsScreen.hs +++ b/hledger-ui/Hledger/UI/AccountsScreen.hs @@ -336,14 +336,13 @@ asHandle ui0@UIState{ 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 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 (EvKey k []) - | k `elem` [KRight, KChar 'l'] - , not $ isBlankElement $ listSelectedElement _asList-> + 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])) $ @@ -374,17 +373,11 @@ asHandle ui0@UIState{ 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 diff --git a/hledger-ui/Hledger/UI/RegisterScreen.hs b/hledger-ui/Hledger/UI/RegisterScreen.hs index ab4ad9476..105055a97 100644 --- a/hledger-ui/Hledger/UI/RegisterScreen.hs +++ b/hledger-ui/Hledger/UI/RegisterScreen.hs @@ -317,11 +317,11 @@ rsHandle ui@UIState{ 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 e | e `elem` moveLeftEvents -> continue $ popScreen ui VtyEvent (EvKey (KChar 'l') [MCtrl]) -> scrollSelectionToMiddle rsList >> invalidateCache >> continue ui -- enter transaction screen for selected transaction - VtyEvent (EvKey k []) | k `elem` [KRight, KChar 'l'] -> do + VtyEvent e | e `elem` moveRightEvents -> do case listSelectedElement rsList of Just (_, RegisterScreenItem{rsItemTransaction=t}) -> let @@ -336,7 +336,7 @@ rsHandle ui@UIState{ -- 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 + VtyEvent e | e `elem` moveDownEvents, isBlankElement mnextelement -> do vScrollBy (viewportScroll $ rsList^.listNameL) 1 continue ui where @@ -355,13 +355,9 @@ rsHandle ui@UIState{ -- 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 diff --git a/hledger-ui/Hledger/UI/TransactionScreen.hs b/hledger-ui/Hledger/UI/TransactionScreen.hs index 735dd9398..2c4636610 100644 --- a/hledger-ui/Hledger/UI/TransactionScreen.hs +++ b/hledger-ui/Hledger/UI/TransactionScreen.hs @@ -168,9 +168,9 @@ tsHandle ui@UIState{aScreen=s@TransactionScreen{tsTransaction=(i,t) -- 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} diff --git a/hledger-ui/Hledger/UI/UIUtils.hs b/hledger-ui/Hledger/UI/UIUtils.hs index a4ceafc57..d743ddf03 100644 --- a/hledger-ui/Hledger/UI/UIUtils.hs +++ b/hledger-ui/Hledger/UI/UIUtils.hs @@ -15,7 +15,7 @@ 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 @@ -44,10 +44,11 @@ helpDialog copts = 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" @@ -125,7 +126,7 @@ helpDialog copts = 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' @@ -285,3 +286,16 @@ scrollSelectionToMiddle list = do 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 KLeft [], 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