From 7abfa8f3cdc99d1d144901609df6b31c692d8ec2 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 14 Mar 2019 10:29:33 -0500 Subject: [PATCH] Modifying tasks works --- app_state.go | 11 ++- helpers.go | 78 +++++++++++++++++ message.go | 85 ++++++++++++++++++ model.go | 13 +++ screen_about.go | 8 +- screen_main.go | 72 ++++++++-------- screen_task.go | 223 +++++++++++++++++++++++++++++++++++++----------- 7 files changed, 399 insertions(+), 91 deletions(-) create mode 100644 helpers.go create mode 100644 message.go diff --git a/app_state.go b/app_state.go index 6888e6d..cc96c97 100644 --- a/app_state.go +++ b/app_state.go @@ -11,6 +11,13 @@ import ( termbox "github.com/nsf/termbox-go" ) +const ( + ScreenIdMain = iota + ScreenIdTask + ScreenIdAbout + ScreenIdExit +) + type AppState struct { Name string Version int @@ -42,8 +49,6 @@ const ( TitleBg = termbox.ColorBlue CursorFg = termbox.ColorBlack CursorBg = termbox.ColorGreen - - ExitScreenId = -1 ) func NewApp() *AppState { @@ -70,7 +75,7 @@ func (a *AppState) run(parms []string) int { mainBundle := termboxScreen.Bundle{} mainBundle.SetValue(MainBundleListKey, MainBundleListTodo) - if err := a.uiManager.InitializeScreen(MainScreenId, mainBundle); err != nil { + if err := a.uiManager.InitializeScreen(ScreenIdMain, mainBundle); err != nil { return 1 } if err := a.uiManager.Loop(); err != nil { diff --git a/helpers.go b/helpers.go new file mode 100644 index 0000000..5000c71 --- /dev/null +++ b/helpers.go @@ -0,0 +1,78 @@ +package main + +import ( + "strconv" + "strings" + "unicode" +) + +func itoa(val int) string { + return strconv.Itoa(val) +} + +func atoi(val string) int { + return oatoi(val, -1) +} + +func oatoi(val string, def int) int { + v, err := strconv.Atoi(val) + if err != nil { + return def + } + return v +} + +func isCombination(s1, s2 []string) bool { + if len(s1) != len(s2) { + return false + } + for _, v := range s1 { + var found bool + for _, vv := range s2 { + if v == vv { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +// We split projects/contexts on spaces or commas +func splitFields(v string) []string { + f := func(c rune) bool { + return unicode.IsSpace(c) || c == ',' + } + return strings.FieldsFunc(v, f) +} + +func tagsToSlice(v map[string]string) []string { + var ret []string + for k, v := range v { + ret = append(ret, k+":"+v) + } + return ret +} + +func sliceToTags(v []string) map[string]string { + ret := make(map[string]string) + for _, vv := range v { + tags := strings.Split(vv, ":") + if len(tags) == 2 { + ret[tags[0]] = tags[1] + } + } + return ret +} + +func sliceIsValidTags(v []string) bool { + for _, vv := range v { + if len(strings.Split(vv, ":")) != 2 { + return false + } + } + return true +} diff --git a/message.go b/message.go new file mode 100644 index 0000000..c07d499 --- /dev/null +++ b/message.go @@ -0,0 +1,85 @@ +package main + +import ( + "time" + + "github.com/br0xen/termbox-util" + termbox "github.com/nsf/termbox-go" +) + +const MessageNoTimeout = -1 + +type Message struct { + value string + timeout time.Duration + setTime time.Time + fg, bg termbox.Attribute + + defaultValue string + defaultTimeout time.Duration + defaultFg termbox.Attribute + defaultBg termbox.Attribute +} + +func NewMessage(defaultVal string, defaultFg, defaultBg termbox.Attribute, defaultTO time.Duration) *Message { + return &Message{ + defaultValue: defaultVal, + defaultTimeout: defaultTO, + defaultFg: defaultFg, + defaultBg: defaultBg, + } +} + +func (m *Message) Get() string { + m.checkExpiration() + return m.value +} + +func (m *Message) Set(v string) { + m.value = v + m.setTime = time.Now() +} + +func (m *Message) Clear() { + m.value = m.defaultValue + m.setTime = time.Time{} + m.setFromDefaults() +} + +func (m *Message) SetWithNoTimeout(v string) { + m.Set(v) + m.timeout = MessageNoTimeout +} + +func (m *Message) SetWithTimeout(v string, t time.Duration) { + m.Set(v) + m.timeout = t +} + +func (m *Message) SetError(v string) { + m.value = v + m.setTime = time.Now() + m.timeout = time.Second * 2 + m.bg = termbox.ColorRed + m.fg = termbox.ColorWhite | termbox.AttrBold +} + +func (m *Message) DrawAt(x, y int) { + termboxUtil.DrawStringAtPoint(m.value, x, y, m.fg, m.bg) +} + +func (m *Message) checkExpiration() { + if m.expired() { + m.Clear() + } +} + +func (m *Message) expired() bool { + return m.timeout > 0 && time.Since(m.setTime) > m.timeout +} + +func (m *Message) setFromDefaults() { + m.value = m.defaultValue + m.timeout = m.defaultTimeout + m.fg, m.bg = m.defaultFg, m.defaultBg +} diff --git a/model.go b/model.go index 9a25b44..89e5507 100644 --- a/model.go +++ b/model.go @@ -48,6 +48,19 @@ func (a *AppState) addTask(taskString string) error { return a.WriteList() } +func (a *AppState) saveTask(t *todotxt.Task) error { + lt, err := a.TaskList.GetTask(t.Id) + if err != nil { + return err + } + lt.Todo = t.Todo + lt.Priority = t.Priority + lt.Projects = t.Projects + lt.Contexts = t.Contexts + lt.AdditionalTags = t.AdditionalTags + return a.WriteList() +} + func (a *AppState) toggleTaskComplete(id int) error { if a.diskListChanged() { return a.e(ResStrListChanged) diff --git a/screen_about.go b/screen_about.go index 23bf10e..679c461 100644 --- a/screen_about.go +++ b/screen_about.go @@ -9,8 +9,6 @@ import ( termbox "github.com/nsf/termbox-go" ) -const AboutScreenId = 2 - // AboutScreen holds all that's going on type AboutScreen struct { message string @@ -27,7 +25,7 @@ type Command struct { description string } -func (screen *AboutScreen) Id() int { return AboutScreenId } +func (screen *AboutScreen) Id() int { return ScreenIdAbout } func (screen *AboutScreen) Initialize(bundle termboxScreen.Bundle) error { screen.titleTemplate = []string{ @@ -60,9 +58,9 @@ func (screen *AboutScreen) Initialize(bundle termboxScreen.Bundle) error { func (screen *AboutScreen) ResizeScreen() { screen.Initialize(nil) } func (screen *AboutScreen) HandleKeyEvent(event termbox.Event) int { - return MainScreenId + return ScreenIdMain } -func (screen *AboutScreen) HandleNoneEvent(event termbox.Event) int { return AboutScreenId } +func (screen *AboutScreen) HandleNoneEvent(event termbox.Event) int { return screen.Id() } func (screen *AboutScreen) DrawScreen() { width, height := termbox.Size() diff --git a/screen_main.go b/screen_main.go index 1345b38..ec1679f 100644 --- a/screen_main.go +++ b/screen_main.go @@ -12,8 +12,6 @@ import ( termbox "github.com/nsf/termbox-go" ) -const MainScreenId = 0 - type ViewPort struct { bytesPerRow int numberOfRows int @@ -50,6 +48,8 @@ const ( MainBundleListTodo = "mainscreen.list.todo" MainBundleListDone = "mainscreen.list.done" + MainBundleListCurrent = "mainscreen.list.current" + MainBackspaceNothing = iota MainBackspaceMain MainBackspaceFilter @@ -60,7 +60,7 @@ const ( InputIDUnArchiveTask = "move task to active list? (y/n)" ) -func (screen *MainScreen) Id() int { return MainScreenId } +func (screen *MainScreen) Id() int { return ScreenIdMain } func (screen *MainScreen) Initialize(bundle termboxScreen.Bundle) error { width, height := termbox.Size() @@ -68,6 +68,10 @@ func (screen *MainScreen) Initialize(bundle termboxScreen.Bundle) error { screen.cursor = make(map[string]int) if bundle != nil { + if bundle.GetString(MainBundleListKey, MainBundleListCurrent) == MainBundleListCurrent { + bundle.SetValue(MainBundleListKey, screen.currentList) + bundle.SetValue(MainBundleFilterKey, screen.currentFilter) + } if err := screen.reloadList(bundle); err != nil { return err } @@ -146,10 +150,10 @@ func (screen *MainScreen) HandleKeyEvent(event termbox.Event) int { if event.Ch == '?' { // Go to About Screen b := termboxScreen.Bundle{} - if err := app.uiManager.InitializeScreen(AboutScreenId, b); err != nil { + if err := app.uiManager.InitializeScreen(ScreenIdAbout, b); err != nil { screen.setErrorMessage(err.Error()) } - return AboutScreenId + return ScreenIdAbout } else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { if screen.backspaceDoes == MainBackspaceNothing { @@ -163,7 +167,7 @@ func (screen *MainScreen) HandleKeyEvent(event termbox.Event) int { } else if screen.backspaceDoes == MainBackspaceFilter { screen.reloadList(screen.buildBundle(screen.currentList, "")) } - return MainScreenId + return screen.Id() } else if event.Key == termbox.KeySpace { return screen.toggleTaskComplete() @@ -223,11 +227,11 @@ func (screen *MainScreen) HandleKeyEvent(event termbox.Event) int { screen.confirmArchiveItem() } else if event.Ch == 'q' { - return ExitScreenId + return ScreenIdExit } - return MainScreenId + return screen.Id() } -func (screen *MainScreen) HandleNoneEvent(event termbox.Event) int { return MainScreenId } +func (screen *MainScreen) HandleNoneEvent(event termbox.Event) int { return screen.Id() } func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { switch screen.inputField.GetID() { @@ -239,7 +243,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetValue("") screen.backspaceDoes = MainBackspaceFilter screen.reloadList(screen.buildBundle(screen.currentList, filter)) - return MainScreenId + return screen.Id() } case InputIDAddTask: if event.Key == termbox.KeyEnter { @@ -251,7 +255,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) - return MainScreenId + return screen.Id() } case InputIDIncompleteArchive: if event.Ch == 'y' || event.Ch == 'Y' { @@ -260,7 +264,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) - return MainScreenId + return screen.Id() case InputIDUnArchiveTask: if event.Ch == 'y' || event.Ch == 'Y' { screen.inputField.SetID("") @@ -271,23 +275,23 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) - return MainScreenId + return screen.Id() } if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { if screen.inputField.GetValue() == "" { screen.reloadList(screen.buildBundle(screen.currentList, screen.inputField.GetValue())) screen.inputField.SetID("") screen.inputField.SetValue("") - return MainScreenId + return screen.Id() } } else if event.Key == termbox.KeyEsc { screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.inputField.SetID("") screen.inputField.SetValue("") - return MainScreenId + return screen.Id() } screen.inputField.HandleEvent(event) - return MainScreenId + return screen.Id() } func (screen *MainScreen) setActiveList(list *todotxt.TaskList) { @@ -365,7 +369,7 @@ func (screen *MainScreen) drawFooter() { func (screen *MainScreen) confirmArchiveItem() int { if screen.currentList != MainBundleListTodo { screen.inputField.SetID(InputIDUnArchiveTask) - return MainScreenId + return screen.Id() } // Find the task under the cursor if screen.cursor[screen.currentList] < len(*screen.displayList) { @@ -377,20 +381,20 @@ func (screen *MainScreen) confirmArchiveItem() int { return screen.archiveCurrentItem() } } - return MainScreenId + return screen.Id() } func (screen *MainScreen) archiveCurrentItem() int { if screen.currentList != MainBundleListTodo { screen.setErrorMessage("Task is already archived") - return MainScreenId + return screen.Id() } // Find the task under the cursor if len(*screen.displayList) > screen.cursor[screen.currentList] { t := (*screen.displayList)[screen.cursor[screen.currentList]] if err := app.archiveTask(t.Id); err != nil { screen.setErrorMessage(err.Error()) - return MainScreenId + return screen.Id() } // Reload the list b := screen.buildBundle(screen.currentList, screen.currentFilter) @@ -398,20 +402,20 @@ func (screen *MainScreen) archiveCurrentItem() int { screen.setErrorMessage(err.Error()) } } - return MainScreenId + return screen.Id() } func (screen *MainScreen) unarchiveCurrentItem() int { if screen.currentList == MainBundleListTodo { screen.setErrorMessage("Task is not archived") - return MainScreenId + return screen.Id() } // Find the task under the cursor if len(*screen.displayList) > screen.cursor[screen.currentList] { t := (*screen.displayList)[screen.cursor[screen.currentList]] if err := app.unarchiveTask(t.Id); err != nil { screen.setErrorMessage(err.Error()) - return MainScreenId + return screen.Id() } // Reload the list b := screen.buildBundle(screen.currentList, screen.currentFilter) @@ -419,7 +423,7 @@ func (screen *MainScreen) unarchiveCurrentItem() int { screen.setErrorMessage(err.Error()) } } - return MainScreenId + return screen.Id() } func (screen *MainScreen) startEditTaskScreen() int { @@ -429,13 +433,13 @@ func (screen *MainScreen) startEditTaskScreen() int { // Load the task screen with this task b := termboxScreen.Bundle{} b.SetValue(TaskBundleTaskIdKey, t.Id) - if err := app.uiManager.InitializeScreen(TaskScreenId, b); err != nil { + if err := app.uiManager.InitializeScreen(ScreenIdTask, b); err != nil { screen.setErrorMessage(err.Error()) - return MainScreenId + return screen.Id() } - return TaskScreenId + return ScreenIdTask } - return MainScreenId + return screen.Id() } func (screen *MainScreen) reloadCurrentView() { @@ -455,18 +459,18 @@ func (screen *MainScreen) toggleViewList() int { } bundle.SetValue(MainBundleFilterKey, screen.currentFilter) screen.reloadList(bundle) - return MainScreenId + return screen.Id() } func (screen *MainScreen) startAddNewTask() int { screen.inputField.SetID(InputIDAddTask) - return MainScreenId + return screen.Id() } func (screen *MainScreen) toggleTaskComplete() int { if screen.currentList == MainBundleListDone { screen.setErrorMessage("Task is archived, unable to modify.") - return MainScreenId + return screen.Id() } // Find the task under the cursor @@ -475,11 +479,11 @@ func (screen *MainScreen) toggleTaskComplete() int { err := app.toggleTaskComplete(t.Id) if err != nil { screen.setErrorMessage(err.Error()) - return MainScreenId + return screen.Id() } } screen.reloadCurrentView() - return MainScreenId + return screen.Id() } func (screen *MainScreen) moveCursorDown() bool { @@ -502,7 +506,7 @@ func (screen *MainScreen) moveCursorUp() bool { func (screen *MainScreen) startFilter() int { screen.inputField.SetID(InputIDFilter) - return MainScreenId + return screen.Id() } func (screen *MainScreen) setErrorMessage(msg string) { diff --git a/screen_task.go b/screen_task.go index 5498c47..77acfcb 100644 --- a/screen_task.go +++ b/screen_task.go @@ -12,30 +12,62 @@ import ( termbox "github.com/nsf/termbox-go" ) -const TaskScreenId = 1 - -// TaskScreen holds all that's going on -type TaskScreen struct { - message string - messageTimeout time.Duration - messageTime time.Time - cursor int - - inputModal *termboxUtil.InputModal - confirmModal *termboxUtil.ConfirmModal - - currentTaskId int - displayTask *todotxt.Task -} - const ( + FieldTaskTodo = iota + FieldTaskPriority + FieldTaskProjects + FieldTaskContexts + FieldTaskTags + FieldTaskError + TaskBundleTaskIdKey = "taskscreen.taskid" ) -func (screen *TaskScreen) Id() int { return TaskScreenId } +type TaskScreen struct { + message *Message + cursor int + + currentTaskId int + displayTask *todotxt.Task + fieldLabels map[int]string + editing bool + + inputField *termboxUtil.InputField +} + +func (screen *TaskScreen) GetFieldValue(fld int) string { + switch fld { + case FieldTaskTodo: + return screen.displayTask.Todo + case FieldTaskPriority: + return screen.displayTask.Priority + case FieldTaskProjects: + return strings.Join(screen.displayTask.Projects, ",") + case FieldTaskContexts: + return strings.Join(screen.displayTask.Contexts, ",") + case FieldTaskTags: + var ret []string + for k, v := range screen.displayTask.AdditionalTags { + ret = append(ret, k+":"+v) + } + return strings.Join(ret, ",") + } + return "" +} + +func (screen *TaskScreen) Id() int { return ScreenIdTask } func (screen *TaskScreen) Initialize(bundle termboxScreen.Bundle) error { + screen.fieldLabels = make(map[int]string) + screen.fieldLabels[FieldTaskTodo] = "Todo" + screen.fieldLabels[FieldTaskPriority] = "Priority" + screen.fieldLabels[FieldTaskProjects] = "Projects" + screen.fieldLabels[FieldTaskContexts] = "Contexts" + screen.fieldLabels[FieldTaskTags] = "Tags" var err error + width, height := termbox.Size() + screen.inputField = termboxUtil.CreateInputField(2, (height - 3), width, 1, DefaultFg, DefaultBg) + screen.message = NewMessage("", DefaultFg, DefaultBg, time.Second*2) if bundle != nil { screen.currentTaskId = bundle.GetInt(TaskBundleTaskIdKey, -1) } @@ -45,14 +77,28 @@ func (screen *TaskScreen) Initialize(bundle termboxScreen.Bundle) error { if screen.displayTask, err = app.TaskList.GetTask(screen.currentTaskId); err != nil { return err } + screen.cursor = FieldTaskTodo return nil } func (screen *TaskScreen) ResizeScreen() { screen.Initialize(nil) } func (screen *TaskScreen) HandleKeyEvent(event termbox.Event) int { - if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 || event.Ch == 'h' || event.Key == termbox.KeyArrowLeft { - return MainScreenId + if screen.editing { + return screen.handleEditingKeyEvent(event) + } + if event.Key == termbox.KeyEnter { + screen.editing = true + screen.inputField.SetTitle(screen.fieldLabels[screen.cursor] + ": ") + screen.inputField.SetValue(screen.GetFieldValue(screen.cursor)) + screen.inputField.SetActive(true) + return screen.Id() + + } else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 || event.Ch == 'h' || event.Key == termbox.KeyArrowLeft { + bundle := termboxScreen.Bundle{} + bundle.SetValue(MainBundleListKey, MainBundleListCurrent) + app.uiManager.InitializeScreen(ScreenIdMain, bundle) + return ScreenIdMain } else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown { screen.moveCursorDown() @@ -60,32 +106,126 @@ func (screen *TaskScreen) HandleKeyEvent(event termbox.Event) int { } else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp { screen.moveCursorUp() } - return TaskScreenId + return screen.Id() } -func (screen *TaskScreen) HandleNoneEvent(event termbox.Event) int { return TaskScreenId } +func (screen *TaskScreen) HandleNoneEvent(event termbox.Event) int { return screen.Id() } func (screen *TaskScreen) DrawScreen() { screen.drawHeader() yPos := 1 - termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, DefaultFg, DefaultBg) + if screen.cursor == FieldTaskTodo { + termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, CursorBg, CursorFg) + } else { + termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, DefaultFg, DefaultBg) + } yPos++ - termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, DefaultFg, DefaultBg) + if screen.cursor == FieldTaskPriority { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, CursorBg, CursorFg) + } else { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, DefaultFg, DefaultBg) + } yPos++ - termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, DefaultFg, DefaultBg) + if screen.cursor == FieldTaskProjects { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, CursorBg, CursorFg) + } else { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, DefaultFg, DefaultBg) + } yPos++ - termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, DefaultFg, DefaultBg) + if screen.cursor == FieldTaskContexts { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, CursorBg, CursorFg) + } else { + termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, DefaultFg, DefaultBg) + } yPos++ - termboxUtil.DrawStringAtPoint("Additional Tags:", 0, yPos, DefaultFg, DefaultBg) + if screen.cursor == FieldTaskTags { + termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, CursorBg, CursorFg) + } else { + termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, DefaultFg, DefaultBg) + } yPos++ for k, v := range screen.displayTask.AdditionalTags { termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s: %s", k, v), 0, yPos, DefaultFg, DefaultBg) yPos++ } + if screen.editing { + w, h := termbox.Size() + termboxUtil.DrawBorder(0, h-4, w-1, h-2, CursorBg, CursorFg) + termboxUtil.FillWithChar(' ', 1, h-3, w-2, h-3, DefaultFg, DefaultBg) + screen.inputField.Draw() + } + screen.drawFooter() } +func (screen *TaskScreen) handleEditingKeyEvent(event termbox.Event) int { + if event.Key == termbox.KeyEnter { + var needsSave bool + // Save the change + switch screen.cursor { + case FieldTaskTodo: + //screen.displayTask.Todo = screen.inputField.GetValue() + if screen.inputField.GetValue() != screen.displayTask.Todo { + screen.displayTask.Todo = screen.inputField.GetValue() + needsSave = true + } + case FieldTaskPriority: + val := screen.inputField.GetValue() + if len(val) > 0 { + val = string(val[0]) + } + if val != screen.displayTask.Priority { + screen.displayTask.Priority = val + needsSave = true + } + case FieldTaskProjects: + projects := splitFields(screen.inputField.GetValue()) + if !isCombination(projects, screen.displayTask.Projects) { + needsSave = true + screen.displayTask.Projects = []string{} + for _, v := range projects { + screen.displayTask.Projects = append(screen.displayTask.Projects, v) + } + } + case FieldTaskContexts: + contexts := splitFields(screen.inputField.GetValue()) + if !isCombination(contexts, screen.displayTask.Contexts) { + needsSave = true + screen.displayTask.Contexts = []string{} + for _, v := range contexts { + screen.displayTask.Contexts = append(screen.displayTask.Contexts, v) + } + } + case FieldTaskTags: + tagsSlice := splitFields(screen.inputField.GetValue()) + if !sliceIsValidTags(tagsSlice) { + screen.message.Set("Tags should be in format :") + return screen.Id() + } + if !isCombination(tagsSlice, tagsToSlice(screen.displayTask.AdditionalTags)) { + needsSave = true + screen.displayTask.AdditionalTags = make(map[string]string) + for k, v := range sliceToTags(tagsSlice) { + screen.displayTask.AdditionalTags[k] = v + } + } + } + if needsSave { + if err := app.saveTask(screen.displayTask); err != nil { + screen.message.SetError(err.Error()) + } + } + screen.editing = false + screen.inputField.SetActive(false) + } else { + if screen.inputField.HandleEvent(event) { + + } + } + return screen.Id() +} + func (screen *TaskScreen) drawHeader() { width, _ := termbox.Size() headerString := screen.displayTask.Todo @@ -94,39 +234,24 @@ func (screen *TaskScreen) drawHeader() { } func (screen *TaskScreen) drawFooter() { - if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout { - screen.clearMessage() - } _, height := termbox.Size() - termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg) + screen.message.DrawAt(0, height-1) } func (screen *TaskScreen) moveCursorDown() bool { - screen.cursor++ + screen.cursor = (screen.cursor + 1) % FieldTaskError return true } func (screen *TaskScreen) moveCursorUp() bool { - screen.cursor-- + if screen.cursor > 0 { + screen.cursor-- + } else { + screen.cursor = FieldTaskError - 1 + } return true } -func (screen *TaskScreen) setMessage(msg string) { - screen.message = msg - screen.messageTime = time.Now() - screen.messageTimeout = time.Second * 2 -} +func (screen *TaskScreen) editCurrentField() { -/* setMessageWithTimeout lets you specify the timeout for the message - * setting it to -1 means it won't timeout - */ -func (screen *TaskScreen) setMessageWithTimeout(msg string, timeout time.Duration) { - screen.message = msg - screen.messageTime = time.Now() - screen.messageTimeout = timeout -} - -func (screen *TaskScreen) clearMessage() { - screen.message = "" - screen.messageTimeout = -1 }