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
+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)