more views
This commit is contained in:
parent
dfa00f5fe3
commit
b797dc0b2c
@ -55,6 +55,46 @@ func TestFlowGroup(t *testing.T) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSeparatorGroup(t *testing.T) {
|
||||||
|
textView := views.NewTextView("hello world!")
|
||||||
|
textView.SetStyle(tui.StyleDefault.Background(tcell.ColorRed).Foreground(tcell.ColorBlack))
|
||||||
|
|
||||||
|
frameView := views.NewFrameView(textView)
|
||||||
|
|
||||||
|
textView2 := views.NewTextView("Hi!")
|
||||||
|
textView2.SetStyle(tui.StyleDefault.Background(tcell.ColorBlue).Foreground(tcell.ColorYellow))
|
||||||
|
|
||||||
|
growView := views.NewGrowView()
|
||||||
|
growView.SetStyle(tui.StyleDefault.Background(tcell.ColorGreen))
|
||||||
|
|
||||||
|
growView2 := views.NewGrowView()
|
||||||
|
growView2.SetStyle(tui.StyleDefault.Background(tcell.ColorYellow))
|
||||||
|
|
||||||
|
separatorGroup := views.NewSeparatorGroup(tui.Vertical)
|
||||||
|
separatorGroup.AppendView(frameView, 1)
|
||||||
|
separatorGroup.AppendView(growView, 1)
|
||||||
|
separatorGroup.AppendView(textView2, 1)
|
||||||
|
|
||||||
|
screen, err := tui.NewScreen(separatorGroup)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
screen.KeyPressed = func(event *tui.KeyEvent) (consumed bool) {
|
||||||
|
textView.Text = event.When().String()
|
||||||
|
|
||||||
|
if event.Key() == tcell.KeyCtrlC {
|
||||||
|
screen.StopWithError(errors.New(fmt.Sprintf("key: %#v | rune: %s", event.Key(), string(event.Rune()))))
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
err = screen.Start()
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestBorderGroup(t *testing.T) {
|
func TestBorderGroup(t *testing.T) {
|
||||||
topView := views.NewConstrainView(nil)
|
topView := views.NewConstrainView(nil)
|
||||||
topView.SetStyle(tui.StyleDefault.Background(tcell.ColorBlue))
|
topView.SetStyle(tui.StyleDefault.Background(tcell.ColorBlue))
|
||||||
|
22
types.go
22
types.go
@ -35,8 +35,22 @@ const (
|
|||||||
type Side uint8
|
type Side uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Top Side = iota
|
SideTop Side = iota
|
||||||
Bottom
|
SideBottom
|
||||||
Left
|
SideLeft
|
||||||
Right
|
SideRight
|
||||||
|
)
|
||||||
|
|
||||||
|
type Anchor uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
AnchorTopLeft Anchor = iota
|
||||||
|
AnchorTop
|
||||||
|
AnchorTopRight
|
||||||
|
AnchorLeft
|
||||||
|
AnchorCenter
|
||||||
|
AnchorRight
|
||||||
|
AnchorBottomLeft
|
||||||
|
AnchorBottom
|
||||||
|
AnchorBottomRight
|
||||||
)
|
)
|
||||||
|
25
utils.go
25
utils.go
@ -87,3 +87,28 @@ func iff[T any](condition bool, trueValue, falseValue T) T {
|
|||||||
}
|
}
|
||||||
return falseValue
|
return falseValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConstrainBufferToAnchor(buf *ViewBuffer, anchor Anchor, width, height int) *ViewBuffer {
|
||||||
|
switch anchor {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
case AnchorTopLeft:
|
||||||
|
return buf.Sub(0, 0, width, height)
|
||||||
|
case AnchorTop:
|
||||||
|
return buf.Sub(buf.Width()/2-width/2, 0, width, height)
|
||||||
|
case AnchorTopRight:
|
||||||
|
return buf.Sub(buf.Width()-width, 0, width, height)
|
||||||
|
case AnchorLeft:
|
||||||
|
return buf.Sub(0, buf.Height()/2-height/2, width, height)
|
||||||
|
case AnchorCenter:
|
||||||
|
return buf.Sub(buf.Width()/2-width/2, buf.Height()/2-height/2, width, height)
|
||||||
|
case AnchorRight:
|
||||||
|
return buf.Sub(buf.Width()-width, buf.Height()/2-height/2, width, height)
|
||||||
|
case AnchorBottomLeft:
|
||||||
|
return buf.Sub(0, buf.Height()-height, width, height)
|
||||||
|
case AnchorBottom:
|
||||||
|
return buf.Sub(buf.Width()/2-width/2, buf.Height()-height, width, height)
|
||||||
|
case AnchorBottomRight:
|
||||||
|
return buf.Sub(buf.Width()-width, buf.Height()-height, width, height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
7
view.go
7
view.go
@ -10,7 +10,14 @@ type View interface {
|
|||||||
SetStyle(s Style)
|
SetStyle(s Style)
|
||||||
Style() Style
|
Style() Style
|
||||||
|
|
||||||
|
// Layout is usually called by the parent view to ask the view's preferred size.
|
||||||
|
// If the parent view does not care about its preferred size, it might not be called at all.
|
||||||
|
// Negative values indicate as much space as possible.
|
||||||
Layout() (prefWidth, prefHeight int)
|
Layout() (prefWidth, prefHeight int)
|
||||||
|
|
||||||
|
// Draw is called for each view when it should print itself onto the screen.
|
||||||
|
// The parent view has full control of the ViewBuffer size
|
||||||
|
// and may or may not use the values returned from Layout() to set the size.
|
||||||
Draw(buf *ViewBuffer)
|
Draw(buf *ViewBuffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import (
|
|||||||
"git.tordarus.net/Tordarus/tui"
|
"git.tordarus.net/Tordarus/tui"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BorderGroup ia a tui.Group which places its children in a linear layout
|
// BorderGroup ia a tui.Group which places its children onto a given tui.Side
|
||||||
type BorderGroup struct {
|
type BorderGroup struct {
|
||||||
tui.ViewTmpl
|
tui.ViewTmpl
|
||||||
views map[Slot]tui.View
|
views map[Slot]tui.View
|
||||||
|
@ -39,6 +39,15 @@ func (g *FlowGroup) InsertView(v tui.View, index int) {
|
|||||||
g.views = append(g.views[:index], append([]tui.View{v}, g.views[index:]...)...)
|
g.views = append(g.views[:index], append([]tui.View{v}, g.views[index:]...)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *FlowGroup) RemoveView(v tui.View) {
|
||||||
|
for index, view := range g.Views() {
|
||||||
|
if view == v {
|
||||||
|
g.views = append(g.views[:index], g.views[index:]...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (g *FlowGroup) Draw(buf *tui.ViewBuffer) {
|
func (g *FlowGroup) Draw(buf *tui.ViewBuffer) {
|
||||||
g.ViewTmpl.Draw(buf)
|
g.ViewTmpl.Draw(buf)
|
||||||
|
|
||||||
|
35
views/frameview.go
Normal file
35
views/frameview.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package views
|
||||||
|
|
||||||
|
import "git.tordarus.net/Tordarus/tui"
|
||||||
|
|
||||||
|
// FrameView is a tui.Wrapper which draws its view preferably with preferred size on its tui.Anchor point
|
||||||
|
type FrameView struct {
|
||||||
|
tui.WrapperTmpl
|
||||||
|
Anchor tui.Anchor
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ tui.View = &FrameView{}
|
||||||
|
|
||||||
|
func NewFrameView(view tui.View) *FrameView {
|
||||||
|
v := new(FrameView)
|
||||||
|
v.SetView(view)
|
||||||
|
v.Anchor = tui.AnchorCenter
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *FrameView) Draw(buf *tui.ViewBuffer) {
|
||||||
|
g.ViewTmpl.Draw(buf)
|
||||||
|
|
||||||
|
w, h := g.View().Layout()
|
||||||
|
w = iff(w >= 0, w, buf.Width())
|
||||||
|
h = iff(h >= 0, h, buf.Height())
|
||||||
|
g.View().Draw(tui.ConstrainBufferToAnchor(buf, g.Anchor, w, h))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *FrameView) Layout() (prefWidth, prefHeight int) {
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *FrameView) Style() tui.Style {
|
||||||
|
return v.ViewTmpl.Style()
|
||||||
|
}
|
@ -18,10 +18,10 @@ func NewMarginView(view tui.View) *MarginView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *MarginView) Draw(buf *tui.ViewBuffer) {
|
func (g *MarginView) Draw(buf *tui.ViewBuffer) {
|
||||||
x := g.Margin[tui.Left]
|
x := g.Margin[tui.SideLeft]
|
||||||
y := g.Margin[tui.Top]
|
y := g.Margin[tui.SideTop]
|
||||||
w := buf.Width() - x - g.Margin[tui.Right]
|
w := buf.Width() - x - g.Margin[tui.SideRight]
|
||||||
h := buf.Height() - y - g.Margin[tui.Bottom]
|
h := buf.Height() - y - g.Margin[tui.SideBottom]
|
||||||
|
|
||||||
g.ViewTmpl.Draw(buf)
|
g.ViewTmpl.Draw(buf)
|
||||||
g.View().Draw(buf.Sub(x, y, w, h))
|
g.View().Draw(buf.Sub(x, y, w, h))
|
||||||
@ -29,8 +29,8 @@ func (g *MarginView) Draw(buf *tui.ViewBuffer) {
|
|||||||
|
|
||||||
func (v *MarginView) Layout() (prefWidth, prefHeight int) {
|
func (v *MarginView) Layout() (prefWidth, prefHeight int) {
|
||||||
w, h := v.View().Layout()
|
w, h := v.View().Layout()
|
||||||
w = iff(w > 0, w+v.Margin[tui.Left]+v.Margin[tui.Right], w)
|
w = iff(w > 0, w+v.Margin[tui.SideLeft]+v.Margin[tui.SideRight], w)
|
||||||
h = iff(h > 0, h+v.Margin[tui.Top]+v.Margin[tui.Bottom], h)
|
h = iff(h > 0, h+v.Margin[tui.SideTop]+v.Margin[tui.SideBottom], h)
|
||||||
return w, h
|
return w, h
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,9 +40,9 @@ func (v *MarginView) Style() tui.Style {
|
|||||||
|
|
||||||
func (v *MarginView) SetMargin(top, right, bottom, left int) {
|
func (v *MarginView) SetMargin(top, right, bottom, left int) {
|
||||||
v.Margin = map[tui.Side]int{
|
v.Margin = map[tui.Side]int{
|
||||||
tui.Top: top,
|
tui.SideTop: top,
|
||||||
tui.Right: right,
|
tui.SideRight: right,
|
||||||
tui.Bottom: bottom,
|
tui.SideBottom: bottom,
|
||||||
tui.Left: left,
|
tui.SideLeft: left,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
111
views/separatorgroup.go
Normal file
111
views/separatorgroup.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.tordarus.net/Tordarus/tui"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SeperatorGroup ia a tui.Group which separates
|
||||||
|
type SeperatorGroup struct {
|
||||||
|
tui.ViewTmpl
|
||||||
|
views []tui.View
|
||||||
|
|
||||||
|
gravity map[tui.View]int
|
||||||
|
gravitySum int
|
||||||
|
|
||||||
|
Orientation tui.Orientation
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ tui.Group = &SeperatorGroup{}
|
||||||
|
|
||||||
|
func NewSeparatorGroup(orientation tui.Orientation) *SeperatorGroup {
|
||||||
|
return &SeperatorGroup{
|
||||||
|
views: make([]tui.View, 0),
|
||||||
|
gravity: map[tui.View]int{},
|
||||||
|
Orientation: orientation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) Views() []tui.View {
|
||||||
|
return g.views[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) AppendView(v tui.View, gravity int) {
|
||||||
|
g.views = append(g.views, v)
|
||||||
|
g.gravitySum += gravity
|
||||||
|
g.gravity[v] = gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) PrependView(v tui.View, gravity int) {
|
||||||
|
g.views = append([]tui.View{v}, g.views...)
|
||||||
|
g.gravitySum += gravity
|
||||||
|
g.gravity[v] = gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) InsertView(v tui.View, index int, gravity int) {
|
||||||
|
g.views = append(g.views[:index], append([]tui.View{v}, g.views[index:]...)...)
|
||||||
|
g.gravitySum += gravity
|
||||||
|
g.gravity[v] = gravity
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) SetGravity(v tui.View, gravity int) {
|
||||||
|
for _, view := range g.Views() {
|
||||||
|
if view == v {
|
||||||
|
g.gravitySum += gravity - g.gravity[v]
|
||||||
|
g.gravity[v] = gravity
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) RemoveView(v tui.View) {
|
||||||
|
for index, view := range g.Views() {
|
||||||
|
if view == v {
|
||||||
|
g.views = append(g.views[:index], g.views[index:]...)
|
||||||
|
g.gravitySum -= g.gravity[v]
|
||||||
|
delete(g.gravity, v)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) View(slot Slot) tui.View {
|
||||||
|
return g.views[slot]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) Draw(buf *tui.ViewBuffer) {
|
||||||
|
g.ViewTmpl.Draw(buf)
|
||||||
|
|
||||||
|
if g.Orientation == tui.Horizontal {
|
||||||
|
x := 0
|
||||||
|
for _, v := range g.Views() {
|
||||||
|
viewGravity := g.gravity[v]
|
||||||
|
percentage := float64(viewGravity) / float64(g.gravitySum)
|
||||||
|
width := int(percentage * float64(buf.Width()))
|
||||||
|
v.Draw(buf.Sub(x, 0, width, buf.Height()))
|
||||||
|
x += width
|
||||||
|
}
|
||||||
|
} else if g.Orientation == tui.Vertical {
|
||||||
|
y := 0
|
||||||
|
for _, v := range g.Views() {
|
||||||
|
viewGravity := g.gravity[v]
|
||||||
|
percentage := float64(viewGravity) / float64(g.gravitySum)
|
||||||
|
height := int(percentage * float64(buf.Height()))
|
||||||
|
v.Draw(buf.Sub(0, y, buf.Width(), height))
|
||||||
|
y += height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) Layout() (prefWidth, prefHeight int) {
|
||||||
|
return -1, -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *SeperatorGroup) OnKeyPressed(event *tui.KeyEvent) (consumed bool) {
|
||||||
|
for _, view := range g.Views() {
|
||||||
|
if view.OnKeyPressed(event) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -13,6 +13,7 @@ type TextView struct {
|
|||||||
var _ tui.View = &TextView{}
|
var _ tui.View = &TextView{}
|
||||||
|
|
||||||
func (v *TextView) Draw(buf *tui.ViewBuffer) {
|
func (v *TextView) Draw(buf *tui.ViewBuffer) {
|
||||||
|
v.ViewTmpl.Draw(buf)
|
||||||
tui.WriteMultiLineString(buf, v.Text, v.Style(), 0, 0)
|
tui.WriteMultiLineString(buf, v.Text, v.Style(), 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user