aboutsummaryrefslogtreecommitdiffstats
path: root/scroller.go
blob: fe411f3ed1a9fd9db953aa62f4839eabf53f701e (plain) (blame)
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
116
117
118
119
120
121
122
123
124
125
126
127
package gui

import (
	"image"
	"image/color"
	"image/draw"

	"git.samanthony.xyz/share"
)

var _ Scheme = Scroller{}

type Scroller struct {
	Background  color.Color
	Length      int
	ChildHeight int
	Offset      int
	Gap         int
	Vertical    bool
}

func (s Scroller) redraw(drw draw.Image, bounds image.Rectangle) {
	col := s.Background
	if col == nil {
		col = image.Black
	}
	draw.Draw(drw, bounds, image.NewUniform(col), image.ZP, draw.Src)
}

func clamp(val, a, b int) int {
	if a > b {
		if val < b {
			return b
		}
		if val > a {
			return a
		}
	} else {
		if val > b {
			return b
		}
		if val < a {
			return a
		}
	}
	return val
}

func (s Scroller) Partition(bounds image.Rectangle) []image.Rectangle {
	items := s.Length
	ch := s.ChildHeight
	gap := s.Gap

	ret := make([]image.Rectangle, items)
	Y := bounds.Min.Y + s.Offset + gap
	for i := 0; i < items; i++ {
		r := image.Rect(bounds.Min.X+gap, Y, bounds.Max.X-gap, Y+ch)
		ret[i] = r
		Y += ch + gap
	}
	return ret
}

func (s Scroller) Intercept(parent Env) Env {
	lastResize := share.NewVal[image.Rectangle]()
	img := share.NewVal[draw.Image]()
	mouseOver := share.NewVal[bool]()

	lastResize.Set <- image.Rectangle{}
	img.Set <- image.NewRGBA(image.Rectangle{})
	mouseOver.Set <- false

	return newEnv(parent,
		func(event Event, events chan<- Event) {
			switch event := event.(type) {
			case MoMove:
				mouseOver.Set <- event.Point.In(lastResize.Get())
			case MoScroll:
				if !mouseOver.Get() {
					break
				}

				oldoff := s.Offset
				v := s.Length*s.ChildHeight + ((s.Length + 1) * s.Gap)
				bounds := lastResize.Get()

				if s.Vertical {
					h := bounds.Dx()
					s.Offset = clamp(s.Offset+event.Point.X*16, h-v, 0)
				} else {
					h := bounds.Dy()
					s.Offset = clamp(s.Offset+event.Point.Y*16, h-v, 0)
				}

				if oldoff != s.Offset {
					m := img.Get()
					s.redraw(m, m.Bounds())
					events <- Resize{bounds}
				}
			case Resize:
				lastResize.Set <- event.Rectangle

				m := image.NewRGBA(event.Rectangle)
				img.Set <- m
				s.redraw(m, m.Bounds())

				events <- event
			default:
				events <- event
			}
		},
		func(drawFunc func(draw.Image) image.Rectangle, drawChan chan<- func(draw.Image) image.Rectangle) {
			m := img.Get()
			if drawFunc(m).Intersect(m.Bounds()) != image.ZR {
				drawChan <- func(drw draw.Image) image.Rectangle {
					bounds := lastResize.Get()
					draw.Draw(drw, bounds, m, bounds.Min, draw.Over)
					return m.Bounds()
				}
			}
		},
		func() {
			lastResize.Close()
			img.Close()
			mouseOver.Close()
		})
}