github.com/cybriq/giocore@v0.0.7-0.20210703034601-cfb9cb5f3900/io/router/key.go (about)

     1  // SPDX-License-Identifier: Unlicense OR MIT
     2  
     3  package router
     4  
     5  import (
     6  	"github.com/cybriq/giocore/internal/opconst"
     7  	"github.com/cybriq/giocore/internal/ops"
     8  	"github.com/cybriq/giocore/io/event"
     9  	"github.com/cybriq/giocore/io/key"
    10  	"github.com/cybriq/giocore/op"
    11  )
    12  
    13  type TextInputState uint8
    14  
    15  type keyQueue struct {
    16  	focus    event.Tag
    17  	handlers map[event.Tag]*keyHandler
    18  	reader   ops.Reader
    19  	state    TextInputState
    20  	hint     key.InputHint
    21  }
    22  
    23  type keyHandler struct {
    24  	// visible will be true if the InputOp is present
    25  	// in the current frame.
    26  	visible bool
    27  	new     bool
    28  	hint    key.InputHint
    29  }
    30  
    31  const (
    32  	TextInputKeep TextInputState = iota
    33  	TextInputClose
    34  	TextInputOpen
    35  )
    36  
    37  // InputState returns the last text input state as
    38  // determined in Frame.
    39  func (q *keyQueue) InputState() TextInputState {
    40  	return q.state
    41  }
    42  
    43  // InputHint returns the input mode from the most recent key.InputOp.
    44  func (q *keyQueue) InputHint() (key.InputHint, bool) {
    45  	if q.focus == nil {
    46  		return q.hint, false
    47  	}
    48  	focused, ok := q.handlers[q.focus]
    49  	if !ok {
    50  		return q.hint, false
    51  	}
    52  	old := q.hint
    53  	q.hint = focused.hint
    54  	return q.hint, old != q.hint
    55  }
    56  
    57  func (q *keyQueue) Frame(root *op.Ops, events *handlerEvents) {
    58  	if q.handlers == nil {
    59  		q.handlers = make(map[event.Tag]*keyHandler)
    60  	}
    61  	for _, h := range q.handlers {
    62  		h.visible, h.new = false, false
    63  	}
    64  	q.reader.Reset(root)
    65  
    66  	focus, changed, state := q.resolveFocus(events)
    67  	for k, h := range q.handlers {
    68  		if !h.visible {
    69  			delete(q.handlers, k)
    70  			if q.focus == k {
    71  				// Remove the focus from the handler that is no longer visible.
    72  				q.focus = nil
    73  				state = TextInputClose
    74  			}
    75  		} else if h.new && k != focus {
    76  			// Reset the handler on (each) first appearance, but don't trigger redraw.
    77  			events.AddNoRedraw(k, key.FocusEvent{Focus: false})
    78  		}
    79  	}
    80  	if changed && focus != nil {
    81  		if _, exists := q.handlers[focus]; !exists {
    82  			focus = nil
    83  		}
    84  	}
    85  	if changed && focus != q.focus {
    86  		if q.focus != nil {
    87  			events.Add(q.focus, key.FocusEvent{Focus: false})
    88  		}
    89  		q.focus = focus
    90  		if q.focus != nil {
    91  			events.Add(q.focus, key.FocusEvent{Focus: true})
    92  		} else {
    93  			state = TextInputClose
    94  		}
    95  	}
    96  	q.state = state
    97  }
    98  
    99  func (q *keyQueue) Push(e event.Event, events *handlerEvents) {
   100  	if q.focus != nil {
   101  		events.Add(q.focus, e)
   102  	}
   103  }
   104  
   105  func (q *keyQueue) resolveFocus(events *handlerEvents) (focus event.Tag, changed bool, state TextInputState) {
   106  	for encOp, ok := q.reader.Decode(); ok; encOp, ok = q.reader.Decode() {
   107  		switch opconst.OpType(encOp.Data[0]) {
   108  		case opconst.TypeKeyFocus:
   109  			op := decodeFocusOp(encOp.Data, encOp.Refs)
   110  			changed = true
   111  			focus = op.Tag
   112  		case opconst.TypeKeySoftKeyboard:
   113  			op := decodeSoftKeyboardOp(encOp.Data, encOp.Refs)
   114  			if op.Show {
   115  				state = TextInputOpen
   116  			} else {
   117  				state = TextInputClose
   118  			}
   119  		case opconst.TypeKeyInput:
   120  			op := decodeKeyInputOp(encOp.Data, encOp.Refs)
   121  			h, ok := q.handlers[op.Tag]
   122  			if !ok {
   123  				h = &keyHandler{new: true}
   124  				q.handlers[op.Tag] = h
   125  			}
   126  			h.visible = true
   127  			h.hint = op.Hint
   128  		}
   129  	}
   130  	return
   131  }
   132  
   133  func decodeKeyInputOp(d []byte, refs []interface{}) key.InputOp {
   134  	if opconst.OpType(d[0]) != opconst.TypeKeyInput {
   135  		panic("invalid op")
   136  	}
   137  	return key.InputOp{
   138  		Tag:  refs[0].(event.Tag),
   139  		Hint: key.InputHint(d[1]),
   140  	}
   141  }
   142  
   143  func decodeSoftKeyboardOp(d []byte, refs []interface{}) key.SoftKeyboardOp {
   144  	if opconst.OpType(d[0]) != opconst.TypeKeySoftKeyboard {
   145  		panic("invalid op")
   146  	}
   147  	return key.SoftKeyboardOp{
   148  		Show: d[1] != 0,
   149  	}
   150  }
   151  
   152  func decodeFocusOp(d []byte, refs []interface{}) key.FocusOp {
   153  	if opconst.OpType(d[0]) != opconst.TypeKeyFocus {
   154  		panic("invalid op")
   155  	}
   156  	return key.FocusOp{
   157  		Tag: refs[0],
   158  	}
   159  }