Really figuring some things out
This commit is contained in:
257
linear_layout.go
257
linear_layout.go
@@ -22,7 +22,7 @@ THE SOFTWARE.
|
||||
package widgets
|
||||
|
||||
import (
|
||||
h "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||
wh "git.bullercodeworks.com/brian/tcell-widgets/helpers"
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
@@ -33,9 +33,12 @@ type LinearLayout struct {
|
||||
|
||||
orientation LinearLayoutOrient
|
||||
|
||||
x, y int
|
||||
w, h int
|
||||
widgets []Widget
|
||||
x, y int
|
||||
w, h int
|
||||
widgets []Widget
|
||||
layoutFlags map[Widget]LayoutFlag
|
||||
layoutWeights map[Widget]int
|
||||
totalWeight int
|
||||
|
||||
active bool
|
||||
visible bool
|
||||
@@ -43,6 +46,7 @@ type LinearLayout struct {
|
||||
disableTab bool
|
||||
|
||||
cursor int
|
||||
logger func(string, ...any)
|
||||
}
|
||||
|
||||
type LinearLayoutOrient int
|
||||
@@ -52,6 +56,8 @@ const (
|
||||
LinLayH
|
||||
)
|
||||
|
||||
var _ Widget = (*LinearLayout)(nil)
|
||||
|
||||
func NewLinearLayout(id string, s tcell.Style) *LinearLayout {
|
||||
ret := &LinearLayout{}
|
||||
ret.Init(id, s)
|
||||
@@ -62,11 +68,15 @@ func (w *LinearLayout) Init(id string, s tcell.Style) {
|
||||
w.id = id
|
||||
w.style = s
|
||||
w.visible = true
|
||||
w.layoutFlags = make(map[Widget]LayoutFlag)
|
||||
w.layoutWeights = make(map[Widget]int)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) Id() string { return w.id }
|
||||
func (w *LinearLayout) HandleResize(ev *tcell.EventResize) {
|
||||
w.w, w.h = ev.Size()
|
||||
w.w = wh.Min(w.w, w.WantW())
|
||||
w.h = wh.Min(w.h, w.WantH())
|
||||
w.updateWidgetLayouts()
|
||||
}
|
||||
|
||||
@@ -151,10 +161,11 @@ func (w *LinearLayout) SetSize(c Coord) { w.w, w.h = c.X, c.Y }
|
||||
func (w *LinearLayout) WantW() int {
|
||||
var wantW int
|
||||
for _, wd := range w.widgets {
|
||||
if w.orientation == LinLayV {
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
// Find the highest want of all widgets
|
||||
wantW = h.Max(wd.WantW(), wantW)
|
||||
} else if w.orientation == LinLayH {
|
||||
wantW = wh.Max(wd.WantW(), wantW)
|
||||
case LinLayH:
|
||||
// Find the sum of all widget widgets wants
|
||||
wantW = wantW + wd.WantW()
|
||||
}
|
||||
@@ -165,12 +176,13 @@ func (w *LinearLayout) WantW() int {
|
||||
func (w *LinearLayout) WantH() int {
|
||||
var wantH int
|
||||
for _, wd := range w.widgets {
|
||||
if w.orientation == LinLayV {
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
// Find the sum of all widget widgets wants
|
||||
wantH = wantH + wd.WantH()
|
||||
} else if w.orientation == LinLayH {
|
||||
case LinLayH:
|
||||
// Find the highest want of all widgets
|
||||
wantH = h.Max(wd.WantH(), wantH)
|
||||
wantH = wh.Max(wd.WantH(), wantH)
|
||||
}
|
||||
}
|
||||
return wantH
|
||||
@@ -179,10 +191,11 @@ func (w *LinearLayout) WantH() int {
|
||||
func (w *LinearLayout) MinW() int {
|
||||
var minW int
|
||||
for _, wd := range w.widgets {
|
||||
if w.orientation == LinLayV {
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
// Find the highest minimum width of all widgets
|
||||
minW = h.Max(wd.MinW(), minW)
|
||||
} else if w.orientation == LinLayH {
|
||||
minW = wh.Max(wd.MinW(), minW)
|
||||
case LinLayH:
|
||||
// Find the sum of all widget minimum widgets
|
||||
minW = minW + wd.MinW()
|
||||
}
|
||||
@@ -193,17 +206,67 @@ func (w *LinearLayout) MinW() int {
|
||||
func (w *LinearLayout) MinH() int {
|
||||
var minH int
|
||||
for _, wd := range w.widgets {
|
||||
if w.orientation == LinLayV {
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
minH = minH + wd.MinH()
|
||||
} else if w.orientation == LinLayH {
|
||||
minH = h.Max(wd.MinH(), minH)
|
||||
case LinLayH:
|
||||
minH = wh.Max(wd.MinH(), minH)
|
||||
}
|
||||
}
|
||||
return minH
|
||||
}
|
||||
|
||||
func (w *LinearLayout) SetOrientation(o LinearLayoutOrient) { w.orientation = o }
|
||||
func (w *LinearLayout) Add(n Widget) { w.widgets = append(w.widgets, n) }
|
||||
func (w *LinearLayout) IndexOf(n Widget) int {
|
||||
for i := range w.widgets {
|
||||
if w.widgets[i] == n {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (w *LinearLayout) Contains(n Widget) bool {
|
||||
return w.IndexOf(n) >= 0
|
||||
}
|
||||
|
||||
func (w *LinearLayout) Add(n Widget) {
|
||||
if w.Contains(n) {
|
||||
// If the widget is already in the layout, move it to the end
|
||||
pFlags, pWeight := w.layoutFlags[n], w.layoutWeights[n]
|
||||
w.Delete(n)
|
||||
w.layoutFlags[n], w.layoutWeights[n] = pFlags, pWeight
|
||||
}
|
||||
w.widgets = append(w.widgets, n)
|
||||
// If we don't already have a weight set, set it to 1
|
||||
if _, ok := w.layoutWeights[n]; !ok {
|
||||
w.layoutWeights[n] = 1
|
||||
}
|
||||
w.updateTotalWeight()
|
||||
}
|
||||
|
||||
func (w *LinearLayout) Insert(n Widget, idx int) {
|
||||
if idx >= len(w.widgets) {
|
||||
w.Add(n)
|
||||
return
|
||||
}
|
||||
if pos := w.IndexOf(n); pos >= 0 {
|
||||
if pos < idx {
|
||||
idx--
|
||||
}
|
||||
// Preserve the flags & weight
|
||||
pFlags, pWeight := w.layoutFlags[n], w.layoutWeights[n]
|
||||
w.Delete(n)
|
||||
w.layoutFlags[n], w.layoutWeights[n] = pFlags, pWeight
|
||||
}
|
||||
w.widgets = append(w.widgets[:idx], append([]Widget{n}, w.widgets[idx:]...)...)
|
||||
// If we don't already have a weight set, set it to 1
|
||||
if _, ok := w.layoutWeights[n]; !ok {
|
||||
w.layoutWeights[n] = 1
|
||||
}
|
||||
w.updateTotalWeight()
|
||||
}
|
||||
|
||||
func (w *LinearLayout) Delete(n Widget) {
|
||||
for i := 0; i < len(w.widgets); i++ {
|
||||
if w.widgets[i] == n {
|
||||
@@ -214,40 +277,59 @@ func (w *LinearLayout) Delete(n Widget) {
|
||||
}
|
||||
|
||||
func (w *LinearLayout) DeleteIndex(idx int) {
|
||||
w.widgets = append(w.widgets[:idx], w.widgets[idx+1:]...)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) Insert(n Widget, idx int) {
|
||||
w.widgets = append(w.widgets[:idx], append([]Widget{n}, w.widgets[idx:]...)...)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateWidgetLayouts() {
|
||||
for _, wd := range w.widgets {
|
||||
w.updateWidgetPos(wd)
|
||||
w.updateWidgetSize(wd)
|
||||
if idx < len(w.widgets) {
|
||||
p := w.widgets[idx]
|
||||
w.widgets = append(w.widgets[:idx], w.widgets[idx+1:]...)
|
||||
delete(w.layoutFlags, p)
|
||||
delete(w.layoutWeights, p)
|
||||
w.updateTotalWeight()
|
||||
}
|
||||
}
|
||||
|
||||
// The Layout should have a static Size set at this point that we can use
|
||||
// For now we're centering all views in the Layout (on the cross-axis)
|
||||
//
|
||||
// The position and size of each widget before this should be correct
|
||||
// Find the position and size of the widget before this one
|
||||
func (w *LinearLayout) updateWidgetPos(wd Widget) {
|
||||
prevP, prevS := 0, 0
|
||||
for _, wrk := range w.widgets {
|
||||
if w.orientation == LinLayV {
|
||||
if wrk == wd {
|
||||
wd.SetPos(Coord{X: w.w - (wd.GetW() / 2), Y: prevP + prevS + 1})
|
||||
return
|
||||
}
|
||||
prevP, prevS = wrk.GetY(), wrk.GetH()
|
||||
} else if w.orientation == LinLayH {
|
||||
if wrk == wd {
|
||||
wd.SetPos(Coord{X: prevP + prevS + 1, Y: w.h - (wd.GetH() / 2)})
|
||||
return
|
||||
}
|
||||
prevP, prevS = wrk.GetX(), wrk.GetW()
|
||||
func (w *LinearLayout) AddFlag(wd Widget, f LayoutFlag) {
|
||||
if f.IsAlignH() {
|
||||
w.layoutFlags[wd].ClearAlignH()
|
||||
} else if f.IsAlignV() {
|
||||
w.layoutFlags[wd].ClearAlignV()
|
||||
}
|
||||
w.layoutFlags[wd].Add(f)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) RemoveFlag(wd Widget, f LayoutFlag) {
|
||||
// Removing an alignment flag centers that direction
|
||||
if f.IsAlignH() {
|
||||
w.layoutFlags[wd].ClearAlignH()
|
||||
} else if f.IsAlignV() {
|
||||
w.layoutFlags[wd].ClearAlignV()
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) SetWeight(wd Widget, wt int) {
|
||||
if !w.Contains(wd) {
|
||||
return
|
||||
}
|
||||
w.layoutWeights[wd] = wt
|
||||
w.updateTotalWeight()
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateTotalWeight() {
|
||||
w.totalWeight = 0
|
||||
for _, v := range w.layoutWeights {
|
||||
w.totalWeight += v
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateWidgetLayouts() {
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
for _, wd := range w.widgets {
|
||||
w.updateVerticalWidgetSize(wd)
|
||||
w.updateVerticalWidgetPos(wd)
|
||||
}
|
||||
case LinLayH:
|
||||
for _, wd := range w.widgets {
|
||||
w.updateHorizontalWidgetSize(wd)
|
||||
w.updateHorizontalWidgetPos(wd)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,12 +337,64 @@ func (w *LinearLayout) updateWidgetPos(wd Widget) {
|
||||
// The Layout should have a static Size set at this point that we can use
|
||||
// For now we're centering all views in the Layout (on the cross-axis)
|
||||
//
|
||||
// The position of this widget should be correct
|
||||
func (w *LinearLayout) updateWidgetSize(wd Widget) {
|
||||
// TODO
|
||||
// We need to determine the allowed size of this widget so we can determine
|
||||
// it's position
|
||||
func (w *LinearLayout) updateVerticalWidgetSize(wd Widget) {
|
||||
wd.HandleResize((&Coord{X: w.w, Y: w.getWeightedH(wd)}).ResizeEvent())
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateHorizontalWidgetSize(wd Widget) {
|
||||
wd.HandleResize((&Coord{X: w.getWeightedW(wd), Y: w.h}).ResizeEvent())
|
||||
}
|
||||
|
||||
// The Layout should have a static Size set at this point that we can use
|
||||
// For now we're centering all views in the Layout (on the cross-axis)
|
||||
// TODO: Use LayoutFlags to determine alignment in each 'cell'
|
||||
//
|
||||
// The position and size of each widget before this should be correct
|
||||
// This widget should also know its size by now. We just need to
|
||||
// position it relative to the layout.
|
||||
func (w *LinearLayout) updateVerticalWidgetPos(wd Widget) {
|
||||
w.Log("Calculating Widget Pos: %s", wd.Id())
|
||||
c := Coord{}
|
||||
for i := range w.widgets {
|
||||
if w.widgets[i] == wd {
|
||||
break
|
||||
}
|
||||
c.Y += w.getWeightedH(w.widgets[i])
|
||||
}
|
||||
w.Log("Set %s Y = %d", wd.Id(), c.Y)
|
||||
c.X = (w.w / 2) - (wd.GetW() / 2)
|
||||
wd.SetPos(c)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateHorizontalWidgetPos(wd Widget) {
|
||||
c := Coord{}
|
||||
for i := range w.widgets {
|
||||
c.X += w.getWeightedH(w.widgets[i])
|
||||
}
|
||||
c.Y = (w.h / 2) - (wd.GetH() / 2)
|
||||
wd.SetPos(c)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) updateWidgetPos(wd Widget) {
|
||||
prevP, prevS := 0, 0
|
||||
for _, wrk := range w.widgets {
|
||||
c := Coord{}
|
||||
switch w.orientation {
|
||||
case LinLayV:
|
||||
c.X, c.Y = w.w-(wd.GetW()/2), prevP+prevS+1
|
||||
prevP, prevS = wrk.GetY(), wrk.GetH()
|
||||
case LinLayH:
|
||||
c.X, c.Y = prevP+prevS+1, w.h-(wd.GetH()/2)
|
||||
prevP, prevS = wrk.GetX(), wrk.GetW()
|
||||
}
|
||||
wd.SetPos(c)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getRelPos(wd Widget) Coord {
|
||||
_ = wd
|
||||
return Coord{}
|
||||
}
|
||||
|
||||
@@ -268,3 +402,24 @@ func (w *LinearLayout) getAbsPos(wd Widget) Coord {
|
||||
rel := w.getRelPos(wd)
|
||||
return rel.Add(Coord{X: w.x, Y: w.y})
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getWeightedH(wd Widget) int {
|
||||
if !w.Contains(wd) {
|
||||
return 0
|
||||
}
|
||||
return w.h * (w.layoutWeights[wd] * w.totalWeight)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) getWeightedW(wd Widget) int {
|
||||
if !w.Contains(wd) {
|
||||
return 0
|
||||
}
|
||||
return w.w * (w.layoutWeights[wd] * w.totalWeight)
|
||||
}
|
||||
|
||||
func (w *LinearLayout) SetLogger(l func(string, ...any)) { w.logger = l }
|
||||
func (w *LinearLayout) Log(txt string, args ...any) {
|
||||
if w.logger != nil {
|
||||
w.logger(txt, args...)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user