Really figuring some things out

This commit is contained in:
2025-08-07 11:18:03 -05:00
parent 7c96fbb187
commit b476c74683
23 changed files with 737 additions and 249 deletions

View File

@@ -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...)
}
}