gioui.org@v0.6.1-0.20240506124620-7a9ce51988ce/widget/dnd.go (about) 1 package widget 2 3 import ( 4 "io" 5 6 "gioui.org/f32" 7 "gioui.org/gesture" 8 "gioui.org/io/event" 9 "gioui.org/io/pointer" 10 "gioui.org/io/transfer" 11 "gioui.org/layout" 12 "gioui.org/op" 13 "gioui.org/op/clip" 14 ) 15 16 // Draggable makes a widget draggable. 17 type Draggable struct { 18 // Type contains the MIME type and matches transfer.SourceOp. 19 Type string 20 21 drag gesture.Drag 22 click f32.Point 23 pos f32.Point 24 } 25 26 func (d *Draggable) Layout(gtx layout.Context, w, drag layout.Widget) layout.Dimensions { 27 if !gtx.Enabled() { 28 return w(gtx) 29 } 30 dims := w(gtx) 31 32 stack := clip.Rect{Max: dims.Size}.Push(gtx.Ops) 33 d.drag.Add(gtx.Ops) 34 event.Op(gtx.Ops, d) 35 stack.Pop() 36 37 if drag != nil && d.drag.Pressed() { 38 rec := op.Record(gtx.Ops) 39 op.Offset(d.pos.Round()).Add(gtx.Ops) 40 drag(gtx) 41 op.Defer(gtx.Ops, rec.Stop()) 42 } 43 44 return dims 45 } 46 47 // Dragging returns whether d is being dragged. 48 func (d *Draggable) Dragging() bool { 49 return d.drag.Dragging() 50 } 51 52 // Update the draggable and returns the MIME type for which the Draggable was 53 // requested to offer data, if any 54 func (d *Draggable) Update(gtx layout.Context) (mime string, requested bool) { 55 pos := d.pos 56 for { 57 ev, ok := d.drag.Update(gtx.Metric, gtx.Source, gesture.Both) 58 if !ok { 59 break 60 } 61 switch ev.Kind { 62 case pointer.Press: 63 d.click = ev.Position 64 pos = f32.Point{} 65 case pointer.Drag, pointer.Release: 66 pos = ev.Position.Sub(d.click) 67 } 68 } 69 d.pos = pos 70 71 for { 72 e, ok := gtx.Event(transfer.SourceFilter{Target: d, Type: d.Type}) 73 if !ok { 74 break 75 } 76 if e, ok := e.(transfer.RequestEvent); ok { 77 return e.Type, true 78 } 79 } 80 return "", false 81 } 82 83 // Offer the data ready for a drop. Must be called after being Requested. 84 // The mime must be one in the requested list. 85 func (d *Draggable) Offer(gtx layout.Context, mime string, data io.ReadCloser) { 86 gtx.Execute(transfer.OfferCmd{Tag: d, Type: mime, Data: data}) 87 } 88 89 // Pos returns the drag position relative to its initial click position. 90 func (d *Draggable) Pos() f32.Point { 91 return d.pos 92 }