diff --git a/bundle.go b/bundle.go new file mode 100644 index 0000000..57dac1e --- /dev/null +++ b/bundle.go @@ -0,0 +1,28 @@ +package termboxScreen + +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/screen.go b/screen.go new file mode 100644 index 0000000..0936cc4 --- /dev/null +++ b/screen.go @@ -0,0 +1,152 @@ +package termboxScreen + +import ( + "errors" + "os" + "runtime" + "syscall" + + termbox "github.com/nsf/termbox-go" +) + +type Screen interface { + Id() int + Initialize(Bundle) error + HandleKeyEvent(termbox.Event) int + HandleNoneEvent(termbox.Event) int + DrawScreen() + ResizeScreen() +} + +type Manager struct { + defaultFg termbox.Attribute + defaultBg termbox.Attribute + + screens map[int]Screen + displayScreenId int + events chan termbox.Event +} + +func NewManager() *Manager { + m := Manager{ + defaultFg: termbox.ColorWhite, + defaultBg: termbox.ColorBlack, + events: make(chan termbox.Event), + screens: make(map[int]Screen), + } + // Add the default user-input provider + m.AddEventProvider(func(e chan termbox.Event) { + for { + e <- termbox.PollEvent() + } + }) + return &m +} + +func (m *Manager) SetDefaultFg(c termbox.Attribute) { m.defaultFg = c } +func (m *Manager) SetDefaultBg(c termbox.Attribute) { m.defaultBg = c } + +// AddScreen adds a screen to the screens map, they're indexed by the value +// returned from the screen's Id() function +func (m *Manager) AddScreen(s Screen) { + m.screens[s.Id()] = s + // If this is the only screen we've added, set it to active + if len(m.screens) == 1 { + m.SetDisplayScreen(s.Id()) + } +} + +func (m *Manager) GetScreens() map[int]Screen { + return m.screens +} + +func (m *Manager) SetDisplayScreen(id int) error { + if id == m.displayScreenId { + return nil + } + var ok bool + if _, ok = m.screens[id]; !ok { + return errors.New("Invalid Screen Id") + } + m.displayScreenId = id + return nil +} + +func (m *Manager) InitializeScreen(id int, b Bundle) error { + var ok bool + if _, ok = m.screens[id]; !ok { + return errors.New("Invalid screen id") + } + return m.screens[id].Initialize(b) +} + +func (m *Manager) AddEventProvider(provider func(chan termbox.Event)) { + go provider(m.events) +} + +func (m *Manager) Loop() error { + if len(m.screens) == 0 { + return errors.New("Loop cannot run without screens") + } + if err := termbox.Init(); err != nil { + return err + } + termbox.SetOutputMode(termbox.Output256) + // We always start display the first screen added + m.layoutAndDrawScreen() + for { + event := <-m.events + 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() + } + } else { + newScreenIndex := m.handleKeyEvent(event) + if err := m.SetDisplayScreen(newScreenIndex); err != nil { + break + } + m.layoutAndDrawScreen() + } + } else if event.Type == termbox.EventNone { + // Type = EventNone is how we can trigger automatic events + newScreenIndex := m.handleNoneEvent(event) + if err := m.SetDisplayScreen(newScreenIndex); err != nil { + break + } + m.layoutAndDrawScreen() + } else if event.Type == termbox.EventResize { + m.resizeScreen() + m.layoutAndDrawScreen() + } + } + termbox.Close() + return nil +} + +func (m *Manager) handleKeyEvent(event termbox.Event) int { + return m.screens[m.displayScreenId].HandleKeyEvent(event) +} + +func (m *Manager) handleNoneEvent(event termbox.Event) int { + return m.screens[m.displayScreenId].HandleNoneEvent(event) +} + +func (m *Manager) resizeScreen() { + m.screens[m.displayScreenId].ResizeScreen() +} + +func (m *Manager) drawBackground() { + termbox.Clear(0, m.defaultBg) +} + +func (m *Manager) layoutAndDrawScreen() { + m.drawBackground() + m.screens[m.displayScreenId].DrawScreen() + termbox.Flush() +}