package main import ( "fmt" "strings" "time" timertxt "git.bullercodeworks.com/brian/go-timertxt" termboxUtil "github.com/br0xen/termbox-util" termbox "github.com/nsf/termbox-go" ) const ( MainBundleListKey = "mainscreen.list" MainBundleFilterKey = "mainscreen.filter" MainBundleListRecent = "mainscreen.list.recent" MainBundleListArchive = "mainscreen.list.archive" MainBackspaceNothing = iota MainBackspaceMain MainBackspaceFilter InputIDFilter = "filter" InputIDAddTimer = "add timer" InputIDUnArchiveTask = "move timer to active list? (y/n)" ) type MainScreen struct { viewPort ViewPort message string messageTimeout time.Duration messageTime time.Time mode int cursor map[string]int inputField *termboxUtil.InputField currentList string currentFilter string backspaceDoes int displayList *timertxt.TimerList activeList *timertxt.TimerList } func (screen *MainScreen) initialize(bundle Bundle) error { width, height := termbox.Size() screen.inputField = termboxUtil.CreateInputField(0, (height - 3), width, 1, DefaultFg, DefaultBg) screen.cursor = make(map[string]int) if bundle != nil { if err := screen.reloadList(bundle); err != nil { return err } screen.inputField.SetID("") screen.inputField.SetBordered(false) } return nil } func (screen *MainScreen) reloadList(bundle Bundle) error { screen.displayList = timertxt.NewTimerList() screen.currentList = bundle.getString(MainBundleListKey, MainBundleListRecent) switch screen.currentList { case MainBundleListRecent: screen.setActiveList(app.TimerList) if screen.currentFilter = bundle.getString(MainBundleFilterKey, ""); screen.currentFilter != "" { filteredList := app.filterList(screen.activeList, screen.currentFilter) for _, av := range *screen.activeList { for _, fv := range *filteredList { if av.String() == fv.String() { screen.displayList.AddTimer(&av) break } } } } else { for _, av := range *screen.activeList { screen.displayList.AddTimer(&av) } } case MainBundleListArchive: //screen.setActiveList( } if screen.cursor[screen.currentList] > len(*screen.displayList)-1 { screen.cursor[screen.currentList] = len(*screen.displayList) - 1 } return nil } func (screen *MainScreen) reloadCurrentView() { bundle := Bundle{} bundle.setValue(MainBundleListKey, screen.currentList) bundle.setValue(MainBundleFilterKey, screen.currentFilter) screen.reloadList(bundle) } func (screen *MainScreen) handleKeyEvent(event termbox.Event) int { if screen.inputField.GetID() != "" { return screen.handleInputKeyEvent(event) } if event.Ch == '?' { // Go to About Screen b := Bundle{} if err := app.screens[ScreenAbout].initialize(b); err != nil { screen.setMessage(err.Error()) } return ScreenAbout } else if event.Ch == 'g' { screen.cursor[screen.currentList] = 0 } else if event.Ch == 'G' { screen.cursor[screen.currentList] = len(*screen.displayList) - 1 } else if event.Key == termbox.KeyCtrlF { // Jump forward half a screen _, h := termbox.Size() screen.cursor[screen.currentList] += (h / 2) if screen.cursor[screen.currentList] >= len(*screen.displayList) { screen.cursor[screen.currentList] = len(*screen.displayList) - 1 } } else if event.Key == termbox.KeyCtrlB { // Jump back half a screen _, h := termbox.Size() screen.cursor[screen.currentList] -= (h / 2) if screen.cursor[screen.currentList] < 0 { screen.cursor[screen.currentList] = 0 } } else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown { screen.moveCursorDown() } else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp { screen.moveCursorUp() } else if event.Ch == '/' { screen.startFilter() } else if event.Ch == 'L' { return screen.toggleViewList() } else if event.Ch == 'q' { return ScreenExit } return ScreenMain } func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { switch screen.inputField.GetID() { case InputIDFilter: if event.Key == termbox.KeyEnter { // Apply the filter filter := screen.inputField.GetValue() screen.inputField.SetID("") screen.inputField.SetValue("") screen.backspaceDoes = MainBackspaceFilter screen.reloadList(screen.buildBundle(screen.currentList, filter)) return ScreenMain } case InputIDAddTimer: if event.Key == termbox.KeyEnter { // Create the new item err := app.addTimer(screen.inputField.GetValue()) if err != nil { screen.setMessage(err.Error()) } screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) return ScreenMain } } if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { if screen.inputField.GetValue() == "" { screen.reloadList(screen.buildBundle(screen.currentList, screen.inputField.GetValue())) screen.inputField.SetID("") screen.inputField.SetValue("") return ScreenMain } } else if event.Key == termbox.KeyEsc { screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.inputField.SetID("") screen.inputField.SetValue("") return ScreenMain } screen.inputField.HandleEvent(event) return ScreenMain } func (screen *MainScreen) drawScreen() { _, height := termbox.Size() screen.viewPort.numberOfRows = height - 1 if screen.inputField.GetID() != "" { screen.viewPort.numberOfRows-- } screen.viewPort.firstRow = 1 displayOffset := 0 maxCursor := screen.viewPort.numberOfRows * 2 / 3 if screen.cursor[screen.currentList] > maxCursor { displayOffset = screen.cursor[screen.currentList] - maxCursor } if screen.message == "" { screen.setMessageWithTimeout("Press '?' for help", -1) } screen.drawHeader() topId := 0 for _, v := range *screen.displayList { if v.Id > topId { topId = v.Id } } padCnt := fmt.Sprintf("%d", topId) for k, v := range *screen.displayList { pad := strings.Repeat(" ", len(padCnt)-len(fmt.Sprintf("%d", v.Id))) useFg, useBg := DefaultFg, DefaultBg if k == screen.cursor[screen.currentList] { useFg, useBg = CursorFg, CursorBg } lineY := k + 1 - displayOffset if lineY > 0 && lineY < screen.viewPort.numberOfRows { termboxUtil.DrawStringAtPoint(pad+app.getTimerString(v), 0, lineY, useFg, useBg) } } screen.drawFooter() } func (screen *MainScreen) drawHeader() { width, _ := termbox.Size() headerString := screen.currentFilter if headerString == "" { if screen.currentList == MainBundleListRecent { headerString = "Timers" } else if screen.currentList == MainBundleListArchive { headerString = "Timer Archive" } } spaces := strings.Repeat(" ", ((width-len(headerString))/2)+1) termboxUtil.DrawStringAtPoint(fmt.Sprintf("%s%s%s", spaces, headerString, spaces), 0, 0, TitleFg, TitleBg) } func (screen *MainScreen) drawFooter() { if screen.messageTimeout > 0 && time.Since(screen.messageTime) > screen.messageTimeout { screen.clearMessage() } width, height := termbox.Size() if screen.inputField.GetID() != "" { screen.inputField.SetX(len(screen.inputField.GetID()) + 2) pad := width - len(screen.inputField.GetID()+":") field := screen.inputField.GetID() + ":" + strings.Repeat(" ", pad) termboxUtil.DrawStringAtPoint(field, 0, height-2, DefaultFg, DefaultBg) screen.inputField.Draw() } // And the 'message' termboxUtil.DrawStringAtPoint(screen.message, 0, height-1, DefaultFg, DefaultBg) } func (screen *MainScreen) toggleViewList() int { bundle := Bundle{} if screen.currentList == MainBundleListRecent { bundle.setValue(MainBundleListKey, MainBundleListArchive) screen.backspaceDoes = MainBackspaceMain } else { bundle.setValue(MainBundleListKey, MainBundleListRecent) } bundle.setValue(MainBundleFilterKey, screen.currentFilter) screen.reloadList(bundle) return ScreenMain } func (screen *MainScreen) moveCursorDown() bool { screen.cursor[screen.currentList]++ if screen.cursor[screen.currentList] >= len(*screen.displayList) { screen.cursor[screen.currentList] = len(*screen.displayList) - 1 return false } return true } func (screen *MainScreen) moveCursorUp() bool { screen.cursor[screen.currentList]-- if screen.cursor[screen.currentList] < 0 { screen.cursor[screen.currentList] = 0 return false } return true } func (screen *MainScreen) startFilter() int { screen.inputField.SetID(InputIDFilter) return ScreenMain } func (screen *MainScreen) setMessage(msg string) { 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 *MainScreen) setMessageWithTimeout(msg string, timeout time.Duration) { screen.message = msg screen.messageTime = time.Now() screen.messageTimeout = timeout } func (screen *MainScreen) clearMessage() { screen.message = fmt.Sprintf("%d Total Tasks", len(*screen.activeList)) screen.messageTimeout = -1 } func (screen *MainScreen) buildBundle(list, filter string) Bundle { bundle := Bundle{} bundle.setValue(MainBundleListKey, list) bundle.setValue(MainBundleFilterKey, filter) return bundle } func (screen *MainScreen) setActiveList(list *timertxt.TimerList) { screen.activeList = list }