- add CCArray::firstObject and CCArray::removeFirstObject
 - add minizip zip headers and sources (going to add file::Zip class soon)
 - redesign Notifications to be simpler
 - rework Result to not have dual implementations
 - Result::expect now has one default named argument for formatting the error in
This commit is contained in:
HJfod 2022-11-30 17:07:05 +02:00
parent c113e97844
commit 2c78b8e620
12 changed files with 2880 additions and 789 deletions
loader

View file

@ -184,6 +184,11 @@ public:
CCString* stringAtIndex(unsigned int index);
);
/**
* Returns first element, or null if empty
* @note Geode addition
*/
CCObject* firstObject();
/** Returns last element */
CCObject* lastObject();
/** Returns a random element */
@ -210,6 +215,11 @@ public:
// Removing Objects
/**
* Remove first object, or do nothing if array is empty
* @note Geode addition
*/
void removeFirstObject(bool bReleaseObj = true);
/** Remove last object */
void removeLastObject(bool bReleaseObj = true);
/** Remove a certain object */

View file

@ -0,0 +1,363 @@
/* zip.h -- IO on .zip files using zlib
Version 1.1, February 14h, 2010
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
Modifications for Zip64 support
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
For more info read MiniZip_info.txt
---------------------------------------------------------------------------
Condition of use and distribution are the same than zlib :
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
---------------------------------------------------------------------------
Changes
See header of zip.h
*/
#ifndef _zip12_H
#define _zip12_H
//#define HAVE_BZIP2
#include "../../platform/CCPlatformDefine.h"
#ifndef _ZLIB_H
#include "../../platform/IncludeZlib.h"
#endif
#include "ioapi.h"
#ifdef HAVE_BZIP2
#include "bzlib.h"
#endif
namespace cocos2d {
#define Z_BZIP2ED 12
#if defined(STRICTZIP) || defined(STRICTZIPUNZIP)
/* like the STRICT of WIN32, we define a pointer that cannot be converted
from (void*) without cast */
typedef struct TagzipFile__ { int unused; } zipFile__;
typedef zipFile__ *zipFile;
#else
typedef voidp zipFile;
#endif
#define ZIP_OK (0)
#define ZIP_EOF (0)
#define ZIP_ERRNO (Z_ERRNO)
#define ZIP_PARAMERROR (-102)
#define ZIP_BADZIPFILE (-103)
#define ZIP_INTERNALERROR (-104)
#ifndef DEF_MEM_LEVEL
# if MAX_MEM_LEVEL >= 8
# define DEF_MEM_LEVEL 8
# else
# define DEF_MEM_LEVEL MAX_MEM_LEVEL
# endif
#endif
/* default memLevel */
/* tm_zip contain date/time info */
typedef struct tm_zip_s
{
int tm_sec; /* seconds after the minute - [0,59] */
int tm_min; /* minutes after the hour - [0,59] */
int tm_hour; /* hours since midnight - [0,23] */
int tm_mday; /* day of the month - [1,31] */
int tm_mon; /* months since January - [0,11] */
int tm_year; /* years - [1980..2044] */
} tm_zip;
typedef struct
{
tm_zip tmz_date; /* date in understandable format */
uLong dosDate; /* if dos_date == 0, tmu_date is used */
/* uLong flag; */ /* general purpose bit flag 2 bytes */
uLong internal_fa; /* internal file attributes 2 bytes */
uLong external_fa; /* external file attributes 4 bytes */
} zip_fileinfo;
typedef const char* zipcharpc;
#define APPEND_STATUS_CREATE (0)
#define APPEND_STATUS_CREATEAFTER (1)
#define APPEND_STATUS_ADDINZIP (2)
extern zipFile CC_ZIP_DLL zipOpen OF((const char *pathname, int append));
extern zipFile CC_ZIP_DLL zipOpen64 OF((const void *pathname, int append));
/*
Create a zipfile.
pathname contain on Windows XP a filename like "c:\\zlib\\zlib113.zip" or on
an Unix computer "zlib/zlib113.zip".
if the file pathname exist and append==APPEND_STATUS_CREATEAFTER, the zip
will be created at the end of the file.
(useful if the file contain a self extractor code)
if the file pathname exist and append==APPEND_STATUS_ADDINZIP, we will
add files in existing zip (be sure you don't add file that doesn't exist)
If the zipfile cannot be opened, the return value is NULL.
Else, the return value is a zipFile Handle, usable with other function
of this zip package.
*/
/* Note : there is no delete function into a zipfile.
If you want delete file into a zipfile, you must open a zipfile, and create another
Of couse, you can use RAW reading and writing to copy the file you did not want delte
*/
extern zipFile CC_ZIP_DLL zipOpen2 OF((const char *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc_def* pzlib_filefunc_def));
extern zipFile CC_ZIP_DLL zipOpen2_64 OF((const void *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc64_def* pzlib_filefunc_def));
extern zipFile CC_ZIP_DLL zipOpen3 OF((const void *pathname,
int append,
zipcharpc* globalcomment,
zlib_filefunc64_32_def* pzlib_filefunc64_32_def));
extern int CC_ZIP_DLL zipOpenNewFileInZip OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level));
extern int CC_ZIP_DLL zipOpenNewFileInZip64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int zip64));
/*
Open a file in the ZIP for writing.
filename : the filename in zip (if NULL, '-' without quote will be used
*zipfi contain supplemental information
if extrafield_local!=NULL and size_extrafield_local>0, extrafield_local
contains the extrafield data the the local header
if extrafield_global!=NULL and size_extrafield_global>0, extrafield_global
contains the extrafield data the the local header
if comment != NULL, comment contain the comment string
method contain the compression method (0 for store, Z_DEFLATED for deflate)
level contain the level of compression (can be Z_DEFAULT_COMPRESSION)
zip64 is set to 1 if a zip64 extended information block should be added to the local file header.
this MUST be '1' if the uncompressed size is >= 0xffffffff.
*/
extern int CC_ZIP_DLL zipOpenNewFileInZip2 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw));
extern int CC_ZIP_DLL zipOpenNewFileInZip2_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int zip64));
/*
Same than zipOpenNewFileInZip, except if raw=1, we write raw file
*/
extern int CC_ZIP_DLL zipOpenNewFileInZip3 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting));
extern int CC_ZIP_DLL zipOpenNewFileInZip3_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
int zip64
));
/*
Same than zipOpenNewFileInZip2, except
windowBits,memLevel,,strategy : see parameter strategy in deflateInit2
password : crypting password (NULL for no crypting)
crcForCrypting : crc of file to compress (needed for crypting)
*/
extern int CC_ZIP_DLL zipOpenNewFileInZip4 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase
));
extern int CC_ZIP_DLL zipOpenNewFileInZip4_64 OF((zipFile file,
const char* filename,
const zip_fileinfo* zipfi,
const void* extrafield_local,
uInt size_extrafield_local,
const void* extrafield_global,
uInt size_extrafield_global,
const char* comment,
int method,
int level,
int raw,
int windowBits,
int memLevel,
int strategy,
const char* password,
uLong crcForCrypting,
uLong versionMadeBy,
uLong flagBase,
int zip64
));
/*
Same than zipOpenNewFileInZip4, except
versionMadeBy : value for Version made by field
flag : value for flag field (compression level info will be added)
*/
extern int CC_ZIP_DLL zipWriteInFileInZip OF((zipFile file,
const void* buf,
unsigned len));
/*
Write data in the zipfile
*/
extern int CC_ZIP_DLL zipCloseFileInZip OF((zipFile file));
/*
Close the current file in the zipfile
*/
extern int CC_ZIP_DLL zipCloseFileInZipRaw OF((zipFile file,
uLong uncompressed_size,
uLong crc32));
extern int CC_ZIP_DLL zipCloseFileInZipRaw64 OF((zipFile file,
ZPOS64_T uncompressed_size,
uLong crc32));
/*
Close the current file in the zipfile, for file opened with
parameter raw=1 in zipOpenNewFileInZip2
uncompressed_size and crc32 are value for the uncompressed size
*/
extern int CC_ZIP_DLL zipClose OF((zipFile file,
const char* global_comment));
/*
Close the zipfile
*/
extern int CC_ZIP_DLL zipRemoveExtraInfoBlock OF((char* pData, int* dataLen, short sHeader));
/*
zipRemoveExtraInfoBlock - Added by Mathias Svensson
Remove extra information block from a extra information data for the local file header or central directory header
It is needed to remove ZIP64 extra information blocks when before data is written if using RAW mode.
0x0001 is the signature header for the ZIP64 extra information blocks
usage.
Remove ZIP64 Extra information from a central director extra field data
zipRemoveExtraInfoBlock(pCenDirExtraFieldData, &nCenDirExtraFieldDataLen, 0x0001);
Remove ZIP64 Extra information from a Local File Header extra field data
zipRemoveExtraInfoBlock(pLocalHeaderExtraFieldData, &nLocalHeaderExtraFieldDataLen, 0x0001);
*/
}
#endif /* _zip64_H */

