UI Work
This commit is contained in:
parent
e0affc82d4
commit
5ff74e851c
12
cli/cli.go
12
cli/cli.go
@ -43,6 +43,18 @@ func (p *Program) LoadTimerList() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Program) WriteLists() error {
|
||||
err := p.WriteTimerList()
|
||||
if dErr := p.WriteDoneList(); dErr != nil {
|
||||
if err == nil {
|
||||
return fmt.Errorf("Error writing Done list %w", dErr)
|
||||
} else {
|
||||
return fmt.Errorf("Error writing Both lists (Timer: %s; Done: %s) (%w)", err.Error(), dErr.Error(), err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Program) WriteTimerList() error {
|
||||
return p.TimerList.WriteToFilename(p.timerPath)
|
||||
}
|
||||
|
@ -53,6 +53,15 @@ type listTimersScreen struct {
|
||||
|
||||
type ListTimersMsg ScreenMsg
|
||||
|
||||
func NewListTimersMsg(data interface{}, err error) ListTimersMsg {
|
||||
return ListTimersMsg{
|
||||
source: ListTimersId,
|
||||
command: CmdArchiveTimer,
|
||||
data: data,
|
||||
err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func NewListTimersScreen(u *Ui) *listTimersScreen {
|
||||
s := listTimersScreen{
|
||||
ui: u,
|
||||
@ -115,6 +124,7 @@ func (s *listTimersScreen) Init() wandle.Cmd {
|
||||
func (s *listTimersScreen) Update(msg wandle.Msg) wandle.Cmd {
|
||||
switch msg := msg.(type) {
|
||||
case ScreenMsg:
|
||||
s.err = msg.err
|
||||
case termbox.Event:
|
||||
return s.handleTermboxEvent(msg)
|
||||
}
|
||||
@ -172,7 +182,17 @@ func (s *listTimersScreen) View(style wandle.Style) {
|
||||
selectedStatus = fmt.Sprintf("%s (%d / %d selected)", selectedStatus, len(s.selected), s.fullFilterList.Size())
|
||||
}
|
||||
wandle.Print(1, h-2, style, selectedStatus)
|
||||
help := "[T]oggle Display, [p]roject(+), [c]ontext(@), [t]ags(:), [A]rchive Selected, [Ctrl+A]: Select All/None, [Ctrl+I]: Invert Selection"
|
||||
var archiveText string
|
||||
if s.areSelectedInSameList() {
|
||||
if s.areSelectedInDoneList() {
|
||||
archiveText = "Un[A]rchive Selected, "
|
||||
} else {
|
||||
archiveText = "[A]rchive Selected, "
|
||||
}
|
||||
} else {
|
||||
archiveText = "Not in Same List"
|
||||
}
|
||||
help := fmt.Sprintf("[T]oggle Display, [p]roject(+), [c]ontext(@), [t]ags(:), %s[Ctrl+A]: Select All/None, [Ctrl+I]: Invert Selection", archiveText)
|
||||
wandle.Print(1, h-1, style, help)
|
||||
|
||||
s.scrollbar.View(style)
|
||||
@ -188,7 +208,10 @@ func (s *listTimersScreen) View(style wandle.Style) {
|
||||
if s.confirm.IsActive() {
|
||||
s.confirm.View(style)
|
||||
}
|
||||
wandle.Print(1, h-2, style, s.msg)
|
||||
wandle.Print(1, h-3, style, s.msg)
|
||||
if s.err != nil {
|
||||
wandle.Print(1, h-4, ErrStyle, s.err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *listTimersScreen) ViewTimer(x, y int, style wandle.Style, tmr *timertxt.Timer) {
|
||||
@ -204,7 +227,12 @@ func (s *listTimersScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
|
||||
return s.confirm.Update(msg)
|
||||
}
|
||||
if s.choiceMenu.IsActive() {
|
||||
return s.choiceMenu.Update(msg)
|
||||
if msg.Type == termbox.EventKey && msg.Key == termbox.KeyEsc {
|
||||
s.choiceMenu.SetActive(false)
|
||||
return wandle.EmptyCmd
|
||||
} else {
|
||||
return s.choiceMenu.Update(msg)
|
||||
}
|
||||
}
|
||||
if s.tagEditor.IsActive() {
|
||||
return s.tagEditor.Update(msg)
|
||||
@ -252,6 +280,8 @@ func (s *listTimersScreen) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
|
||||
return nil
|
||||
} else if msg.Ch == 'G' {
|
||||
s.cursor = s.fullFilterList.Size() - 1
|
||||
} else if msg.Ch == 'g' {
|
||||
s.cursor = 0
|
||||
} else if msg.Ch == 't' {
|
||||
// Edit tag(s)
|
||||
return s.showEditTagsChoice()
|
||||
@ -301,10 +331,10 @@ func (s *listTimersScreen) showArchiveSelected() wandle.Cmd {
|
||||
return func() wandle.Msg {
|
||||
if len(s.selected) > 0 {
|
||||
s.confirm.SetTitle(fmt.Sprintf("Archive %d Timers?", len(s.selected)))
|
||||
s.confirm.SetMessage("Are you sure you want to archive these timers?")
|
||||
s.confirm.SetMessage("Are you sure you want to archive these timers? (y/n)")
|
||||
} else {
|
||||
s.confirm.SetTitle("Archive Timer?")
|
||||
s.confirm.SetMessage("Are you sure you want to archive this timer?")
|
||||
s.confirm.SetMessage("Are you sure you want to archive this timer? (y/n)")
|
||||
}
|
||||
s.confirm.SetOkCommand(func() wandle.Msg {
|
||||
s.confirm.SetVisible(false)
|
||||
@ -319,25 +349,35 @@ func (s *listTimersScreen) showArchiveSelected() wandle.Cmd {
|
||||
}
|
||||
}
|
||||
func (s *listTimersScreen) doArchiveSelected() wandle.Cmd {
|
||||
return func() wandle.Msg {
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
|
||||
archiveTimer := func(t *timertxt.Timer) error {
|
||||
if remErr := s.timerList.RemoveTimer(*t); remErr != nil {
|
||||
return remErr
|
||||
}
|
||||
s.doneList.AddTimer(t)
|
||||
return nil
|
||||
}
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
if archErr := archiveTimer(selTimer); archErr != nil {
|
||||
s.err = archErr
|
||||
return wandle.EmptyCmd
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
if err := archiveTimer(tmr); err != nil {
|
||||
s.err = err
|
||||
return wandle.EmptyCmd
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
//return wandle.EmptyCmd
|
||||
return wandle.EmptyCmd
|
||||
}
|
||||
|
||||
func (s *listTimersScreen) showEditTagsChoice() wandle.Cmd {
|
||||
@ -393,12 +433,19 @@ func (s *listTimersScreen) showEditTagsChoice() wandle.Cmd {
|
||||
removeTag.SetCommand(func() wandle.Msg {
|
||||
s.choiceMenu.ClearOptions()
|
||||
s.choiceMenu.SetTitle("Choose Tag to Remove")
|
||||
for k, v := range tags {
|
||||
opt := widdles.NewMenuItem(fmt.Sprintf("%s: %s", k, v))
|
||||
opt.SetCommand(func() wandle.Msg {
|
||||
s.removeTagOnSelectedTimers(k)
|
||||
return wandle.EmptyCmd
|
||||
})
|
||||
s.choiceMenu.AddOption(opt)
|
||||
}
|
||||
s.choiceMenu.SetActive(true)
|
||||
return wandle.EmptyCmd
|
||||
})
|
||||
s.choiceMenu.AddOption(removeTag)
|
||||
s.choiceMenu.SetActive(true)
|
||||
//tags := s.fullList.GetTagKeys()
|
||||
//_ = tags
|
||||
return wandle.EmptyCmd
|
||||
}
|
||||
|
||||
@ -425,127 +472,112 @@ func (s *listTimersScreen) gotoSettingsScreen() wandle.Msg {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *listTimersScreen) getSelectedTimerTags() map[string][]string {
|
||||
ret := make(map[string][]string)
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
for k, v := range selTimer.AdditionalTags {
|
||||
ret[k] = []string{v}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
for k, v := range tmr.AdditionalTags {
|
||||
if !util.StringSliceContains(ret[k], v) {
|
||||
ret[k] = append(ret[k], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (s *listTimersScreen) updateTagOnSelectedTimers(key, val string) {
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
selTimer.AdditionalTags[key] = val
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
tmr.AdditionalTags[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
// Writes the lists through the program, putting errors in s.err
|
||||
func (s *listTimersScreen) writeLists() {
|
||||
var errText string
|
||||
if err := s.ui.program.WriteTimerList(); err != nil {
|
||||
if err := s.ui.program.WriteLists(); err != nil {
|
||||
errText = fmt.Sprintf("Errors Writing Lists (%v)", err)
|
||||
}
|
||||
if err := s.ui.program.WriteDoneList(); err != nil {
|
||||
if len(errText) == 0 {
|
||||
errText = fmt.Sprintf("Errors Writing Lists (%v)", err)
|
||||
} else {
|
||||
errText = fmt.Sprintf("%s (%v)", errText, err)
|
||||
}
|
||||
}
|
||||
if len(errText) > 0 {
|
||||
s.err = errors.New(errText)
|
||||
}
|
||||
}
|
||||
func (s *listTimersScreen) getSelectedTimerProjects() []string {
|
||||
var ret []string
|
||||
func (s *listTimersScreen) getSelectedTimers() []*timertxt.Timer {
|
||||
var ret []*timertxt.Timer
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
for _, v := range selTimer.Projects {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
ret = append(ret, selTimer)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
for _, v := range tmr.Projects {
|
||||
if !util.StringSliceContains(ret, v) {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
ret = append(ret, tmr)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (s *listTimersScreen) getSelectedTimerTags() map[string][]string {
|
||||
ret := make(map[string][]string)
|
||||
sel := s.getSelectedTimers()
|
||||
for _, tmr := range sel {
|
||||
for k, v := range tmr.AdditionalTags {
|
||||
ret[k] = util.AppendStringIfDistinct(ret[k], v)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (s *listTimersScreen) updateTagOnSelectedTimers(key, val string) {
|
||||
sel := s.getSelectedTimers()
|
||||
for _, tmr := range sel {
|
||||
tmr.AdditionalTags[key] = val
|
||||
}
|
||||
s.writeLists()
|
||||
}
|
||||
func (s *listTimersScreen) removeTagOnSelectedTimers(key string) {
|
||||
sel := s.getSelectedTimers()
|
||||
for _, tmr := range sel {
|
||||
if _, ok := tmr.AdditionalTags[key]; ok {
|
||||
delete(tmr.AdditionalTags, key)
|
||||
}
|
||||
}
|
||||
s.writeLists()
|
||||
}
|
||||
func (s *listTimersScreen) getSelectedTimerProjects() []string {
|
||||
var ret []string
|
||||
sel := s.getSelectedTimers()
|
||||
for _, tmr := range sel {
|
||||
for _, v := range tmr.Contexts {
|
||||
ret = util.AppendStringIfDistinct(ret, v)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (s *listTimersScreen) getSelectedTimerContexts() []string {
|
||||
var ret []string
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
for _, v := range selTimer.Contexts {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
for _, v := range tmr.Contexts {
|
||||
if !util.StringSliceContains(ret, v) {
|
||||
ret = append(ret, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
sel := s.getSelectedTimers()
|
||||
for _, tmr := range sel {
|
||||
for _, v := range tmr.Contexts {
|
||||
ret = util.AppendStringIfDistinct(ret, v)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
func (s *listTimersScreen) getSelectedTimerDuration() time.Duration {
|
||||
selected := len(s.selected)
|
||||
if selected == 0 {
|
||||
if s.cursor < s.fullFilterList.Size() {
|
||||
var selTimer *timertxt.Timer
|
||||
if selTimer, s.err = s.fullFilterList.GetTimer(s.cursor); s.err == nil {
|
||||
return util.Round(selTimer.Duration())
|
||||
}
|
||||
}
|
||||
}
|
||||
sel := s.getSelectedTimers()
|
||||
var ret time.Duration
|
||||
for i := range s.selected {
|
||||
if tmr, err := s.fullFilterList.GetTimer(i); err == nil {
|
||||
ret = util.AddDurations(ret, util.Round(tmr.Duration()))
|
||||
}
|
||||
for _, tmr := range sel {
|
||||
ret = util.AddDurations(ret, util.Round(tmr.Duration()))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Returns true if all selected timers are done
|
||||
func (s *listTimersScreen) areSelectedInDoneList() bool {
|
||||
sel := s.getSelectedTimers()
|
||||
for i := range sel {
|
||||
if s.timerList.Contains(sel[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Return true if all selected timers are from the same list (file)
|
||||
func (s *listTimersScreen) areSelectedInSameList() bool {
|
||||
sel := s.getSelectedTimers()
|
||||
var inActive, inDone int
|
||||
for i := range sel {
|
||||
if s.timerList.Contains(sel[i]) {
|
||||
inActive++
|
||||
}
|
||||
if s.doneList.Contains(sel[i]) {
|
||||
inDone++
|
||||
}
|
||||
}
|
||||
return inActive == 0 || inDone == 0
|
||||
}
|
||||
|
20
ui/ui.go
20
ui/ui.go
@ -22,11 +22,26 @@ const (
|
||||
CmdCanceled = ScreenMsgCommand(iota)
|
||||
CmdSaved
|
||||
|
||||
// ListTimers Commands
|
||||
CmdArchiveTimer
|
||||
|
||||
// Goto Screen Commands
|
||||
CmdGotoSettings
|
||||
CmdGotoTimerList
|
||||
)
|
||||
|
||||
// Styles
|
||||
var (
|
||||
DefaultStyle = wandle.NewStyle(
|
||||
termbox.RGBToAttribute(uint8(0), uint8(255), uint8(0)),
|
||||
termbox.RGBToAttribute(uint8(0), uint8(0), uint8(0)),
|
||||
)
|
||||
ErrStyle = wandle.NewStyle(
|
||||
termbox.RGBToAttribute(uint8(255), uint8(0), uint8(0)),
|
||||
termbox.RGBToAttribute(uint8(0), uint8(0), uint8(0)),
|
||||
)
|
||||
)
|
||||
|
||||
func RunTUI(p *cli.Program) error {
|
||||
ui := NewUi(p)
|
||||
ui.debug = true
|
||||
@ -71,10 +86,7 @@ func NewUi(p *cli.Program) *Ui {
|
||||
ui.screens[sId] = s
|
||||
ui.currScreen = sId
|
||||
ui.wandle = wandle.NewProgram(s)
|
||||
ui.wandle.Style(wandle.NewStyle(
|
||||
termbox.RGBToAttribute(uint8(0), uint8(255), uint8(0)),
|
||||
termbox.RGBToAttribute(uint8(0), uint8(0), uint8(0)),
|
||||
))
|
||||
ui.wandle.Style(DefaultStyle)
|
||||
return ui
|
||||
}
|
||||
|
||||
|
@ -138,7 +138,10 @@ func (w *PromptForTagWiddle) Measure() {
|
||||
}
|
||||
|
||||
func (w *PromptForTagWiddle) handleTermboxEvent(msg termbox.Event) wandle.Cmd {
|
||||
if msg.Key == termbox.KeyEnter {
|
||||
if msg.Key == termbox.KeyEsc {
|
||||
w.Done()
|
||||
return wandle.EmptyCmd
|
||||
} else if msg.Key == termbox.KeyEnter {
|
||||
if w.keyInput.IsEditable() {
|
||||
w.keyInput.SetActive(false)
|
||||
w.keyInput.SetEditable(false)
|
||||
|
@ -441,3 +441,9 @@ func StringSliceContains(sl []string, val string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
func AppendStringIfDistinct(sl []string, val string) []string {
|
||||
if !StringSliceContains(sl, val) {
|
||||
return append(sl, val)
|
||||
}
|
||||
return sl
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user