mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-01 18:00:26 +00:00
Minor changes to improve the UserData::Variant/Properties API
Mainly added ways to forward operator= and the default copy constructor to the std::variant so we don't have to assign values creating new Variant{}s.
This commit is contained in:
parent
1830e5343f
commit
dc0e57728a
@ -1,4 +1,5 @@
|
||||
// Aseprite Document Library
|
||||
// Copyright (c) 2022 Igara Studio S.A.
|
||||
// Copyright (c) 2001-2015 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -15,6 +16,7 @@
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
@ -26,26 +28,40 @@ namespace doc {
|
||||
struct Fixed {
|
||||
fixmath::fixed value;
|
||||
};
|
||||
struct VariantStruct;
|
||||
using Variant = VariantStruct;
|
||||
struct Variant;
|
||||
using Vector = std::vector<Variant>;
|
||||
using Properties = std::map<std::string, Variant>;
|
||||
struct VariantStruct : std::variant<bool,
|
||||
int8_t, uint8_t,
|
||||
int16_t, uint16_t,
|
||||
int32_t, uint32_t,
|
||||
int64_t, uint64_t,
|
||||
Fixed,
|
||||
std::string,
|
||||
gfx::Point,
|
||||
gfx::Size,
|
||||
gfx::Rect,
|
||||
std::vector<Variant>,
|
||||
Properties>{
|
||||
using PropertiesMaps = std::map<std::string, Properties>;
|
||||
using VariantBase = std::variant<bool,
|
||||
int8_t, uint8_t,
|
||||
int16_t, uint16_t,
|
||||
int32_t, uint32_t,
|
||||
int64_t, uint64_t,
|
||||
Fixed,
|
||||
std::string,
|
||||
gfx::Point,
|
||||
gfx::Size,
|
||||
gfx::Rect,
|
||||
Vector,
|
||||
Properties>;
|
||||
|
||||
struct Variant : public VariantBase {
|
||||
Variant() = default;
|
||||
Variant(const Variant& v) = default;
|
||||
|
||||
template<typename T>
|
||||
Variant(T&& v) : VariantBase(std::forward<T>(v)) { }
|
||||
|
||||
template<typename T>
|
||||
Variant& operator=(T&& v) {
|
||||
VariantBase::operator=(std::forward<T>(v));
|
||||
return *this;
|
||||
}
|
||||
|
||||
const uint16_t type() const {
|
||||
return index() + 1;
|
||||
}
|
||||
};
|
||||
using PropertiesMaps = std::map<std::string, Properties>;
|
||||
|
||||
UserData() : m_color(0) {
|
||||
}
|
||||
@ -58,7 +74,7 @@ namespace doc {
|
||||
const std::string& text() const { return m_text; }
|
||||
color_t color() const { return m_color; }
|
||||
const PropertiesMaps& propertiesMaps() const { return m_propertiesMaps; }
|
||||
Properties& properties() { return properties(""); }
|
||||
Properties& properties() { return properties(std::string()); }
|
||||
Properties& properties(const std::string& groupKey) { return m_propertiesMaps[groupKey]; }
|
||||
|
||||
void setText(const std::string& text) { m_text = text; }
|
||||
@ -79,6 +95,26 @@ namespace doc {
|
||||
PropertiesMaps m_propertiesMaps;
|
||||
};
|
||||
|
||||
// macOS 10.9 C++ runtime doesn't support std::get<T>(value)
|
||||
// directly and we have to use std::get_if<T>(value)
|
||||
//
|
||||
// TODO replace this with std::get() in the future when we drop macOS 10.9 support
|
||||
template<typename T>
|
||||
inline const T& get_value(const UserData::Variant& variant) {
|
||||
const T* value = std::get_if<T>(&variant);
|
||||
if (value == nullptr)
|
||||
throw std::runtime_error("bad_variant_access");
|
||||
return *value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline T& get_value(UserData::Variant& variant) {
|
||||
T* value = std::get_if<T>(&variant);
|
||||
if (value == nullptr)
|
||||
throw std::runtime_error("bad_variant_access");
|
||||
return *value;
|
||||
}
|
||||
|
||||
} // namespace doc
|
||||
|
||||
#endif
|
||||
|
@ -15,6 +15,7 @@
|
||||
using namespace doc;
|
||||
using Variant = UserData::Variant;
|
||||
using Fixed = UserData::Fixed;
|
||||
using Vector = UserData::Vector;
|
||||
using Properties = UserData::Properties;
|
||||
|
||||
TEST(CustomProperties, SimpleProperties)
|
||||
@ -23,110 +24,85 @@ TEST(CustomProperties, SimpleProperties)
|
||||
// Initial data doesn't have any properties
|
||||
EXPECT_TRUE(data.properties().empty());
|
||||
|
||||
data.properties()["boolean"] = Variant{false};
|
||||
data.properties()["char"] = Variant{uint8_t('A')};
|
||||
data.properties()["number16"] = Variant{int16_t(-1024)};
|
||||
data.properties()["number32"] = Variant{int32_t(-5628102)};
|
||||
data.properties()["text"] = Variant{std::string("this is some text")};
|
||||
data.properties()["boolean"] = false;
|
||||
data.properties()["char"] = uint8_t('A');
|
||||
data.properties()["number16"] = int16_t(-1024);
|
||||
data.properties()["number32"] = int32_t(-5628102);
|
||||
data.properties()["text"] = std::string("this is some text");
|
||||
EXPECT_TRUE(data.properties().size() == 5);
|
||||
|
||||
bool* boolean = std::get_if<bool>(&data.properties()["boolean"]);
|
||||
EXPECT_TRUE(boolean);
|
||||
EXPECT_FALSE(*boolean);
|
||||
bool boolean = get_value<bool>(data.properties()["boolean"]);
|
||||
EXPECT_FALSE(boolean);
|
||||
|
||||
uint8_t* charac = std::get_if<uint8_t>(&data.properties()["char"]);
|
||||
EXPECT_TRUE(charac);
|
||||
EXPECT_TRUE(*charac == 'A');
|
||||
uint8_t charac = get_value<uint8_t>(data.properties()["char"]);
|
||||
EXPECT_EQ('A', charac);
|
||||
|
||||
int16_t* number16 = std::get_if<int16_t>(&data.properties()["number16"]);
|
||||
EXPECT_TRUE(number16);
|
||||
EXPECT_TRUE(*number16 == -1024);
|
||||
int16_t number16 = get_value<int16_t>(data.properties()["number16"]);
|
||||
EXPECT_EQ(-1024, number16);
|
||||
|
||||
int32_t* number32 = std::get_if<int32_t>(&data.properties()["number32"]);
|
||||
EXPECT_TRUE(number32);
|
||||
EXPECT_TRUE(*number32 == -5628102);
|
||||
int32_t number32 = get_value<int32_t>(data.properties()["number32"]);
|
||||
EXPECT_EQ(-5628102, number32);
|
||||
|
||||
std::string* text = std::get_if<std::string>(&data.properties()["text"]);
|
||||
EXPECT_TRUE(text);
|
||||
EXPECT_TRUE(*text == "this is some text");
|
||||
std::string text = get_value<std::string>(data.properties()["text"]);
|
||||
EXPECT_EQ("this is some text", text);
|
||||
}
|
||||
|
||||
TEST(CustomProperties, ComplexProperties)
|
||||
{
|
||||
UserData data;
|
||||
// Add a vector of ints as the "list" custom property.
|
||||
data.properties()["list"] = Variant{
|
||||
std::vector<Variant>{
|
||||
Variant{1},
|
||||
Variant{2},
|
||||
Variant{3}
|
||||
}
|
||||
};
|
||||
data.properties()["list"] = Vector{ 1, 2, 3 };
|
||||
// Check that we have one property
|
||||
EXPECT_TRUE(data.properties().size() == 1);
|
||||
|
||||
// Get the "list" vector and check some of its content
|
||||
std::vector<Variant>* list = std::get_if<std::vector<Variant>>(&data.properties()["list"]);
|
||||
EXPECT_TRUE(list);
|
||||
EXPECT_TRUE(list->size() == 3);
|
||||
int* v1 = std::get_if<int>(&(*list)[1]);
|
||||
EXPECT_TRUE(*v1 == 2);
|
||||
auto list = get_value<Vector>(data.properties()["list"]);
|
||||
EXPECT_TRUE(list.size() == 3);
|
||||
int v1 = get_value<int>(list[1]);
|
||||
EXPECT_EQ(2, v1);
|
||||
|
||||
// Add Point, Size, and Rect properties
|
||||
data.properties()["point"] = Variant{gfx::Point(10,30)};
|
||||
data.properties()["size"] = Variant{gfx::Size(50,20)};
|
||||
data.properties()["rect"] = Variant{gfx::Rect(11,22,33,44)};
|
||||
EXPECT_TRUE(data.properties().size() == 4);
|
||||
data.properties()["point"] = gfx::Point(10,30);
|
||||
data.properties()["size"] = gfx::Size(50,20);
|
||||
data.properties()["rect"] = gfx::Rect(11,22,33,44);
|
||||
EXPECT_EQ(4, data.properties().size());
|
||||
|
||||
gfx::Point* point = std::get_if<gfx::Point>(&data.properties()["point"]);
|
||||
EXPECT_TRUE(point);
|
||||
EXPECT_TRUE(point->x == 10 && point->y == 30);
|
||||
gfx::Point point = get_value<gfx::Point>(data.properties()["point"]);
|
||||
EXPECT_TRUE(point.x == 10 && point.y == 30);
|
||||
|
||||
gfx::Size* size = std::get_if<gfx::Size>(&data.properties()["size"]);
|
||||
EXPECT_TRUE(size);
|
||||
EXPECT_TRUE(size->w == 50 && size->h == 20);
|
||||
gfx::Size size = get_value<gfx::Size>(data.properties()["size"]);
|
||||
EXPECT_TRUE(size.w == 50 && size.h == 20);
|
||||
|
||||
gfx::Rect* rect = std::get_if<gfx::Rect>(&data.properties()["rect"]);
|
||||
EXPECT_TRUE(rect);
|
||||
EXPECT_TRUE(rect->x == 11 && rect->y == 22);
|
||||
EXPECT_TRUE(rect->w == 33 && rect->h == 44);
|
||||
gfx::Rect rect = get_value<gfx::Rect>(data.properties()["rect"]);
|
||||
EXPECT_TRUE(rect.x == 11 && rect.y == 22);
|
||||
EXPECT_TRUE(rect.w == 33 && rect.h == 44);
|
||||
|
||||
// Add Fixed property
|
||||
data.properties()["fixed"] = Variant{Fixed{fixmath::ftofix(10.5)}};
|
||||
EXPECT_TRUE(data.properties().size() == 5);
|
||||
data.properties()["fixed"] = Fixed{fixmath::ftofix(10.5)};
|
||||
EXPECT_EQ(5, data.properties().size());
|
||||
|
||||
Fixed* fixed = std::get_if<Fixed>(&data.properties()["fixed"]);
|
||||
EXPECT_TRUE(fixed);
|
||||
EXPECT_TRUE(fixed->value == fixmath::ftofix(10.5));
|
||||
Fixed fixed = get_value<Fixed>(data.properties()["fixed"]);
|
||||
EXPECT_EQ(fixmath::ftofix(10.5), fixed.value);
|
||||
|
||||
// Add an object with Properties
|
||||
data.properties()["object"] = Variant{
|
||||
Properties{
|
||||
{"id", Variant{uint16_t(400)}},
|
||||
{"color", Variant{std::string("red")}},
|
||||
{"size", Variant{gfx::Size{25,65}}}
|
||||
}
|
||||
};
|
||||
data.properties()["object"] = Properties{ { "id", uint16_t(400) },
|
||||
{ "color", std::string("red") },
|
||||
{ "size", gfx::Size{ 25, 65 } } };
|
||||
EXPECT_TRUE(data.properties().size() == 6);
|
||||
|
||||
Properties* object = std::get_if<Properties>(&data.properties()["object"]);
|
||||
EXPECT_TRUE(object);
|
||||
Properties object = get_value<Properties>(data.properties()["object"]);
|
||||
uint16_t id = get_value<uint16_t>(object["id"]);
|
||||
EXPECT_EQ(400, id);
|
||||
|
||||
uint16_t* id = std::get_if<uint16_t>(&(*object)["id"]);
|
||||
EXPECT_TRUE(id);
|
||||
EXPECT_TRUE(*id == 400);
|
||||
std::string color = get_value<std::string>(object["color"]);
|
||||
EXPECT_EQ("red", color);
|
||||
|
||||
std::string* color = std::get_if<std::string>(&(*object)["color"]);
|
||||
EXPECT_TRUE(color);
|
||||
EXPECT_TRUE(*color == "red");
|
||||
|
||||
size = std::get_if<gfx::Size>(&(*object)["size"]);
|
||||
EXPECT_TRUE(size);
|
||||
EXPECT_TRUE(size->w == 25 && size->h == 65);
|
||||
size = get_value<gfx::Size>(object["size"]);
|
||||
EXPECT_TRUE(size.w == 25 && size.h == 65);
|
||||
|
||||
// Try to get wrong type
|
||||
gfx::Point* p = std::get_if<gfx::Point>(&data.properties()["rect"]);
|
||||
EXPECT_TRUE(p == nullptr);
|
||||
EXPECT_EQ(nullptr, p);
|
||||
|
||||
data.properties().erase("rect");
|
||||
data.properties().erase("list");
|
||||
@ -134,7 +110,7 @@ TEST(CustomProperties, ComplexProperties)
|
||||
data.properties().erase("size");
|
||||
data.properties().erase("object");
|
||||
data.properties().erase("fixed");
|
||||
EXPECT_TRUE(data.properties().size() == 0);
|
||||
EXPECT_EQ(0, data.properties().size());
|
||||
}
|
||||
|
||||
TEST(ExtensionProperties, SimpleProperties)
|
||||
@ -143,33 +119,28 @@ TEST(ExtensionProperties, SimpleProperties)
|
||||
EXPECT_TRUE(data.properties().empty());
|
||||
EXPECT_TRUE(data.properties("someExtensionId").empty());
|
||||
|
||||
data.properties("someExtensionId")["boolean"] = Variant{false};
|
||||
data.properties("someExtensionId")["char"] = Variant{uint8_t('A')};
|
||||
data.properties("someExtensionId")["number16"] = Variant{int16_t(-1024)};
|
||||
data.properties()["number32"] = Variant{int32_t(-5628102)};
|
||||
data.properties()["text"] = Variant{std::string("this is some text")};
|
||||
data.properties("someExtensionId")["boolean"] = false;
|
||||
data.properties("someExtensionId")["char"] = uint8_t('A');
|
||||
data.properties("someExtensionId")["number16"] = int16_t(-1024);
|
||||
data.properties()["number32"] = int32_t(-5628102);
|
||||
data.properties()["text"] = std::string("this is some text");
|
||||
EXPECT_TRUE(data.properties("someExtensionId").size() == 3);
|
||||
EXPECT_TRUE(data.properties().size() == 2);
|
||||
|
||||
bool* boolean = std::get_if<bool>(&data.properties("someExtensionId")["boolean"]);
|
||||
EXPECT_TRUE(boolean);
|
||||
EXPECT_FALSE(*boolean);
|
||||
auto boolean = get_value<bool>(data.properties("someExtensionId")["boolean"]);
|
||||
EXPECT_FALSE(boolean);
|
||||
|
||||
uint8_t* charac = std::get_if<uint8_t>(&data.properties("someExtensionId")["char"]);
|
||||
EXPECT_TRUE(charac);
|
||||
EXPECT_TRUE(*charac == 'A');
|
||||
auto charac = get_value<uint8_t>(data.properties("someExtensionId")["char"]);
|
||||
EXPECT_EQ('A', charac);
|
||||
|
||||
int16_t* number16 = std::get_if<int16_t>(&data.properties("someExtensionId")["number16"]);
|
||||
EXPECT_TRUE(number16);
|
||||
EXPECT_TRUE(*number16 == -1024);
|
||||
auto number16 = get_value<int16_t>(data.properties("someExtensionId")["number16"]);
|
||||
EXPECT_EQ(-1024, number16);
|
||||
|
||||
int32_t* number32 = std::get_if<int32_t>(&data.properties()["number32"]);
|
||||
EXPECT_TRUE(number32);
|
||||
EXPECT_TRUE(*number32 == -5628102);
|
||||
auto number32 = get_value<int32_t>(data.properties()["number32"]);
|
||||
EXPECT_EQ(-5628102, number32);
|
||||
|
||||
std::string* text = std::get_if<std::string>(&data.properties()["text"]);
|
||||
EXPECT_TRUE(text);
|
||||
EXPECT_TRUE(*text == "this is some text");
|
||||
auto text = get_value<std::string>(data.properties()["text"]);
|
||||
EXPECT_EQ("this is some text", text);
|
||||
|
||||
data.properties().erase("number32");
|
||||
data.properties().erase("text");
|
||||
|
Loading…
Reference in New Issue
Block a user