Modifying tasks works

This commit is contained in:
Brian Buller 2019-03-14 10:29:33 -05:00
parent cbfde8c8fb
commit 7abfa8f3cd
7 changed files with 399 additions and 91 deletions

View File

@ -11,6 +11,13 @@ import (
termbox "github.com/nsf/termbox-go" termbox "github.com/nsf/termbox-go"
) )
const (
ScreenIdMain = iota
ScreenIdTask
ScreenIdAbout
ScreenIdExit
)
type AppState struct { type AppState struct {
Name string Name string
Version int Version int
@ -42,8 +49,6 @@ const (
TitleBg = termbox.ColorBlue TitleBg = termbox.ColorBlue
CursorFg = termbox.ColorBlack CursorFg = termbox.ColorBlack
CursorBg = termbox.ColorGreen CursorBg = termbox.ColorGreen
ExitScreenId = -1
) )
func NewApp() *AppState { func NewApp() *AppState {
@ -70,7 +75,7 @@ func (a *AppState) run(parms []string) int {
mainBundle := termboxScreen.Bundle{} mainBundle := termboxScreen.Bundle{}
mainBundle.SetValue(MainBundleListKey, MainBundleListTodo) mainBundle.SetValue(MainBundleListKey, MainBundleListTodo)
if err := a.uiManager.InitializeScreen(MainScreenId, mainBundle); err != nil { if err := a.uiManager.InitializeScreen(ScreenIdMain, mainBundle); err != nil {
return 1 return 1
} }
if err := a.uiManager.Loop(); err != nil { if err := a.uiManager.Loop(); err != nil {

78
helpers.go Normal file
View File

@ -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
}

85
message.go Normal file
View File

@ -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
}

View File

@ -48,6 +48,19 @@ func (a *AppState) addTask(taskString string) error {
return a.WriteList() 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 { func (a *AppState) toggleTaskComplete(id int) error {
if a.diskListChanged() { if a.diskListChanged() {
return a.e(ResStrListChanged) return a.e(ResStrListChanged)

View File

@ -9,8 +9,6 @@ import (
termbox "github.com/nsf/termbox-go" termbox "github.com/nsf/termbox-go"
) )
const AboutScreenId = 2
// AboutScreen holds all that's going on // AboutScreen holds all that's going on
type AboutScreen struct { type AboutScreen struct {
message string message string
@ -27,7 +25,7 @@ type Command struct {
description string description string
} }
func (screen *AboutScreen) Id() int { return AboutScreenId } func (screen *AboutScreen) Id() int { return ScreenIdAbout }
func (screen *AboutScreen) Initialize(bundle termboxScreen.Bundle) error { func (screen *AboutScreen) Initialize(bundle termboxScreen.Bundle) error {
screen.titleTemplate = []string{ 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) ResizeScreen() { screen.Initialize(nil) }
func (screen *AboutScreen) HandleKeyEvent(event termbox.Event) int { 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() { func (screen *AboutScreen) DrawScreen() {
width, height := termbox.Size() width, height := termbox.Size()

View File

@ -12,8 +12,6 @@ import (
termbox "github.com/nsf/termbox-go" termbox "github.com/nsf/termbox-go"
) )
const MainScreenId = 0
type ViewPort struct { type ViewPort struct {
bytesPerRow int bytesPerRow int
numberOfRows int numberOfRows int
@ -50,6 +48,8 @@ const (
MainBundleListTodo = "mainscreen.list.todo" MainBundleListTodo = "mainscreen.list.todo"
MainBundleListDone = "mainscreen.list.done" MainBundleListDone = "mainscreen.list.done"
MainBundleListCurrent = "mainscreen.list.current"
MainBackspaceNothing = iota MainBackspaceNothing = iota
MainBackspaceMain MainBackspaceMain
MainBackspaceFilter MainBackspaceFilter
@ -60,7 +60,7 @@ const (
InputIDUnArchiveTask = "move task to active list? (y/n)" 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 { func (screen *MainScreen) Initialize(bundle termboxScreen.Bundle) error {
width, height := termbox.Size() width, height := termbox.Size()
@ -68,6 +68,10 @@ func (screen *MainScreen) Initialize(bundle termboxScreen.Bundle) error {
screen.cursor = make(map[string]int) screen.cursor = make(map[string]int)
if bundle != nil { 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 { if err := screen.reloadList(bundle); err != nil {
return err return err
} }
@ -146,10 +150,10 @@ func (screen *MainScreen) HandleKeyEvent(event termbox.Event) int {
if event.Ch == '?' { if event.Ch == '?' {
// Go to About Screen // Go to About Screen
b := termboxScreen.Bundle{} 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()) screen.setErrorMessage(err.Error())
} }
return AboutScreenId return ScreenIdAbout
} else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { } else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
if screen.backspaceDoes == MainBackspaceNothing { if screen.backspaceDoes == MainBackspaceNothing {
@ -163,7 +167,7 @@ func (screen *MainScreen) HandleKeyEvent(event termbox.Event) int {
} else if screen.backspaceDoes == MainBackspaceFilter { } else if screen.backspaceDoes == MainBackspaceFilter {
screen.reloadList(screen.buildBundle(screen.currentList, "")) screen.reloadList(screen.buildBundle(screen.currentList, ""))
} }
return MainScreenId return screen.Id()
} else if event.Key == termbox.KeySpace { } else if event.Key == termbox.KeySpace {
return screen.toggleTaskComplete() return screen.toggleTaskComplete()
@ -223,11 +227,11 @@ func (screen *MainScreen) HandleKeyEvent(event termbox.Event) int {
screen.confirmArchiveItem() screen.confirmArchiveItem()
} else if event.Ch == 'q' { } 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 { func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
switch screen.inputField.GetID() { switch screen.inputField.GetID() {
@ -239,7 +243,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
screen.inputField.SetValue("") screen.inputField.SetValue("")
screen.backspaceDoes = MainBackspaceFilter screen.backspaceDoes = MainBackspaceFilter
screen.reloadList(screen.buildBundle(screen.currentList, filter)) screen.reloadList(screen.buildBundle(screen.currentList, filter))
return MainScreenId return screen.Id()
} }
case InputIDAddTask: case InputIDAddTask:
if event.Key == termbox.KeyEnter { if event.Key == termbox.KeyEnter {
@ -251,7 +255,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
screen.inputField.SetID("") screen.inputField.SetID("")
screen.inputField.SetValue("") screen.inputField.SetValue("")
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
return MainScreenId return screen.Id()
} }
case InputIDIncompleteArchive: case InputIDIncompleteArchive:
if event.Ch == 'y' || event.Ch == 'Y' { if event.Ch == 'y' || event.Ch == 'Y' {
@ -260,7 +264,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
screen.inputField.SetID("") screen.inputField.SetID("")
screen.inputField.SetValue("") screen.inputField.SetValue("")
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
return MainScreenId return screen.Id()
case InputIDUnArchiveTask: case InputIDUnArchiveTask:
if event.Ch == 'y' || event.Ch == 'Y' { if event.Ch == 'y' || event.Ch == 'Y' {
screen.inputField.SetID("") screen.inputField.SetID("")
@ -271,23 +275,23 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int {
screen.inputField.SetID("") screen.inputField.SetID("")
screen.inputField.SetValue("") screen.inputField.SetValue("")
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
return MainScreenId return screen.Id()
} }
if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 {
if screen.inputField.GetValue() == "" { if screen.inputField.GetValue() == "" {
screen.reloadList(screen.buildBundle(screen.currentList, screen.inputField.GetValue())) screen.reloadList(screen.buildBundle(screen.currentList, screen.inputField.GetValue()))
screen.inputField.SetID("") screen.inputField.SetID("")
screen.inputField.SetValue("") screen.inputField.SetValue("")
return MainScreenId return screen.Id()
} }
} else if event.Key == termbox.KeyEsc { } else if event.Key == termbox.KeyEsc {
screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter))
screen.inputField.SetID("") screen.inputField.SetID("")
screen.inputField.SetValue("") screen.inputField.SetValue("")
return MainScreenId return screen.Id()
} }
screen.inputField.HandleEvent(event) screen.inputField.HandleEvent(event)
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) setActiveList(list *todotxt.TaskList) { func (screen *MainScreen) setActiveList(list *todotxt.TaskList) {
@ -365,7 +369,7 @@ func (screen *MainScreen) drawFooter() {
func (screen *MainScreen) confirmArchiveItem() int { func (screen *MainScreen) confirmArchiveItem() int {
if screen.currentList != MainBundleListTodo { if screen.currentList != MainBundleListTodo {
screen.inputField.SetID(InputIDUnArchiveTask) screen.inputField.SetID(InputIDUnArchiveTask)
return MainScreenId return screen.Id()
} }
// Find the task under the cursor // Find the task under the cursor
if screen.cursor[screen.currentList] < len(*screen.displayList) { if screen.cursor[screen.currentList] < len(*screen.displayList) {
@ -377,20 +381,20 @@ func (screen *MainScreen) confirmArchiveItem() int {
return screen.archiveCurrentItem() return screen.archiveCurrentItem()
} }
} }
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) archiveCurrentItem() int { func (screen *MainScreen) archiveCurrentItem() int {
if screen.currentList != MainBundleListTodo { if screen.currentList != MainBundleListTodo {
screen.setErrorMessage("Task is already archived") screen.setErrorMessage("Task is already archived")
return MainScreenId return screen.Id()
} }
// Find the task under the cursor // Find the task under the cursor
if len(*screen.displayList) > screen.cursor[screen.currentList] { if len(*screen.displayList) > screen.cursor[screen.currentList] {
t := (*screen.displayList)[screen.cursor[screen.currentList]] t := (*screen.displayList)[screen.cursor[screen.currentList]]
if err := app.archiveTask(t.Id); err != nil { if err := app.archiveTask(t.Id); err != nil {
screen.setErrorMessage(err.Error()) screen.setErrorMessage(err.Error())
return MainScreenId return screen.Id()
} }
// Reload the list // Reload the list
b := screen.buildBundle(screen.currentList, screen.currentFilter) b := screen.buildBundle(screen.currentList, screen.currentFilter)
@ -398,20 +402,20 @@ func (screen *MainScreen) archiveCurrentItem() int {
screen.setErrorMessage(err.Error()) screen.setErrorMessage(err.Error())
} }
} }
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) unarchiveCurrentItem() int { func (screen *MainScreen) unarchiveCurrentItem() int {
if screen.currentList == MainBundleListTodo { if screen.currentList == MainBundleListTodo {
screen.setErrorMessage("Task is not archived") screen.setErrorMessage("Task is not archived")
return MainScreenId return screen.Id()
} }
// Find the task under the cursor // Find the task under the cursor
if len(*screen.displayList) > screen.cursor[screen.currentList] { if len(*screen.displayList) > screen.cursor[screen.currentList] {
t := (*screen.displayList)[screen.cursor[screen.currentList]] t := (*screen.displayList)[screen.cursor[screen.currentList]]
if err := app.unarchiveTask(t.Id); err != nil { if err := app.unarchiveTask(t.Id); err != nil {
screen.setErrorMessage(err.Error()) screen.setErrorMessage(err.Error())
return MainScreenId return screen.Id()
} }
// Reload the list // Reload the list
b := screen.buildBundle(screen.currentList, screen.currentFilter) b := screen.buildBundle(screen.currentList, screen.currentFilter)
@ -419,7 +423,7 @@ func (screen *MainScreen) unarchiveCurrentItem() int {
screen.setErrorMessage(err.Error()) screen.setErrorMessage(err.Error())
} }
} }
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) startEditTaskScreen() int { func (screen *MainScreen) startEditTaskScreen() int {
@ -429,13 +433,13 @@ func (screen *MainScreen) startEditTaskScreen() int {
// Load the task screen with this task // Load the task screen with this task
b := termboxScreen.Bundle{} b := termboxScreen.Bundle{}
b.SetValue(TaskBundleTaskIdKey, t.Id) 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()) screen.setErrorMessage(err.Error())
return MainScreenId return screen.Id()
} }
return TaskScreenId return ScreenIdTask
} }
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) reloadCurrentView() { func (screen *MainScreen) reloadCurrentView() {
@ -455,18 +459,18 @@ func (screen *MainScreen) toggleViewList() int {
} }
bundle.SetValue(MainBundleFilterKey, screen.currentFilter) bundle.SetValue(MainBundleFilterKey, screen.currentFilter)
screen.reloadList(bundle) screen.reloadList(bundle)
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) startAddNewTask() int { func (screen *MainScreen) startAddNewTask() int {
screen.inputField.SetID(InputIDAddTask) screen.inputField.SetID(InputIDAddTask)
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) toggleTaskComplete() int { func (screen *MainScreen) toggleTaskComplete() int {
if screen.currentList == MainBundleListDone { if screen.currentList == MainBundleListDone {
screen.setErrorMessage("Task is archived, unable to modify.") screen.setErrorMessage("Task is archived, unable to modify.")
return MainScreenId return screen.Id()
} }
// Find the task under the cursor // Find the task under the cursor
@ -475,11 +479,11 @@ func (screen *MainScreen) toggleTaskComplete() int {
err := app.toggleTaskComplete(t.Id) err := app.toggleTaskComplete(t.Id)
if err != nil { if err != nil {
screen.setErrorMessage(err.Error()) screen.setErrorMessage(err.Error())
return MainScreenId return screen.Id()
} }
} }
screen.reloadCurrentView() screen.reloadCurrentView()
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) moveCursorDown() bool { func (screen *MainScreen) moveCursorDown() bool {
@ -502,7 +506,7 @@ func (screen *MainScreen) moveCursorUp() bool {
func (screen *MainScreen) startFilter() int { func (screen *MainScreen) startFilter() int {
screen.inputField.SetID(InputIDFilter) screen.inputField.SetID(InputIDFilter)
return MainScreenId return screen.Id()
} }
func (screen *MainScreen) setErrorMessage(msg string) { func (screen *MainScreen) setErrorMessage(msg string) {

View File

@ -12,30 +12,62 @@ import (
termbox "github.com/nsf/termbox-go" 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 ( const (
FieldTaskTodo = iota
FieldTaskPriority
FieldTaskProjects
FieldTaskContexts
FieldTaskTags
FieldTaskError
TaskBundleTaskIdKey = "taskscreen.taskid" 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 { 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 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 { if bundle != nil {
screen.currentTaskId = bundle.GetInt(TaskBundleTaskIdKey, -1) 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 { if screen.displayTask, err = app.TaskList.GetTask(screen.currentTaskId); err != nil {
return err return err
} }
screen.cursor = FieldTaskTodo
return nil return nil
} }
func (screen *TaskScreen) ResizeScreen() { screen.Initialize(nil) } func (screen *TaskScreen) ResizeScreen() { screen.Initialize(nil) }
func (screen *TaskScreen) HandleKeyEvent(event termbox.Event) int { func (screen *TaskScreen) HandleKeyEvent(event termbox.Event) int {
if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 || event.Ch == 'h' || event.Key == termbox.KeyArrowLeft { if screen.editing {
return MainScreenId 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 { } else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown {
screen.moveCursorDown() screen.moveCursorDown()
@ -60,32 +106,126 @@ func (screen *TaskScreen) HandleKeyEvent(event termbox.Event) int {
} else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp { } else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp {
screen.moveCursorUp() 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() { func (screen *TaskScreen) DrawScreen() {
screen.drawHeader() screen.drawHeader()
yPos := 1 yPos := 1
if screen.cursor == FieldTaskTodo {
termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, CursorBg, CursorFg)
} else {
termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, DefaultFg, DefaultBg) termboxUtil.DrawStringAtPoint(screen.displayTask.Todo, 0, yPos, DefaultFg, DefaultBg)
}
yPos++ yPos++
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) termboxUtil.DrawStringAtPoint(fmt.Sprintf("Priority: %s", screen.displayTask.Priority), 0, yPos, DefaultFg, DefaultBg)
}
yPos++ yPos++
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) termboxUtil.DrawStringAtPoint(fmt.Sprintf("Projects: %s", screen.displayTask.Projects), 0, yPos, DefaultFg, DefaultBg)
}
yPos++ yPos++
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) termboxUtil.DrawStringAtPoint(fmt.Sprintf("Contexts: %s", screen.displayTask.Contexts), 0, yPos, DefaultFg, DefaultBg)
}
yPos++ 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++ yPos++
for k, v := range screen.displayTask.AdditionalTags { for k, v := range screen.displayTask.AdditionalTags {
termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s: %s", k, v), 0, yPos, DefaultFg, DefaultBg) termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s: %s", k, v), 0, yPos, DefaultFg, DefaultBg)
yPos++ 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() 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 <key>:<val>")
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() { func (screen *TaskScreen) drawHeader() {
width, _ := termbox.Size() width, _ := termbox.Size()
headerString := screen.displayTask.Todo headerString := screen.displayTask.Todo
@ -94,39 +234,24 @@ func (screen *TaskScreen) drawHeader() {
} }
func (screen *TaskScreen) drawFooter() { func (screen *TaskScreen) drawFooter() {
if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout {
screen.clearMessage()
}
_, height := termbox.Size() _, height := termbox.Size()
termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg) screen.message.DrawAt(0, height-1)
} }
func (screen *TaskScreen) moveCursorDown() bool { func (screen *TaskScreen) moveCursorDown() bool {
screen.cursor++ screen.cursor = (screen.cursor + 1) % FieldTaskError
return true return true
} }
func (screen *TaskScreen) moveCursorUp() bool { func (screen *TaskScreen) moveCursorUp() bool {
if screen.cursor > 0 {
screen.cursor-- screen.cursor--
} else {
screen.cursor = FieldTaskError - 1
}
return true return true
} }
func (screen *TaskScreen) setMessage(msg string) { func (screen *TaskScreen) editCurrentField() {
screen.message = msg
screen.messageTime = time.Now()
screen.messageTimeout = time.Second * 2
}
/* 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
} }