From 8f375efe41c6be55dcb729e78578e808aa30461d Mon Sep 17 00:00:00 2001 From: Leo Date: Wed, 25 Sep 2024 12:29:03 +0200 Subject: [PATCH] divs and paginators --- examples/main.go | 9 ++--- osui.go | 51 +++++++++++++++----------- ui/div.go | 81 +++++++++++++++++++++++++++++++++++++++++ ui/inputBox.go | 27 ++++++++------ ui/paginator.go | 95 +++++++++++++++++++++++++++--------------------- ui/text.go | 4 ++ utils.go | 23 ++++++++++++ 7 files changed, 210 insertions(+), 80 deletions(-) create mode 100644 ui/div.go diff --git a/examples/main.go b/examples/main.go index 6d642b4..577c9ff 100644 --- a/examples/main.go +++ b/examples/main.go @@ -8,12 +8,9 @@ import ( func main() { colors := ui.Text( - styles.Red + "Red!\n" + styles.Green + "Green!\n" + styles.Blue + "Blue!\n", + styles.Red + "Red!\n" + styles.Green + "Green!\n" + styles.Blue + "Blue!" + styles.Reset, ) - input := ui.InputBox(30) - paginator := ui.Paginator(colors, input) - paginator.UpdatePages[1] = input.Update + paginator := ui.Paginator(ui.Div(colors), ui.Div(ui.InputBox(30))) screen := osui.NewScreen(paginator) - screen.Render() - paginator.Run() + screen.Run() } diff --git a/osui.go b/osui.go index 77295ba..4d169de 100644 --- a/osui.go +++ b/osui.go @@ -8,47 +8,56 @@ import ( ) type ComponentData struct { - X uint16 - Y uint16 - Screen *Screen + X uint16 + Y uint16 + Width int + Height int + IsActive bool + Screen *Screen } type Component interface { Render() string GetComponentData() *ComponentData + Update(string) bool } type Screen struct { - components []Component - Width int - Height int + component Component } -func NewScreen(c ...Component) *Screen { - s := &Screen{components: c} - for _, component := range s.components { - componentData := component.GetComponentData() - if componentData.Screen == nil { - componentData.Screen = s - } else { - componentData.Screen.components = s.components - } - } +func NewScreen(c Component) *Screen { + HideCursor() + s := &Screen{component: c} return s } func (s *Screen) Render() error { Clear() width, height, err := term.GetSize(0) - s.Width = width - s.Height = height if err != nil { return fmt.Errorf("error getting the terminal size: %s", err) } frame := NewFrame(width, height) - for _, component := range s.components { - RenderOnFrame(component, &frame) - } + data := s.component.GetComponentData() + data.Width = width + data.Height = height + data.Screen = s + data.IsActive = true + RenderOnFrame(s.component, &frame) fmt.Print(strings.Join(frame, "\n")) return nil } + +func (s *Screen) Run() { + s.component.GetComponentData().Screen = s + s.component.Update("OSUI:RENDER") + for { + s.Render() + k, _ := ReadKey() + if s.component.Update(k) { + ShowCursor() + return + } + } +} diff --git a/ui/div.go b/ui/div.go new file mode 100644 index 0000000..61ee40d --- /dev/null +++ b/ui/div.go @@ -0,0 +1,81 @@ +package ui + +import ( + "strings" + + "github.com/orus-dev/osui" +) + +type DivComponent struct { + Data osui.ComponentData + Components []osui.Component + ActiveComponent int +} + +func (p *DivComponent) GetComponentData() *osui.ComponentData { + return &p.Data +} + +func (p DivComponent) Render() string { + frame := osui.NewFrame(p.Data.Width, p.Data.Height) + for _, c := range p.Components { + data := c.GetComponentData() + data.Width = p.Data.Width + data.Height = p.Data.Height + data.Screen = p.Data.Screen + osui.RenderOnFrame(c, &frame) + } + return strings.Join(frame, "\n") +} + +func (p *DivComponent) Run() { + for _, c := range p.Components { + d := c.GetComponentData() + d.Screen = p.Data.Screen + } + for { + key, _ := osui.ReadKey() + if p.Update(key) { + return + } + } +} + +func (p *DivComponent) Update(key string) bool { + switch key { + + case "": + p.updateActive(p.ActiveComponent) + case osui.Key.Up: + p.updateActive(p.ActiveComponent - 1) + case osui.Key.Down: + p.updateActive(p.ActiveComponent + 1) + default: + if len(p.Components) > 0 { + p.Components[p.ActiveComponent].GetComponentData().IsActive = p.Data.IsActive + if p.Components[p.ActiveComponent].Update(key) { + if p.ActiveComponent < len(p.Components)-1 { + p.updateActive(p.ActiveComponent + 1) + } else { + return true + } + } + } + } + return false +} + +func (p *DivComponent) updateActive(newIndex int) { + if newIndex >= 0 && newIndex < len(p.Components) && len(p.Components) > 0 { + p.Components[p.ActiveComponent].GetComponentData().IsActive = false + p.ActiveComponent = newIndex + p.Components[p.ActiveComponent].GetComponentData().IsActive = p.Data.IsActive + p.Components[p.ActiveComponent].Update("") + } +} + +func Div(components ...osui.Component) *DivComponent { + return &DivComponent{ + Components: components, + } +} diff --git a/ui/inputBox.go b/ui/inputBox.go index b17fffb..3116342 100644 --- a/ui/inputBox.go +++ b/ui/inputBox.go @@ -9,7 +9,6 @@ import ( type InputBoxComponent struct { Data osui.ComponentData - cursor string max_size uint InputData string } @@ -19,13 +18,20 @@ func (s *InputBoxComponent) GetComponentData() *osui.ComponentData { } func (s InputBoxComponent) Render() string { + if s.max_size > uint(len(s.InputData)) { + return fmt.Sprintf( + " %s\n|%s%s|\n %s", + strings.Repeat("_", int(s.max_size)), + s.InputData+osui.LogicValue(s.Data.IsActive, "█", ""), + strings.Repeat(" ", int(s.max_size)-len(s.InputData)-osui.LogicValueInt(s.Data.IsActive, 1, 0)), + strings.Repeat("‾", int(s.max_size)), + ) + } return fmt.Sprintf( - " %s\n|%s%s|\n %s%s", + " %s\n|%s\n %s", strings.Repeat("_", int(s.max_size)), - s.InputData, - strings.Repeat(" ", int(s.max_size)-len(s.InputData)), + s.InputData+osui.LogicValue(s.Data.IsActive, "█", "|"), strings.Repeat("‾", int(s.max_size)), - s.cursor, ) } @@ -46,23 +52,22 @@ func (s *InputBoxComponent) Read() error { } func (s *InputBoxComponent) Update(key string) bool { - // s.cursor = fmt.Sprintf("\x1B[%d;%dH", s.Data.Y+2, int(s.Data.X+2)+len(s.InputData)) - // s.Render() switch key { + case "": case osui.Key.Enter: return true - case osui.Key.Backspace: if len(s.InputData) > 0 { s.InputData = s.InputData[:len(s.InputData)-1] } default: - if int(s.max_size) > len(s.InputData) { - s.InputData += key + if len(key) == 1 { + if int(s.max_size) > len(s.InputData) { + s.InputData += key + } } } - s.Data.Screen.Render() return false } diff --git a/ui/paginator.go b/ui/paginator.go index 411ff75..0b3c260 100644 --- a/ui/paginator.go +++ b/ui/paginator.go @@ -13,11 +13,10 @@ type PaginatorStyle struct { } type PaginatorComponent struct { - Data osui.ComponentData - Style *PaginatorStyle - Pages []osui.Component - UpdatePages map[int]func(string) bool - ActivePage int + Data osui.ComponentData + Style *PaginatorStyle + Components []osui.Component + ActiveComponent int } func (p *PaginatorComponent) GetComponentData() *osui.ComponentData { @@ -25,59 +24,71 @@ func (p *PaginatorComponent) GetComponentData() *osui.ComponentData { } func (p PaginatorComponent) Render() string { - pgs := strings.Repeat(" ", p.Data.Screen.Width/2) - for page := range p.Pages { - if page == p.ActivePage { + pgs := strings.Repeat(" ", p.Data.Width/2) + for page, c := range p.Components { + if page == p.ActiveComponent { pgs += p.Style.Active + "•" + styles.Reset } else { pgs += styles.Reset + "•" } - } - frame := osui.NewFrame(p.Data.Screen.Width, p.Data.Screen.Height-3) - osui.RenderOnFrame(p.Pages[p.ActivePage], &frame) - return fmt.Sprintf("%s\n%s", pgs, styles.Reset+strings.Join(frame, "\n")) -} - -func (p *PaginatorComponent) Run() { - for _, c := range p.Pages { d := c.GetComponentData() if d.Screen == nil { d.Screen = p.Data.Screen } + d.Height = p.Data.Height - 1 + d.Width = p.Data.Width } - for { - key, _ := osui.ReadKey() - switch key { - case "\x1b[Z": - if p.ActivePage > 0 { - p.ActivePage-- - } else { - p.ActivePage = len(p.Pages) - 1 - } - case osui.Key.Tab: - if p.ActivePage < len(p.Pages)-1 { - p.ActivePage++ - } else { - p.ActivePage = 0 - } - case osui.Key.Escape: - fmt.Print("\n\n") - return - default: - for i, c := range p.UpdatePages { - if i == p.ActivePage { - c(key) + frame := osui.NewFrame(p.Data.Width, p.Data.Height-3) + osui.RenderOnFrame(p.Components[p.ActiveComponent], &frame) + return fmt.Sprintf("%s\n%s", pgs, styles.Reset+strings.Join(frame, "\n")) +} + +func (p *PaginatorComponent) Update(key string) bool { + switch key { + case "": + p.updateActive(p.ActiveComponent) + case "\x1b[Z": + if p.ActiveComponent > 0 { + p.updateActive(p.ActiveComponent - 1) + } else { + p.updateActive(len(p.Components) - 1) + } + case osui.Key.Tab: + if p.ActiveComponent < len(p.Components)-1 { + p.updateActive(p.ActiveComponent + 1) + } else { + p.updateActive(0) + } + case osui.Key.Escape: + fmt.Print("\n\n") + return true + default: + if len(p.Components) > 0 { + p.Components[p.ActiveComponent].GetComponentData().IsActive = p.Data.IsActive + if p.Components[p.ActiveComponent].Update(key) { + if p.ActiveComponent < len(p.Components)-1 { + p.updateActive(p.ActiveComponent + 1) + } else { + p.updateActive(0) } } } - p.Data.Screen.Render() + } + return false +} + +func (p *PaginatorComponent) updateActive(newIndex int) { + if newIndex >= 0 && newIndex < len(p.Components) && len(p.Components) > 0 { + p.Components[p.ActiveComponent].GetComponentData().IsActive = false + p.ActiveComponent = newIndex + p.Components[p.ActiveComponent].GetComponentData().IsActive = p.Data.IsActive + p.Components[p.ActiveComponent].Update("") } } func Paginator(pages ...osui.Component) *PaginatorComponent { return &PaginatorComponent{ - Pages: pages, - Style: osui.SetDefaults(&PaginatorStyle{}).(*PaginatorStyle), - UpdatePages: make(map[int]func(string) bool), + Components: pages, + Style: osui.SetDefaults(&PaginatorStyle{}).(*PaginatorStyle), } } diff --git a/ui/text.go b/ui/text.go index 6ad666b..0ddeb19 100644 --- a/ui/text.go +++ b/ui/text.go @@ -9,6 +9,10 @@ type TextComponent struct { Text string } +func (t *TextComponent) Update(string) bool { + return false +} + func (t *TextComponent) GetComponentData() *osui.ComponentData { return &t.Data } diff --git a/utils.go b/utils.go index 1528c4e..42ee1ff 100644 --- a/utils.go +++ b/utils.go @@ -1,6 +1,7 @@ package osui import ( + "fmt" "os" "os/exec" "reflect" @@ -117,6 +118,28 @@ func SetDefaults(p interface{}) interface{} { return p } +func ShowCursor() { + fmt.Print("\033[?25h") +} + +func HideCursor() { + fmt.Print("\033[?25l") +} + +func LogicValue(b bool, _if, _else string) string { + if b { + return _if + } + return _else +} + +func LogicValueInt(b bool, _if, _else int) int { + if b { + return _if + } + return _else +} + type Key_ = string type key struct {