Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "XLTextInputManager.h"
25 :
26 : namespace STAPPLER_VERSIONIZED stappler::xenolith {
27 :
28 42 : TextInputHandler::~TextInputHandler() {
29 42 : cancel();
30 42 : }
31 :
32 63 : bool TextInputHandler::run(TextInputManager *manager, WideStringView str, TextCursor cursor, TextCursor marked, TextInputType type) {
33 63 : if (!isActive()) {
34 63 : this->manager = manager;
35 63 : return manager->run(this, str, cursor, marked, type);
36 : }
37 0 : return false;
38 : }
39 :
40 84 : void TextInputHandler::cancel() {
41 84 : if (isActive()) {
42 42 : this->manager->cancel();
43 42 : this->manager = nullptr;
44 : }
45 84 : }
46 :
47 : // only if this handler is active
48 21 : bool TextInputHandler::setString(WideStringView str, TextCursor c, TextCursor m) {
49 21 : if (isActive()) {
50 21 : this->manager->setString(str, c, m);
51 21 : return true;
52 : }
53 0 : return false;
54 : }
55 :
56 21 : bool TextInputHandler::setCursor(TextCursor c) {
57 21 : if (isActive()) {
58 21 : this->manager->setCursor(c);
59 21 : return true;
60 : }
61 0 : return false;
62 : }
63 :
64 21 : bool TextInputHandler::setMarked(TextCursor c) {
65 21 : if (isActive()) {
66 21 : this->manager->setMarked(c);
67 21 : return true;
68 : }
69 0 : return false;
70 : }
71 :
72 21 : WideStringView TextInputHandler::getString() const {
73 21 : return this->manager->getString();
74 : }
75 21 : TextCursor TextInputHandler::getCursor() const {
76 21 : return this->manager->getCursor();
77 : }
78 21 : TextCursor TextInputHandler::getMarked() const {
79 21 : return this->manager->getMarked();
80 : }
81 :
82 21 : bool TextInputHandler::isInputEnabled() const {
83 21 : return this->manager->isInputEnabled();
84 : }
85 :
86 231 : bool TextInputHandler::isActive() const {
87 231 : return this->manager && this->manager->getHandler() == this;
88 : }
89 :
90 42 : TextInputManager::TextInputManager() { }
91 :
92 42 : bool TextInputManager::init(TextInputViewInterface *view) {
93 42 : _view = view;
94 42 : return true;
95 : }
96 :
97 21 : bool TextInputManager::hasText() {
98 21 : return _string.length() != 0;
99 : }
100 :
101 945 : void TextInputManager::insertText(WideStringView sInsert, bool compose) {
102 945 : if (doInsertText(sInsert, compose)) {
103 945 : onTextChanged();
104 : }
105 945 : }
106 :
107 42 : void TextInputManager::insertText(WideStringView sInsert, TextCursor replacement) {
108 42 : if (replacement.start != maxOf<uint32_t>()) {
109 42 : _cursor = replacement;
110 : }
111 :
112 42 : if (doInsertText(sInsert, false)) {
113 42 : onTextChanged();
114 : }
115 42 : }
116 :
117 21 : void TextInputManager::setMarkedText(WideStringView sInsert, TextCursor replacement, TextCursor marked) {
118 21 : if (replacement.start != maxOf<uint32_t>()) {
119 21 : _cursor = replacement;
120 : }
121 :
122 21 : auto start = _cursor.start;
123 :
124 21 : if (doInsertText(sInsert, false)) {
125 21 : _marked = TextCursor(start + marked.start, marked.length);
126 21 : onTextChanged();
127 : }
128 21 : }
129 :
130 84 : void TextInputManager::textChanged(WideStringView text, TextCursor cursor, TextCursor marked) {
131 84 : if (text.size() == 0) {
132 42 : _string = WideString();
133 42 : _cursor.start = 0;
134 42 : _cursor.length = 0;
135 42 : _marked = TextCursor::InvalidCursor;
136 42 : onTextChanged();
137 42 : return;
138 : }
139 :
140 42 : _string = text.str<Interface>();
141 42 : _cursor = cursor;
142 42 : _marked = marked;
143 42 : onTextChanged();
144 : }
145 :
146 42 : void TextInputManager::cursorChanged(TextCursor cursor) {
147 42 : _cursor = cursor;
148 42 : onTextChanged();
149 42 : }
150 :
151 21 : void TextInputManager::markedChanged(TextCursor marked) {
152 21 : _marked = marked;
153 21 : onTextChanged();
154 21 : }
155 :
156 84 : void TextInputManager::deleteBackward() {
157 84 : if (_string.empty()) {
158 0 : return;
159 : }
160 :
161 84 : if (_cursor.length > 0) {
162 21 : _string.erase(_string.begin() + _cursor.start, _string.begin() + _cursor.start + _cursor.length);
163 21 : _cursor.length = 0;
164 21 : onTextChanged();
165 21 : return;
166 : }
167 :
168 63 : if (_cursor.start == 0) {
169 0 : return;
170 : }
171 :
172 : // TODO: check for surrogate pairs
173 63 : size_t nDeleteLen = 1;
174 :
175 63 : if (_string.length() <= nDeleteLen) {
176 0 : _string = WideString();
177 0 : _cursor.start = 0;
178 0 : _cursor.length = 0;
179 0 : onTextChanged();
180 0 : return;
181 : }
182 :
183 63 : _string.erase(_string.begin() + _cursor.start - 1, _string.begin() + _cursor.start - 1 + nDeleteLen);
184 63 : _cursor.start -= nDeleteLen;
185 63 : onTextChanged();
186 : }
187 :
188 63 : void TextInputManager::deleteForward() {
189 63 : if (_string.empty()) {
190 0 : return;
191 : }
192 :
193 63 : if (_cursor.length > 0) {
194 21 : _string.erase(_string.begin() + _cursor.start, _string.begin() + _cursor.start + _cursor.length);
195 21 : _cursor.length = 0;
196 21 : onTextChanged();
197 21 : return;
198 : }
199 :
200 42 : if (_cursor.start == _string.size()) {
201 0 : return;
202 : }
203 :
204 : // TODO: check for surrogate pairs
205 42 : size_t nDeleteLen = 1;
206 :
207 42 : if (_string.length() <= nDeleteLen) {
208 0 : _string = WideString();
209 0 : _cursor.start = 0;
210 0 : _cursor.length = 0;
211 0 : onTextChanged();
212 0 : return;
213 : }
214 :
215 42 : _string.erase(_string.begin() + _cursor.start, _string.begin() + _cursor.start + nDeleteLen);
216 42 : onTextChanged();
217 : }
218 :
219 21 : void TextInputManager::unmarkText() {
220 21 : markedChanged(TextCursor::InvalidCursor);
221 21 : }
222 :
223 189 : void TextInputManager::setInputEnabled(bool enabled) {
224 189 : if (_isInputEnabled != enabled) {
225 126 : _isInputEnabled = enabled;
226 126 : _compose = InputKeyComposeState::Nothing;
227 126 : if (_handler && _handler->onInput) {
228 42 : _handler->onInput(enabled);
229 : }
230 126 : if (!_isInputEnabled) {
231 63 : cancel();
232 : }
233 : }
234 189 : }
235 :
236 1302 : void TextInputManager::onTextChanged() {
237 1302 : if (_handler && _handler->onText) {
238 693 : _handler->onText(_string, _cursor, _marked);
239 : }
240 1302 : }
241 :
242 63 : bool TextInputManager::run(TextInputHandler *h, WideStringView str, TextCursor cursor, TextCursor marked, TextInputType type) {
243 63 : auto oldH = _handler;
244 63 : _handler = h;
245 63 : if (oldH && _running) {
246 0 : oldH->onInput(false);
247 : }
248 63 : _cursor = cursor;
249 63 : _marked = marked;
250 63 : _string = str.str<Interface>();
251 63 : _type = type;
252 63 : _handler = h;
253 63 : if (cursor.start > str.size()) {
254 42 : _cursor.start = (uint32_t)str.size();
255 : }
256 63 : if (!_running) {
257 63 : _view->runTextInput(_string, cursor.start, cursor.length, type);
258 63 : _running = true;
259 : } else {
260 0 : _view->updateTextInput(_string, cursor.start, cursor.length, type);
261 0 : if (_running) {
262 0 : _handler->onInput(true);
263 : }
264 0 : return false;
265 : }
266 63 : _compose = InputKeyComposeState::Nothing;
267 63 : return true;
268 : }
269 :
270 21 : void TextInputManager::setString(WideStringView str, TextCursor cursor, TextCursor marked) {
271 21 : _cursor = cursor;
272 21 : _marked = marked;
273 21 : _string = str.str<Interface>();
274 21 : if (cursor.start > str.size()) {
275 21 : _cursor.start = (uint32_t)str.size();
276 : }
277 :
278 21 : _view->updateTextInput(_string, cursor.start, cursor.length, _type);
279 21 : }
280 :
281 21 : void TextInputManager::setCursor(TextCursor cursor) {
282 21 : _cursor = cursor;
283 21 : if (cursor.start > _string.length()) {
284 0 : _cursor.start = (uint32_t)_string.length();
285 : }
286 :
287 21 : if (_running) {
288 21 : _view->updateTextCursor(_cursor.start, _cursor.length);
289 : }
290 21 : }
291 :
292 21 : void TextInputManager::setMarked(TextCursor marked) {
293 21 : _marked = marked;
294 21 : if (marked.start > _string.length()) {
295 0 : _marked.start = (uint32_t)_string.length();
296 : }
297 21 : }
298 :
299 21 : WideStringView TextInputManager::getString() const {
300 21 : return _string;
301 : }
302 :
303 21 : WideStringView TextInputManager::getStringByRange(TextCursor cursor) {
304 21 : WideStringView str = _string;
305 21 : if (cursor.start >= str.size()) {
306 0 : return WideStringView();
307 : }
308 :
309 21 : str += cursor.start;
310 21 : if (cursor.length >= str.size()) {
311 0 : return str;
312 : }
313 :
314 21 : return str.sub(0, cursor.length);
315 : }
316 21 : TextCursor TextInputManager::getCursor() const {
317 21 : return _cursor;
318 : }
319 21 : TextCursor TextInputManager::getMarked() const {
320 21 : return _marked;
321 : }
322 :
323 126 : void TextInputManager::cancel() {
324 126 : if (_running) {
325 126 : _view->cancelTextInput();
326 126 : setInputEnabled(false);
327 126 : _handler = nullptr;
328 :
329 126 : _string = WideString();
330 126 : _cursor.start = 0;
331 126 : _cursor.length = 0;
332 126 : _running = false;
333 : }
334 126 : }
335 :
336 2100 : bool TextInputManager::canHandleInputEvent(const InputEventData &data) {
337 2100 : if (_running && _isInputEnabled && data.key.compose != InputKeyComposeState::Disabled) {
338 1764 : switch (data.event) {
339 1764 : case InputEventName::KeyPressed:
340 : case InputEventName::KeyRepeated:
341 : case InputEventName::KeyReleased:
342 : case InputEventName::KeyCanceled:
343 1764 : if (data.key.keychar || data.key.keycode == InputKeyCode::BACKSPACE || data.key.keycode == InputKeyCode::DELETE
344 21 : || data.key.keycode == InputKeyCode::ESCAPE) {
345 1764 : return true;
346 : }
347 0 : break;
348 0 : default:
349 0 : break;
350 : }
351 : }
352 336 : return false;
353 : }
354 :
355 1764 : bool TextInputManager::handleInputEvent(const InputEventData &data) {
356 1764 : if (data.event == InputEventName::KeyReleased) {
357 756 : if (data.key.compose != InputKeyComposeState::Forced) {
358 735 : return false;
359 : }
360 : }
361 :
362 1029 : switch (data.event) {
363 1029 : case InputEventName::KeyPressed:
364 : case InputEventName::KeyRepeated:
365 : case InputEventName::KeyReleased:
366 1029 : if (data.key.keycode == InputKeyCode::BACKSPACE || data.key.keychar == char32_t(0x0008)) {
367 42 : deleteBackward();
368 42 : return true;
369 987 : } else if (data.key.keycode == InputKeyCode::DELETE || data.key.keychar == char32_t(0x007f)) {
370 21 : deleteForward();
371 21 : return true;
372 966 : } else if (data.key.keycode == InputKeyCode::ESCAPE) {
373 21 : cancel();
374 945 : } else if (data.key.keychar) {
375 945 : auto c = data.key.keychar;
376 : // replace \r with \n for formatter
377 945 : if (c == '\r') {
378 0 : c = '\n';
379 : }
380 945 : switch (data.key.compose) {
381 903 : case InputKeyComposeState::Nothing:
382 : case InputKeyComposeState::Forced:
383 903 : if (_compose == InputKeyComposeState::Composing) {
384 0 : _cursor.start += _cursor.length;
385 0 : _cursor.length = 0;
386 : }
387 903 : insertText(string::toUtf16<Interface>(c));
388 903 : break;
389 21 : case InputKeyComposeState::Composed:
390 21 : insertText(string::toUtf16<Interface>(c), false);
391 21 : break;
392 21 : case InputKeyComposeState::Composing:
393 21 : insertText(string::toUtf16<Interface>(c), true);
394 21 : break;
395 0 : case InputKeyComposeState::Disabled:
396 0 : break;
397 : }
398 945 : _compose = data.key.compose;
399 945 : return true;
400 : }
401 21 : break;
402 0 : case InputEventName::KeyCanceled:
403 0 : break;
404 0 : default:
405 0 : break;
406 : }
407 21 : return false;
408 : }
409 :
410 1008 : bool TextInputManager::doInsertText(WideStringView sInsert, bool compose) {
411 1008 : if (sInsert.size() > 0) {
412 1008 : if (_cursor.length > 0 && (!compose || _compose != InputKeyComposeState::Composing)) {
413 105 : _string.erase(_string.begin() + _cursor.start, _string.begin() + _cursor.start + _cursor.length);
414 105 : _cursor.length = 0;
415 : }
416 :
417 1008 : WideString sText(_string.substr(0, _cursor.start).append(sInsert.data(), sInsert.size()));
418 :
419 1008 : if (_cursor.start < _string.length()) {
420 210 : sText.append(_string.substr(_cursor.start));
421 : }
422 :
423 1008 : _string = std::move(sText);
424 :
425 1008 : if (compose) {
426 21 : _cursor.length += sInsert.size();
427 : } else {
428 987 : _cursor.start += sInsert.size();
429 : }
430 :
431 1008 : return true;
432 1008 : }
433 0 : return false;
434 : }
435 :
436 : }
|