Compare commits

...

11 commits

Author SHA1 Message Date
matcool
9fe3d133e9 beta.1 perhaps
Some checks failed
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Has been cancelled
2024-11-15 17:15:17 -03:00
matcool
3081164600 only send delegate update on closing ColorPickPopup 2024-11-15 16:45:06 -03:00
matcool
bebc7b4074 use fmtlib instead of sstream in numToString and intToHex 2024-11-15 16:43:36 -03:00
matcool
5645399a9f allow building loader in debug mode on windows 2024-11-15 15:38:14 -03:00
matcool
1a201e1d65 pimpl ColorPickPopup 2024-11-15 15:27:22 -03:00
matcool
5592ef68c3 disable forward compat msgbox as it confuses users 2024-11-15 14:55:34 -03:00
matcool
da92090108 change ModSettingsManager::save return type 2024-11-15 14:55:34 -03:00
B1rtek
64d9a289a3
Web support for multiple headers with same name (#1150)
Used for Set-Cookie, for example
2024-11-15 12:40:11 -03:00
matcool
fb504cbf83 theres no need for gdstring.lib since 2.206 2024-11-15 11:58:33 -03:00
matcool
9834cb2a22 fix dangling string crash on modpopup 2024-11-15 11:56:46 -03:00
slideglide
ecec11fa93
Fix every "succes" spelling mistakes I could find (#1151)
* Fix spelling mistakes

* Fix spelling mistake

* Fix spelling mistakes

* Fix spelling mistake

* Fix spelling mistake
2024-11-15 11:53:48 +01:00
20 changed files with 217 additions and 201 deletions

View file

@ -1,15 +1,25 @@
# Geode Changelog
## v4.0.0-alpha.2
## v4.0.0-beta.1
* Button to manually install mods from files (e881dc5)
* Add `ModRequestedAction::Update` (e881dc5)
* Add `ModMetadata::checkGeodeVersion` and `ModMetadata::checkTargetVersions` (e881dc5)
* Add `geode::createModLogo` for creating a logo from a `.geode` package (e881dc5)
* Tags now use names provided by the server (893b03e)
* Add web support for multiple request headers with same name (#1150)
* Fix `Task::chain` using the wrong type in the impl (22a11b9)
* Fix installing mods not checking the current version (#1148)
* Fix searching for mods ignoring geode and gd version (#1153)
* Fix crash when checking tags (01807fe)
* Fix 'Outdated' label being visible while updating (6679a69)
* Fix log nesting issue (0e8d4c6)
* Remove forward compat message box as it confuses users (5592ef6)
* Fix crash on opening mod changelogs (9834cb2)
* Make `ColorPickPopup` pimpl (1a201e1)
* Fix lag issue in `ColorPickPopup` (3081164)
* Change return type of `ModSettingsManager::save` (da92090)
* Fix every misspelling of successfully (#1151)
* Allow building geode itself in debug mode (5645399)
## v4.0.0-alpha.1
* Support for the 2.2074 update

View file

@ -1 +1 @@
4.0.0-alpha.1
4.0.0-beta.1

View file

@ -72,19 +72,27 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Win64")
target_link_libraries(${PROJECT_NAME} INTERFACE
${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib
${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
${GEODE_LOADER_PATH}/include/link/win64/glew32.lib
${GEODE_LOADER_PATH}/include/link/win64/gdstring.lib
${GEODE_LOADER_PATH}/include/link/win64/fmod.lib
opengl32
)
if (PROJECT_IS_TOP_LEVEL AND CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(${PROJECT_NAME} INTERFACE
${GEODE_LOADER_PATH}/include/link/win64/gd-libcurl.lib
)
else()
target_link_libraries(${PROJECT_NAME} INTERFACE
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
)
endif()
# Windows links against .lib and not .dll
set(GEODE_OUTPUT_NAME "Geode")
set(GEODE_PLATFORM_BINARY "Geode.lib")

View file

@ -208,9 +208,9 @@ namespace cocos2d
/**
* Custom function added for geode; returns if the
* zip file was succesfully decoded.
* zip file was successfully decoded.
*
* @return true if the zip was succesfully loaded,
* @return true if the zip was successfully loaded,
* false otherwise.
*
* @since geode v1.0.0

View file

@ -40,12 +40,8 @@ namespace geode {
* The format of the savedata will be an object with the keys being
* setting IDs and then the values the values of the saved settings
* @note If saving a setting fails, it will log a warning to the console
* @warning This will overwrite the whole `json` parameter - be sure to
* pass the full settings savedata to `load()` so you can be sure that
* unregistered custom settings' saved values don't disappear!
* @todo in v4: make this return the value instead lol
*/
void save(matjson::Value& json);
matjson::Value save();
/**
* Get the savedata for settings, aka the JSON object that contains all

View file

@ -13,7 +13,7 @@ namespace geode {
virtual void updateColor(cocos2d::ccColor4B const& color) {}
};
// todo in v4: make this pimpl and maybe use events over the delegate?
// todo in v4: maybe use events over the delegate?
// thing with events is that if you just filter via ColorPickPopup* it
// won't work unless you automatically detach the filter when closing the
// popup (otherwise opening another popup really quickly will just be
@ -24,18 +24,8 @@ namespace geode {
public cocos2d::extension::ColorPickerDelegate,
public TextInputDelegate {
protected:
cocos2d::ccColor4B m_color;
cocos2d::ccColor4B m_originalColor;
cocos2d::extension::CCControlColourPicker* m_picker;
Slider* m_opacitySlider = nullptr;
TextInput* m_rInput;
TextInput* m_gInput;
TextInput* m_bInput;
TextInput* m_hexInput;
TextInput* m_opacityInput = nullptr;
ColorPickPopupDelegate* m_delegate = nullptr;
cocos2d::CCSprite* m_newColorSpr;
CCMenuItemSpriteExtra* m_resetBtn;
class Impl;
std::unique_ptr<Impl> m_impl;
static constexpr auto TAG_OPACITY_INPUT = 0;
static constexpr auto TAG_R_INPUT = 1;
@ -43,10 +33,13 @@ namespace geode {
static constexpr auto TAG_B_INPUT = 3;
static constexpr auto TAG_HEX_INPUT = 4;
ColorPickPopup();
~ColorPickPopup();
bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override;
void onOpacitySlider(cocos2d::CCObject* sender);
void onReset(cocos2d::CCObject* sender);
void onClose(cocos2d::CCObject* sender) override;
void textChanged(CCTextInputNode* input) override;
void colorValueChanged(cocos2d::ccColor3B color) override;

View file

@ -95,7 +95,7 @@ namespace geode {
enum class Status {
/// The task is still running or waiting to start
Pending,
/// The task has succesfully finished
/// The task has successfully finished
Finished,
/// The task has been cancelled
Cancelled,
@ -1032,4 +1032,4 @@ auto operator co_await(geode::Task<T, P> task) {
template <class T, class P, class... Args>
struct std::coroutine_traits<geode::Task<T, P>, Args...> {
using promise_type = geode::geode_internal::TaskPromise<T, P>;
};
};

View file

@ -850,7 +850,7 @@ namespace geode::cocos {
* @param permissive If true, strings like "f" are considered valid
* representations of the color white. Useful for UIs that allow entering
* a hex color. Empty strings evaluate to pure white
* @returns A ccColor3B if it could be succesfully parsed, or an error
* @returns A ccColor3B if it could be successfully parsed, or an error
* indicating the failure reason
*/
GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false);
@ -863,7 +863,7 @@ namespace geode::cocos {
* @param permissive If true, strings like "f" are considered valid
* representations of the color white. Useful for UIs that allow entering
* a hex color. Empty strings evaluate to pure white
* @returns A ccColor4B if it could be succesfully parsed, or an error
* @returns A ccColor4B if it could be successfully parsed, or an error
* indicating the failure reason
*/
GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false);

View file

@ -5,7 +5,6 @@
#include "../DefaultInclude.hpp"
#include <chrono>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <filesystem>
@ -64,9 +63,7 @@ namespace geode {
template <typename T>
std::string intToHex(T i) {
std::stringstream stream;
stream << std::showbase << std::setbase(16) << (uint64_t)i;
return stream.str();
return fmt::format("{:#x}", i);
}
/**
@ -75,15 +72,16 @@ namespace geode {
* @param num Number to convert to string
* @param precision Precision of the converted number
* @returns Number as string
* @note Precision has no effect on integers
*/
template <class Num>
std::string numToString(Num num, size_t precision = 0) {
std::stringstream ss;
if (precision) {
ss << std::fixed << std::setprecision(precision);
if constexpr (std::is_floating_point_v<Num>) {
if (precision) {
return fmt::format("{:.{}f}", num, precision);
}
}
ss << num;
return ss.str();
return fmt::to_string(num);
}
/**

View file

@ -83,6 +83,15 @@ namespace geode::utils::web {
std::vector<std::string> headers() const;
std::optional<std::string> header(std::string_view name) const;
/**
* Retrieves a list of all headers from the response with a given name - there can be
* multiple headers with the same name, such as Set-Cookie, with each cookie in a separate
* header
* @param name name of the header
* @return std::optional<std::vector<std::string>>
*/
std::optional<std::vector<std::string>> getAllHeadersNamed(std::string_view name) const;
};
class GEODE_DLL WebProgress final {
@ -286,9 +295,9 @@ namespace geode::utils::web {
/**
* Gets the request headers
*
* @return std::unordered_map<std::string, std::string>
* @return std::unordered_map<std::string, std::vector<std::string>>
*/
std::unordered_map<std::string, std::string> getHeaders() const;
std::unordered_map<std::string, std::vector<std::string>> getHeaders() const;
/**
* Gets the parameters inside the URL

Binary file not shown.

View file

@ -67,13 +67,13 @@ void tryShowForwardCompat() {
return;
// TODO: change text later
console::messageBox(
"Forward Compatibility Warning",
"Geode is running in a newer version of GD than Geode targets.\n"
"UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
"However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
Severity::Warning
);
// console::messageBox(
// "Forward Compatibility Warning",
// "Geode is running in a newer version of GD than Geode targets.\n"
// "UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
// "However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
// Severity::Warning
// );
Mod::get()->setSavedValue<std::string>(
"last-forward-compat-warn-popup-ver",

View file

@ -1073,7 +1073,7 @@ void Loader::Impl::installModManuallyFromFile(std::filesystem::path const& path,
createQuickPopup(
"Mod Installed",
fmt::format(
"Mod <co>{}</c> has been succesfully installed from file! "
"Mod <co>{}</c> has been successfully installed from file! "
"<cy>Do you want to delete the original file?</c>",
meta.getName()
),
@ -1092,7 +1092,7 @@ void Loader::Impl::installModManuallyFromFile(std::filesystem::path const& path,
"OK"
)->show();
}
// No need to show a confirmation popup if succesful since that's
// No need to show a confirmation popup if successful since that's
// to be assumed via pressing the button on the previous popup
}
}

View file

@ -210,8 +210,7 @@ Result<> Mod::Impl::saveData() {
}
// ModSettingsManager keeps track of the whole savedata
matjson::Value json;
m_settings->save(json);
matjson::Value json = m_settings->save();
// saveData is expected to be synchronous, and always called from GD thread
ModStateEvent(m_self, ModEventType::DataSaved).post();

View file

@ -214,12 +214,12 @@ Result<> ModSettingsManager::load(matjson::Value const& json) {
}
return Ok();
}
void ModSettingsManager::save(matjson::Value& json) {
matjson::Value ModSettingsManager::save() {
for (auto& [key, _] : m_impl->settings) {
m_impl->saveSettingValueToSave(key);
}
// Doing this since `ModSettingsManager` is expected to manage savedata fully
json = m_impl->savedata;
return m_impl->savedata;
}
matjson::Value& ModSettingsManager::getSaveData() {
return m_impl->savedata;

View file

@ -256,7 +256,7 @@ std::optional<ModDownload> ModDownloadManager::startDownload(
std::optional<DependencyFor> const& dependencyFor,
std::optional<std::string> const& replacesMod
) {
// If this mod has already been succesfully downloaded or is currently
// If this mod has already been successfully downloaded or is currently
// being downloaded, return as you can't download multiple versions of the
// same mod simultaniously, since that wouldn't make sense. I mean the new
// version would just immediately override to the other one

View file

@ -51,11 +51,16 @@ protected:
}
void onRequest(Request::Event* event) {
if (event->getValue() && event->getValue()->isOk() && event->getValue()->inspect([](auto&& value) { return value.has_value(); })) {
m_loading->removeFromParent();
m_textarea->setString(event->getValue()->unwrap()->c_str());
if (auto* res = event->getValue(); res && res->isOk()) {
auto value = std::move(*res).unwrap();
if (value) {
m_loading->removeFromParent();
std::string str = std::move(value).value();
m_textarea->setString(str.c_str());
return;
}
}
else if (!event->getProgress()) {
if (!event->getProgress()) {
m_loading->removeFromParent();
m_textarea->setString(m_noneText.c_str());
}

View file

@ -10,59 +10,33 @@
using namespace geode::prelude;
// class ColorPickPopupEvent::Impl final {
// public:
// ColorPickPopup* popup;
// ccColor4B color;
// bool closed = false;
// };
class ColorPickPopup::Impl final {
public:
cocos2d::ccColor4B m_color;
cocos2d::ccColor4B m_originalColor;
cocos2d::extension::CCControlColourPicker* m_picker;
Slider* m_opacitySlider = nullptr;
TextInput* m_rInput;
TextInput* m_gInput;
TextInput* m_bInput;
TextInput* m_hexInput;
TextInput* m_opacityInput = nullptr;
ColorPickPopupDelegate* m_delegate = nullptr;
cocos2d::CCSprite* m_newColorSpr;
CCMenuItemSpriteExtra* m_resetBtn;
};
// ColorPickPopupEvent::ColorPickPopupEvent(ColorPickPopup* popup, ccColor4B const& color)
// : m_impl(std::make_shared<Impl>())
// {
// m_impl->popup = popup;
// m_impl->color = color;
// }
// ColorPickPopupEvent::~ColorPickPopupEvent() = default;
ColorPickPopup::ColorPickPopup() {
m_impl = std::make_unique<Impl>();
}
// ColorPickPopup* ColorPickPopupEvent::getPopup() const {
// return m_impl->popup;
// }
// ccColor4B ColorPickPopupEvent::getColor() const {
// return m_impl->color;
// }
// bool ColorPickPopupEvent::isPopupClosed() const {
// return m_impl->closed;
// }
ColorPickPopup::~ColorPickPopup() {}
// class ColorPickPopupEventFilter::Impl final {
// public:
// ColorPickPopup* popup;
// };
// ListenerResult ColorPickPopupEventFilter::handle(std::function<Callback> fn, ColorPickPopupEvent* event) {
// if (event->getPopup() == m_impl->popup) {
// if (event->isPopupClosed()) {
// m_impl->popup = nullptr;
// }
// else {
// fn(event);
// }
// }
// return ListenerResult::Propagate;
// }
// ColorPickPopupEventFilter::ColorPickPopupEventFilter() : ColorPickPopupEventFilter(nullptr) {}
// ColorPickPopupEventFilter::ColorPickPopupEventFilter(ColorPickPopup* popup)
// : m_impl(std::make_shared<Impl>())
// {
// m_impl->popup = popup;
// }
// ColorPickPopupEventFilter::~ColorPickPopupEventFilter() = default;
bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
m_noElasticity = true;
m_color = color;
m_originalColor = color;
m_impl->m_color = color;
m_impl->m_originalColor = color;
auto winSize = CCDirector::sharedDirector()->getWinSize();
@ -115,25 +89,25 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
// picker
m_picker = CCControlColourPicker::colourPicker();
m_picker->setDelegate(this);
m_picker->setID("color-picker");
m_impl->m_picker = CCControlColourPicker::colourPicker();
m_impl->m_picker->setDelegate(this);
m_impl->m_picker->setID("color-picker");
auto pickerWrapper = CCNode::create();
pickerWrapper->setContentSize(m_picker->getContentSize());
pickerWrapper->setContentSize(m_impl->m_picker->getContentSize());
pickerWrapper->setID("picker-wrapper");
pickerWrapper->addChildAtPosition(m_picker, Anchor::Center, ccp(0, 0));
pickerWrapper->addChildAtPosition(m_impl->m_picker, Anchor::Center, ccp(0, 0));
pickerRow->addChild(pickerWrapper);
auto oldColorSpr = CCSprite::createWithSpriteFrameName("whiteSquare60_001.png");
oldColorSpr->setColor(to3B(m_color));
oldColorSpr->setColor(to3B(m_impl->m_color));
oldColorSpr->setID("old-color-spr");
colorMenu->addChild(oldColorSpr);
m_newColorSpr = CCSprite::createWithSpriteFrameName("whiteSquare60_001.png");
m_newColorSpr->setColor(to3B(m_color));
m_newColorSpr->setID("new-color-spr");
colorMenu->addChild(m_newColorSpr);
m_impl->m_newColorSpr = CCSprite::createWithSpriteFrameName("whiteSquare60_001.png");
m_impl->m_newColorSpr->setColor(to3B(m_impl->m_color));
m_impl->m_newColorSpr->setID("new-color-spr");
colorMenu->addChild(m_impl->m_newColorSpr);
auto resetBtnSpr = ButtonSprite::create(
CCSprite::createWithSpriteFrameName("reset-gold.png"_spr), 0x20, true, 0.f,
@ -141,16 +115,16 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
);
resetBtnSpr->setScale(.6f);
m_resetBtn =
m_impl->m_resetBtn =
CCMenuItemSpriteExtra::create(resetBtnSpr, this, menu_selector(ColorPickPopup::onReset));
m_resetBtn->setPosition({ -165.f, -50.f });
m_resetBtn->setLayoutOptions(
m_impl->m_resetBtn->setPosition({ -165.f, -50.f });
m_impl->m_resetBtn->setLayoutOptions(
AxisLayoutOptions::create()
->setPrevGap(10.f)
->setNextGap(10.f)
);
m_resetBtn->setID("reset-btn");
colorMenu->addChild(m_resetBtn);
m_impl->m_resetBtn->setID("reset-btn");
colorMenu->addChild(m_impl->m_resetBtn);
@ -194,11 +168,11 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
rText->setID("r-text");
rColumn->addChild(rText);
m_rInput = TextInput::create(50.f, "R");
m_rInput->setScale(.7f);
m_rInput->setDelegate(this, TAG_R_INPUT);
m_rInput->setID("r-input");
rColumn->addChild(m_rInput);
m_impl->m_rInput = TextInput::create(50.f, "R");
m_impl->m_rInput->setScale(.7f);
m_impl->m_rInput->setDelegate(this, TAG_R_INPUT);
m_impl->m_rInput->setID("r-input");
rColumn->addChild(m_impl->m_rInput);
rColumn->updateLayout();
auto rRect = calculateChildCoverage(rColumn);
@ -223,11 +197,11 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
gText->setID("g-text");
gColumn->addChild(gText);
m_gInput = TextInput::create(50.f, "G");
m_gInput->setScale(.7f);
m_gInput->setDelegate(this, TAG_G_INPUT);
m_gInput->setID("g-input");
gColumn->addChild(m_gInput);
m_impl->m_gInput = TextInput::create(50.f, "G");
m_impl->m_gInput->setScale(.7f);
m_impl->m_gInput->setDelegate(this, TAG_G_INPUT);
m_impl->m_gInput->setID("g-input");
gColumn->addChild(m_impl->m_gInput);
gColumn->updateLayout();
auto gRect = calculateChildCoverage(gColumn);
@ -252,11 +226,11 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
bText->setID("b-text");
bColumn->addChild(bText);
m_bInput = TextInput::create(50.f, "B");
m_bInput->setScale(.7f);
m_bInput->setDelegate(this, TAG_B_INPUT);
m_bInput->setID("b-input");
bColumn->addChild(m_bInput);
m_impl->m_bInput = TextInput::create(50.f, "B");
m_impl->m_bInput->setScale(.7f);
m_impl->m_bInput->setDelegate(this, TAG_B_INPUT);
m_impl->m_bInput->setID("b-input");
bColumn->addChild(m_impl->m_bInput);
bColumn->updateLayout();
auto bRect = calculateChildCoverage(bColumn);
@ -281,11 +255,11 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
hexText->setID("hex-text");
hexColumn->addChild(hexText);
m_hexInput = TextInput::create(165.f, "Hex");
m_hexInput->setScale(.7f);
m_hexInput->setDelegate(this, TAG_HEX_INPUT);
m_hexInput->setID("hex-input");
hexColumn->addChild(m_hexInput);
m_impl->m_hexInput = TextInput::create(165.f, "Hex");
m_impl->m_hexInput->setScale(.7f);
m_impl->m_hexInput->setDelegate(this, TAG_HEX_INPUT);
m_impl->m_hexInput->setID("hex-input");
hexColumn->addChild(m_impl->m_hexInput);
hexColumn->updateLayout();
rgbRow->updateLayout();
@ -324,22 +298,22 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
opacityText->setID("opacity-text");
sliderColumn->addChild(opacityText);
m_opacitySlider =
m_impl->m_opacitySlider =
Slider::create(this, menu_selector(ColorPickPopup::onOpacitySlider), .75f);
m_opacitySlider->setValue(color.a / 255.f);
m_opacitySlider->setID("opacity-slider");
m_impl->m_opacitySlider->setValue(color.a / 255.f);
m_impl->m_opacitySlider->setID("opacity-slider");
auto sliderWrapper = CCNode::create();
sliderWrapper->setContentSize(ccp(m_opacitySlider->m_width, m_opacitySlider->m_height) * .75f);
sliderWrapper->setContentSize(ccp(m_impl->m_opacitySlider->m_width, m_impl->m_opacitySlider->m_height) * .75f);
sliderWrapper->setID("slider-wrapper");
sliderWrapper->addChildAtPosition(m_opacitySlider, Anchor::Center, ccp(0, 0));
sliderWrapper->addChildAtPosition(m_impl->m_opacitySlider, Anchor::Center, ccp(0, 0));
sliderColumn->addChild(sliderWrapper);
m_opacityInput = TextInput::create(60.f, "Opacity");
m_opacityInput->setScale(.7f);
m_opacityInput->setDelegate(this, TAG_OPACITY_INPUT);
m_opacityInput->setID("opacity-input");
opacitySection->addChild(m_opacityInput);
m_impl->m_opacityInput = TextInput::create(60.f, "Opacity");
m_impl->m_opacityInput->setScale(.7f);
m_impl->m_opacityInput->setDelegate(this, TAG_OPACITY_INPUT);
m_impl->m_opacityInput->setID("opacity-input");
opacitySection->addChild(m_impl->m_opacityInput);
sliderColumn->updateLayout();
opacitySection->updateLayout();
@ -361,75 +335,79 @@ bool ColorPickPopup::setup(ccColor4B const& color, bool isRGBA) {
}
void ColorPickPopup::updateState(CCNode* except) {
#define IF_NOT_EXCEPT(inp, value) \
if (inp->getInputNode() != except) { \
inp->setString(value, false); \
}
#define IF_NOT_EXCEPT(inp, value) \
if (inp->getInputNode() != except) { \
inp->setString(value, false); \
}
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_NOT_EXCEPT(m_impl->m_rInput, numToString<int>(m_impl->m_color.r));
IF_NOT_EXCEPT(m_impl->m_gInput, numToString<int>(m_impl->m_color.g));
IF_NOT_EXCEPT(m_impl->m_bInput, numToString<int>(m_impl->m_color.b));
IF_NOT_EXCEPT(m_impl->m_hexInput, cc3bToHexString(to3B(m_impl->m_color)));
if (m_impl->m_opacityInput) {
IF_NOT_EXCEPT(m_impl->m_opacityInput, numToString(m_impl->m_color.a / 255.f, 2));
}
if (m_opacitySlider) {
m_opacitySlider->setValue(m_color.a / 255.f);
if (m_impl->m_opacitySlider) {
m_impl->m_opacitySlider->setValue(m_impl->m_color.a / 255.f);
}
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);
if (m_impl->m_picker != except) {
m_impl->m_picker->setDelegate(nullptr);
m_impl->m_picker->setColorValue(to3B(m_impl->m_color));
m_impl->m_picker->setDelegate(this);
}
m_impl->m_resetBtn->setVisible(m_impl->m_originalColor != m_impl->m_color);
m_impl->m_newColorSpr->setColor(to3B(m_impl->m_color));
}
void ColorPickPopup::onOpacitySlider(CCObject* sender) {
m_color.a = static_cast<GLubyte>(static_cast<SliderThumb*>(sender)->getValue() * 255.f);
m_impl->m_color.a = static_cast<GLubyte>(static_cast<SliderThumb*>(sender)->getValue() * 255.f);
this->updateState();
}
void ColorPickPopup::onReset(CCObject*) {
m_color = m_originalColor;
m_impl->m_color = m_impl->m_originalColor;
this->updateState();
}
void ColorPickPopup::onClose(CCObject* sender) {
if (m_impl->m_delegate) {
m_impl->m_delegate->updateColor(m_impl->m_color);
}
Popup::onClose(sender);
}
void ColorPickPopup::textChanged(CCTextInputNode* input) {
if (input->getString().size()) {
switch (input->getTag()) {
case TAG_HEX_INPUT:
{
if (auto color = cc3bFromHexString(input->getString(), true)) {
m_color.r = color.unwrap().r;
m_color.g = color.unwrap().g;
m_color.b = color.unwrap().b;
m_impl->m_color.r = color.unwrap().r;
m_impl->m_color.g = color.unwrap().g;
m_impl->m_color.b = color.unwrap().b;
}
}
break;
case TAG_OPACITY_INPUT: {
auto res = numFromString<float>(input->getString().c_str());
if (res) m_color.a = std::clamp(static_cast<int>(res.unwrap() * 255.f), 0, 255);
if (res) m_impl->m_color.a = std::clamp(static_cast<int>(res.unwrap() * 255.f), 0, 255);
break;
}
case TAG_R_INPUT: {
auto res = numFromString<uint32_t>(input->getString().c_str());
if (res) m_color.r = std::clamp(res.unwrap(), 0u, 255u);
if (res) m_impl->m_color.r = std::clamp(res.unwrap(), 0u, 255u);
break;
}
case TAG_G_INPUT: {
auto res = numFromString<uint32_t>(input->getString().c_str());
if (res) m_color.g = std::clamp(res.unwrap(), 0u, 255u);
if (res) m_impl->m_color.g = std::clamp(res.unwrap(), 0u, 255u);
break;
}
case TAG_B_INPUT: {
auto res = numFromString<uint32_t>(input->getString().c_str());
if (res) m_color.b = std::clamp(res.unwrap(), 0u, 255u);
if (res) m_impl->m_color.b = std::clamp(res.unwrap(), 0u, 255u);
break;
}
@ -440,18 +418,18 @@ void ColorPickPopup::textChanged(CCTextInputNode* 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);
m_impl->m_color.r = color.r;
m_impl->m_color.g = color.g;
m_impl->m_color.b = color.b;
this->updateState(m_impl->m_picker);
}
void ColorPickPopup::setDelegate(ColorPickPopupDelegate* delegate) {
m_delegate = delegate;
m_impl->m_delegate = delegate;
}
void ColorPickPopup::setColorTarget(cocos2d::CCSprite* spr) {
m_picker->setColorTarget(spr);
m_impl->m_picker->setColorTarget(spr);
}
ColorPickPopup* ColorPickPopup::create(ccColor4B const& color, bool isRGBA) {

View file

@ -96,7 +96,7 @@ class WebResponse::Impl {
public:
int m_code;
ByteVector m_data;
std::unordered_map<std::string, std::string> m_headers;
std::unordered_map<std::string, std::vector<std::string>> m_headers;
Result<> into(std::filesystem::path const& path) const;
};
@ -146,8 +146,14 @@ std::vector<std::string> WebResponse::headers() const {
}
std::optional<std::string> WebResponse::header(std::string_view name) const {
auto str = std::string(name);
if (m_impl->m_headers.contains(str)) {
if (auto str = std::string(name); m_impl->m_headers.contains(str)) {
return m_impl->m_headers.at(str).at(0);
}
return std::nullopt;
}
std::optional<std::vector<std::string>> WebResponse::getAllHeadersNamed(std::string_view name) const {
if (auto str = std::string(name); m_impl->m_headers.contains(str)) {
return m_impl->m_headers.at(str);
}
return std::nullopt;
@ -189,7 +195,7 @@ public:
std::string m_method;
std::string m_url;
std::unordered_map<std::string, std::string> m_headers;
std::unordered_map<std::string, std::vector<std::string>> m_headers;
std::unordered_map<std::string, std::string> m_urlParameters;
std::optional<std::string> m_userAgent;
std::optional<std::string> m_acceptEncodingType;
@ -270,15 +276,17 @@ WebTask WebRequest::send(std::string_view method, std::string_view url) {
// Set headers
curl_slist* headers = nullptr;
for (auto& [name, value] : impl->m_headers) {
for (auto& [name, values] : impl->m_headers) {
// Sanitize header name
auto header = name;
header.erase(std::remove_if(header.begin(), header.end(), [](char c) {
return c == '\r' || c == '\n';
}), header.end());
// Append value
header += ": " + value;
headers = curl_slist_append(headers, header.c_str());
for (const auto& value: values) {
header += ": " + value;
headers = curl_slist_append(headers, header.c_str());
}
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
@ -406,7 +414,12 @@ WebTask WebRequest::send(std::string_view method, std::string_view url) {
if (value.ends_with('\r')) {
value = value.substr(0, value.size() - 1);
}
headers.insert_or_assign(key, value);
// Create a new vector and add to it or add to an already existing one
if (headers.contains(key)) {
headers.at(key).push_back(value);
} else {
headers.insert_or_assign(key, std::vector{value});
}
}
return size * nitems;
}));
@ -508,7 +521,14 @@ WebRequest& WebRequest::header(std::string_view name, std::string_view value) {
}
}
m_impl->m_headers.insert_or_assign(std::string(name), std::string(value));
// Create a new vector and add to it or add to an already existing one
std::string strName = std::string(name);
std::string strValue = std::string(value);
if (m_impl->m_headers.contains(strName)) {
m_impl->m_headers.at(strName).push_back(strValue);
} else {
m_impl->m_headers.insert_or_assign(strName, std::vector{strValue});
}
return *this;
}
@ -609,7 +629,7 @@ std::string WebRequest::getUrl() const {
return m_impl->m_url;
}
std::unordered_map<std::string, std::string> WebRequest::getHeaders() const {
std::unordered_map<std::string, std::vector<std::string>> WebRequest::getHeaders() const {
return m_impl->m_headers;
}