taskDetailPane modified as a Component (type)
This commit is contained in:
@@ -13,12 +13,12 @@ import (
|
|||||||
var (
|
var (
|
||||||
app *tview.Application
|
app *tview.Application
|
||||||
projectDetailPane *tview.Flex
|
projectDetailPane *tview.Flex
|
||||||
taskDetailPane *tview.Flex
|
|
||||||
layout, contents *tview.Flex
|
layout, contents *tview.Flex
|
||||||
|
|
||||||
statusBar *StatusBar
|
statusBar *StatusBar
|
||||||
projectPane *ProjectPane
|
projectPane *ProjectPane
|
||||||
taskPane *TaskPane
|
taskPane *TaskPane
|
||||||
|
taskDetailPane *TaskDetailPane
|
||||||
|
|
||||||
db *storm.DB
|
db *storm.DB
|
||||||
projectRepo repository.ProjectRepository
|
projectRepo repository.ProjectRepository
|
||||||
@@ -43,10 +43,9 @@ func main() {
|
|||||||
|
|
||||||
statusBar = makeStatusBar(app)
|
statusBar = makeStatusBar(app)
|
||||||
projectPane = NewProjectPane(projectRepo)
|
projectPane = NewProjectPane(projectRepo)
|
||||||
// prepareTaskPane()
|
|
||||||
taskPane = NewTaskPane(projectRepo, taskRepo)
|
taskPane = NewTaskPane(projectRepo, taskRepo)
|
||||||
prepareProjectDetail()
|
prepareProjectDetail()
|
||||||
prepareDetailPane()
|
taskDetailPane = NewTaskDetailPane(taskRepo)
|
||||||
|
|
||||||
contents = tview.NewFlex().
|
contents = tview.NewFlex().
|
||||||
AddItem(projectPane, 25, 1, true).
|
AddItem(projectPane, 25, 1, true).
|
||||||
@@ -77,7 +76,7 @@ func setKeyboardShortcuts() *tview.Application {
|
|||||||
case taskPane.HasFocus():
|
case taskPane.HasFocus():
|
||||||
event = taskPane.handleShortcuts(event)
|
event = taskPane.handleShortcuts(event)
|
||||||
case taskDetailPane.HasFocus():
|
case taskDetailPane.HasFocus():
|
||||||
event = handleDetailPaneShortcuts(event)
|
event = taskDetailPane.handleShortcuts(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global shortcuts
|
// Global shortcuts
|
||||||
|
|||||||
@@ -8,90 +8,103 @@ import (
|
|||||||
"github.com/pgavlin/femto"
|
"github.com/pgavlin/femto"
|
||||||
"github.com/pgavlin/femto/runtime"
|
"github.com/pgavlin/femto/runtime"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
|
|
||||||
|
"github.com/ajaxray/geek-life/model"
|
||||||
|
"github.com/ajaxray/geek-life/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type TaskDetailPane struct {
|
||||||
|
*tview.Flex
|
||||||
taskName, taskDateDisplay *tview.TextView
|
taskName, taskDateDisplay *tview.TextView
|
||||||
editorHint *tview.TextView
|
editorHint *tview.TextView
|
||||||
taskDate *tview.InputField
|
taskDate *tview.InputField
|
||||||
taskDetailView *femto.View
|
|
||||||
taskStatusToggle *tview.Button
|
taskStatusToggle *tview.Button
|
||||||
colorscheme femto.Colorscheme
|
taskDetailView *femto.View
|
||||||
blankCell = tview.NewTextView()
|
colorScheme femto.Colorscheme
|
||||||
)
|
taskRepo repository.TaskRepository
|
||||||
|
task *model.Task
|
||||||
|
}
|
||||||
|
|
||||||
const dateLayoutISO = "2006-01-02"
|
const dateLayoutISO = "2006-01-02"
|
||||||
const dateLayoutHuman = "02 Jan, Monday"
|
const dateLayoutHuman = "02 Jan, Monday"
|
||||||
|
|
||||||
func prepareDetailPane() {
|
var blankCell = tview.NewTextView()
|
||||||
taskName = tview.NewTextView().SetDynamicColors(true)
|
|
||||||
|
|
||||||
prepareDetailsEditor()
|
func NewTaskDetailPane(taskRepo repository.TaskRepository) *TaskDetailPane {
|
||||||
|
pane := TaskDetailPane{
|
||||||
|
Flex: tview.NewFlex().SetDirection(tview.FlexRow),
|
||||||
|
taskName: tview.NewTextView().SetDynamicColors(true),
|
||||||
|
taskDateDisplay: tview.NewTextView().SetDynamicColors(true),
|
||||||
|
taskStatusToggle: makeButton("Complete", nil).SetLabelColor(tcell.ColorLightGray),
|
||||||
|
taskRepo: taskRepo,
|
||||||
|
}
|
||||||
|
|
||||||
taskStatusToggle = makeButton("Complete", toggleActiveTaskStatus).SetLabelColor(tcell.ColorLightGray)
|
pane.prepareDetailsEditor()
|
||||||
|
|
||||||
toggleHint := tview.NewTextView().SetTextColor(tcell.ColorDimGray).SetText("<space> to toggle")
|
toggleHint := tview.NewTextView().SetTextColor(tcell.ColorDimGray).SetText("<space> to toggle")
|
||||||
|
pane.taskStatusToggle.SetSelectedFunc(pane.toggleTaskStatus)
|
||||||
|
|
||||||
|
pane.editorHint = tview.NewTextView().SetText(" e to edit, ↓↑ to scroll").SetTextColor(tcell.ColorDimGray)
|
||||||
|
|
||||||
|
// Prepare static (no external interaction) elements
|
||||||
editorLabel := tview.NewFlex().
|
editorLabel := tview.NewFlex().
|
||||||
AddItem(tview.NewTextView().SetText("Task Not[::u]e[::-]:").SetDynamicColors(true), 0, 1, false).
|
AddItem(tview.NewTextView().SetText("Task Not[::u]e[::-]:").SetDynamicColors(true), 0, 1, false).
|
||||||
AddItem(makeButton("edit", func() { activateEditor() }), 6, 0, false)
|
AddItem(makeButton("edit", func() { pane.activateEditor() }), 6, 0, false)
|
||||||
|
|
||||||
editorHint = tview.NewTextView().
|
|
||||||
SetText(" e to edit, ↓↑ to scroll").
|
|
||||||
SetTextColor(tcell.ColorDimGray)
|
|
||||||
editorHelp := tview.NewFlex().
|
editorHelp := tview.NewFlex().
|
||||||
AddItem(editorHint, 0, 1, false).
|
AddItem(pane.editorHint, 0, 1, false).
|
||||||
AddItem(tview.NewTextView().SetTextAlign(tview.AlignRight).
|
AddItem(tview.NewTextView().SetTextAlign(tview.AlignRight).
|
||||||
SetText("syntax:markdown theme:monakai").
|
SetText("syntax:markdown theme:monakai").
|
||||||
SetTextColor(tcell.ColorDimGray), 0, 1, false)
|
SetTextColor(tcell.ColorDimGray), 0, 1, false)
|
||||||
|
|
||||||
taskDetailPane = tview.NewFlex().SetDirection(tview.FlexRow).
|
pane.
|
||||||
AddItem(taskName, 2, 1, true).
|
AddItem(pane.taskName, 2, 1, true).
|
||||||
AddItem(makeHorizontalLine(tcell.RuneS3, tcell.ColorGray), 1, 1, false).
|
AddItem(makeHorizontalLine(tcell.RuneS3, tcell.ColorGray), 1, 1, false).
|
||||||
AddItem(blankCell, 1, 1, false).
|
AddItem(blankCell, 1, 1, false).
|
||||||
AddItem(makeDateRow(), 1, 1, true).
|
AddItem(pane.makeDateRow(), 1, 1, true).
|
||||||
AddItem(blankCell, 1, 1, false).
|
AddItem(blankCell, 1, 1, false).
|
||||||
AddItem(editorLabel, 1, 1, false).
|
AddItem(editorLabel, 1, 1, false).
|
||||||
AddItem(taskDetailView, 15, 4, false).
|
AddItem(pane.taskDetailView, 15, 4, false).
|
||||||
AddItem(editorHelp, 1, 1, false).
|
AddItem(editorHelp, 1, 1, false).
|
||||||
AddItem(blankCell, 0, 1, false).
|
AddItem(blankCell, 0, 1, false).
|
||||||
AddItem(toggleHint, 1, 1, false).
|
AddItem(toggleHint, 1, 1, false).
|
||||||
AddItem(taskStatusToggle, 3, 1, false)
|
AddItem(pane.taskStatusToggle, 3, 1, false)
|
||||||
|
|
||||||
taskDetailPane.SetBorder(true).SetTitle("Task Detail")
|
pane.SetBorder(true).SetTitle("Task Detail")
|
||||||
|
|
||||||
|
return &pane
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDateRow() *tview.Flex {
|
func (td *TaskDetailPane) makeDateRow() *tview.Flex {
|
||||||
taskDateDisplay = tview.NewTextView().SetDynamicColors(true)
|
|
||||||
taskDate = makeLightTextInput("yyyy-mm-dd").
|
td.taskDate = makeLightTextInput("yyyy-mm-dd").
|
||||||
SetLabel("Set:").
|
SetLabel("Set:").
|
||||||
SetLabelColor(tcell.ColorGray).
|
SetLabelColor(tcell.ColorGray).
|
||||||
SetFieldWidth(12).
|
SetFieldWidth(12).
|
||||||
SetDoneFunc(func(key tcell.Key) {
|
SetDoneFunc(func(key tcell.Key) {
|
||||||
switch key {
|
switch key {
|
||||||
case tcell.KeyEnter:
|
case tcell.KeyEnter:
|
||||||
setTaskDate(parseDateInputOrCurrent(taskDate.GetText()).Unix(), true)
|
td.setTaskDate(parseDateInputOrCurrent(td.taskDate.GetText()).Unix(), true)
|
||||||
case tcell.KeyEsc:
|
case tcell.KeyEsc:
|
||||||
setTaskDate(taskPane.activeTask.DueDate, false)
|
td.setTaskDate(td.task.DueDate, false)
|
||||||
}
|
}
|
||||||
app.SetFocus(taskDetailPane)
|
app.SetFocus(td)
|
||||||
})
|
})
|
||||||
|
|
||||||
todaySelector := func() {
|
todaySelector := func() {
|
||||||
setTaskDate(time.Now().Unix(), true)
|
td.setTaskDate(time.Now().Unix(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
nextDaySelector := func() {
|
nextDaySelector := func() {
|
||||||
setTaskDate(parseDateInputOrCurrent(taskDate.GetText()).AddDate(0, 0, 1).Unix(), true)
|
td.setTaskDate(parseDateInputOrCurrent(td.taskDate.GetText()).AddDate(0, 0, 1).Unix(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
prevDaySelector := func() {
|
prevDaySelector := func() {
|
||||||
setTaskDate(parseDateInputOrCurrent(taskDate.GetText()).AddDate(0, 0, -1).Unix(), true)
|
td.setTaskDate(parseDateInputOrCurrent(td.taskDate.GetText()).AddDate(0, 0, -1).Unix(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
return tview.NewFlex().
|
return tview.NewFlex().
|
||||||
AddItem(taskDateDisplay, 0, 2, true).
|
AddItem(td.taskDateDisplay, 0, 2, true).
|
||||||
AddItem(taskDate, 14, 0, true).
|
AddItem(td.taskDate, 14, 0, true).
|
||||||
AddItem(blankCell, 1, 0, false).
|
AddItem(blankCell, 1, 0, false).
|
||||||
AddItem(makeButton("today", todaySelector), 8, 1, false).
|
AddItem(makeButton("today", todaySelector), 8, 1, false).
|
||||||
AddItem(blankCell, 1, 0, false).
|
AddItem(blankCell, 1, 0, false).
|
||||||
@@ -100,28 +113,28 @@ func makeDateRow() *tview.Flex {
|
|||||||
AddItem(makeButton("-1", prevDaySelector), 4, 1, false)
|
AddItem(makeButton("-1", prevDaySelector), 4, 1, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setStatusToggle() {
|
func (td *TaskDetailPane) updateToggleDisplay() {
|
||||||
if taskPane.activeTask.Completed {
|
if td.task.Completed {
|
||||||
taskStatusToggle.SetLabel("Resume").SetBackgroundColor(tcell.ColorMaroon)
|
td.taskStatusToggle.SetLabel("Resume").SetBackgroundColor(tcell.ColorMaroon)
|
||||||
} else {
|
} else {
|
||||||
taskStatusToggle.SetLabel("Complete").SetBackgroundColor(tcell.ColorDarkGreen)
|
td.taskStatusToggle.SetLabel("Complete").SetBackgroundColor(tcell.ColorDarkGreen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleActiveTaskStatus() {
|
func (td *TaskDetailPane) toggleTaskStatus() {
|
||||||
status := !taskPane.activeTask.Completed
|
status := !td.task.Completed
|
||||||
if taskRepo.UpdateField(taskPane.activeTask, "Completed", status) == nil {
|
if taskRepo.UpdateField(td.task, "Completed", status) == nil {
|
||||||
taskPane.activeTask.Completed = status
|
td.task.Completed = status
|
||||||
taskPane.ActivateTask(taskPane.list.GetCurrentItem())
|
td.SetTask(td.task)
|
||||||
taskPane.list.SetItemText(taskPane.list.GetCurrentItem(), makeTaskListingTitle(*taskPane.activeTask), "")
|
taskPane.list.SetItemText(taskPane.list.GetCurrentItem(), makeTaskListingTitle(*td.task), "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Display Task date in detail pane, and update date if asked to
|
// Display Task date in detail pane, and update date if asked to
|
||||||
func setTaskDate(unixDate int64, update bool) {
|
func (td *TaskDetailPane) setTaskDate(unixDate int64, update bool) {
|
||||||
if update {
|
if update {
|
||||||
taskPane.activeTask.DueDate = unixDate
|
td.task.DueDate = unixDate
|
||||||
if err := taskRepo.UpdateField(taskPane.activeTask, "DueDate", unixDate); err != nil {
|
if err := td.taskRepo.UpdateField(td.task, "DueDate", unixDate); err != nil {
|
||||||
statusBar.showForSeconds("Could not update due date: "+err.Error(), 5)
|
statusBar.showForSeconds("Could not update due date: "+err.Error(), 5)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -135,40 +148,42 @@ func setTaskDate(unixDate int64, update bool) {
|
|||||||
if due.Before(time.Now()) {
|
if due.Before(time.Now()) {
|
||||||
color = "red"
|
color = "red"
|
||||||
}
|
}
|
||||||
taskDateDisplay.SetText(fmt.Sprintf("[::u]D[::-]ue: [%s]%s", color, humanDate))
|
td.taskDateDisplay.SetText(fmt.Sprintf("[::u]D[::-]ue: [%s]%s", color, humanDate))
|
||||||
taskDate.SetText(due.Format(dateLayoutISO))
|
td.taskDate.SetText(due.Format(dateLayoutISO))
|
||||||
} else {
|
} else {
|
||||||
taskDate.SetText("")
|
td.taskDate.SetText("")
|
||||||
taskDateDisplay.SetText("[::u]D[::-]ue: [::d]Not Set")
|
td.taskDateDisplay.SetText("[::u]D[::-]ue: [::d]Not Set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareDetailsEditor() {
|
func (td *TaskDetailPane) prepareDetailsEditor() {
|
||||||
taskDetailView = femto.NewView(makeBufferFromString(""))
|
|
||||||
taskDetailView.SetRuntimeFiles(runtime.Files)
|
|
||||||
|
|
||||||
// var colorscheme femto.Colorscheme
|
td.taskDetailView = femto.NewView(makeBufferFromString(""))
|
||||||
|
td.taskDetailView.SetRuntimeFiles(runtime.Files)
|
||||||
|
|
||||||
|
// var colorScheme femto.Colorscheme
|
||||||
if monokai := runtime.Files.FindFile(femto.RTColorscheme, "monokai"); monokai != nil {
|
if monokai := runtime.Files.FindFile(femto.RTColorscheme, "monokai"); monokai != nil {
|
||||||
if data, err := monokai.Data(); err == nil {
|
if data, err := monokai.Data(); err == nil {
|
||||||
colorscheme = femto.ParseColorscheme(string(data))
|
td.colorScheme = femto.ParseColorscheme(string(data))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
taskDetailView.SetColorscheme(colorscheme)
|
|
||||||
taskDetailView.SetBorder(true)
|
|
||||||
taskDetailView.SetBorderColor(tcell.ColorLightSlateGray)
|
|
||||||
|
|
||||||
taskDetailView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
td.taskDetailView.SetColorscheme(td.colorScheme)
|
||||||
|
td.taskDetailView.SetBorder(true)
|
||||||
|
td.taskDetailView.SetBorderColor(tcell.ColorLightSlateGray)
|
||||||
|
|
||||||
|
td.taskDetailView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
switch event.Key() {
|
switch event.Key() {
|
||||||
case tcell.KeyEsc:
|
case tcell.KeyEsc:
|
||||||
taskPane.activeTask.Details = taskDetailView.Buf.String()
|
td.task.Details = td.taskDetailView.Buf.String()
|
||||||
err := taskRepo.Update(taskPane.activeTask)
|
err := taskRepo.Update(td.task)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
statusBar.showForSeconds("[lime]Saved task detail", 5)
|
statusBar.showForSeconds("[lime]Saved task detail", 5)
|
||||||
} else {
|
} else {
|
||||||
statusBar.showForSeconds("[red]Could not save: "+err.Error(), 5)
|
statusBar.showForSeconds("[red]Could not save: "+err.Error(), 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivateEditor()
|
td.deactivateEditor()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,38 +203,50 @@ func makeBufferFromString(content string) *femto.Buffer {
|
|||||||
return buff
|
return buff
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateEditor() {
|
func (td *TaskDetailPane) activateEditor() {
|
||||||
taskDetailView.Readonly = false
|
td.taskDetailView.Readonly = false
|
||||||
taskDetailView.SetBorderColor(tcell.ColorDarkOrange)
|
td.taskDetailView.SetBorderColor(tcell.ColorDarkOrange)
|
||||||
editorHint.SetText(" Esc to save changes")
|
td.editorHint.SetText(" Esc to save changes")
|
||||||
app.SetFocus(taskDetailView)
|
app.SetFocus(td.taskDetailView)
|
||||||
}
|
}
|
||||||
|
|
||||||
func deactivateEditor() {
|
func (td *TaskDetailPane) deactivateEditor() {
|
||||||
taskDetailView.Readonly = true
|
td.taskDetailView.Readonly = true
|
||||||
taskDetailView.SetBorderColor(tcell.ColorLightSlateGray)
|
td.taskDetailView.SetBorderColor(tcell.ColorLightSlateGray)
|
||||||
editorHint.SetText(" e to edit, ↓↑ to scroll")
|
td.editorHint.SetText(" e to edit, ↓↑ to scroll")
|
||||||
app.SetFocus(taskDetailPane)
|
app.SetFocus(td)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleDetailPaneShortcuts(event *tcell.EventKey) *tcell.EventKey {
|
func (td *TaskDetailPane) handleShortcuts(event *tcell.EventKey) *tcell.EventKey {
|
||||||
switch event.Key() {
|
switch event.Key() {
|
||||||
case tcell.KeyEsc:
|
case tcell.KeyEsc:
|
||||||
app.SetFocus(taskPane)
|
app.SetFocus(taskPane)
|
||||||
case tcell.KeyDown:
|
case tcell.KeyDown:
|
||||||
taskDetailView.ScrollDown(1)
|
td.taskDetailView.ScrollDown(1)
|
||||||
case tcell.KeyUp:
|
case tcell.KeyUp:
|
||||||
taskDetailView.ScrollUp(1)
|
td.taskDetailView.ScrollUp(1)
|
||||||
case tcell.KeyRune:
|
case tcell.KeyRune:
|
||||||
switch event.Rune() {
|
switch event.Rune() {
|
||||||
case 'e':
|
case 'e':
|
||||||
activateEditor()
|
td.activateEditor()
|
||||||
case 'd':
|
case 'd':
|
||||||
app.SetFocus(taskDate)
|
app.SetFocus(td.taskDate)
|
||||||
case ' ':
|
case ' ':
|
||||||
toggleActiveTaskStatus()
|
td.toggleTaskStatus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (td *TaskDetailPane) SetTask(task *model.Task) {
|
||||||
|
td.task = task
|
||||||
|
|
||||||
|
td.taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*td.task), td.task.Title))
|
||||||
|
td.taskDetailView.Buf = makeBufferFromString(td.task.Details)
|
||||||
|
td.taskDetailView.SetColorscheme(td.colorScheme)
|
||||||
|
td.taskDetailView.Start()
|
||||||
|
td.setTaskDate(td.task.DueDate, false)
|
||||||
|
td.updateToggleDisplay()
|
||||||
|
td.deactivateEditor()
|
||||||
|
}
|
||||||
|
|||||||
12
app/tasks.go
12
app/tasks.go
@@ -1,8 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/asdine/storm/v3"
|
"github.com/asdine/storm/v3"
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
"github.com/rivo/tview"
|
"github.com/rivo/tview"
|
||||||
@@ -110,14 +108,8 @@ func (pane *TaskPane) LoadProjectTasks(project model.Project) {
|
|||||||
func (pane *TaskPane) ActivateTask(idx int) {
|
func (pane *TaskPane) ActivateTask(idx int) {
|
||||||
removeThirdCol()
|
removeThirdCol()
|
||||||
pane.activeTask = &pane.tasks[idx]
|
pane.activeTask = &pane.tasks[idx]
|
||||||
|
taskDetailPane.SetTask(pane.activeTask)
|
||||||
taskName.SetText(fmt.Sprintf("[%s::b]# %s", getTaskTitleColor(*pane.activeTask), pane.activeTask.Title))
|
|
||||||
taskDetailView.Buf = makeBufferFromString(pane.activeTask.Details)
|
|
||||||
taskDetailView.SetColorscheme(colorscheme)
|
|
||||||
taskDetailView.Start()
|
|
||||||
setTaskDate(pane.activeTask.DueDate, false)
|
|
||||||
setStatusToggle()
|
|
||||||
|
|
||||||
contents.AddItem(taskDetailPane, 0, 3, false)
|
contents.AddItem(taskDetailPane, 0, 3, false)
|
||||||
deactivateEditor()
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user