io/input: [API] introduce Source, the interface between a Router and widgets

This change gets rid of the event.Queue interface by replacing it with
input.Source values. Source provides the interface to Router necessary
to implement interface widgets.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2023-10-08 17:56:03 -05:00
parent c319f3c214
commit 6027517949
31 changed files with 106 additions and 127 deletions
+3 -3
View File
@@ -9,7 +9,7 @@ import (
"strings"
"time"
"gioui.org/io/event"
"gioui.org/io/input"
"gioui.org/layout"
"gioui.org/op"
"gioui.org/unit"
@@ -53,8 +53,8 @@ type FrameEvent struct {
// Frame completes the FrameEvent by drawing the graphical operations
// from ops into the window.
Frame func(frame *op.Ops)
// Source supplies the events for event handlers.
Source event.Queue
// Source is the interface between the interface state and widgets.
Source input.Source
}
// Insets is the space taken up by
+1 -1
View File
@@ -34,7 +34,7 @@ func FuzzIME(f *testing.F) {
e.Focus()
var r input.Router
gtx := layout.Context{Ops: new(op.Ops), Source: &r}
gtx := layout.Context{Ops: new(op.Ops), Source: r.Source()}
// Layout once to register focus.
e.Layout(gtx, cache, font.Font{}, unit.Sp(10), op.CallOp{}, op.CallOp{})
r.Frame(gtx.Ops)
+1 -1
View File
@@ -832,7 +832,7 @@ func (w *Window) processEvent(d driver, e event.Event) bool {
w.metric = e2.Metric
w.hasNextFrame = false
e2.Frame = w.update
e2.Source = &w.queue
e2.Source = w.queue.Source()
// Prepare the decorations and update the frame insets.
wrapper := &w.decorations.Ops
+5 -5
View File
@@ -17,7 +17,7 @@ import (
"gioui.org/f32"
"gioui.org/internal/fling"
"gioui.org/io/event"
"gioui.org/io/input"
"gioui.org/io/key"
"gioui.org/io/pointer"
"gioui.org/op"
@@ -44,7 +44,7 @@ func (h *Hover) Add(ops *op.Ops) {
}
// Update state and report whether a pointer is inside the area.
func (h *Hover) Update(q event.Queue) bool {
func (h *Hover) Update(q input.Source) bool {
for _, ev := range q.Events(h) {
e, ok := ev.(pointer.Event)
if !ok {
@@ -178,7 +178,7 @@ func (c *Click) Pressed() bool {
}
// Update state and return the click events.
func (c *Click) Update(q event.Queue) []ClickEvent {
func (c *Click) Update(q input.Source) []ClickEvent {
var events []ClickEvent
for _, evt := range q.Events(c) {
e, ok := evt.(pointer.Event)
@@ -269,7 +269,7 @@ func (s *Scroll) Stop() {
}
// Update state and report the scroll distance along axis.
func (s *Scroll) Update(cfg unit.Metric, q event.Queue, t time.Time, axis Axis) int {
func (s *Scroll) Update(cfg unit.Metric, q input.Source, t time.Time, axis Axis) int {
if s.axis != axis {
s.axis = axis
return 0
@@ -372,7 +372,7 @@ func (d *Drag) Add(ops *op.Ops) {
}
// Update state and return the drag events.
func (d *Drag) Update(cfg unit.Metric, q event.Queue, axis Axis) []pointer.Event {
func (d *Drag) Update(cfg unit.Metric, q input.Source, axis Axis) []pointer.Event {
var events []pointer.Event
for _, e := range q.Events(d) {
e, ok := e.(pointer.Event)
+3 -3
View File
@@ -28,14 +28,14 @@ func TestHover(t *testing.T) {
r.Queue(
pointer.Event{Kind: pointer.Move, Position: f32.Pt(30, 30)},
)
if !h.Update(r) {
if !h.Update(r.Source()) {
t.Fatal("expected hovered")
}
r.Queue(
pointer.Event{Kind: pointer.Move, Position: f32.Pt(50, 50)},
)
if h.Update(r) {
if h.Update(r.Source()) {
t.Fatal("expected not hovered")
}
}
@@ -75,7 +75,7 @@ func TestMouseClicks(t *testing.T) {
r.Frame(&ops)
r.Queue(tc.events...)
events := click.Update(&r)
events := click.Update(r.Source())
clicks := filterMouseClicks(events)
if got, want := len(clicks), len(tc.clicks); got != want {
t.Fatalf("got %d mouse clicks, expected %d", got, want)
+1 -35
View File
@@ -1,42 +1,8 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package event contains the types for event handling.
The Queue interface is the protocol for receiving external events.
For example:
var queue event.Queue = ...
for _, e := range queue.Events(h) {
switch e.(type) {
...
}
}
In general, handlers must be declared before events become
available. Other packages such as pointer and key provide
the means for declaring handlers for specific event types.
The following example declares a handler ready for key input:
import gioui.org/io/key
ops := new(op.Ops)
var h *Handler = ...
key.InputOp{Tag: h, Filter: ...}.Add(ops)
*/
// Package event contains types for event handling.
package event
// Queue maps an event handler key to the events
// available to the handler.
type Queue interface {
// Events returns the available events for an
// event handler tag.
Events(t Tag) []Event
}
// Tag is the stable identifier for an event handler.
// For a handler h, the tag is typically &h.
type Tag interface{}
+15
View File
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package input implements input routing and tracking of interface
state for a window.
The [Source] is the interface between the window and the widgets
of a user interface and is exposed by [gioui.org/app.FrameEvent]
received from windows.
The [Router] is used by [gioui.org/app.Window] to track window state and route
events from the platform to event handlers. It is otherwise only
useful for using Gio with external window implementations.
*/
package input
+27 -11
View File
@@ -1,13 +1,5 @@
// SPDX-License-Identifier: Unlicense OR MIT
/*
Package input implements Router, an event.Queue implementation
that disambiguates and routes events to handlers declared
in operation lists.
Router is used by app.Window and is otherwise only useful for
using Gio with external window implementations.
*/
package input
import (
@@ -31,8 +23,8 @@ import (
"gioui.org/op"
)
// Router is a Queue implementation that routes events
// to handlers declared in operation lists.
// Router tracks the [io/event.Tag] identifiers of user interface widgets
// and routes events to them. [Source] is its interface exposed to widgets.
type Router struct {
savedTrans []f32.Affine2D
transStack []f32.Affine2D
@@ -55,6 +47,12 @@ type Router struct {
wakeupTime time.Time
}
// Source implements the interface between a Router and user interface widgets.
// The value Source is disabled.
type Source struct {
r *Router
}
// SemanticNode represents a node in the tree describing the components
// contained in a frame.
type SemanticNode struct {
@@ -95,7 +93,25 @@ type handlerEvents struct {
hadEvents bool
}
// Events returns the available events for the handler key.
// Source returns a Source backed by this Router.
func (q *Router) Source() Source {
return Source{r: q}
}
// Enabled reports whether the source is enabled. Only enabled
// Sources deliver events and respond to commands.
func (s Source) Enabled() bool {
return s.r != nil
}
// Events returns the available events for the handler tag.
func (s Source) Events(k event.Tag) []event.Event {
if !s.Enabled() {
return nil
}
return s.r.Events(k)
}
func (q *Router) Events(k event.Tag) []event.Event {
events := q.handlers.Events(k)
return events
+5 -19
View File
@@ -5,7 +5,7 @@ package layout
import (
"time"
"gioui.org/io/event"
"gioui.org/io/input"
"gioui.org/io/system"
"gioui.org/op"
"gioui.org/unit"
@@ -20,9 +20,6 @@ type Context struct {
Constraints Constraints
Metric unit.Metric
// By convention, a nil Source is a signal to widgets to draw themselves
// in a disabled state.
Source event.Queue
// Now is the animation time.
Now time.Time
@@ -31,6 +28,7 @@ type Context struct {
// Interested users must look up and populate these values manually.
Locale system.Locale
input.Source
*op.Ops
}
@@ -44,21 +42,9 @@ func (c Context) Sp(v unit.Sp) int {
return c.Metric.Sp(v)
}
// Events returns the events available for the key. If no
// queue is configured, Events returns nil.
func (c Context) Events(k event.Tag) []event.Event {
if c.Source == nil {
return nil
}
return c.Source.Events(k)
}
// Disabled returns a copy of this context with a nil Queue,
// blocking events to widgets using it.
//
// By convention, a nil Queue is a signal to widgets to draw themselves
// in a disabled state.
// Disabled returns a copy of this context with a disabled Source,
// blocking widgets from changing its state and receiving events.
func (c Context) Disabled() Context {
c.Source = nil
c.Source = input.Source{}
return c
}
+1 -1
View File
@@ -144,7 +144,7 @@ func (l *List) Dragging() bool {
}
func (l *List) update(gtx Context) {
d := l.scroll.Update(gtx.Metric, gtx, gtx.Now, gesture.Axis(l.Axis))
d := l.scroll.Update(gtx.Metric, gtx.Source, gtx.Now, gesture.Axis(l.Axis))
l.scrollDelta = d
l.Position.Offset += d
}
+1 -1
View File
@@ -70,7 +70,7 @@ func TestListPosition(t *testing.T) {
Constraints: Constraints{
Max: image.Pt(20, 10),
},
Source: r,
Source: r.Source(),
}
el := func(gtx Context, idx int) Dimensions {
return Dimensions{Size: image.Pt(10, 10)}
+1 -1
View File
@@ -46,7 +46,7 @@ func (b *Bool) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
b.Update(gtx)
dims := b.clk.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.SelectedOp(b.Value).Add(gtx.Ops)
semantic.EnabledOp(gtx.Source != nil).Add(gtx.Ops)
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
return w(gtx)
})
return dims
+4 -5
View File
@@ -101,10 +101,9 @@ func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimension
dims := w(gtx)
c := m.Stop()
defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop()
enabled := gtx.Source != nil
semantic.EnabledOp(enabled).Add(gtx.Ops)
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
b.click.Add(gtx.Ops)
if enabled {
if gtx.Enabled() {
keys := key.Set("⏎|Space")
if !b.focused {
keys = ""
@@ -119,7 +118,7 @@ func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimension
// clicks, if any.
func (b *Clickable) Update(gtx layout.Context) []Click {
b.clicks = nil
if gtx.Source == nil {
if !gtx.Enabled() {
b.focused = false
}
if b.requestFocus {
@@ -141,7 +140,7 @@ func (b *Clickable) Update(gtx layout.Context) []Click {
NumClicks: c,
})
}
for _, e := range b.click.Update(gtx) {
for _, e := range b.click.Update(gtx.Source) {
switch e.Kind {
case gesture.KindClick:
if l := len(b.history); l > 0 {
+1 -1
View File
@@ -21,7 +21,7 @@ func TestClickable(t *testing.T) {
b1 widget.Clickable
b2 widget.Clickable
)
gtx := app.NewContext(&ops, app.FrameEvent{Source: &r})
gtx := app.NewContext(&ops, app.FrameEvent{Source: r.Source()})
layout := func() {
b1.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
return layout.Dimensions{Size: image.Pt(100, 100)}
+1 -1
View File
@@ -24,7 +24,7 @@ type Draggable struct {
}
func (d *Draggable) Layout(gtx layout.Context, w, drag layout.Widget) layout.Dimensions {
if gtx.Source == nil {
if !gtx.Enabled() {
return w(gtx)
}
dims := w(gtx)
+1 -1
View File
@@ -17,7 +17,7 @@ func TestDraggable(t *testing.T) {
var r input.Router
gtx := layout.Context{
Constraints: layout.Exact(image.Pt(100, 100)),
Source: &r,
Source: r.Source(),
Ops: new(op.Ops),
}
+4 -6
View File
@@ -225,7 +225,7 @@ 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, gtx.Now, axis)
sdist := e.scroller.Update(gtx.Metric, gtx.Source, gtx.Now, axis)
var soff int
if e.SingleLine {
e.text.ScrollRel(sdist, 0)
@@ -305,10 +305,10 @@ func (e *Editor) processPointer(gtx layout.Context) {
func (e *Editor) clickDragEvents(gtx layout.Context) []event.Event {
var combinedEvents []event.Event
for _, evt := range e.clicker.Update(gtx) {
for _, evt := range e.clicker.Update(gtx.Source) {
combinedEvents = append(combinedEvents, evt)
}
for _, evt := range e.dragger.Update(gtx.Metric, gtx, gesture.Both) {
for _, evt := range e.dragger.Update(gtx.Metric, gtx.Source, gesture.Both) {
combinedEvents = append(combinedEvents, evt)
}
return combinedEvents
@@ -677,14 +677,12 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
}
e.showCaret = e.focused && (!blinking || dt%timePerBlink < timePerBlink/2)
}
disabled := gtx.Source == nil
semantic.Editor.Add(gtx.Ops)
if e.Len() > 0 {
e.paintSelection(gtx, selectMaterial)
e.paintText(gtx, textMaterial)
}
if !disabled {
if gtx.Enabled() {
e.paintCaret(gtx, textMaterial)
}
return visibleDims
+10 -10
View File
@@ -103,7 +103,7 @@ func TestEditorReadOnly(t *testing.T) {
Max: image.Pt(100, 100),
},
Locale: english,
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
fontSize := unit.Sp(10)
@@ -505,7 +505,7 @@ func TestEditorDimensions(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Constraints{Max: image.Pt(100, 100)},
Source: r,
Source: r.Source(),
Locale: english,
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
@@ -891,7 +891,7 @@ g 2 4 6 8 g
gtx := layout.Context{
Ops: new(op.Ops),
Locale: english,
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
font := font.Font{}
@@ -900,13 +900,13 @@ 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.Source = nil
gtx = gtx.Disabled()
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
e.Events() // throw away any events from this layout
e.Focus()
r.Frame(gtx.Ops)
gtx.Source = r
gtx.Source = r.Source()
// Build the selection events
startPos := e.text.closestToRune(start)
endPos := e.text.closestToRune(end)
@@ -972,7 +972,7 @@ g 2 4 6 8 g
// Constrain the editor to roughly 6 columns wide and redraw
gtx.Constraints = layout.Exact(image.Pt(36, 36))
// Keep existing selection
gtx.Source = nil
gtx = gtx.Disabled()
e.Layout(gtx, cache, font, fontSize, op.CallOp{}, op.CallOp{})
caretStart := e.text.closestToRune(e.text.caret.start)
@@ -991,7 +991,7 @@ func TestSelectMove(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Locale: english,
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
font := font.Font{}
@@ -1080,7 +1080,7 @@ func TestEditor_MaxLen(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
fontSize := unit.Sp(10)
@@ -1116,7 +1116,7 @@ func TestEditor_Filter(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
fontSize := unit.Sp(10)
@@ -1146,7 +1146,7 @@ func TestEditor_Submit(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
fontSize := unit.Sp(10)
+4 -5
View File
@@ -40,13 +40,13 @@ func (e *Enum) index(k string) *enumKey {
// Update the state and report whether Value has changed by user interaction.
func (e *Enum) Update(gtx layout.Context) bool {
if gtx.Source == nil {
if !gtx.Enabled() {
e.focused = false
}
e.hovering = false
changed := false
for _, state := range e.keys {
for _, ev := range state.click.Update(gtx) {
for _, ev := range state.click.Update(gtx.Source) {
switch ev.Kind {
case gesture.KindPress:
if ev.Source == pointer.Mouse {
@@ -117,12 +117,11 @@ func (e *Enum) Layout(gtx layout.Context, k string, content layout.Widget) layou
}
clk := &state.click
clk.Add(gtx.Ops)
enabled := gtx.Source != nil
if enabled {
if gtx.Enabled() {
key.InputOp{Tag: &state.tag, Keys: "⏎|Space"}.Add(gtx.Ops)
}
semantic.SelectedOp(k == e.Value).Add(gtx.Ops)
semantic.EnabledOp(enabled).Add(gtx.Ops)
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
c.Add(gtx.Ops)
return dims
+2 -2
View File
@@ -25,7 +25,7 @@ func ExampleClickable_passthrough() {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
Source: &r,
Source: r.Source(),
}
// widget lays out two buttons on top of each other.
@@ -75,7 +75,7 @@ func ExampleDraggable_Layout() {
gtx := layout.Context{
Ops: new(op.Ops),
Constraints: layout.Exact(image.Pt(100, 100)),
Source: &r,
Source: r.Source(),
}
// mime is the type used to match drag and drop operations.
// It could be left empty in this example.
+1 -1
View File
@@ -48,7 +48,7 @@ func (f *Float) Layout(gtx layout.Context, axis layout.Axis, pointerMargin unit.
// The range of f is set by the minimum constraints main axis value.
func (f *Float) Update(gtx layout.Context) bool {
changed := false
for _, e := range f.drag.Update(gtx.Metric, gtx, gesture.Axis(f.axis)) {
for _, e := range f.drag.Update(gtx.Metric, gtx.Source, gesture.Axis(f.axis)) {
if f.length > 0 && (e.Kind == pointer.Press || e.Kind == pointer.Drag) {
pos := e.Position.X
if f.axis == layout.Vertical {
+3 -3
View File
@@ -61,7 +61,7 @@ func (s *Scrollbar) Update(gtx layout.Context, axis layout.Axis, viewportStart,
}
// Jump to a click in the track.
for _, event := range s.track.Update(gtx) {
for _, event := range s.track.Update(gtx.Source) {
if event.Kind != gesture.KindClick ||
event.Modifiers != key.Modifiers(0) ||
event.NumClicks > 1 {
@@ -80,7 +80,7 @@ func (s *Scrollbar) Update(gtx layout.Context, axis layout.Axis, viewportStart,
}
// Offset to account for any drags.
for _, event := range s.drag.Update(gtx.Metric, gtx, gesture.Axis(axis)) {
for _, event := range s.drag.Update(gtx.Metric, gtx.Source, gesture.Axis(axis)) {
switch event.Kind {
case pointer.Drag:
case pointer.Release, pointer.Cancel:
@@ -136,7 +136,7 @@ func (s *Scrollbar) Update(gtx layout.Context, axis layout.Axis, viewportStart,
// Process events from the indicator so that hover is
// detected properly.
_ = s.indicator.Update(gtx)
_ = s.indicator.Update(gtx.Source)
}
// AddTrack configures the track click listener for the scrollbar to use
+2 -2
View File
@@ -133,7 +133,7 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Di
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
background := b.Background
switch {
case gtx.Source == nil:
case !gtx.Enabled():
background = f32color.Disabled(b.Background)
case b.Button.Hovered() || b.Button.Focused():
background = f32color.Hovered(b.Background)
@@ -165,7 +165,7 @@ func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
defer clip.UniformRRect(image.Rectangle{Max: gtx.Constraints.Min}, rr).Push(gtx.Ops).Pop()
background := b.Background
switch {
case gtx.Source == nil:
case !gtx.Enabled():
background = f32color.Disabled(b.Background)
case b.Button.Hovered() || b.Button.Focused():
background = f32color.Hovered(b.Background)
+1 -1
View File
@@ -60,7 +60,7 @@ func (c *checkable) layout(gtx layout.Context, checked, hovered bool) layout.Dim
return layout.UniformInset(2).Layout(gtx, func(gtx layout.Context) layout.Dimensions {
size := gtx.Dp(c.Size)
col := c.IconColor
if gtx.Source == nil {
if !gtx.Enabled() {
col = f32color.Disabled(col)
}
gtx.Constraints.Min = image.Point{X: size}
+1 -1
View File
@@ -61,7 +61,7 @@ func (e EditorStyle) Layout(gtx layout.Context) layout.Dimensions {
paint.ColorOp{Color: e.HintColor}.Add(gtx.Ops)
hintColor := hintColorMacro.Stop()
selectionColorMacro := op.Record(gtx.Ops)
paint.ColorOp{Color: blendDisabledColor(gtx.Source == nil, e.SelectionColor)}.Add(gtx.Ops)
paint.ColorOp{Color: blendDisabledColor(!gtx.Enabled(), e.SelectionColor)}.Add(gtx.Ops)
selectionColor := selectionColorMacro.Stop()
var maxlines int
+1 -1
View File
@@ -51,7 +51,7 @@ func (p ProgressBarStyle) Layout(gtx layout.Context) layout.Dimensions {
layout.Stacked(func(gtx layout.Context) layout.Dimensions {
fillWidth := int(float32(progressBarWidth) * clamp1(p.Progress))
fillColor := p.Color
if gtx.Source == nil {
if !gtx.Enabled() {
fillColor = f32color.Disabled(fillColor)
}
return shader(fillWidth, fillColor)
+1 -1
View File
@@ -56,7 +56,7 @@ func (s SliderStyle) Layout(gtx layout.Context) layout.Dimensions {
trans.Pop()
color := s.Color
if gtx.Source == nil {
if !gtx.Enabled() {
color = f32color.Disabled(color)
}
+1 -1
View File
@@ -55,7 +55,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
if s.Switch.Value {
col = s.Color.Enabled
}
if gtx.Source == nil {
if !gtx.Enabled() {
col = f32color.Disabled(col)
}
trackColor := s.Color.Track
+2 -2
View File
@@ -298,10 +298,10 @@ func (e *Selectable) processPointer(gtx layout.Context) {
func (e *Selectable) clickDragEvents(gtx layout.Context) []event.Event {
var combinedEvents []event.Event
for _, evt := range e.clicker.Update(gtx) {
for _, evt := range e.clicker.Update(gtx.Source) {
combinedEvents = append(combinedEvents, evt)
}
for _, evt := range e.dragger.Update(gtx.Metric, gtx, gesture.Both) {
for _, evt := range e.dragger.Update(gtx.Metric, gtx.Source, gesture.Both) {
combinedEvents = append(combinedEvents, evt)
}
return combinedEvents
+1 -1
View File
@@ -38,7 +38,7 @@ func TestSelectableMove(t *testing.T) {
gtx := layout.Context{
Ops: new(op.Ops),
Locale: english,
Source: r,
Source: r.Source(),
}
cache := text.NewShaper(text.NoSystemFonts(), text.WithCollection(gofont.Collection()))
fnt := font.Font{}
+1 -1
View File
@@ -22,7 +22,7 @@ func TestBool(t *testing.T) {
r input.Router
b widget.Bool
)
gtx := app.NewContext(&ops, app.FrameEvent{Source: &r})
gtx := app.NewContext(&ops, app.FrameEvent{Source: r.Source()})
layout := func() {
b.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
semantic.CheckBox.Add(gtx.Ops)