1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00
wtf/modules/todo/widget.go
Chris Cummer cde904ff08
Use errcheck to find unhandled errors (#795)
Signed-off-by: Chris Cummer <chriscummer@me.com>
2019-12-17 08:26:16 -08:00

251 lines
6.2 KiB
Go

package todo
import (
"fmt"
"io/ioutil"
"github.com/gdamore/tcell"
"github.com/rivo/tview"
"github.com/wtfutil/wtf/cfg"
"github.com/wtfutil/wtf/checklist"
"github.com/wtfutil/wtf/utils"
"github.com/wtfutil/wtf/view"
"github.com/wtfutil/wtf/wtf"
"gopkg.in/yaml.v2"
)
const (
modalHeight = 7
modalWidth = 80
offscreen = -1000
)
// A Widget represents a Todo widget
type Widget struct {
view.KeyboardWidget
view.ScrollableWidget
app *tview.Application
filePath string
list checklist.Checklist
pages *tview.Pages
settings *Settings
}
// NewWidget creates a new instance of a widget
func NewWidget(app *tview.Application, pages *tview.Pages, settings *Settings) *Widget {
widget := Widget{
KeyboardWidget: view.NewKeyboardWidget(app, pages, settings.common),
ScrollableWidget: view.NewScrollableWidget(app, settings.common),
app: app,
settings: settings,
filePath: settings.filePath,
list: checklist.NewChecklist(settings.common.Sigils.Checkbox.Checked, settings.common.Sigils.Checkbox.Unchecked),
pages: pages,
}
widget.init()
widget.initializeKeyboardControls()
widget.View.SetInputCapture(widget.InputCapture)
widget.View.SetRegions(true)
widget.View.SetScrollable(true)
widget.KeyboardWidget.SetView(widget.View)
widget.SetRenderFunction(widget.display)
return &widget
}
/* -------------------- Exported Functions -------------------- */
// HelpText returns the help text for this widget
func (widget *Widget) HelpText() string {
return widget.KeyboardWidget.HelpText()
}
// SelectedItem returns the currently-selected checklist item or nil if no item is selected
func (widget *Widget) SelectedItem() *checklist.ChecklistItem {
var selectedItem *checklist.ChecklistItem
if widget.isItemSelected() {
selectedItem = widget.list.Items[widget.Selected]
}
return selectedItem
}
// Refresh updates the data for this widget and displays it onscreen
func (widget *Widget) Refresh() {
widget.load()
widget.display()
}
func (widget *Widget) SetList(list checklist.Checklist) {
widget.list = list
}
/* -------------------- Unexported Functions -------------------- */
func (widget *Widget) init() {
_, err := cfg.CreateFile(widget.filePath)
if err != nil {
return
}
}
// isItemSelected returns whether any item of the todo is selected or not
func (widget *Widget) isItemSelected() bool {
return widget.Selected >= 0 && widget.Selected < len(widget.list.Items)
}
// Loads the todo list from3 Yaml file
func (widget *Widget) load() {
confDir, _ := cfg.WtfConfigDir()
filePath := fmt.Sprintf("%s/%s", confDir, widget.filePath)
fileData, _ := utils.ReadFileBytes(filePath)
err := yaml.Unmarshal(fileData, &widget.list)
if err != nil {
return
}
widget.ScrollableWidget.SetItemCount(len(widget.list.Items))
widget.setItemChecks()
}
func (widget *Widget) newItem() {
form := widget.modalForm("New Todo:", "")
saveFctn := func() {
text := form.GetFormItem(0).(*tview.InputField).GetText()
widget.list.Add(false, text)
widget.SetItemCount(len(widget.list.Items))
widget.persist()
widget.pages.RemovePage("modal")
widget.app.SetFocus(widget.View)
widget.display()
}
widget.addButtons(form, saveFctn)
widget.modalFocus(form)
widget.app.QueueUpdate(func() {
widget.app.Draw()
})
}
// persist writes the todo list to Yaml file
func (widget *Widget) persist() {
confDir, _ := cfg.WtfConfigDir()
filePath := fmt.Sprintf("%s/%s", confDir, widget.filePath)
fileData, _ := yaml.Marshal(&widget.list)
err := ioutil.WriteFile(filePath, fileData, 0644)
if err != nil {
panic(err)
}
}
// setItemChecks rolls through the checklist and ensures that all checklist
// items have the correct checked/unchecked icon per the user's preferences
func (widget *Widget) setItemChecks() {
for _, item := range widget.list.Items {
item.CheckedIcon = widget.settings.checked
item.UncheckedIcon = widget.settings.unchecked
}
}
// updateSelected sets the text of the currently-selected item to the provided text
func (widget *Widget) updateSelected() {
if !widget.isItemSelected() {
return
}
form := widget.modalForm("Edit:", widget.SelectedItem().Text)
saveFctn := func() {
text := form.GetFormItem(0).(*tview.InputField).GetText()
widget.updateSelectedItem(text)
widget.persist()
widget.pages.RemovePage("modal")
widget.app.SetFocus(widget.View)
widget.display()
}
widget.addButtons(form, saveFctn)
widget.modalFocus(form)
}
// updateSelectedItem update the text of the selected item.
func (widget *Widget) updateSelectedItem(text string) {
selectedItem := widget.SelectedItem()
if selectedItem == nil {
return
}
selectedItem.Text = text
}
/* -------------------- Modal Form -------------------- */
func (widget *Widget) addButtons(form *tview.Form, saveFctn func()) {
widget.addSaveButton(form, saveFctn)
widget.addCancelButton(form)
}
func (widget *Widget) addCancelButton(form *tview.Form) {
cancelFn := func() {
widget.pages.RemovePage("modal")
widget.app.SetFocus(widget.View)
widget.display()
}
form.AddButton("Cancel", cancelFn)
form.SetCancelFunc(cancelFn)
}
func (widget *Widget) addSaveButton(form *tview.Form, fctn func()) {
form.AddButton("Save", fctn)
}
func (widget *Widget) modalFocus(form *tview.Form) {
widget.app.QueueUpdateDraw(func() {
frame := widget.modalFrame(form)
widget.pages.AddPage("modal", frame, false, true)
widget.app.SetFocus(frame)
})
}
func (widget *Widget) modalForm(lbl, text string) *tview.Form {
form := tview.NewForm().SetFieldBackgroundColor(wtf.ColorFor(widget.settings.common.Colors.Background))
form.SetButtonsAlign(tview.AlignCenter).SetButtonTextColor(wtf.ColorFor(widget.settings.common.Colors.Text))
form.AddInputField(lbl, text, 60, nil, nil)
return form
}
func (widget *Widget) modalFrame(form *tview.Form) *tview.Frame {
frame := tview.NewFrame(form).SetBorders(0, 0, 0, 0, 0, 0)
frame.SetRect(offscreen, offscreen, modalWidth, modalHeight)
frame.SetBorder(true)
frame.SetBorders(1, 1, 0, 0, 1, 1)
drawFunc := func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) {
w, h := screen.Size()
frame.SetRect((w/2)-(width/2), (h/2)-(height/2), width, height)
return x, y, width, height
}
frame.SetDrawFunc(drawFunc)
return frame
}