1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
package layout
import (
"image"
"image/draw"
"github.com/faiface/gui"
)
// NewRows creates layout with nrows children arranged in rows.
// It returns a slice containing an Env for each row.
// The height of each row is determined by the draw calls received from that row.
func NewRows(env gui.Env, nrows uint) []gui.Env {
// Create event and draw channels for each row
eventss := make([]chan gui.Event, nrows) // to children
drawss := make([]chan func(draw.Image) image.Rectangle, nrows) // from children
var i uint
for i = 0; i < nrows; i++ {
eventss[i] = make(chan gui.Event)
drawss[i] = make(chan func(draw.Image) image.Rectangle)
}
go func() {
defer close(env.Draw())
defer closeAll(eventss)
resize := func(area image.Rectangle, rowHeights []uint) {
off := area.Min // vertical offset from parent area origin
var i uint
for i = 0; i < nrows; i++ {
eventss[i] <- gui.Resize{image.Rectangle{
off,
off.Add(image.Pt(area.Dx(), int(rowHeights[i])))}}
off.Y += int(rowHeights[i])
}
}
// Receive and send first Resize event
event := (<-env.Events()).(gui.Resize) // first event guaranteed to be Resize
area := event.Rectangle
rowHeights := make([]uint, nrows) // initially zero until draw call received
go resize(area, rowHeights) // send first Resize to children
// Multiplex rows' draw channels. Tag draw functions with row index.
draws := make(chan taggedDrawCall)
wg := newWaitGroup(nrows) // done when all rows close their Draw channel
var i uint
for i = 0; i < nrows; i++ {
go muxDrawCalls(drawss[i], i, draws, wg)
}
defer close(draws)
for {
select {
case event := <-env.Events(): // event from parent
switch event := event.(type) {
case gui.Resize:
area = event.Rectangle
go resize(area, rowHeights)
default:
go multicast(event, eventss) // forward event to all rows
}
case drw := <-draws: // draw call from a row
rh := rowHeight(area, drw.f)
oldrh := rowHeights[drw.idx]
rowHeights[drw.idx] = rh
if rh != oldrh { // size changed; redraw all rows
go resize(area, rowHeights)
} else { // Same size; just redraw the one row
env.Draw() <- drawSubImage(drw.f, rowArea(area, rowHeights, drw.idx))
}
case <-wg.Wait(): // all rows' draw channels closed
return
}
}
}()
// Create and return row Envs
rows := make([]gui.Env, nrows)
for i := range rows {
rows[i] = rowEnv{eventss[i], drawss[i]}
}
return rows
}
// RowHeight calculates the height of a row within the area of the layout
// using a draw function received from the row.
func rowHeight(area image.Rectangle, drw func(draw.Image) image.Rectangle) uint {
img := image.NewAlpha(area)
return uint(drw(img).Canon().Dy())
}
// RowArea returns the drawing area of row i within the area of the layout.
func rowArea(area image.Rectangle, rowHeights []uint, i uint) image.Rectangle {
min := area.Min.Add(image.Pt(0, int(sum(rowHeights[:i]))))
max := min.Add(image.Pt(area.Dx(), int(rowHeights[i])))
return image.Rectangle{min, max}
}
type rowEnv struct {
events <-chan gui.Event
draw chan<- func(draw.Image) image.Rectangle
}
// Events implements the Env interface.
func (r rowEnv) Events() <-chan gui.Event { return r.events }
// Draw implements the Env interface.
func (r rowEnv) Draw() chan<- func(draw.Image) image.Rectangle { return r.draw }
func closeAll[T any](cs []chan T) {
for _, c := range cs {
close(c)
}
}
|