widget: add drag and drop support

This patch adds internal Drag and Drop support to app.Windows.

The new package io/transfer adds the ability to
define draggable and droppable targets, which
are leveraged by the new widget.Draggable type.

The API is generic and could handle future use
cases, such as external Drag and Drop.

Updates gio#153

Signed-off-by: Pierre Curto <pierre.curto@gmail.com>
This commit is contained in:
Pierre Curto
2021-11-27 10:22:10 +01:00
committed by Elias Naur
parent 2d75181b51
commit 03016f0c69
7 changed files with 776 additions and 23 deletions
+86
View File
@@ -0,0 +1,86 @@
package widget
import (
"image"
"testing"
"gioui.org/f32"
"gioui.org/io/pointer"
"gioui.org/io/router"
"gioui.org/io/transfer"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/op/clip"
)
func TestDraggable(t *testing.T) {
var r router.Router
gtx := layout.Context{
Constraints: layout.Exact(image.Pt(100, 100)),
Queue: &r,
Ops: new(op.Ops),
}
drag := &Draggable{
Type: "file",
}
defer pointer.PassOp{}.Push(gtx.Ops).Pop()
dims := drag.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
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)
stack.Pop()
r.Frame(gtx.Ops)
r.Queue(
pointer.Event{
Position: f32.Pt(10, 10),
Type: pointer.Press,
},
pointer.Event{
Position: f32.Pt(20, 10),
Type: pointer.Move,
},
pointer.Event{
Position: f32.Pt(20, 10),
Type: pointer.Release,
},
)
ofr := &offer{data: "hello"}
drag.Offer(gtx.Ops, "file", ofr)
r.Frame(gtx.Ops)
evs := r.Events(drag)
if len(evs) != 1 {
t.Fatalf("expected 1 event, got %d", len(evs))
}
ev := evs[0].(transfer.DataEvent)
ev.Open = nil
if got, want := ev.Type, "file"; got != want {
t.Errorf("expected %v; got %v", got, want)
}
if ofr.closed {
t.Error("offer closed prematurely")
}
r.Frame(gtx.Ops)
if !ofr.closed {
t.Error("offer was not closed")
}
}
// offer satisfies io.ReadCloser for use in data transfers.
type offer struct {
data string
closed bool
}
func (*offer) Read([]byte) (int, error) { return 0, nil }
func (o *offer) Close() error {
o.closed = true
return nil
}