diff --git a/src/TaskMachine/Task.hs b/src/TaskMachine/Task.hs index ae82c10..cc77027 100644 --- a/src/TaskMachine/Task.hs +++ b/src/TaskMachine/Task.hs @@ -21,6 +21,8 @@ module TaskMachine.Task , Description , Snippet(..) -- * Misc stuff + , emptyTask + , newTask , compareTasks -- * Formatting , formatTask @@ -313,6 +315,13 @@ pTasks = many pTask <* eof {- Misc stuff -} +emptyTask :: Task +emptyTask = Task Incomplete Nothing Nothing Nothing [] + +-- | Create a new task with empty description and the given date as creation date +newTask :: Day -> Task +newTask day = Task Incomplete Nothing Nothing (Just day) [] + compareTasks :: Task -> Task -> Ordering compareTasks a b = mconcat [ compare (taskCompletion a) (taskCompletion b) diff --git a/src/TaskMachine/UI.hs b/src/TaskMachine/UI.hs index 26dcd52..38b7048 100644 --- a/src/TaskMachine/UI.hs +++ b/src/TaskMachine/UI.hs @@ -5,21 +5,19 @@ module TaskMachine.UI import qualified Brick as B import qualified Brick.Themes as B -import Control.Monad.Trans import qualified Data.Vector as V import qualified Graphics.Vty.Input.Events as VTY import TaskMachine.Options import TaskMachine.UI.Behaviors import TaskMachine.UI.Popup -import TaskMachine.UI.Stuff import TaskMachine.UI.TaskList import TaskMachine.UI.Types {- Rendering -} drawTaskList :: UIState -> B.Widget RName -drawTaskList s = renderTaskList (taskEdit s) True (tasks s) +drawTaskList s = renderTaskList (editor s) True (tasks s) drawUIState :: UIState -> [B.Widget RName] drawUIState s@UIState{errorPopup=Just p} = [renderPopup p, drawTaskList s] @@ -27,27 +25,13 @@ drawUIState s = [drawTaskList s] {- Updating the state -} -closeBehavior :: (UIState -> VTY.Event -> NewState) -> UIState -> VTY.Event -> NewState -closeBehavior _ s (VTY.EvKey VTY.KEsc []) = B.halt s -closeBehavior _ s (VTY.EvKey (VTY.KChar 'q') []) = B.halt s -closeBehavior f s e = f s e -- wrapper around another behavior - -popupBehavior :: Popup RName (UIState -> NewState) -> UIState -> VTY.Event -> NewState -popupBehavior p s (VTY.EvKey VTY.KEnter []) = - case popupSelection p of - Nothing -> B.continue s{errorPopup=Nothing} - Just action -> action s{errorPopup=Nothing} -popupBehavior p s e = do - newPopup <- handlePopupEvent e p - B.continue s{errorPopup=Just newPopup} - selectBehavior :: UIState -> VTY.Event -> NewState -- Deal with popup if there is one -selectBehavior s@UIState{errorPopup=Just p} e = popupBehavior p s e --- Under the assumption that tasks can only be edited while the task list is focused, edit a task -selectBehavior s@UIState{taskEdit=Just edit} e = taskEditBehavior edit s e --- If nothing immediately jumps out at you, see which part has focus. -selectBehavior s e = closeBehavior taskListBehavior s e +selectBehavior s@UIState{errorPopup=Just p} e = closeModifier (popupBehavior p) s e +-- Continue editing task if previously editing a task +selectBehavior s@UIState{editor=Just edit} e = taskEditBehavior edit s e +-- Default task list behavior +selectBehavior s e = closeModifier taskListBehavior s e updateUIState :: UIState -> B.BrickEvent RName () -> NewState updateUIState s (B.VtyEvent e) = selectBehavior s e @@ -55,15 +39,12 @@ updateUIState s _ = B.continue s {- Starting the app -} -startEvent :: UIState -> B.EventM RName UIState -startEvent = liftIO . loadTasks - myApp :: B.App UIState () RName myApp = B.App { B.appDraw = drawUIState , B.appChooseCursor = B.showFirstCursor , B.appHandleEvent = updateUIState - , B.appStartEvent = startEvent + , B.appStartEvent = actionLoad , B.appAttrMap = const (B.themeToAttrMap defaultTheme) } @@ -72,5 +53,5 @@ startUIState o = UIState { options = o , errorPopup = Nothing , tasks = taskList RTaskList V.empty - , taskEdit = Nothing + , editor = Nothing } diff --git a/src/TaskMachine/UI/Behaviors.hs b/src/TaskMachine/UI/Behaviors.hs index 73bbf53..2f685c2 100644 --- a/src/TaskMachine/UI/Behaviors.hs +++ b/src/TaskMachine/UI/Behaviors.hs @@ -1,58 +1,212 @@ module TaskMachine.UI.Behaviors - ( taskListBehavior + ( Behavior + --, emptyBehavior + -- * Miscellaneous + , getCurrentDay + , closeModifier + -- * Behaviors + , popupBehavior + , taskListBehavior , taskEditBehavior + -- * Actions + , actionLoad + , actionSave + , actionDelete + , actionEditNew + , actionEditSelected + , actionSortTasks + , actionFinishEdit ) where -import qualified Brick as B -import qualified Brick.Widgets.Edit as B -import Control.Monad.Trans -import qualified Data.Text.Zipper as T -import qualified Graphics.Vty as VTY -import Text.Megaparsec +import Control.Monad +import qualified Brick as B +--import qualified Brick.Widgets.Edit as B +import Control.Monad.Trans +--import qualified Data.Text.Zipper as T +import qualified Graphics.Vty as VTY +--import Text.Megaparsec +import Data.Time + +import TaskMachine.LTask +import TaskMachine.Options import TaskMachine.Task -import TaskMachine.UI.Stuff +import TaskMachine.UI.Popup +import TaskMachine.UI.TaskEdit import TaskMachine.UI.TaskList import TaskMachine.UI.Types -startEdit :: UIState -> UIState -startEdit s = - case taskListSelectedElement (tasks s) of - Nothing -> undefined -- TODO: Add popup notification +type Behavior = UIState -> VTY.Event -> B.EventM RName (B.Next UIState) + +type Action = UIState -> B.EventM RName UIState + +{- Miscellaneous -} + +getCurrentDay :: IO Day +getCurrentDay = utctDay <$> liftIO getCurrentTime + +closeModifier :: Behavior -> Behavior +closeModifier _ s (VTY.EvKey VTY.KEsc []) = B.halt s +closeModifier _ s (VTY.EvKey (VTY.KChar 'q') []) = B.halt s +closeModifier f s e = f s e -- wrapper around another behavior + +{- Popups -} + +popupBehavior :: Popup RName (UIState -> NewState) -> Behavior +popupBehavior p s (VTY.EvKey VTY.KEnter []) = + case popupSelection p of + Nothing -> B.continue s{errorPopup=Nothing} -- Just close, no action was specified + Just action -> action s{errorPopup=Nothing} -- Do the thing! (and close the popup) +popupBehavior p s e = do + newPopup <- handlePopupEvent e p + B.continue s{errorPopup=Just newPopup} + +{- On the task list -} + +-- (re-)loading + +actionLoad :: Action +actionLoad s = do + let file = oTodofile $ options s + result <- liftIO $ loadLTasks file + case result of + Right ltasks -> pure s{tasks=taskList RTaskList ltasks} + Left errorMessage -> + let p = popup "Error loading tasks" errorMessage + [ ("Retry", actionLoad >=> B.continue) + , ("Quit", B.halt) + ] + in pure s{errorPopup=Just p} + +-- saving + +actionSave :: Action +actionSave s = do + let filepath = oTodofile (options s) + ltasks = taskListElements (tasks s) + result <- liftIO $ saveLTasks filepath ltasks + case result of + Right _ -> pure s + Left errorMessage -> + let p = popup "Error saving tasks" errorMessage + [ ("Retry", actionSave >=> B.continue) + , ("Continue without saving", B.continue) + , ("Quit", B.halt) + ] + in pure s{errorPopup=Just p} + +-- deleting a task + +actionDelete :: Action +actionDelete s = pure s{tasks=deleteTask (tasks s)} + +-- beginning an edit + +actionEditNew :: Action +actionEditNew s = do + today <- liftIO getCurrentDay + let task = newTask today + edit = taskEdit RTaskEdit task NewTask + pure s{editor=Just edit} + +actionEditSelected :: Action +actionEditSelected s = + case selectedTask (tasks s) of + Nothing -> error "no task selected" -- TODO: Add popup notification Just t -> - let edit = B.editor RTaskEdit (Just 1) (formatTask t) - in s{taskEdit=Just edit} + let edit = taskEdit RTaskEdit t ExistingTask + in pure s{editor=Just edit} -finishEdit :: B.Editor String RName -> UIState -> UIState +-- toggling completion + +actionToggleCompletion :: Action +actionToggleCompletion s = + case selectedTask (tasks s) of + Nothing -> pure s + Just task -> do + newCompletion <- case taskCompletion task of + Complete _ -> pure Incomplete + Incomplete -> Complete . Just <$> liftIO getCurrentDay + let task' = task{taskCompletion=newCompletion} + newTaskList = replaceTask task' (tasks s) + pure s{tasks=newTaskList} + +-- sorting + +actionSortTasks :: Action +actionSortTasks s = pure s{tasks=sortTaskList (tasks s)} + +-- combining all of the above... + +taskListBehavior :: Behavior +-- Clean up: Add todays date where creation/completion date is missing +--taskListBehavior s (VTY.EvKey (VTY.KChar 'c') []) = undefined s +-- Delete currently selected task (implicit save) +taskListBehavior s (VTY.EvKey (VTY.KChar 'd') []) = + actionDelete >=> actionSave >=> B.continue $ s +-- Begin editing currently selected task +taskListBehavior s (VTY.EvKey (VTY.KChar 'e') []) = + actionEditSelected >=> B.continue $ s +-- Begin creating new task +taskListBehavior s (VTY.EvKey (VTY.KChar 'n') []) = + actionEditNew >=> B.continue $ s +-- Reload tasks (and sort them) +taskListBehavior s (VTY.EvKey (VTY.KChar 'r') []) = + actionLoad >=> B.continue $ s +-- Sort tasks +taskListBehavior s (VTY.EvKey (VTY.KChar 's') []) = + actionSortTasks >=> B.continue $ s +-- Toggle completion (implicit save) +taskListBehavior s (VTY.EvKey (VTY.KChar 'x') []) = + actionToggleCompletion >=> actionSave >=> B.continue $ s +-- Update the task list (scroll etc.) +taskListBehavior s e = do + newTasks <- updateTaskList e (tasks s) + B.continue s{tasks=newTasks} + +{- In the task editor -} + +actionFinishEdit :: TaskEdit RName -> Action +actionFinishEdit t = pure . finishEdit t + +-- get result of task editing +-- if editing an existing task, modify that task +-- if editing a new task, append that task +finishEdit :: TaskEdit RName -> UIState -> UIState finishEdit edit s = - let editedText = unlines $ B.getEditContents edit - in case parse pTask "edited task" editedText of - Left parseError -> undefined parseError -- TODO: Add popup notification - Right newTask -> - let newTaskList = taskListModify (const newTask) (tasks s) - in s{tasks=newTaskList, taskEdit=Nothing} + case editedTask edit of + Left e -> error e -- TODO: Error popup + Right task -> s{tasks=modify task, editor=Nothing} + where + modify :: Task -> TaskList RName + modify task = case editState edit of + ExistingTask -> replaceTask task $ tasks s + NewTask -> appendTask task $ tasks s -taskEditBehavior :: B.Editor String RName -> UIState -> VTY.Event -> NewState -taskEditBehavior _ s (VTY.EvKey VTY.KEsc []) = B.continue s{taskEdit=Nothing} -taskEditBehavior edit s (VTY.EvKey VTY.KHome []) = B.continue s{taskEdit=Just (B.applyEdit T.gotoBOL edit)} -taskEditBehavior edit s (VTY.EvKey VTY.KEnd []) = B.continue s{taskEdit=Just (B.applyEdit T.gotoEOL edit)} -taskEditBehavior edit s (VTY.EvKey VTY.KEnter []) = do - newState <- liftIO $ saveTasks $ finishEdit edit s - B.continue newState +taskEditBehavior :: TaskEdit RName -> Behavior +taskEditBehavior _ s (VTY.EvKey VTY.KEsc []) = B.continue s{editor=Nothing} +taskEditBehavior edit s (VTY.EvKey VTY.KEnter []) = + actionFinishEdit edit >=> actionSave >=> B.continue $ s +--taskEditBehavior edit s (VTY.EvKey VTY.KEnter []) = do +-- newState <- liftIO $ saveTasks $ finishEdit edit s +-- B.continue newState taskEditBehavior edit s e = do - newEdit <- B.handleEditorEvent e edit - B.continue s{taskEdit=Just newEdit} + newEdit <- updateTaskEdit e edit + B.continue s{editor=Just newEdit} -taskListBehavior :: UIState -> VTY.Event -> NewState +{- -- Reload while running taskListBehavior s (VTY.EvKey (VTY.KChar 'r') []) = actionLoad s -- Mark/unmark a task as completed taskListBehavior s (VTY.EvKey (VTY.KChar 'x') []) = undefined s -- Delete tasks taskListBehavior s (VTY.EvKey (VTY.KChar 'd') []) = undefined s +-- Delete tasks +taskListBehavior s (VTY.EvKey (VTY.KChar 'd') []) = undefined s -- Start editing a new task taskListBehavior s (VTY.EvKey (VTY.KChar 'e') []) = B.continue (startEdit s) -- Update the task list (scroll etc.) taskListBehavior s e = do newTasks <- updateTaskList e (tasks s) B.continue s{tasks=newTasks} +-} diff --git a/src/TaskMachine/UI/Popup.hs b/src/TaskMachine/UI/Popup.hs index dab846c..d13cd5b 100644 --- a/src/TaskMachine/UI/Popup.hs +++ b/src/TaskMachine/UI/Popup.hs @@ -1,23 +1,17 @@ module TaskMachine.UI.Popup - ( minPopupWidth - -- * Ok popup - , Popup + ( Popup , popup , popup' , renderPopup , handlePopupEvent , popupSelection + , minPopupWidth ) where import qualified Brick as B import qualified Brick.Widgets.Dialog as B import qualified Graphics.Vty as VTY -minPopupWidth :: Int -minPopupWidth = 78 - -{- Ok popup -} - data Popup n r = Popup (B.Dialog r) (B.Widget n) popup :: String -> String -> [(String, r)] -> Popup n r @@ -37,3 +31,6 @@ handlePopupEvent e (Popup dialog widget) = Popup <$> B.handleDialogEvent e dialo popupSelection :: Popup n r -> Maybe r popupSelection (Popup dialog _) = B.dialogSelection dialog + +minPopupWidth :: Int +minPopupWidth = 78 diff --git a/src/TaskMachine/UI/Stuff.hs b/src/TaskMachine/UI/Stuff.hs deleted file mode 100644 index 75c85c5..0000000 --- a/src/TaskMachine/UI/Stuff.hs +++ /dev/null @@ -1,54 +0,0 @@ -module TaskMachine.UI.Stuff where - -import qualified Brick as B -import Control.Monad.Trans - -import TaskMachine.LTask -import TaskMachine.Options -import TaskMachine.UI.Popup -import TaskMachine.UI.TaskList -import TaskMachine.UI.Types - -actionQuit :: UIState -> NewState -actionQuit = B.halt - -actionDoNothing :: UIState -> NewState -actionDoNothing = B.continue - -actionLoad :: UIState -> NewState -actionLoad s = do - state <- liftIO $ loadTasks s - B.continue state - -loadTasks :: UIState -> IO UIState -loadTasks s = do - let file = oTodofile $ options s - result <- loadLTasks file - case result of - Right ltasks -> pure s{tasks=taskList RTaskList ltasks} - Left errorMessage -> - let p = popup "Error loading tasks" errorMessage - [ ("Retry", actionLoad) - , ("Quit", actionQuit) - ] - in pure s{errorPopup=Just p} - -actionSave :: UIState -> NewState -actionSave s = do - state <- liftIO $ saveTasks s - B.continue state - -saveTasks :: UIState -> IO UIState -saveTasks s = do - let filepath = oTodofile (options s) - ltasks = taskListElements (tasks s) - result <- saveLTasks filepath ltasks - case result of - Right _ -> pure s - Left errorMessage -> - let p = popup "Error saving tasks" errorMessage - [ ("Retry", actionSave) - , ("Continue without saving", actionDoNothing) - , ("Quit", actionQuit) - ] - in pure s{errorPopup=Just p} diff --git a/src/TaskMachine/UI/TaskEdit.hs b/src/TaskMachine/UI/TaskEdit.hs new file mode 100644 index 0000000..d4cb133 --- /dev/null +++ b/src/TaskMachine/UI/TaskEdit.hs @@ -0,0 +1,56 @@ +module TaskMachine.UI.TaskEdit + ( TaskEdit + , EditState(..) + , taskEdit + , editState + , editedTask + , renderTaskEdit + , updateTaskEdit + ) where + +import qualified Brick as B +import qualified Brick.Widgets.Edit as B +import qualified Data.Text.Zipper as T +import qualified Graphics.Vty as VTY +import Text.Megaparsec + +import TaskMachine.Task + +data TaskEdit n = TaskEdit EditState (B.Editor String n) + deriving (Show) + +data EditState = ExistingTask | NewTask + deriving (Show) + +taskEdit :: n -> Task -> EditState -> TaskEdit n +taskEdit name task s = TaskEdit s $ B.editor name (Just 1) (formatTask task) + +editState :: TaskEdit n -> EditState +editState (TaskEdit s _) = s + +editedLine :: TaskEdit n -> Either String String +editedLine (TaskEdit _ edit) = + case B.getEditContents edit of + [s] -> Right s + _ -> Left "Editor empty" + +editedTask :: TaskEdit n -> Either String Task +editedTask te = do + s <- editedLine te + case parse pTask "task editor" s of + Left parseError -> Left $ parseErrorPretty parseError + Right task -> Right task + +renderRows :: [String] -> B.Widget n +renderRows = B.vBox . map B.str + +renderTaskEdit :: (Ord n, Show n) => Bool -> TaskEdit n -> B.Widget n +renderTaskEdit focus (TaskEdit _ edit) = B.renderEditor renderRows focus edit + +updateTaskEdit :: Ord n => VTY.Event -> TaskEdit n -> B.EventM n (TaskEdit n) +updateTaskEdit (VTY.EvKey VTY.KHome []) (TaskEdit s edit) = + pure $ TaskEdit s $ B.applyEdit T.gotoBOL edit +updateTaskEdit (VTY.EvKey VTY.KEnd []) (TaskEdit s edit) = + pure $ TaskEdit s $ B.applyEdit T.gotoEOL edit +updateTaskEdit event (TaskEdit s edit) = + TaskEdit s <$> B.handleEditorEvent event edit diff --git a/src/TaskMachine/UI/TaskList.hs b/src/TaskMachine/UI/TaskList.hs index 4423904..2db48a6 100644 --- a/src/TaskMachine/UI/TaskList.hs +++ b/src/TaskMachine/UI/TaskList.hs @@ -1,63 +1,96 @@ module TaskMachine.UI.TaskList ( TaskList , taskList + , taskListElements , renderTaskList , updateTaskList - , taskListElements - , taskListFilter - , taskListSelectedElement - , taskListModify + , sortTaskList + , selectedTask + , appendTask + , replaceTask + , deleteTask ) where -import qualified Brick as B -import qualified Brick.Widgets.Edit as B -import qualified Brick.Widgets.List as B -import qualified Data.Vector as V -import qualified Graphics.Vty as VTY +import Data.Function +import Data.List + +import qualified Brick as B +import qualified Brick.Widgets.List as B +import qualified Data.Vector as V +import qualified Graphics.Vty as VTY import TaskMachine.LTask import TaskMachine.Task import TaskMachine.UI.Task +import TaskMachine.UI.TaskEdit -data TaskList n = TaskList - { visibleTasks :: B.List n LTask - , invisibleTasks :: V.Vector LTask - } deriving (Show) - -newList :: n -> V.Vector LTask -> B.List n LTask -newList name ltasks = B.list name ltasks 1 +newtype TaskList n = TaskList (B.List n LTask) taskList :: n -> V.Vector LTask -> TaskList n -taskList name ltasks = TaskList{visibleTasks=newList name ltasks, invisibleTasks=V.empty} - -renderLTask :: (Ord n, Show n) => Maybe (B.Editor String n) -> Bool -> LTask -> B.Widget n -renderLTask (Just e) True _ = B.renderEditor (B.str . unlines) True e -renderLTask _ _ lt = renderTask (toTask lt) - -renderTaskList :: (Ord n, Show n) => Maybe (B.Editor String n) -> Bool -> TaskList n -> B.Widget n -renderTaskList edit focus tl = B.renderList (renderLTask edit) focus (visibleTasks tl) - -updateTaskList :: (Ord n) => VTY.Event -> TaskList n -> B.EventM n (TaskList n) -updateTaskList e tl = do - updatedList <- B.handleListEventVi B.handleListEvent e (visibleTasks tl) - pure tl{visibleTasks=updatedList} - -{- Managing tasks -} +taskList name tasks = TaskList $ B.list name tasks 1 taskListElements :: TaskList n -> V.Vector LTask -taskListElements tl = B.listElements (visibleTasks tl) <> invisibleTasks tl +taskListElements (TaskList list) = B.listElements list -taskListFilter :: (Task -> Bool) -> TaskList n -> TaskList n -taskListFilter f tl = - let (yes, no) = V.partition (f . toTask) $ taskListElements tl - name = B.listName (visibleTasks tl) - list = newList name yes - in TaskList{visibleTasks=list, invisibleTasks=no} +renderRow :: Maybe (B.Widget n) -> Bool -> LTask -> B.Widget n +renderRow (Just w) True _ = w +renderRow _ _ lt = renderTask (toTask lt) -taskListSelectedElement :: TaskList n -> Maybe Task -taskListSelectedElement tl = toTask . snd <$> B.listSelectedElement (visibleTasks tl) +renderLast :: (Ord n, Show n) => B.Widget n -> Bool -> B.List n LTask -> B.Widget n +renderLast widget focus list = + let listWithPlaceholder = focusOnLastTask $ appendTask' emptyTask list + in B.renderList (renderRow (Just widget)) focus listWithPlaceholder -taskListModify :: (Task -> Task) -> TaskList n -> TaskList n -taskListModify f tl = - let list = B.listModify (modifyLTask f) (visibleTasks tl) - in tl{visibleTasks=list} +renderTaskList :: (Ord n, Show n) => Maybe (TaskEdit n) -> Bool -> TaskList n -> B.Widget n +renderTaskList Nothing focus (TaskList list) + | listSize list == 0 = renderLast (B.str "--- empty ---") focus list + | otherwise = B.renderList (renderRow Nothing) focus list +renderTaskList (Just te) focus (TaskList list) = + case editState te of + ExistingTask -> B.renderList (renderRow (Just teWidget)) focus list + NewTask -> renderLast teWidget focus list + where + teWidget = renderTaskEdit focus te + +updateTaskList :: Ord n => VTY.Event -> TaskList n -> B.EventM n (TaskList n) +updateTaskList event (TaskList list) = + TaskList <$> B.handleListEventVi B.handleListEvent event list + +sortTaskList :: TaskList n -> TaskList n +sortTaskList (TaskList list) = + let tasks = V.toList $ B.listElements list + sortedTasks = sortBy (compareTasks `on` toTask) tasks + newVector = V.fromList sortedTasks + in TaskList $ B.listReplace newVector Nothing list + +selectedTask :: TaskList n -> Maybe Task +selectedTask (TaskList list) = toTask . snd <$> B.listSelectedElement list + +appendTask' :: Task -> B.List n LTask -> B.List n LTask +appendTask' task list = + let size = listSize list + lt = lTask task + in focusOnLastTask $ B.listInsert size lt list + +appendTask :: Task -> TaskList n -> TaskList n +appendTask task (TaskList list) = TaskList $ appendTask' task list + +replaceTask :: Task -> TaskList n -> TaskList n +replaceTask task (TaskList list) = TaskList $ B.listModify replace list + where + replace :: LTask -> LTask + replace = modifyLTask (const task) + +deleteTask :: TaskList n -> TaskList n +deleteTask tl@(TaskList list) = + case listSize list of + 0 -> tl + n -> TaskList $ B.listRemove (n-1) list + +{- helper functions -} + +listSize :: B.List n e -> Int +listSize list = V.length $ B.listElements list + +focusOnLastTask :: B.List n e -> B.List n e +focusOnLastTask list = B.listMoveTo (listSize list - 1) list diff --git a/src/TaskMachine/UI/Types.hs b/src/TaskMachine/UI/Types.hs index 6a313f7..f39235d 100644 --- a/src/TaskMachine/UI/Types.hs +++ b/src/TaskMachine/UI/Types.hs @@ -22,6 +22,7 @@ import qualified Graphics.Vty as VTY import TaskMachine.Options import TaskMachine.UI.Popup import TaskMachine.UI.Task +import TaskMachine.UI.TaskEdit import TaskMachine.UI.TaskList -- | Resource names @@ -36,7 +37,7 @@ data UIState = UIState { options :: Options -- includes todo file and other config , errorPopup :: Maybe (Popup RName (UIState -> NewState)) , tasks :: TaskList RName - , taskEdit :: Maybe (B.Editor String RName) + , editor :: Maybe (TaskEdit RName) } type NewState = B.EventM RName (B.Next UIState) diff --git a/todo.txt b/todo.txt index ad3d01f..eecbfce 100644 --- a/todo.txt +++ b/todo.txt @@ -2,7 +2,7 @@ - c2018-09-18 Purge - move completed tasks to a separate file - c2018-09-28 Move cursor to beginning of task description when editing tasks - c2018-09-28 Syntax highlighting while editing tasks -- c2018-09-30 Sort tasks when (re-)loading +x2018-10-23 c2018-09-30 Display "-empty-" when TaskList is empty x2018-09-27 c2018-09-18 Quit using Esc or q x2018-09-29 c2018-09-28 Use B.EventM's MonadIO instance instead of B.suspendAndResume (facepalm) x2018-09-30 c2018-09-18 Offer "retry" or "quit" +dialogue - couldn't load from todo file