clickable.go raw
1 package gel
2
3 import (
4 "image"
5 "time"
6
7 "github.com/p9c/gio/f32"
8 "github.com/p9c/gio/gesture"
9 "github.com/p9c/gio/io/key"
10 "github.com/p9c/gio/io/pointer"
11 l "github.com/p9c/gio/layout"
12 "github.com/p9c/gio/op"
13 )
14
15 type clickEvents struct {
16 Click, Cancel, Press func()
17 }
18
19 // Clickable represents a clickable area.
20 type Clickable struct {
21 *Window
22 click gesture.Click
23 clicks []click
24 // prevClicks is the index into clicks that marks the clicks from the most recent Fn call. prevClicks is used to
25 // keep clicks bounded.
26 prevClicks int
27 history []press
28 Events clickEvents
29 }
30
31 func (w *Window) Clickable() (c *Clickable) {
32 c = &Clickable{
33 Window: w,
34 click: gesture.Click{},
35 clicks: nil,
36 prevClicks: 0,
37 history: nil,
38 Events: clickEvents{
39 Click: func() {
40 D.Ln("click event")
41 },
42 Cancel: func() {
43 D.Ln("cancel event")
44 },
45 Press: func() {
46 D.Ln("press event")
47 },
48 },
49 }
50 return
51 }
52
53 func (c *Clickable) SetClick(fn func()) *Clickable {
54 c.Events.Click = fn
55 return c
56 }
57
58 func (c *Clickable) SetCancel(fn func()) *Clickable {
59 c.Events.Cancel = fn
60 return c
61 }
62
63 func (c *Clickable) SetPress(fn func()) *Clickable {
64 c.Events.Press = fn
65 return c
66 }
67
68 // click represents a click.
69 type click struct {
70 Modifiers key.Modifiers
71 NumClicks int
72 }
73
74 // press represents a past pointer press.
75 type press struct {
76 // Position of the press.
77 Position f32.Point
78 // Start is when the press began.
79 Start time.Time
80 // End is when the press was ended by a release or Cancel. A zero End means it hasn't ended yet.
81 End time.Time
82 // Cancelled is true for cancelled presses.
83 Cancelled bool
84 }
85
86 // Clicked reports whether there are pending clicks as would be reported by Clicks. If so, Clicked removes the earliest
87 // click.
88 func (c *Clickable) Clicked() bool {
89 if len(c.clicks) == 0 {
90 return false
91 }
92 n := copy(c.clicks, c.clicks[1:])
93 c.clicks = c.clicks[:n]
94 if c.prevClicks > 0 {
95 c.prevClicks--
96 }
97 return true
98 }
99
100 // Clicks returns and clear the clicks since the last call to Clicks.
101 func (c *Clickable) Clicks() []click {
102 clicks := c.clicks
103 c.clicks = nil
104 c.prevClicks = 0
105 return clicks
106 }
107
108 // History is the past pointer presses useful for drawing markers. History is retained for a short duration (about a
109 // second).
110 func (c *Clickable) History() []press {
111 return c.history
112 }
113
114 func (c *Clickable) Fn(gtx l.Context) l.Dimensions {
115 c.update(gtx)
116 stack := op.Save(gtx.Ops)
117 pointer.Rect(image.Rectangle{Max: gtx.Constraints.Min}).Add(gtx.Ops)
118 c.click.Add(gtx.Ops)
119 stack.Load()
120 for len(c.history) > 0 {
121 cc := c.history[0]
122 if cc.End.IsZero() || gtx.Now.Sub(cc.End) < 1*time.Second {
123 break
124 }
125 n := copy(c.history, c.history[1:])
126 c.history = c.history[:n]
127 }
128 return l.Dimensions{Size: gtx.Constraints.Min}
129 }
130
131 // update the button changeState by processing clickEvents.
132 func (c *Clickable) update(gtx l.Context) {
133 // Flush clicks from before the last update.
134 n := copy(c.clicks, c.clicks[c.prevClicks:])
135 c.clicks = c.clicks[:n]
136 c.prevClicks = n
137 for _, ev := range c.click.Events(gtx) {
138 switch ev.Type {
139 case gesture.TypeClick:
140 var clk click
141 clk = click{
142 Modifiers: ev.Modifiers,
143 NumClicks: ev.NumClicks,
144 }
145 c.clicks = append(c.clicks, clk)
146 if ll := len(c.history); ll > 0 {
147 c.history[ll-1].End = gtx.Now
148 }
149 c.Window.Runner <- func() (e error) { c.Events.Click(); return nil }
150 case gesture.TypeCancel:
151 for i := range c.history {
152 c.history[i].Cancelled = true
153 if c.history[i].End.IsZero() {
154 c.history[i].End = gtx.Now
155 }
156 }
157 c.Window.Runner <- func() (e error) { c.Events.Cancel(); return nil }
158 case gesture.TypePress:
159 c.history = append(c.history, press{
160 Position: ev.Position,
161 Start: gtx.Now,
162 })
163 c.
164 Window.
165 Runner <- func() (e error) {
166 c.
167 Events.
168 Press()
169 return nil
170 }
171 }
172 }
173 }
174