add sane TextInput class

This commit is contained in:
HJfod 2024-02-16 23:11:18 +02:00
parent 83cb61bb20
commit 28f393b4de
6 changed files with 253 additions and 3 deletions

View file

@ -1 +1 @@
2.0.0-beta.19
2.0.0-beta.20

View file

@ -5,7 +5,7 @@
#include <cocos2d.h>
namespace geode {
class GEODE_DLL InputNode : public cocos2d::CCMenuItem {
class GEODE_DLL [[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]] InputNode : public cocos2d::CCMenuItem {
protected:
cocos2d::extension::CCScale9Sprite* m_bgSprite;
CCTextInputNode* m_input;

View file

@ -0,0 +1,124 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <Geode/binding/CCTextInputNode.hpp>
#include <cocos2d.h>
namespace geode {
enum class CommonFilter {
// Allow an unsigned integer
Uint,
// Allow a signed integer
Int,
// Allow a floating point number
Float,
// Allow letters, numbers, dashes, underscores, and dots
ID,
// Allow word-like characters & spaces
Name,
// Allows basically anything possible to type in an input
Any,
// Allow a hexadecimal number
Hex,
// Allow a non-URL-safe Base64 number
Base64Normal,
// Allow a URL-safe Base64 number
Base64URL,
};
GEODE_DLL const char* getCommonFilterAllowedChars(CommonFilter filter);
/**
* A single-line text input node
*/
class GEODE_DLL TextInput : public cocos2d::CCNode, public TextInputDelegate {
protected:
cocos2d::extension::CCScale9Sprite* m_bgSprite;
CCTextInputNode* m_input;
std::function<void(std::string const&)> m_onInput = nullptr;
bool init(float width, std::string const& placeholder, std::string const& font);
void textChanged(CCTextInputNode* input) override;
public:
/**
* Create a single-line text input with a background.
* Can either be used in delegate or callback mode;
* with callback mode, you don't need to deal with adding
* TextInputDelegate to your class' base list, you just install a
* callback function directly to the input itself
* @param width The width of the input
* @param placeholder Placeholder text for the input
* @param font The font to use
*/
static TextInput* create(float width, std::string const& placeholder, std::string const& font = "bigFont.fnt");
/**
* Set the placeholder label for this input
*/
void setPlaceholder(std::string const& placeholder);
/**
* Set the filter (allowed characters) for this input
* @param allowedChars String of allowed characters; each character in
* the string represents one allowed character
*/
void setFilter(std::string const& allowedChars);
/**
* Set a commonly used filter (number, text, etc.)
*/
void setCommonFilter(CommonFilter filter);
/**
* Set the maximum amount of characters for this input. Use 0 for
* infinite length
*/
void setMaxCharCount(size_t length);
/**
* Enable/disable password mode (all input characters are rendered as
* dots rather than the actual characters)
*/
void setPasswordMode(bool enable);
/**
* Set the width of the label. This does not set the maximum character
* count; use `setMaxCharCount` for that
*/
void setWidth(float width);
/**
* Install a delegate that handles input events. Removes any currently
* set direct callbacks
* @param delegate The delegate to install
* @param tag Some legacy delegates use a tag to distinguish between
* inputs; this is a convenience parameter for setting the tag of the
* internal CCTextInputNode for those cases
*/
void setDelegate(TextInputDelegate* delegate, std::optional<int> tag = std::nullopt);
/**
* Set a direct callback function that is called when the user types in
* the input. Overrides any delegate that is currently installed
* @param onInput Function to call when the user changes the value of
* the text input
*/
void setCallback(std::function<void(std::string const&)> onInput);
/**
* Hides the background of this input. Shorthand for
* `input->getBGSprite()->setVisible(false)`
*/
void hideBG();
/**
* Set the value of the input
* @param str The new text of the input
* @param triggerCallback Whether this should trigger the callback
* function / delegate's textChanged event or not
*/
void setString(std::string const& str, bool triggerCallback = false);
/**
* Get the current value of the input
*/
std::string getString() const;
CCTextInputNode* getInputNode() const;
cocos2d::extension::CCScale9Sprite* getBGSprite() const;
};
}

View file

@ -35,7 +35,7 @@ void CopySizeLayout::apply(CCNode* in) {
// Prevent accidental infinite loop
if (node == in) continue;
node->ignoreAnchorPointForPosition(false);
node->setContentSize(in->getContentSize());
node->setContentSize(in->getContentSize() * ccp(1 / in->getScaleX(), 1 / in->getScaleY()));
node->setPosition(in->getContentSize() / 2);
node->updateLayout();
}

View file

@ -61,7 +61,10 @@ CCScale9Sprite* InputNode::getBG() const {
}
void InputNode::activate() {
auto const size = m_input->getContentSize();
auto const pos = m_input->convertToNodeSpace(getMousePos()) + m_input->m_textField->getAnchorPoint() * size;
m_input->onClickTrackNode(true);
m_input->updateCursorPosition(pos, { CCPointZero, size });
}
void InputNode::setEnabled(bool enabled) {

View file

@ -0,0 +1,123 @@
#include <Geode/binding/CCTextInputNode.hpp>
#include <Geode/binding/TextInputDelegate.hpp>
#include <Geode/ui/TextInput.hpp>
using namespace geode::prelude;
const char* geode::getCommonFilterAllowedChars(CommonFilter filter) {
switch (filter) {
default:
case CommonFilter::Uint: return "0123456789";
case CommonFilter::Int: return "-0123456789";
case CommonFilter::Float: return "-.0123456789";
case CommonFilter::ID: return "abcdefghijklmnopqrstuvwxyz0123456789-_.";
case CommonFilter::Name: return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_ ";
case CommonFilter::Any: return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-+/\\&$%^~*\'\"{}()[]<>=!?@,;.:|• ";
case CommonFilter::Hex: return "0123456789abcdefABCDEF";
case CommonFilter::Base64Normal: return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/=";
case CommonFilter::Base64URL: return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_=";
}
}
bool TextInput::init(float width, std::string const& placeholder, std::string const& font) {
if (!CCNode::init())
return false;
constexpr float HEIGHT = 30.f;
this->setContentSize({ width, HEIGHT });
this->setAnchorPoint({ .5f, .5f });
m_bgSprite = cocos2d::extension::CCScale9Sprite::create("square02b_001.png", { 0, 0, 80, 80 });
m_bgSprite->setScale(.5f);
m_bgSprite->setColor({ 0, 0, 0 });
m_bgSprite->setOpacity(90);
m_bgSprite->setContentSize({ width * 2, HEIGHT * 2 });
this->addChildAtPosition(m_bgSprite, cocos2d::Anchor::Center);
m_input = CCTextInputNode::create(width - 10.f, HEIGHT, placeholder.c_str(), "Thonburi", 24, font.c_str());
m_input->setLabelPlaceholderColor({ 150, 150, 150 });
m_input->setLabelPlaceholderScale(.6f);
m_input->setMaxLabelScale(.6f);
this->addChildAtPosition(m_input, cocos2d::Anchor::Center);
handleTouchPriority(this);
return true;
}
TextInput* TextInput::create(float width, std::string const& placeholder, std::string const& font) {
auto ret = new TextInput();
if (ret && ret->init(width, placeholder, font)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
void TextInput::textChanged(CCTextInputNode* input) {
if (m_onInput) {
m_onInput(input->getString());
}
}
void TextInput::setPlaceholder(std::string const& placeholder) {
m_input->m_caption = placeholder;
m_input->refreshLabel();
}
void TextInput::setFilter(std::string const& allowedChars) {
m_input->m_allowedChars = allowedChars;
}
void TextInput::setCommonFilter(CommonFilter filter) {
this->setFilter(getCommonFilterAllowedChars(filter));
}
void TextInput::setMaxCharCount(size_t length) {
m_input->m_maxLabelLength = length == 0 ? 9999999 : length;
}
void TextInput::setPasswordMode(bool enable) {
m_input->m_usePasswordChar = enable;
m_input->refreshLabel();
}
void TextInput::setWidth(float width) {
m_input->m_maxLabelWidth = width - 10;
m_input->setContentWidth(width * 2);
m_bgSprite->setContentWidth(width * 2);
}
void TextInput::setDelegate(TextInputDelegate* delegate, std::optional<int> tag) {
m_input->setDelegate(delegate);
m_onInput = nullptr;
if (tag.has_value()) {
m_input->setTag(tag.value());
}
}
void TextInput::setCallback(std::function<void(std::string const&)> onInput) {
this->setDelegate(this);
m_onInput = onInput;
}
void TextInput::hideBG() {
m_bgSprite->setVisible(false);
}
void TextInput::setString(std::string const& str, bool triggerCallback) {
auto oldDelegate = m_input->m_delegate;
// Avoid triggering the callback
m_input->m_delegate = nullptr;
m_input->setString(str);
m_input->m_delegate = oldDelegate;
if (triggerCallback && m_input->m_delegate) {
m_input->m_delegate->textChanged(m_input);
}
}
std::string TextInput::getString() const {
return m_input->getString();
}
CCTextInputNode* TextInput::getInputNode() const {
return m_input;
}
CCScale9Sprite* TextInput::getBGSprite() const {
return m_bgSprite;
}