mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
io: [API] introduce event filters; convert pointer input to use them
Instead of having to supply the predicates for event filtering at the
time of layout, the new Filter type allows widgets to filter at the time
of calling Source.Events. There is then only the need for a single input
op type, in package event.
Filters most importantly allow the use of one tag for several event types,
and we can define that a widget w has &w as its primary tag, by convention.
This allows the replacement of per-widget Focus methods with direct uses
of FocusCmd{&w}, and the later addition of Source.Focused(&w) queries.
Note that the TestCursor test needed restructuring to avoid its use of
InputOps.
Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
+3
-5
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/gesture"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/transfer"
|
||||
"gioui.org/layout"
|
||||
@@ -31,10 +32,7 @@ func (d *Draggable) Layout(gtx layout.Context, w, drag layout.Widget) layout.Dim
|
||||
|
||||
stack := clip.Rect{Max: dims.Size}.Push(gtx.Ops)
|
||||
d.drag.Add(gtx.Ops)
|
||||
transfer.SourceOp{
|
||||
Tag: &d.handle,
|
||||
Type: d.Type,
|
||||
}.Add(gtx.Ops)
|
||||
event.InputOp(gtx.Ops, &d.handle)
|
||||
stack.Pop()
|
||||
|
||||
if drag != nil && d.drag.Pressed() {
|
||||
@@ -67,7 +65,7 @@ func (d *Draggable) Update(gtx layout.Context) (mime string, requested bool) {
|
||||
}
|
||||
d.pos = pos
|
||||
|
||||
for _, ev := range gtx.Source.Events(&d.handle) {
|
||||
for _, ev := range gtx.Events(&d.handle, transfer.SourceFilter{Type: d.Type}) {
|
||||
if e, ok := ev.(transfer.RequestEvent); ok {
|
||||
return e.Type, true
|
||||
}
|
||||
|
||||
+7
-5
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/input"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/transfer"
|
||||
@@ -29,12 +30,11 @@ func TestDraggable(t *testing.T) {
|
||||
return layout.Dimensions{Size: gtx.Constraints.Min}
|
||||
}, nil)
|
||||
stack := clip.Rect{Max: dims.Size}.Push(gtx.Ops)
|
||||
transfer.TargetOp{
|
||||
Tag: drag,
|
||||
Type: drag.Type,
|
||||
}.Add(gtx.Ops)
|
||||
event.InputOp(gtx.Ops, drag)
|
||||
stack.Pop()
|
||||
|
||||
drag.Update(gtx)
|
||||
r.Events(drag, transfer.TargetFilter{Type: drag.Type})
|
||||
r.Frame(gtx.Ops)
|
||||
r.Queue(
|
||||
pointer.Event{
|
||||
@@ -52,9 +52,11 @@ func TestDraggable(t *testing.T) {
|
||||
)
|
||||
ofr := &offer{data: "hello"}
|
||||
drag.Offer(gtx, "file", ofr)
|
||||
drag.Update(gtx)
|
||||
r.Events(drag, transfer.TargetFilter{Type: drag.Type})
|
||||
r.Frame(gtx.Ops)
|
||||
|
||||
evs := r.Events(drag)
|
||||
evs := r.Events(drag, transfer.TargetFilter{Type: drag.Type})
|
||||
if len(evs) != 2 {
|
||||
t.Fatalf("expected 2 event, got %d", len(evs))
|
||||
}
|
||||
|
||||
+15
-14
@@ -225,7 +225,19 @@ func (e *Editor) processPointer(gtx layout.Context) {
|
||||
axis = gesture.Vertical
|
||||
smin, smax = sbounds.Min.Y, sbounds.Max.Y
|
||||
}
|
||||
sdist := e.scroller.Update(gtx.Metric, gtx.Source, gtx.Now, axis)
|
||||
var scrollRange image.Rectangle
|
||||
textDims := e.text.FullDimensions()
|
||||
visibleDims := e.text.Dimensions()
|
||||
if e.SingleLine {
|
||||
scrollOffX := e.text.ScrollOff().X
|
||||
scrollRange.Min.X = min(-scrollOffX, 0)
|
||||
scrollRange.Max.X = max(0, textDims.Size.X-(scrollOffX+visibleDims.Size.X))
|
||||
} else {
|
||||
scrollOffY := e.text.ScrollOff().Y
|
||||
scrollRange.Min.Y = -scrollOffY
|
||||
scrollRange.Max.Y = max(0, textDims.Size.Y-(scrollOffY+visibleDims.Size.Y))
|
||||
}
|
||||
sdist := e.scroller.Update(gtx.Metric, gtx.Source, gtx.Now, axis, scrollRange)
|
||||
var soff int
|
||||
if e.SingleLine {
|
||||
e.text.ScrollRel(sdist, 0)
|
||||
@@ -320,7 +332,7 @@ func (e *Editor) processKey(gtx layout.Context) {
|
||||
}
|
||||
// adjust keeps track of runes dropped because of MaxLen.
|
||||
var adjust int
|
||||
for _, ke := range gtx.Events(&e.eventKey) {
|
||||
for _, ke := range gtx.Events(&e.eventKey, transfer.TargetFilter{Type: "application/text"}) {
|
||||
e.blinkStart = gtx.Now
|
||||
switch ke := ke.(type) {
|
||||
case key.FocusEvent:
|
||||
@@ -609,7 +621,6 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
||||
e.scrollCaret = false
|
||||
e.text.ScrollToCaret()
|
||||
}
|
||||
textDims := e.text.FullDimensions()
|
||||
visibleDims := e.text.Dimensions()
|
||||
|
||||
defer clip.Rect(image.Rectangle{Max: visibleDims.Size}).Push(gtx.Ops).Pop()
|
||||
@@ -642,17 +653,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
||||
}
|
||||
key.InputOp{Tag: &e.eventKey, Hint: e.InputHint, Keys: keys}.Add(gtx.Ops)
|
||||
|
||||
var scrollRange image.Rectangle
|
||||
if e.SingleLine {
|
||||
scrollOffX := e.text.ScrollOff().X
|
||||
scrollRange.Min.X = min(-scrollOffX, 0)
|
||||
scrollRange.Max.X = max(0, textDims.Size.X-(scrollOffX+visibleDims.Size.X))
|
||||
} else {
|
||||
scrollOffY := e.text.ScrollOff().Y
|
||||
scrollRange.Min.Y = -scrollOffY
|
||||
scrollRange.Max.Y = max(0, textDims.Size.Y-(scrollOffY+visibleDims.Size.Y))
|
||||
}
|
||||
e.scroller.Add(gtx.Ops, scrollRange)
|
||||
e.scroller.Add(gtx.Ops)
|
||||
|
||||
e.clicker.Add(gtx.Ops)
|
||||
e.dragger.Add(gtx.Ops)
|
||||
|
||||
@@ -900,7 +900,6 @@ g 2 4 6 8 g
|
||||
var tim time.Duration
|
||||
selected := func(start, end int) string {
|
||||
// Layout once with no events; populate e.lines.
|
||||
gtx = gtx.Disabled()
|
||||
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
|
||||
e.Events() // throw away any events from this layout
|
||||
|
||||
|
||||
+11
-15
@@ -5,8 +5,11 @@ package widget_test
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"gioui.org/f32"
|
||||
"gioui.org/io/event"
|
||||
"gioui.org/io/input"
|
||||
"gioui.org/io/pointer"
|
||||
"gioui.org/io/transfer"
|
||||
@@ -79,7 +82,7 @@ func ExampleDraggable_Layout() {
|
||||
}
|
||||
// mime is the type used to match drag and drop operations.
|
||||
// It could be left empty in this example.
|
||||
mime := "MyMime"
|
||||
const mime = "MyMime"
|
||||
drag := &widget.Draggable{Type: mime}
|
||||
var drop int
|
||||
// widget lays out the drag and drop handlers and processes
|
||||
@@ -94,7 +97,7 @@ func ExampleDraggable_Layout() {
|
||||
// drag must respond with an Offer event when requested.
|
||||
// Use the drag method for this.
|
||||
if m, ok := drag.Update(gtx); ok {
|
||||
drag.Offer(gtx, m, offer{Data: "hello world"})
|
||||
drag.Offer(gtx, m, io.NopCloser(strings.NewReader("hello world")))
|
||||
}
|
||||
|
||||
// Setup the area for drops.
|
||||
@@ -102,17 +105,17 @@ func ExampleDraggable_Layout() {
|
||||
Min: image.Pt(20, 20),
|
||||
Max: image.Pt(40, 40),
|
||||
}.Push(gtx.Ops)
|
||||
transfer.TargetOp{
|
||||
Tag: &drop,
|
||||
Type: mime, // this must match the drag Type for the drop to succeed
|
||||
}.Add(gtx.Ops)
|
||||
event.InputOp(gtx.Ops, &drop)
|
||||
ds.Pop()
|
||||
|
||||
// Check for the received data.
|
||||
for _, ev := range gtx.Events(&drop) {
|
||||
for _, ev := range gtx.Events(&drop, transfer.TargetFilter{Type: mime}) {
|
||||
switch e := ev.(type) {
|
||||
case transfer.DataEvent:
|
||||
data := e.Open()
|
||||
fmt.Println(data.(offer).Data)
|
||||
defer data.Close()
|
||||
content, _ := io.ReadAll(data)
|
||||
fmt.Println(string(content))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,10 +148,3 @@ func ExampleDraggable_Layout() {
|
||||
// Output:
|
||||
// hello world
|
||||
}
|
||||
|
||||
type offer struct {
|
||||
Data string
|
||||
}
|
||||
|
||||
func (offer) Read([]byte) (int, error) { return 0, nil }
|
||||
func (offer) Close() error { return nil }
|
||||
|
||||
Reference in New Issue
Block a user