Modifying tasks works
This commit is contained in:
parent
cbfde8c8fb
commit
7abfa8f3cd
11
app_state.go
11
app_state.go
@ -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
78
helpers.go
Normal 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
85
message.go
Normal 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
|
||||||
|
}
|
13
model.go
13
model.go
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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) {
|
||||||
|
211
screen_task.go
211
screen_task.go
@ -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++
|
||||||
|
if screen.cursor == FieldTaskTags {
|
||||||
|
termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, CursorBg, CursorFg)
|
||||||
|
} else {
|
||||||
termboxUtil.DrawStringAtPoint("Additional Tags: ", 0, yPos, DefaultFg, DefaultBg)
|
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
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user