1 // SPDX-License-Identifier: Unlicense OR MIT
2 3 /*
4 Package key implements key and text events and operations.
5 6 The InputOp operations is used for declaring key input handlers. Use
7 an implementation of the Queue interface from package ui to receive
8 events.
9 */
10 package key
11 12 import (
13 "fmt"
14 "strings"
15 16 "github.com/p9c/p9/pkg/gel/gio/internal/opconst"
17 "github.com/p9c/p9/pkg/gel/gio/io/event"
18 "github.com/p9c/p9/pkg/gel/gio/op"
19 )
20 21 // InputOp declares a handler ready for key events.
22 // Key events are in general only delivered to the
23 // focused key handler.
24 type InputOp struct {
25 Tag event.Tag
26 }
27 28 // SoftKeyboardOp shows or hide the on-screen keyboard, if available.
29 // It replaces any previous SoftKeyboardOp.
30 type SoftKeyboardOp struct {
31 Show bool
32 }
33 34 // FocusOp sets or clears the keyboard focus. It replaces any previous
35 // FocusOp in the same frame.
36 type FocusOp struct {
37 // Tag is the new focus. The focus is cleared if Tag is nil, or if Tag
38 // has no InputOp in the same frame.
39 Tag event.Tag
40 }
41 42 // A FocusEvent is generated when a handler gains or loses
43 // focus.
44 type FocusEvent struct {
45 Focus bool
46 }
47 48 // An Event is generated when a key is pressed. For text input
49 // use EditEvent.
50 type Event struct {
51 // Name of the key. For letters, the upper case form is used, via
52 // unicode.ToUpper. The shift modifier is taken into account, all other
53 // modifiers are ignored. For example, the "shift-1" and "ctrl-shift-1"
54 // combinations both give the Name "!" with the US keyboard layout.
55 Name string
56 // Modifiers is the set of active modifiers when the key was pressed.
57 Modifiers Modifiers
58 // State is the state of the key when the event was fired.
59 State State
60 }
61 62 // An EditEvent is generated when text is input.
63 type EditEvent struct {
64 Text string
65 }
66 67 // State is the state of a key during an event.
68 type State uint8
69 70 const (
71 // Press is the state of a pressed key.
72 Press State = iota
73 // Release is the state of a key that has been released.
74 //
75 // Note: release events are only implemented on the following platforms:
76 // macOS, Linux, Windows, WebAssembly.
77 Release
78 )
79 80 // Modifiers
81 type Modifiers uint32
82 83 const (
84 // ModCtrl is the ctrl modifier key.
85 ModCtrl Modifiers = 1 << iota
86 // ModCommand is the command modifier key
87 // found on Apple keyboards.
88 ModCommand
89 // ModShift is the shift modifier key.
90 ModShift
91 // ModAlt is the alt modifier key, or the option
92 // key on Apple keyboards.
93 ModAlt
94 // ModSuper is the "logo" modifier key, often
95 // represented by a Windows logo.
96 ModSuper
97 )
98 99 const (
100 // Names for special keys.
101 NameLeftArrow = "←"
102 NameRightArrow = "→"
103 NameUpArrow = "↑"
104 NameDownArrow = "↓"
105 NameReturn = "⏎"
106 NameEnter = "⌤"
107 NameEscape = "⎋"
108 NameHome = "⇱"
109 NameEnd = "⇲"
110 NameDeleteBackward = "⌫"
111 NameDeleteForward = "⌦"
112 NamePageUp = "⇞"
113 NamePageDown = "⇟"
114 NameTab = "⇥"
115 NameSpace = "Space"
116 )
117 118 // Contain reports whether m contains all modifiers
119 // in m2.
120 func (m Modifiers) Contain(m2 Modifiers) bool {
121 return m&m2 == m2
122 }
123 124 func (h InputOp) Add(o *op.Ops) {
125 if h.Tag == nil {
126 panic("Tag must be non-nil")
127 }
128 data := o.Write1(opconst.TypeKeyInputLen, h.Tag)
129 data[0] = byte(opconst.TypeKeyInput)
130 }
131 132 func (h SoftKeyboardOp) Add(o *op.Ops) {
133 data := o.Write(opconst.TypeKeySoftKeyboardLen)
134 data[0] = byte(opconst.TypeKeySoftKeyboard)
135 if h.Show {
136 data[1] = 1
137 }
138 }
139 140 func (h FocusOp) Add(o *op.Ops) {
141 data := o.Write1(opconst.TypeKeyFocusLen, h.Tag)
142 data[0] = byte(opconst.TypeKeyFocus)
143 }
144 145 func (EditEvent) ImplementsEvent() {}
146 func (Event) ImplementsEvent() {}
147 func (FocusEvent) ImplementsEvent() {}
148 149 func (e Event) String() string {
150 return fmt.Sprintf("%v %v %v}", e.Name, e.Modifiers, e.State)
151 }
152 153 func (m Modifiers) String() string {
154 var strs []string
155 if m.Contain(ModCtrl) {
156 strs = append(strs, "ModCtrl")
157 }
158 if m.Contain(ModCommand) {
159 strs = append(strs, "ModCommand")
160 }
161 if m.Contain(ModShift) {
162 strs = append(strs, "ModShift")
163 }
164 if m.Contain(ModAlt) {
165 strs = append(strs, "ModAlt")
166 }
167 if m.Contain(ModSuper) {
168 strs = append(strs, "ModSuper")
169 }
170 return strings.Join(strs, "|")
171 }
172 173 func (s State) String() string {
174 switch s {
175 case Press:
176 return "Press"
177 case Release:
178 return "Release"
179 default:
180 panic("invalid State")
181 }
182 }
183