github.com/utopiagio/gio@v0.0.8/widget/enum.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package widget
     4  
     5  import (
     6  	"github.com/utopiagio/gio/gesture"
     7  	"github.com/utopiagio/gio/io/event"
     8  	"github.com/utopiagio/gio/io/key"
     9  	"github.com/utopiagio/gio/io/pointer"
    10  	"github.com/utopiagio/gio/io/semantic"
    11  	"github.com/utopiagio/gio/layout"
    12  	"github.com/utopiagio/gio/op"
    13  	"github.com/utopiagio/gio/op/clip"
    14  )
    15  
    16  type Enum struct {
    17  	Value    string
    18  	hovered  string
    19  	hovering bool
    20  
    21  	focus   string
    22  	focused bool
    23  
    24  	keys []*enumKey
    25  }
    26  
    27  type enumKey struct {
    28  	key   string
    29  	click gesture.Click
    30  	tag   struct{}
    31  }
    32  
    33  func (e *Enum) index(k string) *enumKey {
    34  	for _, v := range e.keys {
    35  		if v.key == k {
    36  			return v
    37  		}
    38  	}
    39  	return nil
    40  }
    41  
    42  // Update the state and report whether Value has changed by user interaction.
    43  func (e *Enum) Update(gtx layout.Context) bool {
    44  	if !gtx.Enabled() {
    45  		e.focused = false
    46  	}
    47  	e.hovering = false
    48  	changed := false
    49  	for _, state := range e.keys {
    50  		for {
    51  			ev, ok := state.click.Update(gtx.Source)
    52  			if !ok {
    53  				break
    54  			}
    55  			switch ev.Kind {
    56  			case gesture.KindPress:
    57  				if ev.Source == pointer.Mouse {
    58  					gtx.Execute(key.FocusCmd{Tag: &state.tag})
    59  				}
    60  			case gesture.KindClick:
    61  				if state.key != e.Value {
    62  					e.Value = state.key
    63  					changed = true
    64  				}
    65  			}
    66  		}
    67  		for {
    68  			ev, ok := gtx.Event(
    69  				key.FocusFilter{Target: &state.tag},
    70  				key.Filter{Focus: &state.tag, Name: key.NameReturn},
    71  				key.Filter{Focus: &state.tag, Name: key.NameSpace},
    72  			)
    73  			if !ok {
    74  				break
    75  			}
    76  			switch ev := ev.(type) {
    77  			case key.FocusEvent:
    78  				if ev.Focus {
    79  					e.focused = true
    80  					e.focus = state.key
    81  				} else if state.key == e.focus {
    82  					e.focused = false
    83  				}
    84  			case key.Event:
    85  				if ev.State != key.Release {
    86  					break
    87  				}
    88  				if ev.Name != key.NameReturn && ev.Name != key.NameSpace {
    89  					break
    90  				}
    91  				if state.key != e.Value {
    92  					e.Value = state.key
    93  					changed = true
    94  				}
    95  			}
    96  		}
    97  		if state.click.Hovered() {
    98  			e.hovered = state.key
    99  			e.hovering = true
   100  		}
   101  	}
   102  
   103  	return changed
   104  }
   105  
   106  // Hovered returns the key that is highlighted, or false if none are.
   107  func (e *Enum) Hovered() (string, bool) {
   108  	return e.hovered, e.hovering
   109  }
   110  
   111  // Focused reports the focused key, or false if no key is focused.
   112  func (e *Enum) Focused() (string, bool) {
   113  	return e.focus, e.focused
   114  }
   115  
   116  // Layout adds the event handler for the key k.
   117  func (e *Enum) Layout(gtx layout.Context, k string, content layout.Widget) layout.Dimensions {
   118  	e.Update(gtx)
   119  	m := op.Record(gtx.Ops)
   120  	dims := content(gtx)
   121  	c := m.Stop()
   122  	defer clip.Rect{Max: dims.Size}.Push(gtx.Ops).Pop()
   123  
   124  	state := e.index(k)
   125  	if state == nil {
   126  		state = &enumKey{
   127  			key: k,
   128  		}
   129  		e.keys = append(e.keys, state)
   130  	}
   131  	clk := &state.click
   132  	clk.Add(gtx.Ops)
   133  	event.Op(gtx.Ops, &state.tag)
   134  	semantic.SelectedOp(k == e.Value).Add(gtx.Ops)
   135  	semantic.EnabledOp(gtx.Enabled()).Add(gtx.Ops)
   136  	c.Add(gtx.Ops)
   137  
   138  	return dims
   139  }