key.go raw

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