diff --git a/app_state.go b/app_state.go index e219699..fd20c80 100644 --- a/app_state.go +++ b/app_state.go @@ -6,7 +6,9 @@ import ( "strings" todotxt "github.com/br0xen/go-todotxt" + "github.com/br0xen/termbox-screen" "github.com/br0xen/user-config" + termbox "github.com/nsf/termbox-go" ) type AppState struct { @@ -28,11 +30,22 @@ type AppState struct { taskListLoaded bool doneListLoaded bool - screens []Screen + uiManager *termboxScreen.Manager lang *Translator } +const ( + DefaultFg = termbox.ColorWhite + DefaultBg = termbox.ColorBlack + TitleFg = termbox.ColorWhite + TitleBg = termbox.ColorBlue + CursorFg = termbox.ColorBlack + CursorBg = termbox.ColorGreen + + ExitScreenId = -1 +) + func NewApp() *AppState { app := &AppState{Name: AppName, Version: AppVersion} app.initialize() @@ -49,7 +62,25 @@ func (a *AppState) run(parms []string) int { if len(parms) == 0 || parms[0] == "ui" { // UI Mode a.mode = ResModeUI - return uiLoop() + a.uiManager = termboxScreen.NewManager() + a.uiManager.AddScreen(&MainScreen{}) + a.uiManager.AddScreen(&TaskScreen{}) + a.uiManager.AddScreen(&AboutScreen{}) + for k := range a.uiManager.GetScreens() { + fmt.Print("Screen:", k, "\n") + } + + a.uiManager.SetDisplayScreen(MainScreenId) + mainBundle := termboxScreen.Bundle{} + mainBundle.SetValue(MainBundleListKey, MainBundleListTodo) + + if err := a.uiManager.InitializeScreen(MainScreenId, mainBundle); err != nil { + return 1 + } + if err := a.uiManager.Loop(); err != nil { + return 1 + } + return 0 } a.mode = ResModeCLI if fn, ok := a.OpFuncs[parms[0]]; ok { diff --git a/bundle.go b/bundle.go deleted file mode 100644 index 8d51992..0000000 --- a/bundle.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -type Bundle map[string]interface{} - -func (b Bundle) setValue(key string, val interface{}) { - b[key] = val -} - -func (b Bundle) getBool(key string, def bool) bool { - if v, ok := b[key].(bool); ok { - return v - } - return def -} - -func (b Bundle) getString(key, def string) string { - if v, ok := b[key].(string); ok { - return v - } - return def -} - -func (b Bundle) getInt(key string, def int) int { - if v, ok := b[key].(int); ok { - return v - } - return def -} diff --git a/resources.go b/resources.go index 0fbf0bf..309a05c 100644 --- a/resources.go +++ b/resources.go @@ -2,7 +2,7 @@ package main import "errors" -type ResourceId uint16 +type ResourceId uint32 type Translator struct { values map[ResourceId]string diff --git a/screen.go b/screen.go deleted file mode 100644 index 3a099c8..0000000 --- a/screen.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "time" - - termbox "github.com/nsf/termbox-go" -) - -type Screen interface { - handleKeyEvent(termbox.Event) int - initialize(Bundle) error - drawScreen() -} - -const ( - ScreenMain = iota - ScreenTask - ScreenAbout - ScreenExit - - DefaultBg = termbox.ColorBlack - DefaultFg = termbox.ColorWhite - TitleFg = termbox.ColorWhite - TitleBg = termbox.ColorBlue - CursorFg = termbox.ColorBlack - CursorBg = termbox.ColorGreen -) - -func (a *AppState) BuildScreens() { - mainScreen := MainScreen{} - aboutScreen := AboutScreen{} - taskScreen := TaskScreen{} - a.screens = append(a.screens, &mainScreen) - a.screens = append(a.screens, &taskScreen) - a.screens = append(a.screens, &aboutScreen) -} - -func (a *AppState) drawBackground(bg termbox.Attribute) { - termbox.Clear(0, bg) -} - -func (a *AppState) layoutAndDrawScreen(s Screen) { - a.drawBackground(DefaultBg) - s.drawScreen() - termbox.Flush() -} - -func readUserInput(e chan termbox.Event) { - for { - e <- termbox.PollEvent() - } -} - -func checkForUpdate(e chan termbox.Event) { - for { - time.Sleep(time.Minute) - if app.diskListChanged() { - e <- termbox.Event{ - Type: termbox.EventError, - Err: app.e(ResStrListChanged), - } - } - } -} - -/* - * ViewPort helps keep track of what's being displayed on the screen - */ -type ViewPort struct { - bytesPerRow int - numberOfRows int - firstRow int -} diff --git a/screen_about.go b/screen_about.go index d348d36..a83d80d 100644 --- a/screen_about.go +++ b/screen_about.go @@ -4,13 +4,15 @@ import ( "fmt" "time" + "github.com/br0xen/termbox-screen" "github.com/br0xen/termbox-util" termbox "github.com/nsf/termbox-go" ) +const AboutScreenId = 2 + // AboutScreen holds all that's going on type AboutScreen struct { - viewPort ViewPort message string messageTimeout time.Duration messageTime time.Time @@ -25,7 +27,9 @@ type Command struct { description string } -func (screen *AboutScreen) initialize(bundle Bundle) error { +func (screen *AboutScreen) Id() int { return AboutScreenId } + +func (screen *AboutScreen) Initialize(bundle termboxScreen.Bundle) error { screen.titleTemplate = []string{ " __ ", " _________ _____| | __", @@ -53,12 +57,13 @@ func (screen *AboutScreen) initialize(bundle Bundle) error { return nil } +func (screen *AboutScreen) ResizeScreen() { screen.Initialize(nil) } -func (screen *AboutScreen) handleKeyEvent(event termbox.Event) int { - return ScreenMain +func (screen *AboutScreen) HandleKeyEvent(event termbox.Event) int { + return MainScreenId } -func (screen *AboutScreen) drawScreen() { +func (screen *AboutScreen) DrawScreen() { width, height := termbox.Size() xPos := (width - len(screen.titleTemplate[0])) / 2 yPos := 1 diff --git a/screen_main.go b/screen_main.go index f402126..132cbfb 100644 --- a/screen_main.go +++ b/screen_main.go @@ -7,10 +7,19 @@ import ( "time" todotxt "github.com/br0xen/go-todotxt" + "github.com/br0xen/termbox-screen" "github.com/br0xen/termbox-util" termbox "github.com/nsf/termbox-go" ) +const MainScreenId = 0 + +type ViewPort struct { + bytesPerRow int + numberOfRows int + firstRow int +} + // MainScreen holds all that's going on type MainScreen struct { viewPort ViewPort @@ -51,7 +60,9 @@ const ( InputIDUnArchiveTask = "move task to active list? (y/n)" ) -func (screen *MainScreen) initialize(bundle Bundle) error { +func (screen *MainScreen) Id() int { return MainScreenId } + +func (screen *MainScreen) Initialize(bundle termboxScreen.Bundle) error { width, height := termbox.Size() screen.inputField = termboxUtil.CreateInputField(0, (height - 3), width, 1, DefaultFg, DefaultBg) @@ -66,9 +77,10 @@ func (screen *MainScreen) initialize(bundle Bundle) error { return nil } +func (screen *MainScreen) ResizeScreen() { screen.Initialize(nil) } -func (screen *MainScreen) refreshList(bundle Bundle) error { - whichList := bundle.getString(MainBundleListKey, MainBundleListTodo) +func (screen *MainScreen) refreshList(bundle termboxScreen.Bundle) error { + whichList := bundle.GetString(MainBundleListKey, MainBundleListTodo) switch whichList { case MainBundleListTodo: return app.LoadTaskList() @@ -78,14 +90,14 @@ func (screen *MainScreen) refreshList(bundle Bundle) error { return errors.New("Invalid refresh request.") } -func (screen *MainScreen) reloadList(bundle Bundle) error { +func (screen *MainScreen) reloadList(bundle termboxScreen.Bundle) error { // We add tasks to the display list using append because we want to persist task Ids screen.displayList = todotxt.NewTaskList() - screen.currentList = bundle.getString(MainBundleListKey, MainBundleListTodo) + screen.currentList = bundle.GetString(MainBundleListKey, MainBundleListTodo) switch screen.currentList { case MainBundleListTodo: screen.setActiveList(app.TaskList) - if screen.currentFilter = bundle.getString(MainBundleFilterKey, ""); screen.currentFilter != "" { + if screen.currentFilter = bundle.GetString(MainBundleFilterKey, ""); screen.currentFilter != "" { filteredList := app.filterList(screen.activeList, screen.currentFilter) for _, av := range *screen.activeList { for _, fv := range *filteredList { @@ -105,7 +117,7 @@ func (screen *MainScreen) reloadList(bundle Bundle) error { return err } screen.setActiveList(app.DoneList) - if screen.currentFilter = bundle.getString(MainBundleFilterKey, ""); screen.currentFilter != "" { + if screen.currentFilter = bundle.GetString(MainBundleFilterKey, ""); screen.currentFilter != "" { filteredList := app.filterList(screen.activeList, screen.currentFilter) for _, av := range *screen.activeList { for _, fv := range *filteredList { @@ -127,17 +139,17 @@ func (screen *MainScreen) reloadList(bundle Bundle) error { return nil } -func (screen *MainScreen) handleKeyEvent(event termbox.Event) int { +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 { + b := termboxScreen.Bundle{} + if err := app.uiManager.InitializeScreen(AboutScreenId, b); err != nil { screen.setErrorMessage(err.Error()) } - return ScreenAbout + return AboutScreenId } else if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 { if screen.backspaceDoes == MainBackspaceNothing { @@ -151,7 +163,7 @@ func (screen *MainScreen) handleKeyEvent(event termbox.Event) int { } else if screen.backspaceDoes == MainBackspaceFilter { screen.reloadList(screen.buildBundle(screen.currentList, "")) } - return ScreenMain + return MainScreenId } else if event.Key == termbox.KeySpace { return screen.toggleTaskComplete() @@ -211,9 +223,9 @@ func (screen *MainScreen) handleKeyEvent(event termbox.Event) int { screen.confirmArchiveItem() } else if event.Ch == 'q' { - return ScreenExit + return ExitScreenId } - return ScreenMain + return MainScreenId } func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { @@ -226,7 +238,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetValue("") screen.backspaceDoes = MainBackspaceFilter screen.reloadList(screen.buildBundle(screen.currentList, filter)) - return ScreenMain + return MainScreenId } case InputIDAddTask: if event.Key == termbox.KeyEnter { @@ -238,7 +250,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) - return ScreenMain + return MainScreenId } case InputIDIncompleteArchive: if event.Ch == 'y' || event.Ch == 'Y' { @@ -247,7 +259,7 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) - return ScreenMain + return MainScreenId case InputIDUnArchiveTask: if event.Ch == 'y' || event.Ch == 'Y' { screen.inputField.SetID("") @@ -258,30 +270,30 @@ func (screen *MainScreen) handleInputKeyEvent(event termbox.Event) int { screen.inputField.SetID("") screen.inputField.SetValue("") screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) - return ScreenMain + return MainScreenId } 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 + return MainScreenId } } else if event.Key == termbox.KeyEsc { screen.reloadList(screen.buildBundle(screen.currentList, screen.currentFilter)) screen.inputField.SetID("") screen.inputField.SetValue("") - return ScreenMain + return MainScreenId } screen.inputField.HandleEvent(event) - return ScreenMain + return MainScreenId } func (screen *MainScreen) setActiveList(list *todotxt.TaskList) { screen.activeList = list } -func (screen *MainScreen) drawScreen() { +func (screen *MainScreen) DrawScreen() { _, height := termbox.Size() screen.viewPort.numberOfRows = height - 1 if screen.inputField.GetID() != "" { @@ -352,7 +364,7 @@ func (screen *MainScreen) drawFooter() { func (screen *MainScreen) confirmArchiveItem() int { if screen.currentList != MainBundleListTodo { screen.inputField.SetID(InputIDUnArchiveTask) - return ScreenMain + return MainScreenId } // Find the task under the cursor if screen.cursor[screen.currentList] < len(*screen.displayList) { @@ -364,20 +376,20 @@ func (screen *MainScreen) confirmArchiveItem() int { return screen.archiveCurrentItem() } } - return ScreenMain + return MainScreenId } func (screen *MainScreen) archiveCurrentItem() int { if screen.currentList != MainBundleListTodo { screen.setErrorMessage("Task is already archived") - return ScreenMain + return MainScreenId } // Find the task under the cursor if len(*screen.displayList) > screen.cursor[screen.currentList] { t := (*screen.displayList)[screen.cursor[screen.currentList]] if err := app.archiveTask(t.Id); err != nil { screen.setErrorMessage(err.Error()) - return ScreenMain + return MainScreenId } // Reload the list b := screen.buildBundle(screen.currentList, screen.currentFilter) @@ -385,20 +397,20 @@ func (screen *MainScreen) archiveCurrentItem() int { screen.setErrorMessage(err.Error()) } } - return ScreenMain + return MainScreenId } func (screen *MainScreen) unarchiveCurrentItem() int { if screen.currentList == MainBundleListTodo { screen.setErrorMessage("Task is not archived") - return ScreenMain + return MainScreenId } // Find the task under the cursor if len(*screen.displayList) > screen.cursor[screen.currentList] { t := (*screen.displayList)[screen.cursor[screen.currentList]] if err := app.unarchiveTask(t.Id); err != nil { screen.setErrorMessage(err.Error()) - return ScreenMain + return MainScreenId } // Reload the list b := screen.buildBundle(screen.currentList, screen.currentFilter) @@ -406,7 +418,7 @@ func (screen *MainScreen) unarchiveCurrentItem() int { screen.setErrorMessage(err.Error()) } } - return ScreenMain + return MainScreenId } func (screen *MainScreen) startEditTaskScreen() int { @@ -414,46 +426,46 @@ func (screen *MainScreen) startEditTaskScreen() int { if len(*screen.displayList) > screen.cursor[screen.currentList] { t := (*screen.displayList)[screen.cursor[screen.currentList]] // Load the task screen with this task - b := Bundle{} - b.setValue(TaskBundleTaskIdKey, t.Id) - if err := app.screens[ScreenTask].initialize(b); err != nil { + b := termboxScreen.Bundle{} + b.SetValue(TaskBundleTaskIdKey, t.Id) + if err := app.uiManager.InitializeScreen(TaskScreenId, b); err != nil { screen.setErrorMessage(err.Error()) - return ScreenMain + return MainScreenId } - return ScreenTask + return TaskScreenId } - return ScreenMain + return MainScreenId } func (screen *MainScreen) reloadCurrentView() { - bundle := Bundle{} - bundle.setValue(MainBundleListKey, screen.currentList) - bundle.setValue(MainBundleFilterKey, screen.currentFilter) + bundle := termboxScreen.Bundle{} + bundle.SetValue(MainBundleListKey, screen.currentList) + bundle.SetValue(MainBundleFilterKey, screen.currentFilter) screen.reloadList(bundle) } func (screen *MainScreen) toggleViewList() int { - bundle := Bundle{} + bundle := termboxScreen.Bundle{} if screen.currentList == MainBundleListTodo { - bundle.setValue(MainBundleListKey, MainBundleListDone) + bundle.SetValue(MainBundleListKey, MainBundleListDone) screen.backspaceDoes = MainBackspaceMain } else { - bundle.setValue(MainBundleListKey, MainBundleListTodo) + bundle.SetValue(MainBundleListKey, MainBundleListTodo) } - bundle.setValue(MainBundleFilterKey, screen.currentFilter) + bundle.SetValue(MainBundleFilterKey, screen.currentFilter) screen.reloadList(bundle) - return ScreenMain + return MainScreenId } func (screen *MainScreen) startAddNewTask() int { screen.inputField.SetID(InputIDAddTask) - return ScreenMain + return MainScreenId } func (screen *MainScreen) toggleTaskComplete() int { if screen.currentList == MainBundleListDone { screen.setErrorMessage("Task is archived, unable to modify.") - return ScreenMain + return MainScreenId } // Find the task under the cursor @@ -462,11 +474,11 @@ func (screen *MainScreen) toggleTaskComplete() int { err := app.toggleTaskComplete(t.Id) if err != nil { screen.setErrorMessage(err.Error()) - return ScreenMain + return MainScreenId } } screen.reloadCurrentView() - return ScreenMain + return MainScreenId } func (screen *MainScreen) moveCursorDown() bool { @@ -489,7 +501,7 @@ func (screen *MainScreen) moveCursorUp() bool { func (screen *MainScreen) startFilter() int { screen.inputField.SetID(InputIDFilter) - return ScreenMain + return MainScreenId } func (screen *MainScreen) setErrorMessage(msg string) { @@ -524,9 +536,9 @@ func (screen *MainScreen) clearMessage() { screen.messageColorFg = DefaultFg } -func (screen *MainScreen) buildBundle(list, filter string) Bundle { - bundle := Bundle{} - bundle.setValue(MainBundleListKey, list) - bundle.setValue(MainBundleFilterKey, filter) +func (screen *MainScreen) buildBundle(list, filter string) termboxScreen.Bundle { + bundle := termboxScreen.Bundle{} + bundle.SetValue(MainBundleListKey, list) + bundle.SetValue(MainBundleFilterKey, filter) return bundle } diff --git a/screen_task.go b/screen_task.go index 33ebb0d..8789e33 100644 --- a/screen_task.go +++ b/screen_task.go @@ -7,10 +7,13 @@ import ( "time" todotxt "github.com/br0xen/go-todotxt" + "github.com/br0xen/termbox-screen" "github.com/br0xen/termbox-util" termbox "github.com/nsf/termbox-go" ) +const TaskScreenId = 1 + // TaskScreen holds all that's going on type TaskScreen struct { message string @@ -29,10 +32,12 @@ const ( TaskBundleTaskIdKey = "taskscreen.taskid" ) -func (screen *TaskScreen) initialize(bundle Bundle) error { +func (screen *TaskScreen) Id() int { return TaskScreenId } + +func (screen *TaskScreen) Initialize(bundle termboxScreen.Bundle) error { var err error if bundle != nil { - screen.currentTaskId = bundle.getInt(TaskBundleTaskIdKey, -1) + screen.currentTaskId = bundle.GetInt(TaskBundleTaskIdKey, -1) } if screen.currentTaskId == -1 { return errors.New("Task Screen Initialization Failed") @@ -43,9 +48,11 @@ func (screen *TaskScreen) initialize(bundle Bundle) error { return nil } -func (screen *TaskScreen) handleKeyEvent(event termbox.Event) int { +func (screen *TaskScreen) ResizeScreen() { screen.Initialize(nil) } + +func (screen *TaskScreen) HandleKeyEvent(event termbox.Event) int { if event.Key == termbox.KeyBackspace || event.Key == termbox.KeyBackspace2 || event.Ch == 'h' || event.Key == termbox.KeyArrowLeft { - return ScreenMain + return MainScreenId } else if event.Ch == 'j' || event.Key == termbox.KeyArrowDown { screen.moveCursorDown() @@ -53,10 +60,10 @@ func (screen *TaskScreen) handleKeyEvent(event termbox.Event) int { } else if event.Ch == 'k' || event.Key == termbox.KeyArrowUp { screen.moveCursorUp() } - return ScreenTask + return TaskScreenId } -func (screen *TaskScreen) drawScreen() { +func (screen *TaskScreen) DrawScreen() { screen.drawHeader() yPos := 1 diff --git a/ui_loop.go b/ui_loop.go deleted file mode 100644 index 5c594ad..0000000 --- a/ui_loop.go +++ /dev/null @@ -1,58 +0,0 @@ -package main - -import ( - "fmt" - "os" - "runtime" - "syscall" - - termbox "github.com/nsf/termbox-go" -) - -func uiLoop() int { - err := termbox.Init() - if err != nil { - fmt.Println(err.Error()) - return 1 - } - termbox.SetOutputMode(termbox.Output256) - app.BuildScreens() - displayScreen := app.screens[ScreenMain] - bundle := Bundle{} - bundle.setValue(MainBundleListKey, MainBundleListTodo) - - displayScreen.initialize(bundle) - app.layoutAndDrawScreen(displayScreen) - eventChan := make(chan termbox.Event) - go readUserInput(eventChan) - go checkForUpdate(eventChan) - for { - event := <-eventChan - if event.Type == termbox.EventKey { - if event.Key == termbox.KeyCtrlC { - break - } else if event.Key == termbox.KeyCtrlZ { - if runtime.GOOS != "windows" { - process, _ := os.FindProcess(os.Getpid()) - termbox.Close() - process.Signal(syscall.SIGSTOP) - termbox.Init() - } - } - newScreenIndex := displayScreen.handleKeyEvent(event) - if newScreenIndex < len(app.screens) { - displayScreen = app.screens[newScreenIndex] - app.layoutAndDrawScreen(displayScreen) - } else { - break - } - } - if event.Type == termbox.EventResize { - displayScreen.initialize(nil) - app.layoutAndDrawScreen(displayScreen) - } - } - termbox.Close() - // Any wrap up should be done here... - return 0 -}