Creating Projects, Tasks and editing Task details, done/resume working.
This commit is contained in:
121
app/cli.go
Normal file
121
app/cli.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/asdine/storm/v3"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"github.com/ajaxray/geek-life/model"
|
||||
"github.com/ajaxray/geek-life/repository"
|
||||
repo "github.com/ajaxray/geek-life/repository/storm"
|
||||
"github.com/ajaxray/geek-life/util"
|
||||
)
|
||||
|
||||
var (
|
||||
app *tview.Application
|
||||
newProject, newTask *tview.InputField
|
||||
projectList, taskList *tview.List
|
||||
projectPane, taskPane, detailPane *tview.Flex
|
||||
layout, contents *tview.Flex
|
||||
statusBar *tview.Pages
|
||||
message *tview.TextView
|
||||
shortcutsPage, messagePage string = "shortcuts", "message"
|
||||
|
||||
db *storm.DB
|
||||
projectRepo repository.ProjectRepository
|
||||
taskRepo repository.TaskRepository
|
||||
|
||||
projects []model.Project
|
||||
currentProject model.Project
|
||||
)
|
||||
|
||||
func main() {
|
||||
app = tview.NewApplication()
|
||||
|
||||
db = util.ConnectStorm()
|
||||
defer db.Close()
|
||||
|
||||
projectRepo = repo.NewProjectRepository(db)
|
||||
taskRepo = repo.NewTaskRepository(db)
|
||||
|
||||
titleText := tview.NewTextView().SetText("[lime::b]Geek-life [::-]- life management for geeks!").SetDynamicColors(true)
|
||||
cloudStatus := tview.NewTextView().SetText("[::d]Cloud Sync: off").SetTextAlign(tview.AlignRight).SetDynamicColors(true)
|
||||
|
||||
titleBar := tview.NewFlex().
|
||||
AddItem(titleText, 0, 2, false).
|
||||
AddItem(cloudStatus, 0, 1, false)
|
||||
|
||||
prepareProjectPane()
|
||||
prepareTaskPane()
|
||||
prepareStatusBar()
|
||||
prepareDetailPane()
|
||||
|
||||
contents = tview.NewFlex().
|
||||
AddItem(projectPane, 25, 1, true).
|
||||
AddItem(taskPane, 0, 2, false)
|
||||
//AddItem(detailPane, 0, 3, true)
|
||||
|
||||
layout = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(titleBar, 2, 1, false).
|
||||
AddItem(contents, 0, 2, true).
|
||||
AddItem(statusBar, 1, 1, false)
|
||||
|
||||
setKeyboardShortcuts(projectPane, taskPane)
|
||||
|
||||
if err := app.SetRoot(layout, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setKeyboardShortcuts(projectPane *tview.Flex, taskPane *tview.Flex) *tview.Application {
|
||||
return app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if ignoreKeyEvt() {
|
||||
return event
|
||||
}
|
||||
switch event.Rune() {
|
||||
case 'p':
|
||||
app.SetFocus(projectPane)
|
||||
case 't':
|
||||
app.SetFocus(taskPane)
|
||||
case 'n':
|
||||
if projectPane.HasFocus() {
|
||||
app.SetFocus(newProject)
|
||||
} else if taskPane.HasFocus() {
|
||||
app.SetFocus(newTask)
|
||||
}
|
||||
case 'e':
|
||||
if detailPane.HasFocus() {
|
||||
activateEditor()
|
||||
} else {
|
||||
// @TODO : Remove
|
||||
showMessage(reflect.TypeOf(app.GetFocus()).String())
|
||||
}
|
||||
case 'f':
|
||||
// @TODO : Remove
|
||||
showMessage(reflect.TypeOf(app.GetFocus()).String())
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func prepareStatusBar() {
|
||||
statusBar = tview.NewPages()
|
||||
|
||||
message = tview.NewTextView().SetDynamicColors(true).SetText("Loading...")
|
||||
statusBar.AddPage(messagePage, message, true, true)
|
||||
|
||||
statusBar.AddPage(shortcutsPage,
|
||||
tview.NewGrid().
|
||||
SetColumns(0, 0, 0, 0).
|
||||
SetRows(0).
|
||||
AddItem(tview.NewTextView().SetText("Shortcuts: Alt+.(dot)"), 0, 0, 1, 1, 0, 0, false).
|
||||
AddItem(tview.NewTextView().SetText("New Project: n").SetTextAlign(tview.AlignCenter), 0, 1, 1, 1, 0, 0, false).
|
||||
AddItem(tview.NewTextView().SetText("New Task: t").SetTextAlign(tview.AlignCenter), 0, 2, 1, 1, 0, 0, false).
|
||||
AddItem(tview.NewTextView().SetText("Quit: Ctrl+C").SetTextAlign(tview.AlignRight), 0, 3, 1, 1, 0, 0, false),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
78
app/projects.go
Normal file
78
app/projects.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/asdine/storm/v3"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
func prepareProjectPane() {
|
||||
var err error
|
||||
projects, err = projectRepo.GetAll()
|
||||
if err != nil {
|
||||
showMessage("Could not load Projects: " + err.Error())
|
||||
}
|
||||
|
||||
projectList = tview.NewList().ShowSecondaryText(false)
|
||||
|
||||
for i := range projects {
|
||||
addProjectToList(i, false)
|
||||
}
|
||||
|
||||
newProject = makeLightTextInput("+[New Project]").
|
||||
SetDoneFunc(func(key tcell.Key) {
|
||||
switch key {
|
||||
case tcell.KeyEnter:
|
||||
project, err := projectRepo.Create(newProject.GetText(), "")
|
||||
if err != nil {
|
||||
showMessage("[red::]Failed to create Project:" + err.Error())
|
||||
} else {
|
||||
showMessage(fmt.Sprintf("[green::]Project %s created. Press n to start adding new tasks.", newProject.GetText()))
|
||||
projects = append(projects, project)
|
||||
addProjectToList(len(projects)-1, true)
|
||||
newProject.SetText("")
|
||||
}
|
||||
case tcell.KeyEsc:
|
||||
app.SetFocus(projectPane)
|
||||
}
|
||||
})
|
||||
|
||||
projectPane = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(projectList, 0, 1, true).
|
||||
AddItem(newProject, 1, 0, false)
|
||||
|
||||
projectPane.SetBorder(true).SetTitle("[::u]P[::-]rojects")
|
||||
}
|
||||
|
||||
func addProjectToList(i int, selectItem bool) {
|
||||
// To avoid overriding of loop variables - https://www.calhoun.io/gotchas-and-common-mistakes-with-closures-in-go/
|
||||
projectList.AddItem("- "+projects[i].Title, "", 0, func(idx int) func() {
|
||||
return func() { loadProject(idx) }
|
||||
}(i))
|
||||
|
||||
if selectItem {
|
||||
projectList.SetCurrentItem(i)
|
||||
loadProject(i)
|
||||
}
|
||||
}
|
||||
|
||||
func loadProject(idx int) {
|
||||
currentProject = projects[idx]
|
||||
taskList.Clear()
|
||||
app.SetFocus(taskPane)
|
||||
var err error
|
||||
|
||||
if tasks, err = taskRepo.GetAllByProject(currentProject); err != nil && err != storm.ErrNotFound {
|
||||
showMessage("[red::]Error: " + err.Error())
|
||||
}
|
||||
|
||||
for i, task := range tasks {
|
||||
taskList.AddItem(makeTaskListingTitle(task), "", 0, func(taskidx int) func() {
|
||||
return func() { loadTask(taskidx) }
|
||||
}(i))
|
||||
}
|
||||
|
||||
contents.RemoveItem(detailPane)
|
||||
}
|
||||
183
app/task_detail.go
Normal file
183
app/task_detail.go
Normal file
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/pgavlin/femto"
|
||||
"github.com/pgavlin/femto/runtime"
|
||||
"github.com/rivo/tview"
|
||||
)
|
||||
|
||||
var (
|
||||
taskName *tview.TextView
|
||||
taskDate *tview.InputField
|
||||
taskDetailView *femto.View
|
||||
taskStatusToggle *tview.Button
|
||||
colorscheme femto.Colorscheme
|
||||
)
|
||||
|
||||
const dateLayoutISO = "2006-01-02"
|
||||
|
||||
func prepareDetailPane() {
|
||||
taskName = tview.NewTextView().SetDynamicColors(true)
|
||||
hr := makeHorizontalLine(tview.BoxDrawingsLightHorizontal)
|
||||
|
||||
prepareDetailsEditor()
|
||||
|
||||
taskStatusToggle = makeButton("Complete", func() {}).SetLabelColor(tcell.ColorLightGray)
|
||||
|
||||
hint := tview.NewTextView().SetTextColor(tcell.ColorYellow).
|
||||
SetText("press Enter to save changes, Esc to ignore")
|
||||
|
||||
detailPane = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(taskName, 2, 1, true).
|
||||
AddItem(hr, 1, 1, false).
|
||||
AddItem(nil, 1, 1, false).
|
||||
AddItem(makeDateRow(), 1, 1, true).
|
||||
AddItem(taskDetailView, 15, 4, false).
|
||||
AddItem(tview.NewTextView(), 1, 1, false).
|
||||
AddItem(hint, 1, 1, false).
|
||||
AddItem(nil, 0, 1, false).
|
||||
AddItem(taskStatusToggle, 3, 1, false)
|
||||
|
||||
detailPane.SetBorder(true).SetTitle("Detail")
|
||||
|
||||
// taskName is the default focus attracting child of detailPane
|
||||
taskName.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
|
||||
switch event.Key() {
|
||||
case tcell.KeyEsc:
|
||||
app.SetFocus(taskPane)
|
||||
case tcell.KeyDown:
|
||||
taskDetailView.ScrollDown(1)
|
||||
case tcell.KeyUp:
|
||||
taskDetailView.ScrollUp(1)
|
||||
case tcell.KeyRune:
|
||||
// switch event.Rune() {
|
||||
// case 'n':
|
||||
// app.SetFocus(projectPane)
|
||||
// case 'e':
|
||||
// if detailPane.HasFocus() {
|
||||
// activateEditor()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func makeDateRow() *tview.Flex {
|
||||
taskDate = makeLightTextInput("yyyy-mm-dd").
|
||||
SetLabel("Due Date: ").
|
||||
SetLabelColor(tcell.ColorGray).
|
||||
SetFieldWidth(12)
|
||||
|
||||
todaySelector := func() {
|
||||
taskDate.SetText(time.Now().Format(dateLayoutISO))
|
||||
}
|
||||
|
||||
nextDaySelector := func() {
|
||||
currentText := taskDate.GetText()
|
||||
if date, err := time.Parse(dateLayoutISO, currentText); err == nil {
|
||||
taskDate.SetText(date.AddDate(0, 0, 1).Format(dateLayoutISO))
|
||||
} else {
|
||||
taskDate.SetText(time.Now().AddDate(0, 0, 1).Format(dateLayoutISO))
|
||||
}
|
||||
}
|
||||
|
||||
prevDaySelector := func() {
|
||||
currentText := taskDate.GetText()
|
||||
if date, err := time.Parse(dateLayoutISO, currentText); err == nil {
|
||||
taskDate.SetText(date.AddDate(0, 0, -1).Format(dateLayoutISO))
|
||||
} else {
|
||||
taskDate.SetText(time.Now().AddDate(0, 0, -1).Format(dateLayoutISO))
|
||||
}
|
||||
}
|
||||
|
||||
return tview.NewFlex().
|
||||
AddItem(taskDate, 25, 2, true).
|
||||
AddItem(nil, 1, 0, false).
|
||||
AddItem(makeButton("today", todaySelector), 8, 1, false).
|
||||
AddItem(nil, 1, 0, false).
|
||||
AddItem(makeButton("+1", nextDaySelector), 4, 1, false).
|
||||
AddItem(nil, 1, 0, false).
|
||||
AddItem(makeButton("-1", prevDaySelector), 4, 1, false)
|
||||
}
|
||||
|
||||
func setStatusToggle(idx int) {
|
||||
action := func(i int, label string, color tcell.Color, status bool) {
|
||||
taskStatusToggle.SetLabel(label).SetBackgroundColor(color)
|
||||
taskStatusToggle.SetSelectedFunc(func() {
|
||||
if taskRepo.UpdateField(currentTask, "Completed", status) == nil {
|
||||
currentTask.Completed = status
|
||||
loadTask(i)
|
||||
taskList.SetItemText(i, makeTaskListingTitle(*currentTask), "")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if currentTask.Completed {
|
||||
action(idx, "Resume", tcell.ColorMaroon, false)
|
||||
} else {
|
||||
action(idx, "Complete", tcell.ColorDarkGreen, true)
|
||||
}
|
||||
}
|
||||
|
||||
func prepareDetailsEditor() {
|
||||
taskDetailView = femto.NewView(makeBufferFromString(""))
|
||||
taskDetailView.SetRuntimeFiles(runtime.Files)
|
||||
|
||||
// var colorscheme femto.Colorscheme
|
||||
if monokai := runtime.Files.FindFile(femto.RTColorscheme, "monokai"); monokai != nil {
|
||||
if data, err := monokai.Data(); err == nil {
|
||||
colorscheme = femto.ParseColorscheme(string(data))
|
||||
}
|
||||
}
|
||||
taskDetailView.SetColorscheme(colorscheme)
|
||||
taskDetailView.SetBorder(true)
|
||||
taskDetailView.SetBorderColor(tcell.ColorLightSlateGray)
|
||||
|
||||
taskDetailView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
switch event.Key() {
|
||||
case tcell.KeyEsc:
|
||||
currentTask.Details = taskDetailView.Buf.String()
|
||||
err := taskRepo.Update(currentTask)
|
||||
if err == nil {
|
||||
showMessage("[lime]Saved task detail")
|
||||
} else {
|
||||
showMessage("[red]Could not save: " + err.Error())
|
||||
}
|
||||
|
||||
deactivateEditor()
|
||||
return nil
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func makeBufferFromString(content string) *femto.Buffer {
|
||||
buff := femto.NewBufferFromString(content, "")
|
||||
// taskDetail.Settings["ruler"] = false
|
||||
buff.Settings["filetype"] = "markdown"
|
||||
buff.Settings["keepautoindent"] = true
|
||||
buff.Settings["statusline"] = false
|
||||
buff.Settings["softwrap"] = true
|
||||
buff.Settings["scrollbar"] = true
|
||||
|
||||
return buff
|
||||
}
|
||||
|
||||
func activateEditor() {
|
||||
taskDetailView.Readonly = false
|
||||
taskDetailView.SetBorderColor(tcell.ColorDarkOrange)
|
||||
app.SetFocus(taskDetailView)
|
||||
}
|
||||
|
||||
func deactivateEditor() {
|
||||
taskDetailView.Readonly = true
|
||||
taskDetailView.SetBorderColor(tcell.ColorLightSlateGray)
|
||||
app.SetFocus(detailPane)
|
||||
}
|
||||
84
app/tasks.go
Normal file
84
app/tasks.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"github.com/ajaxray/geek-life/model"
|
||||
)
|
||||
|
||||
var (
|
||||
tasks []model.Task
|
||||
currentTask *model.Task
|
||||
)
|
||||
|
||||
func prepareTaskPane() {
|
||||
taskList = tview.NewList().ShowSecondaryText(false)
|
||||
taskList.SetDoneFunc(func() {
|
||||
app.SetFocus(projectPane)
|
||||
})
|
||||
|
||||
newTask = makeLightTextInput("+[New Task]").
|
||||
SetDoneFunc(func(key tcell.Key) {
|
||||
switch key {
|
||||
case tcell.KeyEnter:
|
||||
task, err := taskRepo.Create(currentProject, newTask.GetText(), "", "", time.Now().Unix())
|
||||
if err != nil {
|
||||
showMessage("[red::]Could not create Task:" + err.Error())
|
||||
}
|
||||
|
||||
taskList.AddItem(task.Title, "", 0, nil)
|
||||
newTask.SetText("")
|
||||
case tcell.KeyEsc:
|
||||
app.SetFocus(taskPane)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
taskPane = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(taskList, 0, 1, true).
|
||||
AddItem(newTask, 1, 0, false)
|
||||
|
||||
taskPane.SetBorder(true).SetTitle("[::u]T[::-]asks")
|
||||
}
|
||||
|
||||
func loadTask(idx int) {
|
||||
contents.RemoveItem(detailPane)
|
||||
currentTask = &tasks[idx]
|
||||
|
||||
taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*currentTask), currentTask.Title))
|
||||
taskDetailView.Buf = makeBufferFromString(currentTask.Details)
|
||||
taskDetailView.SetColorscheme(colorscheme)
|
||||
taskDetailView.Start()
|
||||
|
||||
taskDate.SetText("")
|
||||
if currentTask.DueDate != 0 {
|
||||
taskDate.SetText(time.Unix(currentTask.DueDate, 0).Format(dateLayoutISO))
|
||||
}
|
||||
|
||||
contents.AddItem(detailPane, 0, 3, false)
|
||||
setStatusToggle(idx)
|
||||
deactivateEditor()
|
||||
}
|
||||
|
||||
func getTaskTitleColor(task model.Task) string {
|
||||
colorName := "whitesmoke"
|
||||
if task.Completed {
|
||||
colorName = "lime"
|
||||
} else if task.DueDate != 0 && task.DueDate < time.Now().Unix() {
|
||||
colorName = "red"
|
||||
}
|
||||
|
||||
return colorName
|
||||
}
|
||||
|
||||
func makeTaskListingTitle(task model.Task) string {
|
||||
checkbox := "[ []"
|
||||
if task.Completed {
|
||||
checkbox = "[x[]"
|
||||
}
|
||||
return fmt.Sprintf("[%s]%s %s", getTaskTitleColor(task), checkbox, task.Title)
|
||||
}
|
||||
243
app/tui.go
243
app/tui.go
@@ -1,243 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/asdine/storm/v3"
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"github.com/ajaxray/geek-life/model"
|
||||
"github.com/ajaxray/geek-life/repository"
|
||||
repo "github.com/ajaxray/geek-life/repository/storm"
|
||||
"github.com/ajaxray/geek-life/util"
|
||||
)
|
||||
|
||||
var (
|
||||
app *tview.Application
|
||||
newProject, newTask *tview.InputField
|
||||
projectList, taskList *tview.List
|
||||
projectPane, taskPane *tview.Flex
|
||||
statusBar *tview.Pages
|
||||
message *tview.TextView
|
||||
shortcutsPage, messagePage string = "shortcuts", "message"
|
||||
|
||||
db *storm.DB
|
||||
projectRepo repository.ProjectRepository
|
||||
taskRepo repository.TaskRepository
|
||||
|
||||
projects []model.Project
|
||||
currentProject model.Project
|
||||
|
||||
tasks []model.Task
|
||||
currentTask model.Task
|
||||
)
|
||||
|
||||
func main() {
|
||||
app = tview.NewApplication()
|
||||
|
||||
db = util.ConnectStorm()
|
||||
defer db.Close()
|
||||
|
||||
projectRepo = repo.NewProjectRepository(db)
|
||||
taskRepo = repo.NewTaskRepository(db)
|
||||
|
||||
titleText := tview.NewTextView().SetText("[lime::b]Geek-life [::-]- life management for geeks!").SetDynamicColors(true)
|
||||
cloudStatus := tview.NewTextView().SetText("[::d]Cloud Sync: off").SetTextAlign(tview.AlignRight).SetDynamicColors(true)
|
||||
|
||||
prepareStatusBar()
|
||||
|
||||
titleBar := tview.NewFlex().
|
||||
AddItem(titleText, 0, 2, false).
|
||||
AddItem(cloudStatus, 0, 1, false)
|
||||
|
||||
projectPane = makeProjectPane()
|
||||
taskPane = makeTaskPane()
|
||||
|
||||
layout := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(titleBar, 2, 1, false).
|
||||
AddItem(tview.NewFlex().
|
||||
AddItem(projectPane, 25, 1, true).
|
||||
AddItem(taskPane, 0, 2, false).
|
||||
AddItem(tview.NewBox().SetBorder(true).SetTitle("Details"), 0, 3, false),
|
||||
0, 2, true).
|
||||
AddItem(statusBar, 1, 1, false)
|
||||
|
||||
setKeyboardShortcuts(projectPane, taskPane)
|
||||
|
||||
if err := app.SetRoot(layout, true).EnableMouse(true).Run(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func makeProjectPane() *tview.Flex {
|
||||
var err error
|
||||
projects, err = projectRepo.GetAll()
|
||||
util.FatalIfError(err, "Could not load Projects")
|
||||
|
||||
projectList = tview.NewList()
|
||||
|
||||
for i := range projects {
|
||||
addProjectToList(i, false)
|
||||
}
|
||||
|
||||
newProject = makeLightTextInput("+[New Project]").
|
||||
SetDoneFunc(func(key tcell.Key) {
|
||||
switch key {
|
||||
case tcell.KeyEnter:
|
||||
project, err := projectRepo.Create(newProject.GetText(), "")
|
||||
if err != nil {
|
||||
showMessage("[red::]Failed to create Project:" + err.Error())
|
||||
} else {
|
||||
showMessage(fmt.Sprintf("[green::]Project %s created. Press n to start adding new tasks.", newProject.GetText()))
|
||||
projects = append(projects, project)
|
||||
addProjectToList(len(projects)-1, true)
|
||||
newProject.SetText("")
|
||||
}
|
||||
case tcell.KeyEsc:
|
||||
app.SetFocus(projectPane)
|
||||
}
|
||||
})
|
||||
|
||||
projectBar := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(projectList, 0, 1, true).
|
||||
AddItem(newProject, 1, 0, false)
|
||||
|
||||
projectBar.SetBorder(true).SetTitle("Projects (p)")
|
||||
|
||||
return projectBar
|
||||
}
|
||||
|
||||
func addProjectToList(i int, selectItem bool) {
|
||||
// To avoid overriding of loop variables - https://www.calhoun.io/gotchas-and-common-mistakes-with-closures-in-go/
|
||||
projectList.AddItem(projects[i].Title, "", 0, func(idx int) func() {
|
||||
return func() { loadProject(idx) }
|
||||
}(i))
|
||||
|
||||
if selectItem {
|
||||
projectList.SetCurrentItem(i)
|
||||
loadProject(i)
|
||||
}
|
||||
}
|
||||
|
||||
func makeTaskPane() *tview.Flex {
|
||||
taskList = tview.NewList().ShowSecondaryText(false)
|
||||
taskList.SetDoneFunc(func() {
|
||||
app.SetFocus(projectPane)
|
||||
})
|
||||
|
||||
newTask = makeLightTextInput("+[New Task]").
|
||||
SetDoneFunc(func(key tcell.Key) {
|
||||
switch key {
|
||||
case tcell.KeyEnter:
|
||||
task, err := taskRepo.Create(currentProject, newTask.GetText(), "", "", time.Now().Unix())
|
||||
if err != nil {
|
||||
showMessage("[red::]Could not create Task:" + err.Error())
|
||||
}
|
||||
|
||||
taskList.AddItem(task.Title, "", 0, nil)
|
||||
newTask.SetText("")
|
||||
case tcell.KeyEsc:
|
||||
app.SetFocus(taskPane)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
taskPane := tview.NewFlex().SetDirection(tview.FlexRow).
|
||||
AddItem(taskList, 0, 1, true).
|
||||
AddItem(newTask, 1, 0, false)
|
||||
|
||||
taskPane.SetBorder(true).SetTitle("Tasks (t)")
|
||||
|
||||
return taskPane
|
||||
}
|
||||
|
||||
func loadProject(idx int) {
|
||||
currentProject = projects[idx]
|
||||
taskList.Clear()
|
||||
app.SetFocus(taskPane)
|
||||
var err error
|
||||
|
||||
if tasks, err = taskRepo.GetAllByProject(currentProject); err != nil && err != storm.ErrNotFound {
|
||||
showMessage("[red::]Error: " + err.Error())
|
||||
}
|
||||
|
||||
for i, task := range tasks {
|
||||
taskList.AddItem(task.Title, "", 0, func(taskidx int) func() {
|
||||
return func() { loadTask(taskidx) }
|
||||
}(i))
|
||||
}
|
||||
}
|
||||
|
||||
func loadTask(idx int) {
|
||||
currentTask = tasks[idx]
|
||||
// taskList.Clear()
|
||||
// app.SetFocus(taskPane)
|
||||
}
|
||||
|
||||
func makeLightTextInput(placeholder string) *tview.InputField {
|
||||
return tview.NewInputField().
|
||||
SetPlaceholder(placeholder).
|
||||
SetPlaceholderTextColor(tcell.ColorLightSlateGray).
|
||||
SetFieldTextColor(tcell.ColorBlack).
|
||||
SetFieldBackgroundColor(tcell.ColorGray)
|
||||
}
|
||||
|
||||
func ignoreKeyEvt() bool {
|
||||
return reflect.TypeOf(app.GetFocus()).String() == "*tview.InputField"
|
||||
}
|
||||
|
||||
func setKeyboardShortcuts(projectPane *tview.Flex, taskPane *tview.Flex) *tview.Application {
|
||||
return app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||
if ignoreKeyEvt() {
|
||||
return event
|
||||
}
|
||||
switch event.Rune() {
|
||||
case 'p':
|
||||
app.SetFocus(projectPane)
|
||||
case 't':
|
||||
app.SetFocus(taskPane)
|
||||
case 'n':
|
||||
if projectPane.HasFocus() {
|
||||
app.SetFocus(newProject)
|
||||
} else if taskPane.HasFocus() {
|
||||
app.SetFocus(newTask)
|
||||
}
|
||||
}
|
||||
|
||||
return event
|
||||
})
|
||||
}
|
||||
|
||||
func showMessage(text string) {
|
||||
message.SetText(text)
|
||||
statusBar.SwitchToPage(messagePage)
|
||||
|
||||
go func() {
|
||||
app.QueueUpdateDraw(func() {
|
||||
time.Sleep(time.Second * 5)
|
||||
statusBar.SwitchToPage(shortcutsPage)
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func prepareStatusBar() {
|
||||
statusBar = tview.NewPages()
|
||||
|
||||
message = tview.NewTextView().SetDynamicColors(true).SetText("Loading...")
|
||||
statusBar.AddPage(messagePage, message, true, true)
|
||||
|
||||
statusBar.AddPage(shortcutsPage,
|
||||
tview.NewGrid().
|
||||
SetColumns(0, 0, 0, 0).
|
||||
SetRows(0).
|
||||
AddItem(tview.NewTextView().SetText("Shortcuts: Alt+.(dot)"), 0, 0, 1, 1, 0, 0, false).
|
||||
AddItem(tview.NewTextView().SetText("New Project: n").SetTextAlign(tview.AlignCenter), 0, 1, 1, 1, 0, 0, false).
|
||||
AddItem(tview.NewTextView().SetText("New Task: t").SetTextAlign(tview.AlignCenter), 0, 2, 1, 1, 0, 0, false).
|
||||
AddItem(tview.NewTextView().SetText("Quit: Ctrl+C").SetTextAlign(tview.AlignRight), 0, 3, 1, 1, 0, 0, false),
|
||||
true,
|
||||
true,
|
||||
)
|
||||
}
|
||||
62
app/util.go
Normal file
62
app/util.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
"github.com/rivo/tview"
|
||||
|
||||
"github.com/ajaxray/geek-life/util"
|
||||
)
|
||||
|
||||
func makeHorizontalLine(lineChar rune) *tview.TextView {
|
||||
hr := tview.NewTextView()
|
||||
hr.SetDrawFunc(func(screen tcell.Screen, x int, y int, width int, height int) (int, int, int, int) {
|
||||
// Draw a horizontal line across the middle of the box.
|
||||
style := tcell.StyleDefault.Foreground(tcell.ColorWhite).Background(tcell.ColorBlack)
|
||||
centerY := y + height/2
|
||||
for cx := x; cx < x+width; cx++ {
|
||||
screen.SetContent(cx, centerY, lineChar, nil, style)
|
||||
}
|
||||
|
||||
// Space for other content.
|
||||
return x + 1, centerY + 1, width - 2, height - (centerY + 1 - y)
|
||||
})
|
||||
|
||||
return hr
|
||||
}
|
||||
|
||||
func makeLightTextInput(placeholder string) *tview.InputField {
|
||||
return tview.NewInputField().
|
||||
SetPlaceholder(placeholder).
|
||||
SetPlaceholderTextColor(tcell.ColorLightSlateGray).
|
||||
SetFieldTextColor(tcell.ColorBlack).
|
||||
SetFieldBackgroundColor(tcell.ColorGray)
|
||||
}
|
||||
|
||||
func showMessage(text string) {
|
||||
message.SetText(text)
|
||||
statusBar.SwitchToPage(messagePage)
|
||||
|
||||
go func() {
|
||||
app.QueueUpdateDraw(func() {
|
||||
time.Sleep(time.Second * 5)
|
||||
statusBar.SwitchToPage(shortcutsPage)
|
||||
})
|
||||
}()
|
||||
}
|
||||
|
||||
func makeButton(label string, handler func()) *tview.Button {
|
||||
btn := tview.NewButton(label).SetSelectedFunc(handler).
|
||||
SetLabelColor(tcell.ColorWhite)
|
||||
|
||||
btn.SetBackgroundColor(tcell.ColorCornflowerBlue)
|
||||
|
||||
return btn
|
||||
}
|
||||
|
||||
func ignoreKeyEvt() bool {
|
||||
textInputs := []string{"*tview.InputField", "*femto.View"}
|
||||
return util.InArray(reflect.TypeOf(app.GetFocus()).String(), textInputs)
|
||||
}
|
||||
Reference in New Issue
Block a user