Onionskin, input rebinding, alt icons, settings refactor

This commit is contained in:
2025-09-08 20:18:28 -04:00
parent 4d098f8f1c
commit 9ad464a74a
14 changed files with 1087 additions and 723 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -10,6 +10,7 @@
#include <algorithm>
#include <chrono>
#include <climits>
#include <cmath>
#include <cstring>
#include <filesystem>
@@ -342,8 +343,6 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
map[insertIndex] = value;
}
#define DEFINE_ENUM_TO_STRING_FUNCTION(function, array, count) \
static inline std::string function(s32 index) \
{ \
@@ -356,19 +355,30 @@ static inline void map_insert_shift(std::map<int, T>& map, s32 index, const T& v
return static_cast<enumType>(string_to_enum(string, stringArray, count)); \
};
#define DATATYPE_LIST \
X(TYPE_INT, s32) \
X(TYPE_BOOL, bool) \
X(TYPE_FLOAT, f32) \
X(TYPE_STRING, std::string) \
X(TYPE_IVEC2, ivec2) \
X(TYPE_IVEC2_WH, ivec2) \
X(TYPE_VEC2, vec2) \
X(TYPE_VEC2_WH, vec2) \
X(TYPE_VEC3, vec3) \
X(TYPE_VEC4, vec4)
enum DataType
{
TYPE_INT,
TYPE_BOOL,
TYPE_FLOAT,
TYPE_STRING,
TYPE_IVEC2,
TYPE_VEC2,
TYPE_VEC3,
TYPE_VEC4
#define X(symbol, ctype) symbol,
DATATYPE_LIST
#undef X
};
#define DATATYPE_TO_CTYPE(dt) DATATYPE_CTYPE_##dt
#define X(symbol, ctype) typedef ctype DATATYPE_CTYPE_##symbol;
DATATYPE_LIST
#undef X
enum OriginType
{
ORIGIN_TOP_LEFT,

View File

@@ -7,104 +7,106 @@
const u8 TEXTURE_ATLAS[] =
{
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x78,
0x04, 0x03, 0x00, 0x00, 0x00, 0xff, 0x33, 0xea, 0xfd, 0x00, 0x00, 0x00,
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xa0,
0x02, 0x03, 0x00, 0x00, 0x00, 0x8e, 0x1e, 0x81, 0x1f, 0x00, 0x00, 0x00,
0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x12, 0x00, 0x00, 0x0b,
0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0f, 0x50, 0x4c,
0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x60, 0x60, 0x60, 0xff,
0xff, 0xff, 0x60, 0x60, 0x60, 0x15, 0x68, 0x14, 0xc2, 0x00, 0x00, 0x00,
0x03, 0x74, 0x52, 0x4e, 0x53, 0x00, 0x00, 0x00, 0xfa, 0x76, 0xc4, 0xde,
0x00, 0x00, 0x03, 0xf4, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0x98,
0x6d, 0x6e, 0xde, 0x38, 0x0c, 0x84, 0x27, 0x20, 0x0f, 0xb0, 0xc1, 0x5e,
0x60, 0x81, 0x5c, 0x80, 0x9b, 0xe1, 0x01, 0x08, 0x88, 0xf7, 0x3f, 0xd3,
0x42, 0x94, 0x04, 0xa9, 0xad, 0x83, 0xd8, 0x0b, 0x34, 0xed, 0x8f, 0x4e,
0x90, 0xc8, 0xb4, 0xf9, 0x84, 0x5f, 0xd2, 0xeb, 0x20, 0xf8, 0x3f, 0x22,
0x71, 0x29, 0x31, 0x94, 0x9a, 0x97, 0xd7, 0xe1, 0x4f, 0xd6, 0x12, 0x42,
0xc6, 0xe1, 0x4f, 0xda, 0xf1, 0x0b, 0x99, 0x0e, 0x68, 0xdd, 0x07, 0x68,
0x90, 0x22, 0x87, 0x4b, 0x23, 0x09, 0x29, 0x73, 0x03, 0x29, 0x04, 0x64,
0x86, 0x22, 0xeb, 0x72, 0x7b, 0x90, 0xc2, 0x92, 0xed, 0x94, 0x84, 0xae,
0x1c, 0x80, 0x90, 0x38, 0x1c, 0xd4, 0x80, 0xd3, 0x16, 0x9b, 0x00, 0xc9,
0x61, 0xd1, 0xc4, 0xa4, 0xb2, 0xd7, 0xed, 0x30, 0x53, 0xba, 0x02, 0x84,
0x10, 0x13, 0x94, 0xac, 0xdb, 0x15, 0x26, 0x1b, 0xe9, 0x19, 0xdf, 0x02,
0x5e, 0x16, 0xed, 0x04, 0x1c, 0x2d, 0x0a, 0xc8, 0xe6, 0xd9, 0x01, 0x1d,
0xbd, 0x63, 0x0b, 0xd2, 0x3a, 0x20, 0xc4, 0x01, 0x28, 0x41, 0x60, 0x10,
0xe5, 0x8f, 0x66, 0xe2, 0x1d, 0x58, 0xd5, 0x17, 0x46, 0x2c, 0x00, 0x0b,
0x88, 0xee, 0x1f, 0xa1, 0x64, 0xcd, 0x08, 0x3c, 0xe7, 0x7c, 0x02, 0x8d,
0x5e, 0x01, 0x54, 0x35, 0x55, 0x21, 0x95, 0x06, 0x6d, 0x02, 0x7b, 0x70,
0xab, 0x4b, 0xf5, 0xb0, 0x14, 0x5d, 0x17, 0xc0, 0xdc, 0x1a, 0xca, 0x52,
0xa0, 0x9e, 0xcc, 0x18, 0x00, 0x94, 0x31, 0x7e, 0xf0, 0xf3, 0xcd, 0x87,
0xc0, 0x2f, 0x96, 0x92, 0x71, 0x65, 0x8b, 0x61, 0xd9, 0xd8, 0xda, 0xe7,
0xa2, 0x71, 0x6c, 0x81, 0xec, 0x6b, 0x1e, 0x00, 0xb9, 0x89, 0xa3, 0xbd,
0x70, 0x74, 0xaa, 0x83, 0x40, 0x07, 0x85, 0x7c, 0x07, 0x80, 0x37, 0x66,
0xd0, 0x8e, 0xd0, 0xcc, 0x4c, 0x12, 0xb0, 0xa2, 0x01, 0xc6, 0xe8, 0x9f,
0x8b, 0xb5, 0x0a, 0xa0, 0x91, 0xe4, 0xf0, 0xef, 0x84, 0x32, 0xb3, 0x91,
0xb1, 0x00, 0x25, 0x20, 0x65, 0x8b, 0xc9, 0x04, 0xa4, 0x00, 0xf1, 0xcc,
0x66, 0xb5, 0xd0, 0x9b, 0x2d, 0x60, 0x9d, 0x13, 0x96, 0x80, 0x56, 0xcf,
0xc5, 0xa0, 0xde, 0x9a, 0x7b, 0x0b, 0xf1, 0x6c, 0xcc, 0x13, 0x80, 0x10,
0x07, 0x50, 0xcf, 0xc5, 0xc0, 0x10, 0x31, 0x53, 0x36, 0x4b, 0x7a, 0x8a,
0x0f, 0x40, 0xb1, 0xce, 0xc9, 0x4a, 0x49, 0x4c, 0xe9, 0xdd, 0x56, 0x93,
0xe8, 0xdf, 0xe2, 0x42, 0x60, 0x45, 0xd8, 0xe7, 0x64, 0x15, 0xdd, 0x9f,
0x67, 0xb7, 0x21, 0x20, 0x61, 0x50, 0xd2, 0x33, 0x67, 0xd1, 0x64, 0xb7,
0x0d, 0xc9, 0xa8, 0xb6, 0x92, 0x00, 0xe9, 0x91, 0xd5, 0x25, 0x47, 0x1d,
0x4a, 0x56, 0x5f, 0xd1, 0x01, 0x69, 0x6e, 0x05, 0x75, 0x00, 0x62, 0xd3,
0x8e, 0xb2, 0x6b, 0xb4, 0x5e, 0x83, 0xcb, 0x9c, 0x83, 0xab, 0x2c, 0x1a,
0x91, 0xf4, 0xec, 0x40, 0xd9, 0xc2, 0x08, 0xba, 0x5e, 0x6d, 0x8d, 0xe8,
0x55, 0x21, 0xcb, 0x0e, 0xe0, 0xf5, 0x9f, 0xb2, 0x31, 0xec, 0xeb, 0xcd,
0xb7, 0x26, 0x9a, 0x79, 0xd8, 0x58, 0xf6, 0x73, 0x89, 0xed, 0xf5, 0x96,
0xc8, 0xbd, 0xee, 0xd4, 0xc4, 0x28, 0xc6, 0xbd, 0x96, 0x32, 0xf6, 0x7b,
0xa0, 0xd6, 0x5d, 0xbc, 0xac, 0xf0, 0x38, 0xa4, 0x05, 0x9c, 0xa9, 0xfd,
0x05, 0xfc, 0x4d, 0x76, 0x52, 0xc6, 0x6f, 0xb0, 0x09, 0x18, 0x5e, 0x01,
0x89, 0x8c, 0xb3, 0x3b, 0x52, 0x77, 0xc9, 0x58, 0x87, 0xb1, 0x03, 0x98,
0xc0, 0xcb, 0x2b, 0xe0, 0x54, 0x00, 0xd6, 0x42, 0x5d, 0x00, 0xeb, 0x80,
0x60, 0x7d, 0x70, 0x49, 0x8d, 0xc4, 0xce, 0x94, 0xa4, 0x8d, 0xc1, 0x3b,
0xd0, 0xc4, 0xc4, 0xd0, 0x1c, 0x62, 0x52, 0xe1, 0x06, 0x50, 0x29, 0xed,
0x08, 0x4e, 0x37, 0xa0, 0xdf, 0x33, 0x11, 0x2e, 0x09, 0x51, 0x40, 0x75,
0xa1, 0xed, 0x94, 0xf0, 0x2a, 0x8d, 0x0d, 0xd7, 0x80, 0x81, 0x26, 0x45,
0x9d, 0x29, 0x55, 0x80, 0xeb, 0x94, 0x28, 0x34, 0x61, 0x55, 0x6e, 0x05,
0x88, 0x19, 0x50, 0x01, 0x7e, 0x2c, 0x5a, 0x69, 0x20, 0x19, 0x73, 0x2a,
0xb6, 0xfa, 0x07, 0x91, 0x0a, 0x70, 0xd5, 0x56, 0x23, 0xb9, 0xc7, 0xb9,
0x46, 0x4a, 0xab, 0x00, 0x57, 0x83, 0x33, 0xd2, 0x16, 0x60, 0x1b, 0x20,
0x4a, 0x17, 0x5b, 0x63, 0x47, 0x38, 0x52, 0xd2, 0x66, 0x57, 0x9b, 0x6f,
0x5d, 0xd0, 0x3a, 0x40, 0x31, 0x80, 0x52, 0xab, 0xc6, 0xe5, 0xf6, 0xfe,
0x1a, 0xd9, 0x11, 0x1b, 0x43, 0x4a, 0x4c, 0xc5, 0xb0, 0xc7, 0x22, 0xd1,
0x7c, 0x27, 0x76, 0x24, 0xc9, 0x5c, 0x40, 0x58, 0x2d, 0xd3, 0x56, 0x27,
0x01, 0x9e, 0x00, 0x01, 0x88, 0x33, 0x46, 0x18, 0x9f, 0x0f, 0xb5, 0x4d,
0xa7, 0x56, 0xed, 0xec, 0x8a, 0xf5, 0x1e, 0x36, 0xb0, 0xf9, 0x4c, 0xeb,
0xfd, 0x2d, 0x86, 0x63, 0x92, 0x3e, 0x32, 0xa6, 0x83, 0x43, 0xc0, 0xba,
0x60, 0xa0, 0xd4, 0xf2, 0xfd, 0x2d, 0x19, 0x23, 0x00, 0x73, 0x54, 0x2b,
0x01, 0xf9, 0x16, 0x30, 0x24, 0xfb, 0x17, 0x94, 0x49, 0x66, 0xc1, 0x41,
0x7a, 0x54, 0x00, 0xa8, 0x03, 0xb4, 0xfd, 0x17, 0x8c, 0x10, 0x03, 0x98,
0xa9, 0xd6, 0xb4, 0x95, 0xb2, 0x02, 0x84, 0xb5, 0xd9, 0x19, 0xb1, 0xb9,
0xcc, 0x46, 0x2a, 0x04, 0x1d, 0x18, 0x95, 0xda, 0x0a, 0x20, 0xe8, 0x3a,
0x01, 0x00, 0x59, 0x2a, 0xc0, 0xd7, 0x3b, 0x51, 0x57, 0x00, 0x94, 0x26,
0xb0, 0x47, 0xa5, 0x50, 0x08, 0x99, 0x05, 0x60, 0x34, 0x11, 0xc7, 0x69,
0xdc, 0x35, 0x9c, 0x80, 0x49, 0xfa, 0xda, 0xd5, 0x18, 0x80, 0x61, 0x48,
0x56, 0x97, 0x6c, 0x03, 0x75, 0xbb, 0x0d, 0x0f, 0x7a, 0x2a, 0x02, 0x8d,
0xb8, 0x04, 0xb2, 0x04, 0x40, 0x17, 0xa0, 0x19, 0x11, 0x38, 0x24, 0x6b,
0xd2, 0x86, 0x2f, 0x95, 0x1c, 0xf1, 0xd4, 0x6e, 0x01, 0x1b, 0x62, 0x00,
0x2f, 0x9c, 0x86, 0x30, 0x84, 0x31, 0xba, 0xf1, 0x01, 0x20, 0x44, 0x07,
0x68, 0x9f, 0x01, 0xfb, 0x73, 0x65, 0x00, 0xb4, 0x7b, 0x80, 0x10, 0x13,
0xa0, 0x5d, 0x03, 0x27, 0x54, 0xf7, 0x63, 0x01, 0xb4, 0x0d, 0x94, 0xed,
0x6b, 0x3d, 0x6a, 0x50, 0x92, 0x5c, 0x00, 0xe3, 0x63, 0xe0, 0xdc, 0x39,
0xbe, 0x00, 0xff, 0x10, 0x38, 0x95, 0xb4, 0x09, 0x78, 0xec, 0x94, 0x32,
0xe9, 0x99, 0x40, 0x96, 0xf6, 0x07, 0xf2, 0x6e, 0x6b, 0xf9, 0x7f, 0x54,
0x74, 0x01, 0x67, 0x5b, 0x81, 0xf2, 0xbf, 0x05, 0x28, 0xb1, 0x9a, 0x75,
0x0f, 0x90, 0xe1, 0x98, 0x81, 0xb5, 0xdb, 0x75, 0x5c, 0x67, 0x5e, 0x01,
0x37, 0xb5, 0x8b, 0x7e, 0xac, 0xfc, 0x4e, 0x81, 0xef, 0x44, 0xbb, 0x06,
0x56, 0x05, 0x9f, 0x02, 0x8d, 0x25, 0xff, 0x3a, 0x00, 0x7f, 0x80, 0xdf,
0x12, 0x20, 0x69, 0x4f, 0x00, 0x76, 0xd9, 0x7d, 0x40, 0x08, 0x9a, 0xf0,
0x01, 0x60, 0xa0, 0xbd, 0x3c, 0x00, 0x58, 0x45, 0x8b, 0xfd, 0x0c, 0x80,
0x3f, 0xea, 0xdf, 0x2f, 0x05, 0xbe, 0xbe, 0xe8, 0xe7, 0x73, 0x78, 0x3e,
0xe9, 0xe7, 0x7b, 0x69, 0x13, 0xf6, 0xe7, 0x4c, 0x3f, 0x06, 0x94, 0xa5,
0xb8, 0x05, 0xec, 0x10, 0x8e, 0xfb, 0x80, 0x56, 0x00, 0xdc, 0x7e, 0x3f,
0x54, 0x08, 0xc7, 0x13, 0x40, 0xc9, 0x78, 0x04, 0xa0, 0x39, 0x9e, 0x01,
0xe5, 0x94, 0x53, 0x9f, 0xfd, 0x33, 0xf0, 0x3f, 0x53, 0x26, 0x87, 0x23,
0x59, 0xac, 0x2b, 0x06, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44,
0xae, 0x42, 0x60, 0x82
0x12, 0x01, 0xd2, 0xdd, 0x7e, 0xfc, 0x00, 0x00, 0x00, 0x0c, 0x50, 0x4c,
0x54, 0x45, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x60,
0x60, 0x60, 0x58, 0xe6, 0xdc, 0x65, 0x00, 0x00, 0x00, 0x02, 0x74, 0x52,
0x4e, 0x53, 0x00, 0x00, 0x76, 0x93, 0xcd, 0x38, 0x00, 0x00, 0x04, 0x15,
0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xe5, 0xd3, 0xb1, 0x6a, 0x1c, 0x47,
0x1c, 0xc7, 0xf1, 0x2f, 0xb3, 0xc4, 0x2c, 0xe3, 0x63, 0xaf, 0x4b, 0x91,
0x6a, 0x49, 0xb5, 0xcc, 0x1d, 0xf6, 0x81, 0x1b, 0xb1, 0x36, 0x56, 0x1e,
0x21, 0x75, 0xde, 0xc0, 0x79, 0x8a, 0xc1, 0x95, 0x48, 0x61, 0x5c, 0x24,
0xfd, 0x20, 0x1c, 0x98, 0xfc, 0x77, 0xd1, 0x2d, 0x49, 0x63, 0x14, 0x81,
0xb6, 0x34, 0x41, 0xe4, 0x19, 0x5c, 0x8a, 0xb3, 0x09, 0x02, 0x37, 0xe1,
0x72, 0xf8, 0x92, 0x19, 0x5d, 0xb4, 0x91, 0x6e, 0x94, 0x20, 0xab, 0x48,
0xe1, 0x1f, 0x0c, 0x03, 0xf7, 0xe1, 0xfe, 0x33, 0xf3, 0xdf, 0x19, 0xae,
0x8f, 0x76, 0x5c, 0x44, 0x59, 0x28, 0x3a, 0x34, 0x00, 0x22, 0xe8, 0xd6,
0xb4, 0x00, 0x18, 0x0f, 0xda, 0xa1, 0xff, 0x60, 0x86, 0x72, 0xda, 0x96,
0x5a, 0x3c, 0x2c, 0x44, 0x89, 0xd8, 0x00, 0x63, 0x43, 0x89, 0x46, 0xbb,
0x92, 0x00, 0x22, 0x46, 0xc4, 0xc7, 0x52, 0xe6, 0x27, 0x87, 0xc6, 0xb8,
0xf8, 0x43, 0x66, 0x89, 0xb3, 0xb2, 0x68, 0x23, 0x8e, 0x0a, 0x5f, 0x4d,
0x61, 0x07, 0x65, 0x09, 0xa5, 0x06, 0x50, 0xae, 0x2a, 0x21, 0xaf, 0x94,
0x65, 0x77, 0xd1, 0xac, 0x36, 0xd0, 0x51, 0x69, 0x1b, 0x61, 0x52, 0xf4,
0xec, 0xbe, 0x3f, 0x5e, 0x51, 0xb7, 0xe8, 0xb7, 0x62, 0xa9, 0x0c, 0x11,
0x6a, 0x0d, 0xd9, 0x7a, 0xdd, 0x73, 0x3a, 0x41, 0xf3, 0x06, 0xb4, 0xc7,
0x05, 0xd0, 0x1a, 0x46, 0xeb, 0xd5, 0x49, 0xed, 0x74, 0xab, 0x37, 0x07,
0x8f, 0x50, 0x34, 0xf0, 0xe0, 0xc1, 0xbb, 0x99, 0xb1, 0xca, 0x07, 0x88,
0x07, 0x0c, 0xbb, 0x52, 0x1e, 0x38, 0x39, 0xe9, 0x2f, 0x00, 0x11, 0x6a,
0x91, 0x16, 0x07, 0x3c, 0x98, 0x51, 0xf7, 0x59, 0xab, 0xb7, 0x9a, 0x48,
0xcf, 0x6d, 0x53, 0xb7, 0xc3, 0x7c, 0x29, 0x22, 0x14, 0x22, 0xdd, 0x58,
0xe4, 0x0c, 0x86, 0xc4, 0x2d, 0xdf, 0x85, 0xd7, 0x85, 0xd3, 0xdd, 0xa5,
0x32, 0x7a, 0xbd, 0x74, 0x39, 0x94, 0xba, 0x1f, 0xb6, 0x48, 0x26, 0xd2,
0xd7, 0xeb, 0x45, 0x1b, 0xa0, 0xc6, 0xb4, 0x43, 0x99, 0xe3, 0xf7, 0x76,
0xb2, 0x6e, 0x4e, 0x03, 0x84, 0x6f, 0x73, 0x01, 0x0f, 0x7f, 0xfb, 0xf9,
0xed, 0xe4, 0xfd, 0xfc, 0x34, 0x57, 0xb6, 0xac, 0x94, 0x1b, 0xa0, 0xfd,
0x7c, 0xaf, 0x3e, 0x5d, 0x1e, 0x4f, 0xfe, 0x82, 0x59, 0xa5, 0xed, 0x00,
0x3b, 0xd3, 0x9d, 0xe9, 0xc4, 0x10, 0x4a, 0x51, 0x19, 0x06, 0x50, 0xda,
0xd9, 0xba, 0x59, 0xb7, 0x39, 0xda, 0xd5, 0x7e, 0x3c, 0x2c, 0x9e, 0x75,
0x45, 0xaf, 0xe7, 0x6b, 0x97, 0x73, 0xef, 0xa5, 0x76, 0xbb, 0x03, 0xb0,
0x68, 0x50, 0xf3, 0xa5, 0xbd, 0xcb, 0x8f, 0xcf, 0x0a, 0xb7, 0x3c, 0xde,
0x6e, 0x49, 0x3b, 0xdd, 0x19, 0x8b, 0x8c, 0x12, 0x4d, 0xd4, 0x8e, 0x7a,
0x7e, 0xc6, 0x7f, 0x47, 0xd9, 0x30, 0x92, 0xef, 0x23, 0x8c, 0x58, 0x4e,
0x7d, 0x53, 0xb9, 0x30, 0x80, 0x15, 0x18, 0x4f, 0x1c, 0x22, 0x94, 0x10,
0x07, 0x64, 0x2b, 0x62, 0x36, 0x17, 0xba, 0x54, 0xb2, 0x57, 0x42, 0x0e,
0xd3, 0x55, 0x28, 0x11, 0x41, 0xb7, 0xb5, 0x03, 0xd9, 0x23, 0xc2, 0xe1,
0x8c, 0xfc, 0xed, 0xc3, 0x32, 0x74, 0x36, 0x5c, 0xb4, 0x92, 0x85, 0x2d,
0x01, 0xd4, 0xa2, 0xa7, 0xea, 0x8a, 0xaa, 0x2a, 0xba, 0xca, 0xf4, 0xf8,
0x92, 0x4d, 0xa9, 0xbb, 0x8d, 0xc5, 0x3f, 0x7d, 0x6a, 0x44, 0x44, 0x0c,
0x78, 0xd4, 0x41, 0x2c, 0xc5, 0xbd, 0x03, 0x18, 0xc0, 0xfa, 0x52, 0xdb,
0x58, 0x8a, 0xc3, 0x97, 0x30, 0x94, 0xda, 0xf7, 0x46, 0xc8, 0x4b, 0xd4,
0x1e, 0x8b, 0xd7, 0x5c, 0x2c, 0x5e, 0x5b, 0x69, 0x8d, 0x27, 0x07, 0x65,
0xd5, 0xc4, 0x32, 0x6c, 0xd7, 0x8b, 0x33, 0x1e, 0x4a, 0x8c, 0xd7, 0xcf,
0x60, 0x38, 0xa0, 0x17, 0x1b, 0xfe, 0x11, 0xc1, 0xc1, 0xd0, 0x92, 0xf0,
0x8f, 0x4d, 0xa9, 0xc7, 0x16, 0x86, 0x26, 0x22, 0xde, 0xf8, 0x7d, 0xab,
0xab, 0x7d, 0xfb, 0x28, 0xd1, 0xf6, 0x9b, 0xc4, 0x02, 0x28, 0x20, 0x73,
0xc0, 0x08, 0x78, 0x04, 0xea, 0xd7, 0x4e, 0x9d, 0x83, 0x85, 0x79, 0x80,
0x57, 0xc0, 0x12, 0xb2, 0x43, 0xa7, 0xcf, 0xc1, 0x31, 0x69, 0xa1, 0xee,
0x3c, 0x64, 0x0b, 0x0b, 0x0b, 0x67, 0x44, 0xfa, 0x4c, 0xc4, 0xeb, 0x37,
0x90, 0xf9, 0xa9, 0x87, 0xb1, 0x74, 0x28, 0xdd, 0x89, 0x88, 0xd3, 0x22,
0xa2, 0x7b, 0x28, 0x7e, 0xbf, 0xdf, 0xc2, 0x63, 0x39, 0x23, 0x57, 0xbd,
0x39, 0x07, 0x3f, 0x9e, 0xcf, 0x5d, 0xbd, 0x9c, 0xf7, 0xf0, 0x4b, 0xd3,
0x53, 0x66, 0x9d, 0xb6, 0x9c, 0x4e, 0x50, 0x2e, 0x80, 0x91, 0x03, 0x4b,
0xb6, 0x3f, 0x07, 0xfb, 0xea, 0xb5, 0xb2, 0x54, 0x15, 0xca, 0x8e, 0x60,
0x56, 0xca, 0x01, 0x14, 0xfe, 0x08, 0x45, 0x09, 0xe7, 0xc0, 0x78, 0xbd,
0x3e, 0x2b, 0xe5, 0x10, 0x6a, 0x99, 0x91, 0x8f, 0x2c, 0x10, 0x00, 0x46,
0xcc, 0x66, 0x66, 0x7e, 0x08, 0x5a, 0x7a, 0x72, 0x05, 0x10, 0xd6, 0x20,
0x42, 0x75, 0xff, 0x25, 0x28, 0x81, 0x3c, 0x07, 0x54, 0xd8, 0x95, 0x8d,
0x80, 0x39, 0x05, 0x9a, 0x77, 0xa3, 0xc2, 0x0d, 0x10, 0xd6, 0xa0, 0x0e,
0xb0, 0x7b, 0xd2, 0x43, 0x80, 0x70, 0x72, 0xcb, 0x87, 0x45, 0x59, 0x00,
0x76, 0x12, 0x10, 0xa9, 0x45, 0x5b, 0x50, 0xad, 0xe9, 0xd1, 0x1d, 0x03,
0x28, 0x87, 0xf6, 0x57, 0xc1, 0x78, 0xd0, 0x16, 0x2d, 0x36, 0x01, 0x06,
0xb4, 0xf8, 0x01, 0x36, 0x04, 0x4d, 0x8f, 0x16, 0xb1, 0x01, 0xb4, 0x48,
0x17, 0x46, 0x5c, 0x23, 0x13, 0x09, 0xd0, 0x5e, 0x06, 0x40, 0x49, 0x13,
0xe0, 0xe8, 0x9f, 0x40, 0xcc, 0xd8, 0xa3, 0xa5, 0xe9, 0x03, 0x8c, 0x97,
0xc7, 0x67, 0xa1, 0x57, 0xf1, 0x5a, 0xc7, 0xed, 0xca, 0xd1, 0xa5, 0xc5,
0x8d, 0xff, 0x7b, 0xbb, 0x4d, 0x9f, 0x80, 0x1a, 0xf4, 0x11, 0x09, 0x98,
0xc2, 0xb8, 0x87, 0x6c, 0xb5, 0xdb, 0x33, 0x3e, 0x1b, 0x20, 0x95, 0xb8,
0xf8, 0xb5, 0x59, 0x6f, 0xd2, 0x43, 0x8c, 0x72, 0x0c, 0x10, 0x96, 0xd8,
0x82, 0x85, 0x48, 0xf3, 0x61, 0xc0, 0xc7, 0x02, 0x46, 0xd2, 0xa0, 0x44,
0x5c, 0x12, 0xf4, 0x53, 0xe7, 0xd3, 0xa0, 0x5c, 0x95, 0x84, 0x4a, 0x39,
0x7d, 0x03, 0x90, 0x8b, 0xdc, 0x1c, 0x6e, 0xb3, 0x78, 0xfa, 0x1c, 0xe9,
0x93, 0xa7, 0x7b, 0x95, 0xee, 0xee, 0xff, 0xff, 0xcd, 0x6b, 0x91, 0x36,
0x09, 0x85, 0x48, 0x97, 0x84, 0x4c, 0xa4, 0x4f, 0xbe, 0x0f, 0x16, 0x0d,
0x69, 0xa8, 0x5b, 0xd2, 0x50, 0x74, 0x49, 0x88, 0x3f, 0x6d, 0x3d, 0xe7,
0x54, 0x44, 0x48, 0xc6, 0x88, 0xfc, 0x40, 0x22, 0x9f, 0x0a, 0x88, 0x4d,
0xc0, 0xd7, 0x16, 0x94, 0x67, 0x2b, 0x9f, 0xc4, 0x32, 0x26, 0x51, 0xe9,
0x3b, 0x80, 0x3b, 0xcf, 0xb7, 0xe0, 0xab, 0x2f, 0x01, 0x78, 0xb1, 0x05,
0x4f, 0xbe, 0x00, 0xa0, 0xda, 0x82, 0xef, 0x89, 0xd1, 0x5c, 0xc9, 0x9d,
0x6f, 0x37, 0xb3, 0xbd, 0x0a, 0x9b, 0x55, 0xd5, 0x55, 0xf8, 0x8c, 0x4d,
0x5c, 0x02, 0x92, 0xdb, 0x7a, 0xc2, 0x26, 0xd5, 0x6d, 0x61, 0x28, 0xad,
0x6f, 0x0d, 0x0e, 0x50, 0xf6, 0xa6, 0x00, 0xb7, 0x82, 0xad, 0x73, 0x55,
0xb7, 0x85, 0xeb, 0xdb, 0xae, 0xaf, 0xfb, 0x50, 0xca, 0x0e, 0xf3, 0x65,
0x78, 0x4e, 0xfa, 0x32, 0xe0, 0xd2, 0xc7, 0x80, 0x6a, 0x98, 0x92, 0xdb,
0x7a, 0x01, 0x89, 0x8b, 0x95, 0xbc, 0xd4, 0x78, 0xd2, 0xcf, 0x00, 0x93,
0x7e, 0x38, 0xa0, 0x86, 0xa7, 0x96, 0x78, 0x9c, 0xff, 0xfe, 0x9c, 0xff,
0x04, 0xe2, 0xd8, 0x90, 0xc1, 0x18, 0xe1, 0xf4, 0x9b, 0x00, 0x00, 0x00,
0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
};
const u32 TEXTURE_ATLAS_LENGTH = (u32)std::size(TEXTURE_ATLAS);
const vec2 TEXTURE_ATLAS_SIZE = {96, 120};
const vec2 TEXTURE_ATLAS_SIZE = {96, 160};
enum AtlasType
{
@@ -118,8 +120,8 @@ enum AtlasType
ATLAS_INVISIBLE,
ATLAS_SHOW_RECT,
ATLAS_HIDE_RECT,
ATLAS_SHOW_TARGETS,
ATLAS_HIDE_TARGETS,
ATLAS_PLACEHOLDER,
ATLAS_PLACEHOLDER2,
ATLAS_PAN,
ATLAS_MOVE,
ATLAS_ROTATE,
@@ -145,6 +147,7 @@ enum AtlasType
ATLAS_FRAME,
ATLAS_FRAME_ALT,
ATLAS_TARGET,
ATLAS_TARGET_ALT,
ATLAS_COUNT
};
@@ -197,7 +200,8 @@ const inline AtlasEntry ATLAS_ENTRIES[ATLAS_COUNT] =
{{ 0, 80}, ATLAS_SIZE_OBLONG},
{{16, 80}, ATLAS_SIZE_OBLONG},
{{32, 80}, ATLAS_SIZE_OBLONG},
{{48, 80}, ATLAS_SIZE_BIG}
{{48, 80}, ATLAS_SIZE_BIG},
{{48, 120}, ATLAS_SIZE_BIG}
};
#define ATLAS_POSITION(type) ATLAS_ENTRIES[type].position

View File

@@ -54,8 +54,6 @@ bool anm2_serialize(Anm2* self, const std::string& path)
for (auto& [id, spritesheet] : self->spritesheets)
{
if (id == ID_NONE) continue;
XMLElement* spritesheetElement;
// Spritesheet
@@ -72,8 +70,6 @@ bool anm2_serialize(Anm2* self, const std::string& path)
for (auto& [id, layer] : self->layers)
{
if (id == ID_NONE) continue;
XMLElement* layerElement;
// Layer
@@ -92,8 +88,6 @@ bool anm2_serialize(Anm2* self, const std::string& path)
for (auto& [id, null] : self->nulls)
{
if (id == ID_NONE) continue;
XMLElement* nullElement;
// Null
@@ -101,9 +95,9 @@ bool anm2_serialize(Anm2* self, const std::string& path)
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_NAME], null.name.c_str()); // Name
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_ID], id); // ID
// special case; only serialize if this is true
if (null.isShowRect)
nullElement->SetAttribute(ANM2_ATTRIBUTE_STRINGS[ANM2_ATTRIBUTE_SHOW_RECT], null.isShowRect); // ShowRect
nullsElement->InsertEndChild(nullElement);
}
@@ -114,8 +108,6 @@ bool anm2_serialize(Anm2* self, const std::string& path)
for (auto& [id, event] : self->events)
{
if (id == ID_NONE) continue;
XMLElement* eventElement;
// Event
@@ -137,8 +129,6 @@ bool anm2_serialize(Anm2* self, const std::string& path)
for (auto& [id, animation] : self->animations)
{
if (id == ID_NONE) continue;
XMLElement* animationElement;
XMLElement* rootAnimationElement;
XMLElement* layerAnimationsElement;

View File

@@ -45,14 +45,16 @@
X(TRIGGERS, "Triggers") \
X(TRIGGER, "Trigger")
typedef enum {
typedef enum
{
#define X(name, str) ANM2_ELEMENT_##name,
ANM2_ELEMENT_LIST
#undef X
ANM2_ELEMENT_COUNT
} Anm2Element;
static const char* ANM2_ELEMENT_STRINGS[] = {
const inline char* ANM2_ELEMENT_STRINGS[] =
{
#define X(name, str) str,
ANM2_ELEMENT_LIST
#undef X
@@ -259,6 +261,12 @@ enum Anm2ChangeType
ANM2_CHANGE_SET
};
enum OnionskinDrawOrder
{
ONIONSKIN_BELOW,
ONIONSKIN_ABOVE
};
void anm2_layer_add(Anm2* self);
void anm2_layer_remove(Anm2* self, s32 id);
void anm2_null_add(Anm2* self);

View File

@@ -5,13 +5,12 @@
#define CANVAS_ZOOM_MIN 1.0f
#define CANVAS_ZOOM_MAX 2000.0f
#define CANVAS_ZOOM_DEFAULT 100.0f
#define CANVAS_ZOOM_STEP 10.0f
#define CANVAS_ZOOM_MOD 10.0f
#define CANVAS_ZOOM_STEP 100.0f
#define CANVAS_GRID_MIN 1
#define CANVAS_GRID_MAX 1000
#define CANVAS_GRID_DEFAULT 32
const inline vec2 CANVAS_PIVOT_SIZE = {8, 8};
const inline vec2 CANVAS_PIVOT_SIZE = {4, 4};
const inline vec2 CANVAS_SCALE_DEFAULT = {1.0f, 1.0f};
const inline f32 CANVAS_AXIS_VERTICES[] = {-1.0f, 1.0f};

View File

@@ -7,7 +7,6 @@ static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
case CLIPBOARD_FRAME:
{
Anm2FrameWithReference* frameWithReference = std::get_if<Anm2FrameWithReference>(&self->data);
if (!frameWithReference) break;
anm2_frame_erase(anm2, &frameWithReference->reference);
@@ -16,7 +15,6 @@ static void _clipboard_item_remove(ClipboardItem* self, Anm2* anm2)
case CLIPBOARD_ANIMATION:
{
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
if (!animationWithID) break;
for (auto & [id, animation] : anm2->animations)
@@ -58,7 +56,6 @@ static void _clipboard_item_paste(ClipboardItem* self, ClipboardLocation* locati
case CLIPBOARD_ANIMATION:
{
Anm2AnimationWithID* animationWithID = std::get_if<Anm2AnimationWithID>(&self->data);
if (!animationWithID) break;
s32 index = 0;

View File

@@ -1,5 +1,15 @@
#include "imgui.h"
static bool _imgui_chord_pressed(ImGuiKeyChord chord)
{
if (chord == IMGUI_CHORD_NONE) return false;
ImGuiKey key = (ImGuiKey)(chord & ~ImGuiMod_Mask_);
if (key == ImGuiKey_None) return false;
if (key < ImGuiKey_NamedKey_BEGIN || key >= ImGuiKey_NamedKey_END) return false;
return ImGui::IsKeyChordPressed(chord);
}
static const char* _imgui_f32_format_get(const ImguiItem& item, f32& value)
{
if (item.isEmptyFormat) return "";
@@ -38,7 +48,6 @@ static void _imgui_anm2_open(Imgui* self, const std::string& path)
if (anm2_deserialize(self->anm2, path))
{
window_title_from_path_set(self->window, path);
snapshots_reset(self->snapshots);
imgui_log_push(self, std::format(IMGUI_LOG_FILE_OPEN_FORMAT, path));
}
else
@@ -152,6 +161,7 @@ static void _imgui_item_pre(const ImguiItem& self, ImguiItemType type)
break;
case IMGUI_INPUT_INT:
case IMGUI_INPUT_TEXT:
case IMGUI_INPUT_FLOAT:
case IMGUI_DRAG_FLOAT:
case IMGUI_SLIDER_FLOAT:
case IMGUI_COLOR_EDIT:
@@ -178,6 +188,16 @@ static void _imgui_item_pre(const ImguiItem& self, ImguiItemType type)
default:
break;
}
/*
if (self.is_hotkey())
{
std::string chordString = imgui_string_from_chord_get(imgui->hotkeys[self.hotkey]);
if (isShortcutInLabel)
label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString);
tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString);
}
*/
}
static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType type, bool& isActivated)
@@ -201,7 +221,7 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
ImU32 color = ImGui::GetColorU32(ImGuiCol_Text);
ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd, color, 1.0f);
if (ImGui::IsKeyChordPressed(ImGuiMod_Alt | self.mnemonicKey))
if (_imgui_chord_pressed(ImGuiMod_Alt | self.mnemonicKey))
{
if (!self.isDisabled) isActivated = true;
imgui_close_current_popup(imgui);
@@ -210,20 +230,20 @@ static void _imgui_item_post(const ImguiItem& self, Imgui* imgui, ImguiItemType
if (self.isUseItemActivated && !self.isDisabled) isActivated = ImGui::IsItemActivated();
if (imgui->isContextualActionsEnabled && (self.is_chord() && ImGui::IsKeyChordPressed(self.chord)))
if (self.is_focus_window() && (imgui_window_get() == self.focusWindow))
if (!self.isDisabled) isActivated = true;
if
(
imgui->isContextualActionsEnabled && _imgui_chord_pressed(self.chord_get()) &&
self.is_focus_window() && (imgui_window_get() == self.focusWindow)
)
if (!self.isDisabled) isActivated = true;
if (self.is_tooltip() && ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal))
ImGui::SetTooltip(self.tooltip.c_str());
if (isActivated)
{
if (self.is_undoable())
imgui_snapshot(imgui, self.snapshotAction);
if (self.function)
self.function(imgui);
if (self.is_undoable()) imgui_snapshot(imgui, self.snapshotAction);
if (self.is_function()) self.function(imgui);
if (self.is_popup())
{
@@ -372,6 +392,8 @@ static bool NAME(const ImguiItem& self, Imgui* imgui, bool& boolValue)
ImguiItem checkboxItem = self.copy \
({.label = std::format(IMGUI_INVISIBLE_FORMAT, self.label), .isMnemonicDisabled = true}); \
checkboxItem.isDisabled = false; \
checkboxItem.isSeparator = false; \
checkboxItem.value = 0; \
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
ImGui::SameLine(); \
bool isActivated = ([&] { return FUNCTION; })(); \
@@ -386,6 +408,18 @@ static bool NAME(const ImguiItem& self, Imgui* imgui, VALUE& value, bool& boolVa
ImguiItem checkboxItem = self.copy \
({.label = std::format(IMGUI_INVISIBLE_FORMAT, self.label), .isMnemonicDisabled = true}); \
checkboxItem.isDisabled = false; \
checkboxItem.isSeparator = false; \
checkboxItem.value = 0; \
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
ImGui::SameLine(); \
bool isActivated = ([&](VALUE& value) { return FUNCTION; })(value); \
if (isCheckboxActivated) isActivated = true; \
return isActivated; \
}
#define IMGUI_ITEM_AND_CHECKBOX_FUNCTION(NAME, VALUE, FUNCTION) \
static bool NAME(const ImguiItem& self, const ImguiItem& checkboxItem, Imgui* imgui, VALUE& value, bool& boolValue) \
{ \
bool isCheckboxActivated = _imgui_checkbox(checkboxItem, imgui, boolValue); \
ImGui::SameLine(); \
bool isActivated = ([&](VALUE& value) { return FUNCTION; })(value); \
@@ -403,6 +437,12 @@ IMGUI_ITEM_FUNCTION(_imgui_begin_child, IMGUI_CHILD, ImGui::BeginChild(self.labe
static void _imgui_end_child(void) {ImGui::EndChild(); }
IMGUI_ITEM_VOID_FUNCTION(_imgui_text, IMGUI_TEXT, ImGui::Text(self.label_get()));
IMGUI_ITEM_FUNCTION(_imgui_button, IMGUI_BUTTON, ImGui::Button(self.label_get(), _imgui_item_size_get(self, type)));
IMGUI_ITEM_FUNCTION(_imgui_begin_table, IMGUI_TABLE, ImGui::BeginTable(self.label_get(), self.value, self.flags));
static void _imgui_end_table(void) {ImGui::EndTable(); }
static void _imgui_table_setup_column(const char* text) {ImGui::TableSetupColumn(text); }
static void _imgui_table_headers_row(void) {ImGui::TableHeadersRow(); }
static void _imgui_table_next_row(void) {ImGui::TableNextRow(); }
static void _imgui_table_set_column_index(s32 index) {ImGui::TableSetColumnIndex(index); }
IMGUI_ITEM_FUNCTION(_imgui_selectable, IMGUI_SELECTABLE, ImGui::Selectable(self.label_get(), self.isSelected, self.flags, _imgui_item_size_get(self, type)));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_radio_button, IMGUI_RADIO_BUTTON, s32, ImGui::RadioButton(self.label_get(), &value, self.value));
IMGUI_ITEM_VALUE_FUNCTION(_imgui_color_button, IMGUI_COLOR_BUTTON, vec4, ImGui::ColorButton(self.label_get(), ImVec4(value), self.flags));
@@ -1264,6 +1304,40 @@ static void _imgui_timeline(Imgui* self)
_imgui_end(); // IMGUI_TIMELINE
}
static void _imgui_onionskin(Imgui* self)
{
IMGUI_BEGIN_OR_RETURN(IMGUI_ONIONSKIN, self);
static auto& isEnabled = self->settings->onionskinIsEnabled;
static auto& beforeCount = self->settings->onionskinBeforeCount;
static auto& afterCount = self->settings->onionskinAfterCount;
static auto& beforeColorOffset = self->settings->onionskinBeforeColorOffset;
static auto& afterColorOffset = self->settings->onionskinAfterColorOffset;
static auto& drawOrder = self->settings->onionskinDrawOrder;
_imgui_checkbox(IMGUI_ONIONSKIN_ENABLED, self, isEnabled);
auto onionskin_section = [&](auto& text, auto& count, auto& colorOffset)
{
ImGui::PushID(text.label.c_str());
_imgui_text(text, self);
_imgui_input_int(IMGUI_ONIONSKIN_COUNT.copy({!isEnabled}), self, count);
_imgui_color_edit3(IMGUI_ONIONSKIN_COLOR_OFFSET.copy({!isEnabled}), self, colorOffset);
ImGui::PopID();
};
onionskin_section(IMGUI_ONIONSKIN_BEFORE, beforeCount, beforeColorOffset);
onionskin_section(IMGUI_ONIONSKIN_AFTER, afterCount, afterColorOffset);
ImGui::Separator();
_imgui_text(IMGUI_ONIONSKIN_DRAW_ORDER, self);
_imgui_radio_button(IMGUI_ONIONSKIN_BELOW.copy({!isEnabled}), self, drawOrder);
_imgui_radio_button(IMGUI_ONIONSKIN_ABOVE.copy({!isEnabled}), self, drawOrder);
_imgui_end(); // IMGUI_ONIONSKIN
}
static void _imgui_taskbar(Imgui* self)
{
static ImguiPopupState exitConfirmState = IMGUI_POPUP_STATE_CLOSED;
@@ -1277,15 +1351,15 @@ static void _imgui_taskbar(Imgui* self)
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
Anm2Item* item = anm2_item_from_reference(self->anm2, self->reference);
_imgui_selectable(IMGUI_FILE.copy({}), self);
_imgui_selectable(IMGUI_FILE, self);
if (imgui_begin_popup(IMGUI_FILE.popup, self))
{
_imgui_selectable(IMGUI_NEW, self);
_imgui_selectable(IMGUI_NEW.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_OPEN, self);
_imgui_selectable(IMGUI_SAVE, self);
_imgui_selectable(IMGUI_SAVE_AS, self);
_imgui_selectable(IMGUI_EXPLORE_ANM2_LOCATION, self);
_imgui_selectable(IMGUI_SAVE.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_SAVE_AS.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_EXPLORE_ANM2_LOCATION.copy({self->anm2->path.empty()}), self);
_imgui_selectable(IMGUI_EXIT, self);
imgui_end_popup(self);
}
@@ -1440,6 +1514,7 @@ static void _imgui_taskbar(Imgui* self)
_imgui_checkbox_color_edit4(IMGUI_FRAME_PROPERTIES_TINT.copy({!isTint}), self, tint, isTint);
_imgui_checkbox_color_edit3(IMGUI_FRAME_PROPERTIES_COLOR_OFFSET.copy({!isColorOffset}), self, colorOffset, isColorOffset);
_imgui_checkbox_checkbox(IMGUI_FRAME_PROPERTIES_VISIBLE.copy({!isVisibleSet}), self, isVisible, isVisibleSet);
ImGui::NewLine();
_imgui_checkbox_checkbox(IMGUI_FRAME_PROPERTIES_INTERPOLATED.copy({!isInterpolatedSet}), self, isInterpolated, isInterpolatedSet);
_imgui_end_child(); // IMGUI_FOOTER_CHILD
@@ -1658,10 +1733,76 @@ static void _imgui_taskbar(Imgui* self)
if (imgui_begin_popup(IMGUI_SETTINGS.popup, self, IMGUI_SETTINGS.popupSize))
{
if (_imgui_checkbox_selectable(IMGUI_VSYNC, self, self->settings->isVsync)) window_vsync_set(self->settings->isVsync);
_imgui_selectable(IMGUI_HOTKEYS, self);
if (_imgui_selectable(IMGUI_DEFAULT_SETTINGS, self)) *self->settings = Settings();
imgui_end_popup(self);
}
if (imgui_begin_popup_modal(IMGUI_HOTKEYS.popup, self, IMGUI_HOTKEYS.popupSize))
{
_imgui_begin_child(IMGUI_HOTKEYS_CHILD, self);
if (_imgui_begin_table(IMGUI_HOTKEYS_TABLE, self))
{
static s32 selectedIndex = INDEX_NONE;
_imgui_table_setup_column(IMGUI_HOTKEYS_FUNCTION);
_imgui_table_setup_column(IMGUI_HOTKEYS_HOTKEY);
_imgui_table_headers_row();
for (s32 i = 0; i < HOTKEY_COUNT; i++)
{
if (!SETTINGS_HOTKEY_MEMBERS[i]) continue;
bool isSelected = selectedIndex == i;
const char* string = HOTKEY_STRINGS[i];
std::string* settingString = &(self->settings->*SETTINGS_HOTKEY_MEMBERS[i]);
std::string chordString = isSelected ? IMGUI_HOTKEY_CHANGE : *settingString;
ImGui::PushID(i);
_imgui_table_next_row();
_imgui_table_set_column_index(0);
ImGui::TextUnformatted(string);
_imgui_table_set_column_index(1);
if (ImGui::Selectable(chordString.c_str(), isSelected)) selectedIndex = i;
ImGui::PopID();
if (isSelected)
{
ImGuiKeyChord chord = IMGUI_CHORD_NONE;
if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) chord |= ImGuiMod_Ctrl;
if (ImGui::IsKeyDown(ImGuiMod_Shift)) chord |= ImGuiMod_Shift;
if (ImGui::IsKeyDown(ImGuiMod_Alt)) chord |= ImGuiMod_Alt;
if (ImGui::IsKeyDown(ImGuiMod_Super)) chord |= ImGuiMod_Super;
for (auto& [_, key] : IMGUI_KEY_MAP)
{
if (ImGui::IsKeyPressed(key))
{
chord |= key;
imgui_hotkey_chord_registry()[i] = chord;
*settingString = imgui_string_from_chord_get(chord);
selectedIndex = INDEX_NONE;
break;
}
}
}
}
_imgui_end_table();
}
_imgui_end_child(); // IMGUI_HOTKEYS_CHILD;
_imgui_begin_child(IMGUI_HOTKEYS_OPTIONS_CHILD, self);
if (_imgui_button(IMGUI_HOTKEYS_CONFIRM, self)) imgui_close_current_popup(self);
_imgui_end_child(); // IMGUI_HOTKEYS_OPTIONS_CHILD
imgui_end_popup(self);
}
_imgui_end();
}
@@ -2156,7 +2297,7 @@ static void _imgui_animation_preview(Imgui* self)
_imgui_begin_child(IMGUI_CANVAS_VIEW_CHILD, self);
_imgui_drag_float(IMGUI_CANVAS_ZOOM, self, zoom);
if (_imgui_button(IMGUI_CANVAS_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
if (_imgui_button(IMGUI_ANIMATION_PREVIEW_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
ImGui::Text(mousePositionString.c_str());
_imgui_end_child(); //IMGUI_CANVAS_VIEW_CHILD
@@ -2194,6 +2335,8 @@ static void _imgui_animation_preview(Imgui* self)
_imgui_checkbox(IMGUI_CANVAS_AXES, self, self->settings->previewIsAxes);
ImGui::SameLine();
_imgui_color_edit4(IMGUI_CANVAS_AXES_COLOR, self, self->settings->previewAxesColor);
ImGui::SameLine();
_imgui_checkbox(IMGUI_CANVAS_ALT_ICONS, self, self->settings->previewIsAltIcons);
_imgui_checkbox(IMGUI_CANVAS_ROOT_TRANSFORM, self, self->settings->previewIsRootTransform);
ImGui::SameLine();
_imgui_checkbox(IMGUI_CANVAS_TRIGGERS, self, self->settings->previewIsTriggers);
@@ -2241,8 +2384,8 @@ static void _imgui_animation_preview(Imgui* self)
const bool isUp = ImGui::IsKeyPressed(IMGUI_INPUT_UP);
const bool isDown = ImGui::IsKeyPressed(IMGUI_INPUT_DOWN);
const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
const bool isZoomIn = ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_IN);
const bool isZoomOut = ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_OUT);
const bool isZoomIn = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_IN]);
const bool isZoomOut = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_OUT]);
const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
@@ -2306,7 +2449,6 @@ static void _imgui_animation_preview(Imgui* self)
if (mouseWheel != 0 || isZoomIn || isZoomOut)
{
f32 delta = (mouseWheel > 0 || isZoomIn) ? CANVAS_ZOOM_STEP : -CANVAS_ZOOM_STEP;
delta = isMod ? delta * CANVAS_ZOOM_MOD : delta;
zoom = std::clamp(ROUND_NEAREST_MULTIPLE(zoom + delta, CANVAS_ZOOM_STEP), CANVAS_ZOOM_MIN, CANVAS_ZOOM_MAX);
}
}
@@ -2340,7 +2482,7 @@ static void _imgui_spritesheet_editor(Imgui* self)
_imgui_begin_child(IMGUI_CANVAS_VIEW_CHILD, self);
_imgui_drag_float(IMGUI_CANVAS_ZOOM, self, zoom);
if (_imgui_button(IMGUI_CANVAS_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
if (_imgui_button(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW.copy({pan == vec2()}), self)) pan = vec2();
ImGui::Text(mousePositionString.c_str());
_imgui_end_child(); // IMGUI_CANVAS_VIEW_CHILD
@@ -2371,9 +2513,8 @@ static void _imgui_spritesheet_editor(Imgui* self)
const bool isMouseClick = ImGui::IsMouseClicked(ImGuiMouseButton_Left);
const bool isMouseDown = ImGui::IsMouseDown(ImGuiMouseButton_Left);
const bool isMouseMiddleDown = ImGui::IsMouseDown(ImGuiMouseButton_Middle);
const bool isZoomIn = ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_IN);
const bool isZoomOut = ImGui::IsKeyPressed(IMGUI_INPUT_ZOOM_OUT);
const bool isMod = ImGui::IsKeyDown(IMGUI_INPUT_SHIFT);
const bool isZoomIn = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_IN]);
const bool isZoomOut = _imgui_chord_pressed(imgui_hotkey_chord_registry()[HOTKEY_ZOOM_OUT]);
const f32 mouseWheel = ImGui::GetIO().MouseWheel;
const ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
@@ -2442,7 +2583,6 @@ static void _imgui_spritesheet_editor(Imgui* self)
if (mouseWheel != 0 || isZoomIn || isZoomOut)
{
f32 delta = (mouseWheel > 0 || isZoomIn) ? CANVAS_ZOOM_STEP : -CANVAS_ZOOM_STEP;
delta = isMod ? delta * CANVAS_ZOOM_MOD : delta;
zoom = std::clamp(ROUND_NEAREST_MULTIPLE(zoom + delta, CANVAS_ZOOM_STEP), CANVAS_ZOOM_MIN, CANVAS_ZOOM_MAX);
}
}
@@ -2538,7 +2678,6 @@ static void _imgui_log(Imgui* self)
}
}
static void _imgui_dock(Imgui* self)
{
ImguiItem window = IMGUI_WINDOW_MAIN;
@@ -2558,6 +2697,7 @@ static void _imgui_dock(Imgui* self)
_imgui_animation_preview(self);
_imgui_spritesheet_editor(self);
_imgui_timeline(self);
_imgui_onionskin(self);
_imgui_frame_properties(self);
_imgui_end(); // IMGUI_WINDOW_MAIN
@@ -2608,6 +2748,12 @@ void imgui_init
io.ConfigWindowsMoveFromTitleBarOnly = true;
ImGui::LoadIniSettingsFromDisk(settings_path_get().c_str());
for (s32 i = 0; i < HOTKEY_COUNT; i++)
{
if (!SETTINGS_HOTKEY_MEMBERS[i]) continue;
imgui_hotkey_chord_registry()[i] = imgui_chord_from_string_get(*&(self->settings->*SETTINGS_HOTKEY_MEMBERS[i]));
}
}
void imgui_update(Imgui* self)
@@ -2622,13 +2768,13 @@ void imgui_update(Imgui* self)
if (self->isContextualActionsEnabled)
{
for (const auto& hotkey : imgui_hotkey_registry())
for (const auto& item : imgui_item_registry())
{
if (ImGui::IsKeyChordPressed(hotkey.chord))
if (item->is_chord() && _imgui_chord_pressed(item->chord_get()))
{
if (hotkey.is_undoable()) imgui_snapshot(self, hotkey.snapshotAction);
if (hotkey.is_focus_window()) continue;
hotkey.function(self);
if (item->is_undoable()) imgui_snapshot(self, item->snapshotAction);
if (item->is_focus_window()) continue;
if (item->is_function()) item->function(self);
}
}
}

View File

@@ -52,14 +52,15 @@
#define IMGUI_FRAME_BORDER 2.0f
#define IMGUI_LOG_DURATION 3.0f
#define IMGUI_LOG_PADDING 10.0f
#define IMGUI_PLAYHEAD_LINE_COLOR IM_COL32(255, 255, 255, 255)
#define IMGUI_TRIGGERS_EVENT_COLOR IM_COL32(255, 255, 255, 128)
#define IMGUI_PLAYHEAD_LINE_COLOR IM_COL32(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, UCHAR_MAX)
#define IMGUI_TRIGGERS_EVENT_COLOR IM_COL32(UCHAR_MAX, UCHAR_MAX, UCHAR_MAX, 128)
#define IMGUI_PLAYHEAD_LINE_WIDTH 2.0f
#define IMGUI_SPRITESHEETS_FOOTER_HEIGHT 65
#define IMGUI_TIMELINE_FRAME_MULTIPLE 5
#define IMGUI_TIMELINE_MERGE
#define IMGUI_TOOL_COLOR_PICKER_DURATION 0.25f
#define IMGUI_OPTION_POPUP_ROW_COUNT 2
#define IMGUI_CHORD_REPEAT_TIME 0.25f
#define IMGUI_ACTION_FRAME_CROP "Frame Crop"
#define IMGUI_ACTION_FRAME_SWAP "Frame Swap"
@@ -72,6 +73,7 @@
#define IMGUI_ACTION_ERASE "Erase"
#define IMGUI_ACTION_RELOAD_SPRITESHEET "Reload Spritesheet(s)"
#define IMGUI_ACTION_REPLACE_SPRITESHEET "Replace Spritesheet"
#define IMGUI_ACTION_OPEN_FILE "Open File"
#define IMGUI_POPUP_FLAGS ImGuiWindowFlags_NoMove
#define IMGUI_POPUP_MODAL_FLAGS ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize
@@ -93,7 +95,7 @@
#define IMGUI_NONE "None"
#define IMGUI_ANIMATION_DEFAULT_FORMAT "(*) {}"
#define IMGUI_BUFFER_MAX 255
#define IMGUI_BUFFER_MAX UCHAR_MAX
#define IMGUI_INVISIBLE_LABEL_MARKER "##"
#define IMGUI_ITEM_SELECTABLE_EDITABLE_LABEL "## Editing"
#define IMGUI_LOG_FORMAT "## Log {}"
@@ -107,7 +109,7 @@
#define IMGUI_TIMELINE_FRAME_LABEL_FORMAT "## {}"
#define IMGUI_SELECTABLE_INPUT_INT_FORMAT "#{}"
#define IMGUI_TIMELINE_ANIMATION_NONE "Select an animation to show timeline..."
#define IMGUI_HOTKEY_CHANGE "Input new hotkey..."
#define IMGUI_LABEL_SHORTCUT_FORMAT "({})"
#define IMGUI_TOOLTIP_SHORTCUT_FORMAT "\n(Shortcut: {})"
#define IMGUI_INVISIBLE_FORMAT "## {}"
@@ -206,18 +208,13 @@ struct Imgui
bool isTryQuit = false;
};
typedef void(*ImguiFunction)(Imgui*);
struct ImguiHotkey
static inline void imgui_snapshot(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
{
ImGuiKeyChord chord;
ImguiFunction function;
std::string focusWindow{};
std::string snapshotAction{};
bool is_focus_window() const { return !focusWindow.empty(); }
bool is_undoable() const { return !snapshotAction.empty(); }
};
self->snapshots->action = action;
Snapshot snapshot = snapshot_get(self->snapshots);
snapshots_undo_push(self->snapshots, &snapshot);
}
static void imgui_log_push(Imgui* self, const std::string& text)
{
@@ -225,12 +222,6 @@ static void imgui_log_push(Imgui* self, const std::string& text)
log_imgui(text);
}
static std::vector<ImguiHotkey>& imgui_hotkey_registry()
{
static std::vector<ImguiHotkey> registry;
return registry;
}
static inline void imgui_file_new(Imgui* self)
{
anm2_reference_clear(self->reference);
@@ -274,13 +265,6 @@ static inline void imgui_explore(Imgui* self)
dialog_explorer_open(parentPath.string());
}
static inline void imgui_snapshot(Imgui* self, const std::string& action = SNAPSHOT_ACTION)
{
self->snapshots->action = action;
Snapshot snapshot = snapshot_get(self->snapshots);
snapshots_undo_push(self->snapshots, &snapshot);
}
static inline void imgui_tool_pan_set(Imgui* self)
{
self->settings->tool = TOOL_PAN;
@@ -353,6 +337,125 @@ static inline void imgui_paste(Imgui* self)
clipboard_paste(self->clipboard);
}
static inline void imgui_onionskin_toggle(Imgui* self)
{
self->settings->onionskinIsEnabled = !self->settings->onionskinIsEnabled;
}
static const std::unordered_map<std::string, ImGuiKey> IMGUI_KEY_MAP =
{
{ "A", ImGuiKey_A },
{ "B", ImGuiKey_B },
{ "C", ImGuiKey_C },
{ "D", ImGuiKey_D },
{ "E", ImGuiKey_E },
{ "F", ImGuiKey_F },
{ "G", ImGuiKey_G },
{ "H", ImGuiKey_H },
{ "I", ImGuiKey_I },
{ "J", ImGuiKey_J },
{ "K", ImGuiKey_K },
{ "L", ImGuiKey_L },
{ "M", ImGuiKey_M },
{ "N", ImGuiKey_N },
{ "O", ImGuiKey_O },
{ "P", ImGuiKey_P },
{ "Q", ImGuiKey_Q },
{ "R", ImGuiKey_R },
{ "S", ImGuiKey_S },
{ "T", ImGuiKey_T },
{ "U", ImGuiKey_U },
{ "V", ImGuiKey_V },
{ "W", ImGuiKey_W },
{ "X", ImGuiKey_X },
{ "Y", ImGuiKey_Y },
{ "Z", ImGuiKey_Z },
{ "0", ImGuiKey_0 },
{ "1", ImGuiKey_1 },
{ "2", ImGuiKey_2 },
{ "3", ImGuiKey_3 },
{ "4", ImGuiKey_4 },
{ "5", ImGuiKey_5 },
{ "6", ImGuiKey_6 },
{ "7", ImGuiKey_7 },
{ "8", ImGuiKey_8 },
{ "9", ImGuiKey_9 },
{ "Num0", ImGuiKey_Keypad0 },
{ "Num1", ImGuiKey_Keypad1 },
{ "Num2", ImGuiKey_Keypad2 },
{ "Num3", ImGuiKey_Keypad3 },
{ "Num4", ImGuiKey_Keypad4 },
{ "Num5", ImGuiKey_Keypad5 },
{ "Num6", ImGuiKey_Keypad6 },
{ "Num7", ImGuiKey_Keypad7 },
{ "Num8", ImGuiKey_Keypad8 },
{ "Num9", ImGuiKey_Keypad9 },
{ "NumAdd", ImGuiKey_KeypadAdd },
{ "NumSubtract", ImGuiKey_KeypadSubtract },
{ "NumMultiply", ImGuiKey_KeypadMultiply },
{ "NumDivide", ImGuiKey_KeypadDivide },
{ "NumEnter", ImGuiKey_KeypadEnter },
{ "NumDecimal", ImGuiKey_KeypadDecimal },
{ "F1", ImGuiKey_F1 },
{ "F2", ImGuiKey_F2 },
{ "F3", ImGuiKey_F3 },
{ "F4", ImGuiKey_F4 },
{ "F5", ImGuiKey_F5 },
{ "F6", ImGuiKey_F6 },
{ "F7", ImGuiKey_F7 },
{ "F8", ImGuiKey_F8 },
{ "F9", ImGuiKey_F9 },
{ "F10", ImGuiKey_F10 },
{ "F11", ImGuiKey_F11 },
{ "F12", ImGuiKey_F12 },
{ "Up", ImGuiKey_UpArrow },
{ "Down", ImGuiKey_DownArrow },
{ "Left", ImGuiKey_LeftArrow },
{ "Right", ImGuiKey_RightArrow },
{ "Space", ImGuiKey_Space },
{ "Enter", ImGuiKey_Enter },
{ "Escape", ImGuiKey_Escape },
{ "Tab", ImGuiKey_Tab },
{ "Backspace", ImGuiKey_Backspace },
{ "Delete", ImGuiKey_Delete },
{ "Insert", ImGuiKey_Insert },
{ "Home", ImGuiKey_Home },
{ "End", ImGuiKey_End },
{ "PageUp", ImGuiKey_PageUp },
{ "PageDown", ImGuiKey_PageDown },
{ "Minus", ImGuiKey_Minus },
{ "Equal", ImGuiKey_Equal },
{ "LeftBracket", ImGuiKey_LeftBracket },
{ "RightBracket", ImGuiKey_RightBracket },
{ "Semicolon", ImGuiKey_Semicolon },
{ "Apostrophe", ImGuiKey_Apostrophe },
{ "Comma", ImGuiKey_Comma },
{ "Period", ImGuiKey_Period },
{ "Slash", ImGuiKey_Slash },
{ "Backslash", ImGuiKey_Backslash },
{ "GraveAccent", ImGuiKey_GraveAccent },
{ "MouseLeft", ImGuiKey_MouseLeft },
{ "MouseRight", ImGuiKey_MouseRight },
{ "MouseMiddle", ImGuiKey_MouseMiddle },
{ "MouseX1", ImGuiKey_MouseX1 },
{ "MouseX2", ImGuiKey_MouseX2 }
};
static std::unordered_map<std::string, ImGuiKey> IMGUI_MOD_MAP =
{
{ "Ctrl", ImGuiMod_Ctrl },
{ "Shift", ImGuiMod_Shift },
{ "Alt", ImGuiMod_Alt },
{ "Super", ImGuiMod_Super },
};
static inline ImGuiKey imgui_key_from_char_get(char c)
{
if (c >= 'a' && c <= 'z') c -= 'a' - 'A';
@@ -367,16 +470,12 @@ static inline std::string imgui_string_from_chord_get(ImGuiKeyChord chord)
if (chord & ImGuiMod_Ctrl) result += "Ctrl+";
if (chord & ImGuiMod_Shift) result += "Shift+";
if (chord & ImGuiMod_Alt) result += "Alt+";
if (chord & ImGuiMod_Super) result += "Super+";
ImGuiKey key = (ImGuiKey)(chord & ~ImGuiMod_Mask_);
if (key >= ImGuiKey_A && key <= ImGuiKey_Z)
result.push_back('A' + (key - ImGuiKey_A));
else if (key >= ImGuiKey_0 && key <= ImGuiKey_9)
result.push_back('0' + (key - ImGuiKey_0));
else
if (key != ImGuiKey_None)
{
// Fallback to ImGui's built-in name for non-alphanumerics
const char* name = ImGui::GetKeyName(key);
if (name && *name)
result += name;
@@ -384,9 +483,44 @@ static inline std::string imgui_string_from_chord_get(ImGuiKeyChord chord)
result += "Unknown";
}
if (!result.empty() && result.back() == '+')
result.pop_back();
return result;
}
static inline ImGuiKeyChord imgui_chord_from_string_get(const std::string& str)
{
ImGuiKeyChord chord = 0;
ImGuiKey baseKey = ImGuiKey_None;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, '+'))
{
// trim
token.erase(0, token.find_first_not_of(" \t\r\n"));
token.erase(token.find_last_not_of(" \t\r\n") + 1);
if (token.empty())
continue;
if (auto it = IMGUI_MOD_MAP.find(token); it != IMGUI_MOD_MAP.end()) {
chord |= it->second;
}
else if (baseKey == ImGuiKey_None) {
if (auto it2 = IMGUI_KEY_MAP.find(token); it2 != IMGUI_KEY_MAP.end())
baseKey = it2->second;
}
}
if (baseKey != ImGuiKey_None)
chord |= baseKey;
return chord;
}
static void imgui_contextual_actions_enable(Imgui* self) { self->isContextualActionsEnabled = true; }
static void imgui_contextual_actions_disable(Imgui* self){ self->isContextualActionsEnabled = false; }
static inline bool imgui_is_popup_open(const std::string& label) { return ImGui::IsPopupOpen(label.c_str()); }
@@ -449,6 +583,7 @@ enum ImguiItemType
IMGUI_WINDOW,
IMGUI_DOCKSPACE,
IMGUI_CHILD,
IMGUI_TABLE,
IMGUI_OPTION_POPUP,
IMGUI_SELECTABLE,
IMGUI_BUTTON,
@@ -478,6 +613,22 @@ struct ImguiItemOverride
s32 value{};
};
struct ImguiItem;
static std::vector<ImguiItem*>& imgui_item_registry(void)
{
static std::vector<ImguiItem*> registry;
return registry;
}
static ImGuiKeyChord* imgui_hotkey_chord_registry(void)
{
static ImGuiKeyChord registry[HOTKEY_COUNT];
return registry;
}
typedef void(*ImguiFunction)(Imgui*);
// Item
struct ImguiItem
{
@@ -491,6 +642,7 @@ struct ImguiItem
std::vector<std::string> items{};
AtlasType atlas = ATLAS_NONE;
ImGuiKeyChord chord = IMGUI_CHORD_NONE;
HotkeyType hotkey = HOTKEY_NONE;
ImGuiKey mnemonicKey = ImGuiKey_None;
s32 mnemonicIndex = INDEX_NONE;
ImVec2 size{};
@@ -526,15 +678,7 @@ struct ImguiItem
static s32 idNew = 0;
id = idNew++;
if (is_chord())
{
std::string chordString = imgui_string_from_chord_get(chord);
if (isShortcutInLabel)
label += std::format(IMGUI_LABEL_SHORTCUT_FORMAT, chordString);
tooltip += std::format(IMGUI_TOOLTIP_SHORTCUT_FORMAT, chordString);
if (function)
imgui_hotkey_registry().push_back({chord, function, focusWindow, snapshotAction});
}
imgui_item_registry().push_back(this);
std::string labelNew{};
@@ -578,12 +722,20 @@ struct ImguiItem
return out;
}
ImGuiKeyChord chord_get() const
{
if (is_hotkey()) return imgui_hotkey_chord_registry()[hotkey];
return chord;
}
bool is_border() const { return border != 0; }
bool is_row() const { return rowCount != 0; }
bool is_chord() const { return chord != IMGUI_CHORD_NONE; }
bool is_hotkey() const { return hotkey != HOTKEY_NONE; }
bool is_chord() const { return chord != IMGUI_CHORD_NONE || is_hotkey(); }
bool is_drag_drop() const { return !dragDrop.empty(); }
bool is_focus_window() const { return !focusWindow.empty(); }
bool is_popup() const { return !popup.empty(); }
bool is_function() const { return function; }
bool is_size() const { return size != ImVec2(); }
bool is_popup_size() const { return popupSize != ImVec2(); }
bool is_tooltip() const { return !tooltip.empty(); }
@@ -643,7 +795,7 @@ IMGUI_ITEM(IMGUI_NEW,
self.label = "&New ",
self.tooltip = "Load a blank .anm2 file to edit.",
self.function = imgui_file_new,
self.chord = ImGuiMod_Ctrl | ImGuiKey_N,
self.hotkey = HOTKEY_NEW,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -652,7 +804,7 @@ IMGUI_ITEM(IMGUI_OPEN,
self.label = "&Open ",
self.tooltip = "Open an existing .anm2 file to edit.",
self.function = imgui_file_open,
self.chord = ImGuiMod_Ctrl | ImGuiKey_O,
self.hotkey = HOTKEY_OPEN,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -661,7 +813,7 @@ IMGUI_ITEM(IMGUI_SAVE,
self.label = "&Save ",
self.tooltip = "Saves the current .anm2 file to its path.\nIf no path exists, one can be chosen.",
self.function = imgui_file_save,
self.chord = ImGuiMod_Ctrl | ImGuiKey_S,
self.hotkey = HOTKEY_SAVE,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -670,7 +822,7 @@ IMGUI_ITEM(IMGUI_SAVE_AS,
self.label = "S&ave As ",
self.tooltip = "Saves the current .anm2 file to a chosen path.",
self.function = imgui_file_save_as,
self.chord = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_S,
self.hotkey = HOTKEY_SAVE_AS,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -687,7 +839,7 @@ IMGUI_ITEM(IMGUI_EXIT,
self.label = "&Exit ",
self.tooltip = "Exits the program.",
self.function = imgui_quit,
self.chord = ImGuiMod_Alt | ImGuiKey_F4,
self.hotkey = HOTKEY_EXIT,
self.isSizeToText = true,
self.isShortcutInLabel = true
);
@@ -697,6 +849,11 @@ IMGUI_ITEM(IMGUI_EXIT_CONFIRMATION,
self.text = "Unsaved changes will be lost!\nAre you sure you want to exit?"
);
IMGUI_ITEM(IMGUI_OPEN_CONFIRMATION,
self.label = "Open Confirmation",
self.text = "Unsaved changes will be lost!\nAre you sure you open a new file?"
);
IMGUI_ITEM(IMGUI_WIZARD,
self.label = "&Wizard",
self.tooltip = "Opens the wizard menu, for neat functions related to the .anm2.",
@@ -747,12 +904,14 @@ IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_PIVOT,
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_ROWS,
self.label = "Rows",
self.tooltip = "Set how many rows will be used in the generated animation.",
self.min = 1,
self.max = 1000
);
IMGUI_ITEM(IMGUI_GENERATE_ANIMATION_FROM_GRID_COLUMNS,
self.label = "Columns",
self.tooltip = "Set how many columns will be used in the generated animation.",
self.min = 1,
self.max = 1000
);
@@ -812,7 +971,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES,
self.tooltip = "Change all frame properties in the selected animation item (or selected frame).",
self.popup = "Change All Frame Properties",
self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {500, 380}
self.popupSize = {500, 405}
);
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CHILD,
@@ -820,7 +979,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_CHILD,
self.size =
{
IMGUI_CHANGE_ALL_FRAME_PROPERTIES.popupSize.x,
250
275
},
self.flags = true
);
@@ -862,7 +1021,7 @@ IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_ADD,
IMGUI_ITEM(IMGUI_CHANGE_ALL_FRAME_PROPERTIES_SUBTRACT,
self.label = "Subtract",
self.tooltip = "The specified values will be added to all selected frames.",
self.tooltip = "The specified values will be subtracted from all selected frames.",
self.snapshotAction = "Subtract Frame Properties",
self.rowCount = IMGUI_CHANGE_ALL_FRAME_PROPERTIES_OPTIONS_ROW_COUNT,
self.isSameLine = true
@@ -887,7 +1046,7 @@ IMGUI_ITEM(IMGUI_SCALE_ANM2,
self.tooltip = "Scale up all size and position-related frame properties in the anm2.",
self.popup = "Scale Anm2",
self.popupType = IMGUI_POPUP_CENTER_WINDOW,
self.popupSize = {260, 72},
self.popupSize = {260, 75},
self.isSizeToText = true,
self.isSeparator = true
);
@@ -963,7 +1122,7 @@ IMGUI_ITEM(IMGUI_RENDER_ANIMATION_OUTPUT,
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_FORMAT,
self.label = "Format",
self.tooltip = "(PNG images only).\nSet the format of each output frame; i.e., its filename.\nThe format will only take one argument; that being the frame's index.\nFor example, a format like \"{}.png\" will export a frame of index 0 as \"0.png\".",
self.max = 255
self.max = UCHAR_MAX
);
IMGUI_ITEM(IMGUI_RENDER_ANIMATION_CONFIRM,
@@ -1014,7 +1173,43 @@ IMGUI_ITEM(IMGUI_SETTINGS,
IMGUI_ITEM(IMGUI_VSYNC,
self.label = "&Vsync",
self.tooltip = "Toggle vertical sync; synchronizes program framerate with your monitor's refresh rate.",
self.isSizeToText = true
self.isSizeToText = true,
self.isSeparator = true
);
IMGUI_ITEM(IMGUI_HOTKEYS,
self.label = "&Hotkeys",
self.tooltip = "Change the program's hotkeys.",
self.popup = "Hotkeys",
self.popupSize = {500, 405},
self.isSizeToText = true,
self.isSeparator = true
);
IMGUI_ITEM(IMGUI_HOTKEYS_CHILD,
self.label = "## Hotkeys Child",
self.size = {IMGUI_HOTKEYS.popupSize.x, IMGUI_HOTKEYS.popupSize.y - 35},
self.flags = true
);
#define IMGUI_HOTKEYS_FUNCTION "Function"
#define IMGUI_HOTKEYS_HOTKEY "Hotkey"
IMGUI_ITEM(IMGUI_HOTKEYS_TABLE,
self.label = "## Hotkeys Table",
self.value = 2,
self.flags = ImGuiTableFlags_Borders
);
IMGUI_ITEM(IMGUI_HOTKEYS_OPTIONS_CHILD,
self.label = "## Merge Options Child",
self.size = {IMGUI_HOTKEYS.popupSize.x, 35},
self.flags = true
);
IMGUI_ITEM(IMGUI_HOTKEYS_CONFIRM,
self.label = "Confirm",
self.tooltip = "Use these hotkeys.",
self.rowCount = 1
);
IMGUI_ITEM(IMGUI_DEFAULT_SETTINGS,
@@ -1298,14 +1493,11 @@ IMGUI_ITEM(IMGUI_CANVAS_ZOOM,
self.tooltip = "Change the zoom of the canvas.",
self.min = CANVAS_ZOOM_MIN,
self.max = CANVAS_ZOOM_MAX,
self.speed = 1.0f,
self.value = CANVAS_ZOOM_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the canvas.",
self.size = {-FLT_MIN, 0}
);
IMGUI_ITEM(IMGUI_CANVAS_VISUAL_CHILD,
self.label = "## Animation Preview Visual Child",
@@ -1327,8 +1519,8 @@ IMGUI_ITEM(IMGUI_CANVAS_ANIMATION_OVERLAY,
IMGUI_ITEM(IMGUI_CANVAS_ANIMATION_OVERLAY_TRANSPARENCY,
self.label = "Alpha",
self.tooltip = "Set the transparency of the animation overlay.",
self.value = 255,
self.max = 255
self.value = SETTINGS_PREVIEW_OVERLAY_TRANSPARENCY_DEFAULT,
self.max = UCHAR_MAX
);
IMGUI_ITEM(IMGUI_CANVAS_HELPER_CHILD,
@@ -1351,28 +1543,37 @@ IMGUI_ITEM(IMGUI_CANVAS_AXES_COLOR,
IMGUI_ITEM(IMGUI_CANVAS_ROOT_TRANSFORM,
self.label = "Root Transform",
self.tooltip = "Toggles the root frames's attributes transforming the other items in an animation.",
self.value = true
self.value = SETTINGS_PREVIEW_IS_ROOT_TRANSFORM_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_TRIGGERS,
self.label = "Triggers",
self.tooltip = "Toggles activated triggers drawing their event name.",
self.value = true
self.value = SETTINGS_PREVIEW_IS_TRIGGERS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_PIVOTS,
self.label = "Pivots",
self.tooltip = "Toggles drawing each layer's pivot."
self.tooltip = "Toggles drawing each layer's pivot.",
self.value = SETTINGS_PREVIEW_IS_PIVOTS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_TARGETS,
self.label = "Targets",
self.tooltip = "Toggles drawing the targets (i.e., the colored root/null icons)."
self.tooltip = "Toggles drawing the targets (the colored root/null icons).",
self.value = SETTINGS_PREVIEW_IS_TARGETS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_ALT_ICONS,
self.label = "Alt Icons",
self.tooltip = "Toggles the use of alternate icons for the targets (the colored root/null icons).",
self.value = SETTINGS_PREVIEW_IS_ALT_ICONS_DEFAULT
);
IMGUI_ITEM(IMGUI_CANVAS_BORDER,
self.label = "Border",
self.tooltip = "Toggles the appearance of a border around the items."
self.tooltip = "Toggles the appearance of a border around the items.",
self.value = SETTINGS_PREVIEW_IS_BORDER_DEFAULT
);
IMGUI_ITEM(IMGUI_ANIMATION_PREVIEW,
@@ -1380,11 +1581,27 @@ IMGUI_ITEM(IMGUI_ANIMATION_PREVIEW,
self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_ANIMATION_PREVIEW_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the animation preview.",
self.hotkey = HOTKEY_CENTER_VIEW,
self.focusWindow = IMGUI_ANIMATION_PREVIEW.label,
self.size = {-FLT_MIN, 0}
);
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR,
self.label = "Spritesheet Editor",
self.flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse
);
IMGUI_ITEM(IMGUI_SPRITESHEET_EDITOR_CENTER_VIEW,
self.label = "Center View",
self.tooltip = "Centers the current view on the spritesheet editor.",
self.hotkey = HOTKEY_CENTER_VIEW,
self.focusWindow = IMGUI_SPRITESHEET_EDITOR.label,
self.size = {-FLT_MIN, 0}
);
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES, self.label = "Frame Properties");
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_POSITION,
@@ -1419,8 +1636,7 @@ IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_SCALE,
self.label = "Scale",
self.tooltip = "Change the scale of the selected frame.",
self.snapshotAction = "Frame Scale",
self.isUseItemActivated = true,
self.value = 100
self.isUseItemActivated = true
);
IMGUI_ITEM(IMGUI_FRAME_PROPERTIES_ROTATION,
@@ -1505,7 +1721,7 @@ IMGUI_ITEM(IMGUI_TOOL_PAN,
self.label = "## Pan",
self.tooltip = "Use the pan tool.\nWill shift the view as the cursor is dragged.\nYou can also use the middle mouse button to pan at any time.",
self.function = imgui_tool_pan_set,
self.chord = ImGuiKey_P,
self.hotkey = HOTKEY_PAN,
self.atlas = ATLAS_PAN
);
@@ -1513,7 +1729,7 @@ IMGUI_ITEM(IMGUI_TOOL_MOVE,
self.label = "## Move",
self.tooltip = "Use the move tool.\nWill move the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)",
self.function = imgui_tool_move_set,
self.chord = ImGuiKey_M,
self.hotkey = HOTKEY_MOVE,
self.atlas = ATLAS_MOVE
);
@@ -1521,7 +1737,7 @@ IMGUI_ITEM(IMGUI_TOOL_ROTATE,
self.label = "## Rotate",
self.tooltip = "Use the rotate tool.\nWill rotate the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)",
self.function = imgui_tool_rotate_set,
self.chord = ImGuiKey_R,
self.hotkey = HOTKEY_ROTATE,
self.atlas = ATLAS_ROTATE
);
@@ -1529,7 +1745,7 @@ IMGUI_ITEM(IMGUI_TOOL_SCALE,
self.label = "## Scale",
self.tooltip = "Use the scale tool.\nWill scale the selected item as the cursor is dragged, or directional keys are pressed.\n(Animation Preview only.)",
self.function = imgui_tool_scale_set,
self.chord = ImGuiKey_S,
self.hotkey = HOTKEY_SCALE,
self.atlas = ATLAS_SCALE
);
@@ -1537,7 +1753,7 @@ IMGUI_ITEM(IMGUI_TOOL_CROP,
self.label = "## Crop",
self.tooltip = "Use the crop tool.\nWill produce a crop rectangle based on how the cursor is dragged.\n(Spritesheet Editor only.)",
self.function = imgui_tool_crop_set,
self.chord = ImGuiKey_C,
self.hotkey = HOTKEY_CROP,
self.atlas = ATLAS_CROP
);
@@ -1545,7 +1761,7 @@ IMGUI_ITEM(IMGUI_TOOL_DRAW,
self.label = "## Draw",
self.tooltip = "Draws pixels onto the selected spritesheet, with the current color.\n(Spritesheet Editor only.)",
self.function = imgui_tool_draw_set,
self.chord = ImGuiKey_B,
self.hotkey = HOTKEY_DRAW,
self.atlas = ATLAS_DRAW
);
@@ -1553,7 +1769,7 @@ IMGUI_ITEM(IMGUI_TOOL_ERASE,
self.label = "## Erase",
self.tooltip = "Erases pixels from the selected spritesheet.\n(Spritesheet Editor only.)",
self.function = imgui_tool_erase_set,
self.chord = ImGuiKey_E,
self.hotkey = HOTKEY_ERASE,
self.atlas = ATLAS_ERASE
);
@@ -1561,7 +1777,7 @@ IMGUI_ITEM(IMGUI_TOOL_COLOR_PICKER,
self.label = "## Color Picker",
self.tooltip = "Selects a color from the canvas, to be used for drawing.\n(Spritesheet Editor only).",
self.function = imgui_tool_color_picker_set,
self.chord = ImGuiKey_W,
self.hotkey = HOTKEY_COLOR_PICKER,
self.atlas = ATLAS_COLOR_PICKER
);
@@ -1569,7 +1785,7 @@ IMGUI_ITEM(IMGUI_TOOL_UNDO,
self.label = "## Undo",
self.tooltip = "Undo the last action.",
self.function = imgui_undo,
self.chord = ImGuiMod_Ctrl | ImGuiKey_Z,
self.hotkey = HOTKEY_UNDO,
self.atlas = ATLAS_UNDO
);
@@ -1577,7 +1793,7 @@ IMGUI_ITEM(IMGUI_TOOL_REDO,
self.label = "## Redo",
self.tooltip = "Redo the last action.",
self.function = imgui_redo,
self.chord = ImGuiMod_Ctrl | ImGuiKey_Y,
self.hotkey = HOTKEY_REDO,
self.atlas = ATLAS_REDO
);
@@ -1875,7 +2091,7 @@ IMGUI_ITEM(IMGUI_PLAY,
self.label = "|> Play",
self.tooltip = "Play the current animation, if paused.",
self.focusWindow = IMGUI_TIMELINE.label,
self.chord = ImGuiKey_Space,
self.hotkey = HOTKEY_PLAY_PAUSE,
self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
self.isSameLine = true
);
@@ -1884,7 +2100,7 @@ IMGUI_ITEM(IMGUI_PAUSE,
self.label = "|| Pause",
self.tooltip = "Pause the current animation, if playing.",
self.focusWindow = IMGUI_TIMELINE.label,
self.chord = ImGuiKey_Space,
self.hotkey = HOTKEY_PLAY_PAUSE,
self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
self.isSameLine = true
);
@@ -1992,7 +2208,55 @@ IMGUI_ITEM(IMGUI_CREATED_BY,
self.label = "Author",
self.tooltip = "Sets the author of the animation.",
self.rowCount = IMGUI_TIMELINE_OPTIONS_ROW_COUNT,
self.max = 255
self.max = UCHAR_MAX
);
#define IMGUI_ONIONSKIN_ROW_COUNT 3
IMGUI_ITEM(IMGUI_ONIONSKIN, self.label = "Onionskin");
IMGUI_ITEM(IMGUI_ONIONSKIN_ENABLED,
self.label = "Enabled",
self.tooltip = "Toggle onionskin (previews of frames before/after the current one.)",
self.function = imgui_onionskin_toggle,
self.hotkey = HOTKEY_ONIONSKIN,
self.isSeparator = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_BEFORE, self.label = "-- Before-- ");
IMGUI_ITEM(IMGUI_ONIONSKIN_AFTER, self.label = "-- After -- ");
IMGUI_ITEM(IMGUI_ONIONSKIN_COUNT,
self.label = "Count",
self.tooltip = "Set the number of previewed frames appearing.",
self.min = 1,
self.max = 100,
self.value = SETTINGS_ONIONSKIN_BEFORE_COUNT_DEFAULT,
self.rowCount = IMGUI_ONIONSKIN_ROW_COUNT,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_COLOR_OFFSET,
self.label = "Color Offset",
self.tooltip = "Set the color offset of the previewed frames.",
self.flags = ImGuiColorEditFlags_NoInputs,
self.rowCount = IMGUI_ONIONSKIN_ROW_COUNT
);
IMGUI_ITEM(IMGUI_ONIONSKIN_DRAW_ORDER,
self.label = "Draw Order",
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_BELOW,
self.label = "Below",
self.tooltip = "The onionskin frames will draw below the base frame.",
self.value = ONIONSKIN_BELOW,
self.isSameLine = true
);
IMGUI_ITEM(IMGUI_ONIONSKIN_ABOVE,
self.label = "Above",
self.tooltip = "The onionskin frames will draw above the base frame.",
self.value = ONIONSKIN_ABOVE
);
IMGUI_ITEM(IMGUI_CONTEXT_MENU, self.label = "## Context Menu");
@@ -2002,7 +2266,7 @@ IMGUI_ITEM(IMGUI_CUT,
self.tooltip = "Cuts the currently selected contextual element; removing it and putting it to the clipboard.",
self.snapshotAction = "Cut",
self.function = imgui_cut,
self.chord = ImGuiMod_Ctrl | ImGuiKey_X,
self.hotkey = HOTKEY_CUT,
self.isSizeToText = true
);
@@ -2011,7 +2275,7 @@ IMGUI_ITEM(IMGUI_COPY,
self.tooltip = "Copies the currently selected contextual element to the clipboard.",
self.snapshotAction = "Copy",
self.function = imgui_copy,
self.chord = ImGuiMod_Ctrl | ImGuiKey_C,
self.hotkey = HOTKEY_COPY,
self.isSizeToText = true
);
@@ -2020,7 +2284,7 @@ IMGUI_ITEM(IMGUI_PASTE,
self.tooltip = "Pastes the currently selection contextual element from the clipboard.",
self.snapshotAction = "Paste",
self.function = imgui_paste,
self.chord = ImGuiMod_Ctrl | ImGuiKey_V,
self.hotkey = HOTKEY_PASTE,
self.isSizeToText = true
);
@@ -2029,7 +2293,7 @@ IMGUI_ITEM(IMGUI_CHANGE_INPUT_TEXT,
self.tooltip = "Rename the selected item.",
self.snapshotAction = "Rename Item",
self.flags = ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_EnterReturnsTrue,
self.max = 255
self.max = UCHAR_MAX
);
IMGUI_ITEM(IMGUI_CHANGE_INPUT_INT,

View File

@@ -98,155 +98,139 @@ void preview_draw(Preview* self)
if (self->settings->previewIsAxes)
canvas_axes_draw(&self->canvas, shaderAxis, transform, self->settings->previewAxesColor);
Anm2Animation* animation = anm2_animation_from_reference(self->anm2, self->reference);
s32& animationID = self->reference->animationID;
if (animation)
auto animation_draw = [&](s32 animationID)
{
Anm2Frame root;
mat4 rootModel = mat4(1.0f);
Anm2Animation* animation = map_find(self->anm2->animations, animationID);
if (!animation) return;
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, self->time);
if (self->settings->previewIsRootTransform)
rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation);
// Root
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
auto root_draw = [&](Anm2Frame root, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
mat4 model = canvas_model_get(PREVIEW_TARGET_SIZE, root.position, PREVIEW_TARGET_SIZE * 0.5f, PERCENT_TO_UNIT(root.scale), root.rotation);
mat4 rootTransform = transform * model;
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_TARGET);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, PREVIEW_ROOT_COLOR);
}
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_ROOT_COLOR;
AtlasType atlas = self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, rootTransform, vertices, color);
};
// Layers
for (auto [i, id] : self->anm2->layerMap)
auto layer_draw = [&](mat4 rootModel, s32 id, f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
Anm2Frame frame;
Anm2Item& layerAnimation = animation->layerAnimations[id];
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0) return;
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, self->time);
if (!frame.isVisible)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_LAYER, id}, time);
if (!frame.isVisible) return;
mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 layerTransform = transform * (rootModel * model);
vec3 frameColorOffset = frame.offsetRGB + colorOffset;
vec4 frameTint = frame.tintRGBA;
frameTint.a = std::max(0.0f, frameTint.a - alphaOffset);
Anm2Spritesheet* spritesheet = map_find(self->anm2->spritesheets, self->anm2->layers[id].spritesheetID);
if (!spritesheet) continue;
if (!spritesheet) return;
Texture& texture = spritesheet->texture;
if (!texture.isInvalid)
{
vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frame.tintRGBA, frame.offsetRGB);
}
if (self->settings->previewIsBorder)
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, PREVIEW_BORDER_COLOR);
if (self->settings->previewIsPivots)
{
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
mat4 pivotModel = canvas_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 pivotTransform = transform * (rootModel * pivotModel);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, PREVIEW_PIVOT_COLOR);
}
}
// Nulls
if (self->settings->previewIsTargets)
{
for (auto& [id, nullAnimation] : animation->nullAnimations)
{
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0)
continue;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, self->time);
if (!frame.isVisible)
continue;
Anm2Null null = self->anm2->nulls[id];
vec4 color = (self->reference->itemType == ANM2_NULL && self->reference->itemID == id) ?
PREVIEW_NULL_SELECTED_COLOR :
PREVIEW_NULL_COLOR;
vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : ATLAS_TARGET;
mat4 model = canvas_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 nullTransform = transform * (rootModel * model);
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
if (null.isShowRect)
{
mat4 rectModel = canvas_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 rectTransform = transform * (rootModel * rectModel);
canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
}
}
}
}
s32& animationOverlayID = self->animationOverlayID;
Anm2Animation* animationOverlay = map_find(self->anm2->animations, animationOverlayID);
if (animationOverlay)
{
Anm2Frame root;
mat4 rootModel = mat4(1.0f);
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationOverlayID, ANM2_ROOT}, self->time);
if (self->settings->previewIsRootTransform)
rootModel = canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale));
for (auto [i, id] : self->anm2->layerMap)
{
Anm2Frame frame;
Anm2Item& layerAnimation = animationOverlay->layerAnimations[id];
if (!layerAnimation.isVisible || layerAnimation.frames.size() <= 0)
continue;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationOverlayID, ANM2_LAYER, id}, self->time);
if (!frame.isVisible)
continue;
Texture& texture = self->anm2->spritesheets[self->anm2->layers[id].spritesheetID].texture;
if (texture.isInvalid) continue;
if (texture.isInvalid) return;
vec2 uvMin = frame.crop / vec2(texture.size);
vec2 uvMax = (frame.crop + frame.size) / vec2(texture.size);
f32 vertices[] = UV_VERTICES(uvMin, uvMax);
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, frameTint, frameColorOffset);
mat4 model = canvas_model_get(frame.size, frame.position, frame.pivot, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 layerTransform = transform * (rootModel * model);
if (self->settings->previewIsBorder)
{
vec4 borderColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_BORDER_COLOR;
canvas_rect_draw(&self->canvas, shaderLine, layerTransform, borderColor);
}
vec4 tint = frame.tintRGBA;
tint.a *= U8_TO_FLOAT(self->settings->previewOverlayTransparency);
if (self->settings->previewIsPivots)
{
vec4 pivotColor = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) : PREVIEW_PIVOT_COLOR;
f32 vertices[] = ATLAS_UV_VERTICES(ATLAS_PIVOT);
mat4 pivotModel = canvas_model_get(CANVAS_PIVOT_SIZE, frame.position, CANVAS_PIVOT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 pivotTransform = transform * (rootModel * pivotModel);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, pivotTransform, vertices, pivotColor);
}
};
canvas_texture_draw(&self->canvas, shaderTexture, texture.id, layerTransform, vertices, tint, frame.offsetRGB);
}
}
auto null_draw = [&](mat4 rootModel, s32 id, f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
Anm2Item& nullAnimation = animation->nullAnimations[id];
if (!nullAnimation.isVisible || nullAnimation.frames.size() <= 0) return;
Anm2Frame frame;
anm2_frame_from_time(self->anm2, &frame, Anm2Reference{animationID, ANM2_NULL, id}, time);
if (!frame.isVisible) return;
Anm2Null null = self->anm2->nulls[id];
vec4 color = isOnionskin ? vec4(colorOffset, 1.0f - alphaOffset) :
(self->reference->itemType == ANM2_NULL && self->reference->itemID == id) ?
PREVIEW_NULL_SELECTED_COLOR : PREVIEW_NULL_COLOR;
vec2 size = null.isShowRect ? CANVAS_PIVOT_SIZE : PREVIEW_TARGET_SIZE;
AtlasType atlas = null.isShowRect ? ATLAS_SQUARE : self->settings->previewIsAltIcons ? ATLAS_TARGET_ALT : ATLAS_TARGET;
mat4 model = canvas_model_get(size, frame.position, size * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 nullTransform = transform * (rootModel * model);
f32 vertices[] = ATLAS_UV_VERTICES(atlas);
canvas_texture_draw(&self->canvas, shaderTexture, self->resources->atlas.id, nullTransform, vertices, color);
if (null.isShowRect)
{
mat4 rectModel = canvas_model_get(PREVIEW_NULL_RECT_SIZE, frame.position, PREVIEW_NULL_RECT_SIZE * 0.5f, PERCENT_TO_UNIT(frame.scale), frame.rotation);
mat4 rectTransform = transform * (rootModel * rectModel);
canvas_rect_draw(&self->canvas, shaderLine, rectTransform, color);
}
};
auto base_draw = [&](f32 time, vec3 colorOffset = {}, f32 alphaOffset = {}, bool isOnionskin = {})
{
Anm2Frame root;
anm2_frame_from_time(self->anm2, &root, Anm2Reference{animationID, ANM2_ROOT}, time);
mat4 rootModel = self->settings->previewIsRootTransform ?
canvas_parent_model_get(root.position, {}, PERCENT_TO_UNIT(root.scale), root.rotation) : mat4(1.0f);
if (self->settings->previewIsTargets && animation->rootAnimation.isVisible && root.isVisible)
root_draw(root, colorOffset, alphaOffset, isOnionskin);
for (auto [i, id] : self->anm2->layerMap)
layer_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
if (self->settings->previewIsTargets)
for (auto& [id, _] : animation->nullAnimations)
null_draw(rootModel, id, time, colorOffset, alphaOffset, isOnionskin);
};
auto onionskin_draw = [&](s32 count, s32 direction, vec3 colorOffset)
{
for (s32 i = 1; i <= count; i++)
{
f32 time = self->time + (f32)(direction * i);
f32 alphaOffset = (1.0f / (count + 1)) * i;
base_draw(time, colorOffset, alphaOffset, true);
}
};
auto onionskins_draw = [&]()
{
if (!self->settings->onionskinIsEnabled) return;
onionskin_draw(self->settings->onionskinBeforeCount, -1, self->settings->onionskinBeforeColorOffset);
onionskin_draw(self->settings->onionskinAfterCount, 1, self->settings->onionskinAfterColorOffset);
};
if (self->settings->onionskinDrawOrder == ONIONSKIN_BELOW) onionskins_draw();
base_draw(self->time);
if (self->settings->onionskinDrawOrder == ONIONSKIN_ABOVE) onionskins_draw();
};
animation_draw(self->reference->animationID);
animation_draw(self->animationOverlayID);
canvas_unbind();
}

