axmol/core/ui/UITextFieldEx.cpp

1068 lines
27 KiB
C++
Raw Normal View History

2019-11-24 23:15:56 +08:00
//
2022-06-11 15:30:51 +08:00
// Copyright (c) 2014-2022 @HALX99 - All Rights Reserved
2019-11-24 23:15:56 +08:00
//
#ifndef _UITEXTFIELD_CPP_H_
#define _UITEXTFIELD_CPP_H_
#include "UITextFieldEx.h"
#include "base/CCDirector.h"
/// cocos2d singleton objects
2022-08-08 18:02:17 +08:00
#define CCDIRECTOR ax::Director::getInstance()
2021-12-25 10:04:45 +08:00
#define CCRUNONGL CCDIRECTOR->getScheduler()->performFunctionInCocosThread
#define CCEVENTMGR CCDIRECTOR->getEventDispatcher()
#define CCSCHTASKS CCDIRECTOR->getScheduler()
2019-11-24 23:15:56 +08:00
#define CCACTIONMGR CCDIRECTOR->getActionManager()
2022-08-08 18:02:17 +08:00
#define CCFILEUTILS ax::FileUtils::getInstance()
#define CCAUDIO ax::SimpleAudioEngine::getInstance()
#define CCAPP ax::CCApplication::getInstance()
2019-11-24 23:15:56 +08:00
NS_AX_BEGIN
2019-11-24 23:15:56 +08:00
#ifdef _WIN32
2021-12-25 10:04:45 +08:00
# define nxbeep(t) MessageBeep(t)
2019-11-24 23:15:56 +08:00
#else
2021-12-25 10:04:45 +08:00
# define nxbeep(t)
2019-11-24 23:15:56 +08:00
#endif
2021-12-26 23:26:34 +08:00
static Label* createLabel(std::string_view text,
std::string_view font,
2021-12-25 10:04:45 +08:00
float fontSize,
const Vec2& dimensions = Vec2::ZERO,
TextHAlignment hAlignment = TextHAlignment::LEFT,
TextVAlignment vAlignment = TextVAlignment::TOP)
2019-11-25 01:35:26 +08:00
{
if (FileUtils::getInstance()->isFileExist(font))
{
return Label::createWithTTF(text, font, fontSize, dimensions, hAlignment, vAlignment);
}
else
{
return Label::createWithSystemFont(text, font, fontSize, dimensions, hAlignment, vAlignment);
}
}
2019-11-24 23:15:56 +08:00
static bool engine_inj_checkVisibility(Node* theNode)
{
2022-07-15 19:17:01 +08:00
// AX_ASSERT(theNode != NULL);
2019-11-24 23:15:56 +08:00
bool visible = false;
for (Node* ptr = theNode; (ptr != nullptr && (visible = ptr->isVisible())); ptr = ptr->getParent())
;
return visible;
}
2022-08-08 18:02:17 +08:00
static bool engine_inj_containsTouchPoint(ax::Node* target, ax::Touch* touch)
2019-11-24 23:15:56 +08:00
{
assert(target != nullptr);
2022-08-08 18:02:17 +08:00
ax::Point pt = target->convertTouchToNodeSpace(touch);
2019-11-24 23:15:56 +08:00
2021-10-23 23:27:14 +08:00
const Vec2& size = target->getContentSize();
2019-11-24 23:15:56 +08:00
2022-08-08 18:02:17 +08:00
ax::Rect rc(0, 0, size.width, size.height);
2019-11-24 23:15:56 +08:00
bool contains = (rc.containsPoint(pt));
2022-07-16 10:43:05 +08:00
// AXLOG("check %#x coordinate:(%f, %f), contains:%d", target, pt.x, pt.y, contains);
2019-11-24 23:15:56 +08:00
return contains;
}
2022-08-08 18:02:17 +08:00
static bool engine_inj_containsPoint(ax::Node* target, const ax::Vec2& worldPoint)
2019-11-24 23:15:56 +08:00
{
2022-08-08 18:02:17 +08:00
ax::Point pt = target->convertToNodeSpace(worldPoint);
2019-11-24 23:15:56 +08:00
2021-10-23 23:27:14 +08:00
const Vec2& size = target->getContentSize();
2019-11-24 23:15:56 +08:00
2022-08-08 18:02:17 +08:00
ax::Rect rc(0, 0, size.width, size.height);
2019-11-24 23:15:56 +08:00
bool contains = (rc.containsPoint(pt));
2022-07-16 10:43:05 +08:00
// AXLOG("check %#x coordinate:(%f, %f), contains:%d", target, pt.x, pt.y, contains);
2019-11-24 23:15:56 +08:00
return contains;
}
static uint32_t engine_inj_c4b2dw(const Color4B& value)
{
2021-12-25 10:04:45 +08:00
auto rvalue = (uint32_t)value.a << 24 | (uint32_t)value.b << 16 | (uint32_t)value.g << 8 | (uint32_t)value.r;
2019-11-24 23:15:56 +08:00
return rvalue;
}
static Sprite* engine_inj_create_lump(const Color4B& color, int height, int width)
{
unsigned int* pixels((unsigned int*)malloc(height * width * sizeof(unsigned int)));
// Fill Pixels
2021-12-25 10:04:45 +08:00
uint32_t* ptr = pixels;
2020-08-04 00:14:35 +08:00
const Color4B fillColor = Color4B::WHITE;
2019-11-24 23:15:56 +08:00
for (int i = 0; i < height * width; ++i)
{
2021-12-25 10:04:45 +08:00
ptr[i] = engine_inj_c4b2dw(fillColor); // 0xffffffff;
2019-11-24 23:15:56 +08:00
}
// create cursor by pixels
Texture2D* texture = new Texture2D();
2022-02-06 13:06:49 +08:00
texture->initWithData(pixels, height * width * sizeof(unsigned int), backend::PixelFormat::RGBA8, width, height);
2019-11-24 23:15:56 +08:00
auto cursor = Sprite::createWithTexture(texture);
cursor->setColor(Color3B(color));
cursor->setOpacity(color.a);
texture->release();
free(pixels);
return cursor;
}
2021-12-25 10:04:45 +08:00
namespace ui
{
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
/// calculate the UTF-8 string's char count.
static int _calcCharCount(const char* text)
{
int n = 0;
char ch = 0;
while ((ch = *text) != 0x0)
2019-11-24 23:15:56 +08:00
{
2022-07-15 19:17:01 +08:00
AX_BREAK_IF(!ch);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (0x80 != (0xC0 & ch))
{
++n;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
++text;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
return n;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
/// calculate the UTF-8 string's char count.
static int _truncateUTF8String(const char* text, int limit, int& nb)
{
int n = 0;
char ch = 0;
nb = 0;
while ((ch = *text) != 0x0)
2019-11-24 23:15:56 +08:00
{
2022-07-15 19:17:01 +08:00
AX_BREAK_IF(!ch || n > limit);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (0x80 != (0xC0 & ch))
{
++n;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
++nb;
++text;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
return n;
}
2019-11-24 23:15:56 +08:00
2021-12-26 23:26:34 +08:00
static void internalSetLableFont(Label* l, std::string_view fontName, float fontSize)
2021-12-25 10:04:45 +08:00
{
if (FileUtils::getInstance()->isFileExist(fontName))
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
TTFConfig config = l->getTTFConfig();
config.fontFilePath = fontName;
config.fontSize = fontSize;
l->setTTFConfig(config);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
else
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
l->setSystemFontName(fontName);
l->requestSystemFontRefresh();
l->setSystemFontSize(fontSize);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-26 23:26:34 +08:00
static float internalCalcStringWidth(std::string_view s, std::string_view fontName, float fontSize)
2021-12-25 10:04:45 +08:00
{
auto label = createLabel(std::string{s}, fontName, fontSize);
return label->getContentSize().width;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
static std::string internalUTF8MoveLeft(std::string_view utf8Text, int length /* default utf8Text.length() */)
{
if (!utf8Text.empty() && length > 0)
{
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// get the delete byte number
int deleteLen = 1; // default, erase 1 byte
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
while (length >= deleteLen && 0x80 == (0xC0 & utf8Text.at(length - deleteLen)))
{
++deleteLen;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
return std::string{utf8Text.data(), static_cast<size_t>(length - deleteLen)};
}
else
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
return std::string{utf8Text};
}
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
static std::string internalUTF8MoveRight(std::string_view utf8Text, int length /* default utf8Text.length() */)
{
if (!utf8Text.empty() && length >= 0)
{
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// get the delete byte number
size_t addLen = 1; // default, erase 1 byte
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
while ((length + addLen) < utf8Text.size() && 0x80 == (0xC0 & utf8Text.at(length + addLen)))
{
++addLen;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
return std::string{utf8Text.data(), static_cast<size_t>(length + addLen)};
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
else
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
return std::string{utf8Text};
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
//////////////////////////////////////////////////////////////////////////
// constructor and destructor
//////////////////////////////////////////////////////////////////////////
bool TextFieldEx::s_keyboardVisible = false;
TextFieldEx::TextFieldEx()
: editable(true)
, renderLabel(nullptr)
, charCount(0)
, inputText("")
, placeHolder("")
, colorText(Color4B::WHITE)
, colorSpaceHolder(Color4B::GRAY)
, secureTextEntry(false)
, cursor(nullptr)
, enabled(true)
, touchListener(nullptr)
, kbdListener(nullptr)
, onTextModify(nullptr)
, onOpenIME(nullptr)
, onCloseIME(nullptr)
, charLimit(std::numeric_limits<int>::max())
, systemFontUsed(false)
, fontSize(24)
, insertPosUtf8(0)
, insertPos(0)
, cursorPos(0)
, touchCursorControlEnabled(true)
, cursorVisible(false)
, _continuousTouchDelayTimerID(nullptr)
, _continuousTouchDelayTime(0.6)
{}
TextFieldEx::~TextFieldEx()
{
if (this->kbdListener != nullptr)
CCEVENTMGR->removeEventListener(this->kbdListener);
if (this->touchListener != nullptr)
CCEVENTMGR->removeEventListener(this->touchListener);
}
//////////////////////////////////////////////////////////////////////////
// static constructor
//////////////////////////////////////////////////////////////////////////
2021-12-26 23:26:34 +08:00
TextFieldEx* TextFieldEx::create(std::string_view placeholder,
std::string_view fontName,
2021-12-25 10:04:45 +08:00
float fontSize,
float cursorWidth,
const Color4B& cursorColor)
{
TextFieldEx* ret = new TextFieldEx();
if (ret && ret->initWithPlaceHolder("", fontName, fontSize, cursorWidth, cursorColor))
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
ret->autorelease();
if (placeholder.size() > 0)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
ret->setPlaceholderText(placeholder);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
return ret;
2019-11-24 23:15:56 +08:00
}
2022-07-15 19:17:01 +08:00
AX_SAFE_DELETE(ret);
2021-12-25 10:04:45 +08:00
return nullptr;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
//////////////////////////////////////////////////////////////////////////
// initialize
//////////////////////////////////////////////////////////////////////////
2021-12-26 23:26:34 +08:00
bool TextFieldEx::initWithPlaceHolder(std::string_view placeholder,
std::string_view fontName,
2021-12-25 10:04:45 +08:00
float fontSize,
float cursorWidth,
const Color4B& cursorColor)
{
this->placeHolder = placeholder;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->renderLabel =
createLabel(placeholder, fontName, fontSize, Vec2::ZERO, TextHAlignment::CENTER, TextVAlignment::CENTER);
this->renderLabel->setAnchorPoint(Point::ANCHOR_MIDDLE_LEFT);
this->addChild(this->renderLabel);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
CCRUNONGL([this] { renderLabel->setPosition(Point(0, this->getContentSize().height / 2)); });
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
__initCursor(fontSize, cursorWidth, cursorColor);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->fontName = fontName;
this->fontSize = fontSize;
this->systemFontUsed = !FileUtils::getInstance()->isFileExist(fontName);
return true;
}
2021-12-26 23:26:34 +08:00
std::string_view TextFieldEx::getTextFontName() const
2021-12-25 10:04:45 +08:00
{
return this->fontName;
}
2019-11-24 23:15:56 +08:00
2021-12-26 23:26:34 +08:00
void TextFieldEx::setTextFontName(std::string_view fontName)
2021-12-25 10:04:45 +08:00
{
if (FileUtils::getInstance()->isFileExist(fontName))
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
TTFConfig config = renderLabel->getTTFConfig();
config.fontFilePath = fontName;
config.fontSize = this->fontSize;
renderLabel->setTTFConfig(config);
systemFontUsed = false;
_fontType = 1;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
else
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
renderLabel->setSystemFontName(fontName);
if (!systemFontUsed)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
renderLabel->requestSystemFontRefresh();
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
renderLabel->setSystemFontSize(this->fontSize);
systemFontUsed = true;
_fontType = 0;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
this->fontName = fontName;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
using namespace std::string_view_literals;
this->asteriskWidth = internalCalcStringWidth("*"sv, this->fontName, this->fontSize);
}
void TextFieldEx::setTextFontSize(float size)
{
if (this->systemFontUsed)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
renderLabel->setSystemFontSize(size);
}
else
{
TTFConfig config = renderLabel->getTTFConfig();
config.fontSize = size;
renderLabel->setTTFConfig(config);
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->fontSize = size;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
using namespace std::string_view_literals;
this->asteriskWidth = internalCalcStringWidth("*"sv, this->fontName, this->fontSize);
}
float TextFieldEx::getTextFontSize() const
{
return this->fontSize;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::enableIME(Node* control)
{
if (touchListener != nullptr)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
return;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
touchListener = EventListenerTouchOneByOne::create();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (control == nullptr)
control = this;
2019-11-24 23:15:56 +08:00
touchListener->onTouchBegan = [=, this](Touch* touch, Event*) {
2021-12-25 10:04:45 +08:00
bool focus = (engine_inj_checkVisibility(this) && this->editable && this->enabled &&
engine_inj_containsTouchPoint(control, touch));
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (this->_continuousTouchDelayTimerID != nullptr)
{
stimer::kill(this->_continuousTouchDelayTimerID);
this->_continuousTouchDelayTimerID = nullptr;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (focus && this->cursorVisible)
{
auto worldPoint = touch->getLocation();
if (this->_continuousTouchCallback)
{
this->_continuousTouchDelayTimerID = stimer::delay(
this->_continuousTouchDelayTime, [=, this]() { this->_continuousTouchCallback(worldPoint); });
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
return true;
};
touchListener->onTouchEnded = [control, this](Touch* touch, Event* e) {
if (this->_continuousTouchDelayTimerID != nullptr)
{
stimer::kill(this->_continuousTouchDelayTimerID);
this->_continuousTouchDelayTimerID = nullptr;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
bool focus = (engine_inj_checkVisibility(this) && this->editable && this->enabled &&
engine_inj_containsTouchPoint(control, touch));
if (focus)
{
if (!s_keyboardVisible || !this->cursorVisible)
openIME();
if (this->touchCursorControlEnabled)
{
auto renderLabelPoint = renderLabel->convertToNodeSpace(touch->getLocation());
__moveCursorTo(renderLabelPoint.x);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
else
{
closeIME();
}
};
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
CCEVENTMGR->addEventListenerWithSceneGraphPriority(touchListener, this);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
/// enable use keyboard <- -> to move cursor.
kbdListener = EventListenerKeyboard::create();
kbdListener->onKeyPressed = [this](EventKeyboard::KeyCode code, Event*) {
if (this->cursorVisible)
{
switch (code)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
this->__moveCursor(-1);
break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
this->__moveCursor(1);
break;
case EventKeyboard::KeyCode::KEY_DELETE:
case EventKeyboard::KeyCode::KEY_KP_DELETE:
this->handleDeleteKeyEvent();
break;
default:;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
};
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
CCEVENTMGR->addEventListenerWithSceneGraphPriority(kbdListener, this);
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::disableIME(void)
{
CCEVENTMGR->removeEventListener(kbdListener);
CCEVENTMGR->removeEventListener(touchListener);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
kbdListener = nullptr;
touchListener = nullptr;
closeIME();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
Label* TextFieldEx::getRenderLabel()
{
return this->renderLabel;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
//////////////////////////////////////////////////////////////////////////
// IMEDelegate
//////////////////////////////////////////////////////////////////////////
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
bool TextFieldEx::attachWithIME()
{
bool ret = IMEDelegate::attachWithIME();
if (ret)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
// open keyboard
GLView* pGlView = _director->getOpenGLView();
if (pGlView)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
pGlView->setIMEKeyboardState(true);
2019-11-24 23:15:56 +08:00
}
}
2021-12-25 10:04:45 +08:00
return ret;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
bool TextFieldEx::detachWithIME()
{
bool ret = IMEDelegate::detachWithIME();
if (ret)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
// close keyboard
GLView* glView = _director->getOpenGLView();
if (glView)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
glView->setIMEKeyboardState(false);
2019-11-24 23:15:56 +08:00
}
}
2021-12-25 10:04:45 +08:00
return ret;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::keyboardDidShow(IMEKeyboardNotificationInfo& /*info*/)
{
s_keyboardVisible = true;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::keyboardDidHide(IMEKeyboardNotificationInfo& /*info*/)
{
s_keyboardVisible = false;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::openIME(void)
{
2022-07-16 10:43:05 +08:00
AXLOG("TextFieldEx:: openIME");
2021-12-25 10:04:45 +08:00
this->attachWithIME();
__updateCursorPosition();
__showCursor();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (this->onOpenIME)
this->onOpenIME();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::closeIME(void)
{
2022-07-16 10:43:05 +08:00
AXLOG("TextFieldEx:: closeIME");
2021-12-25 10:04:45 +08:00
__hideCursor();
this->detachWithIME();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (this->onCloseIME)
this->onCloseIME();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
bool TextFieldEx::canAttachWithIME()
{
return true; //(_delegate) ? (! _delegate->onTextFieldAttachWithIME(this)) : true;
}
bool TextFieldEx::canDetachWithIME()
{
return true; //(_delegate) ? (! _delegate->onTextFieldDetachWithIME(this)) : true;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::insertText(const char* text, size_t len)
{
if (!this->editable || !this->enabled)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
return;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
if (this->charLimit > 0 && this->charCount >= this->charLimit)
{ // regard zero as unlimited
nxbeep(0);
return;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
int nb;
auto n = _truncateUTF8String(text, static_cast<int>(this->charLimit - this->charCount), nb);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
std::string insert(text, nb);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// insert \n means input end
auto pos = insert.find('\n');
if (insert.npos != pos)
{
len = pos;
insert.erase(pos);
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (len > 0)
{
// if (_delegate && _delegate->onTextFieldInsertText(this, insert.c_str(), len))
//{
// // delegate doesn't want to insert text
// return;
// }
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
charCount += n; // _calcCharCount(insert.c_str());
std::string sText(inputText);
sText.insert(this->insertPos, insert); // original is: sText.append(insert);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// bool needUpdatePos
this->setString(sText);
while (n-- > 0)
__moveCursor(1);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// this->contentDirty = true;
// __updateCursorPosition();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (this->onTextModify)
this->onTextModify();
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
if (insert.npos == pos)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
return;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// '\n' inserted, let delegate process first
/*if (_delegate && _delegate->onTextFieldInsertText(this, "\n", 1))
{
return;
}*/
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// if delegate hasn't processed, detach from IME by default
this->closeIME();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::deleteBackward()
{
if (!this->editable || !this->enabled || 0 == this->charCount)
{
nxbeep(0);
return;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
size_t len = inputText.length();
if (0 == len || insertPos == 0)
{
nxbeep(0);
// there is no string
// __updateCursorPosition();
return;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// get the delete byte number
size_t deleteLen = 1; // default, erase 1 byte
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
while (0x80 == (0xC0 & inputText.at(insertPos - deleteLen)))
{
++deleteLen;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// if (_delegate && _delegate->onTextFieldDeleteBackward(this, _inputText.c_str() + len - deleteLen,
// static_cast<int>(deleteLen)))
//{
// // delegate doesn't wan't to delete backwards
// return;
// }
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// if all text deleted, show placeholder string
if (len <= deleteLen)
{
2019-11-24 23:15:56 +08:00
__moveCursor(-1);
2021-12-25 10:04:45 +08:00
this->inputText.clear();
this->charCount = 0;
this->renderLabel->setTextColor(colorSpaceHolder);
this->renderLabel->setString(placeHolder);
// __updateCursorPosition();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// this->contentDirty = true;
2019-11-24 23:15:56 +08:00
if (this->onTextModify)
this->onTextModify();
2021-12-25 10:04:45 +08:00
return;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
// set new input text
std::string text = inputText; // (inputText.c_str(), len - deleteLen);
text.erase(insertPos - deleteLen, deleteLen);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
__moveCursor(-1);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->setString(text);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
//__updateCursorPosition();
// __moveCursor(-1);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (this->onTextModify)
this->onTextModify();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::handleDeleteKeyEvent()
{
if (!this->editable || !this->enabled || 0 == this->charCount)
{
nxbeep(0);
return;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
size_t len = inputText.length();
if (0 == len || insertPosUtf8 == this->charCount)
{
nxbeep(0);
// there is no string
// __updateCursorPosition();
return;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// get the delete byte number
size_t deleteLen = 1; // default, erase 1 byte
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
while ((inputText.length() > insertPos + deleteLen) && 0x80 == (0xC0 & inputText.at(insertPos + deleteLen)))
{
++deleteLen;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// if (_delegate && _delegate->onTextFieldDeleteBackward(this, _inputText.c_str() + len - deleteLen,
// static_cast<int>(deleteLen)))
//{
// // delegate doesn't wan't to delete backwards
// return;
// }
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// if all text deleted, show placeholder string
if (len <= deleteLen)
{
this->inputText.clear();
this->charCount = 0;
this->renderLabel->setTextColor(colorSpaceHolder);
this->renderLabel->setString(placeHolder);
__updateCursorPosition();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// this->contentDirty = true;
2019-11-24 23:15:56 +08:00
if (this->onTextModify)
this->onTextModify();
2021-12-25 10:04:45 +08:00
return;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
// set new input text
std::string text = inputText; // (inputText.c_str(), len - deleteLen);
text.erase(insertPos, deleteLen);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// __moveCursor(-1);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->setString(text);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (this->onTextModify)
this->onTextModify();
}
2019-11-24 23:15:56 +08:00
2021-12-26 23:26:34 +08:00
std::string_view TextFieldEx::getContentText()
2021-12-25 10:04:45 +08:00
{
return inputText;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::setTextColor(const Color4B& color)
{
colorText = color;
if (!this->inputText.empty())
this->renderLabel->setTextColor(colorText);
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
const Color4B& TextFieldEx::getTextColor(void) const
{
return colorText;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::setCursorColor(const Color3B& color)
{
this->cursor->setColor(color);
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
const Color3B& TextFieldEx::getCursorColor(void) const
{
return this->cursor->getColor();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
const Color4B& TextFieldEx::getPlaceholderColor() const
{
return colorSpaceHolder;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::setPlaceholderColor(const Color4B& color)
{
colorSpaceHolder = color;
if (this->inputText.empty())
this->renderLabel->setTextColor(color);
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
//////////////////////////////////////////////////////////////////////////
// properties
//////////////////////////////////////////////////////////////////////////
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// input text property
2021-12-26 23:26:34 +08:00
void TextFieldEx::setString(std::string_view text)
2021-12-25 10:04:45 +08:00
{
static char bulletString[] = {(char)0xe2, (char)0x80, (char)0xa2, (char)0x00};
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->inputText = text;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
std::string secureText;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
std::string* displayText = &this->inputText;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (!this->inputText.empty())
{
if (secureTextEntry)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
size_t length = inputText.length();
displayText = &secureText;
while (length > 0)
{
displayText->append(bulletString);
--length;
}
2019-11-24 23:15:56 +08:00
}
}
2021-12-25 10:04:45 +08:00
// if there is no input text, display placeholder instead
if (this->inputText.empty())
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
renderLabel->setTextColor(colorSpaceHolder);
renderLabel->setString(placeHolder);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
else
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
renderLabel->setTextColor(colorText);
renderLabel->setString(*displayText);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
bool bInsertAtEnd = (insertPosUtf8 == charCount);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
charCount = _calcCharCount(inputText.c_str());
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (bInsertAtEnd)
2019-11-24 23:15:56 +08:00
{
insertPosUtf8 = static_cast<int>(charCount);
insertPos = static_cast<int>(inputText.length());
cursorPos = static_cast<int>(displayText->length());
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::updateContentSize(void)
{
this->setContentSize(renderLabel->getContentSize());
}
2021-12-26 23:26:34 +08:00
std::string_view TextFieldEx::getString() const
2021-12-25 10:04:45 +08:00
{
return inputText;
}
// place holder text property
2021-12-26 23:26:34 +08:00
void TextFieldEx::setPlaceholderText(std::string_view text)
2021-12-25 10:04:45 +08:00
{
placeHolder = text;
if (inputText.empty())
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
renderLabel->setTextColor(colorSpaceHolder);
renderLabel->setString(placeHolder);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2021-12-26 23:26:34 +08:00
std::string_view TextFieldEx::getPlaceholderText() const
2021-12-25 10:04:45 +08:00
{
return placeHolder;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
// secureTextEntry
void TextFieldEx::setPasswordEnabled(bool value)
{
if (secureTextEntry != value)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
secureTextEntry = value;
this->setString(this->getString());
__updateCursorPosition();
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
bool TextFieldEx::isPasswordEnabled() const
{
return secureTextEntry;
}
const Vec2& TextFieldEx::getContentSize() const
{
// const_cast<TextFieldEx*>(this)->setContentSize(renderLabel->getContentSize());
return Node::getContentSize();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::setEnabled(bool bEnabled)
{
if (this->enabled != bEnabled)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
if (!bEnabled)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
this->closeIME();
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
this->enabled = bEnabled;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
bool TextFieldEx::isEnabled(void) const
{
return this->enabled;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
int TextFieldEx::getFontType() const
{
return _fontType;
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::__initCursor(int height, int width, const Color4B& color)
{
this->cursor = engine_inj_create_lump(Color4B(color), height, width);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->addChild(this->cursor);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
this->cursor->setPosition(Point(0, this->getContentSize().height / 2));
2022-08-08 18:02:17 +08:00
// nodes_layout::setNodeLB(this->cursor, ax::Point::ZERO);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
/*CCAction* blink = CCRepeatForever::create(
(CCActionInterval *)CCSequence::create(CCFadeOut::create(0.25f),
CCFadeIn::create(0.25f),
NULL));*/
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
__hideCursor();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
__updateCursorPosition();
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::__showCursor(void)
{
if (this->cursor)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
this->cursorVisible = true;
this->cursor->setVisible(true);
this->cursor->runAction(RepeatForever::create(Blink::create(1, 1)));
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::__hideCursor(void)
{
if (this->cursor)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
this->cursor->setVisible(false);
this->cursorVisible = false;
this->cursor->stopAllActions();
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::__updateCursorPosition(void)
{
if (this->cursor && this->insertPosUtf8 == this->charCount)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
if (0 == this->getCharCount())
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
this->cursor->setPosition(Point(0, this->getContentSize().height / 2));
}
else
{
this->cursor->setPosition(Point(renderLabel->getContentSize().width, this->getContentSize().height / 2));
2019-11-24 23:15:56 +08:00
}
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::__moveCursor(int direction)
{
/*bool checkSupport = this->renderLabel->getLetter(0) != nullptr;
if (!checkSupport)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
MessageBeep(MB_ICONHAND);
return;
}*/
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
auto newOffset = this->insertPosUtf8 + direction;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (newOffset > 0 && newOffset <= this->charCount)
{
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
std::string_view displayText;
if (!secureTextEntry)
displayText = this->getString();
else if (!this->inputText.empty())
displayText = renderLabel->getString();
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
if (direction < 0)
{
this->insertPos = static_cast<int>(internalUTF8MoveLeft(this->inputText, this->insertPos).size());
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
auto s = internalUTF8MoveLeft(displayText, this->cursorPos);
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
auto width = internalCalcStringWidth(s, this->fontName, this->fontSize);
this->cursor->setPosition(Point(width, this->getContentSize().height / 2));
this->cursorPos = static_cast<int>(s.length());
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
else
{
this->insertPos = static_cast<int>(internalUTF8MoveRight(this->inputText, this->insertPos).size());
2021-12-25 10:04:45 +08:00
auto s = internalUTF8MoveRight(displayText, this->cursorPos);
auto width = internalCalcStringWidth(s, this->fontName, this->fontSize);
this->cursor->setPosition(Point(width, this->getContentSize().height / 2));
this->cursorPos = static_cast<int>(s.length());
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
this->insertPosUtf8 = newOffset;
}
else if (newOffset == 0)
{
this->cursor->setPosition(Point(0, this->getContentSize().height / 2));
this->insertPosUtf8 = newOffset;
this->insertPos = 0;
this->cursorPos = 0;
}
else
{
// MessageBeep(0);
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
void TextFieldEx::__moveCursorTo(float x)
{ // test
// normalized x
float normalizedX = 0;
2019-11-24 23:15:56 +08:00
2021-12-25 10:04:45 +08:00
std::string_view displayText;
if (!secureTextEntry)
{
displayText = this->inputText;
}
else
{
if (!this->inputText.empty())
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
displayText = renderLabel->getString();
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
}
int length = static_cast<int>(displayText.length());
int n = static_cast<int>(this->charCount); // UTF8 char counter
2021-12-25 10:04:45 +08:00
int insertWhere = 0;
int insertWhereUtf8 = 0;
while (length > 0)
{
auto checkX = internalCalcStringWidth(displayText, this->fontName, this->fontSize);
if (x >= checkX)
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
insertWhere = length;
insertWhereUtf8 = n;
normalizedX = checkX;
break;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
// clamp backward
size_t backwardLen = 1; // default, erase 1 byte
while (0x80 == (0xC0 & displayText.at(displayText.length() - backwardLen)))
2019-11-24 23:15:56 +08:00
{
2021-12-25 10:04:45 +08:00
++backwardLen;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
--n;
displayText.remove_suffix(backwardLen);
length -= backwardLen;
2019-11-24 23:15:56 +08:00
}
2021-12-25 10:04:45 +08:00
this->insertPos = !this->secureTextEntry ? insertWhere : insertWhereUtf8;
this->cursorPos = insertWhere;
this->insertPosUtf8 = insertWhereUtf8;
this->cursor->setPosition(Point(normalizedX, this->getContentSize().height / 2));
}
}; // namespace ui
2019-11-24 23:15:56 +08:00
NS_AX_END
2019-11-24 23:15:56 +08:00
#endif