2023-04-24 11:41:38 +02:00
|
|
|
package layouts
|
2022-04-02 13:01:41 +02:00
|
|
|
|
|
|
|
import (
|
2023-04-24 11:55:04 +02:00
|
|
|
"git.milar.in/milarin/tui"
|
2022-04-02 13:01:41 +02:00
|
|
|
)
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
// FlowLayout ia a tui.Layout which places its children in a linear layout
|
|
|
|
type FlowLayout struct {
|
2022-04-02 13:01:41 +02:00
|
|
|
tui.ViewTmpl
|
|
|
|
views []tui.View
|
|
|
|
lastLayoutPhase *LayoutResult
|
|
|
|
|
2022-05-04 14:47:55 +02:00
|
|
|
viewDims map[tui.View]tui.Dimension
|
|
|
|
|
2022-04-02 13:01:41 +02:00
|
|
|
// Orientation defines in which direction the children will be placed
|
|
|
|
Orientation tui.Orientation
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
var _ tui.Layout = &FlowLayout{}
|
2022-04-02 13:01:41 +02:00
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func NewFlowLayout(orientation tui.Orientation) *FlowLayout {
|
|
|
|
return &FlowLayout{
|
2022-04-02 13:01:41 +02:00
|
|
|
views: make([]tui.View, 0),
|
2022-05-04 14:47:55 +02:00
|
|
|
viewDims: map[tui.View]tui.Dimension{},
|
2022-04-02 13:01:41 +02:00
|
|
|
Orientation: orientation,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) Views() []tui.View {
|
2022-04-03 17:31:46 +02:00
|
|
|
return g.views
|
2022-04-02 13:01:41 +02:00
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) AppendViews(v ...tui.View) {
|
2022-04-02 13:01:41 +02:00
|
|
|
g.views = append(g.views, v...)
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) PrependViews(v ...tui.View) {
|
2022-04-02 13:01:41 +02:00
|
|
|
g.views = append(v, g.views...)
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) InsertView(v tui.View, index int) {
|
2022-04-02 13:01:41 +02:00
|
|
|
g.views = append(g.views[:index], append([]tui.View{v}, g.views[index:]...)...)
|
|
|
|
}
|
|
|
|
|
2022-04-03 17:07:15 +02:00
|
|
|
func (g *FlowLayout) removeView(v tui.View) {
|
2022-04-02 15:09:52 +02:00
|
|
|
for index, view := range g.Views() {
|
2022-04-03 17:07:15 +02:00
|
|
|
if v == view {
|
2022-05-04 14:47:55 +02:00
|
|
|
delete(g.viewDims, view)
|
2022-04-03 17:07:15 +02:00
|
|
|
g.views = append(g.views[:index], g.views[index+1:]...)
|
2022-04-02 15:09:52 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-03 17:07:15 +02:00
|
|
|
func (g *FlowLayout) RemoveViews(v ...tui.View) {
|
2022-05-04 10:17:16 +02:00
|
|
|
views := append(make([]tui.View, 0, len(v)), v...)
|
2022-04-03 17:31:46 +02:00
|
|
|
for _, view := range views {
|
2022-04-03 17:07:15 +02:00
|
|
|
g.removeView(view)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) Draw(buf *tui.ViewBuffer) {
|
2022-04-02 13:01:41 +02:00
|
|
|
g.ViewTmpl.Draw(buf)
|
|
|
|
|
|
|
|
if g.lastLayoutPhase == nil {
|
|
|
|
g.Layout()
|
|
|
|
}
|
|
|
|
layout := g.lastLayoutPhase
|
|
|
|
|
|
|
|
if g.Orientation == tui.Horizontal {
|
|
|
|
remainingSpacePerView := buf.Width() - layout.Sum.Width
|
2022-04-03 16:29:01 +02:00
|
|
|
if remainingSpacePerView < 0 {
|
|
|
|
remainingSpacePerView = 0
|
|
|
|
}
|
2022-04-02 13:01:41 +02:00
|
|
|
if layout.HorizontalNegativeCount > 0 {
|
|
|
|
remainingSpacePerView /= layout.HorizontalNegativeCount
|
|
|
|
}
|
|
|
|
|
|
|
|
x := 0
|
|
|
|
for _, view := range g.views {
|
|
|
|
size := layout.Sizes[view]
|
|
|
|
|
|
|
|
size.Height = iff(size.Height < 0, buf.Height(), size.Height)
|
|
|
|
if size.Width < 0 {
|
|
|
|
size.Width = iff(layout.Sum.Width > buf.Width(), 0, remainingSpacePerView)
|
|
|
|
}
|
|
|
|
|
2022-05-04 14:47:55 +02:00
|
|
|
g.viewDims[view] = tui.D(x, 0, size.Width, size.Height)
|
2022-04-02 13:01:41 +02:00
|
|
|
view.Draw(buf.Sub(x, 0, size.Width, size.Height))
|
2022-04-03 16:29:01 +02:00
|
|
|
|
2022-04-02 13:01:41 +02:00
|
|
|
x += size.Width
|
2022-04-03 16:29:01 +02:00
|
|
|
if x >= buf.Width() {
|
|
|
|
break
|
|
|
|
}
|
2022-04-02 13:01:41 +02:00
|
|
|
}
|
|
|
|
} else if g.Orientation == tui.Vertical {
|
|
|
|
remainingSpacePerView := buf.Height() - layout.Sum.Height
|
2022-04-03 16:29:01 +02:00
|
|
|
if remainingSpacePerView < 0 {
|
|
|
|
remainingSpacePerView = 0
|
|
|
|
}
|
2022-04-02 13:01:41 +02:00
|
|
|
if layout.VerticalNegativeCount > 0 {
|
|
|
|
remainingSpacePerView /= layout.VerticalNegativeCount
|
|
|
|
}
|
|
|
|
|
|
|
|
y := 0
|
|
|
|
for _, view := range g.views {
|
|
|
|
size := layout.Sizes[view]
|
|
|
|
|
|
|
|
size.Width = iff(size.Width < 0, buf.Width(), size.Width)
|
|
|
|
if size.Height < 0 {
|
|
|
|
size.Height = iff(layout.Sum.Height > buf.Height(), 0, remainingSpacePerView)
|
|
|
|
}
|
|
|
|
|
2022-05-04 14:47:55 +02:00
|
|
|
g.viewDims[view] = tui.D(0, y, size.Width, size.Height)
|
2022-04-02 13:01:41 +02:00
|
|
|
view.Draw(buf.Sub(0, y, size.Width, size.Height))
|
2022-04-03 16:29:01 +02:00
|
|
|
|
2022-04-02 13:01:41 +02:00
|
|
|
y += size.Height
|
2022-04-03 16:29:01 +02:00
|
|
|
if y >= buf.Height() {
|
|
|
|
break
|
|
|
|
}
|
2022-04-02 13:01:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
g.lastLayoutPhase = nil
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) Layout() (prefWidth, prefHeight int) {
|
2022-04-02 13:01:41 +02:00
|
|
|
layout := CalculateLayoutResult(g.Views())
|
|
|
|
g.lastLayoutPhase = layout
|
|
|
|
|
|
|
|
if g.Orientation == tui.Horizontal {
|
|
|
|
prefWidth = iff(layout.HorizontalNegativeCount == 0, layout.Sum.Width, -1)
|
|
|
|
prefHeight = iff(layout.VerticalNegativeCount == 0, layout.Max.Height, -1)
|
|
|
|
} else if g.Orientation == tui.Vertical {
|
|
|
|
prefWidth = iff(layout.HorizontalNegativeCount == 0, layout.Max.Width, -1)
|
2022-04-03 16:29:01 +02:00
|
|
|
prefHeight = iff(layout.VerticalNegativeCount == 0, layout.Sum.Height, -1)
|
2022-04-02 13:01:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
layout.Pref = tui.Size{Width: prefWidth, Height: prefHeight}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-04-02 15:21:17 +02:00
|
|
|
func (g *FlowLayout) OnKeyPressed(event *tui.KeyEvent) (consumed bool) {
|
2022-04-02 13:01:41 +02:00
|
|
|
for _, view := range g.Views() {
|
|
|
|
if view.OnKeyPressed(event) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2022-05-04 12:03:51 +02:00
|
|
|
|
2022-05-04 14:47:55 +02:00
|
|
|
func (g *FlowLayout) OnMouseEvent(event *tui.MouseEvent) (consumed bool) {
|
|
|
|
for view, dim := range g.viewDims {
|
|
|
|
if event.Position.In(dim) {
|
|
|
|
view.OnMouseEvent(event)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|