mirror of
https://github.com/geode-sdk/geode.git
synced 2024-11-27 09:55:34 -05:00
Merge branch 'main' into macos-is-mean-yet-again
This commit is contained in:
commit
97b6a68b7d
25 changed files with 525 additions and 145 deletions
|
@ -1,5 +1,14 @@
|
|||
# Geode Changelog
|
||||
|
||||
## v3.4.0
|
||||
* Add an API for modifying the Geode UI via events; see [the corresponding docs page](https://docs.geode-sdk.org/tutorials/modify-geode) (2a3c35f)
|
||||
* Add `openInfoPopup` overload that accepts a mod ID and can open both an installed mod page or a server page (028bbf9)
|
||||
* Add `LoadingSpinner` for creating loading circles easily (5c84012)
|
||||
* Add `TextInput::focus` and `TextInput::unfocus` (749fdf1)
|
||||
* MDTextArea changes: hex colors are now formatted as `<c-XXXXXX></c>`; added support for `<cc>`, `<cd>`, `<cf>`, and `<cs>`; fixed `mod:` links (028bbf9)
|
||||
* Deprecate `cc3x` (6080fdb)
|
||||
* Don't cancel subtasks on `Task` destructor (4b4bc0e)
|
||||
|
||||
## v3.3.1
|
||||
* Move ObjectDecoder and its delegate to Cocos headers (95f9eeb, dceb91e)
|
||||
* Fix weird behavior with textures, objects and more by changing en-US.utf8 locale to C (2cd1a9e)
|
||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
|||
3.3.1
|
||||
3.4.0
|
|
@ -8,7 +8,7 @@ ${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "このセットアップは$(^Na
|
|||
; installer
|
||||
|
||||
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\nこのパスにはGeometry Dashがインストールされていません!"
|
||||
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nYour version of Geometry Dash is too old for this version of Geode!"
|
||||
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nGeometry DashのバージョンはこのGeodeのバージョンには古すぎます!"
|
||||
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "このパスには他のモッドがインストールされています!$\r$\nそれらはGeodeによって上書きされます。(the dll trademark)"
|
||||
|
||||
; uninstaller
|
||||
|
|
|
@ -107,6 +107,7 @@ public:
|
|||
* @lua NA
|
||||
*/
|
||||
CCGLProgram();
|
||||
GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCGLProgram, CCObject);
|
||||
/**
|
||||
* @js NA
|
||||
* @lua NA
|
||||
|
|
|
@ -1,8 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include "../loader/Mod.hpp"
|
||||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
|
||||
class ModPopup;
|
||||
class ModItem;
|
||||
class ModLogoSprite;
|
||||
class FLAlertLayer; // for macos :3
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
* Event posted whenever a popup is opened for a mod. Allows mods to modify
|
||||
* the Geode UI. See the [tutorial on Geode UI modification](https://docs.geode-sdk.org/tutorials/modify-geode)
|
||||
* for **very important notes on these events**!
|
||||
*/
|
||||
class GEODE_DLL ModPopupUIEvent final : public Event {
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
friend class ::ModPopup;
|
||||
|
||||
ModPopupUIEvent(std::unique_ptr<Impl>&& impl);
|
||||
|
||||
public:
|
||||
virtual ~ModPopupUIEvent();
|
||||
|
||||
/**
|
||||
* Get the popup itself
|
||||
*/
|
||||
FLAlertLayer* getPopup() const;
|
||||
/**
|
||||
* Get the ID of the mod this popup is for
|
||||
*/
|
||||
std::string getModID() const;
|
||||
/**
|
||||
* If this popup is of an installed mod, get it
|
||||
*/
|
||||
std::optional<Mod*> getMod() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event posted whenever a logo sprite is created for a mod. Allows mods to modify
|
||||
* the Geode UI. See the [tutorial on Geode UI modification](https://docs.geode-sdk.org/tutorials/modify-geode)
|
||||
* for **very important notes on these events**!
|
||||
*/
|
||||
class GEODE_DLL ModItemUIEvent final : public Event {
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
friend class ::ModItem;
|
||||
|
||||
ModItemUIEvent(std::unique_ptr<Impl>&& impl);
|
||||
|
||||
public:
|
||||
virtual ~ModItemUIEvent();
|
||||
|
||||
/**
|
||||
* Get the item itself
|
||||
*/
|
||||
cocos2d::CCNode* getItem() const;
|
||||
/**
|
||||
* Get the ID of the mod this logo is for
|
||||
*/
|
||||
std::string getModID() const;
|
||||
/**
|
||||
* If this logo is of an installed mod, get it
|
||||
*/
|
||||
std::optional<Mod*> getMod() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event posted whenever a logo sprite is created for a mod. Allows mods to modify
|
||||
* the Geode UI. See the [tutorial on Geode UI modification](https://docs.geode-sdk.org/tutorials/modify-geode)
|
||||
* for **very important notes on these events**!
|
||||
*/
|
||||
class GEODE_DLL ModLogoUIEvent final : public Event {
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
friend class ::ModLogoSprite;
|
||||
|
||||
ModLogoUIEvent(std::unique_ptr<Impl>&& impl);
|
||||
|
||||
public:
|
||||
virtual ~ModLogoUIEvent();
|
||||
|
||||
/**
|
||||
* Get the sprite itself
|
||||
*/
|
||||
cocos2d::CCNode* getSprite() const;
|
||||
/**
|
||||
* Get the ID of the mod this logo is for
|
||||
*/
|
||||
std::string getModID() const;
|
||||
/**
|
||||
* If this logo is of an installed mod, get it
|
||||
*/
|
||||
std::optional<Mod*> getMod() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open the Geode mods list
|
||||
*/
|
||||
|
@ -11,6 +110,15 @@ namespace geode {
|
|||
* Open the info popup for a mod
|
||||
*/
|
||||
GEODE_DLL void openInfoPopup(Mod* mod);
|
||||
/**
|
||||
* Open the info popup for a mod based on an ID. If the mod is installed,
|
||||
* its installed popup is opened. Otherwise will check if the servers
|
||||
* have this mod, or if not, show an error popup
|
||||
* @returns A Task that completes to `true` if the mod was found and a
|
||||
* popup was opened, and `false` otherwise. If you wish to modify the
|
||||
* created popup, listen for the Geode UI events listed in `GeodeUI.hpp`
|
||||
*/
|
||||
GEODE_DLL Task<bool> openInfoPopup(std::string const& modID);
|
||||
/**
|
||||
* Open the info popup for a mod on the changelog page
|
||||
*/
|
||||
|
|
29
loader/include/Geode/ui/LoadingSpinner.hpp
Normal file
29
loader/include/Geode/ui/LoadingSpinner.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <cocos2d.h>
|
||||
|
||||
namespace geode {
|
||||
/**
|
||||
* An eternally spinning loading circle. Essentially just a more convenient
|
||||
* alternative to RobTop's `LoadingCircle` class, as this one respects its
|
||||
* content size and is a lot more stripped down (not requiring a `show`
|
||||
* method or anything - it just works!)
|
||||
*/
|
||||
class GEODE_DLL LoadingSpinner : public cocos2d::CCNode {
|
||||
protected:
|
||||
cocos2d::CCSprite* m_spinner;
|
||||
|
||||
bool init(float size);
|
||||
|
||||
void spin();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a loading circle
|
||||
* @param size The diameter of the circle in Cocos units
|
||||
*/
|
||||
static LoadingSpinner* create(float size);
|
||||
|
||||
void setVisible(bool visible) override;
|
||||
};
|
||||
}
|
|
@ -138,6 +138,15 @@ namespace geode {
|
|||
*/
|
||||
std::string getString() const;
|
||||
|
||||
/**
|
||||
* Focus this input (activate the cursor)
|
||||
*/
|
||||
void focus();
|
||||
/**
|
||||
* Defocus this input (deactivate the cursor)
|
||||
*/
|
||||
void defocus();
|
||||
|
||||
CCTextInputNode* getInputNode() const;
|
||||
cocos2d::extension::CCScale9Sprite* getBGSprite() const;
|
||||
};
|
||||
|
|
|
@ -163,9 +163,12 @@ namespace geode {
|
|||
m_status = Status::Cancelled;
|
||||
// If this task carries extra data, call the extra data's
|
||||
// handling method
|
||||
if (m_extraData) {
|
||||
m_extraData->cancel();
|
||||
}
|
||||
// Actually: don't do this! This will cancel tasks even if
|
||||
// they have other listeners! The extra data's destructor
|
||||
// will handle cancellation if it has no other listeners!
|
||||
// if (m_extraData) {
|
||||
// m_extraData->cancel();
|
||||
// }
|
||||
// No need to actually post an event because this Task is
|
||||
// unlisteanable
|
||||
m_finalEventPosted = true;
|
||||
|
|
|
@ -149,16 +149,16 @@ namespace cocos2d {
|
|||
return s1.width != s2.width || s1.height != s2.height;
|
||||
}
|
||||
static bool operator<(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width < s2.width || s1.height < s2.height;
|
||||
return s1.width < s2.width && s1.height < s2.height;
|
||||
}
|
||||
static bool operator<=(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width <= s2.width || s1.height <= s2.height;
|
||||
return s1.width <= s2.width && s1.height <= s2.height;
|
||||
}
|
||||
static bool operator>(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width > s2.width || s1.height > s2.height;
|
||||
return s1.width > s2.width && s1.height > s2.height;
|
||||
}
|
||||
static bool operator>=(cocos2d::CCSize const& s1, cocos2d::CCSize const& s2) {
|
||||
return s1.width >= s2.width || s1.height >= s2.height;
|
||||
return s1.width >= s2.width && s1.height >= s2.height;
|
||||
}
|
||||
static bool operator==(cocos2d::CCRect const& r1, cocos2d::CCRect const& r2) {
|
||||
return r1.origin == r2.origin && r1.size == r2.size;
|
||||
|
@ -861,6 +861,7 @@ namespace geode::cocos {
|
|||
return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f};
|
||||
}
|
||||
|
||||
[[deprecated("This function may have unintended behavior, use cc3bFromHexString or manually expand the color instead")]]
|
||||
constexpr cocos2d::ccColor3B cc3x(int hexValue) {
|
||||
if (hexValue <= 0xf)
|
||||
return cocos2d::ccColor3B{
|
||||
|
|
|
@ -125,11 +125,13 @@ namespace geode::utils::web {
|
|||
WebTask patch(std::string_view url);
|
||||
|
||||
WebRequest& header(std::string_view name, std::string_view value);
|
||||
WebRequest& removeHeader(std::string_view name);
|
||||
WebRequest& param(std::string_view name, std::string_view value);
|
||||
template <std::integral T>
|
||||
WebRequest& param(std::string_view name, T value) {
|
||||
return this->param(name, std::to_string(value));
|
||||
}
|
||||
WebRequest& removeParam(std::string_view name);
|
||||
|
||||
/**
|
||||
* Sets the request's user agent.
|
||||
|
|
|
@ -2,11 +2,78 @@
|
|||
#include <Geode/loader/Dirs.hpp>
|
||||
#include <Geode/ui/GeodeUI.hpp>
|
||||
#include <Geode/ui/MDPopup.hpp>
|
||||
#include <Geode/ui/LoadingSpinner.hpp>
|
||||
#include <Geode/utils/web.hpp>
|
||||
#include <server/Server.hpp>
|
||||
#include "mods/GeodeStyle.hpp"
|
||||
#include "mods/settings/ModSettingsPopup.hpp"
|
||||
#include "mods/popups/ModPopup.hpp"
|
||||
#include "GeodeUIEvent.hpp"
|
||||
|
||||
class LoadServerModLayer : public Popup<std::string const&> {
|
||||
protected:
|
||||
std::string m_id;
|
||||
EventListener<server::ServerRequest<server::ServerModMetadata>> m_listener;
|
||||
|
||||
bool setup(std::string const& id) override {
|
||||
m_closeBtn->setVisible(false);
|
||||
|
||||
this->setTitle("Loading mod...");
|
||||
|
||||
auto spinner = LoadingSpinner::create(40);
|
||||
m_mainLayer->addChildAtPosition(spinner, Anchor::Center, ccp(0, -10));
|
||||
|
||||
m_id = id;
|
||||
m_listener.bind(this, &LoadServerModLayer::onRequest);
|
||||
m_listener.setFilter(server::getMod(id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void onRequest(server::ServerRequest<server::ServerModMetadata>::Event* event) {
|
||||
if (auto res = event->getValue()) {
|
||||
if (res->isOk()) {
|
||||
// Copy info first as onClose may free the listener which will free the event
|
||||
auto info = **res;
|
||||
this->onClose(nullptr);
|
||||
// Run this on next frame because otherwise the popup is unable to call server::getMod for some reason
|
||||
Loader::get()->queueInMainThread([info = std::move(info)]() mutable {
|
||||
ModPopup::create(ModSource(std::move(info)))->show();
|
||||
});
|
||||
}
|
||||
else {
|
||||
auto id = m_id;
|
||||
this->onClose(nullptr);
|
||||
FLAlertLayer::create(
|
||||
"Error Loading Mod",
|
||||
fmt::format("Unable to find mod with the ID <cr>{}</c>!", id),
|
||||
"OK"
|
||||
)->show();
|
||||
}
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
this->onClose(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Task<bool> listen() const {
|
||||
return m_listener.getFilter().map(
|
||||
[](auto* result) -> bool { return result->isOk(); },
|
||||
[](auto) -> std::monostate { return std::monostate(); }
|
||||
);
|
||||
}
|
||||
|
||||
static LoadServerModLayer* create(std::string const& id) {
|
||||
auto ret = new LoadServerModLayer();
|
||||
if (ret && ret->initAnchored(180, 100, id, "square01_001.png", CCRectZero)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
CC_SAFE_RELEASE(ret);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
void geode::openModsList() {
|
||||
ModsLayer::scene();
|
||||
|
@ -68,6 +135,18 @@ void geode::openSupportPopup(ModMetadata const& metadata) {
|
|||
void geode::openInfoPopup(Mod* mod) {
|
||||
ModPopup::create(mod)->show();
|
||||
}
|
||||
Task<bool> geode::openInfoPopup(std::string const& modID) {
|
||||
if (auto mod = Loader::get()->getInstalledMod(modID)) {
|
||||
openInfoPopup(mod);
|
||||
return Task<bool>::immediate(true);
|
||||
}
|
||||
else {
|
||||
auto popup = LoadServerModLayer::create(modID);
|
||||
auto task = popup->listen();
|
||||
popup->show();
|
||||
return task;
|
||||
}
|
||||
}
|
||||
void geode::openIndexPopup(Mod* mod) {
|
||||
// deprecated func
|
||||
openInfoPopup(mod);
|
||||
|
@ -98,6 +177,9 @@ protected:
|
|||
this->setAnchorPoint({ .5f, .5f });
|
||||
this->setContentSize({ 50, 50 });
|
||||
|
||||
// This is a default ID, nothing should ever rely on the ID of any ModLogoSprite being this
|
||||
this->setID(std::string(Mod::get()->expandSpriteName(fmt::format("sprite-{}", id))));
|
||||
|
||||
m_modID = id;
|
||||
m_listener.bind(this, &ModLogoSprite::onFetch);
|
||||
|
||||
|
@ -105,19 +187,22 @@ protected:
|
|||
if (!fetch) {
|
||||
this->setSprite(id == "geode.loader" ?
|
||||
CCSprite::createWithSpriteFrameName("geode-logo.png"_spr) :
|
||||
CCSprite::create(fmt::format("{}/logo.png", id).c_str())
|
||||
CCSprite::create(fmt::format("{}/logo.png", id).c_str()),
|
||||
false
|
||||
);
|
||||
}
|
||||
// Asynchronously fetch from server
|
||||
else {
|
||||
this->setSprite(createLoadingCircle(25));
|
||||
this->setSprite(createLoadingCircle(25), false);
|
||||
m_listener.setFilter(server::getModLogo(id));
|
||||
}
|
||||
|
||||
ModLogoUIEvent(std::make_unique<ModLogoUIEvent::Impl>(this, id)).post();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setSprite(CCNode* sprite) {
|
||||
void setSprite(CCNode* sprite, bool postEvent) {
|
||||
// Remove any existing sprite
|
||||
if (m_sprite) {
|
||||
m_sprite->removeFromParent();
|
||||
|
@ -129,15 +214,20 @@ protected:
|
|||
}
|
||||
// Set sprite and scale it to node size
|
||||
m_sprite = sprite;
|
||||
m_sprite->setID("sprite");
|
||||
limitNodeSize(m_sprite, m_obContentSize, 99.f, 0.f);
|
||||
this->addChildAtPosition(m_sprite, Anchor::Center);
|
||||
|
||||
if (postEvent) {
|
||||
ModLogoUIEvent(std::make_unique<ModLogoUIEvent::Impl>(this, m_modID)).post();
|
||||
}
|
||||
}
|
||||
|
||||
void onFetch(server::ServerRequest<ByteVector>::Event* event) {
|
||||
if (auto result = event->getValue()) {
|
||||
// Set default sprite on error
|
||||
if (result->isErr()) {
|
||||
this->setSprite(nullptr);
|
||||
this->setSprite(nullptr, true);
|
||||
}
|
||||
// Otherwise load downloaded sprite to memory
|
||||
else {
|
||||
|
@ -146,11 +236,11 @@ protected:
|
|||
image->initWithImageData(const_cast<uint8_t*>(data.data()), data.size());
|
||||
|
||||
auto texture = CCTextureCache::get()->addUIImage(image, m_modID.c_str());
|
||||
this->setSprite(CCSprite::createWithTexture(texture));
|
||||
this->setSprite(CCSprite::createWithTexture(texture), true);
|
||||
}
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
this->setSprite(nullptr);
|
||||
this->setSprite(nullptr, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
68
loader/src/ui/GeodeUIEvent.cpp
Normal file
68
loader/src/ui/GeodeUIEvent.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include "GeodeUIEvent.hpp"
|
||||
|
||||
ModPopupUIEvent::ModPopupUIEvent(std::unique_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
|
||||
ModPopupUIEvent::~ModPopupUIEvent() = default;
|
||||
|
||||
FLAlertLayer* ModPopupUIEvent::getPopup() const {
|
||||
return m_impl->popup;
|
||||
}
|
||||
std::string ModPopupUIEvent::getModID() const {
|
||||
return m_impl->popup->getSource().getID();
|
||||
}
|
||||
std::optional<Mod*> ModPopupUIEvent::getMod() const {
|
||||
auto mod = m_impl->popup->getSource().asMod();
|
||||
return mod ? std::optional(mod) : std::nullopt;
|
||||
}
|
||||
|
||||
ModItemUIEvent::ModItemUIEvent(std::unique_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
|
||||
ModItemUIEvent::~ModItemUIEvent() = default;
|
||||
|
||||
CCNode* ModItemUIEvent::getItem() const {
|
||||
return m_impl->item;
|
||||
}
|
||||
std::string ModItemUIEvent::getModID() const {
|
||||
return m_impl->item->getSource().getID();
|
||||
}
|
||||
std::optional<Mod*> ModItemUIEvent::getMod() const {
|
||||
auto mod = m_impl->item->getSource().asMod();
|
||||
return mod ? std::optional(mod) : std::nullopt;
|
||||
}
|
||||
|
||||
ModLogoUIEvent::ModLogoUIEvent(std::unique_ptr<Impl>&& impl) : m_impl(std::move(impl)) {}
|
||||
ModLogoUIEvent::~ModLogoUIEvent() = default;
|
||||
|
||||
CCNode* ModLogoUIEvent::getSprite() const {
|
||||
return m_impl->sprite;
|
||||
}
|
||||
std::string ModLogoUIEvent::getModID() const {
|
||||
return m_impl->modID;
|
||||
}
|
||||
std::optional<Mod*> ModLogoUIEvent::getMod() const {
|
||||
if (auto mod = Loader::get()->getInstalledMod(m_impl->modID)) {
|
||||
return mod;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// $execute {
|
||||
// new EventListener<EventFilter<ModLogoUIEvent>>(+[](ModLogoUIEvent* event) {
|
||||
// if (event->getModID() == "geode.loader") {
|
||||
// auto fart = CCSprite::createWithSpriteFrameName("GJ_demonIcon_001.png");
|
||||
// fart->setScaleX(5);
|
||||
// fart->setScaleY(3);
|
||||
// event->getSprite()->addChildAtPosition(fart, Anchor::Center);
|
||||
// }
|
||||
// return ListenerResult::Propagate;
|
||||
// });
|
||||
// new EventListener<EventFilter<ModItemUIEvent>>(+[](ModItemUIEvent* event) {
|
||||
// if (event->getModID() == "geode.loader") {
|
||||
// auto fart = CCSprite::createWithSpriteFrameName("GJ_demonIcon_001.png");
|
||||
// fart->setScaleX(4);
|
||||
// fart->setScaleY(2);
|
||||
// if (auto dev = event->getItem()->querySelector("developers-button")) {
|
||||
// dev->addChildAtPosition(fart, Anchor::Center, ccp(-15, 0));
|
||||
// }
|
||||
// }
|
||||
// return ListenerResult::Propagate;
|
||||
// });
|
||||
// }
|
32
loader/src/ui/GeodeUIEvent.hpp
Normal file
32
loader/src/ui/GeodeUIEvent.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/ui/GeodeUI.hpp>
|
||||
#include "mods/popups/ModPopup.hpp"
|
||||
#include "mods/list/ModItem.hpp"
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
class ModPopupUIEvent::Impl {
|
||||
public:
|
||||
ModPopup* popup;
|
||||
|
||||
Impl(ModPopup* popup)
|
||||
: popup(popup) {}
|
||||
};
|
||||
|
||||
class ModItemUIEvent::Impl {
|
||||
public:
|
||||
ModItem* item;
|
||||
|
||||
Impl(ModItem* item)
|
||||
: item(item) {}
|
||||
};
|
||||
|
||||
class ModLogoUIEvent::Impl {
|
||||
public:
|
||||
CCNode* sprite;
|
||||
std::string modID;
|
||||
|
||||
Impl(CCNode* sprite, std::string const& modID)
|
||||
: sprite(sprite), modID(modID) {}
|
||||
};
|
|
@ -3,6 +3,7 @@
|
|||
#include <Geode/utils/ColorProvider.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <Geode/binding/ButtonSprite.hpp>
|
||||
#include <Geode/ui/LoadingSpinner.hpp>
|
||||
|
||||
$on_mod(Loaded) {
|
||||
// todo: these names should probably be shorter so they fit in SSO...
|
||||
|
@ -134,51 +135,6 @@ void GeodeSquareSprite::setState(bool state) {
|
|||
}
|
||||
}
|
||||
|
||||
class LoadingSpinner : public CCNode {
|
||||
protected:
|
||||
CCSprite* m_spinner;
|
||||
|
||||
bool init(float sideLength) {
|
||||
if (!CCNode::init())
|
||||
return false;
|
||||
|
||||
this->setID("loading-spinner");
|
||||
this->setContentSize({ sideLength, sideLength });
|
||||
this->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
m_spinner = CCSprite::create("loadingCircle.png");
|
||||
m_spinner->setBlendFunc({ GL_ONE, GL_ONE });
|
||||
limitNodeSize(m_spinner, m_obContentSize, 1.f, .1f);
|
||||
this->addChildAtPosition(m_spinner, Anchor::Center);
|
||||
|
||||
this->spin();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void spin() {
|
||||
m_spinner->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f)));
|
||||
}
|
||||
|
||||
public:
|
||||
static LoadingSpinner* create(float sideLength) {
|
||||
auto ret = new LoadingSpinner();
|
||||
if (ret->init(sideLength)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
delete ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void setVisible(bool visible) override {
|
||||
CCNode::setVisible(visible);
|
||||
if (visible) {
|
||||
this->spin();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CCNode* createLoadingCircle(float sideLength, const char* id) {
|
||||
auto spinner = LoadingSpinner::create(sideLength);
|
||||
spinner->setID(id);
|
||||
|
|
|
@ -350,17 +350,17 @@ bool ModsLayer::init() {
|
|||
reloadBtn->setID("reload-button");
|
||||
actionsMenu->addChild(reloadBtn);
|
||||
|
||||
auto themeSpr = createGeodeCircleButton(
|
||||
auto settingsSpr = createGeodeCircleButton(
|
||||
CCSprite::createWithSpriteFrameName("settings.png"_spr), 1.f,
|
||||
CircleBaseSize::Medium
|
||||
);
|
||||
themeSpr->setScale(.8f);
|
||||
themeSpr->setTopOffset(ccp(.5f, 0));
|
||||
auto themeBtn = CCMenuItemSpriteExtra::create(
|
||||
themeSpr, this, menu_selector(ModsLayer::onSettings)
|
||||
settingsSpr->setScale(.8f);
|
||||
settingsSpr->setTopOffset(ccp(.5f, 0));
|
||||
auto settingsBtn = CCMenuItemSpriteExtra::create(
|
||||
settingsSpr, this, menu_selector(ModsLayer::onSettings)
|
||||
);
|
||||
themeBtn->setID("theme-button");
|
||||
actionsMenu->addChild(themeBtn);
|
||||
settingsBtn->setID("settings-button");
|
||||
actionsMenu->addChild(settingsBtn);
|
||||
|
||||
auto folderSpr = createGeodeCircleButton(
|
||||
CCSprite::createWithSpriteFrameName("gj_folderBtn_001.png"), 1.f,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "../popups/DevPopup.hpp"
|
||||
#include "ui/mods/popups/ModErrorPopup.hpp"
|
||||
#include "ui/mods/sources/ModSource.hpp"
|
||||
#include "../../GeodeUIEvent.hpp"
|
||||
|
||||
bool ModItem::init(ModSource&& source) {
|
||||
if (!CCNode::init())
|
||||
|
@ -407,6 +408,8 @@ void ModItem::updateState() {
|
|||
on->setOpacity(105);
|
||||
}
|
||||
}
|
||||
|
||||
ModItemUIEvent(std::make_unique<ModItemUIEvent::Impl>(this)).post();
|
||||
}
|
||||
|
||||
void ModItem::updateSize(float width, bool big) {
|
||||
|
@ -521,3 +524,7 @@ ModItem* ModItem::create(ModSource&& source) {
|
|||
delete ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModSource& ModItem::getSource() & {
|
||||
return m_source;
|
||||
}
|
||||
|
|
|
@ -56,4 +56,6 @@ public:
|
|||
static ModItem* create(ModSource&& source);
|
||||
|
||||
void updateSize(float width, bool big);
|
||||
|
||||
ModSource& getSource() &;
|
||||
};
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "ConfirmUninstallPopup.hpp"
|
||||
#include "../settings/ModSettingsPopup.hpp"
|
||||
#include "../../../internal/about.hpp"
|
||||
#include "../../GeodeUIEvent.hpp"
|
||||
|
||||
class FetchTextArea : public CCNode {
|
||||
public:
|
||||
|
@ -29,6 +30,7 @@ protected:
|
|||
m_noneText = noneText;
|
||||
|
||||
m_textarea = MDTextArea::create("", size);
|
||||
m_textarea->setID("textarea");
|
||||
this->addChildAtPosition(m_textarea, Anchor::Center);
|
||||
|
||||
m_loading = createLoadingCircle(30);
|
||||
|
@ -67,6 +69,8 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
m_source = std::move(src);
|
||||
m_noElasticity = true;
|
||||
|
||||
this->setID(std::string(Mod::get()->expandSpriteName(fmt::format("popup-{}", src.getID()))));
|
||||
|
||||
if (src.asMod() == Mod::get()) {
|
||||
// Display commit hashes
|
||||
auto loaderHash = about::getLoaderCommitHash();
|
||||
|
@ -97,6 +101,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
titleContainer->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
auto logo = m_source.createModLogo();
|
||||
logo->setID("mod-logo");
|
||||
limitNodeSize(
|
||||
logo,
|
||||
ccp(titleContainer->getContentHeight(), titleContainer->getContentHeight()),
|
||||
|
@ -112,12 +117,14 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto title = CCLabelBMFont::create(m_source.getMetadata().getName().c_str(), "bigFont.fnt");
|
||||
title->limitLabelWidth(titleContainer->getContentWidth() - devAndTitlePos, .45f, .1f);
|
||||
title->setAnchorPoint({ .0f, .5f });
|
||||
title->setID("mod-name-label");
|
||||
titleContainer->addChildAtPosition(title, Anchor::TopLeft, ccp(devAndTitlePos, -titleContainer->getContentHeight() * .25f));
|
||||
|
||||
auto by = "By " + m_source.formatDevelopers();
|
||||
auto dev = CCLabelBMFont::create(by.c_str(), "goldFont.fnt");
|
||||
dev->limitLabelWidth(titleContainer->getContentWidth() - devAndTitlePos, .35f, .05f);
|
||||
dev->setAnchorPoint({ .0f, .5f });
|
||||
dev->setID("mod-developer-label");
|
||||
titleContainer->addChildAtPosition(dev, Anchor::BottomLeft, ccp(devAndTitlePos, titleContainer->getContentHeight() * .25f));
|
||||
|
||||
// Suggestions
|
||||
|
@ -170,6 +177,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
idLabel->limitLabelWidth(leftColumn->getContentWidth(), .25f, .05f);
|
||||
idLabel->setColor({ 150, 150, 150 });
|
||||
idLabel->setOpacity(140);
|
||||
idLabel->setID("mod-id-label");
|
||||
leftColumn->addChild(idLabel);
|
||||
|
||||
auto statsContainer = CCNode::create();
|
||||
|
@ -186,6 +194,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
m_stats = CCNode::create();
|
||||
m_stats->setContentSize(statsContainer->getContentSize() - ccp(10, 10));
|
||||
m_stats->setAnchorPoint({ .5f, .5f });
|
||||
m_stats->setID("mod-stats-container");
|
||||
|
||||
for (auto stat : std::initializer_list<std::tuple<
|
||||
const char*, const char*, const char*, std::optional<std::string>, const char*
|
||||
|
@ -264,6 +273,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
m_tags->ignoreAnchorPointForPosition(false);
|
||||
m_tags->setContentSize(tagsContainer->getContentSize() - ccp(10, 10));
|
||||
m_tags->setAnchorPoint({ .5f, .5f });
|
||||
m_tags->setID("tags-container");
|
||||
|
||||
m_tags->addChild(createLoadingCircle(50));
|
||||
|
||||
|
@ -438,6 +448,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
linksMenu->ignoreAnchorPointForPosition(false);
|
||||
linksMenu->setContentSize(linksContainer->getContentSize() - ccp(10, 10));
|
||||
linksMenu->setAnchorPoint({ .5f, .5f });
|
||||
linksMenu->setID("links-container");
|
||||
|
||||
// auto linksLabel = CCLabelBMFont::create("Links", "bigFont.fnt");
|
||||
// linksLabel->setLayoutOptions(
|
||||
|
@ -447,28 +458,29 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
// linksMenu->addChild(linksLabel);
|
||||
|
||||
for (auto stat : std::initializer_list<std::tuple<
|
||||
const char*, std::optional<std::string>, SEL_MenuHandler
|
||||
const char*, const char*, std::optional<std::string>, SEL_MenuHandler
|
||||
>> {
|
||||
{ "homepage.png"_spr, m_source.getMetadata().getLinks().getHomepageURL(), nullptr },
|
||||
{ "github.png"_spr, m_source.getMetadata().getLinks().getSourceURL(), nullptr },
|
||||
{ "gj_discordIcon_001.png", m_source.getMetadata().getLinks().getCommunityURL(), nullptr },
|
||||
{ "gift.png"_spr, m_source.getMetadata().getSupportInfo(), menu_selector(ModPopup::onSupport) },
|
||||
{ "homepage", "homepage.png"_spr, m_source.getMetadata().getLinks().getHomepageURL(), nullptr },
|
||||
{ "github", "github.png"_spr, m_source.getMetadata().getLinks().getSourceURL(), nullptr },
|
||||
{ "discord", "gj_discordIcon_001.png", m_source.getMetadata().getLinks().getCommunityURL(), nullptr },
|
||||
{ "support", "gift.png"_spr, m_source.getMetadata().getSupportInfo(), menu_selector(ModPopup::onSupport) },
|
||||
}) {
|
||||
auto spr = CCSprite::createWithSpriteFrameName(std::get<0>(stat));
|
||||
auto spr = CCSprite::createWithSpriteFrameName(std::get<1>(stat));
|
||||
spr->setScale(.75f);
|
||||
if (!std::get<1>(stat).has_value()) {
|
||||
if (!std::get<2>(stat).has_value()) {
|
||||
spr->setColor({ 155, 155, 155 });
|
||||
spr->setOpacity(155);
|
||||
}
|
||||
auto btn = CCMenuItemSpriteExtra::create(
|
||||
spr, this, (
|
||||
std::get<1>(stat).has_value() ?
|
||||
(std::get<2>(stat) ? std::get<2>(stat) : menu_selector(ModPopup::onLink)) :
|
||||
std::get<2>(stat).has_value() ?
|
||||
(std::get<3>(stat) ? std::get<3>(stat) : menu_selector(ModPopup::onLink)) :
|
||||
nullptr
|
||||
)
|
||||
);
|
||||
if (!std::get<2>(stat) && std::get<1>(stat)) {
|
||||
btn->setUserObject("url", CCString::create(*std::get<1>(stat)));
|
||||
btn->setID(std::get<0>(stat));
|
||||
if (!std::get<3>(stat) && std::get<2>(stat)) {
|
||||
btn->setUserObject("url", CCString::create(*std::get<2>(stat)));
|
||||
}
|
||||
linksMenu->addChild(btn);
|
||||
}
|
||||
|
@ -510,17 +522,19 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
tabsMenu->setScale(.65f);
|
||||
tabsMenu->setContentWidth(m_rightColumn->getContentWidth() / tabsMenu->getScale());
|
||||
tabsMenu->setAnchorPoint({ .5f, 1.f });
|
||||
tabsMenu->setID("tabs-menu");
|
||||
|
||||
for (auto mdTab : std::initializer_list<std::tuple<const char*, const char*, Tab>> {
|
||||
{ "message.png"_spr, "Description", Tab::Details },
|
||||
{ "changelog.png"_spr, "Changelog", Tab::Changelog }
|
||||
for (auto mdTab : std::initializer_list<std::tuple<const char*, const char*, const char*, Tab>> {
|
||||
{ "message.png"_spr, "Description", "description", Tab::Details },
|
||||
{ "changelog.png"_spr, "Changelog", "changelog", Tab::Changelog }
|
||||
// { "version.png"_spr, "Versions", Tab::Versions },
|
||||
}) {
|
||||
auto spr = GeodeTabSprite::create(std::get<0>(mdTab), std::get<1>(mdTab), 140, m_source.asServer());
|
||||
auto btn = CCMenuItemSpriteExtra::create(spr, this, menu_selector(ModPopup::onTab));
|
||||
btn->setTag(static_cast<int>(std::get<2>(mdTab)));
|
||||
btn->setTag(static_cast<int>(std::get<3>(mdTab)));
|
||||
btn->setID(std::get<2>(mdTab));
|
||||
tabsMenu->addChild(btn);
|
||||
m_tabs.insert({ std::get<2>(mdTab), { spr, nullptr } });
|
||||
m_tabs.insert({ std::get<3>(mdTab), { spr, nullptr } });
|
||||
}
|
||||
|
||||
// placeholder external link until versions tab is implemented
|
||||
|
@ -532,7 +546,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
|
||||
auto externalLinkBtn = CCMenuItemSpriteExtra::create(externalLinkSpr, this, menu_selector(ModPopup::onLink));
|
||||
externalLinkBtn->setUserObject("url", CCString::create(modUrl));
|
||||
|
||||
externalLinkBtn->setID("mod-online-page-button");
|
||||
m_buttonMenu->addChildAtPosition(externalLinkBtn, Anchor::TopRight, ccp(-14, -16));
|
||||
|
||||
tabsMenu->setLayout(RowLayout::create()->setAxisAlignment(AxisAlignment::Start));
|
||||
|
@ -548,6 +562,7 @@ bool ModPopup::setup(ModSource&& src) {
|
|||
auto settingsBtn = CCMenuItemSpriteExtra::create(
|
||||
settingsSpr, this, menu_selector(ModPopup::onSettings)
|
||||
);
|
||||
settingsBtn->setID("settings-button");
|
||||
m_buttonMenu->addChildAtPosition(settingsBtn, Anchor::BottomLeft, ccp(28, 25));
|
||||
|
||||
if (!m_source.asMod() || !m_source.asMod()->hasSettings()) {
|
||||
|
@ -707,6 +722,8 @@ void ModPopup::updateState() {
|
|||
}
|
||||
|
||||
m_installMenu->updateLayout();
|
||||
|
||||
ModPopupUIEvent(std::make_unique<ModPopupUIEvent::Impl>(this)).post();
|
||||
}
|
||||
|
||||
void ModPopup::setStatIcon(CCNode* stat, const char* spr) {
|
||||
|
@ -794,6 +811,7 @@ void ModPopup::onLoadServerInfo(typename server::ServerRequest<server::ServerMod
|
|||
this->setStatValue(stat, id.second);
|
||||
}
|
||||
}
|
||||
ModPopupUIEvent(std::make_unique<ModPopupUIEvent::Impl>(this)).post();
|
||||
}
|
||||
else if (event->isCancelled() || (event->getValue() && event->getValue()->isErr())) {
|
||||
for (auto child : CCArrayExt<CCNode*>(m_stats->getChildren())) {
|
||||
|
@ -801,6 +819,7 @@ void ModPopup::onLoadServerInfo(typename server::ServerRequest<server::ServerMod
|
|||
this->setStatValue(child, "N/A");
|
||||
}
|
||||
}
|
||||
ModPopupUIEvent(std::make_unique<ModPopupUIEvent::Impl>(this)).post();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -851,6 +870,8 @@ void ModPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<std:
|
|||
}
|
||||
|
||||
m_tags->updateLayout();
|
||||
|
||||
ModPopupUIEvent(std::make_unique<ModPopupUIEvent::Impl>(this)).post();
|
||||
}
|
||||
else if (event->isCancelled() || (event->getValue() && event->getValue()->isErr())) {
|
||||
m_tags->removeAllChildren();
|
||||
|
@ -860,6 +881,8 @@ void ModPopup::onLoadTags(typename server::ServerRequest<std::unordered_set<std:
|
|||
m_tags->addChild(label);
|
||||
|
||||
m_tags->updateLayout();
|
||||
|
||||
ModPopupUIEvent(std::make_unique<ModPopupUIEvent::Impl>(this)).post();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -888,6 +911,7 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
|
|||
"No description provided",
|
||||
size / mdScale
|
||||
);
|
||||
m_currentTabPage->setID("description-container");
|
||||
m_currentTabPage->setScale(mdScale);
|
||||
} break;
|
||||
|
||||
|
@ -897,12 +921,14 @@ void ModPopup::loadTab(ModPopup::Tab tab) {
|
|||
"No changelog provided",
|
||||
size / mdScale
|
||||
);
|
||||
m_currentTabPage->setID("changelog-container");
|
||||
m_currentTabPage->setScale(mdScale);
|
||||
} break;
|
||||
|
||||
case Tab::Versions: {
|
||||
m_currentTabPage = CCNode::create();
|
||||
m_currentTabPage->setContentSize(size);
|
||||
m_currentTabPage->setID("versions-container");
|
||||
} break;
|
||||
}
|
||||
m_currentTabPage->setAnchorPoint({ .5f, .0f });
|
||||
|
@ -995,3 +1021,7 @@ ModPopup* ModPopup::create(ModSource&& src) {
|
|||
delete ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModSource& ModPopup::getSource() & {
|
||||
return m_source;
|
||||
}
|
||||
|
|
|
@ -65,4 +65,6 @@ protected:
|
|||
public:
|
||||
void loadTab(Tab tab);
|
||||
static ModPopup* create(ModSource&& src);
|
||||
|
||||
ModSource& getSource() &;
|
||||
};
|
||||
|
|
|
@ -150,10 +150,10 @@ protected:
|
|||
|
||||
virtual void valueChanged(bool updateText = true) {
|
||||
if (this->hasUncommittedChanges()) {
|
||||
m_nameLabel->setColor(cc3x(0x1d0));
|
||||
m_nameLabel->setColor({0x11, 0xdd, 0x00});
|
||||
}
|
||||
else {
|
||||
m_nameLabel->setColor(cc3x(0xfff));
|
||||
m_nameLabel->setColor({0xff, 0xff, 0xff});
|
||||
}
|
||||
if (m_resetBtn) m_resetBtn->setVisible(this->hasNonDefaultValue());
|
||||
auto isValid = setting()->validate(m_uncommittedValue);
|
||||
|
|
|
@ -145,22 +145,22 @@ void ModSettingsPopup::onResetAll(CCObject*) {
|
|||
|
||||
void ModSettingsPopup::settingValueCommitted(SettingNode*) {
|
||||
if (this->hasUncommitted()) {
|
||||
m_applyBtnSpr->setColor(cc3x(0xf));
|
||||
m_applyBtnSpr->setColor({0xff, 0xff, 0xff});
|
||||
m_applyBtn->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
m_applyBtnSpr->setColor(cc3x(0x4));
|
||||
m_applyBtnSpr->setColor({0x44, 0x44, 0x44});
|
||||
m_applyBtn->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ModSettingsPopup::settingValueChanged(SettingNode*) {
|
||||
if (this->hasUncommitted()) {
|
||||
m_applyBtnSpr->setColor(cc3x(0xf));
|
||||
m_applyBtnSpr->setColor({0xff, 0xff, 0xff});
|
||||
m_applyBtn->setEnabled(true);
|
||||
}
|
||||
else {
|
||||
m_applyBtnSpr->setColor(cc3x(0x4));
|
||||
m_applyBtnSpr->setColor({0x44, 0x44, 0x44});
|
||||
m_applyBtn->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
|
43
loader/src/ui/nodes/LoadingSpinner.cpp
Normal file
43
loader/src/ui/nodes/LoadingSpinner.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include <Geode/ui/LoadingSpinner.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
bool LoadingSpinner::init(float sideLength) {
|
||||
if (!CCNode::init())
|
||||
return false;
|
||||
|
||||
this->setID("loading-spinner");
|
||||
this->setContentSize({ sideLength, sideLength });
|
||||
this->setAnchorPoint({ .5f, .5f });
|
||||
|
||||
m_spinner = CCSprite::create("loadingCircle.png");
|
||||
m_spinner->setBlendFunc({ GL_ONE, GL_ONE });
|
||||
limitNodeSize(m_spinner, m_obContentSize, 1.f, .1f);
|
||||
this->addChildAtPosition(m_spinner, Anchor::Center);
|
||||
|
||||
this->spin();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LoadingSpinner::spin() {
|
||||
m_spinner->runAction(CCRepeatForever::create(CCRotateBy::create(1.f, 360.f)));
|
||||
}
|
||||
|
||||
LoadingSpinner* LoadingSpinner::create(float sideLength) {
|
||||
auto ret = new LoadingSpinner();
|
||||
if (ret->init(sideLength)) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
delete ret;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LoadingSpinner::setVisible(bool visible) {
|
||||
CCNode::setVisible(visible);
|
||||
if (visible) {
|
||||
this->spin();
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@
|
|||
#include <md4c.h>
|
||||
#include <charconv>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/ui/GeodeUI.hpp>
|
||||
#include <server/Server.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
|
@ -22,7 +24,7 @@ static constexpr float g_fontScale = .5f;
|
|||
static constexpr float g_paragraphPadding = 7.f;
|
||||
static constexpr float g_indent = 7.f;
|
||||
static constexpr float g_codeBlockIndent = 8.f;
|
||||
static constexpr ccColor3B g_linkColor = cc3x(0x7ff4f4);
|
||||
static constexpr ccColor3B g_linkColor = {0x7f, 0xf4, 0xf4};
|
||||
|
||||
TextRenderer::Font g_mdFont = [](int style) -> TextRenderer::Label {
|
||||
if ((style & TextStyleBold) && (style & TextStyleItalic)) {
|
||||
|
@ -77,13 +79,12 @@ public:
|
|||
};
|
||||
|
||||
Result<ccColor3B> colorForIdentifier(std::string const& tag) {
|
||||
if (utils::string::contains(tag, ' ')) {
|
||||
auto hexStr = utils::string::split(utils::string::normalize(tag), " ").at(1);
|
||||
auto res = numFromString<uint32_t>(hexStr, 16);
|
||||
if (res.isErr()) {
|
||||
return Err("Invalid hex");
|
||||
}
|
||||
return Ok(cc3x(res.unwrap()));
|
||||
if (tag.length() > 2 && tag[1] == '-') {
|
||||
return cc3bFromHexString(tag.substr(2));
|
||||
}
|
||||
// Support the old form of <carbitaryletters hex>
|
||||
else if (tag.find(' ') != std::string::npos) {
|
||||
return cc3bFromHexString(string::trim(tag.substr(tag.find(' ') + 1)));
|
||||
}
|
||||
else {
|
||||
auto colorText = tag.substr(1);
|
||||
|
@ -95,15 +96,19 @@ Result<ccColor3B> colorForIdentifier(std::string const& tag) {
|
|||
}
|
||||
else {
|
||||
switch (colorText.front()) {
|
||||
case 'a': return Ok(cc3x(0x9632ff)); break;
|
||||
case 'b': return Ok(cc3x(0x4a52e1)); break;
|
||||
case 'g': return Ok(cc3x(0x40e348)); break;
|
||||
case 'l': return Ok(cc3x(0x60abef)); break;
|
||||
case 'j': return Ok(cc3x(0x32c8ff)); break;
|
||||
case 'y': return Ok(cc3x(0xffff00)); break;
|
||||
case 'o': return Ok(cc3x(0xffa54b)); break;
|
||||
case 'r': return Ok(cc3x(0xff5a5a)); break;
|
||||
case 'p': return Ok(cc3x(0xff00ff)); break;
|
||||
case 'a': return Ok(ccc3(150, 50, 255)); break;
|
||||
case 'b': return Ok(ccc3(74, 82, 225)); break;
|
||||
case 'c': return Ok(ccc3(255, 255, 150)); break;
|
||||
case 'd': return Ok(ccc3(255, 150, 255)); break;
|
||||
case 'f': return Ok(ccc3(150, 255, 255)); break;
|
||||
case 'g': return Ok(ccc3(64, 227, 72)); break;
|
||||
case 'j': return Ok(ccc3(50, 200, 255)); break;
|
||||
case 'l': return Ok(ccc3(96, 171, 239)); break;
|
||||
case 'o': return Ok(ccc3(255, 165, 75)); break;
|
||||
case 'p': return Ok(ccc3(255, 0, 255)); break;
|
||||
case 'r': return Ok(ccc3(255, 90, 90)); break;
|
||||
case 's': return Ok(ccc3(255, 220, 65)); break;
|
||||
case 'y': return Ok(ccc3(255, 255, 0)); break;
|
||||
default: return Err("Unknown color " + colorText);
|
||||
}
|
||||
}
|
||||
|
@ -223,44 +228,10 @@ void MDTextArea::onGDLevel(CCObject* pSender) {
|
|||
CCDirector::sharedDirector()->replaceScene(CCTransitionFade::create(0.5f, scene));
|
||||
}
|
||||
|
||||
void MDTextArea::onGeodeMod(CCObject* pSender) {
|
||||
// TODO
|
||||
// auto href = as<CCString*>(as<CCNode*>(pSender)->getUserObject());
|
||||
// auto modString = std::string(href->getCString());
|
||||
// modString = modString.substr(modString.find(":") + 1);
|
||||
// auto loader = Loader::get();
|
||||
// auto index = Index::get();
|
||||
// Mod* mod;
|
||||
// bool success = false;
|
||||
// IndexItemHandle indexItem;
|
||||
// bool isIndexMod = !loader->isModInstalled(modString);
|
||||
|
||||
// if (isIndexMod) {
|
||||
// auto indexSearch = index->getItemsByModID(modString);
|
||||
// if (indexSearch.size() != 0) {
|
||||
// indexItem = indexSearch.back();
|
||||
// Mod mod2 = Mod(indexItem->getMetadata());
|
||||
// mod = &mod2;
|
||||
// auto item = Index::get()->getItem(mod);
|
||||
// IndexItemInfoPopup::create(item, nullptr)->show();
|
||||
// success = true;
|
||||
// }
|
||||
// } else {
|
||||
// mod = loader->getLoadedMod(modString);
|
||||
// LocalModInfoPopup::create(mod, nullptr)->show();
|
||||
// success = true;
|
||||
// }
|
||||
|
||||
// if (!success) {
|
||||
// FLAlertLayer::create(
|
||||
// "Error",
|
||||
// "Invalid mod ID: <cr>" + modString +
|
||||
// "</c>. This is "
|
||||
// "probably the mod developers's fault, report the bug to them.",
|
||||
// "OK"
|
||||
// )
|
||||
// ->show();
|
||||
// }
|
||||
void MDTextArea::onGeodeMod(CCObject* sender) {
|
||||
auto href = as<CCString*>(as<CCNode*>(sender)->getUserObject());
|
||||
auto modID = std::string(href->getCString());
|
||||
(void)openInfoPopup(modID.substr(modID.find(":") + 1));
|
||||
}
|
||||
|
||||
void MDTextArea::FLAlert_Clicked(FLAlertLayer* layer, bool btn) {
|
||||
|
|
|
@ -191,6 +191,13 @@ std::string TextInput::getString() const {
|
|||
return m_input->getString();
|
||||
}
|
||||
|
||||
void TextInput::focus() {
|
||||
m_input->onClickTrackNode(true);
|
||||
}
|
||||
void TextInput::defocus() {
|
||||
m_input->detachWithIME();
|
||||
}
|
||||
|
||||
CCTextInputNode* TextInput::getInputNode() const {
|
||||
return m_input;
|
||||
}
|
||||
|
|
|
@ -505,11 +505,21 @@ WebRequest& WebRequest::header(std::string_view name, std::string_view value) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
WebRequest& WebRequest::removeHeader(std::string_view name) {
|
||||
m_impl->m_headers.erase(std::string(name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
WebRequest& WebRequest::param(std::string_view name, std::string_view value) {
|
||||
m_impl->m_urlParameters.insert_or_assign(std::string(name), std::string(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
WebRequest& WebRequest::removeParam(std::string_view name) {
|
||||
m_impl->m_urlParameters.erase(std::string(name));
|
||||
return *this;
|
||||
}
|
||||
|
||||
WebRequest& WebRequest::userAgent(std::string_view name) {
|
||||
m_impl->m_userAgent = name;
|
||||
return *this;
|
||||
|
|
Loading…
Reference in a new issue