package tkimport ()// ColView is a Widget that arranges several widgets in a column.typeColViewinterface {Widget// MutateState mutates the state.MutateState(f func(*ColViewState))// CopyState returns a copy of the state.CopyState() ColViewState// Left triggers the OnLeft callback.Left()// Right triggers the OnRight callback.Right()}// ColViewSpec specifies the configuration and initial state for ColView.typeColViewSpecstruct {// Key bindings.BindingsBindings// A function that takes the number of columns and return weights for the // widths of the columns. The returned slice must have a size of n. If this // function is nil, all the columns will have the same weight.Weightsfunc(n int) []int// A function called when the Left method of Widget is called, or when Left // is pressed and unhandled.OnLeftfunc(w ColView)// A function called when the Right method of Widget is called, or when // Right is pressed and unhandled.OnRightfunc(w ColView)// State. Specifies the initial state when used in New.StateColViewState}// ColViewState keeps the mutable state of the ColView widget.typeColViewStatestruct {Columns []WidgetFocusColumnint}typecolViewstruct {// Mutex for synchronizing access to State.StateMutexsync.RWMutexColViewSpec}// NewColView creates a new ColView from the given spec.func ( ColViewSpec) ColView {if .Bindings == nil { .Bindings = DummyBindings{} }if .Weights == nil { .Weights = equalWeights }if .OnLeft == nil { .OnLeft = func(ColView) {} }if .OnRight == nil { .OnRight = func(ColView) {} }return &colView{ColViewSpec: }}func ( int) []int { := make([]int, )for := 0; < ; ++ { [] = 1 }return}func ( *colView) ( func(*ColViewState)) { .StateMutex.Lock()defer .StateMutex.Unlock() (&.State)}func ( *colView) () ColViewState { .StateMutex.RLock()defer .StateMutex.RUnlock() := .State .Columns = append([]Widget(nil), .State.Columns...)return}constcolViewColGap = 1// Render renders all the columns side by side, putting the dot in the focused// column.func ( *colView) (, int) *term.Buffer { := .CopyState() := len(.Columns)if == 0 {// No column.return &term.Buffer{Width: } }if < {// To narrow; give up by rendering nothing.return &term.Buffer{Width: } } := distribute(-(-1)*colViewColGap, .Weights())varterm.Bufferfor , := range .Columns {if > 0 { .Width += colViewColGap } := .Render([], ) .ExtendRight() }return &}// Handle handles the event first by consulting the overlay handler, and then// delegating the event to the currently focused column.func ( *colView) ( term.Event) bool {if .Bindings.Handle(, ) {returntrue } := .CopyState()if0 <= .FocusColumn && .FocusColumn < len(.Columns) {if .Columns[.FocusColumn].Handle() {returntrue } }switch {caseterm.K(ui.Left): .Left()returntruecaseterm.K(ui.Right): .Right()returntruedefault:returnfalse }}func ( *colView) () { .OnLeft()}func ( *colView) () { .OnRight()}// Distributes fullWidth according to the weights, rounding to integers.//// This works iteratively each step by taking the sum of all remaining weights,// and using floor(remainedWidth * currentWeight / remainedAllWeights) for the// current column.//// A simpler alternative is to simply use floor(fullWidth * currentWeight /// allWeights) at each step, and also giving the remainder to the last column.// However, this means that the last column gets all the rounding errors from// flooring, which can be big. The more sophisticated algorithm distributes the// rounding errors among all the remaining elements and can result in a much// better distribution, and as a special upside, does not need to handle the// last column as a special case.//// As an extreme example, consider the case of fullWidth = 9, weights = {1, 1,// 1, 1, 1} (five 1's). Using the simplistic algorithm, the widths are {1, 1, 1,// 1, 5}. Using the more complex algorithm, the widths are {1, 2, 2, 2, 2}.func ( int, []int) []int { := := 0for , := range { += } := make([]int, len())for , := range { [] = * / -= [] -= }return}
The pages are generated with Goldsv0.2.8-preview. (GOOS=darwin GOARCH=arm64)