diff --git a/loader/include/Geode/ui/ColorPickPopup.hpp b/loader/include/Geode/ui/ColorPickPopup.hpp new file mode 100644 index 00000000..49d3f17d --- /dev/null +++ b/loader/include/Geode/ui/ColorPickPopup.hpp @@ -0,0 +1,55 @@ +#include "Popup.hpp" +#include "InputNode.hpp" + +namespace geode { + class ColorPickPopupDelegate { + public: + virtual void updateColor(cocos2d::ccColor4B const& color) {} + }; + + class ColorPickPopup : + public Popup<cocos2d::ccColor4B const&, bool>, + public cocos2d::extension::ColorPickerDelegate, + public TextInputDelegate + { + protected: + cocos2d::ccColor4B m_color; + cocos2d::ccColor4B m_originalColor; + cocos2d::extension::CCControlColourPicker* m_picker; + Slider* m_opacitySlider = nullptr; + InputNode* m_rInput; + InputNode* m_gInput; + InputNode* m_bInput; + InputNode* m_hexInput; + InputNode* m_opacityInput = nullptr; + ColorPickPopupDelegate* m_delegate = nullptr; + cocos2d::CCSprite* m_newColorSpr; + CCMenuItemSpriteExtra* m_resetBtn; + + static constexpr auto TAG_OPACITY_INPUT = 0; + static constexpr auto TAG_R_INPUT = 1; + static constexpr auto TAG_G_INPUT = 2; + static constexpr auto TAG_B_INPUT = 3; + static constexpr auto TAG_HEX_INPUT = 4; + + bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override; + + void onOpacitySlider(cocos2d::CCObject* sender); + void onReset(cocos2d::CCObject* sender); + + void textChanged(CCTextInputNode* input) override; + void colorValueChanged(cocos2d::ccColor3B color) override; + + void updateState(cocos2d::CCNode* except = nullptr); + + static ColorPickPopup* create(cocos2d::ccColor4B const& color, bool isRGBA); + + public: + static ColorPickPopup* create(bool isRGBA); + static ColorPickPopup* create(cocos2d::ccColor3B const& color); + static ColorPickPopup* create(cocos2d::ccColor4B const& color); + + void setColorTarget(cocos2d::CCSprite* spr); + void setDelegate(ColorPickPopupDelegate* delegate); + }; +} diff --git a/loader/resources/hue-sliders.png b/loader/resources/hue-sliders.png new file mode 100644 index 00000000..7c2b93bf Binary files /dev/null and b/loader/resources/hue-sliders.png differ diff --git a/loader/src/load/Setting.cpp b/loader/src/load/Setting.cpp index 4944902d..4b722453 100644 --- a/loader/src/load/Setting.cpp +++ b/loader/src/load/Setting.cpp @@ -22,7 +22,8 @@ Result<std::shared_ptr<Setting>> Setting::parse( case hash("int"): return IntSetting::parse(key, obj); case hash("float"): return FloatSetting::parse(key, obj); case hash("string"): return StringSetting::parse(key, obj); - case hash("color"): return ColorSetting::parse(key, obj); + case hash("rgb"): case hash("color"): + return ColorSetting::parse(key, obj); case hash("rgba"): return ColorAlphaSetting::parse(key, obj); default: return Err( "Setting \"" + key + "\" has unknown type \"" + type + "\"" diff --git a/loader/src/ui/internal/settings/GeodeSettingNode.cpp b/loader/src/ui/internal/settings/GeodeSettingNode.cpp index 9dd5bc70..c9f165be 100644 --- a/loader/src/ui/internal/settings/GeodeSettingNode.cpp +++ b/loader/src/ui/internal/settings/GeodeSettingNode.cpp @@ -98,6 +98,11 @@ void ColorSettingNode::valueChanged(bool updateText) { m_colorSpr->setColor(m_uncommittedValue); } +void ColorSettingNode::updateColor(ccColor4B const& color) { + m_uncommittedValue = to3B(color); + this->valueChanged(true); +} + bool ColorSettingNode::setup(std::shared_ptr<ColorSetting> setting, float width) { m_colorSpr = ColorChannelSprite::create(); m_colorSpr->setColor(m_uncommittedValue); @@ -105,7 +110,10 @@ bool ColorSettingNode::setup(std::shared_ptr<ColorSetting> setting, float width) auto button = CCMenuItemSpriteExtra::create( m_colorSpr, this, makeMenuSelector([this](CCObject*) { - ColorPickPopup<ColorSettingNode>::create(this)->show(); + auto popup = ColorPickPopup::create(m_uncommittedValue); + popup->setDelegate(this); + popup->setColorTarget(m_colorSpr); + popup->show(); }) ); button->setPositionX(-10.f); @@ -122,6 +130,11 @@ void ColorAlphaSettingNode::valueChanged(bool updateText) { m_colorSpr->updateOpacity(m_uncommittedValue.a / 255.f); } +void ColorAlphaSettingNode::updateColor(ccColor4B const& color) { + m_uncommittedValue = color; + this->valueChanged(true); +} + bool ColorAlphaSettingNode::setup(std::shared_ptr<ColorAlphaSetting> setting, float width) { m_colorSpr = ColorChannelSprite::create(); m_colorSpr->setColor(to3B(m_uncommittedValue)); @@ -130,7 +143,10 @@ bool ColorAlphaSettingNode::setup(std::shared_ptr<ColorAlphaSetting> setting, fl auto button = CCMenuItemSpriteExtra::create( m_colorSpr, this, makeMenuSelector([this](CCObject*) { - ColorPickPopup<ColorAlphaSettingNode>::create(this)->show(); + auto popup = ColorPickPopup::create(m_uncommittedValue); + popup->setDelegate(this); + popup->setColorTarget(m_colorSpr); + popup->show(); }) ); button->setPositionX(-10.f); diff --git a/loader/src/ui/internal/settings/GeodeSettingNode.hpp b/loader/src/ui/internal/settings/GeodeSettingNode.hpp index 98b0217d..d3dc6dc3 100644 --- a/loader/src/ui/internal/settings/GeodeSettingNode.hpp +++ b/loader/src/ui/internal/settings/GeodeSettingNode.hpp @@ -9,12 +9,10 @@ #include <Geode/utils/string.hpp> #include <Geode/utils/cocos.hpp> #include <Geode/ui/Popup.hpp> +#include <Geode/ui/ColorPickPopup.hpp> USE_GEODE_NAMESPACE(); -template<class T> -class ColorPickPopup; - namespace { template<class Num> Num parseNumForInput(std::string const& str) { @@ -497,155 +495,27 @@ protected: }; class ColorSettingNode : - public GeodeSettingNode<ColorSettingNode, ColorSetting> + public GeodeSettingNode<ColorSettingNode, ColorSetting>, + public ColorPickPopupDelegate { protected: ColorChannelSprite* m_colorSpr; - friend class ColorPickPopup<ColorSettingNode>; - void valueChanged(bool updateText) override; + void updateColor(ccColor4B const& color) override; bool setup(std::shared_ptr<ColorSetting> setting, float width) override; }; class ColorAlphaSettingNode : - public GeodeSettingNode<ColorAlphaSettingNode, ColorAlphaSetting> + public GeodeSettingNode<ColorAlphaSettingNode, ColorAlphaSetting>, + public ColorPickPopupDelegate { protected: ColorChannelSprite* m_colorSpr; - friend class ColorPickPopup<ColorAlphaSettingNode>; - void valueChanged(bool updateText) override; + void updateColor(ccColor4B const& color) override; bool setup(std::shared_ptr<ColorAlphaSetting> setting, float width) override; }; - -template<class T> -class ColorPickPopup : - public Popup<T*>, - public ColorPickerDelegate, - public TextInputDelegate -{ -protected: - T* m_settingNode; - Slider* m_opacitySlider = nullptr; - InputNode* m_opacityInput = nullptr; - - static constexpr auto TAG_OPACITY_INPUT = 0; - static constexpr auto TAG_R_INPUT = 1; - static constexpr auto TAG_G_INPUT = 2; - static constexpr auto TAG_B_INPUT = 3; - static constexpr auto TAG_HEX_INPUT = 4; - static constexpr auto IS_RGBA = std::is_same_v< - decltype(T::m_uncommittedValue), - ccColor4B - >; - - FLAlertLayer* asAlert() { - return static_cast<FLAlertLayer*>(this); - } - - bool setup(T* node) override { - asAlert()->m_noElasticity = true; - - m_settingNode = node; - - auto winSize = CCDirector::sharedDirector()->getWinSize(); - - auto picker = CCControlColourPicker::colourPicker(); - if constexpr (IS_RGBA) { - picker->setColorValue(to3B(node->m_uncommittedValue)); - } else { - picker->setColorValue(node->m_uncommittedValue); - } - picker->setColorTarget(node->m_colorSpr); - picker->setPosition( - winSize.width / 2, - winSize.height / 2 + (IS_RGBA ? 20.f : 0.f) - ); - picker->setDelegate(this); - asAlert()->m_mainLayer->addChild(picker); - - if constexpr (IS_RGBA) { - m_opacitySlider = Slider::create( - this, - menu_selector(ColorPickPopup::onOpacity), - .75f - ); - m_opacitySlider->setPosition( - winSize.width / 2 - 30.f, - winSize.height / 2 - 90.f - ); - m_opacitySlider->setValue(node->m_uncommittedValue.a / 255.f); - m_opacitySlider->updateBar(); - asAlert()->m_mainLayer->addChild(m_opacitySlider); - - m_opacityInput = InputNode::create(45.f, "0.00"); - m_opacityInput->setPosition(85.f, -90.f); - m_opacityInput->setString(numToString( - node->m_uncommittedValue.a / 255.f, 2 - )); - m_opacityInput->getInput()->setTag(TAG_OPACITY_INPUT); - m_opacityInput->getInput()->setDelegate(this); - asAlert()->m_buttonMenu->addChild(m_opacityInput); - } - - return true; - } - - void textChanged(CCTextInputNode* input) override { - switch (input->getTag()) { - case TAG_OPACITY_INPUT: { - if constexpr (IS_RGBA) { - try { - auto value = std::stof(input->getString()); - m_settingNode->m_uncommittedValue.a = static_cast<GLubyte>( - value * 255.f - ); - m_opacitySlider->setValue(value); - m_opacitySlider->updateBar(); - m_settingNode->valueChanged(true); - } catch(...) {} - } - } break; - - default: break; - } - } - - void onOpacity(CCObject* sender) { - if constexpr (IS_RGBA) { - m_settingNode->m_uncommittedValue.a = static_cast<GLubyte>( - static_cast<SliderThumb*>(sender)->getValue() * 255.f - ); - m_opacityInput->setString(numToString( - m_settingNode->m_uncommittedValue.a / 255.f, 3 - )); - m_settingNode->valueChanged(true); - } - } - - void colorValueChanged(ccColor3B color) override { - if constexpr (IS_RGBA) { - m_settingNode->m_uncommittedValue = to4B( - color, m_settingNode->m_uncommittedValue.a - ); - } else { - m_settingNode->m_uncommittedValue = color; - } - m_settingNode->valueChanged(true); - } - -public: - static ColorPickPopup* create(T* node) { - auto ret = new ColorPickPopup(); - if (ret && ret->init(250.f, (IS_RGBA ? 250.f : 200.f), node)) { - ret->autorelease(); - return ret; - } - CC_SAFE_DELETE(ret); - return nullptr; - } -}; diff --git a/loader/src/ui/nodes/ColorPickPopup.cpp b/loader/src/ui/nodes/ColorPickPopup.cpp new file mode 100644 index 00000000..7595718d --- /dev/null +++ b/loader/src/ui/nodes/ColorPickPopup.cpp @@ -0,0 +1,289 @@ +#include <Geode/ui/ColorPickPopup.hpp> +#include <Geode/utils/operators.hpp> + +USE_GEODE_NAMESPACE(); + +static GLubyte parseInt(const char* str) { + try { + return static_cast<GLubyte>(std::stoi(str)); + } catch(...) { + return 255; + } +} + +static GLubyte parseFloat(const char* str) { + try { + return static_cast<GLubyte>(std::stof(str) * 255.f); + } catch(...) { + return 255; + } +} + +bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) { + m_noElasticity = true; + m_color = color; + m_originalColor = color; + + auto winSize = CCDirector::sharedDirector()->getWinSize(); + + this->setTitle("Select Color"); + + // picker + + m_picker = CCControlColourPicker::colourPicker(); + m_picker->setPosition( + winSize.width / 2 - 45.f, + winSize.height / 2 + (isRGBA ? 25.f : 0.f) + ); + m_picker->setDelegate(this); + m_mainLayer->addChild(m_picker); + + // color difference + + auto oldColorSpr = CCSprite::createWithSpriteFrameName("whiteSquare60_001.png"); + oldColorSpr->setPosition({ + winSize.width / 2 - 165.f, + winSize.height / 2 + 15.f + }); + oldColorSpr->setColor(to3B(m_color)); + m_mainLayer->addChild(oldColorSpr); + + m_newColorSpr = CCSprite::createWithSpriteFrameName("whiteSquare60_001.png"); + m_newColorSpr->setPosition({ + winSize.width / 2 - 165.f, + winSize.height / 2 - 15.f + }); + m_newColorSpr->setColor(to3B(m_color)); + m_mainLayer->addChild(m_newColorSpr); + + auto resetBtnSpr = ButtonSprite::create( + CCSprite::createWithSpriteFrameName("reset-gold.png"_spr), + 0x20, true, 0.f, "GJ_button_01.png", 1.25f + ); + resetBtnSpr->setScale(.6f); + + m_resetBtn = CCMenuItemSpriteExtra::create( + resetBtnSpr, this, menu_selector(ColorPickPopup::onReset) + ); + m_resetBtn->setPosition({ -165.f, -50.f }); + m_buttonMenu->addChild(m_resetBtn); + + // r + + auto rText = CCLabelBMFont::create("R", "goldFont.fnt"); + rText->setPosition( + winSize.width / 2 + 75.f, + winSize.height / 2 + (isRGBA ? 60.f : 35.f) + ); + rText->setScale(.55f); + m_mainLayer->addChild(rText); + + m_rInput = InputNode::create(50.f, "R"); + m_rInput->setPosition(75.f, (isRGBA ? 40.f : 15.f)); + m_rInput->setScale(.7f); + m_rInput->getInput()->setTag(TAG_R_INPUT); + m_rInput->getInput()->setDelegate(this); + m_buttonMenu->addChild(m_rInput); + + // g + + auto gText = CCLabelBMFont::create("G", "goldFont.fnt"); + gText->setPosition( + winSize.width / 2 + 115.f, + winSize.height / 2 + (isRGBA ? 60.f : 35.f) + ); + gText->setScale(.55f); + m_mainLayer->addChild(gText); + + m_gInput = InputNode::create(50.f, "G"); + m_gInput->setPosition(115.f, (isRGBA ? 40.f : 15.f)); + m_gInput->setScale(.7f); + m_gInput->getInput()->setTag(TAG_G_INPUT); + m_gInput->getInput()->setDelegate(this); + m_buttonMenu->addChild(m_gInput); + + // b + + auto bText = CCLabelBMFont::create("B", "goldFont.fnt"); + bText->setPosition( + winSize.width / 2 + 155.f, + winSize.height / 2 + (isRGBA ? 60.f : 35.f) + ); + bText->setScale(.55f); + m_mainLayer->addChild(bText); + + m_bInput = InputNode::create(50.f, "B"); + m_bInput->setPosition(155.f, (isRGBA ? 40.f : 15.f)); + m_bInput->setScale(.7f); + m_bInput->getInput()->setTag(TAG_B_INPUT); + m_bInput->getInput()->setDelegate(this); + m_buttonMenu->addChild(m_bInput); + + // hex + + auto hexText = CCLabelBMFont::create("Hex", "goldFont.fnt"); + hexText->setPosition( + winSize.width / 2 + 115.f, + winSize.height / 2 + (isRGBA ? 20.f : -5.f) + ); + hexText->setScale(.55f); + m_mainLayer->addChild(hexText); + + m_hexInput = InputNode::create(165.f, "Hex"); + m_hexInput->setPosition(115.f, (isRGBA ? 0.f : -25.f)); + m_hexInput->setScale(.7f); + m_hexInput->getInput()->setTag(TAG_HEX_INPUT); + m_hexInput->getInput()->setDelegate(this); + m_buttonMenu->addChild(m_hexInput); + + if (isRGBA) { + auto opacityText = CCLabelBMFont::create("Opacity", "goldFont.fnt"); + opacityText->setPosition( + winSize.width / 2 - 30.f, + winSize.height / 2 - 70.f + ); + opacityText->setScale(.55f); + m_mainLayer->addChild(opacityText); + + m_opacitySlider = Slider::create( + this, + menu_selector(ColorPickPopup::onOpacitySlider), + .75f + ); + m_opacitySlider->setPosition( + winSize.width / 2 - 30.f, + winSize.height / 2 - 90.f + ); + m_opacitySlider->setValue(color.a / 255.f); + m_opacitySlider->updateBar(); + m_mainLayer->addChild(m_opacitySlider); + + m_opacityInput = InputNode::create(60.f, "0.00"); + m_opacityInput->setPosition(85.f, -90.f); + m_opacityInput->setScale(.7f); + m_opacityInput->getInput()->setTag(TAG_OPACITY_INPUT); + m_opacityInput->getInput()->setDelegate(this); + m_buttonMenu->addChild(m_opacityInput); + } + + this->updateState(); + + auto okBtnSpr = ButtonSprite::create("OK"); + okBtnSpr->setScale(.7f); + + auto okBtn = CCMenuItemSpriteExtra::create( + okBtnSpr, this, menu_selector(ColorPickPopup::onClose) + ); + okBtn->setPosition(.0f, -m_size.height / 2 + 25.f); + m_buttonMenu->addChild(okBtn); + + return true; +} + +void ColorPickPopup::updateState(CCNode* except) { + #define IF_NOT_EXCEPT(inp, value)\ + if (inp->getInput() != except) {\ + inp->getInput()->setDelegate(nullptr);\ + inp->setString(value);\ + inp->getInput()->setDelegate(this);\ + } + + IF_NOT_EXCEPT(m_rInput, numToString<int>(m_color.r)); + IF_NOT_EXCEPT(m_gInput, numToString<int>(m_color.g)); + IF_NOT_EXCEPT(m_bInput, numToString<int>(m_color.b)); + IF_NOT_EXCEPT(m_hexInput, cc3bToHexString(to3B(m_color))); + if (m_opacityInput) { + IF_NOT_EXCEPT( + m_opacityInput, + numToString(m_color.a / 255.f, 2) + ); + } + if (m_opacitySlider) { + m_opacitySlider->setValue(m_color.a / 255.f); + m_opacitySlider->updateBar(); + } + if (m_picker != except) { + m_picker->setDelegate(nullptr); + m_picker->setColorValue(to3B(m_color)); + m_picker->setDelegate(this); + } + m_resetBtn->setVisible(m_originalColor != m_color); + m_newColorSpr->setColor(to3B(m_color)); + if (m_delegate) m_delegate->updateColor(m_color); +} + +void ColorPickPopup::onOpacitySlider(CCObject* sender) { + m_color.a = static_cast<GLubyte>( + static_cast<SliderThumb*>(sender)->getValue() * 255.f + ); + this->updateState(); +} + +void ColorPickPopup::onReset(CCObject*) { + m_color = m_originalColor; + this->updateState(); +} + +void ColorPickPopup::textChanged(CCTextInputNode* input) { + if (input->getString() && strlen(input->getString())) { + switch (input->getTag()) { + case TAG_HEX_INPUT: { + if (auto color = cc3bFromHexString(input->getString())) { + m_color.r = color.value().r; + m_color.g = color.value().g; + m_color.b = color.value().b; + } + } break; + + case TAG_OPACITY_INPUT: { + m_color.a = parseFloat(input->getString()); + } break; + + case TAG_R_INPUT: m_color.r = parseInt(input->getString()); break; + case TAG_G_INPUT: m_color.g = parseInt(input->getString()); break; + case TAG_B_INPUT: m_color.b = parseInt(input->getString()); break; + + default: break; + } + } + this->updateState(input); +} + +void ColorPickPopup::colorValueChanged(ccColor3B color) { + m_color.r = color.r; + m_color.g = color.g; + m_color.b = color.b; + this->updateState(m_picker); +} + +void ColorPickPopup::setDelegate(ColorPickPopupDelegate* delegate) { + m_delegate = delegate; +} + +void ColorPickPopup::setColorTarget(cocos2d::CCSprite* spr) { + m_picker->setColorTarget(spr); +} + +ColorPickPopup* ColorPickPopup::create(ccColor4B const& color, bool isRGBA) { + auto ret = new ColorPickPopup(); + if (ret && ret->init( + 380.f, (isRGBA ? 290.f : 240.f), color, isRGBA + )) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +ColorPickPopup* ColorPickPopup::create(bool isRGBA) { + return ColorPickPopup::create({ 255, 255, 255, 255 }, isRGBA); +} + +ColorPickPopup* ColorPickPopup::create(ccColor3B const& color) { + return ColorPickPopup::create(to4B(color), false); +} + +ColorPickPopup* ColorPickPopup::create(ccColor4B const& color) { + return ColorPickPopup::create(color, true); +}