Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
54dcb05
Added rich text!
abb2k Jan 28, 2026
ef54576
remove CCObjects and changed to geode::Function
abb2k Jan 28, 2026
8c6b489
small naming changes and stuff!
abb2k Jan 28, 2026
5001acc
some styling improvements
abb2k Jan 28, 2026
2cd0a0f
some more very small changes
abb2k Jan 28, 2026
e9f1fa2
Merge remote-tracking branch 'upstream/main' into v5
abb2k Feb 2, 2026
d29597c
split rich text functionality to RichTextArea!
abb2k Feb 3, 2026
b8ab3bc
Merge remote-tracking branch 'upstream/main' into v5
abb2k Feb 3, 2026
a3c1f38
added links and clickable text keys :) (pls let me know what i can do…
abb2k Feb 3, 2026
c55bd57
Merge remote-tracking branch 'upstream/main' into rich-txt-area
xblazegmd Feb 3, 2026
5e66c45
changed fmt::localtime and moved registerRichTextKey out of impl
abb2k Feb 3, 2026
96218fa
changed localtime (again)
abb2k Feb 3, 2026
88807e9
Better pimpl
xblazegmd Feb 3, 2026
46eae0a
Merge remote-tracking branch 'upstream/main' into rich-txt-area
xblazegmd Feb 3, 2026
cfaca0b
Forgot abt this 🫠
xblazegmd Feb 3, 2026
4a44ac4
Merge branch 'v5' into rich-txt-area
xblazegmd Feb 3, 2026
d0aaad9
Merge pull request #1 from xblazegmd/rich-txt-area
abb2k Feb 3, 2026
d0f7028
Merge remote-tracking branch 'upstream/main' into v5
abb2k Feb 3, 2026
e3e0d89
Merge remote-tracking branch 'upstream/main' into v5
abb2k Feb 4, 2026
5fad745
fixed pimpl kinda but also rich text not working anymore?? :( help
abb2k Feb 4, 2026
9abf2d0
made both classes use the same m_impl variable so they actually work …
abb2k Feb 4, 2026
26194b3
Some polish
xblazegmd Feb 5, 2026
f565c01
Fix history
xblazegmd Feb 5, 2026
e9915b1
Merge remote-tracking branch 'origin/main' into rich-txt-area-fix
xblazegmd Feb 5, 2026
e5a1e46
Merge pull request #2 from xblazegmd/rich-txt-area-fix
abb2k Feb 5, 2026
392151c
Merge remote-tracking branch 'upstream/main' into v5
abb2k Feb 6, 2026
3ed3f57
rich text fixed! added wave effect key!
abb2k Feb 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions loader/include/Geode/UI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
#include "ui/SimpleAxisLayout.hpp"
#include "ui/SpacerNode.hpp"
#include "ui/TextArea.hpp"
#include "ui/RichTextArea.hpp"
#include "ui/TextInput.hpp"
#include "ui/TextRenderer.hpp"
24 changes: 24 additions & 0 deletions loader/include/Geode/cocos/actions/CCWaveAction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <cocos2d.h>

class CCWaveAction : public cocos2d::CCActionInterval
{
public:
static CCWaveAction* create(float period, float distX, float distY, float phase);

bool init(float period, float distX, float distY, float phase);

void startWithTarget(cocos2d::CCNode* target) override;

void update(float t) override;

cocos2d::CCActionInterval* reverse() override;

private:
float m_distX;
float m_distY;
float m_phase;
float m_baseX;
float m_baseY;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this and why is this

171 changes: 171 additions & 0 deletions loader/include/Geode/ui/RichTextArea.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
#pragma once

#include <Geode/ui/TextArea.hpp>
#include <memory>

namespace geode {

// abb2k wuz here :)

template <class T>
class RichTextKey;

class RichTextKeyInstanceBase {
public:
virtual ~RichTextKeyInstanceBase() = default;
virtual void applyChangesToSprite(cocos2d::CCFontSprite* spr, int index) = 0;
virtual std::string getKey() const = 0;
virtual bool isCancellation() const = 0;
virtual std::string runStrAddition() = 0;
virtual bool isButton() const = 0;
virtual void callButton(bool keyDown, cocos2d::CCFontSprite* spr, std::set<cocos2d::CCFontSprite*> const& word) = 0;
};

template <class T>
class RichTextKeyInstance final : public RichTextKeyInstanceBase {
public:
RichTextKeyInstance(RichTextKey<T>* key, T data, bool cancellation)
: m_key(std::move(key)), m_value(std::move(data)), m_cancellation(std::move(cancellation)) {}

RichTextKey<T>* m_key = nullptr;
T m_value;
bool m_cancellation = false;

void applyChangesToSprite(cocos2d::CCFontSprite* spr, int index) override;

std::string getKey() const override {
return m_key->getKey();
}

bool isCancellation() const override {
return m_cancellation;
}

std::string runStrAddition() override;

bool isButton() const override {
return m_key->m_buttonFunctionallity != NULL;
}

void callButton(bool keyDown, cocos2d::CCFontSprite* spr, std::set<cocos2d::CCFontSprite*> const& word){
if (m_key->m_buttonFunctionallity != NULL)
m_key->m_buttonFunctionallity(m_value, keyDown, spr, word);
}
};

class RichTextKeyBase {
public:
virtual ~RichTextKeyBase() = default;
virtual Result<std::shared_ptr<RichTextKeyInstanceBase>> createInstance(std::string const& value, bool cancellation) = 0;
virtual std::string getKey() const = 0;
};

template <class T>
class RichTextKey final : public RichTextKeyBase {
public:
/**
@param key The identifier name for this rich text key
@param validCheck Function to validate and parse the value string into type T (if an error is returned the key will not be processed)
@param applyToSprite Function to apply the parsed value to a font sprite (optional)
*/
RichTextKey(
std::string key,
geode::Function<Result<T>(std::string const& value)> validCheck,
geode::Function<void(
T const& value,
bool keyDown,
cocos2d::CCFontSprite* specificSpriteClicked,
std::set<cocos2d::CCFontSprite*> const& wordClicked)> buttonFunctionallity
) : m_key(std::move(key)),
m_validCheck(std::move(validCheck)),
m_buttonFunctionallity(std::move(buttonFunctionallity)) {}
/**
@param key The identifier name for this rich text key
@param validCheck Function to validate and parse the value string into type T (if an error is returned the key will not be processed)
@param applyToSprite Function to apply the parsed value to a font sprite (optional)
*/
RichTextKey(
std::string key,
geode::Function<Result<T>(std::string const& value)> validCheck,
geode::Function<void(T const& value, cocos2d::CCFontSprite* sprite, int charIndex)> applyToSprite
) : m_key(std::move(key)),
m_validCheck(std::move(validCheck)),
m_applyToSprite(std::move(applyToSprite)) {}
/**
@param key The identifier name for this rich text key
@param validCheck Function to validate and parse the value string into type T (if an error is returned the key will not be processed)
@param stringAddition Function to add a new string at the point where the key is (optional)
*/
RichTextKey(
std::string key,
geode::Function<Result<T>(std::string const& value)> validCheck,
geode::Function<std::string(T const& value)> stringAddition
) : m_key(std::move(key)),
m_validCheck(std::move(validCheck)),
m_stringAddition(std::move(stringAddition)) {}
/**
@param key The identifier name for this rich text key
@param validCheck Function to validate and parse the value string into type T (if an error is returned the key will not be processed)
@param applyToSprite Function to apply the parsed value to a font sprite (optional)
@param stringAddition Function to add a new string at the point where the key is (optional)
*/
RichTextKey(
std::string key,
geode::Function<Result<T>(std::string const& value)> validCheck,
geode::Function<void(T const& value, cocos2d::CCFontSprite* sprite, int charIndex)> applyToSprite,
geode::Function<std::string(T const& value)> stringAddition
) : m_key(std::move(key)),
m_validCheck(std::move(validCheck)),
m_applyToSprite(std::move(applyToSprite)),
m_stringAddition(std::move(stringAddition)) {}

Result<std::shared_ptr<RichTextKeyInstanceBase>> createInstance(std::string const& value, bool cancellation) override;

std::string getKey() const override {
return m_key;
}

private:
std::string m_key;

public:
geode::Function<Result<T>(std::string const& value)> m_validCheck = NULL;
geode::Function<void(T const& value, cocos2d::CCFontSprite* sprite, int charIndex)> m_applyToSprite = NULL;
geode::Function<std::string(T const& value)> m_stringAddition = NULL;
geode::Function<void(
T const& value,
bool keyDown,
cocos2d::CCFontSprite* specificSpriteClicked,
std::set<cocos2d::CCFontSprite*> const& wordClicked)> m_buttonFunctionallity = NULL;
};


class GEODE_DLL RichTextArea : public SimpleTextArea, public cocos2d::CCTouchDelegate {
public:
static RichTextArea* create(std::string text, std::string font = "chatFont.fnt", float scale = 1.0f);
static RichTextArea* create(std::string text, std::string font, float scale, float width);

void setText(std::string text) override;

template<class T>
void registerRichTextKey(std::shared_ptr<RichTextKey<T>> key);
protected:
RichTextArea();
~RichTextArea();

class RichImpl;
std::unique_ptr<SimpleTextAreaImpl> createImpl() override;
Copy link
Member

@altalk23 altalk23 Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really needed instead of just exposing protected methods to simpletextarea for richtextarea to use


private:
static RichTextArea* create(std::string font, std::string text, float scale, float width, const bool artificialWidth);

bool init(std::string font, std::string text, float scale, float width, const bool artificialWidth);

bool ccTouchBegan(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) override;

void ccTouchEnded(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) override;
void ccTouchCancelled(cocos2d::CCTouch *pTouch, cocos2d::CCEvent *pEvent) override;

RichImpl* castedImpl();
};
}
17 changes: 10 additions & 7 deletions loader/include/Geode/ui/TextArea.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#include <memory>
#include <cocos2d.h>

class SimpleTextAreaImpl;

namespace geode {
enum WrappingMode {
NO_WRAP,
Expand All @@ -24,6 +26,7 @@ namespace geode {
*
* Contact me on Discord (\@smjs) if you have any questions, suggestions or bugs.
*/

class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
public:
static SimpleTextArea* create(std::string text, std::string font = "chatFont.fnt", float scale = 1.0f);
Expand All @@ -37,7 +40,7 @@ namespace geode {
cocos2d::CCTextAlignment getAlignment();
void setWrappingMode(WrappingMode mode);
WrappingMode getWrappingMode();
void setText(std::string text);
virtual void setText(std::string text);
std::string getText();
void setMaxLines(size_t maxLines);
size_t getMaxLines();
Expand All @@ -50,17 +53,17 @@ namespace geode {
std::vector<cocos2d::CCLabelBMFont*> getLines();
float getHeight();
float getLineHeight();

protected:
SimpleTextArea();
~SimpleTextArea() override;

private:
static SimpleTextArea* create(std::string font, std::string text, float scale, float width, const bool artificialWidth);

bool init(std::string font, std::string text, float scale, float width, const bool artificialWidth);
class Impl;
virtual std::unique_ptr<SimpleTextAreaImpl> createImpl();
std::unique_ptr<SimpleTextAreaImpl> m_impl;

bool init(std::string font, std::string text, float scale, float width, const bool artificialWidth);
private:
class Impl;
std::unique_ptr<Impl> m_impl;
static SimpleTextArea* create(std::string font, std::string text, float scale, float width, const bool artificialWidth);
};
}
53 changes: 53 additions & 0 deletions loader/src/cocos2d-ext/CCWaveAction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <Geode/cocos/actions/CCWaveAction.h>

CCWaveAction* CCWaveAction::create(float period, float distX, float distY, float phase)
{
auto ret = new CCWaveAction();
if (ret && ret->init(period, distX, distY, phase))
{
ret->autorelease();
return ret;
}

CC_SAFE_DELETE(ret);
return nullptr;
}

bool CCWaveAction::init(float period, float distX, float distY, float phase)
{
if (!cocos2d::CCActionInterval::initWithDuration(period))
return false;

m_distX = distX;
m_distY = distY;
m_phase = phase;
m_baseX = NAN;
m_baseY = NAN;

return true;
}

void CCWaveAction::startWithTarget(cocos2d::CCNode* target)
{
cocos2d::CCActionInterval::startWithTarget(target);

if (!std::isnan(m_baseX)) return;

m_baseX = target->getPositionX();
m_baseY = target->getPositionY();
}

void CCWaveAction::update(float t)
{
float angle = (t * 2.f * M_PI) + m_phase;

float x = m_baseX + cosf(angle) * m_distX;
float y = m_baseY + sinf(angle) * m_distY;

m_pTarget->setPosition(x, y);
}

cocos2d::CCActionInterval* CCWaveAction::reverse()
{
return CCWaveAction::create(m_fDuration, m_distX, m_distY, -m_phase);
}
2 changes: 1 addition & 1 deletion loader/src/loader/ModMetadataImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
}
).unwrapOr("v0.0.0")
);

auto root = checkJson(impl->m_rawJSON, checkerRoot);
root.needs("geode").into(impl->m_geodeVersion);

Expand Down
Loading