gioui.org/ui@v0.0.0-20190926171558-ce74bc0cbaea/app/internal/input/key.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package input
     4  
     5  import (
     6  	"gioui.org/ui"
     7  	"gioui.org/ui/internal/opconst"
     8  	"gioui.org/ui/internal/ops"
     9  	"gioui.org/ui/key"
    10  )
    11  
    12  type TextInputState uint8
    13  
    14  type keyQueue struct {
    15  	focus    ui.Key
    16  	handlers map[ui.Key]*keyHandler
    17  	reader   ops.Reader
    18  	state    TextInputState
    19  }
    20  
    21  type keyHandler struct {
    22  	active bool
    23  }
    24  
    25  type listenerPriority uint8
    26  
    27  const (
    28  	priNone listenerPriority = iota
    29  	priDefault
    30  	priCurrentFocus
    31  	priNewFocus
    32  )
    33  
    34  const (
    35  	TextInputKeep TextInputState = iota
    36  	TextInputClose
    37  	TextInputOpen
    38  )
    39  
    40  // InputState returns the last text input state as
    41  // determined in Frame.
    42  func (q *keyQueue) InputState() TextInputState {
    43  	return q.state
    44  }
    45  
    46  func (q *keyQueue) Frame(root *ui.Ops, events *handlerEvents) {
    47  	if q.handlers == nil {
    48  		q.handlers = make(map[ui.Key]*keyHandler)
    49  	}
    50  	for _, h := range q.handlers {
    51  		h.active = false
    52  	}
    53  	q.reader.Reset(root)
    54  	focus, pri, hide := q.resolveFocus(events)
    55  	for k, h := range q.handlers {
    56  		if !h.active {
    57  			delete(q.handlers, k)
    58  			if q.focus == k {
    59  				q.focus = nil
    60  				hide = true
    61  			}
    62  		}
    63  	}
    64  	if focus != q.focus {
    65  		if q.focus != nil {
    66  			events.Add(q.focus, key.FocusEvent{Focus: false})
    67  		}
    68  		q.focus = focus
    69  		if q.focus != nil {
    70  			events.Add(q.focus, key.FocusEvent{Focus: true})
    71  		} else {
    72  			hide = true
    73  		}
    74  	}
    75  	switch {
    76  	case pri == priNewFocus:
    77  		q.state = TextInputOpen
    78  	case hide:
    79  		q.state = TextInputClose
    80  	default:
    81  		q.state = TextInputKeep
    82  	}
    83  }
    84  
    85  func (q *keyQueue) Push(e ui.Event, events *handlerEvents) {
    86  	if q.focus != nil {
    87  		events.Add(q.focus, e)
    88  	}
    89  }
    90  
    91  func (q *keyQueue) resolveFocus(events *handlerEvents) (ui.Key, listenerPriority, bool) {
    92  	var k ui.Key
    93  	var pri listenerPriority
    94  	var hide bool
    95  loop:
    96  	for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
    97  		switch opconst.OpType(encOp.Data[0]) {
    98  		case opconst.TypeKeyInput:
    99  			op := decodeKeyInputOp(encOp.Data, encOp.Refs)
   100  			var newPri listenerPriority
   101  			switch {
   102  			case op.Focus:
   103  				newPri = priNewFocus
   104  			case op.Key == q.focus:
   105  				newPri = priCurrentFocus
   106  			default:
   107  				newPri = priDefault
   108  			}
   109  			// Switch focus if higher priority or if focus requested.
   110  			if newPri.replaces(pri) {
   111  				k, pri = op.Key, newPri
   112  			}
   113  			h, ok := q.handlers[op.Key]
   114  			if !ok {
   115  				h = new(keyHandler)
   116  				q.handlers[op.Key] = h
   117  				// Reset the handler on (each) first appearance.
   118  				events.Set(op.Key, []ui.Event{key.FocusEvent{Focus: false}})
   119  			}
   120  			h.active = true
   121  		case opconst.TypeHideInput:
   122  			hide = true
   123  		case opconst.TypePush:
   124  			newK, newPri, h := q.resolveFocus(events)
   125  			hide = hide || h
   126  			if newPri.replaces(pri) {
   127  				k, pri = newK, newPri
   128  			}
   129  		case opconst.TypePop:
   130  			break loop
   131  		}
   132  	}
   133  	return k, pri, hide
   134  }
   135  
   136  func (p listenerPriority) replaces(p2 listenerPriority) bool {
   137  	// Favor earliest default focus or latest requested focus.
   138  	return p > p2 || p == p2 && p == priNewFocus
   139  }
   140  
   141  func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp {
   142  	if opconst.OpType(d[0]) != opconst.TypeKeyInput {
   143  		panic("invalid op")
   144  	}
   145  	return key.InputOp{
   146  		Focus: d[1] != 0,
   147  		Key:   refs[0].(ui.Key),
   148  	}
   149  }