mirror of
https://git.sr.ht/~eliasnaur/gio
synced 2026-07-01 07:35:40 +00:00
io/input,widget: [API] replace per-widget Focused with Source.Focused
Widgets have themselves as tags, by convention, and so it's possible to replace the per-widget Focused methods with a general-purpose Source. Focused query. Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
@@ -318,8 +318,8 @@ func TestKeyRouting(t *testing.T) {
|
||||
|
||||
func assertFocus(t *testing.T, router *Router, expected event.Tag) {
|
||||
t.Helper()
|
||||
if got := router.state().focus; got != expected {
|
||||
t.Errorf("expected %v to be focused, got %v", expected, got)
|
||||
if !router.Source().Focused(expected) {
|
||||
t.Errorf("expected %v to be focused", expected)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,6 +184,15 @@ func (s Source) Enabled() bool {
|
||||
return s.r != nil
|
||||
}
|
||||
|
||||
// Focused reports whether tag is focused, according to the most recent
|
||||
// [key.FocusEvent] delivered.
|
||||
func (s Source) Focused(tag event.Tag) bool {
|
||||
if !s.Enabled() {
|
||||
return false
|
||||
}
|
||||
return s.r.state().keyState.focus == tag
|
||||
}
|
||||
|
||||
// Event returns the next event that matches at least one of filters.
|
||||
func (s Source) Event(filters ...event.Filter) (event.Event, bool) {
|
||||
if !s.Enabled() {
|
||||
|
||||
+2
-7
@@ -16,7 +16,7 @@ type Bool struct {
|
||||
// Update the widget state and report whether Value was changed.
|
||||
func (b *Bool) Update(gtx layout.Context) bool {
|
||||
changed := false
|
||||
for b.clk.Clicked(gtx) {
|
||||
for b.clk.clicked(b, gtx) {
|
||||
b.Value = !b.Value
|
||||
changed = true
|
||||
}
|
||||
@@ -33,18 +33,13 @@ func (b *Bool) Pressed() bool {
|
||||
return b.clk.Pressed()
|
||||
}
|
||||
|
||||
// Focused reports whether b has focus.
|
||||
func (b *Bool) Focused() bool {
|
||||
return b.clk.Focused()
|
||||
}
|
||||
|
||||
func (b *Bool) History() []Press {
|
||||
return b.clk.History()
|
||||
}
|
||||
|
||||
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 {
|
||||
dims := b.clk.layout(b, gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
semantic.SelectedOp(b.Value).Add(gtx.Ops)
|
||||
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
|
||||
return w(gtx)
|
||||
|
||||
+21
-19
@@ -22,7 +22,6 @@ type Clickable struct {
|
||||
history []Press
|
||||
|
||||
requestClicks int
|
||||
focused bool
|
||||
pressedKey key.Name
|
||||
}
|
||||
|
||||
@@ -52,7 +51,11 @@ func (b *Clickable) Click() {
|
||||
|
||||
// Clicked calls Update and reports whether a click was registered.
|
||||
func (b *Clickable) Clicked(gtx layout.Context) bool {
|
||||
_, clicked := b.Update(gtx)
|
||||
return b.clicked(b, gtx)
|
||||
}
|
||||
|
||||
func (b *Clickable) clicked(t event.Tag, gtx layout.Context) bool {
|
||||
_, clicked := b.update(t, gtx)
|
||||
return clicked
|
||||
}
|
||||
|
||||
@@ -66,11 +69,6 @@ func (b *Clickable) Pressed() bool {
|
||||
return b.click.Pressed()
|
||||
}
|
||||
|
||||
// Focused reports whether b has focus.
|
||||
func (b *Clickable) Focused() bool {
|
||||
return b.focused
|
||||
}
|
||||
|
||||
// History is the past pointer presses useful for drawing markers.
|
||||
// History is retained for a short duration (about a second).
|
||||
func (b *Clickable) History() []Press {
|
||||
@@ -79,8 +77,12 @@ func (b *Clickable) History() []Press {
|
||||
|
||||
// Layout and update the button state.
|
||||
func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||||
return b.layout(b, gtx, w)
|
||||
}
|
||||
|
||||
func (b *Clickable) layout(t event.Tag, gtx layout.Context, w layout.Widget) layout.Dimensions {
|
||||
for {
|
||||
_, ok := b.Update(gtx)
|
||||
_, ok := b.update(t, gtx)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
@@ -91,7 +93,7 @@ func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimension
|
||||
defer clip.Rect(image.Rectangle{Max: dims.Size}).Push(gtx.Ops).Pop()
|
||||
semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
|
||||
b.click.Add(gtx.Ops)
|
||||
event.InputOp(gtx.Ops, b)
|
||||
event.InputOp(gtx.Ops, t)
|
||||
c.Add(gtx.Ops)
|
||||
return dims
|
||||
}
|
||||
@@ -99,9 +101,10 @@ func (b *Clickable) Layout(gtx layout.Context, w layout.Widget) layout.Dimension
|
||||
// Update the button state by processing events, and return the next
|
||||
// click, if any.
|
||||
func (b *Clickable) Update(gtx layout.Context) (Click, bool) {
|
||||
if !gtx.Enabled() {
|
||||
b.focused = false
|
||||
}
|
||||
return b.update(b, gtx)
|
||||
}
|
||||
|
||||
func (b *Clickable) update(t event.Tag, gtx layout.Context) (Click, bool) {
|
||||
for len(b.history) > 0 {
|
||||
c := b.history[0]
|
||||
if c.End.IsZero() || gtx.Now.Sub(c.End) < 1*time.Second {
|
||||
@@ -139,7 +142,7 @@ func (b *Clickable) Update(gtx layout.Context) (Click, bool) {
|
||||
}
|
||||
case gesture.KindPress:
|
||||
if e.Source == pointer.Mouse {
|
||||
gtx.Execute(key.FocusCmd{Tag: b})
|
||||
gtx.Execute(key.FocusCmd{Tag: t})
|
||||
}
|
||||
b.history = append(b.history, Press{
|
||||
Position: e.Position,
|
||||
@@ -149,21 +152,20 @@ func (b *Clickable) Update(gtx layout.Context) (Click, bool) {
|
||||
}
|
||||
for {
|
||||
e, ok := gtx.Event(
|
||||
key.FocusFilter{Target: b},
|
||||
key.Filter{Focus: b, Name: key.NameReturn},
|
||||
key.Filter{Focus: b, Name: key.NameSpace},
|
||||
key.FocusFilter{Target: t},
|
||||
key.Filter{Focus: t, Name: key.NameReturn},
|
||||
key.Filter{Focus: t, Name: key.NameSpace},
|
||||
)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
switch e := e.(type) {
|
||||
case key.FocusEvent:
|
||||
b.focused = e.Focus
|
||||
if !b.focused {
|
||||
if e.Focus {
|
||||
b.pressedKey = ""
|
||||
}
|
||||
case key.Event:
|
||||
if !b.focused {
|
||||
if !gtx.Focused(t) {
|
||||
break
|
||||
}
|
||||
if e.Name != key.NameReturn && e.Name != key.NameSpace {
|
||||
|
||||
@@ -39,10 +39,10 @@ func TestClickable(t *testing.T) {
|
||||
}
|
||||
gtx.Execute(key.FocusCmd{Tag: &b1})
|
||||
frame()
|
||||
if !b1.Focused() {
|
||||
if !gtx.Focused(&b1) {
|
||||
t.Error("button 1 did not gain focus")
|
||||
}
|
||||
if b2.Focused() {
|
||||
if gtx.Focused(&b2) {
|
||||
t.Error("button 2 should not have focus")
|
||||
}
|
||||
r.Queue(
|
||||
@@ -73,10 +73,10 @@ func TestClickable(t *testing.T) {
|
||||
frame()
|
||||
gtx.Execute(key.FocusCmd{Tag: &b2})
|
||||
frame()
|
||||
if b1.Focused() {
|
||||
if gtx.Focused(&b1) {
|
||||
t.Error("button 1 should not have focus")
|
||||
}
|
||||
if !b2.Focused() {
|
||||
if !gtx.Focused(&b2) {
|
||||
t.Error("button 2 did not gain focus")
|
||||
}
|
||||
r.Queue(
|
||||
|
||||
+4
-13
@@ -72,7 +72,6 @@ type Editor struct {
|
||||
// from the textView.
|
||||
scratch []byte
|
||||
blinkStart time.Time
|
||||
focused bool
|
||||
|
||||
// ime tracks the state relevant to input methods.
|
||||
ime struct {
|
||||
@@ -385,14 +384,13 @@ func (e *Editor) processKey(gtx layout.Context) {
|
||||
e.blinkStart = gtx.Now
|
||||
switch ke := ke.(type) {
|
||||
case key.FocusEvent:
|
||||
e.focused = ke.Focus
|
||||
// Reset IME state.
|
||||
e.ime.imeState = imeState{}
|
||||
if ke.Focus {
|
||||
gtx.Execute(key.SoftKeyboardCmd{Show: true})
|
||||
}
|
||||
case key.Event:
|
||||
if !e.focused || ke.State != key.Press {
|
||||
if !gtx.Focused(e) || ke.State != key.Press {
|
||||
break
|
||||
}
|
||||
if !e.ReadOnly && e.Submit && (ke.Name == key.NameReturn || ke.Name == key.NameEnter) {
|
||||
@@ -558,11 +556,6 @@ func (e *Editor) command(gtx layout.Context, k key.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
// Focused returns whether the editor is focused or not.
|
||||
func (e *Editor) Focused() bool {
|
||||
return e.focused
|
||||
}
|
||||
|
||||
// initBuffer should be invoked first in every exported function that accesses
|
||||
// text state. It ensures that the underlying text widget is both ready to use
|
||||
// and has its fields synced with the editor.
|
||||
@@ -583,7 +576,6 @@ func (e *Editor) initBuffer() {
|
||||
func (e *Editor) Update(gtx layout.Context) {
|
||||
e.initBuffer()
|
||||
e.processEvents(gtx)
|
||||
if e.focused {
|
||||
// Notify IME of selection if it changed.
|
||||
newSel := e.ime.selection
|
||||
start, end := e.text.Selection()
|
||||
@@ -603,7 +595,6 @@ func (e *Editor) Update(gtx layout.Context) {
|
||||
}
|
||||
|
||||
e.updateSnippet(gtx, e.ime.start, e.ime.end)
|
||||
}
|
||||
}
|
||||
|
||||
// Layout lays out the editor using the provided textMaterial as the paint material
|
||||
@@ -679,7 +670,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
||||
e.clicker.Add(gtx.Ops)
|
||||
e.dragger.Add(gtx.Ops)
|
||||
e.showCaret = false
|
||||
if e.focused {
|
||||
if gtx.Focused(e) {
|
||||
now := gtx.Now
|
||||
dt := now.Sub(e.blinkStart)
|
||||
blinking := dt < maxBlinkDuration
|
||||
@@ -688,7 +679,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
||||
if blinking {
|
||||
gtx.Execute(op.InvalidateCmd{At: nextBlink})
|
||||
}
|
||||
e.showCaret = e.focused && (!blinking || dt%timePerBlink < timePerBlink/2)
|
||||
e.showCaret = !blinking || dt%timePerBlink < timePerBlink/2
|
||||
}
|
||||
semantic.Editor.Add(gtx.Ops)
|
||||
if e.Len() > 0 {
|
||||
@@ -705,7 +696,7 @@ func (e *Editor) layout(gtx layout.Context, textMaterial, selectMaterial op.Call
|
||||
// material to set the painting material for the selection.
|
||||
func (e *Editor) paintSelection(gtx layout.Context, material op.CallOp) {
|
||||
e.initBuffer()
|
||||
if !e.focused {
|
||||
if !gtx.Focused(e) {
|
||||
return
|
||||
}
|
||||
e.text.PaintSelection(gtx, material)
|
||||
|
||||
@@ -96,7 +96,7 @@ func Clickable(gtx layout.Context, button *widget.Clickable, w layout.Widget) la
|
||||
return layout.Background{}.Layout(gtx,
|
||||
func(gtx layout.Context) layout.Dimensions {
|
||||
defer clip.Rect{Max: gtx.Constraints.Min}.Push(gtx.Ops).Pop()
|
||||
if button.Hovered() || button.Focused() {
|
||||
if button.Hovered() || gtx.Focused(button) {
|
||||
paint.Fill(gtx.Ops, f32color.Hovered(color.NRGBA{}))
|
||||
}
|
||||
for _, c := range button.History() {
|
||||
@@ -135,7 +135,7 @@ func (b ButtonLayoutStyle) Layout(gtx layout.Context, w layout.Widget) layout.Di
|
||||
switch {
|
||||
case !gtx.Enabled():
|
||||
background = f32color.Disabled(b.Background)
|
||||
case b.Button.Hovered() || b.Button.Focused():
|
||||
case b.Button.Hovered() || gtx.Focused(b.Button):
|
||||
background = f32color.Hovered(b.Background)
|
||||
}
|
||||
paint.Fill(gtx.Ops, background)
|
||||
@@ -167,7 +167,7 @@ func (b IconButtonStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
switch {
|
||||
case !gtx.Enabled():
|
||||
background = f32color.Disabled(b.Background)
|
||||
case b.Button.Hovered() || b.Button.Focused():
|
||||
case b.Button.Hovered() || gtx.Focused(b.Button):
|
||||
background = f32color.Hovered(b.Background)
|
||||
}
|
||||
paint.Fill(gtx.Ops, background)
|
||||
|
||||
@@ -35,6 +35,6 @@ func CheckBox(th *Theme, checkBox *widget.Bool, label string) CheckBoxStyle {
|
||||
func (c CheckBoxStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
return c.CheckBox.Layout(gtx, func(gtx layout.Context) layout.Dimensions {
|
||||
semantic.CheckBox.Add(gtx.Ops)
|
||||
return c.layout(gtx, c.CheckBox.Value, c.CheckBox.Hovered() || c.CheckBox.Focused())
|
||||
return c.layout(gtx, c.CheckBox.Value, c.CheckBox.Hovered() || gtx.Focused(c.CheckBox))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func (s SwitchStyle) Layout(gtx layout.Context) layout.Dimensions {
|
||||
return clip.Ellipse(b).Op(gtx.Ops)
|
||||
}
|
||||
// Draw hover.
|
||||
if s.Switch.Hovered() || s.Switch.Focused() {
|
||||
if s.Switch.Hovered() || gtx.Focused(s.Switch) {
|
||||
r := thumbRadius * 10 / 17
|
||||
background := f32color.MulAlpha(s.Color.Enabled, 70)
|
||||
paint.FillShape(gtx.Ops, background, circle(thumbRadius, thumbRadius, r))
|
||||
|
||||
Reference in New Issue
Block a user