View file

@ -1,180 +1,89 @@
#pragma once
#include "../utils/cocos.hpp"
#include "SceneManager.hpp"
#include <chrono>
#include <cocos2d.h>
#include <cocos-ext.h>
#include <Geode/binding/TextAlertPopup.hpp>
#include "../utils/cocos.hpp"
namespace geode {
enum class NotificationLocation {
TopLeft,
TopCenter,
TopRight,
BottomLeft,
BottomCenter,
BottomRight,
constexpr auto NOTIFICATION_DEFAULT_TIME = 1.f;
enum class NotificationIcon {
None,
Loading,
Success,
Warning,
Error,
};
static constexpr float DEFAULT_NOTIFICATION_TIME = 4.f;
static constexpr NotificationLocation PLATFORM_NOTIFICATION_LOCATION =
#ifdef GEODE_IS_DESKTOP
NotificationLocation::BottomRight;
#else
NotificationLocation::TopCenter;
#endif
class Notification;
class NotificationManager;
struct GEODE_DLL NotificationBuilder {
Mod* m_owner = Mod::get();
std::string m_title = "";
std::string m_text = "";
std::string m_icon = "GJ_infoIcon_001.png";
Ref<cocos2d::CCNode> m_iconNode = nullptr;
std::string m_bg = "GJ_square02.png";
std::function<void(Notification*)> m_callback = nullptr;
float m_time = DEFAULT_NOTIFICATION_TIME;
NotificationLocation m_location = PLATFORM_NOTIFICATION_LOCATION;
bool m_hideOnClick = true;
inline NotificationBuilder& from(Mod* owner) {
m_owner = owner;
return *this;
}
inline NotificationBuilder& title(std::string const& title) {
m_title = title;
return *this;
}
inline NotificationBuilder& text(std::string const& text) {
m_text = text;
return *this;
}
inline NotificationBuilder& icon(std::string const& icon) {
m_icon = icon;
m_iconNode = nullptr;
return *this;
}
inline NotificationBuilder& icon(cocos2d::CCNode* icon) {
m_icon = "";
m_iconNode = icon;
return *this;
}
inline NotificationBuilder& loading() {
auto spr = cocos2d::CCSprite::create("loadingCircle.png");
spr->runAction(cocos2d::CCRepeat::create(cocos2d::CCRotateBy::create(1.f, 360.f), 40000)
);
spr->setBlendFunc({ GL_ONE, GL_ONE });
return this->icon(spr);
}
inline NotificationBuilder& bg(std::string const& bg) {
m_bg = bg;
return *this;
}
inline NotificationBuilder& location(NotificationLocation location) {
m_location = location;
return *this;
}
inline NotificationBuilder& time(float time) {
m_time = time;
return *this;
}
inline NotificationBuilder& stay() {
m_time = .0f;
return *this;
}
inline NotificationBuilder& clicked(
std::function<void(Notification*)> cb, bool hide = true
) {
m_callback = cb;
m_hideOnClick = hide;
return *this;
}
Notification* show();
};
class GEODE_DLL Notification : public cocos2d::CCLayer {
class GEODE_DLL Notification : public cocos2d::CCNodeRGBA {
protected:
Mod* m_owner;
std::function<void(Notification*)> m_callback = nullptr;
static Ref<cocos2d::CCArray> s_queue;
cocos2d::extension::CCScale9Sprite* m_bg;
cocos2d::CCNode* m_icon = nullptr;
cocos2d::CCLabelBMFont* m_title = nullptr;
Ref<cocos2d::CCArray> m_labels = nullptr;
cocos2d::CCPoint m_showDest;
cocos2d::CCPoint m_hideDest;
cocos2d::CCPoint m_posAtTouchStart;
NotificationLocation m_location;
cocos2d::CCLabelBMFont* m_label;
cocos2d::CCSprite* m_icon = nullptr;
float m_time;
bool m_hiding = false;
bool m_clicking;
bool m_hovered;
bool m_hideOnClicked = true;
float m_targetScale = 1.f;
bool m_showing = false;
bool init(
Mod* owner, std::string const& title, std::string const& text, cocos2d::CCNode* icon,
char const* bg, std::function<void(Notification*)> callback, bool hideOnClick
);
bool init(std::string const& text, cocos2d::CCSprite* icon, float time);
void updateLayout();
Notification();
virtual ~Notification();
bool ccTouchBegan(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) override;
void ccTouchEnded(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) override;
void ccTouchMoved(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) override;
void registerWithTouchDispatcher() override;
void clicked();
static cocos2d::CCSprite* createIcon(NotificationIcon icon);
void animateIn();
void animateOut();
void animateOutClicked();
void animateClicking();
void hidden();
void showForReal();
friend class NotificationManager;
void showNextNotification();
void wait();
public:
/**
* Create a notification, similar to TextAlertPopup but more customizable
* @param text Notification text
* @param icon Icon to show in the notification
* @param time Time to show the notification on screen; pass 0 to show
* the notification indefinitely until hide() is called
* @returns The new notification. Make sure to call show() to show the
* notification
*/
static Notification* create(
Mod* owner, std::string const& title, std::string const& text, cocos2d::CCNode* icon,
char const* bg, std::function<void(Notification*)> callback, bool hideOnClick
std::string const& text,
NotificationIcon icon = NotificationIcon::None,
float time = NOTIFICATION_DEFAULT_TIME
);
/**
* Create a notification with a custom icon
* @param text Notification text
* @param icon Icon to show in the notification
* @param time Time to show the notification on screen; pass 0 to show
* the notification indefinitely until hide() is called
* @returns The new notification. Make sure to call show() to show the
* notification
*/
static Notification* create(
std::string const& text,
cocos2d::CCSprite* icon,
float time
);
static NotificationBuilder build();
void show(
NotificationLocation = PLATFORM_NOTIFICATION_LOCATION,
float time = DEFAULT_NOTIFICATION_TIME
);
void setString(std::string const& text);
void setIcon(NotificationIcon icon);
void setIcon(cocos2d::CCSprite* icon);
void setTime(float time);
/**
* Adds the notification to the current scene if it doesn't have a
* parent yet, and displays the show animation. If the time for the
* notification was specified, the notification waits that time and
* then automatically hides
*/
void show();
/**
* Hide the notification. If you passed a time to the create function,
* this function doesn't need to be called manually, unless you want
* to prematurily hide the notification
*/
void hide();
};
class NotificationManager {
protected:
std::unordered_map<NotificationLocation, std::vector<Ref<Notification>>> m_notifications;
void push(Notification*);
void pop(Notification*);
bool isInQueue(Notification*);
friend class Notification;
public:
static NotificationManager* get();
};
}

View file

@ -7,6 +7,8 @@
#include <variant>
#include <fmt/format.h>
// clang-format off
namespace geode {
namespace {
struct AnyType {
@ -20,10 +22,124 @@ namespace geode {
using DefaultValue = std::monostate;
using DefaultError = std::string;
template<class T, class E>
struct Storage {
std::variant<T, E> m_value;
bool holdsOk() const {
return std::holds_alternative<T>(m_value);
}
T getOk() const requires std::is_copy_constructible_v<T> {
return std::get<T>(m_value);
}
T&& getOk() requires(!std::is_copy_constructible_v<T>) {
return std::move(std::get<T>(m_value));
}
E getErr() const requires std::is_copy_constructible_v<E> {
return std::get<E>(m_value);
}
E&& getErr() requires(!std::is_copy_constructible_v<E>) {
return std::move(std::get<E>(m_value));
}
template<class T2, class E2>
requires
std::is_convertible_v<T2, T> &&
std::is_convertible_v<E2, E>
Storage(Storage<T2, E2> const& other)
requires
std::is_copy_constructible_v<T> &&
std::is_copy_constructible_v<E>
{
if (other.holdsOk()) {
m_value = other.getOk();
} else {
m_value = other.getErr();
}
}
Storage(T const& value)
requires std::is_copy_constructible_v<T>
: m_value(value) {}
Storage(T&& value)
requires std::is_move_constructible_v<T>
: m_value(std::forward<T>(value)) {}
Storage(E const& value, std::monostate)
requires std::is_copy_constructible_v<E>
: m_value(value) {}
Storage(E&& value, std::monostate)
requires std::is_move_constructible_v<E>
: m_value(std::forward<E>(value)) {}
};
template<class T>
struct Storage<T, T> {
bool m_holdsOk;
T m_value;
bool holdsOk() const {
return m_holdsOk;
}
T getOk() const requires std::is_copy_constructible_v<T> {
return m_value;
}
T&& getOk() requires(!std::is_copy_constructible_v<T>) {
return std::move(m_value);
}
T getErr() const requires std::is_copy_constructible_v<T> {
return m_value;
}
T&& getErr() requires(!std::is_copy_constructible_v<T>) {
return std::move(m_value);
}
template<class T2, class E2>
requires
std::is_convertible_v<T2, T> &&
std::is_convertible_v<E2, T>
Storage(Storage<T2, E2> const& other)
requires
std::is_copy_constructible_v<T> &&
std::is_copy_constructible_v<T>
: m_value(other.holdsOk() ? other.getOk() : other.getErr()),
m_holdsOk(other.holdsOk()) {}
Storage(T const& value)
requires std::is_copy_constructible_v<T>
: m_value(value),
m_holdsOk(true) {}
Storage(T&& value)
requires std::is_move_constructible_v<T>
: m_value(std::forward<T>(value)),
m_holdsOk(true) {}
Storage(T const& value, std::monostate)
requires std::is_copy_constructible_v<T>
: m_value(value),
m_holdsOk(false) {}
Storage(T&& value, std::monostate)
requires std::is_move_constructible_v<T>
: m_value(std::forward<T>(value)),
m_holdsOk(false) {}
};
}
template <class T = DefaultValue, class E = DefaultError>
class [[nodiscard]] Result {
class [[nodiscard]] Result final {
public:
using value_type = std::remove_reference_t<T>;
using error_type = std::remove_reference_t<E>;
@ -39,265 +155,168 @@ namespace geode {
);
protected:
std::variant<value_type, error_type> m_value;
Storage<value_type, error_type> m_value;
public:
Storage<value_type, error_type> const& _Raw_Storage() const {
return m_value;
}
bool isOk() const {
return std::holds_alternative<value_type>(m_value);
return m_value.holdsOk();
}
bool isErr() const {
return std::holds_alternative<error_type>(m_value);
return !m_value.holdsOk();
}
template<class ... Args>
Result<T, std::string> expect(const char* str, Args&&... args) {
if (isErr()) {
return Result(fmt::format(str, std::forward<Args>(args)...));
return Result<T, std::string>(fmt::format(
str,
std::forward<Args>(args)...,
fmt::arg("error", unwrapErr())
), std::monostate());
} else {
return *this;
}
}
explicit Result(value_type const& value
) requires std::is_copy_constructible_v<value_type> : m_value(value) {}
explicit Result(value_type const& value)
requires std::is_copy_constructible_v<value_type>
: m_value(value) {}
explicit Result(value_type&& value) requires std::is_move_constructible_v<value_type> :
m_value(std::forward<value_type>(value)) {}
explicit Result(value_type&& value)
requires std::is_move_constructible_v<value_type>
: m_value(std::forward<value_type>(value)) {}
explicit Result(error_type const& value
) requires std::is_copy_constructible_v<error_type> : m_value(value) {}
explicit Result(error_type const& value, std::monostate)
requires std::is_copy_constructible_v<error_type>
: m_value(value, std::monostate()) {}
explicit Result(error_type&& value) requires std::is_move_constructible_v<error_type> :
m_value(std::forward<error_type>(value)) {}
explicit Result(error_type&& value, std::monostate)
requires std::is_move_constructible_v<error_type>
: m_value(std::forward<error_type>(value), std::monostate()) {}
Result(Result<T, E> const& other) requires std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
= default;
Result(Result<T, E> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
= default;
Result(Result<T, E>&& other
) requires(!std::is_copy_constructible_v<value_type> || !std::is_copy_constructible_v<error_type>) =
default;
Result(Result<T, E>&& other)
requires(
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
) = default;
template <class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, E> Result(
Result<T2, E2> const& other
)
requires std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
{
if (other.isOk()) {
m_value = other.unwrap();
} else {
m_value = other.unwrapErr();
}
}
template <class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, E> Result(
Result<T2, E2>&& other
)
requires(!std::is_copy_constructible_v<value_type> || !std::is_copy_constructible_v<error_type>) :
m_value(other.isOk() ? other.unwrap() : other.unwrapErr()) {}
template<class T2, class E2>
requires
std::is_convertible_v<T2, T> &&
std::is_convertible_v<E2, E>
Result(Result<T2, E2> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: m_value(other._Raw_Storage()) {}
template <class T2>
requires ConvertibleToResult<T2, T> Result(Result<T2, AnyType> const& other)
requires std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type> : Result(value_type(other.unwrap())) {}
requires ConvertibleToResult<T2, T>
Result(Result<T2, AnyType> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: Result(value_type(other.unwrap())) {}
template <class E2>
requires ConvertibleToResult<E2, E> Result(Result<AnyType, E2> const& other)
requires std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type> :
m_value(std::forward<E2>(other.unwrapErr())) {}
requires ConvertibleToResult<E2, E>
Result(Result<AnyType, E2> const& other)
requires
std::is_copy_constructible_v<value_type> &&
std::is_copy_constructible_v<error_type>
: m_value(std::forward<E2>(other.unwrapErr()), std::monostate()) {}
template <class T2>
requires ConvertibleToResult<T2, T> Result(Result<T2, AnyType>&& other)
requires(!std::is_copy_constructible_v<value_type> || !std::is_copy_constructible_v<error_type>) :
m_value(other.unwrap()) {}
requires ConvertibleToResult<T2, T>
Result(Result<T2, AnyType>&& other)
requires(
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
)
: m_value(other.unwrap()) {}
template <class E2>
requires ConvertibleToResult<E2, E> Result(Result<AnyType, E2>&& other)
requires(!std::is_copy_constructible_v<value_type> || !std::is_copy_constructible_v<error_type>) :
Result(std::forward<error_type>(other.unwrapErr())) {}
requires ConvertibleToResult<E2, E>
Result(Result<AnyType, E2>&& other)
requires(
!std::is_copy_constructible_v<value_type> ||
!std::is_copy_constructible_v<error_type>
)
: Result(std::forward<error_type>(other.unwrapErr()), std::monostate()) {}
value_type unwrap() const requires std::is_copy_constructible_v<value_type> {
return std::get<value_type>(m_value);
return m_value.getOk();
}
value_type&& unwrap() requires(!std::is_copy_constructible_v<value_type>) {
return std::move(std::get<value_type>(m_value));
return std::forward<value_type>(m_value.getOk());
}
error_type unwrapErr() const requires std::is_copy_constructible_v<error_type> {
return std::get<error_type>(m_value);
return m_value.getErr();
}
error_type&& unwrapErr() requires(!std::is_copy_constructible_v<error_type>) {
return std::move(std::get<error_type>(m_value));
return std::forward<error_type>(m_value.getErr());
}
explicit operator bool() const
requires(!std::is_same_v<T, bool> && !std::is_same_v<E, bool>) {
return this->isOk();
}
};
template <class T>
class [[nodiscard]] Result<T, T> {
public:
using value_type = std::remove_reference_t<T>;
using error_type = std::remove_reference_t<T>;
// for some reason doing requires causes errors with pch...
static_assert(
std::is_copy_constructible_v<value_type> ||
std::is_move_constructible_v<value_type>,
"T must be copiable or movable!"
);
protected:
bool m_success;
value_type m_value;
public:
bool isOk() const {
return m_success;
}
bool isErr() const {
return !m_success;
}
template<class ... Args>
Result<T, std::string> expect(const char* str, Args&&... args) {
if (isErr()) {
return Result(fmt::format(str, std::forward<Args>(args)...), false);
} else {
return *this;
}
}
explicit Result(value_type const& value, bool success) requires
std::is_copy_constructible_v<value_type> :
m_value(value),
m_success(success) {}
explicit Result(value_type&& value, bool success) requires
std::is_move_constructible_v<value_type> :
m_value(std::forward<value_type>(value)),
m_success(success) {}
Result(Result<T, T> const& other) requires std::is_copy_constructible_v<value_type>
= default;
Result(Result<T, T>&& other
) requires(!std::is_copy_constructible_v<value_type>) = default;
template <class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, T> Result(
Result<T2, E2> const& other
)
requires std::is_copy_constructible_v<value_type> :
m_value(other.isOk() ? other.unwrap() : other.unwrapErr()),
m_success(other.isOk()) {}
template <class T2, class E2>
requires ConvertibleToResult<T2, T> && ConvertibleToResult<E2, T> Result(
Result<T2, E2>&& other
)
requires(!std::is_copy_constructible_v<value_type>) :
m_value(other.isOk() ? other.unwrap() : other.unwrapErr()), m_success(other.isOk()) {}
template <class T2>
requires ConvertibleToResult<T2, T> Result(Result<T2, AnyType> const& other)
requires std::is_copy_constructible_v<value_type> :
Result(value_type(other.unwrap()), true) {}
template <class T2>
requires ConvertibleToResult<T2, T> Result(Result<T2, AnyType>&& other)
requires(!std::is_copy_constructible_v<value_type>) :
Result(std::forward<value_type>(other.unwrap()), true) {}
template <class E2>
requires ConvertibleToResult<E2, T> Result(Result<AnyType, E2> const& other)
requires std::is_copy_constructible_v<value_type> :
Result(error_type(other.unwrapErr()), false) {}
template <class E2>
requires ConvertibleToResult<E2, T> Result(Result<AnyType, E2>&& other)
requires(!std::is_copy_constructible_v<value_type>) :
Result(std::forward<error_type>(other.unwrapErr()), false) {}
value_type unwrap() const requires std::is_copy_constructible_v<value_type> {
return m_value;
}
value_type&& unwrap() requires(!std::is_copy_constructible_v<value_type>) {
return std::move(m_value);
}
error_type unwrapErr() const requires std::is_copy_constructible_v<error_type> {
return m_value;
}
error_type&& unwrapErr() requires(!std::is_copy_constructible_v<error_type>) {
return std::move(m_value);
}
explicit operator bool() const requires(!std::is_same_v<T, bool>) {
explicit operator bool() const requires(
!std::is_same_v<T, bool> &&
!std::is_same_v<E, bool>
) {
return this->isOk();
}
};
template <class T = DefaultValue, class E = AnyType>
requires std::is_copy_constructible_v<T> Result<T, E> Ok(T value = T()) {
requires std::is_copy_constructible_v<T>
Result<T, E> Ok(T value = T()) {
return Result<T, E>(value);
}
template <class T = DefaultValue, class E = AnyType>
requires(!std::is_copy_constructible_v<T>) Result<T, E> Ok(T&& value) {
requires(!std::is_copy_constructible_v<T>)
Result<T, E> Ok(T&& value) {
return Result<T, E>(std::forward<T>(value));
}
template <class E = DefaultError, class T = AnyType>
requires std::is_copy_constructible_v<E> Result<T, E> Err(E error = E()) {
return Result<T, E>(error);
requires std::is_copy_constructible_v<E>
Result<T, E> Err(E error) {
return Result<T, E>(error, std::monostate());
}
template <class E = DefaultError, class T = AnyType>
requires(!std::is_copy_constructible_v<E>) Result<T, E> Err(E&& error) {
return Result<T, E>(std::forward<E>(error));
requires(!std::is_copy_constructible_v<E>)
Result<T, E> Err(E&& error) {
return Result<T, E>(std::forward<E>(error), std::monostate());
}
#define GEODE_UNWRAP_INTO(into, ...) \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap())
#define GEODE_UNWRAP(...) \
{ \
#define GEODE_UNWRAP_INTO(into, ...) \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
}
into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap())
#define GEODE_UNWRAP(...) \
{ \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
}
}
// clang-format on

View file

@ -0,0 +1,19 @@
#include <cocos2d.h>
using namespace cocos2d;
#pragma warning(push)
#pragma warning(disable : 4273)
CCObject* CCArray::firstObject() {
if (data->num) {
return data->arr[0];
}
return nullptr;
}
void CCArray::removeFirstObject(bool bReleaseObj) {
this->removeObjectAtIndex(0, bReleaseObj);
}
#pragma warning(pop)

View file

@ -75,11 +75,6 @@ void CCFileUtils::updatePaths() {
this->addSearchPath(path.c_str());
}
DONT_ADD_PATHS = false;
log::debug("Search Paths: {}", m_searchPathArray.size());
for (auto& path : m_searchPathArray) {
log::debug("Path: {}", path.c_str());
}
}
#pragma warning(pop)

File diff suppressed because it is too large Load diff

View file

@ -33,30 +33,23 @@ static void addUpdateIcon(char const* icon = "updates-available.png"_spr) {
static void updateIndexProgress(UpdateStatus status, std::string const& info, uint8_t progress) {
if (status == UpdateStatus::Failed) {
g_indexUpdateNotif->hide();
g_indexUpdateNotif->setIcon(NotificationIcon::Error);
g_indexUpdateNotif->setString("Index update failed");
g_indexUpdateNotif->setTime(2.f);
g_indexUpdateNotif = nullptr;
NotificationBuilder()
.title("Index Update")
.text("Index update failed :(")
.icon("info-alert.png"_spr)
.show();
addUpdateIcon("updates-failed.png"_spr);
}
if (status == UpdateStatus::Finished) {
g_indexUpdateNotif->hide();
g_indexUpdateNotif = nullptr;
g_indexUpdateNotif->setIcon(NotificationIcon::Success);
if (Index::get()->areUpdatesAvailable()) {
NotificationBuilder()
.title("Updates available")
.text("Some mods have updates available!")
.icon("updates-available.png"_spr)
.clicked([](auto) -> void {
ModListLayer::scene();
})
.show();
g_indexUpdateNotif->setString("Updates Available");
addUpdateIcon();
}
} else {
g_indexUpdateNotif->setString("Everything Up-to-Date");
}
g_indexUpdateNotif->setTime(2.f);
g_indexUpdateNotif = nullptr;
}
}
@ -107,12 +100,15 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
}
// show if some mods failed to load
auto failed = Loader::get()->getFailedMods();
if (failed.size()) {
NotificationBuilder()
.title("Failed to load")
.text("Some mods failed to load")
.show();
static bool shownFailedNotif = false;
if (!shownFailedNotif) {
shownFailedNotif = true;
if (Loader::get()->getFailedMods().size()) {
Notification::create(
"Some mods failed to load",
NotificationIcon::Error
)->show();
}
}
// show crash info
@ -140,12 +136,10 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
// update mods index
if (!g_indexUpdateNotif && !Index::get()->isIndexUpdated()) {
g_indexUpdateNotif = NotificationBuilder()
.title("Index Update")
.text("Updating index...")
.loading()
.stay()
.show();
g_indexUpdateNotif = Notification::create(
"Updating Index", NotificationIcon::Loading, 0
);
g_indexUpdateNotif->show();
Index::get()->updateIndex(updateIndexProgress);
}

View file

@ -181,7 +181,10 @@ Result<ModInfo> ModInfo::createFromGeodeZip(file::Unzip& unzip) {
}
// Read mod.json & parse if possible
GEODE_UNWRAP_INTO(auto jsonData, unzip.extract("mod.json"));
GEODE_UNWRAP_INTO(
auto jsonData,
unzip.extract("mod.json").expect("Unable to read mod.json: {error}")
);
ModJson json;
try {
json = ModJson::parse(std::string(jsonData.begin(), jsonData.end()));
@ -197,7 +200,10 @@ Result<ModInfo> ModInfo::createFromGeodeZip(file::Unzip& unzip) {
auto info = res.unwrap();
info.m_path = unzip.getPath();
GEODE_UNWRAP(info.addSpecialFiles(unzip));
GEODE_UNWRAP(
info.addSpecialFiles(unzip)
.expect("Unable to add extra files: {error}")
);
return Ok(info);
}

View file

@ -1,234 +1,110 @@
#include <Geode/binding/GameSoundManager.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/binding/LoadingCircle.hpp>
#include <Geode/ui/Notification.hpp>
#include <Geode/ui/TextRenderer.hpp>
#include <Geode/utils/cocos.hpp>
#include <Geode/utils/ranges.hpp>
#include <Geode/loader/Mod.hpp>
USE_GEODE_NAMESPACE();
// todo: make sure notifications dont disappear
// off the screen if the user happens to switch
// scenes or smth that causes actions from being
// run / completed
constexpr auto NOTIFICATION_FADEIN = .3f;
constexpr auto NOTIFICATION_FADEOUT = 1.f;
Notification::Notification() {}
Ref<CCArray> Notification::s_queue = CCArray::create();
Notification::~Notification() {
CCDirector::sharedDirector()->getTouchDispatcher()->decrementForcePrio(2);
}
void Notification::registerWithTouchDispatcher() {
CCDirector::sharedDirector()->getTouchDispatcher()->addTargetedDelegate(this, 0, true);
}
static bool isHovered(CCNode* node, CCTouch* touch) {
auto csize = node->getScaledContentSize();
if (CCRect { node->getPositionX() - csize.width / 2, node->getPositionY() - csize.height / 2,
csize.width, csize.height }
.containsPoint(touch->getLocation())) {
return true;
}
return false;
}
static bool shouldHideNotification(CCTouch* touch, NotificationLocation const& location) {
static constexpr float const HIDE_THRESHOLD = 20.f;
auto dist = touch->getLocation() - touch->getStartLocation();
switch (location) {
case NotificationLocation::BottomLeft:
case NotificationLocation::TopLeft: return dist.x < -HIDE_THRESHOLD;
case NotificationLocation::BottomRight:
case NotificationLocation::TopRight: return dist.x > HIDE_THRESHOLD;
case NotificationLocation::BottomCenter: return dist.y < -HIDE_THRESHOLD;
case NotificationLocation::TopCenter: return dist.y > HIDE_THRESHOLD;
}
return false;
}
void Notification::ccTouchMoved(CCTouch* touch, CCEvent* event) {
auto dist = touch->getLocation() - touch->getStartLocation();
switch (m_location) {
case NotificationLocation::BottomLeft:
case NotificationLocation::TopLeft:
this->setPositionX(m_posAtTouchStart.x + dist.x);
if (this->getPositionX() > m_showDest.x) {
this->setPositionX(m_showDest.x);
}
break;
case NotificationLocation::BottomRight:
case NotificationLocation::TopRight:
this->setPositionX(m_posAtTouchStart.x + dist.x);
if (this->getPositionX() < m_showDest.x) {
this->setPositionX(m_showDest.x);
}
break;
case NotificationLocation::BottomCenter:
this->setPositionY(m_posAtTouchStart.y + dist.y);
if (this->getPositionY() > m_showDest.y) {
this->setPositionY(m_showDest.y);
}
break;
case NotificationLocation::TopCenter:
this->setPositionY(m_posAtTouchStart.y + dist.y);
if (this->getPositionY() < m_showDest.y) {
this->setPositionY(m_showDest.y);
}
break;
}
auto clicking = !shouldHideNotification(touch, m_location);
if (m_clicking != clicking) {
m_clicking = clicking;
this->animateClicking();
}
auto hovered = isHovered(this, touch);
if (m_hovered != hovered) {
m_hovered = hovered;
if (hovered) {
m_bg->setColor({ 150, 150, 150 });
}
else {
m_bg->setColor({ 255, 255, 255 });
}
this->animateClicking();
}
}
bool Notification::ccTouchBegan(CCTouch* touch, CCEvent* event) {
if (!isHovered(this, touch)) {
bool Notification::init(std::string const& text, CCSprite* icon, float time) {
if (!CCNodeRGBA::init())
return false;
}
m_bg->setColor({ 150, 150, 150 });
this->stopAllActions();
m_posAtTouchStart = this->getPosition();
m_clicking = true;
m_hovered = true;
this->animateClicking();
return true;
}
void Notification::ccTouchEnded(CCTouch* touch, CCEvent* event) {
m_clicking = false;
m_hovered = false;
this->animateClicking();
m_bg->setColor({ 255, 255, 255 });
if (shouldHideNotification(touch, m_location)) {
return this->hide();
}
if (isHovered(this, touch)) {
this->animateIn();
this->clicked();
}
}
void Notification::clicked() {
if (m_callback) {
m_callback(this);
if (m_hideOnClicked) {
this->animateOutClicked();
}
}
}
bool Notification::init(
Mod* owner, std::string const& title, std::string const& text, CCNode* icon, char const* bg,
std::function<void(Notification*)> callback, bool hideOnClick
) {
if (!CCLayer::init()) return false;
m_owner = owner;
m_callback = callback;
m_hideOnClicked = hideOnClick;
// m_labels is Ref so no need to call
// retain manually
m_labels = CCArray::create();
m_bg = CCScale9Sprite::create(bg);
m_bg->setScale(.6f);
// using TextRenderer to create the text
// so it automatically wraps the lines
auto renderer = TextRenderer::create();
renderer->begin(this, CCPointZero, { 120.f, 20.f });
renderer->pushBMFont("chatFont.fnt");
renderer->pushScale(.4f);
for (auto& label : renderer->renderString(text + "\n(from " + owner->getName() + ")")) {
m_labels->addObject(label.m_node);
}
renderer->end();
renderer->release();
// add icon
float iconSpace = .0f;
if (icon) {
m_icon = icon;
iconSpace = 20.f;
m_icon->setPosition({ -m_obContentSize.width / 2 + iconSpace / 2, .0f });
limitNodeSize(m_icon, { iconSpace - 8.f, m_obContentSize.height - 8.f }, 1.f, .1f);
this->addChild(m_icon);
}
// add title
if (title.size()) {
m_title = CCLabelBMFont::create(title.c_str(), "goldFont.fnt");
m_title->limitLabelWidth(m_obContentSize.width - iconSpace, .4f, .01f);
m_obContentSize.height += 14;
m_title->setPosition(
-m_obContentSize.width / 2 + iconSpace, m_obContentSize.height / 2 - 6.f
);
m_title->setAnchorPoint({ .0f, .5f });
this->addChild(m_title);
}
// move text content if an icon is present
m_obContentSize.width += iconSpace;
m_icon->setPositionX(m_icon->getPositionX() - iconSpace / 2);
m_title->setPositionX(m_title->getPositionX() - iconSpace / 2);
for (auto label : CCArrayExt<CCNode>(m_labels)) {
label->setPosition(
label->getPositionX() + iconSpace - m_obContentSize.width / 2,
label->getPositionY() - m_obContentSize.height / 2 + 2.f
);
}
// fit bg to content
m_bg->setContentSize(m_obContentSize / m_bg->getScale() + CCSize { 6.f, 6.f });
m_bg->setPosition(0, 0);
m_bg->setZOrder(-1);
m_time = time;
m_bg = CCScale9Sprite::create("square02b_small.png", { 0, 0, 40, 40 });
m_bg->setColor({ 0, 0, 0 });
this->addChild(m_bg);
// set anchor point to middle so the
// notification properly scales
this->setAnchorPoint({ .0f, .0f });
this->setVisible(false);
m_label = CCLabelBMFont::create(text.c_str(), "bigFont.fnt");
m_label->setScale(.6f);
m_bg->addChild(m_label);
// make sure ~CCLayer properly removes
// the notification from touch dispatcher
this->setTouchEnabled(true);
if (m_icon = icon) {
m_bg->addChild(icon);
}
// make this notification the most important
// touch fella on the screen
CCDirector::sharedDirector()->getTouchDispatcher()->incrementForcePrio(2);
this->registerWithTouchDispatcher();
this->setScale(.75f);
this->updateLayout();
return true;
}
void Notification::updateLayout() {
constexpr auto PADDING = 5.f;
auto size = m_label->getScaledContentSize();
float spaceForIcon = 0.f;
if (m_icon) {
limitNodeSize(m_icon, { size.height, size.height }, 1.f, .1f);
spaceForIcon += m_icon->getScaledContentSize().width + PADDING;
}
size += CCSize { spaceForIcon + PADDING * 2, PADDING * 2 };
m_bg->setContentSize(size);
if (m_icon) {
m_icon->setPosition({ size.height / 2, size.height / 2 });
m_label->setPosition(size / 2 + CCSize { spaceForIcon / 2, .0f });
} else {
m_label->setPosition(size / 2);
}
}
void Notification::showNextNotification() {
m_showing = false;
SceneManager::get()->forget(this);
// remove self from front of queue
s_queue->removeFirstObject();
if (auto obj = s_queue->firstObject()) {
as<Notification*>(obj)->show();
}
this->removeFromParent();
}
CCSprite* Notification::createIcon(NotificationIcon icon) {
switch (icon) {
default:
case NotificationIcon::None: {
return nullptr;
} break;
case NotificationIcon::Loading: {
auto icon = CCSprite::create("loadingCircle.png");
icon->runAction(CCRepeatForever::create(
CCRotateBy::create(1.f, 360.f)
));
icon->setBlendFunc({ GL_ONE, GL_ONE });
return icon;
} break;
case NotificationIcon::Success: {
return CCSprite::createWithSpriteFrameName("GJ_completesIcon_001.png");
} break;
case NotificationIcon::Warning: {
return CCSprite::createWithSpriteFrameName("info-alert.png"_spr);
} break;
case NotificationIcon::Error: {
return CCSprite::createWithSpriteFrameName("GJ_deleteIcon_001.png");
} break;
}
}
Notification* Notification::create(
Mod* owner, std::string const& title, std::string const& text, CCNode* icon, char const* bg,
std::function<void(Notification*)> callback, bool hideOnClick
std::string const& text,
NotificationIcon icon,
float time
) {
return Notification::create(text, createIcon(icon), time);
}
Notification* Notification::create(std::string const& text, CCSprite* icon, float time) {
auto ret = new Notification();
if (ret && ret->init(owner, title, text, icon, bg, callback, hideOnClick)) {
if (ret && ret->init(text, icon, time)) {
ret->autorelease();
return ret;
}
@ -236,198 +112,89 @@ Notification* Notification::create(
return nullptr;
}
void Notification::showForReal() {
if (!m_pParent) {
CCDirector::sharedDirector()->getRunningScene()->addChild(this);
}
SceneManager::get()->keepAcrossScenes(this);
// haha i am incredibly mature
this->setZOrder(0xB00B1E5);
this->setVisible(true);
static constexpr float const pad = 15.f;
float xMovement = .0f, yMovement = .0f;
float xStart = .0f, yStart = .0f;
switch (m_location) {
case NotificationLocation::TopLeft:
{
xMovement = this->getScaledContentSize().width + pad * 2;
xStart = -this->getScaledContentSize().width / 2 - pad;
yStart = m_pParent->getContentSize().height - pad -
this->getScaledContentSize().height / 2;
}
break;
case NotificationLocation::BottomLeft:
{
xMovement = this->getScaledContentSize().width + pad * 2;
xStart = -this->getScaledContentSize().width / 2 - pad;
yStart = pad + this->getScaledContentSize().height / 2;
}
break;
case NotificationLocation::TopRight:
{
xMovement = -this->getScaledContentSize().width - pad * 2;
xStart = m_pParent->getContentSize().width +
this->getScaledContentSize().width / 2 + pad;
yStart = m_pParent->getContentSize().height - pad -
this->getScaledContentSize().height / 2;
}
break;
case NotificationLocation::BottomRight:
{
xMovement = -this->getScaledContentSize().width - pad * 2;
xStart = m_pParent->getContentSize().width +
this->getScaledContentSize().width / 2 + pad;
yStart = pad + this->getScaledContentSize().height / 2;
}
break;
case NotificationLocation::BottomCenter:
{
yMovement = pad * 2 + this->getScaledContentSize().height;
xStart = m_pParent->getContentSize().width / 2;
yStart = -pad - this->getScaledContentSize().height / 2;
}
break;
case NotificationLocation::TopCenter:
{
yMovement = -pad * 2 - this->getScaledContentSize().height;
xStart = m_pParent->getContentSize().width / 2;
yStart = m_pParent->getContentSize().height + pad +
this->getScaledContentSize().height / 2;
}
break;
}
m_hideDest = CCPoint { xStart, yStart };
m_showDest = CCPoint { xStart + xMovement, yStart + yMovement };
GameSoundManager::sharedManager()->playEffect("newNotif03.ogg"_spr, 1.f, 1.f, 1.f);
this->setPosition(xStart, yStart);
this->animateIn();
void Notification::setString(std::string const& text) {
m_label->setString(text.c_str());
this->updateLayout();
}
void Notification::hide() {
// if this notification has already been hidden,
// don't do anything
if (m_hiding || !NotificationManager::get()->isInQueue(this)) {
return;
void Notification::setIcon(NotificationIcon icon) {
this->setIcon(createIcon(icon));
}
void Notification::setIcon(cocos2d::CCSprite* icon) {
if (m_icon) {
m_icon->removeFromParent();
}
GameSoundManager::sharedManager()->playEffect("byeNotif00.ogg"_spr, 1.f, 1.f, 1.f);
m_hiding = true;
this->animateOut();
if (m_icon = icon) {
m_bg->addChild(icon);
}
this->updateLayout();
}
void Notification::setTime(float time) {
m_time = time;
this->wait();
}
void Notification::animateIn() {
this->runAction(CCEaseInOut::create(CCMoveTo::create(.3f, m_showDest), 6.f));
m_label->setOpacity(0);
m_icon->setOpacity(0);
m_bg->setOpacity(0);
m_label->runAction(CCFadeTo::create(NOTIFICATION_FADEIN, 255));
m_icon->runAction(CCFadeTo::create(NOTIFICATION_FADEIN, 255));
m_bg->runAction(CCFadeTo::create(NOTIFICATION_FADEIN, 150));
}
void Notification::animateOut() {
m_label->runAction(CCFadeTo::create(NOTIFICATION_FADEOUT, 0));
m_icon->runAction(CCFadeTo::create(NOTIFICATION_FADEOUT, 0));
m_bg->runAction(CCFadeTo::create(NOTIFICATION_FADEOUT, 0));
}
void Notification::show() {
if (!m_showing) {
if (!s_queue->containsObject(this)) {
s_queue->addObject(this);
}
if (s_queue->firstObject() != this) {
return;
}
if (!this->getParent()) {
auto winSize = CCDirector::get()->getWinSize();
this->setPosition(winSize.width / 2, winSize.height / 4);
CCDirector::get()->getRunningScene()->addChild(this);
}
SceneManager::get()->keepAcrossScenes(this);
m_showing = true;
}
this->runAction(CCSequence::create(
CCCallFunc::create(this, callfunc_selector(Notification::animateIn)),
// wait for fade-in to finish
CCDelayTime::create(NOTIFICATION_FADEIN),
CCCallFunc::create(this, callfunc_selector(Notification::wait)),
nullptr
));
}
void Notification::wait() {
this->stopAllActions();
if (m_time) {
this->runAction(CCSequence::create(
CCDelayTime::create(m_time),
CCCallFunc::create(this, callfunc_selector(Notification::hide)), nullptr
CCCallFunc::create(this, callfunc_selector(Notification::hide)),
nullptr
));
}
}
void Notification::animateOut() {
void Notification::hide() {
this->stopAllActions();
this->runAction(CCSequence::create(
CCEaseInOut::create(CCMoveTo::create(.3f, { m_hideDest }), 6.f),
CCCallFunc::create(this, callfunc_selector(Notification::hidden)), nullptr
CCCallFunc::create(this, callfunc_selector(Notification::animateOut)),
// wait for fade-out to finish
CCDelayTime::create(NOTIFICATION_FADEOUT),
CCCallFunc::create(this, callfunc_selector(Notification::showNextNotification)),
nullptr
));
}
void Notification::animateOutClicked() {
this->runAction(CCSequence::create(
CCEaseBackIn::create(CCScaleTo::create(.2f, .0f)),
CCCallFunc::create(this, callfunc_selector(Notification::hidden)), nullptr
));
}
void Notification::animateClicking() {
this->runAction(CCEaseInOut::create(
CCScaleTo::create(.1f, ((m_clicking && m_hovered) ? m_targetScale * .9f : m_targetScale)),
2.f
));
}
void Notification::show(NotificationLocation location, float time) {
if (location == NotificationLocation::TopCenter) {
// the notification is larger at top center to
// be more easily readable on mobile
this->setScale(1.5f);
}
else {
this->setScale(1.2f);
}
m_targetScale = m_fScaleX;
m_time = time;
m_location = location;
NotificationManager::get()->push(this);
}
void Notification::hidden() {
NotificationManager::get()->pop(this);
this->removeFromParent();
SceneManager::get()->forget(this);
}
NotificationBuilder Notification::build() {
return std::move(NotificationBuilder());
}
Notification* NotificationBuilder::show() {
auto icon = m_iconNode;
if (!icon && m_icon.size()) {
icon = CCSprite::create(m_icon.c_str());
if (!icon) icon = CCSprite::createWithSpriteFrameName(m_icon.c_str());
}
auto notif = Notification::create(
m_owner, m_title, m_text, icon, m_bg.c_str(), m_callback, m_hideOnClick
);
notif->show(m_location, m_time);
return notif;
}
bool NotificationManager::isInQueue(Notification* notification) {
auto location = notification->m_location;
if (m_notifications.count(location)) {
return utils::ranges::contains(m_notifications.at(location), Ref(notification));
}
return false;
}
void NotificationManager::push(Notification* notification) {
auto location = notification->m_location;
if (!m_notifications.count(location)) {
m_notifications[location] = { notification };
notification->showForReal();
}
else {
m_notifications[location].push_back(notification);
}
}
void NotificationManager::pop(Notification* notification) {
auto location = notification->m_location;
if (m_notifications.count(location)) {
auto ref = Ref(notification);
ranges::remove(m_notifications.at(location), ref);
if (!m_notifications.at(location).size()) {
m_notifications.erase(location);
}
else {
m_notifications.at(location).front()->showForReal();
}
}
}
NotificationManager* NotificationManager::get() {
static auto inst = new NotificationManager;
return inst;
}

View file

@ -295,8 +295,7 @@ template <class Json>
void JsonMaybeObject<Json>::checkUnknownKeys() {
for (auto& [key, _] : self().m_json.items()) {
if (!m_knownKeys.count(key)) {
// log::debug(self().m_hierarchy + " contains unknown key \"" + key + "\"");
log::debug("{} contains unknown key \"{}\"", self().m_hierarchy, key);
log::warn("{} contains unknown key \"{}\"", self().m_hierarchy, key);
}
}
}

View file

@ -1,6 +1,7 @@
#include <Geode/utils/file.hpp>
#include <Geode/utils/string.hpp>
#include <Geode/utils/map.hpp>
#include <Geode/loader/Log.hpp>
#include <fstream>
#include <../support/zip_support/ZipUtils.h>
#include <../support/zip_support/ioapi.h>
@ -155,7 +156,7 @@ public:
while (true) {
// Read file and add to entries
unz_file_pos pos;
if (unzGetFilePos(m_zip, &pos)) {
if (unzGetFilePos(m_zip, &pos) == UNZ_OK) {
m_entries.insert({
fileName, ZipEntry {
.m_pos = pos,
@ -166,8 +167,8 @@ public:
}
// Read next file, or break on error
if (unzGoToNextFile64(
m_zip, &fileInfo, fileName, sizeof(fileName) - 1)
) {
m_zip, &fileInfo, fileName, sizeof(fileName) - 1
) != UNZ_OK) {
break;
}
}
@ -181,16 +182,16 @@ public:
auto entry = m_entries.at(name);
if (!unzGoToFilePos(m_zip, &entry.m_pos)) {
if (unzGoToFilePos(m_zip, &entry.m_pos) != UNZ_OK) {
return Err("Unable to navigate to entry");
}
if (!unzOpenCurrentFile(m_zip)) {
if (unzOpenCurrentFile(m_zip) != UNZ_OK) {
return Err("Unable to open entry");
}
byte_array res;
res.reserve(entry.m_uncompressedSize);
res.resize(entry.m_uncompressedSize);
auto size = unzReadCurrentFile(m_zip, res.data(), entry.m_uncompressedSize);
if (size == 0 || size == entry.m_uncompressedSize) {
if (size < 0 || size != entry.m_uncompressedSize) {
return Err("Unable to extract entry");
}
unzCloseCurrentFile(m_zip);
@ -232,6 +233,7 @@ Result<Unzip> Unzip::create(Path const& file) {
}
auto impl = new UnzipImpl(zip, file);
if (!impl->loadEntries()) {
delete impl;
return Err("Unable to read zip file");
}
return Ok(Unzip(impl));