mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 15:45:38 +00:00
ef8171b971
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>
86 lines
2.0 KiB
Go
86 lines
2.0 KiB
Go
package widget
|
|
|
|
import (
|
|
"io"
|
|
|
|
"gioui.org/f32"
|
|
"gioui.org/gesture"
|
|
"gioui.org/io/event"
|
|
"gioui.org/io/pointer"
|
|
"gioui.org/io/transfer"
|
|
"gioui.org/layout"
|
|
"gioui.org/op"
|
|
"gioui.org/op/clip"
|
|
)
|
|
|
|
// Draggable makes a widget draggable.
|
|
type Draggable struct {
|
|
// Type contains the MIME type and matches transfer.SourceOp.
|
|
Type string
|
|
|
|
handle struct{}
|
|
drag gesture.Drag
|
|
click f32.Point
|
|
pos f32.Point
|
|
}
|
|
|
|
func (d *Draggable) Layout(gtx layout.Context, w, drag layout.Widget) layout.Dimensions {
|
|
if !gtx.Enabled() {
|
|
return w(gtx)
|
|
}
|
|
dims := w(gtx)
|
|
|
|
stack := clip.Rect{Max: dims.Size}.Push(gtx.Ops)
|
|
d.drag.Add(gtx.Ops)
|
|
event.InputOp(gtx.Ops, &d.handle)
|
|
stack.Pop()
|
|
|
|
if drag != nil && d.drag.Pressed() {
|
|
rec := op.Record(gtx.Ops)
|
|
op.Offset(d.pos.Round()).Add(gtx.Ops)
|
|
drag(gtx)
|
|
op.Defer(gtx.Ops, rec.Stop())
|
|
}
|
|
|
|
return dims
|
|
}
|
|
|
|
// Dragging returns whether d is being dragged.
|
|
func (d *Draggable) Dragging() bool {
|
|
return d.drag.Dragging()
|
|
}
|
|
|
|
// Update the draggable and returns the MIME type for which the Draggable was
|
|
// requested to offer data, if any
|
|
func (d *Draggable) Update(gtx layout.Context) (mime string, requested bool) {
|
|
pos := d.pos
|
|
for _, ev := range d.drag.Update(gtx.Metric, gtx.Source, gesture.Both) {
|
|
switch ev.Kind {
|
|
case pointer.Press:
|
|
d.click = ev.Position
|
|
pos = f32.Point{}
|
|
case pointer.Drag, pointer.Release:
|
|
pos = ev.Position.Sub(d.click)
|
|
}
|
|
}
|
|
d.pos = pos
|
|
|
|
for _, ev := range gtx.Events(&d.handle, transfer.SourceFilter{Type: d.Type}) {
|
|
if e, ok := ev.(transfer.RequestEvent); ok {
|
|
return e.Type, true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
// Offer the data ready for a drop. Must be called after being Requested.
|
|
// The mime must be one in the requested list.
|
|
func (d *Draggable) Offer(gtx layout.Context, mime string, data io.ReadCloser) {
|
|
gtx.Queue(transfer.OfferCmd{Tag: &d.handle, Type: mime, Data: data})
|
|
}
|
|
|
|
// Pos returns the drag position relative to its initial click position.
|
|
func (d *Draggable) Pos() f32.Point {
|
|
return d.pos
|
|
}
|