LCOV - code coverage report
Current view: top level - xenolith/scene/input - XLTextInputManager.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 229 269 85.1 %
Date: 2024-05-12 00:16:13 Functions: 37 37 100.0 %

          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             : }

Generated by: LCOV version 1.14