View File

@@ -42,6 +42,8 @@ struct Preview
f32 time{};
};
void preview_init(Preview* self, Anm2* anm2, Anm2Reference* reference, Resources* resources, Settings* settings);
void preview_draw(Preview* self);
void preview_tick(Preview* self);

View File

@@ -7,17 +7,16 @@ enum RenderType
RENDER_PNG,
RENDER_GIF,
RENDER_WEBM,
RENDER_MP4
RENDER_MP4,
RENDER_COUNT
};
constexpr inline s32 RENDER_COUNT = RENDER_MP4 + 1;
const inline std::string RENDER_TYPE_STRINGS[] =
{
"PNG Images",
"GIF image",
"WebM video",
"MP4 video"
"MP4 video",
};
const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] =
@@ -25,5 +24,5 @@ const inline std::string RENDER_EXTENSIONS[RENDER_COUNT] =
".png",
".gif",
".webm",
".mp4"
".mp4",
};

View File

@@ -6,70 +6,87 @@ static void _settings_setting_load(Settings* self, const std::string& line)
{
const auto& entry = SETTINGS_ENTRIES[i];
const std::string& key = entry.key;
void* target = (u8*)self + entry.offset;
auto match_key = [&](const std::string& full) -> const char*
{
if (!line.starts_with(full))
return nullptr;
size_t p = full.size();
while (p < line.size() && std::isspace((u8)line[p])) ++p;
if (p < line.size() && line[p] == '=') return line.c_str() + p + 1;
if (p < line.size() && line[p] == '=')
return line.c_str() + p + 1;
return nullptr;
};
auto* value = match_key(key);
const char* value = nullptr;
if (value)
switch (entry.type)
{
switch (entry.type)
case TYPE_INT:
if ((value = match_key(key))) { *(s32*)target = std::atoi(value); return; }
break;
case TYPE_BOOL:
if ((value = match_key(key))) { *(bool*)target = string_to_bool(value); return; }
break;
case TYPE_FLOAT:
if ((value = match_key(key))) { *(f32*)target = std::atof(value); return; }
break;
case TYPE_STRING:
if ((value = match_key(key))) { *(std::string*)target = value; return; }
break;
case TYPE_IVEC2:
{
case TYPE_INT:
*(s32*)target = std::atoi(value);
return;
case TYPE_BOOL:
*(bool*)target = string_to_bool(value);
return;
case TYPE_FLOAT:
*(f32*)target = std::atof(value);
return;
case TYPE_STRING:
*(std::string*)target = value;
return;
default:
break;
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atoi(value); return; }
break;
}
}
if (entry.type == TYPE_VEC2)
{
vec2* v = (vec2*)target;
if ((value = match_key(key + (entry.isWidthHeight ? "W" : "X")))) { v->x = std::atof(value); return; }
if ((value = match_key(key + (entry.isWidthHeight ? "H" : "Y")))) { v->y = std::atof(value); return; }
}
else if (entry.type == TYPE_IVEC2)
{
ivec2* v = (ivec2*)target;
if ((value = match_key(key + (entry.isWidthHeight ? "W" : "X")))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + (entry.isWidthHeight ? "H" : "Y")))) { v->y = std::atoi(value); return; }
}
else if (entry.type == TYPE_VEC3)
{
vec3* v = (vec3*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
}
else if (entry.type == TYPE_VEC4)
{
vec4* v = (vec4*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
case TYPE_IVEC2_WH:
{
ivec2* v = (ivec2*)target;
if ((value = match_key(key + "W"))) { v->x = std::atoi(value); return; }
if ((value = match_key(key + "H"))) { v->y = std::atoi(value); return; }
break;
};
case TYPE_VEC2:
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "X"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "Y"))) { v->y = std::atof(value); return; }
break;
}
case TYPE_VEC2_WH:
{
vec2* v = (vec2*)target;
if ((value = match_key(key + "W"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "H"))) { v->y = std::atof(value); return; }
break;
};
case TYPE_VEC3:
{
vec3* v = (vec3*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
break;
}
case TYPE_VEC4:
{
vec4* v = (vec4*)target;
if ((value = match_key(key + "R"))) { v->x = std::atof(value); return; }
if ((value = match_key(key + "G"))) { v->y = std::atof(value); return; }
if ((value = match_key(key + "B"))) { v->z = std::atof(value); return; }
if ((value = match_key(key + "A"))) { v->w = std::atof(value); return; }
break;
}
default:
break;
}
}
log_warning(std::format(SETTINGS_VALUE_INIT_WARNING, line));
}
std::string settings_path_get(void)
@@ -107,15 +124,29 @@ static void _settings_setting_write(Settings* self, std::ostream& out, SettingsE
case TYPE_IVEC2:
{
ivec2* data = (ivec2*)(selfPointer + entry.offset);
out << entry.key << (entry.isWidthHeight ? "W=" : "X=") << data->x << "\n";
out << entry.key << (entry.isWidthHeight ? "H=" : "Y=") << data->y << "\n";
out << entry.key << "X=" << data->x << "\n";
out << entry.key << "Y=" << data->y << "\n";
break;
}
case TYPE_IVEC2_WH:
{
ivec2* data = (ivec2*)(selfPointer + entry.offset);
out << entry.key << "W=" << data->x << "\n";
out << entry.key << "H=" << data->y << "\n";
break;
}
case TYPE_VEC2:
{
vec2* data = (vec2*)(selfPointer + entry.offset);
out << entry.key << (entry.isWidthHeight ? "W=" : "X=") << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
out << entry.key << (entry.isWidthHeight ? "H=" : "Y=") << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
out << entry.key << "X=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
out << entry.key << "Y=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
break;
}
case TYPE_VEC2_WH:
{
vec2* data = (vec2*)(selfPointer + entry.offset);
out << entry.key << "W=" << std::format(SETTINGS_FLOAT_FORMAT, data->x) << "\n";
out << entry.key << "H=" << std::format(SETTINGS_FLOAT_FORMAT, data->y) << "\n";
break;
}
case TYPE_VEC3:
@@ -214,47 +245,32 @@ void settings_init(Settings* self)
{
const std::string path = settings_path_get();
std::ifstream file(path, std::ios::binary);
std::istream* in = nullptr;
std::istringstream defaultSettings;
if (file)
{
log_info(std::format(SETTINGS_INIT_INFO, path));
in = &file;
}
else
{
log_error(std::format(SETTINGS_INIT_ERROR, path));
log_info(SETTINGS_DEFAULT_INFO);
defaultSettings.str(SETTINGS_DEFAULT);
in = &defaultSettings;
log_warning(std::format(SETTINGS_INIT_WARNING, path));
settings_save(self);
std::ofstream out(path, std::ios::binary | std::ios::app);
out << SETTINGS_IMGUI_DEFAULT;
out.flush();
out.close();
file.open(path, std::ios::binary);
}
std::string line;
bool inSettingsSection = false;
while (std::getline(*in, line))
while (std::getline(file, line))
{
if (line == SETTINGS_SECTION)
{
inSettingsSection = true;
continue;
}
if (line.empty()) continue;
if (line == SETTINGS_SECTION_IMGUI) break;
if (inSettingsSection) _settings_setting_load(self, line);
}
// Save default settings
if (!file)
{
std::ofstream out(path, std::ios::binary | std::ios::trunc);
if (out)
{
out << SETTINGS_DEFAULT;
out.flush();
log_info(std::format(SETTINGS_SAVE_INFO, path));
}
else
log_error(std::format(SETTINGS_DEFAULT_ERROR, path));
}
}

View File

@@ -4,17 +4,15 @@
#include "render.h"
#include "tool.h"
#define SETTINGS_BUFFER 0xFFFF
#define SETTINGS_BUFFER_ITEM 0xFF
#define SETTINGS_SECTION "[Settings]"
#define SETTINGS_SECTION_IMGUI "# Dear ImGui"
#define SETTINGS_INIT_ERROR "Failed to read settings file: {}"
#define SETTINGS_DEFAULT_ERROR "Failed to write default settings file: {}"
#define SETTINGS_INIT_WARNING "Unable to read settings file: {}; using default settings"
#define SETTINGS_INIT_ERROR "Unable to read settings file: {}"
#define SETTINGS_SAVE_ERROR "Failed to write settings file: {}"
#define SETTINGS_SAVE_FINALIZE_ERROR "Failed to write settings file: {} ({})"
#define SETTINGS_VALUE_INIT_WARNING "Unknown setting: {}"
#define SETTINGS_FLOAT_FORMAT "{:.3f}"
#define SETTINGS_INIT_INFO "Initialized settings from: {}"
#define SETTINGS_DEFAULT_INFO "Using default settings"
#define SETTINGS_DIRECTORY_ERROR "Failed to create settings directory: {} ({})"
#define SETTINGS_SAVE_INFO "Saved settings to: {}"
@@ -22,277 +20,224 @@
#define SETTINGS_PATH "settings.ini"
#define SETTINGS_TEMPORARY_EXTENSION ".tmp"
#define SETTINGS_LIST \
/* name, symbol, type, defaultValue */ \
X(windowSize, WINDOW_SIZE, TYPE_IVEC2_WH, {1280, 720}) \
X(isVsync, IS_VSYNC, TYPE_BOOL, true) \
\
X(hotkeyCenterView, HOTKEY_CENTER_VIEW, TYPE_STRING, "Home") \
X(hotkeyZoomIn, HOTKEY_ZOOM_IN, TYPE_STRING, "Ctrl++") \
X(hotkeyZoomOut, HOTKEY_ZOOM_OUT, TYPE_STRING, "Ctrl+-") \
X(hotkeyPlayPause, HOTKEY_PLAY_PAUSE, TYPE_STRING, "Space") \
X(hotkeyOnionskin, HOTKEY_ONIONSKIN, TYPE_STRING, "O") \
X(hotkeyNew, HOTKEY_NEW, TYPE_STRING, "Ctrl+N") \
X(hotkeyOpen, HOTKEY_OPEN, TYPE_STRING, "Ctrl+O") \
X(hotkeySave, HOTKEY_SAVE, TYPE_STRING, "Ctrl+S") \
X(hotkeySaveAs, HOTKEY_SAVE_AS, TYPE_STRING, "Ctrl+Shift+S") \
X(hotkeyExit, HOTKEY_EXIT, TYPE_STRING, "Alt+F4") \
X(hotkeyPan, HOTKEY_PAN, TYPE_STRING, "P") \
X(hotkeyMove, HOTKEY_MOVE, TYPE_STRING, "V") \
X(hotkeyRotate, HOTKEY_ROTATE, TYPE_STRING, "R") \
X(hotkeyScale, HOTKEY_SCALE, TYPE_STRING, "S") \
X(hotkeyCrop, HOTKEY_CROP, TYPE_STRING, "C") \
X(hotkeyDraw, HOTKEY_DRAW, TYPE_STRING, "B") \
X(hotkeyErase, HOTKEY_ERASE, TYPE_STRING, "E") \
X(hotkeyColorPicker, HOTKEY_COLOR_PICKER, TYPE_STRING, "I") \
X(hotkeyUndo, HOTKEY_UNDO, TYPE_STRING, "Ctrl+Z") \
X(hotkeyRedo, HOTKEY_REDO, TYPE_STRING, "Ctrl+Shift+Z") \
X(hotkeyCopy, HOTKEY_COPY, TYPE_STRING, "Ctrl+C") \
X(hotkeyCut, HOTKEY_CUT, TYPE_STRING, "Ctrl+X") \
X(hotkeyPaste, HOTKEY_PASTE, TYPE_STRING, "Ctrl+V") \
\
X(playbackIsLoop, PLAYBACK_IS_LOOP, TYPE_BOOL, true) \
X(playbackIsClampPlayhead,PLAYBACK_IS_CLAMP_PLAYHEAD, TYPE_BOOL, true) \
\
X(changeIsCrop, CHANGE_IS_CROP, TYPE_BOOL, false) \
X(changeIsSize, CHANGE_IS_SIZE, TYPE_BOOL, false) \
X(changeIsPosition, CHANGE_IS_POSITION, TYPE_BOOL, false) \
X(changeIsPivot, CHANGE_IS_PIVOT, TYPE_BOOL, false) \
X(changeIsScale, CHANGE_IS_SCALE, TYPE_BOOL, false) \
X(changeIsRotation, CHANGE_IS_ROTATION, TYPE_BOOL, false) \
X(changeIsDelay, CHANGE_IS_DELAY, TYPE_BOOL, false) \
X(changeIsTint, CHANGE_IS_TINT, TYPE_BOOL, false) \
X(changeIsColorOffset, CHANGE_IS_COLOR_OFFSET, TYPE_BOOL, false) \
X(changeIsVisibleSet, CHANGE_IS_VISIBLE_SET, TYPE_BOOL, false) \
X(changeIsInterpolatedSet,CHANGE_IS_INTERPOLATED_SET, TYPE_BOOL, false) \
X(changeIsFromSelectedFrame,CHANGE_IS_FROM_SELECTED_FRAME,TYPE_BOOL, false) \
X(changeCrop, CHANGE_CROP, TYPE_VEC2, {}) \
X(changeSize, CHANGE_SIZE, TYPE_VEC2, {}) \
X(changePosition, CHANGE_POSITION, TYPE_VEC2, {}) \
X(changePivot, CHANGE_PIVOT, TYPE_VEC2, {}) \
X(changeScale, CHANGE_SCALE, TYPE_VEC2, {}) \
X(changeRotation, CHANGE_ROTATION, TYPE_FLOAT, 0.0f) \
X(changeDelay, CHANGE_DELAY, TYPE_INT, 0) \
X(changeTint, CHANGE_TINT, TYPE_VEC4, {}) \
X(changeColorOffset, CHANGE_COLOR_OFFSET, TYPE_VEC3, {}) \
X(changeIsVisible, CHANGE_IS_VISIBLE, TYPE_BOOL, false) \
X(changeIsInterpolated, CHANGE_IS_INTERPOLATED, TYPE_BOOL, false) \
X(changeNumberFrames, CHANGE_NUMBER_FRAMES, TYPE_INT, 1) \
\
X(scaleValue, SCALE_VALUE, TYPE_FLOAT, 1.0f) \
\
X(previewIsAxes, PREVIEW_IS_AXES, TYPE_BOOL, true) \
X(previewIsGrid, PREVIEW_IS_GRID, TYPE_BOOL, true) \
X(previewIsRootTransform, PREVIEW_IS_ROOT_TRANSFORM, TYPE_BOOL, false) \
X(previewIsTriggers, PREVIEW_IS_TRIGGERS, TYPE_BOOL, true) \
X(previewIsPivots, PREVIEW_IS_PIVOTS, TYPE_BOOL, false) \
X(previewIsTargets, PREVIEW_IS_TARGETS, TYPE_BOOL, true) \
X(previewIsBorder, PREVIEW_IS_BORDER, TYPE_BOOL, false) \
X(previewIsAltIcons, PREVIEW_IS_ALT_ICONS, TYPE_BOOL, false) \
X(previewOverlayTransparency,PREVIEW_OVERLAY_TRANSPARENCY,TYPE_FLOAT, 255.0f) \
X(previewZoom, PREVIEW_ZOOM, TYPE_FLOAT, 200.0f) \
X(previewPan, PREVIEW_PAN, TYPE_VEC2, {}) \
X(previewGridSize, PREVIEW_GRID_SIZE, TYPE_IVEC2, {32,32}) \
X(previewGridOffset, PREVIEW_GRID_OFFSET, TYPE_IVEC2, {}) \
X(previewGridColor, PREVIEW_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
X(previewAxesColor, PREVIEW_AXES_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
X(previewBackgroundColor, PREVIEW_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
\
X(generateStartPosition, GENERATE_START_POSITION, TYPE_IVEC2, {}) \
X(generateSize, GENERATE_SIZE, TYPE_IVEC2, {64,64}) \
X(generatePivot, GENERATE_PIVOT, TYPE_IVEC2, {32,32}) \
X(generateRows, GENERATE_ROWS, TYPE_INT, 4) \
X(generateColumns, GENERATE_COLUMNS, TYPE_INT, 4) \
X(generateCount, GENERATE_COUNT, TYPE_INT, 16) \
X(generateDelay, GENERATE_DELAY, TYPE_INT, 1) \
\
X(editorIsGrid, EDITOR_IS_GRID, TYPE_BOOL, true) \
X(editorIsGridSnap, EDITOR_IS_GRID_SNAP, TYPE_BOOL, true) \
X(editorIsBorder, EDITOR_IS_BORDER, TYPE_BOOL, true) \
X(editorZoom, EDITOR_ZOOM, TYPE_FLOAT, 200.0f) \
X(editorPan, EDITOR_PAN, TYPE_VEC2, {0.0,0.0}) \
X(editorGridSize, EDITOR_GRID_SIZE, TYPE_IVEC2, {32,32}) \
X(editorGridOffset, EDITOR_GRID_OFFSET, TYPE_IVEC2, {32,32}) \
X(editorGridColor, EDITOR_GRID_COLOR, TYPE_VEC4, {1.0,1.0,1.0,0.125}) \
X(editorBackgroundColor, EDITOR_BACKGROUND_COLOR, TYPE_VEC4, {0.113,0.184,0.286,1.0}) \
\
X(mergeType, MERGE_TYPE, TYPE_INT, ANM2_MERGE_APPEND_FRAMES) \
X(mergeIsDeleteAnimationsAfter,MERGE_IS_DELETE_ANIMATIONS_AFTER,TYPE_BOOL, false) \
\
X(bakeInterval, BAKE_INTERVAL, TYPE_INT, 1) \
X(bakeIsRoundScale, BAKE_IS_ROUND_SCALE, TYPE_BOOL, true) \
X(bakeIsRoundRotation, BAKE_IS_ROUND_ROTATION, TYPE_BOOL, true) \
\
X(onionskinIsEnabled, ONIONSKIN_IS_ENABLED, TYPE_BOOL, false) \
X(onionskinDrawOrder, ONIONSKIN_DRAW_ORDER, TYPE_INT, ONIONSKIN_BELOW) \
X(onionskinBeforeCount, ONIONSKIN_BEFORE_COUNT, TYPE_INT, 1) \
X(onionskinAfterCount, ONIONSKIN_AFTER_COUNT, TYPE_INT, 1) \
X(onionskinBeforeColorOffset,ONIONSKIN_BEFORE_COLOR_OFFSET,TYPE_VEC3, COLOR_RED) \
X(onionskinAfterColorOffset, ONIONSKIN_AFTER_COLOR_OFFSET,TYPE_VEC3, COLOR_BLUE) \
\
X(tool, TOOL, TYPE_INT, TOOL_PAN) \
X(toolColor, TOOL_COLOR, TYPE_VEC4, {1.0,1.0,1.0,1.0}) \
\
X(renderType, RENDER_TYPE, TYPE_INT, RENDER_PNG) \
X(renderPath, RENDER_PATH, TYPE_STRING, ".") \
X(renderFormat, RENDER_FORMAT, TYPE_STRING, "{}.png") \
X(ffmpegPath, FFMPEG_PATH, TYPE_STRING, "")
#define X(name, symbol, type, ...) \
const inline DATATYPE_TO_CTYPE(type) SETTINGS_##symbol##_DEFAULT = __VA_ARGS__;
SETTINGS_LIST
#undef X
struct Settings
{
#define X(name, symbol, type, ...) \
DATATYPE_TO_CTYPE(type) name = SETTINGS_##symbol##_DEFAULT;
SETTINGS_LIST
#undef X
};
struct SettingsEntry
{
std::string key;
DataType type;
s32 offset;
bool isWidthHeight = false;
};
struct Settings
const inline SettingsEntry SETTINGS_ENTRIES[] =
{
ivec2 windowSize = {1600, 900};
bool isVsync = true;
bool playbackIsLoop = true;
bool playbackIsClampPlayhead = true;
bool changeIsCrop = false;
bool changeIsSize = false;
bool changeIsPosition = false;
bool changeIsPivot = false;
bool changeIsScale = false;
bool changeIsRotation = false;
bool changeIsDelay = false;
bool changeIsTint = false;
bool changeIsColorOffset = false;
bool changeIsVisibleSet = false;
bool changeIsInterpolatedSet = false;
bool changeIsFromSelectedFrame = false;
vec2 changeCrop{};
vec2 changeSize{};
vec2 changePosition{};
vec2 changePivot{};
vec2 changeScale{};
f32 changeRotation{};
s32 changeDelay{};
vec4 changeTint{};
vec3 changeColorOffset{};
bool changeIsVisible{};
bool changeIsInterpolated{};
s32 changeNumberFrames = 1;
f32 scaleValue = 1.0f;
bool previewIsAxes = true;
bool previewIsGrid = true;
bool previewIsRootTransform = false;
bool previewIsTriggers = true;
bool previewIsPivots = false;
bool previewIsTargets = true;
bool previewIsBorder = false;
f32 previewOverlayTransparency = 255.0f;
f32 previewZoom = 200.0;
vec2 previewPan = {0.0, 0.0};
ivec2 previewGridSize = {32, 32};
ivec2 previewGridOffset{};
vec4 previewGridColor = {1.0, 1.0, 1.0, 0.125};
vec4 previewAxesColor = {1.0, 1.0, 1.0, 0.125};
vec4 previewBackgroundColor = {0.113, 0.184, 0.286, 1.0};
ivec2 generateStartPosition = {0, 0};
ivec2 generateSize = {64, 64};
ivec2 generatePivot = {32, 32};
s32 generateRows = 4;
s32 generateColumns = 4;
s32 generateCount = 16;
s32 generateDelay = 1;
bool editorIsGrid = true;
bool editorIsGridSnap = true;
bool editorIsBorder = true;
f32 editorZoom = 200.0;
vec2 editorPan = {0.0, 0.0};
ivec2 editorGridSize = {32, 32};
ivec2 editorGridOffset = {32, 32};
vec4 editorGridColor = {1.0, 1.0, 1.0, 0.125};
vec4 editorBackgroundColor = {0.113, 0.184, 0.286, 1.0};
s32 mergeType = ANM2_MERGE_APPEND_FRAMES;
bool mergeIsDeleteAnimationsAfter = false;
s32 bakeInterval = 1;
bool bakeIsRoundScale = true;
bool bakeIsRoundRotation = true;
s32 tool = TOOL_PAN;
vec4 toolColor = {1.0, 1.0, 1.0, 1.0};
s32 renderType = RENDER_PNG;
std::string renderPath = ".";
std::string renderFormat = "{}.png";
std::string ffmpegPath{};
#define X(name, symbol, type, ...) \
{ #name, type, offsetof(Settings, name) },
SETTINGS_LIST
#undef X
};
const SettingsEntry SETTINGS_ENTRIES[] =
{
{"window", TYPE_IVEC2, offsetof(Settings, windowSize), true},
{"isVsync", TYPE_BOOL, offsetof(Settings, isVsync)},
{"playbackIsLoop", TYPE_BOOL, offsetof(Settings, playbackIsLoop)},
{"playbackIsClampPlayhead", TYPE_BOOL, offsetof(Settings, playbackIsClampPlayhead)},
{"changeIsCrop", TYPE_BOOL, offsetof(Settings, changeIsCrop)},
{"changeIsSize", TYPE_BOOL, offsetof(Settings, changeIsSize)},
{"changeIsPosition", TYPE_BOOL, offsetof(Settings, changeIsPosition)},
{"changeIsPivot", TYPE_BOOL, offsetof(Settings, changeIsPivot)},
{"changeIsScale", TYPE_BOOL, offsetof(Settings, changeIsScale)},
{"changeIsRotation", TYPE_BOOL, offsetof(Settings, changeIsRotation)},
{"changeIsDelay", TYPE_BOOL, offsetof(Settings, changeIsDelay)},
{"changeIsTint", TYPE_BOOL, offsetof(Settings, changeIsTint)},
{"changeIsColorOffset", TYPE_BOOL, offsetof(Settings, changeIsColorOffset)},
{"changeIsVisibleSet", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
{"changeIsInterpolatedSet", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
{"changeIsFromSelectedFrame", TYPE_BOOL, offsetof(Settings, changeIsFromSelectedFrame)},
{"changeCrop", TYPE_VEC2, offsetof(Settings, changeCrop)},
{"changeSize", TYPE_VEC2, offsetof(Settings, changeSize)},
{"changePosition", TYPE_VEC2, offsetof(Settings, changePosition)},
{"changePivot", TYPE_VEC2, offsetof(Settings, changePivot)},
{"changeScale", TYPE_VEC2, offsetof(Settings, changeScale)},
{"changeRotation", TYPE_FLOAT, offsetof(Settings, changeRotation)},
{"changeDelay", TYPE_INT, offsetof(Settings, changeDelay)},
{"changeTint", TYPE_VEC4, offsetof(Settings, changeTint)},
{"changeColorOffset", TYPE_VEC3, offsetof(Settings, changeColorOffset)},
{"changeIsVisible", TYPE_BOOL, offsetof(Settings, changeIsVisibleSet)},
{"changeIsInterpolated", TYPE_BOOL, offsetof(Settings, changeIsInterpolatedSet)},
{"changeNumberFrames", TYPE_INT, offsetof(Settings, changeNumberFrames)},
{"scaleValue", TYPE_FLOAT, offsetof(Settings, scaleValue)},
{"previewIsAxes", TYPE_BOOL, offsetof(Settings, previewIsAxes)},
{"previewIsGrid", TYPE_BOOL, offsetof(Settings, previewIsGrid)},
{"previewIsRootTransform", TYPE_BOOL, offsetof(Settings, previewIsRootTransform)},
{"previewIsTriggers", TYPE_BOOL, offsetof(Settings, previewIsTriggers)},
{"previewIsPivots", TYPE_BOOL, offsetof(Settings, previewIsPivots)},
{"previewIsTargets", TYPE_BOOL, offsetof(Settings, previewIsTargets)},
{"previewIsBorder", TYPE_BOOL, offsetof(Settings, previewIsBorder)},
{"previewOverlayTransparency", TYPE_FLOAT, offsetof(Settings, previewOverlayTransparency)},
{"previewZoom", TYPE_FLOAT, offsetof(Settings, previewZoom)},
{"previewPan", TYPE_VEC2, offsetof(Settings, previewPan)},
{"previewGridSize", TYPE_IVEC2, offsetof(Settings, previewGridSize)},
{"previewGridOffset", TYPE_IVEC2, offsetof(Settings, previewGridOffset)},
{"previewGridColor", TYPE_VEC4, offsetof(Settings, previewGridColor)},
{"previewAxesColor", TYPE_VEC4, offsetof(Settings, previewAxesColor)},
{"previewBackgroundColor", TYPE_VEC4, offsetof(Settings, previewBackgroundColor)},
{"generateStartPosition", TYPE_IVEC2, offsetof(Settings, generateStartPosition)},
{"generateSize", TYPE_IVEC2, offsetof(Settings, generateSize)},
{"generatePivot", TYPE_IVEC2, offsetof(Settings, generatePivot)},
{"generateRows", TYPE_INT, offsetof(Settings, generateRows)},
{"generateColumns", TYPE_INT, offsetof(Settings, generateColumns)},
{"generateCount", TYPE_INT, offsetof(Settings, generateCount)},
{"generateDelay", TYPE_INT, offsetof(Settings, generateDelay)},
{"editorIsGrid", TYPE_BOOL, offsetof(Settings, editorIsGrid)},
{"editorIsGridSnap", TYPE_BOOL, offsetof(Settings, editorIsGridSnap)},
{"editorIsBorder", TYPE_BOOL, offsetof(Settings, editorIsBorder)},
{"editorZoom", TYPE_FLOAT, offsetof(Settings, editorZoom)},
{"editorPan", TYPE_VEC2, offsetof(Settings, editorPan)},
{"editorGridSize", TYPE_IVEC2, offsetof(Settings, editorGridSize)},
{"editorGridOffset", TYPE_IVEC2, offsetof(Settings, editorGridOffset)},
{"editorGridColor", TYPE_VEC4, offsetof(Settings, editorGridColor)},
{"editorBackgroundColor", TYPE_VEC4, offsetof(Settings, editorBackgroundColor)},
{"mergeType", TYPE_INT, offsetof(Settings, mergeType)},
{"mergeIsDeleteAnimationsAfter", TYPE_BOOL, offsetof(Settings, mergeIsDeleteAnimationsAfter)},
{"bakeInterval", TYPE_INT, offsetof(Settings, bakeInterval)},
{"bakeRoundScale", TYPE_BOOL, offsetof(Settings, bakeIsRoundScale)},
{"bakeRoundRotation", TYPE_BOOL, offsetof(Settings, bakeIsRoundRotation)},
{"tool", TYPE_INT, offsetof(Settings, tool)},
{"toolColor", TYPE_VEC4, offsetof(Settings, toolColor)},
{"renderType", TYPE_INT, offsetof(Settings, renderType)},
{"renderPath", TYPE_STRING, offsetof(Settings, renderPath)},
{"renderFormat", TYPE_STRING, offsetof(Settings, renderFormat)},
{"ffmpegPath", TYPE_STRING, offsetof(Settings, ffmpegPath)}
};
constexpr s32 SETTINGS_COUNT = (s32)std::size(SETTINGS_ENTRIES);
const std::string SETTINGS_DEFAULT = R"(
[Settings]
windowW=1600
windowH=900
isVsync=true
playbackIsLoop=true
playbackIsClampPlayhead=false
changeIsCrop=false
changeIsSize=false
changeIsPosition=false
changeIsPivot=false
changeIsScale=false
changeIsRotation=false
changeIsDelay=false
changeIsTint=false
changeIsColorOffset=false
changeIsVisibleSet=false
changeIsInterpolatedSet=false
changeIsFromSelectedFrame=false
changeCropX=0.000
changeCropY=0.000
changeSizeX=0.000
changeSizeY=0.000
changePositionX=0.000
changePositionY=0.000
changePivotX=0.000
changePivotY=0.000
changeScaleX=0.000
changeScaleY=0.000
changeRotation=0.000
changeDelay=1
changeTintR=0.000
changeTintG=0.000
changeTintB=0.000
changeTintA=0.000
changeColorOffsetR=0.000
changeColorOffsetG=0.000
changeColorOffsetB=0.000
changeIsVisible=false
changeIsInterpolated=false
changeNumberFrames=1
scaleValue=1.000
previewIsAxes=true
previewIsGrid=false
previewIsRootTransform=true
previewIsTriggers=false
previewIsPivots=false
previewIsTargets=true
previewIsBorder=false
previewOverlayTransparency=255.000
previewZoom=400.000
previewPanX=0.000
previewPanY=0.000
previewGridSizeX=32
previewGridSizeY=32
previewGridOffsetX=16
previewGridOffsetY=16
previewGridColorR=1.000
previewGridColorG=1.000
previewGridColorB=1.000
previewGridColorA=0.125
previewAxesColorR=1.000
previewAxesColorG=1.000
previewAxesColorB=1.000
previewAxesColorA=0.125
previewBackgroundColorR=0.114
previewBackgroundColorG=0.184
previewBackgroundColorB=0.286
previewBackgroundColorA=1.000
generateStartPositionX=0
generateStartPositionY=0
generateSizeX=0
generateSizeY=0
generatePivotX=0
generatePivotY=0
generateRows=4
generateColumns=4
generateCount=16
generateDelay=1
editorIsGrid=true
editorIsGridSnap=true
editorIsBorder=true
editorZoom=400.000
editorPanX=0.000
editorPanY=0.000
editorGridSizeX=32
editorGridSizeY=32
editorGridOffsetX=16
editorGridOffsetY=16
editorGridColorR=1.000
editorGridColorG=1.000
editorGridColorB=1.000
editorGridColorA=0.125
editorBackgroundColorR=0.113
editorBackgroundColorG=0.183
editorBackgroundColorB=0.286
editorBackgroundColorA=1.000
mergeType=1
mergeIsDeleteAnimationsAfter=false
bakeInterval=1
bakeRoundScale=true
bakeRoundRotation=true
tool=0
toolColorR=0.000
toolColorG=0.000
toolColorB=0.000
toolColorA=1.000
renderType=0
renderPath=.
renderFormat={}.png
ffmpegPath=
#define HOTKEY_LIST \
X(NONE, "None") \
X(CENTER_VIEW, "Center View") \
X(ZOOM_IN, "Zoom In") \
X(ZOOM_OUT, "Zoom Out") \
X(PLAY_PAUSE, "Play/Pause") \
X(ONIONSKIN, "Onionskin") \
X(NEW, "New") \
X(OPEN, "Open") \
X(SAVE, "Save") \
X(SAVE_AS, "Save As") \
X(EXIT, "Exit") \
X(PAN, "Pan") \
X(MOVE, "Move") \
X(ROTATE, "Rotate") \
X(SCALE, "Scale") \
X(CROP, "Crop") \
X(DRAW, "Draw") \
X(ERASE, "Erase") \
X(COLOR_PICKER, "Color Picker") \
X(UNDO, "Undo") \
X(REDO, "Redo") \
X(COPY, "Copy") \
X(CUT, "Cut") \
X(PASTE, "Paste") \
typedef enum
{
#define X(name, str) HOTKEY_##name,
HOTKEY_LIST
#undef X
HOTKEY_COUNT
} HotkeyType;
const inline char* HOTKEY_STRINGS[] =
{
#define X(name, str) str,
HOTKEY_LIST
#undef X
};
using HotkeyMember = std::string Settings::*;
const inline HotkeyMember SETTINGS_HOTKEY_MEMBERS[HOTKEY_COUNT] =
{
nullptr,
&Settings::hotkeyCenterView,
&Settings::hotkeyZoomIn,
&Settings::hotkeyZoomOut,
&Settings::hotkeyPlayPause,
&Settings::hotkeyOnionskin,
&Settings::hotkeyNew,
&Settings::hotkeyOpen,
&Settings::hotkeySave,
&Settings::hotkeySaveAs,
&Settings::hotkeyExit,
&Settings::hotkeyPan,
&Settings::hotkeyMove,
&Settings::hotkeyRotate,
&Settings::hotkeyScale,
&Settings::hotkeyCrop,
&Settings::hotkeyDraw,
&Settings::hotkeyErase,
&Settings::hotkeyColorPicker,
&Settings::hotkeyUndo,
&Settings::hotkeyRedo,
&Settings::hotkeyCopy,
&Settings::hotkeyCut,
&Settings::hotkeyPaste
};
const std::string SETTINGS_IMGUI_DEFAULT = R"(
# Dear ImGui
[Window][## Window]
Pos=0,32