diff --git a/osui.go b/osui.go index 27d3559..e28be07 100644 --- a/osui.go +++ b/osui.go @@ -2,60 +2,40 @@ package osui import ( "fmt" - "os" "strings" "golang.org/x/term" ) +type ComponentData struct { + X uint16 + Y uint16 + Screen *Screen +} + type Component interface { - Render(*ComponentWrapper) string - Run(*ComponentWrapper) error + Render() string + GetComponentData() *ComponentData } -type ComponentWrapper struct { - Component Component - X uint16 - Y uint16 - render *Render - Update func(*ComponentWrapper, any) UpdateOutput_ +type Screen struct { + components []Component } -func (c *ComponentWrapper) Run() { - c.Component.Run(c) -} - -func (cw *ComponentWrapper) Render(c Component) error { - cw.Component = c - return cw.render.Render() -} - -func (c *ComponentWrapper) SetRender(r *Render) { - c.render = r -} - -func NewComponent(c Component) *ComponentWrapper { - return &ComponentWrapper{ - Component: c, - Update: func(cw *ComponentWrapper, a any) UpdateOutput_ { return UpdateOutput.Continue }, +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 + } } + return s } -type Render struct { - components []*ComponentWrapper -} - -func NewRender() *Render { - return &Render{components: []*ComponentWrapper{}} -} - -func (r *Render) Add(c *ComponentWrapper) *ComponentWrapper { - c.SetRender(r) - r.components = append(r.components, c) - return c -} - -func (r *Render) Render() error { +func (s *Screen) Render() error { Clear() width, height, err := term.GetSize(0) if err != nil { @@ -65,106 +45,9 @@ func (r *Render) Render() error { for i := 0; i < height; i++ { frame[i] = strings.Repeat(" ", width) } - for _, c := range r.components { - for i, line := range strings.Split(c.Component.Render(c), "\n") { - if int(c.Y)+i < len(frame) { - frame[int(c.Y)+i] = renderLine(frame[int(c.Y)+i], line, int(c.X)) - } - } + for _, component := range s.components { + RenderOnFrame(component, &frame) } fmt.Print(strings.Join(frame, "\n")) return nil } - -func (r *Render) Run() { - for _, c := range r.components { - c.Run() - } -} - -func Clear() { - fmt.Print("\x1b[H\x1b[2J\x1b[3J") -} - -func renderLine(frameLine, line string, x int) string { - result := "" - if x >= len(frameLine) { - return result - } - - lChars := []rune(line) - for i, c := range frameLine { - if i >= x && len(lChars) > i-x { - result += string(lChars[i-x]) - } else { - result += string(c) - } - } - return result -} - -func ReadKey() (string, error) { - oldState, err := term.MakeRaw(int(os.Stdin.Fd())) - if err != nil { - return "", err - } - defer term.Restore(int(os.Stdin.Fd()), oldState) - var b [3]byte - n, err := os.Stdin.Read(b[:]) - if err != nil { - return "", err - } - return string(b[:n]), nil -} - -func KeyUpdate(keys map[string]UpdateOutput_) func(c *ComponentWrapper, key any) UpdateOutput_ { - return func(c *ComponentWrapper, k any) UpdateOutput_ { - action, ok := keys[fmt.Sprint(k)] - if ok { - return action - } else { - return UpdateOutput.Continue - } - } -} - -type UpdateOutput_ = int - -type updateOutput struct { - /*Continue the default check*/ - Continue UpdateOutput_ - /*Jump to the next check*/ - Jmp UpdateOutput_ - /*Leave the loop and stop the function*/ - Exit UpdateOutput_ -} - -var UpdateOutput = updateOutput{Continue: 0, Jmp: 1, Exit: 2} - -/* -Keys -*/ - -type Key_ = string - -type key struct { - Enter Key_ - Tab Key_ - Backspace Key_ - Escape Key_ - Up Key_ - Down Key_ - Right Key_ - Left Key_ -} - -var Key = key{ - Enter: "\r", - Tab: "\t", - Backspace: "\x7f", - Escape: "\x1b", - Up: "\x1b[A", - Down: "\x1b[B", - Right: "\x1b[C", - Left: "\x1b[D", -} diff --git a/ui/components.go b/ui/components.go index 5c142d5..3008b6f 100644 --- a/ui/components.go +++ b/ui/components.go @@ -7,67 +7,64 @@ import ( "github.com/orus-dev/osui" ) -type ComponentText struct { +type TextComponent struct { + Data osui.ComponentData Text string } -func (t ComponentText) Render(cw *osui.ComponentWrapper) string { +func (t *TextComponent) GetComponentData() *osui.ComponentData { + return &t.Data +} + +func (t TextComponent) Render() string { return t.Text } -func (t ComponentText) Run(cw *osui.ComponentWrapper) error { - return nil +type InputBoxComponent struct { + Data osui.ComponentData + max_size uint + InputData string } -type ComponentInputBox struct { - max_size uint - Data string +func (s *InputBoxComponent) GetComponentData() *osui.ComponentData { + return &s.Data } -func (s ComponentInputBox) Render(cw *osui.ComponentWrapper) string { +func (s InputBoxComponent) Render() string { return fmt.Sprintf( " %s\n|%s%s|\n %s", strings.Repeat("_", int(s.max_size)), - s.Data, - strings.Repeat(" ", int(s.max_size)-len(s.Data)), + s.InputData, + strings.Repeat(" ", int(s.max_size)-len(s.InputData)), strings.Repeat("‾", int(s.max_size)), ) } -func (s ComponentInputBox) Run(cw *osui.ComponentWrapper) error { +func (s *InputBoxComponent) Read() error { for { - fmt.Printf("\x1B[%d;%dH", cw.Y+2, int(cw.X+2)+len(s.Data)) + fmt.Printf("\x1B[%d;%dH", s.Data.Y+2, int(s.Data.X+2)+len(s.InputData)) key, err := osui.ReadKey() if err != nil { + fmt.Print("\n\n") fmt.Println(err) break } - switch cw.Update(cw, key) { - - case osui.UpdateOutput.Jmp: - continue - case osui.UpdateOutput.Exit: - fmt.Print("\n\n") - return nil - - } switch key { - case osui.Key.Enter: fmt.Print("\n\n") return nil case osui.Key.Backspace: - if len(s.Data) > 0 { - s.Data = s.Data[:len(s.Data)-1] + if len(s.InputData) > 0 { + s.InputData = s.InputData[:len(s.InputData)-1] } default: - if int(s.max_size) > len(s.Data) { - s.Data += key + if int(s.max_size) > len(s.InputData) { + s.InputData += key } } - cw.Render(s) + s.Data.Screen.Render() } return nil } diff --git a/ui/ui.go b/ui/ui.go index 9e99e1c..b94e34b 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -1,13 +1,9 @@ package ui -import ( - "github.com/orus-dev/osui" -) - -func InputBox(max_size uint) *osui.ComponentWrapper { - return osui.NewComponent(ComponentInputBox{max_size: max_size}) +func Text(text string) *TextComponent { + return &TextComponent{Text: text} } -func Text(text string) *osui.ComponentWrapper { - return osui.NewComponent(ComponentText{Text: text}) +func InputBox(max_size uint) *InputBoxComponent { + return &InputBoxComponent{max_size: max_size} } diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..7c814be --- /dev/null +++ b/utils.go @@ -0,0 +1,77 @@ +package osui + +import ( + "fmt" + "os" + "strings" + + "golang.org/x/term" +) + +func renderLine(frameLine, line string, x int) string { + result := "" + if x >= len(frameLine) { + return result + } + + lChars := []rune(line) + for i, c := range frameLine { + if i >= x && len(lChars) > i-x { + result += string(lChars[i-x]) + } else { + result += string(c) + } + } + return result +} + +func RenderOnFrame(c Component, frame *[]string) { + componentData := c.GetComponentData() + for i, line := range strings.Split(c.Render(), "\n") { + if int(componentData.Y)+i < len(*frame) { + (*frame)[int(componentData.Y)+i] = renderLine((*frame)[int(componentData.Y)+i], line, int(componentData.X)) + } + } +} + +func ReadKey() (string, error) { + oldState, err := term.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + return "", err + } + defer term.Restore(int(os.Stdin.Fd()), oldState) + var b [3]byte + n, err := os.Stdin.Read(b[:]) + if err != nil { + return "", err + } + return string(b[:n]), nil +} + +func Clear() { + fmt.Print("\x1b[H\x1b[2J\x1b[3J") +} + +type Key_ = string + +type key struct { + Enter Key_ + Tab Key_ + Backspace Key_ + Escape Key_ + Up Key_ + Down Key_ + Right Key_ + Left Key_ +} + +var Key = key{ + Enter: "\r", + Tab: "\t", + Backspace: "\x7f", + Escape: "\x1b", + Up: "\x1b[A", + Down: "\x1b[B", + Right: "\x1b[C", + Left: "\x1b[D", +}