From f186ddf99917d42d20be6193fd35f14d5dfa554e Mon Sep 17 00:00:00 2001 From: Qi Xiao Date: Mon, 30 Dec 2024 17:37:25 +0000 Subject: [PATCH] Add flex weight to BoxView --- pkg/etk/comps/hiernav.go | 2 +- pkg/etk/view.go | 80 +++++++++++++++++++-------------- pkg/etkedit/addon_completion.go | 2 +- pkg/etkedit/addon_histlist.go | 2 +- pkg/etkedit/addon_histwalk.go | 2 +- pkg/etkedit/addon_lastcmd.go | 2 +- pkg/etkedit/addon_location.go | 2 +- pkg/etkedit/addon_minibuf.go | 2 +- pkg/etkedit/addon_navigation.go | 2 +- pkg/etkedit/app.go | 6 +-- 10 files changed, 57 insertions(+), 45 deletions(-) diff --git a/pkg/etk/comps/hiernav.go b/pkg/etk/comps/hiernav.go index 28a1cf89c..de4783cc5 100644 --- a/pkg/etk/comps/hiernav.go +++ b/pkg/etk/comps/hiernav.go @@ -45,7 +45,7 @@ func HierNav(c etk.Context) (etk.View, etk.React) { } } - return etk.Box("parent* 1 [current*] 1 preview*", parent, currentView, preview), + return etk.Box("parent*1 1 [current*3] 1 preview*4", parent, currentView, preview), func(e term.Event) etk.Reaction { switch e { case term.K(ui.Left): diff --git a/pkg/etk/view.go b/pkg/etk/view.go index f0311246b..69215f559 100644 --- a/pkg/etk/view.go +++ b/pkg/etk/view.go @@ -140,32 +140,34 @@ type BoxView struct { type BoxChild struct { View View - Flex bool + Flex int // 0 for non-flex child, > 0 for flex weight } // Child verbs // - "=" for non-flex child -// - "*" for flex child +// - "*" for flex child, may be followed by weight (default is 1) // // Example: // -// a* b= c* - -// Expressing focus +// a* b= c*2 +// +// Names don't matter +// +// Expressing focus: +// +// a* [b=] c*2 +// +// Expressing gap: +// +// a* 2 [b=] 1 c*2 // -// a* [b=] c* - -// Expressing gap - -// a* 2 [b=] 1 c* - // Calling box // -// Box("a* [b=] c*", aView, bView, cView) +// Box("a* [b=] c*2", aView, bView, cView) // // Box(`a* // [b=] -// c*`, aView, bView, cView) +// c*2`, aView, bView, cView) func Box(layout string, views ...View) BoxView { return BoxFocus(layout, 0, views...) @@ -199,9 +201,17 @@ func BoxFocus(layout string, focus int, views ...View) BoxView { verb = verb[1 : len(verb)-1] focus = len(children) } - var flex bool - if strings.HasSuffix(verb, "*") { - flex = true + var flexWeight int + if _, weight, ok := strings.Cut(verb, "*"); ok { + if weight == "" { + flexWeight = 1 + } else { + var err error + flexWeight, err = strconv.Atoi(weight) + if err != nil { + panic(fmt.Sprintf("bad weight: %s", weight)) + } + } } else if strings.HasSuffix(verb, "=") { } else if n, err := strconv.Atoi(verb); err == nil { var view View @@ -210,12 +220,12 @@ func BoxFocus(layout string, focus int, views ...View) BoxView { } else { view = VerticalGapView{n} } - children = append(children, BoxChild{view, false}) + children = append(children, BoxChild{view, 0}) continue } else { panic(fmt.Sprintf("invalid verb (must be number or end in * or =): %q", verb)) } - children = append(children, BoxChild{views[j], flex}) + children = append(children, BoxChild{views[j], flexWeight}) j++ } if j != len(views) { @@ -225,19 +235,20 @@ func BoxFocus(layout string, focus int, views ...View) BoxView { } func (v BoxView) Render(width, height int) *term.Buffer { - var render func(View, bool) *term.Buffer - var flexChildren int + var render func(View, int) *term.Buffer + var flexWeightSum int if v.Horizontal { budget := width - render = func(v View, flex bool) *term.Buffer { + render = func(v View, flexWeight int) *term.Buffer { width := budget - if flex { - width = budget / flexChildren + if flexWeight > 0 { + width = budget * flexWeight / flexWeightSum + flexWeightSum -= flexWeight } buf := v.Render(width, height) // TODO: Maybe term.Buffer should keep track of the actual width // itself - if !flex { + if flexWeight == 0 { actualWidth := 0 for _, line := range buf.Lines { actualWidth = max(actualWidth, cellsWidth(line)) @@ -249,10 +260,11 @@ func (v BoxView) Render(width, height int) *term.Buffer { } } else { budget := height - render = func(v View, flex bool) *term.Buffer { + render = func(v View, flexWeight int) *term.Buffer { height := budget - if flex { - height = budget / flexChildren + if flexWeight > 0 { + height = budget * flexWeight / flexWeightSum + flexWeightSum -= flexWeight } buf := v.Render(width, height) budget -= len(buf.Lines) @@ -263,17 +275,17 @@ func (v BoxView) Render(width, height int) *term.Buffer { childBufs := make([]*term.Buffer, len(v.Children)) // Render non-flex children first. for i, child := range v.Children { - if child.Flex { - flexChildren++ + if child.Flex > 0 { + flexWeightSum += child.Flex } else { - childBufs[i] = render(child.View, false) + childBufs[i] = render(child.View, 0) } } - // Render flex children first. + // Render flex children now. for i, child := range v.Children { - if child.Flex { - childBufs[i] = render(child.View, true) - flexChildren-- + if child.Flex > 0 { + childBufs[i] = render(child.View, child.Flex) + flexWeightSum-- } } diff --git a/pkg/etkedit/addon_completion.go b/pkg/etkedit/addon_completion.go index b7f8ba427..c0c01507b 100644 --- a/pkg/etkedit/addon_completion.go +++ b/pkg/etkedit/addon_completion.go @@ -69,7 +69,7 @@ func startCompletion(ed *Editor, c etk.Context) { } return r }), - true, + 1, func() { pendingVar.Set(comps.PendingText{}) }, ) } diff --git a/pkg/etkedit/addon_histlist.go b/pkg/etkedit/addon_histlist.go index 0dffb2918..ec64594a7 100644 --- a/pkg/etkedit/addon_histlist.go +++ b/pkg/etkedit/addon_histlist.go @@ -35,7 +35,7 @@ func startHistlist(ed *Editor, c etk.Context) { // TODO: Implement insertion of selected item return r }, - ), true) + ), 1) } type histlistItems struct { diff --git a/pkg/etkedit/addon_histwalk.go b/pkg/etkedit/addon_histwalk.go index c1812514d..2e135008f 100644 --- a/pkg/etkedit/addon_histwalk.go +++ b/pkg/etkedit/addon_histwalk.go @@ -30,7 +30,7 @@ func startHistwalk(ed *Editor, c etk.Context) { // TODO: Handle up/down return r }), - true, + 1, func() { pendingVar.Set(comps.PendingText{}) }, ) } diff --git a/pkg/etkedit/addon_lastcmd.go b/pkg/etkedit/addon_lastcmd.go index 49f3d14e8..9cf4fa1e0 100644 --- a/pkg/etkedit/addon_lastcmd.go +++ b/pkg/etkedit/addon_lastcmd.go @@ -26,7 +26,7 @@ func startLastcmd(ed *Editor, c etk.Context) { return items, 0 }, "binding", etkBindingFromBindingMap(ed, &ed.lastcmdBinding), - ), true) + ), 1) } type lastcmdItems struct { diff --git a/pkg/etkedit/addon_location.go b/pkg/etkedit/addon_location.go index 0c1757e14..1792e12f9 100644 --- a/pkg/etkedit/addon_location.go +++ b/pkg/etkedit/addon_location.go @@ -23,7 +23,7 @@ func startLocation(ed *Editor, c etk.Context) { return locationItems{dirs}, 0 }, "binding", etkBindingFromBindingMap(ed, &ed.locationBinding), - ), true) + ), 1) } type locationItems struct { diff --git a/pkg/etkedit/addon_minibuf.go b/pkg/etkedit/addon_minibuf.go index 33219a892..1d3bd27a8 100644 --- a/pkg/etkedit/addon_minibuf.go +++ b/pkg/etkedit/addon_minibuf.go @@ -24,7 +24,7 @@ func startMinibuf(c etk.Context) { c.AddMsg(modes.ErrorText(err)) } }, - ), true) + ), 1) } // TODO: Is this the correct abstraction?? diff --git a/pkg/etkedit/addon_navigation.go b/pkg/etkedit/addon_navigation.go index 49959a2ca..3513b96f3 100644 --- a/pkg/etkedit/addon_navigation.go +++ b/pkg/etkedit/addon_navigation.go @@ -5,7 +5,7 @@ import "src.elv.sh/pkg/etk" func startNavigation(ed *Editor, c etk.Context) { pushAddon(c, etk.WithInit( navigation, - "binding", etkBindingFromBindingMap(ed, &ed.navigationBinding)), true) + "binding", etkBindingFromBindingMap(ed, &ed.navigationBinding)), 1) } func navigation(c etk.Context) (etk.View, etk.React) { diff --git a/pkg/etkedit/app.go b/pkg/etkedit/app.go index 4ae65e782..f67682b95 100644 --- a/pkg/etkedit/app.go +++ b/pkg/etkedit/app.go @@ -18,7 +18,7 @@ type addons struct { type addon struct { Comp etk.Comp - Flex bool + Flex int Dismiss func() ID int } @@ -77,11 +77,11 @@ func app(c etk.Context) (etk.View, etk.React) { } } -func pushAddon(c etk.Context, f etk.Comp, flex bool) { +func pushAddon(c etk.Context, f etk.Comp, flex int) { pushAddonWithDismiss(c, f, flex, nil) } -func pushAddonWithDismiss(c etk.Context, f etk.Comp, flex bool, dismiss func()) { +func pushAddonWithDismiss(c etk.Context, f etk.Comp, flex int, dismiss func()) { addonsVar := etk.BindState(c, "addons", addons{}) addonsVar.Swap(func(a addons) addons { // TODO: Is the use of append correct here??