widgets: add support for specifing size and position options via percentages of output dimensions (#541)

* config: introduce a custom value type for layout related options

* widgets: use CLayoutValueData for size and position options

* widgets: catch bad_any_cast and out_of_range when contructing widgets other than label

* config: rename and restrict CLayoutValueData::fromAny to fromAnyPv

This is only for casting `any` variables that represent a void * to a
CLayoutValueData*, not just any any.

* misc: remove debug prints
This commit is contained in:
Maximilian Seidler 2024-11-06 17:50:42 +01:00 committed by GitHub
parent 1cd3231537
commit 4fc133c96f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 200 additions and 90 deletions

View file

@ -0,0 +1,53 @@
#pragma once
#include "../helpers/Log.hpp"
#include <hyprutils/math/Vector2D.hpp>
#include <any>
#include <string>
enum eConfigValueDataTypes {
CVD_TYPE_INVALID = -1,
CVD_TYPE_LAYOUT = 0,
};
class ICustomConfigValueData {
public:
virtual ~ICustomConfigValueData() = 0;
virtual eConfigValueDataTypes getDataType() = 0;
virtual std::string toString() = 0;
};
class CLayoutValueData : public ICustomConfigValueData {
public:
CLayoutValueData() {};
virtual ~CLayoutValueData() {};
virtual eConfigValueDataTypes getDataType() {
return CVD_TYPE_LAYOUT;
}
virtual std::string toString() {
return std::format("{}{},{}{}", m_vValues.x, (m_sIsRelative.x) ? "%" : "px", m_vValues.y, (m_sIsRelative.y) ? "%" : "px");
}
static CLayoutValueData* fromAnyPv(const std::any& v) {
RASSERT(v.type() == typeid(void*), "Invalid config value type");
const auto P = (CLayoutValueData*)std::any_cast<void*>(v);
RASSERT(P, "Empty config value");
return P;
}
Hyprutils::Math::Vector2D getAbsolute(const Hyprutils::Math::Vector2D& viewport) {
return {
(m_sIsRelative.x ? (m_vValues.x / 100) * viewport.x : m_vValues.x),
(m_sIsRelative.y ? (m_vValues.y / 100) * viewport.y : m_vValues.y),
};
}
Hyprutils::Math::Vector2D m_vValues;
struct {
bool x = false;
bool y = false;
} m_sIsRelative;
};

View file

@ -1,12 +1,17 @@
#include "ConfigManager.hpp"
#include "../helpers/MiscFunctions.hpp"
#include "../helpers/Log.hpp"
#include "../config/ConfigDataValues.hpp"
#include <hyprutils/path/Path.hpp>
#include <filesystem>
#include <glob.h>
#include <cstring>
#include <mutex>
ICustomConfigValueData::~ICustomConfigValueData() {
; // empty
}
static Hyprlang::CParseResult handleSource(const char* c, const char* v) {
const std::string VALUE = v;
const std::string COMMAND = c;
@ -19,6 +24,51 @@ static Hyprlang::CParseResult handleSource(const char* c, const char* v) {
return result;
}
static Hyprlang::CParseResult configHandleLayoutOption(const char* v, void** data) {
const std::string VALUE = v;
Hyprlang::CParseResult result;
if (!*data)
*data = new CLayoutValueData();
const auto DATA = (CLayoutValueData*)(*data);
const auto SPLIT = VALUE.find(',');
if (SPLIT == std::string::npos) {
result.setError(std::format("expected two comma seperated values, got {}", VALUE).c_str());
return result;
}
auto lhs = VALUE.substr(0, SPLIT);
auto rhs = VALUE.substr(SPLIT + 1);
if (rhs.starts_with(" "))
rhs = rhs.substr(1);
if (lhs.contains(",") || rhs.contains(",")) {
result.setError(std::format("too many arguments in {}", VALUE).c_str());
return result;
}
if (lhs.ends_with("%")) {
DATA->m_sIsRelative.x = true;
lhs.pop_back();
}
if (rhs.ends_with("%")) {
DATA->m_sIsRelative.y = true;
rhs.pop_back();
}
DATA->m_vValues = Hyprutils::Math::Vector2D{std::stof(lhs), std::stof(rhs)};
return result;
}
static void configHandleLayoutOptionDestroy(void** data) {
if (*data)
delete reinterpret_cast<CLayoutValueData*>(*data);
}
static std::string getMainConfigPath() {
static const auto paths = Hyprutils::Path::findConfig("hyprlock");
if (paths.first.has_value())
@ -39,7 +89,6 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue(name, "shadow_passes", Hyprlang::INT{0}); \
m_config.addSpecialConfigValue(name, "shadow_color", Hyprlang::INT{0xFF000000}); \
m_config.addSpecialConfigValue(name, "shadow_boost", Hyprlang::FLOAT{1.2});
m_config.addConfigValue("general:disable_loading_bar", Hyprlang::INT{0});
m_config.addConfigValue("general:text_trim", Hyprlang::INT{1});
m_config.addConfigValue("general:hide_cursor", Hyprlang::INT{0});
@ -69,12 +118,12 @@ void CConfigManager::init() {
m_config.addSpecialCategory("shape", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("shape", "monitor", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("shape", "size", Hyprlang::VEC2{100, 100});
m_config.addSpecialConfigValue("shape", "size", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "100,100"});
m_config.addSpecialConfigValue("shape", "rounding", Hyprlang::INT{0});
m_config.addSpecialConfigValue("shape", "border_size", Hyprlang::INT{0});
m_config.addSpecialConfigValue("shape", "border_color", Hyprlang::INT{0xFF00CFE6});
m_config.addSpecialConfigValue("shape", "color", Hyprlang::INT{0xFF111111});
m_config.addSpecialConfigValue("shape", "position", Hyprlang::VEC2{0, 0});
m_config.addSpecialConfigValue("shape", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("shape", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("shape", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("shape", "rotate", Hyprlang::FLOAT{0});
@ -89,7 +138,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("image", "rounding", Hyprlang::INT{-1});
m_config.addSpecialConfigValue("image", "border_size", Hyprlang::INT{4});
m_config.addSpecialConfigValue("image", "border_color", Hyprlang::INT{0xFFDDDDDD});
m_config.addSpecialConfigValue("image", "position", Hyprlang::VEC2{0, 0});
m_config.addSpecialConfigValue("image", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("image", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("image", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("image", "rotate", Hyprlang::FLOAT{0});
@ -100,7 +149,7 @@ void CConfigManager::init() {
m_config.addSpecialCategory("input-field", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("input-field", "monitor", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("input-field", "size", Hyprlang::VEC2{400, 90});
m_config.addSpecialConfigValue("input-field", "size", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "400,90"});
m_config.addSpecialConfigValue("input-field", "inner_color", Hyprlang::INT{0xFFDDDDDD});
m_config.addSpecialConfigValue("input-field", "outer_color", Hyprlang::INT{0xFF111111});
m_config.addSpecialConfigValue("input-field", "outline_thickness", Hyprlang::INT{4});
@ -116,7 +165,7 @@ void CConfigManager::init() {
m_config.addSpecialConfigValue("input-field", "font_family", Hyprlang::STRING{"Sans"});
m_config.addSpecialConfigValue("input-field", "halign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("input-field", "valign", Hyprlang::STRING{"center"});
m_config.addSpecialConfigValue("input-field", "position", Hyprlang::VEC2{0, 0});
m_config.addSpecialConfigValue("input-field", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("input-field", "placeholder_text", Hyprlang::STRING{"<i>Input Password</i>"});
m_config.addSpecialConfigValue("input-field", "hide_input", Hyprlang::INT{0});
m_config.addSpecialConfigValue("input-field", "rounding", Hyprlang::INT{-1});
@ -135,7 +184,7 @@ void CConfigManager::init() {
m_config.addSpecialCategory("label", Hyprlang::SSpecialCategoryOptions{.key = nullptr, .anonymousKeyBased = true});
m_config.addSpecialConfigValue("label", "monitor", Hyprlang::STRING{""});
m_config.addSpecialConfigValue("label", "position", Hyprlang::VEC2{0, 0});
m_config.addSpecialConfigValue("label", "position", Hyprlang::CUSTOMTYPE{&configHandleLayoutOption, configHandleLayoutOptionDestroy, "0,0"});
m_config.addSpecialConfigValue("label", "color", Hyprlang::INT{0xFFFFFFFF});
m_config.addSpecialConfigValue("label", "font_size", Hyprlang::INT{16});
m_config.addSpecialConfigValue("label", "text", Hyprlang::STRING{"Sample Text"});

View file

@ -5,8 +5,3 @@
#include <hyprutils/math/Vector2D.hpp>
std::string absolutePath(const std::string&, const std::string&);
//
inline Hyprutils::Math::Vector2D Vector2DFromHyprlang(const Hyprlang::VEC2& vec) {
return Hyprutils::Math::Vector2D{vec.x, vec.y};
};

View file

@ -2,7 +2,7 @@
#include "../Renderer.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Log.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp"
#include <cmath>
#include <hyprlang.hpp>
@ -82,18 +82,24 @@ void CImage::plantTimer() {
CImage::CImage(const Vector2D& viewport_, COutput* output_, const std::string& resourceID_, const std::unordered_map<std::string, std::any>& props) :
viewport(viewport_), resourceID(resourceID_), output(output_), shadow(this, props, viewport_) {
size = std::any_cast<Hyprlang::INT>(props.at("size"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
color = std::any_cast<Hyprlang::INT>(props.at("border_color"));
pos = Vector2DFromHyprlang(std::any_cast<Hyprlang::VEC2>(props.at("position")));
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
try {
size = std::any_cast<Hyprlang::INT>(props.at("size"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
color = std::any_cast<Hyprlang::INT>(props.at("border_color"));
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_);
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
path = std::any_cast<Hyprlang::STRING>(props.at("path"));
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
path = std::any_cast<Hyprlang::STRING>(props.at("path"));
reloadTime = std::any_cast<Hyprlang::INT>(props.at("reload_time"));
reloadCommand = std::any_cast<Hyprlang::STRING>(props.at("reload_cmd"));
} catch (const std::bad_any_cast& e) {
RASSERT(false, "Failed to construct CImage: {}", e.what()); //
} catch (const std::out_of_range& e) {
RASSERT(false, "Missing propperty for CImage: {}", e.what()); //
}
try {
modificationTime = std::filesystem::last_write_time(path);

View file

@ -3,7 +3,7 @@
#include "../../helpers/Log.hpp"
#include "../../core/hyprlock.hpp"
#include "../../helpers/Color.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp"
#include <hyprlang.hpp>
#include <stdexcept>
@ -73,7 +73,13 @@ void CLabel::plantTimer() {
CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) :
outputStringPort(output), shadow(this, props, viewport_) {
try {
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_);
labelPreFormat = std::any_cast<Hyprlang::STRING>(props.at("text"));
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
angle = angle * M_PI / 180.0;
std::string textAlign = std::any_cast<Hyprlang::STRING>(props.at("text_align"));
std::string fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
CColor labelColor = std::any_cast<Hyprlang::INT>(props.at("color"));
@ -93,27 +99,18 @@ CLabel::CLabel(const Vector2D& viewport_, const std::unordered_map<std::string,
if (!textAlign.empty())
request.props["text_align"] = textAlign;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
pos = Vector2DFromHyprlang(std::any_cast<Hyprlang::VEC2>(props.at("position")));
configPos = pos;
viewport = viewport_;
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
angle = angle * M_PI / 180.0;
plantTimer();
} catch (const std::bad_any_cast& e) {
Debug::log(ERR, "Failed to construct CLabel: {}", e.what());
throw;
RASSERT(false, "Failed to construct CLabel: {}", e.what()); //
} catch (const std::out_of_range& e) {
Debug::log(ERR, "Missing propperty for CLabel:{}", e.what());
throw;
RASSERT(false, "Missing property for CLabel: {}", e.what()); //
}
configPos = pos;
viewport = viewport_;
g_pRenderer->asyncResourceGatherer->requestAsyncAssetPreload(request);
plantTimer();
}
bool CLabel::draw(const SRenderData& data) {

View file

@ -2,7 +2,8 @@
#include "../Renderer.hpp"
#include "../../core/hyprlock.hpp"
#include "../../core/Auth.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp"
#include "../../helpers/Log.hpp"
#include <hyprutils/string/String.hpp>
#include <algorithm>
#include <hyprlang.hpp>
@ -10,44 +11,47 @@
using namespace Hyprutils::String;
CPasswordInputField::CPasswordInputField(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props, const std::string& output) :
outputStringPort(output), shadow(this, props, viewport_) {
size = Vector2DFromHyprlang(std::any_cast<Hyprlang::VEC2>(props.at("size")));
outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness"));
dots.size = std::any_cast<Hyprlang::FLOAT>(props.at("dots_size"));
dots.spacing = std::any_cast<Hyprlang::FLOAT>(props.at("dots_spacing"));
dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center"));
dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding"));
dots.fadeMs = std::any_cast<Hyprlang::INT>(props.at("dots_fade_time"));
dots.textFormat = std::any_cast<Hyprlang::STRING>(props.at("dots_text_format"));
fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
configFailTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fail_timeout"));
fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
colorConfig.transitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
colorConfig.outer = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
colorConfig.font = std::any_cast<Hyprlang::INT>(props.at("font_color"));
colorConfig.fail = std::any_cast<Hyprlang::INT>(props.at("fail_color"));
colorConfig.check = std::any_cast<Hyprlang::INT>(props.at("check_color"));
colorConfig.both = std::any_cast<Hyprlang::INT>(props.at("bothlock_color"));
colorConfig.caps = std::any_cast<Hyprlang::INT>(props.at("capslock_color"));
colorConfig.num = std::any_cast<Hyprlang::INT>(props.at("numlock_color"));
colorConfig.invertNum = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
colorConfig.swapFont = std::any_cast<Hyprlang::INT>(props.at("swap_font_color"));
viewport = viewport_;
viewport(viewport_), outputStringPort(output), shadow(this, props, viewport_) {
try {
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_);
size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_);
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
outThick = std::any_cast<Hyprlang::INT>(props.at("outline_thickness"));
dots.size = std::any_cast<Hyprlang::FLOAT>(props.at("dots_size"));
dots.spacing = std::any_cast<Hyprlang::FLOAT>(props.at("dots_spacing"));
dots.center = std::any_cast<Hyprlang::INT>(props.at("dots_center"));
dots.rounding = std::any_cast<Hyprlang::INT>(props.at("dots_rounding"));
dots.fadeMs = std::any_cast<Hyprlang::INT>(props.at("dots_fade_time"));
dots.textFormat = std::any_cast<Hyprlang::STRING>(props.at("dots_text_format"));
fadeOnEmpty = std::any_cast<Hyprlang::INT>(props.at("fade_on_empty"));
fadeTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fade_timeout"));
hiddenInputState.enabled = std::any_cast<Hyprlang::INT>(props.at("hide_input"));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
configPlaceholderText = std::any_cast<Hyprlang::STRING>(props.at("placeholder_text"));
configFailText = std::any_cast<Hyprlang::STRING>(props.at("fail_text"));
configFailTimeoutMs = std::any_cast<Hyprlang::INT>(props.at("fail_timeout"));
fontFamily = std::any_cast<Hyprlang::STRING>(props.at("font_family"));
colorConfig.transitionMs = std::any_cast<Hyprlang::INT>(props.at("fail_transition"));
colorConfig.outer = std::any_cast<Hyprlang::INT>(props.at("outer_color"));
colorConfig.inner = std::any_cast<Hyprlang::INT>(props.at("inner_color"));
colorConfig.font = std::any_cast<Hyprlang::INT>(props.at("font_color"));
colorConfig.fail = std::any_cast<Hyprlang::INT>(props.at("fail_color"));
colorConfig.check = std::any_cast<Hyprlang::INT>(props.at("check_color"));
colorConfig.both = std::any_cast<Hyprlang::INT>(props.at("bothlock_color"));
colorConfig.caps = std::any_cast<Hyprlang::INT>(props.at("capslock_color"));
colorConfig.num = std::any_cast<Hyprlang::INT>(props.at("numlock_color"));
colorConfig.invertNum = std::any_cast<Hyprlang::INT>(props.at("invert_numlock"));
colorConfig.swapFont = std::any_cast<Hyprlang::INT>(props.at("swap_font_color"));
} catch (const std::bad_any_cast& e) {
RASSERT(false, "Failed to construct CPasswordInputField: {}", e.what()); //
} catch (const std::out_of_range& e) {
RASSERT(false, "Missing property for CPasswordInputField: {}", e.what()); //
}
auto POS__ = std::any_cast<Hyprlang::VEC2>(props.at("position"));
pos = {POS__.x, POS__.y};
configPos = pos;
configSize = size;
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
pos = posFromHVAlign(viewport, size, pos, halign, valign);
dots.size = std::clamp(dots.size, 0.2f, 0.8f);
dots.spacing = std::clamp(dots.spacing, -1.f, 1.f);

View file

@ -1,21 +1,27 @@
#include "Shape.hpp"
#include "../Renderer.hpp"
#include "../../helpers/MiscFunctions.hpp"
#include "../../config/ConfigDataValues.hpp"
#include <cmath>
#include <hyprlang.hpp>
CShape::CShape(const Vector2D& viewport_, const std::unordered_map<std::string, std::any>& props) : shadow(this, props, viewport_) {
size = Vector2DFromHyprlang(std::any_cast<Hyprlang::VEC2>(props.at("size")));
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
color = std::any_cast<Hyprlang::INT>(props.at("color"));
borderColor = std::any_cast<Hyprlang::INT>(props.at("border_color"));
pos = Vector2DFromHyprlang(std::any_cast<Hyprlang::VEC2>(props.at("position")));
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
xray = std::any_cast<Hyprlang::INT>(props.at("xray"));
try {
size = CLayoutValueData::fromAnyPv(props.at("size"))->getAbsolute(viewport_);
rounding = std::any_cast<Hyprlang::INT>(props.at("rounding"));
border = std::any_cast<Hyprlang::INT>(props.at("border_size"));
color = std::any_cast<Hyprlang::INT>(props.at("color"));
borderColor = std::any_cast<Hyprlang::INT>(props.at("border_color"));
pos = CLayoutValueData::fromAnyPv(props.at("position"))->getAbsolute(viewport_);
halign = std::any_cast<Hyprlang::STRING>(props.at("halign"));
valign = std::any_cast<Hyprlang::STRING>(props.at("valign"));
angle = std::any_cast<Hyprlang::FLOAT>(props.at("rotate"));
xray = std::any_cast<Hyprlang::INT>(props.at("xray"));
} catch (const std::bad_any_cast& e) {
RASSERT(false, "Failed to construct CShape: {}", e.what()); //
} catch (const std::out_of_range& e) {
RASSERT(false, "Missing property for CShape: {}", e.what()); //
}
viewport = viewport_;
angle = angle * M_PI / 180.0;